Uh oh!
There was an error while loading. Please reload this page.
fix(devx): re-adjudicate 17 bare-root verdicts whose "unspellable" basis is false - #12476
Conversation
…sis is false #12300 taught `hintCovers` to MATCH a glob in a non-final segment instead of collapsing it. That retirement was general, but 17 of the TRIAGE map's 26 REFUSE-UNSPELLABLE verdicts were written against the collapse, so each kept asserting "no precise spelling exists" about a population that now has one — silently, with `--self-test` green, because the self-test audits keys and verdicts and never what a `why` SAYS. Re-measured all 17 on post-merge main, driven through the real hintCovers and trackedFiles. Every refusal was false of the tree. Split by measured downstream pull, per the maintainer ruling: - 15 rows with no recorded consumer move to a new verdict, SPELLABLE-UNDECLARED — "a precise live spelling exists; declaration deferred, reason recorded". Every record carries a `spelling`, and `--self-test` pins each spelling LIVE, PRECISE and COMPLETE in hintCovers' own terms against the live corpus. An unpinned vocabulary value is the allowlist rot the ruling rejects the option on. - 2 rows with measured pull get their spelling declared beside their population constant under the ROOT_DIR_WATCH_HINTS idiom, and their refusal withdrawn to DECLARED-NARROWER: check:objectql-double-limit reaches 0 -> 2755 of its test corpus, check:skill-refs 9 -> 12 of its reference files. One row was re-measured into a different population, not merely fresher digits: #12392 made check-skills-token-ratchet walk skill directories recursively, so its old 11-of-50 filename filter is now a 49-file subtree. Also fixes a phantom assertion in the new declaration pin: spelling the needle as a literal lets `includes` find it in the assertion's own source. The needle is assembled instead. The same shape in check-cli-command-ids is filed as #12472, unfixed here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
yinlianghui
commented
Aug 26, 2026
PM review —
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#12369
scripts/pm/bare-root-worklist.mjsrecords a verdict per row of the bare-root sweep. 26 of its 39 verdicts readREFUSE-UNSPELLABLE, which asserts no precise spelling of the population exists. #12300 (783111d252) taughthintCoversto MATCH a glob in a non-final segment instead of collapsing it. That retirement was general, not specific to the rows whose prose happens to cite the collapse — so 17 verdicts kept asserting "unspellable" about populations that now have a precise, live spelling. Silently:--self-testaudits keys and verdicts and never what awhySAYS.Implements the maintainer ruling on the card (2026-08-26, verbatim 「同意」): D — split by measured pull, under its three binding conditions.
Re-measured first — the table on the card was stale in two ways
Every row re-derived on post-merge
main(6899 tracked files), driven through the realhintCovers+trackedFiles, nevercollapseHintalone, and cross-checked against each gate's own exported walk where one exists.packages/**/*.test.tsskills/*/references/**packages/**/package.json·apps/**/package.json·examples/**/package.jsonpackages/**/scripts/i18n-extract.config.tsskills/*/**examples/*/objectstack.config.tsskills/*/SKILL.mdThe 17-row class survives; the split holds at 15 without a recorded consumer + 2 with measured pull. Two corrections to the card's own table:
check-skills-token-ratchetchanged population, not just digits.[finding] The published-skills TOKEN ratchet prices only SKILL.md — 37% of the shipped bundle (68,390 tokens) carries no ceiling #12392 (PR Price the whole hand-authored skills bundle, not just SKILL.md #12423,69d0e18) merged, sodiscoverBundleFiles()now walks every published skill directory recursively — 49 files, not the 11SKILL.mdfiles the old row described.skills/*/SKILL.mdis now 11 of 49 (22%). The precise spelling is the recursiveskills/*/**at 49/49, and the row's numbers, spelling and deferral reason are all new: those 49 are 49 of the 50 tracked files under the root, so declaring the precise spelling buys one file of discrimination over the bare root — the REFUSE-WIDE trade, arriving at a row that is no longer unspellable.packages/**/*.test.tsre-measured 2736 → 2755; manifests 74/74 (the card said 74, the old row said 73).check:skill-docsis recorded honestly as the one row with no complete spelling: 100% precise at 11 of 12, the twelfth beingskills/README.md, which the generator writes and which sits outside any skill directory. That is the record, not a rounding.Condition 1 — the 15 rows, and the pin that licenses the verdict
New vocabulary value
SPELLABLE-UNDECLARED: "a precise live spelling exists; declaration deferred, reason recorded."⛔ Every record carries a
spelling, and--self-testpins it — the ruling rejects the whole option on an unpinned value (#10840 allowlist rot). Following #12330's shape: not a scanner over thewhyprose, but the mechanism each record depends on, asked of the live corpus inhintCovers' own terms. Three directions:*collapses to a double slash, soskills/*/references/_index.mdnames 9 real files and reaches none — and the family is filed as "THE LAYOUT MOVED" #12246 dead-hint species the replaced refusals were written about).hintCoversadmits satisfies the spelling's own claim.Each spelling's claim (
holds) is written independently ofhintCovers— a plain segment test — so the two agreeing is evidence, not a tautology:hintCoversroutes throughglobInNonFinalSegment→zeroSegmentForms→triggerCovers' regex, and those genuinely disagree with a segment walk on**'s zero-segment forms. Spellings are held as segment arrays joined at runtime, never spelled as literals — a glob literal in this file would enter its own hint set, which its self-test refuses in as many words. (Caught in exactly that way mid-implementation: onewhystring spelled a path inside backticks and the self-test red on it.)Condition 2 — the 2 rows with measured pull
Precise spellings declared beside their population constants under the
ROOT_DIR_WATCH_HINTSidiom, refusals withdrawn:check:objectql-double-limitcheck:skill-refsBoth consumers verified still open and still asking. Ablated: removing either declaration returns the reach to 0 and 9 respectively.
On "verdicts individually withdrawn": both land as
DECLARED-NARROWERrather than leaving the map. That verdict is defined for this shape — "the gate took the escape … the row stays in the sweep because the bare root is still not covered." Deleting the rows instead would land them as untriagedFRESHrows on the next run, which the file's own assertion says in as many words. Confirmed by measurement: neither declaration makes its rowcovered(hintCoversagainst an arbitrary file at the top of the root is false for both), soCONTRADICTEDstays quiet andDECLARED-NARROWERis the honest verdict.check:skill-refs' generator carries no--self-test, so that declaration's coupling is held from the other side — by this row's own liveness/precision pin. Stated in the declaration's docblock.Condition 3 — the authorization, and its limit
Re-adjudicating 17 rows on a shrink-only map is a re-decision, which the ruling authorizes because the definitional basis moved under them. ⛔ It authorizes nothing else: no row was added. 39 verdicts before, 39 after. The split moved
1 / 12 / 26 / 0→3 / 12 / 9 / 15(DECLARED-NARROWER / REFUSE-WIDE / REFUSE-UNSPELLABLE / SPELLABLE-UNDECLARED).The three
check:runner-env-posturerows stay REFUSED with a livepackages/**+srcspelling in hand — it is 42% true, so there is nothing there a precision pin could hold. That line, between "no spelling" and "no precise spelling", is what keeps the new value from becoming the same lie one value further along.Ablation — every pin proven able to fail
Mutation confirmed on disk each leg (anchor count → 0, injected present, sha printed); restored under
trap … EXIT INT TERM; byte-identity re-proved withgit hash-object. These are.mjs/.tsread directly from source — nodist/, so no rebuild leg applies.holdsnarrowed below the hintspellingstripped from one recordSCAN_ROOTskill entrypointsclaim to "any depth" stayed green. Measured why rather than assumed — every trackedSKILL.mdsits at depth 3, so the widened claim names the same 11 files and the tree holds no specimen that could move it. COMPLETE's ability to fire is carried by the LIVE leg instead, which reds it directly.A phantom pin found and fixed inside this diff
The first version of the new declaration pin spelled its needle as a literal:
The ablation caught it:
includesfinds the needle in the assertion's own source text, so the check is satisfied by itself and survives deleting the declaration entirely. The needle is now assembled (`'${SCAN_ROOT}/` + '*'.repeat(2) + …), and re-ablation reds on exactly the computed-declaration form the assertion names.⛔ The same shape in
scripts/check-cli-command-ids.mjsis filed as #12472 and not fixed here — a third gate, a different defect class, outside this card's declared surface. That issue remains open and is not addressed by this PR. Measured there too:'scripts/**'occurs 3× in that file, so computing its declaration away leaves theincludesgreen.check-parse-guard.mjswas checked and cleared (1 occurrence, no pin of this shape).Verification
Gate union re-derived and re-run at the final commit
3195fff5a0— the sha the numbers below come from. Derivation vianode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackwith no paths passed (it reads the merge-base change set itself); re-derived after the final commit and identical to the first pass, no gates added.Green (exit codes captured before any pipe; verdicts quoted from each gate's own line):
check:nul-bytes·bare-root-worklist --self-test·check:pm-dispatch-gates(703 cases) ·check:objectql-double-limit+ its--self-test·check:skill-refs·check:slot-lookup·check:entry-guard·check:cli-command-ids·check:agent-test-spelling·check:bash32-floor·check:parse-guard·check:pnpm-filter-targets·check:merge-driver·check:pm-governed-merges·check:published-files·check:test-source-alias·check:type-source-resolution·check:page-declaration-shape·check:cross-package-test-inputs·check:objectui-changeset· speccheck:empty-state/check:liveness/check:strictness-ledger/check:variant-docs·check-ci-filter-parity·check-comment-mask-adoption·check-plugin-teardown-shape·check-affected-docs·check-drift-comment.The worklist's own verdict line at
3195fff5a0:pnpm lint— a measured narrowing, declared as one. ESLint was run over the 3 changed files rather than the repo. Three pieces of evidence, not two: (1) the population came from ESLint's own config —isPathIgnored()returnedfalsefor all three, so none is outside the linted set; (2) the file count came from the API result, 3 linted, 0 errors, 0 warnings; (3)eslint.config.mjsenables no type-aware linting for any file — noparserOptions.project, no typed@typescript-eslintrules — which the config states at:326-336as its own measured claim with a positive control, so a diff confined to these three files cannot move any untouched file's verdict. CI runs the full farm regardless.check-dev-prereqs.mjsexits 1 in this worktree and is not a finding: it reports "The workspace is not built — 1 unmet precondition, not a list of problems" (67/67 packages missingdist/), which is the state of any fresh worktree and is unrelated to this diff.No changeset. Tooling only —
scripts/pm/, onescripts/check-*gate, and onepackages/spec/scripts/build script. Nothing published changes;packages/spec'sfileswhitelist (verified green bycheck:published-files) coversdist/+ README, notscripts/. Labelledskip-changeset.No
skills/**file is touched by this diff, so no published-bundle line or token reading applies.Generated by Claude Code