Skip to content

Commit 2326eab

Browse files
os-billclaude
andauthored
fix(lint): ADR-0087's prescription detector reads a rewrite whose TO side is an instruction (#18834)
Fixes #18745 ADR-0087's `no-migration-prescription` detector missed a framed FROM → TO line again, so an exemption the categories inherit can be held by SILENCE rather than by a finding. This adds the one arm that was missing, answers the three questions the claim could not, and hands back the one reading that is not this PR's to act on. This is ⛔ NOT a defect in PR #18736. That PR's exemption rests on four positively verified predicates and the gate printed `-- verified`. What recurred is the INSTRUMENT. Clause-②: no — one repo gate script, no published byte moves (measured below). ## Q1 — which spelling was missed, beside the branch that should have caught it Anchored on SYMBOLS and reported where they actually are. In `scripts/check-adr-0087-registration.mjs`: `findMigrationPrescription` is at **:1562** on `origin/main` and at **:1685** on this branch. The card's `:204-214` for the header paragraph is NOT stale — `That argument is correct and it was accepted` really does open at **:204** on `origin/main`, and still does here (this PR appends its cross-reference after that paragraph rather than editing it). ``` MISSED: **Migration — `api: { … }` → delete the property.** .changeset/18318-evalcontext-no-query-api.md BRANCH: 2, `framed-line` (REWRITE_RE under MIGRATION_FRAMING_RE) ``` Predicate by predicate on that one line, each with its own non-zero control: | predicate | on the missed line | non-zero control | |---|---|---| | `MIGRATION_FRAMING_RE` | `true` | `false` on `plain prose` | | FROM side is an `OPERAND` | `true` (a backticked span) | — | | an `ARROW` is present | `true` | — | | `REWRITE_RE` (needs an `OPERAND` on BOTH sides) | **`false`** | `true` on the same line with `` `os.exists` `` as the TO side | So it failed on exactly ONE predicate. `delete the property` is an INSTRUCTION, not a name — and `OPERAND` is a backticked code span or a dotted/slashed identifier path. A removal has no new name to put on the right, so the most careful prescription an author can write for one is invisible to every arm. ## Q2 — the #8277 miss in the gate's header is a DIFFERENT hole Measured on `fc71b84:.changeset/lucky-schools-smash.md` through the exported detector. | | #8277 (the header's case) | #18745 (this round) | |---|---|---| | the prescription | `read` `` `result.driverFault?.message` `` `where you read` `` `result.error` `` | `` `api: { … }` `` → `delete the property` | | arrow on that line | no | yes | | migration framing on that line | no | yes | | FROM side an operand | n/a — no pair at all | yes | | what it fails on | there is no rewrite PAIR to match | the TO side is not an operand | | after this PR | still `null` | `framed-removal` | #8277 is the **IMPERATIVE-SENTENCE** residue this gate's own header already lists as deliberate ("no arrow, no table, just …"), and this PR does not move it one inch. RM11 and RM12 pin that in the self-test, so a later author widening toward it argues with a red test rather than with prose. Closing the second hole does not close the first, and a repair that claimed to would be the over-matching criterion the residue paragraph says to falsify before implementing. ## Q3 — does tightening red the existing changeset stock? ONE reading, handed back Measured over the 414 changesets in `.changeset/` at `origin/main`: ``` BEFORE hits 28 / 414 {framed-line: 1, from-to-label: 25, header-framed-table: 2} AFTER hits 29 / 414 {framed-line: 1, from-to-label: 25, header-framed-table: 2, framed-removal: 1} ``` Exactly one row moves, and it is the card's own: `18318-evalcontext-no-query-api.md`, `null` → `framed-removal`. It holds `not-required (runtime-interface-only …)`, a category that INHERITS the prescription refusal, so it WOULD be refused if the gate re-judged it. It does not today: this gate judges only changesets a diff adds or modifies, and this PR touches none. `pnpm check:adr-0087-registration` is green on this branch. ⚠️ **That reading is handed back, not acted on.** ⛔ No other author's changeset is edited here. The order is the seat's to decide. ## LIT — the behaviour flip, on the REAL missed text Not "the gate is still green". The same 414-changeset verdict table, before and after: ``` 2,4c2,4 < HIT (prescription found): 28 < BY BRANCH: {"framed-line":1,"from-to-label":25,"header-framed-table":2} --- > HIT (prescription found): 29 > BY BRANCH: {"framed-line":1,"from-to-label":25,"header-framed-table":2,"framed-removal":1} 191c191 < null 18318-evalcontext-no-query-api.md --- > framed-removal 18318-evalcontext-no-query-api.md ``` End to end through `scan()`, the card's own scenario: the real missed text carrying `not-required (runtime-interface-only packages/formula/src/types.ts#EvalContext)` is now REFUSED with `Evidence (framed-removal)` (`RM-E2E1`), and the SAME claim on the SAME symbol with the removal sentence taken out is still ACCEPTED (`RM-E2E1b`) — which is what makes the red attributable to the prescription refusal rather than to a hand-built fixture failing some `runtime-interface-only` predicate for an unrelated reason. ## DARK — the spellings it already recognised keep their verdict The other **413** rows of that table are byte-identical before and after, and every pre-existing branch keeps its count (`framed-line` 1, `from-to-label` 25, `header-framed-table` 2). That is structural, not measured-and-remembered: the new hit is held back and consulted LAST, so the function is exactly `arrowBranches ?? framedTable ?? headerFramedTable ?? framedRemoval` and no body an older arm reads can change its branch or its evidence line. Self-test case counts: **384 → 405** assertions, exit 0 both times. Two new batteries (`RM` end-to-end, 9 cases; `RM1-RM12` unit pins, 12) are declared in `SELF_TEST_BATTERIES` with their own floors. ## The class is closed, and derived from the stock rather than invented Measured over EVERY `.changeset/*.md` blob in this repository's history — 5524 unique blobs — the framed arrow lines whose right side is not an operand number **three**. Every candidate vocabulary tried (bare imperatives; plus `unset`/`omit`; plus `none`/`nothing`; plus the past participles; plus the Chinese arm) flags the **same two** changesets and no others — `18318-evalcontext-no-query-api.md` and `drop-dead-env-template-flag.md` (`**Migration.** …create --template… → drop the flag`), both genuine removal prescriptions on inspection. Zero false positives at any width, so the NARROW class ships: a wider one buys nothing measured and each extra word is a claim somebody later has to defend. The third non-operand right side, `lookup-reference-target-gate.md`, is running prose about resolution rungs, and this arm leaves it alone. `none` / `nothing` in arrow position was measured and REFUSED: the stock evidences that spelling only as a table CELL, which branch 4 already reads, and in arrow position it has a competing reading an imperative verb does not (`… → none of this changes what you write` is a running sentence). RM8 pins the refusal. ## Reverse verification (ablation) Committed first, then mutated on disk, then restored and proven: Committed first, then mutated on disk, then restored and proven — the mutation is `return table ?? headerTable ?? removal;` back to `return table ?? headerTable;`, which disables the arm and nothing else: ``` HEAD blob: 84cce40 working-tree blob BEFORE: 84cce40 BEFORE: arm-live count=1 ablated count=0 AFTER MUTATION: arm-live count=0 ablated count=1 on-disk proof, blob AFTER: 6d8d731286f2d643873ce63658b55959bec9a3bc ABLATED self-test exit: 1 -- 16 failure(s) RM-E2E1 ... : expected RED, got green RM-E2E2 ... : expected RED, got green RM1 / RM2 / RM3 ... and check-adr-0087-registration itself: 1 input problem -- the framing-scan-rot control restored blob: 84cce40 restore proven: byte-identical to HEAD, git diff HEAD empty RESTORED self-test exit: 0 -- 405 assertions ``` The ablated run NAMES the RM cases, so they are wired to the arm rather than passing for some other reason, and the new framing-scan-rot control fires on the live run too. The script is read by `node` directly and no package builds it, so there is no `dist/` leg to this ablation. ## Changeset — measured, not inferred from the path `skip-changeset`. The repo-root manifest is `"private": true`, so nothing at repo root is published; the edited file is tracked only at `scripts/check-adr-0087-registration.mjs` (plus its ADR anchor JSON), and **0 of the 70** workspace manifests carrying a `files[]` name a path that could ship it — non-zero control: 70 manifests were read, e.g. `packages/adapters/hono` ships `dist,README.md,CHANGELOG.md`. ## Acceptance notes Gates derived with `scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (32 commands) and all 32 run on this branch: **31 exit 0**. The 32nd, `pnpm check:pm-dispatch-gates`, is two batteries chained and exceeded the 480s window it was given (exit 124 = timeout, NOT MEASURED); re-run on its own, `node scripts/pm/check-dispatch-gates.mjs` is **exit 0, 1809 cases, 535.2s on this box**. `check-widening-tells --declaration no` reports this file as NOT MEASURED (no declared surface covers `scripts/`), which is ⛔ not a clearance and is stated as such. Repo-wide scans (`pnpm lint` and the artifact-roster families the deriver lists as silent) are CI's run, not this PR's claim. Noted, ⛔ not filed, and ⛔ not folded in — recorded so they are not lost: - `check-adr-0087-registration`'s predicate-4 refusal text for `type-surface-only` prints "fix: register the migration" without naming `runtime-interface-only`. Already recorded on the card by the filing seat; the carrier is whoever next edits that message. Not touched here — it is a refusal-message improvement, not a wrong verdict. - `.changeset/unscoped-multi-update-refusal.md` writes its TO side as a BOLD-WRAPPED code span (`** ` + backticked name + ` **`), which `OPERAND` does not reach because `\s*` after the arrow cannot cross the asterisks. It is caught today by another line in the same body, so no verdict rests on it, and widening `OPERAND` is a different claim with its own false-positive surface. Carrier: whoever next measures the operand vocabulary. --- _Generated by [Claude Code](https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 631dcbd commit 2326eab

1 file changed

Lines changed: 289 additions & 2 deletions

File tree

0 commit comments

Comments
 (0)