Uh oh!
There was an error while loading. Please reload this page.
Consolidate the private pnpm-workspace.yaml parsers behind one shared enumerator - #11797
Conversation
…s behind one shared enumerator Nine scripts carried a private parse of the `packages:` block. Measured against each other on their real source bytes, they formed four behaviour clusters that agreed on this repo's workspace file and disagreed on nine of seventeen adversarial inputs. The shared module is deliberately NOT a gate file and declares NO path population: priced on the live tree, spelling the workspace globs as literals there would have handed importers +41725 (gate, file) pairs and turned check:release-body red by contradicting its no-path-population marker. Each gate keeps declaring its own population. check-test-source-alias and check-type-source-resolution keep WORKSPACE_PARENT_GLOBS and gain a both-directions reconciliation against the live parse, which is what retires the drift between their byte-identical copies. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
…le to fail The guard stripped block comments with a hand-rolled /\*...\*/ regex. A workspace glob contains a comment opener -- the `/` and `*` of `packages/*` ARE `/*` -- so the stripper opened a comment at the literal it was hunting and ate forward to the next `*/`. An ablation planting `['packages/*', 'apps/*']` in the module passed the guard while really contributing 5154 pairs per importer. Now uses maskComments from js-comment-mask.mjs (proper string/comment scanning) and applies extractWatchHints' leading-`./` strip so a relative import specifier is not misread as a declaration. Re-ablated: the planted literal is now caught by all five consolidated gates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
…migration Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
os-steve
commented
Aug 24, 2026
ACCEPT. Verified by content on ⭐ Leg 3 first PASSED, and keeping that failure in the diff is the most valuable thing here
Verified, trivially and damningly: So the guard meant to prove the shared module declares zero path population was vacuous against the exact literal it existed to catch — and the planted literal really did contribute 5154 pairs per importer while the guard read green. A guard that is blind precisely to its own subject is the worst shape a guard can take, and nothing but an ablation would have found it: it passes every test you would think to write, because the thing it cannot see is the thing you are testing for. You found it because leg 3 passed when you had predicted red, and you preserved the failure as its own commit ( Both card assumptions falsified, and both verified(1) The thirteen are not thirteen parsers. Checked each on
Three of the card's thirteen were never in scope, and leaving them untouched is right. ⭐ And a fourth real parser the family measurement could not see — (2) The parsers were not equivalent — which is the exact hazard I named in Zone 2 as "the way this PR could do real damage, and it would not show up as a red gate." Sliced from their real source bytes and run against 17 inputs: agreement on the repo's actual file, four clusters on 9 of 17. A shared answer that was only ever true by coincidence. Consolidating those blind would have changed several gates' populations with nothing going red. Partial consolidation, and the pricing that defends itThe parse is shared; the declaration is not. Priced on the live tree: spelling the workspace globs as literals in the shared module hands importers +41725 (gate, file) pairs — 13.6× the +3065 the follow already refuses — and turns And the counterfactual is measured rather than argued: consolidating the declaration too drops This is the "partial consolidation you can defend beats a uniform one you cannot" outcome I asked for, arrived at by measurement rather than by caution. The sweep closes exactlyand the +6 is accounted for one file at a time across the six The three named regression checks all still name real cards ( Both declared narrowings are unbuilt-tree refusals, and both are disposed of properly — Full-repo Flipping to ready. Arming once every check is green. Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#11510
Step 2 of #11190: nine scripts carried a private parse of
pnpm-workspace.yaml'spackages:block. They now sharescripts/workspace-enumerator.mjs. Verified at38a1d66c9.The card's count, re-measured on the day
The farm grew 140 → 154; the 15/13 figure holds.
Assumption falsified: the thirteen are not thirteen parsers
The card's list is grep-shaped. Three of its thirteen never parse the
packages:block, so there was nothing to consolidate in them and they are untouched:check-osv-exemptions.mjscheck-cross-package-test-inputs.mjsWORKSPACE_ROOT_MARKERS)check-prerelease-pin-watch.mjsoverrides:block — a different block, one readerTwo more carry no parser but a hardcoded 11-entry
WORKSPACE_PARENT_GLOBS— these are the byte-identical pair the card names. See "the declaration" below. A fourth parser the family measurement could not see,ablation-dist-preflight.mjs(not a gate), was consolidated too.The parsers were not equivalent — measured, not read
Each parser was sliced out of its real source bytes and run against 17 inputs. They agree on the repo's actual file (positive control) and split into four clusters on 9 of 17:
- pkg/* # note- vendor/c#sharpvendor/cpackages :(space)packages:blockpackages:key[]packages:block[][]/ throw- packages/**Every divergence is settled toward refusing over under-reporting. Three matter:
#is a YAML comment only at line start or after whitespace. Four parsers stripped/#.*$/unconditionally, silently truncating a legitimate member path to a directory that does not exist. Two others glued the comment onto the pattern. No old parser was right; the new one is.[]for an absent/empty block is a clean run over an empty workspace — every "is every member covered" gate passes vacuously, loudly green.packages:key is invalid YAML; three parsers stopped at the first block and three appended the second. Neither is a reading anyone chose, so the input is refused.No caller's answer changes on this tree. Proven, not assumed — each caller's member set computed from
origin/main's bytes vs the shared module:The module consolidates the PARSE, never the DECLARATION
This is the load-bearing decision, and it is a measurement. Had the shared module spelled the workspace globs as literals, the import follow would have handed them to every importer:
13.6× the +3065 the follow already refuses — and ⛔
check:release-bodycarries ano-path-populationmarker, so a literal there turns that gate red via dispatch-gates' own "no family both DECLARES no path population and names paths anyway" assertion. Three callers had already measured and refused this declaration in writing.So the module declares zero path population, verified with dispatch-gates' own extractor:
'pnpm-workspace.yaml'is the one string it spells, and it is safe:hintCoversrefuses a literal with no path separator.Sweep: before → after
The +6 is exactly the one new file matched by the six
scripts/**families. Every other family's count is unchanged; nothing was re-attributed.The three named regression checks
Live derivations, before and after, on three card paths:
check:published-filesROOT_DIR_WATCH_HINTScheck:test-source-aliasWORKSPACE_PARENT_GLOBScheck:type-source-resolutionWORKSPACE_PARENT_GLOBSAll three still name real cards, via each gate's own declaration.
Counterfactual, measured: replacing
WORKSPACE_PARENT_GLOBSwith a runtime parse — i.e. consolidating the declaration too — dropscheck:test-source-aliasto 0 matched-list occurrences for apackages/card. That is "goes back to naming nothing", exactly as the card predicted.Instead, the two byte-identical arrays stay and gain a both-directions reconciliation against the live parse (the
check-published-files.mjsshape). That is what retires the drift: an entry that no longer matches the workspace now fails loudly rather than leaving both gates walking the old set.Reverse verification
Every leg restores via
trapand confirms the mutation on disk by anchor-text count (an editor's exit code is not evidence).examples/*fromWORKSPACE_PARENT_GLOBS['packages/*','apps/*']in the enumeratorThe third ablation initially passed — and that is why it is in the diff. The zero-literal guard stripped block comments with a hand-rolled
/\*...\*/regex, and a workspace glob contains a comment opener: the/and*ofpackages/*are/*, so the stripper opened a comment at the literal it was hunting and ate forward to the next*/. The planted literal really did contribute 5154 pairs per importer while the guard read green. Fixed to usemaskCommentsfromjs-comment-mask.mjs(commit 2 of 3).Verification
Full
pnpm lint(eslint . --no-inline-config, whole repo, no narrowing): green in 55s.Gate union at
38a1d66c9, all exit 0:check:agent-test-spelling,check:cross-package-test-inputs,check:entry-guard,check:parse-guard,check:pnpm-filter-targets,check:published-files,check:release-body,check:test-source-alias,check:type-check-coverage,check:type-source-resolution,check:override-consistency,check:nul-bytes,check:pm-dispatch-gates(579 cases),check-changeset-fixed.mjs,check-cross-package-test-inputs.mjs,check-osv-exemptions.mjs,check-dev-prereqs --self-test,ablation-dist-preflight --self-test.Two gates could not be closed locally and are declared narrowings, both unbuilt-tree refusals in a worktree with no
dist/:check:published-readme-exports— output byte-identical to the pre-change capture at006c181a8(same 198 unbuilt packages, same lines). lint.yml builds at line 3592 before this step.check:type-check-debt— refuses--re-measurewithout a built closure by design. lint.yml builds at line 3393 before it. Its coverage half printed byte-identical output before and after (65/78 type-checked, 13 DEBT, 19 hiding tests) — that output is the enumeration readout.The shared module is a plain module: no
check:*script names it, no workflow invokes it, so it is not a discovered gate file. It has no CI invocation of its own, so itsselfTest()is folded into the--self-testof every consolidated gate that has one — five of them, each independently red in the third ablation.skip-changeset: scripts-only, nothing published changes.Generated by Claude Code
Generated by Claude Code