diff --git a/docs/remediation/AC-RM61-TYPESTRICT.md b/docs/remediation/AC-RM61-TYPESTRICT.md new file mode 100644 index 00000000..0583339f --- /dev/null +++ b/docs/remediation/AC-RM61-TYPESTRICT.md @@ -0,0 +1,60 @@ +# RM-61 / PR #1033 — PRE-REGISTERED ACCEPTANCE CHECKS: `exit_code` TYPE-STRICTNESS (D-40) + +**Registered by:** `mos-remediation` (orchestrator) — **BEFORE the fix was pushed and before any +reviewer read a diff.** At registration time the fix existed only as an unpushed local commit +(`6e7a336d`) on coder-mos1's machine which **I have not read**. There is therefore no diff for these +checks to have been retrofitted to. + +**Subject head:** TBD — binds to the NEW head once coder-mos1 pushes. The prior head +`033b2ffb46674b2c0bcc5197273c109b461f62d9` and its review 69 / pipeline #2193 go VOID on that push; +that cost was accepted deliberately by Mos's BLOCKING ruling on D-40. + +**Ruling being enforced (Mos, 2026-08-01):** `exit_code` must be accepted ONLY as a real integer. +`false`, `0.0`, `"0"`, and `null` must all fail to satisfy the exemption. Wrong-ACCEPT is the +disqualifying direction; this hole sits inside the load-bearing discriminator. + +## ⚠ Read this before writing the tests — RED-FIRST HERE IS NOT UNIFORM + +Two of the four near-miss values **already block** at `033b2ffb`. Demanding "all four observed RED +first" would be demanding an impossible red for two of them, and the predictable response to an +impossible demand is a fudge — weakening something real to manufacture the red. So state it precisely: + +| value | behaviour at `033b2ffb` (pre-fix) | what the new case is | +| ------- | --------------------------------- | ------------------------- | +| `false` | **WRONGLY EXEMPTS** (exit 0) | **genuine RED-FIRST** | +| `0.0` | **WRONGLY EXEMPTS** (exit 0) | **genuine RED-FIRST** | +| `"0"` | correctly blocks (exit 1) | **regression guard** only | +| `null` | correctly blocks (exit 1) | **regression guard** only | + +Claiming red-first for `"0"` or `null` would be a false claim about your own evidence. Say which is +which. **Do not weaken anything to make a green case go red** (D-8). + +## Checks + +| id | check | verdict form | +| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| **TS1** | ★RED-FIRST. `exit_code: false` on the real #2188 record is **observed wrongly exempting at `033b2ffb`** (exit 0, `exempted_steps 1`), then blocks after the fix | pre-fix ⇒0/exempt 1 · post-fix ⇒1/exempt 0 | +| **TS2** | ★RED-FIRST. Same for `exit_code: 0.0` | pre-fix ⇒0/exempt 1 · post-fix ⇒1/exempt 0 | +| **TS3** | REGRESSION GUARD. `exit_code: "0"` blocked before AND after — state honestly that it was already green | both ⇒1, `exempted_steps 0` | +| **TS4** | REGRESSION GUARD. `exit_code: null` blocked before AND after | both ⇒1, `exempted_steps 0` | +| **TS5** | ★NOT OVER-TIGHTENED. The genuine artifact — real #2188, real integer `exit_code: 0`, correctly bound head — still passes | ⇒0, `exempted_steps 1`, exactly one `WP-K8S-1000-CI-POSTGRES-TEARDOWN` | +| **TS6** | The strictness sits on the value the EXEMPTION rests on, not on a cosmetic sibling. Show the guarded comparison is the one feeding `exemption_applies` | reviewer quotes the line | +| **TS7** | `bool` is excluded EXPLICITLY, not incidentally. A bare `isinstance(x, int)` still admits `True`/`False` — verify the `not isinstance(x, bool)` clause exists | ⇒ clause present; `true` also blocks | +| **TS8** | Negative control unaffected: a genuine failed step with a real integer non-zero exit still blocks | ⇒1, `exempted_steps 0` | +| **TS9** | NO REGRESSION ON THE PRIOR ROUND'S BINDING WORK: mutated record commit ⇒1; `--expect-commit` omitted ⇒2; record `commit` absent ⇒1 | ⇒1 / ⇒2 / ⇒1 | +| **TS10** | Signature conjunction NOT widened elsewhere by this fix — `POD_NOT_FOUND` / name / type / state untouched | diff-scoped ⇒ no widening | +| **TS11** | Still no fetch / trigger / retry / re-roll / sleep / network | grep ⇒ no call sites | +| **TS12** | Full harness passes at the new head; **state the case count** (12 previously, expected 16 — confirm the actual number rather than the expected one) | ⇒0, count stated | + +## Reviewer instruction + +Verdict bound to the NEW head, evidence enumerated per check, CI counts from `pipeline-status.sh -f json` +(never default text — it omits `clone`, D-33). If a check is unrunnable, **say so**; never substitute a +passing variant. + +**Attack outside this set and report what you find.** Both blockers on this PR so far — the missing +commit binding, and this type confusion — were found outside the registered set, by mutating a field +nobody had registered a check for. That is now the expectation, not a bonus. + +**Nobody dispatching this review may state a conclusion on an open check here (D-39).** If you are sent +an observation, it is an observation; the ruling is yours. diff --git a/docs/remediation/TASKS.md b/docs/remediation/TASKS.md index d5be90a7..a336d0c0 100644 --- a/docs/remediation/TASKS.md +++ b/docs/remediation/TASKS.md @@ -338,8 +338,38 @@ conjunction earns a tracked owner even while unreachable — because RM-61's own **correlates pass today and break the moment infrastructure shifts**, and the charter's rule is that a documented gap with no owner becomes a permanent gap that reads as intentional. -**Disposition:** recommended NON-BLOCKING for #1033 and routed to Mos/the merge-gate as a recommendation, -explicitly **not** an orchestrator ruling. Fix: `isinstance(x, int) and not isinstance(x, bool)`. +**★ RULED BLOCKING (Mos, 2026-08-01).** The orchestrator recommended non-blocking, then moved off that +recommendation on the record; Mos's own earlier lean was also non-blocking on the unreachability +argument. **Neither seat shipped it.** The five grounds, first two decisive alone: + +1. **The direction is wrong-ACCEPT.** `false` and `0.0` both GRANT the exemption. This mission's bright + line is that wrong-ACCEPT disqualifies and wrong-REJECT fails safe — precisely why RR12 was tolerable + (wrong-REJECT only) and this is not. +2. **The unreachability defence is the exact argument RM-61 forbade.** _"Woodpecker is Go with an int + type, so it won't emit a boolean or a float"_ is structurally identical to the incidental correlate + RM-61 rejected for the signature: a property that holds today and breaks the moment infrastructure + shifts. The record is JSON off an API whose type contract we do not enforce. **You cannot certify the + discriminator against infra-shift and then defend a hole in it with an infra-stability assumption** — + the argument is self-undermining. +3. **It is inside the load-bearing discriminator** — "a failed service step with `exit_code 0` under a + successful workflow" is the single structural fact the exemption rests on. Highest-stakes location, + not an edge. +4. **Keystone stakes** — RM-02 and everything downstream depend on this exemption being what it claims. + Shipping it with a known wrong-ACCEPT is the cure carrying the disease. +5. **The fix is ~6 lines + 4 cases.** A tracked-owner "documented gap" is the right pattern for an + unreachable, out-of-scope, wrong-REJECT gap. It is the wrong pattern for a wrong-ACCEPT hole in the + load-bearing discriminator. We blocked on less all night; consistency demands it here. + +> **★ RM-02 STANDING CLAUSE (the generalisation, and the real prize): DISCRIMINATOR AND COMPARISON +> INPUTS MUST BE TYPE-STRICT.** A comparison that accepts a type it should not is a **wrong-ACCEPT hole +> by construction**. `== 0` matching `False` and `0.0` is one instance of a class. **D-40 is the +> instance; the clause is the fix.** Registry must ask it of every gate, alongside the D-38 clause +> ("does this gate bind its evidence to the subject?"). + +Cost accepted: the push voids head `033b2ffb`, review 69, and pipeline #2193. Fix requires +`isinstance(x, int) and not isinstance(x, bool)`; ACs pre-registered at +[`AC-RM61-TYPESTRICT.md`](./AC-RM61-TYPESTRICT.md) **before the fix was pushed and before any reviewer +read a diff** — registered against an unpushed local commit the orchestrator had not read. **Provenance note worth keeping:** this was found by a seat dispatched to adjudicate something else entirely (D-39's RR12 re-run), which looked outside its question and said so. The clean-context dispatch @@ -386,6 +416,23 @@ asymmetry is the signal, not the outcome. the re-adjudication was only trustworthy because the seat was **mechanically** fresh rather than asked to be, which is D-4's lesson applied to independence rather than to context. +**★ D-39b — THE ROLES-SWAPPED HALF (banked by Mos, 2026-08-01), found within the hour.** On D-40, +`coder-mos1` — **the author** — escalated the finding on its own PR to BLOCKER, declared the +orchestrator's gate-ready report withdrawn, and moved to **push**, which would have voided a live +APPROVED review (69) and a terminal-green exact-head pipeline (#2193). The orchestrator held it. + +> **The principle is symmetric: a seat with a stake in the answer does not settle the question.** +> Neither a dispatcher handing a reviewer its conclusion (D-39), nor an author adjudicating its own PR's +> blocker status and moving the head to enforce it (D-39b), is permitted. **A conservative motive does +> not exempt either** — the rule does not get softer because the direction is cautious. Escalating your +> own work to blocker is still adjudicating your own work. + +Correct behaviour, which is the actual division of labour: **the author surfaces evidence and prepares +the remediation; the coordinator/merge-gate adjudicates blocker status; the orchestrator routes and +holds the head.** `coder-mos1` was RIGHT to want it fixed and WRONG to try to settle it — and it was +right again immediately after, ACKing the hold and volunteering unprompted that it held an unpushed +local commit. That disclosure is the behaviour to reinforce. + ### D-38 — a gate that certified the right RECORD for the wrong COMMIT (and the AC set that could not see it) **RM-61's terminal-green verifier reported the pipeline's commit and never bound it to the PR head.**