Uh oh!
There was an error while loading. Please reload this page.
fix(pm): derive the gates that re-derive from a package export surface - #14207
Merged
Merged
Conversation
Six gates re-derive their population from `@objectstack/spec`'s public export surface, and every one was absent from the derivation for `packages/spec/src/index.ts` -- the entry point that IS their subject. One shape, one hole. None of them spells that subject as a path. Each reads the package manifest, walks its `exports` map, and resolves each subpath into a built `dist/` that is untracked by construction. The derivation models three follows and all three end at a PROGRAM whose declared population it can inherit; a manifest is not a program, so the whole class ended at a read that contributed nothing. Adds the fourth follow -- the manifest edge -- routed through the same pieces as the other three: the followed file's own declaration decides what a caller inherits (here the `exports` map), `hintOrigin`/`hintEdge` label it so it never travels as a claim the gate made itself, and it is appended last so it can only fill a hole. A gate must READ the exports map to follow it: three live gates read a manifest for `.version` or the `scripts` map and inherit nothing. Also reads a package root held in a PARAMETER (one call site, one value), consulted only where nothing binds the name -- the branch that returned `unknown` before -- which is the spelling one of the six uses. Measured over 199 families x 7762 files: 166173 -> 172797 pairs (+6624), 0 lost, 0 re-attributed, 6 verdicts changed. A seventh live gate the card never named, `check:dual-build-cjs-loads`, is covered by the same edge, and the self-test pins an eighth that exists nowhere but the case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
baozhoutao
marked this pull request as ready for review
September 1, 2026 12:57
Uh oh!
There was an error while loading. Please reload this page.
baozhoutao
deleted the
claude/issue-13518-spec-export-gate-derivation
branch
September 1, 2026 13:16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#13518
The reading, re-derived on this branch's base (
987fe370, after PR #13862 / #14012 / #14188)The card's premise still held. Its own re-check, run on a fresh tree:
check:api-surfacecheck:export-originscheck:entry-nameabilitycheck:exported-anycheck:dual-source-exportscheck:browser-reachable-entriescheck:strictness-ledger(positive control)33 matched families before, 39 after. The control was present in both runs, which is what makes the six an absence rather than a dead query.
Why the whole CLASS fell out of the derivation
Not six defects. Every one of the six re-derives its population from
@objectstack/spec's public export surface, and none of them spells that surface as a path. Each readspackages/spec/package.json, walks itsexportsmap, and resolves each subpath into a builtdist/*.d.ts. So the population is computed through the manifest, and both places a literal could have carried it fail:dist/is a build output and untracked, so any literal reaching it dies in the reachability sweep by construction;resolve(PKG_DIR, 'src/index.ts')), andextractWatchHintsresolves against exactly one anchor -- the writer's own directory. A literal with no./prefix is taken from the repo root, so'src/index.ts'becomes the root hintsrc/index.tsand reaches nothing.The derivation models three follows -- import, program-text read, exec -- and all three end at a program whose declared population can be inherited. A package manifest is not a program. So the entire class ended at a manifest read that contributed nothing, and each of the six scored
silentwith only its helper-module imports as population.That this is a class and not a story is printed on other gates today:
check:generated's dead leads areapi-surface,export-originsandsrc/meta-spelling/...-- three literals alive underpackages/spec/and dead at the root.The repair: an EDGE, not six names
⛔ No gate name is added to any table. Following the pattern PR #14012 established for exec edges, this adds the fourth follow -- the manifest edge -- routed through the same three pieces and adding none:
declaredInheritedPopulation; for a manifest it is theexportsmap, read from the tree rather than from a regex over the gate's prose;hintOrigin/hintEdgelabel the inherited hint so it never travels as a claim the gate made itself. The via column readsgate source via the export surface declared by packages/spec/package.json;A second, smaller hop was required by one member:
build-export-origins.tsreaches its package root through a function parameter (collectSourceEntries(pkgDir)), where the others use a constant.singleCallSiteParametersreads a parameter bound at exactly one call site -- the same one-hop shapesingleReturnExpressionsalready uses. It is consulted only where nothing binds the name, which is the branch that returnedunknownbefore, so it can add a reading and can never move one.The narrowing that decides the number
A gate must actually read the
exportsmap to follow the edge. Without that test the edge admits every reader of any manifest -- +1678 pairs on this tree, every one fabricated. The three live refusals, each checked at its own declaration site:check:docs-image-tagpackages/cli/package.json->.versioncheck:authorable-surfacepackages/spec/package.json->.versioncheck:generatedpackages/spec/package.json-> thescriptsmapcheck:generatedis under-matched forpackages/spec/srcfor the other reason above (package-root-anchored literals dying at the root). That is a different defect and is filed separately rather than fixed here under a test it does not pass.Does a seventh gate of the class get covered automatically?
Yes, and it is demonstrated two ways rather than argued:
check:dual-build-cjs-loadswalks the sameexportsmap andrequire()s every published entry (metadata-core's CJS entry point is unloadable since #12843 — import.meta emitted verbatim into dist/index.cjs is a parse-time error, so the guarding try/catch never runs #12971). It is picked up by the edge with nothing listing it. It gains no pairs -- its CI job filter ispackages/**and already covered them -- it gains the right provenance, and the self-test pins it by name.Blast radius, measured before and after
Over 199 discovered families x 7762 tracked files, counted through
coveringKey-- the same key the printed block renders from:The six take 1104 pairs each -- the whole of
packages/spec/src, once, which is the defect exactly. No family outside the class moves in either direction.Verification (all at
f2b1d1fa)scripts/pm/dispatch-gates.mjsis a gate script, so its own self-test is owed on top of the derived family:pnpm check:pm-dispatch-gatesnode scripts/pm/bare-root-worklist.mjs --self-testnode scripts/check-ratchet-remedy-authority.mjspnpm check:declared-population-livepnpm check:watch-hint-literalpnpm check:entry-guard/check:parse-guardpnpm check:cross-package-test-inputs+node scripts/check-cross-package-test-inputs.mjsnode scripts/check-ci-filter-parity.mjsnode scripts/check-shard-attestation.mjsnode scripts/check-self-test-wired.mjspnpm check:agent-test-spelling/check:bash32-floor/check:cli-command-ids/check:pnpm-filter-targetspnpm check:nul-bytesnode scripts/check-test-completeness.mjsturbo run testlog; run bare it exits 3 and its own text says to record it as not measuredThe family list was derived from the actual diff with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands(no path arguments -- the script takes its own change set), not quoted from the card.Every exit code above was captured before any pipe. Two
exit=3readings on the first pass (check-ci-filter-parity,check-shard-attestation) were PREREQUISITE NOT MET -- a fresh worktree with nonode_modules-- and are recorded green above only afterpnpm installand a re-run.Both directions pinned
The 15 new self-test cases pin the repair and its refusals, not that a derivation ran:
packages/spec/src/index.ts, and the control still does;exportsread removed contributes nothing;entry.manifestsis exactly what the scan finds over the family's own files -- never a list kept in the file;20 cases in total, counted from the diff. The pre-change baseline was not separately measured, so no before/after case count is claimed -- only that the suite is green at
f2b1d1fawith 0 failures.No changeset
The diff is one file under
scripts/pm/**and publishes nothing from any package, soskip-changesetapplies and the label is set on this PR.Note for review
scripts/pm/dispatch-gates.mjsis the board's single-writer toolchain file. No open PR claimed it when this branch started -- re-checked against all 8 open PRs immediately before the first push.Authoring session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Generated by Claude Code
Generated by Claude Code