Skip to content

fix(devx): mark ratchet-editing remedies as maintainer-only in both gates' own output (#8435) - #8517

Merged
qq9340100 merged 3 commits into
mainfrom
claude/issue-8435-ratchet-remedy-authority
Aug 13, 2026
Merged

fix(devx): mark ratchet-editing remedies as maintainer-only in both gates' own output (#8435)#8517
qq9340100 merged 3 commits into
mainfrom
claude/issue-8435-ratchet-remedy-authority

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

Fixes#8435

Committed scope item 1 only: any gate whose second remedy edits a ratchet/ledger/baseline file now marks that path as maintainer-only in its own output. Item 2 (the shared engine-double helper) is declined with a measured reason — see "Item 2: not now" below.

What changed

Diagnostic text plus embedded self-tests in the two gates the card names. Nothing else.

  • scripts/check-engine-double-contract.mjs — PINNED's second remedy used to read "Or add a MEASURED entry to scripts/engine-double-contract.baseline.json saying why not", presented symmetrically with the real fix. It now reads "That is the fix, and the only one of the two you can take on your own. ⛔ MAINTAINER-ONLY, NOT a co-equal option: … That baseline is shrink-only, so an entry weakens a ratchet and needs a maintainer to agree first — do not take this path to get CI green."
  • scripts/check-type-check-coverage.mjs — the TEST_DEBT upward-drift message got the same treatment, with every existing requirement preserved verbatim (the note-rewriting clause, the "if the delta cannot be attributed" clause).

Both gates carry the same greppable marker token, ⛔ MAINTAINER-ONLY, so it reads as one farm convention rather than one author's phrasing.

⛔ Nothing is weakened

No threshold moved, no baseline entry added, no ledger number raised, no check made skippable. scripts/engine-double-contract.baseline.json and the TEST_DEBT numbers are untouched — confirmed by the diff (two files, both under scripts/).

Verdicts proven unchanged, not asserted: each gate was run on this branch and on origin/main in the same worktree, and the outputs diffed.

  • check-engine-double-contract — full output byte-identical, OK — 197 pinned, 133 in the DEBT ledger, 2 exempt both sides.
  • check-type-check-coverage — full output byte-identical, OK — 64/77 workspace packages type-checked … 13 in the DEBT ledger.

The changed text renders only on a failing run, so on a green tree it is the self-tests that exercise it — which is why the assertions below exist.

Assertions, and the mutation testing that proves they are not empty

Three new named assertions per gate, deliberately non-overlapping so each way this can rot has exactly one owner:

  1. the offer detector still reaches its subject — fails if the remedy is reworded out from under the detector, which would otherwise make (3) pass vacuously forever;
  2. the real emitted message carries the marker — fails if the label is dropped;
  3. an unmarked offer is REJECTED — fails if the predicate stops discriminating.

Each direction was predicted in writing before running. Six mutations, one per assertion per gate; every one was caught by exactly one named assertion:

mutationcaught byexclusive
reword the offer phrase(1) detectoryes
drop the marker from the message(2) markeryes
reduce the predicate to return true(3) discriminationyes

Honest note on how (3) got there: its fixture was first derived from the real message with the marker stripped out, and in that shape the reword mutation fired both (1) and (3) — two failures for one rot, the second misdescribing the cause. It was rebuilt as a synthetic fixture, independent of the real message's wording, and the mutation matrix above is the re-run.

Item 2: not now — and the measurement that decides it

The card asks whether a shared in-memory engine-double helper can route the dispatch predicates by construction. It cannot, not as an additive change, and the blocker is this gate's own discovery scope:

scripts/check-engine-double-contract.mjs walks only files matching \.(test|spec)\.(ts|tsx|mts)$. A helper living in packages/qa/src/… is not a test file, so the gate would not see it. Two consequences, both bad:

  • every test that adopted the helper would drop out of the gate's discovered population — silently, because DISCOVERED only fires at zero, not on a gradual shrink;
  • the helper itself would be ungated — precisely "a second place that can drift from ObjectQL.update", with nothing reading it.

Two further findings that bear on the design, measured on the three landed files the card names:

  • the doubles are not uniform in the part a factory would have to own. system-caller-inert-grant.test.ts returns { ok: true, updated } from a multi-row update and { ok: true } from delete; impersonation-bearer-rotation.test.ts returns targets.length and a deleted-row count. The dispatch routing is uniform and already one line; the storage and return semantics are per-test contracts.
  • so a factory owning only the predicate call adds indirection without removing drift, and one owning the whole double must itself track ObjectQL.update — the second-source problem again.

A helper is still plausible if it is paired with a change to the gate's scan scope so the helper is itself pinned and adopters stay counted. That is a gate-verdict change, out of this card's declared surface and explicitly out of its 裁决.

Census — the "only two instances" hypothesis is falsified

The card and its triage list two instances. Swept the farm (76 scripts, 9 ratchet/baseline/ledger files, plus in-script ledgers) and found five, with a positive control confirming the sweep reaches the two known ones:

gateratchetin this PR
check-engine-double-contract.mjsengine-double-contract.baseline.jsonyes
check-type-check-coverage.mjsin-script TEST_DEBTyes
check-durability-degradation-log-level.mjsdurability-read-invention.baseline.json — its own text already says "(shrink-only, hand-edited)"no
check-role-word.mjsrole-word-baseline.json via --updateno
check-driver-conformance.mjsin-script DEBT/EXEMPT ledger — the file's comments already say a DEBT entry is one "the maintainer has agreed to", but that authority never reaches the gate's outputno

The last three are outside this card's declared file surface, so they are filed rather than fixed here; that issue is referenced from the report comment.

Deliberately not counted, because adding an entry there is the correct fix rather than a weakening: check-cross-package-test-inputs.mjs (input-radius declaration), check-agent-model-declared.mjs (INHERIT_JUSTIFIED), and check-durability-degradation-log-level.mjs's FAILURE_PROPAGATION lists. check-driver-memory-census.mjs is the precedent worth copying — its output already refuses the weakening remedy outright ("Do NOT add an entry to make …").

Verification

  • both gates' --self-test and full runs: green
  • pnpm check:type-check-debt after building the closure as lint.yml does (turbo run build --filter='./packages/*' --filter='./packages/*/*', 70/70 successful): OK — 33 ledger entr(ies) re-measured, 1969 raw tsc error(s) total, none above its recorded number. surplus: none
  • pnpm check:type-source-resolution: green — this one was not in the dispatch brief; it came out of re-deriving scripts/pm/dispatch-gates.mjs against the actual changed paths
  • pnpm check:nul-bytes: green, plus a control-character self-scan of both changed files
  • eslint on both changed files: clean

Scripts-only, no package surface touched, so skip-changeset.


Generated by Claude Code

@vercel

vercelBot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 13, 2026 4:53pm

Request Review

@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 13, 2026
@qq9340100
qq9340100 marked this pull request as ready for review August 13, 2026 17:13
@qq9340100
qq9340100 added this pull request to the merge queueAug 13, 2026
Merged via the queue into main with commit 6f83a7fAug 13, 2026
24 checks passed
@qq9340100
qq9340100 deleted the claude/issue-8435-ratchet-remedy-authority branch August 13, 2026 17:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@qq9340100@claude