Skip to content

Commit d099764

Browse files
os-justinclaude
andauthored
fix(pm): key dispatch-gates' dropped-declaration probe on the derived marker roster (#18856)
Fixes #18825 Clause-②: no ## The defect `scripts/pm/dispatch-gates.mjs` built its dropped-declaration probe's pattern — `POPULATION_MARKER_LOOKALIKE` — out of `POPULATION_MARKER_KEYS` alone, while the tool reads **six** reason-bearing `dispatch-gates:` keys. So `unparsedPopulationMarkers` was a probe over three of them and a silence over the other three: a `no-check-families`, `inherited-population` or `self-test-reads` line that READS as a declaration and does not PARSE produced exactly what a file that declares nothing produces — no reason, no refusal, no row, no count. That is #18661's own sentence, measured on the keys its repair never reached. The expensive member is `self-test-reads`: a dropped declaration takes a family out of the derived set, and `--ran` then renders its zero-NOT-MEASURED verdict over a set that no longer contains it (#18673's hole, from the parsing side). ## The before-reading, re-derived at the tip Taken through the exported `unparsedPopulationMarkers` on `origin/main` `42f8df1723` (the card's own reading was on `ad1f94e8ec` / `05389952e`; PR #18822 has landed on this file since, so every line number and the roster were re-derived rather than inherited). One line each, `file` = `scripts/x.mjs`: | probe line | before | after | |---|---|---| | `// dispatch-gates: no-path-population reason without separator` (CONTROL) | **1 row**, form `//` | **1 row**, form `//` | | `-- dispatch-gates: no-path-population -- x` (CONTROL, unknown opener) | **1 row**, form `--` | **1 row**, form `--` | | `# dispatch-gates: no-check-families reason without separator` | **0 rows** | **1 row**, form `#` | | `// dispatch-gates: no-check-families -- x` (a `//` line in YAML) | **0 rows** | **0 rows** — document content, deliberately | | `// dispatch-gates: inherited-population a b reason` | **0 rows** | **1 row**, form `//` | | `-- dispatch-gates: self-test-reads a -- x` | **0 rows** | **1 row**, form `--` | The two controls are unmoved. The three shapes the old roster never reached now sound by file, line, form and text. The `//`-in-YAML shape stays silent on purpose — see below. ## The roster, derived `MARKER_LOOKALIKES` is keyed on `Object.keys(MARKER_REASON_GRAMMARS)` — the roster PR #18822 already derives from both grammar builders (`REASON_TAIL_MARKER_KEYS` + `PATH_LIST_MARKER_KEYS`) — never a hand-list. A hand-list here would be a second copy of "which keys carry a reason that can be dropped", and it would be wrong in the silent direction the moment a seventh key arrived: the key would parse, and the probe would not know it existed. The self-test holds the two rosters EQUAL, so neither can grow alone. ## The per-key forms `markerLookalikeHead(key)` is the counterpart of `markerLineHead(key)` one question further out: that one is built from the forms the grammar ACCEPTS, this one from the forms the key's LANGUAGE has. A key `MARKER_KEY_FORMS` restricts gets exactly its own openers; every other key keeps the wide punctuation class (`MARKER_LOOKALIKE_OPENER`, unchanged bytes). So on `no-check-families` a lookalike is a **`#`-form line that fails to parse**, and a `//` or block-form line in a workflow is **document content, not a dropped declaration** — YAML has no other comment, so a line spelled that way was never a comment to have been dropped out of. Admitting one would report a line the workflow's own parser never treats as a remark, which is the same reading error the grammar refuses one level up. A consequence stated in the docblock rather than left to be found: on a restricted key only the missing-`-- REASON` tail half of the probe exists, because the unrecognised-comment-form half has nothing to find. `unparsedPopulationMarkerRefusal` now names **that key's** admissible forms per row (`markerFormsFor(key)`), not the whole roster. A refusal that offered `//` as the repair for a dropped workflow line would prescribe a spelling YAML has no comment for: the author would take the advice, the line would parse as document content, and the declaration would still be dropped — the same silence, now with the tool's endorsement. ``` .github/workflows/x.yml:1 declares no-check-families in form # — "..." (no-check-families may be written in: #) scripts/x.mjs:1 declares wide-population in form ; — "..." (wide-population may be written in: //, #, /**, /*, *) ``` ## The pins (all in this file's own `--self-test`) - the lookalike roster and `MARKER_REASON_GRAMMARS` asserted EQUAL — neither can grow alone; - the two population controls, 1 row each, key and form named — unmoved by this card; - the card's own three shapes, each one row, by file / line / form / text; - derived over the roster rather than written out six times: **every** key sounds on a dropped declaration in its own first admissible form, and **every** key stays silent on one that parses — so a seventh key arrives probed; - the `//`-and-block forms on `no-check-families` pinned as NOT lookalikes, with the contrast beside it (the same four forms on an unrestricted key are one row each) and the positive leg (the `#` form there DOES sound) — the restriction narrows which spellings are lookalikes, it does not empty them; - the refusal's remedy is the key's own forms: the workflow refusal names `#` and does not offer `//`; the script refusal names all five. ## The census, over the real tree PR #18822's live census (the `.github/workflows` reader and the `scripts/` `maskSelfTests` reader) now retains its corpus, and the LOOKALIKE half is taken over exactly those files: ``` ✓ no live file carries a DROPPED declaration on the three keys outside the population roster (37 workflow(s) + 296 script(s) swept; found none) ✓ no live gate source carries a declaration this grammar drops, on any of the 6 reason-bearing keys (250 source(s) swept) ``` **Zero is the reading, not a dead instrument** — three non-vacuity legs, each putting a dropped line directly under a LIVE declaration so the corpus, the masking and the line numbering are the real ones: a `#` line with no separator under the live `no-check-families` declaration is refused by file and line and by `#` as its remedy; a `//` line with no ` -- ` separator under the live `self-test-reads` declaration is found and nothing else is; and the SAME insertion spelled `//` in the workflow is found by neither reading. That last one is where a fix that keyed six keys on the wide opener would go red. This answers the card's `⛔ Not measured`: no live file in this tree carries a dropped declaration on those three keys today. ## The ablation ### Leg 1 — the roster, from the committed fix, full suite `MARKER_LOOKALIKES` put back on `POPULATION_MARKER_KEYS` on disk, from the committed tree, under a `trap … EXIT INT TERM` restore: ``` HEAD blob : 260f6e8 on disk before : 260f6e8 (equal — the mutation is FROM the fix) anchor occurrences 1 -> 0 · injected occurrences 0 -> 1 on disk after : 4028201a93c1369bdaeac6fa61449edffcbbaefb MUTATION LANDED ``` The FIRST attempt at this leg was a **no-op** — the mutation step and the anchor had two different spellings of the same string and nothing reached the disk. It is recorded rather than quietly retried: the on-disk guard refused it (`MUTATION FAILURE: the file on disk did not move — this ablation did NOT run`, blob unmoved, exit 9) and the run below is the repaired one, with OLD and NEW single-sourced. Ablated suite: `✗ dispatch-gates self-test: 7 of 1848 case(s) failed` (lock VERDICT `command-exit 1`, held 742s). **All seven are pins this PR adds; nothing pre-existing reds:** ``` ✗ the lookalike roster IS the derived grammar roster … ✗ and the three keys the old roster never reached now SOUND, each by file, line, form and text … ✗ every key in the roster sounds on a dropped declaration written in its own first admissible form … ✗ and the `#` form on `no-check-families` is the one that DOES sound … ✗ the refusal prescribes the forms of the KEY it names and not the whole roster … ✗ and that sweep is not vacuous: put a `#` line with no separator under the LIVE `no-check-families` declaration … ✗ and the same in a SCRIPT, on the path-list key: a `//` line under the LIVE `self-test-reads` declaration … ``` The population CONTROL pin stays GREEN under the ablation, which is the point of it; so does the census row itself (0 either way — that row is a liveness reading, and its three non-vacuity legs are what red). ``` RESTORE head-blob=260f6e85df6c62798248bdb3e0fb66e17edcf554 on-disk=260f6e85df6c62798248bdb3e0fb66e17edcf554 git diff HEAD: empty · git status --porcelain: empty ``` ### Leg 2 — the per-key FORM restriction (targeted reading, NOT a suite run) Said by name: this leg is a probe-level reading through the exported function, not a run of the 1,848-case battery. The six-key roster kept, the per-key restriction dropped (`const opener = MARKER_KEY_FORMS[key]` → `const opener = false`), on disk from the committed fix, blob `260f6e85…` → `d88df903…`, same trap: ``` 1 row(s) :: // dispatch-gates: no-check-families -- x 1 row(s) :: /* dispatch-gates: no-check-families -- x 1 row(s) :: # dispatch-gates: no-check-families reason without separator ``` That is the triage's second blind spot, reproduced: a fix that widened the roster and kept the wide opener reports two lines of workflow DOCUMENT CONTENT as dropped declarations. With the restriction in place the first two are 0 rows and the third is 1 — the pin 「a `//` or block-form line on `no-check-families` is DOCUMENT CONTENT …」 is what would red. Restored and verified by blob hash; `git diff HEAD` empty. ## Gates Derived from the worktree with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no hand-fed path list), every derived command run, exit code captured redirect-then-`$?`, reconciled with `--ran`: | exit | command | |---|---| | 0 | `node scripts/check-ci-filter-parity.mjs` | | 0 | `node scripts/check-closing-keyword-parity.mjs` | | 0 | `node scripts/check-closing-keyword-parity.mjs --self-test` | | 0 | `node scripts/check-comment-mask-corpus.mjs` | | 0 | `node scripts/check-declaration-mirrors.mjs` | | 0 | `node scripts/check-declaration-mirrors.mjs --self-test` | | 0 | `node scripts/check-scripts-symbol-anchors.mjs` | | 0 | `node scripts/check-scripts-symbol-anchors.mjs --self-test` | | 0 | `node scripts/check-self-test-wired.mjs` | | 0 | `node scripts/check-self-test-wired.mjs --self-test` | | 0 | `node scripts/check-self-test-workflow-commands.mjs` | | 0 | `node scripts/check-self-test-workflow-commands.mjs --self-test` | | 0 | `node scripts/check-whole-set-label-write.mjs` | | 0 | `node scripts/check-whole-set-label-write.mjs --self-test` | | 0 | `pnpm check:agent-test-spelling` | | 0 | `pnpm check:bash32-floor` | | 0 | `pnpm check:cli-command-ids` | | 0 | `pnpm check:cross-package-test-inputs` | | 0 | `pnpm check:declared-population-live` | | 0 | `pnpm check:driver-memory-census` | | 0 | `pnpm check:entry-guard` | | 0 | `pnpm check:nul-bytes` | | 0 | `pnpm check:parse-guard` | | 0 | `pnpm check:pnpm-filter-targets` | | 0 | `pnpm check:ratchet-remedy-authority` | | 0 | `pnpm check:refd-timer-probe` | | 0 | `pnpm check:watch-hint-literal` | | 0 | `pnpm check:pm-dispatch-gates` — the same run as the self-test line below, taken under the shared verify lock | `--ran` reconciliation (exit codes recorded as `command :: exit code`, `$?` captured before any pipe): `28 derived famil(ies) accounted for — 28 run, 0 NOT-MEASURED (a DERIVED zero — all 28 recorded an exit code and none of them is 3)`, exit 0. Self-test line (detached, waited on with `tail --pid`, never under a foreground timeout): `✓ dispatch-gates self-test: 1848 cases pass` — lock VERDICT `command-exit 0`, held 745s (12m25s). Repo-wide `pnpm lint` (`eslint . --no-inline-config`): exit 0, no narrowing. Tier, re-derived in the worktree: `Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s)`. ## Coordination - **PR #18840** (os-bill, open, another lane) edits this file at `@@ -4965,7 +4965,7 @@` in `COMPOUND_ANCHOR_LEDGER` — a one-line rename of a `scripts/check-platform-checklist.mjs` self-test symbol. Read; not touched. This diff's lowest line is well above it, and adds no compound `selfTest`-shaped name, so it needs no ledger row. Either PR lands first. - **#18536**'s machine-side half is the next card on this file. Title read; not folded in. - PR #18822 / #18661 / #18784 are landed and read, not re-opened. ## Acceptance notes - `skip-changeset`: nothing published moves. `scripts/pm/**` is the fast lane — not in any package's `files[]`. --- _Generated by [Claude Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7607076 commit d099764

1 file changed

Lines changed: 322 additions & 29 deletions

File tree

0 commit comments

Comments
 (0)