Uh oh!
There was an error while loading. Please reload this page.
fix(devx): tier the check:parse-guard out-of-tree census, so no row is printed under a reason that is false of it - #10651
Conversation
…s printed under a reason that is false of it The census printed one sentence over every out-of-tree row: "They cannot import scripts/ts-parse.mjs — a published package answering 'did this parse?' through repo tooling trades this bug for a worse one." That is true of 4 of the 28 sites. The other 9 non-test sites live in `<pkg>/scripts/**`, which their own package.json packs into no tarball — they are scripts/** in everything but their path, and one of them already imports scripts/js-comment-mask.mjs the same way. A real measurement reported under a reason that does not apply to most of what it counted is this gate's own subject matter, reproduced one block down from where it is argued. Rows are now tiered by a READ of the owning package.json — derived, never listed — and each tier prints under the sentence true of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
Three cases offered a manifest for EVERY directory, which makes `<pkg>/scripts` its own package: the row stopped there, never reached the scripts/ test, and the case went green without exercising `packsScripts` at all. Caught by ablating `packsScripts` to a constant false and seeing only one of the expected two cases go red. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
os-zhuang
commented
Aug 21, 2026
PM review — ACCEPT, arming when its five checks clear. ⭐ You found the gate committing its own defect, one block below where it argues against it.Head ⭐ The findingThe census prints one sentence over all 28 rows — "They cannot import They are
That is the defect class this entire family of cards is about, found inside the instrument built to find it. And it survived because the number was right — 28 in 20 files reproduces the card exactly, no drift. The count was never the problem; the sentence attached to it was. ⭐ The decision, and why the regrouping is the substanceYou took option 3 and then said its row grouping is wrong: rows split by what the parse is over, not by published/unpublished or gate/runtime.
⇒ A uniform "make it refuse" would have broken two working designs. That is exactly the outcome my brief was trying to avoid when it said the deliverable could be a written decision, and it is why I asked you not to pick an option just to have shipped something. Recommending against option 4 (ratchet) with a reason the card's own option-4 text elides: 15 of 28 are tests, so a ratchet "makes an unrelated PR red with no remedy its author can apply." And you noted the repo's established shape if it is ever wanted (a shrink-only hand-edited baseline, per Implemented only the unarguable part — the census tiers its rows and prints each under the sentence true of it, tier derived by walking up to the nearest ⭐⭐ The ablation that caught phantom assertions in your own new testsThis is the best paragraph in tonight's reports:
Two new cases were passing for the wrong reason, and the only thing that surfaced it was an ablation whose prediction was stated before the run and whose shortfall was treated as a signal rather than as noise. A weaker discipline — "it went red, good enough" — ships those two fixtures green forever. This is the third time tonight a control caught a void or phantom result, and the first where the phantom was inside the PR's own additions. The brief corrections, all accepted
The open questions — my rulings
One thing worth noting for the fleetOne of your lock attempts returned Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Part of #10606
check:parse-guardprints a census of every TypeScript parse outsidescripts/**— the population it reports on but does not govern. It closed that block with one sentence, printed over every row:Measured on this tree, that is true of 4 of the 28 sites.
What the measurement actually says
Re-derived on
47aff09388(the card measuredd5e7b9f5a1): 28 parses in 20 files, unchanged in total. The split the census printed — 13 shipped/gate, 15 tests — hides a third population:packages/lint/src/**(3),packages/cli/src/utils/**(1)packages/spec/scripts/**(6),packages/lint/scripts/**(3)The middle row is the finding. Those 9 sit in
<pkg>/scripts/**, and nofilesentry in their ownpackage.jsonpacks that path —@objectstack/specpacksdist,json-schema,liveness,prompts,llms.txt,README.md,src/**/*.zod.ts,CHANGELOG.md,api-surface,spec-changes.json;@objectstack/lintpacksdist,README.md,CHANGELOG.md. They reach no tarball. They arescripts/**in everything but their path, and the constraint the census recited over them is not one they have:packages/spec/scripts/check-browser-reachable-entries.ts:122already doesimport { scanSource } from '../../../scripts/js-comment-mask.mjs'— a root-scripts/parsing helper, imported from a.tstool in exactly the directory in question. Four morepackages/spec/scripts/**files import../../../scripts/check-regen-pending.mjsthe same way..d.mtsmirrors that make such an import typecheck already exist and are already a governed corpus (scripts/js-comment-mask.d.mts,scripts/check-regen-pending.d.mts, kept in step bycheck-declaration-mirrors.mjs, whose corpus is discovered rather than listed).A real measurement reported under a reason that does not apply to most of what it counted is this gate's own subject matter — a green line whose claim is wider than the truth — reproduced one block down from where it is argued.
What this PR changes
Only the census's verdict, not its scan. Rows are tiered and each tier prints under the sentence that is true of it. The tier is derived, never listed: walk up to the nearest
package.jsonand ask whether the file sits in that package'sscripts/while the manifest packs no such path. A hand-kept list of tooling directories would fail by quietly leaving a new one in the wrong tier — the same silencewalkOutsidealready refuses.Every unresolved case answers
shipped, the tier that keeps the strong claim: no manifest found, nofilesfield (npm then packs everything), or afilesarray that does packscripts/. An unproven row keeps the cautious sentence rather than acquiring a capability nobody demonstrated.No shipped source is touched and nothing is converted. The shape question is still open for the 4 shipped-source rows; this makes the census stop overstating the reason it is open.
Why not the card's own recommendation (3 + 4)
Option 4 (ratchet the census) — recommended against. The gate's header already argues the other way, and it is right for a reason the card's option-4 text elides: 15 of the 28 are tests, and a test that parses TypeScript to assert something about a parse is doing its job. A ratchet makes an unrelated PR red with no remedy its author can apply. If the population is later ratcheted, the repo's own established shape for this is a shrink-only hand-edited baseline (
scripts/startup-registry-verdict.baseline.jsonis the precedent AGENTS.md cites) — which keeps the addition visible in a diff while leaving a deliberate way through.Option 3 (per-row) — right conclusion, wrong row grouping. The rows do not split by published/unpublished or gate/runtime. They split by what the parse is over, and that axis cuts across the card's table:
packages/spec/scripts/**Programs,strictness-ledger.ts:169, andcheck-doc-formula-expressions.mjs:619(6 sites) each parse repo source. All six are in thetoolingtier and can import the helper today.check-doc-formula-expressions.mjs:266,272deliberately re-reads a top-level block statement as a parenthesized expression — a parse that fails is ordinary control flow, not an error.check-skill-examples.ts:308says so in its own docstring, and the claim verifies: a realtsc --noEmitis spawned over the same blocks (:436-439), and that file's header records thattsc"reports syntactic diagnostics and then STOPS". The loud half exists and is a separate process.So the cheapest half is larger and better-precedented than the card thought (6 sites needing no new shape at all), and 3 of the 9 must not move — a distinction that only appears once the tiering is in place.
Controls
Three ablations, each mutation confirmed on disk by anchor count before the run (
sed/perl -iexit 0 on zero matches; the helper aborts instead):packsScripts→ constantfalsetierOf's tooling branch → always'shipped'cannot answerinto thetoolingtier's prose["shipped","tooling"]The first ablation earned its keep. It was predicted to fail 2 cases and failed 1 — three fixtures offered a manifest for every directory, which makes
<pkg>/scriptsa package in its own right, so the row stopped there, never reached thescripts/test, and passed without exercising the code under test. Those phantom assertions are repaired in the second commit; the re-run then failed both as predicted. Restore leg verified by anchor: zeroABLATIONresidue, all three original anchors present, self-test green.Self-test: 30 → 41 cases.
Gates run at
beec0e46be(final commit):check:parse-guard(self-test 41/41 + tree scan),check:cross-package-test-inputs("OK: 13 package(s) read outside themselves, all declared"),check:nul-bytes("scanned 6211 text file(s) ... no raw ASCII control bytes"),eslint scripts/check-parse-guard.mjs. Union derived withnode scripts/pm/dispatch-gates.mjs(no paths passed); it named 3 families for this surface and all 3 ran. Both heavy runs went throughscripts/pm/os-verify-lock.sh—VERDICT command-exit 0 · held the lock 1s · waited 248sandVERDICT command-exit 0 · held the lock 3s · waited 366s.No changeset: the diff is confined to root
scripts/**, which no packagefilesarray packs, so it publishes nothing (AGENTS.md:943, precedent #10502).skip-changesetapplied additively.Generated by Claude Code
Generated by Claude Code