test(types): pin the host-only resolution defect on a name the workspace can never contain - #16723
Merged
os-project-manager merged 1 commit intoSep 8, 2026
Conversation
…ace can never contain The host-only example was `@objectstack/organizations`, a real cloud-private package when the pin was written. Once that package became a workspace member, pnpm's hoisted store carried it and the framework-anchored resolve succeeded on any built tree — so the assertion held in CI only because the shard running it had not built that package. Build order, not the property it asserts. The example is now `@fixture/host-only`, a scope this workspace cannot contain, and the case proves its own premise instead of assuming it: it first resolves the name FROM THE HOST APP (without which a name existing nowhere would satisfy the assertion vacuously), then requires the framework-side failure to name the BARE SPECIFIER. A reachable-but-unbuilt package throws MODULE_NOT_FOUND naming its missing entry file instead, so that throw can no longer satisfy this pin in either build state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
Contributor
📓 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): |
os-project-manager
marked this pull request as ready for review
September 8, 2026 02:24
os-project-manager
enabled auto-merge
September 8, 2026 02:24
os-project-manager
deleted the
claude/issue-16552-node-test-host-only-pin
branch
September 8, 2026 02:50
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 free
to 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 #16552
One file:
packages/types/src/node.test.ts. The pin's example changed; what the pin asserts did not.The defect, reproduced
The case
host-app package resolution (cloud#1013, #4700) > the framework package's own resolution cannot see a host-only package — the defectused@objectstack/organizationsas its example of a host-only package. #16215 (c677cda816) made that package a workspace member, so pnpm's hoisted store now carries it and a framework-anchoredrequire.resolvefinds it.Resolution measured directly from
packages/types, with the pnpm bin shim'sNODE_PATHpointed at the hoisted store — the same two tree states, one command apart:Read the unbuilt throw closely: resolution found the package and failed only because its entry file was not on disk. That is what held the pin green in CI — build order, not the property. At test level, on the built tree, before this change:
The repair
The example is now
@fixture/host-only— the fixture-only scope this file already uses for@fixture/hoisted-only,@fixture/throws-on-loadand@fixture/declared-but-missing. Measured absent from the workspace: no trackedpackage.jsondeclares an@fixture/*name, and the scope is absent from bothnode_modules/@fixtureand the hoisted store.The case now proves its own premise rather than assuming it, which is the shape triage preferred, because a test that proves its own premise cannot silently decay into a vacuous assertion:
/Cannot find module/alone.Leg 2's tightening is what removes the build-order dependence. A package the framework can see, whose entry file merely is not on disk, also throws
MODULE_NOT_FOUND— but names the missing entry path, not the specifier. That throw can no longer satisfy this pin, in either build state.The identifier was renamed
ORGANIZATIONStoHOST_ONLYat all six sites: a constant named after a real package is exactly what let the example stop being host-only without anyone noticing. The fixture package still exports the real plugin's shape (OrganizationsPlugin/com.objectstack.organizations), because the callers' cases assert that shape; only the NAME is fixture-only.Same verdict on BOTH tree states (acceptance criterion)
The fixed assertion, unmodified at
HEAD(git diff HEADempty for both runs), run twice — once withpackages/plugins/organizations/distpresent, once with it parked aside. The environment genuinely differs between the two runs, shown by a framework-anchored resolve of the old example taken immediately before each:Tree state 1 — organizations WITH dist
Tree state 2 — organizations WITHOUT dist
Same verdict, both trees.
organizations/distwas restored afterwards and the tree left unmodified. The old pin could not produce this pair: it was green in state 2 and red in state 1, which is the whole card.The ablation below is the other half of the same square — it shows the reverted example is red in BOTH states too, so the new assertion is build-order-independent in both directions rather than merely green twice.
Ablation — the guard is proved to fail, in both tree states
Run from the committed state, each leg mutated on disk (proof printed), then restored via
git checkout HEAD -- pathwithgit diff HEADempty and the worktree blob equal to the HEAD blob (394683344fbdf8dd985a905882e930a4938e66f9) after every leg.@objectstack/organizationsexpected [Function] to throw an error, 1 failed / 69 passedorganizations/distparked aside)expected [Function] to throw error matching /Cannot find module '@ob…/organizations' but got 'Cannot find module \'/home/user/objec…', 1 failed / 69 passedLeg B is the one that matters: the old pin was green in exactly that state. That green was the card. Leg C shows the positive leg is load-bearing — the assertion cannot be satisfied vacuously by a name that exists nowhere.
Verification
All readings taken on a tree with the
@objectstack/organizationsbuild closure present (28 turbo tasks), at613a2b0ca.pnpm --filter @objectstack/types test—Test Files 19 passed (19),Tests 573 passed (573)pnpm --filter @objectstack/types test:repo—Test Files 1 passed (1),Tests 7 passed (7)pnpm --filter @objectstack/types typecheck— exit 0;tsc --noEmit --listFilesnamessrc/node.test.ts, so this reading really does cover the edited filescripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 45 families, all 45 run, reconciled with--ran— "45 derived famil(ies) accounted for — 45 run, 0 NOT-MEASURED". 43 exit 0. Two exit 3 = PREREQUISITE NOT MET, both because this tree is not fully built, quoting each gate:check:dual-build-cjs-loads— "this gate reads built output, and some package has no dist/";check:type-check-debt— "--re-measure cannot run: 5 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk". Neither is a failure and neither is a pass: NOT MEASURED, left to CI.pnpm lintrun repo-wide, not narrowed: exit 0 over 6337 files, 0 errors, 0 warnings, the edited file present in eslint's own reported population.skip-changeset
Measured, not assumed.
@objectstack/typespublishesfiles: ["dist", "README.md", "CHANGELOG.md"]. The subject symbols (HOST_ONLY,@fixture/host-only) have zero hits acrossdist/andREADME.md; positive controlcreateHostRequirehits 4 files underdist/. A test file moves no published surface, so theskip-changesetlabel is applied rather than a changeset written.Scope
#16539is not addressed here, and this PR was deliberately not widened to it. Read as instructed, and the two cards cannot share one fixture-only scope: in this file the name is a pure fixture stand-in that the test itself writes to disk, whereas inpackages/qa/dogfood/test/enterprise-organizations.tsthe name is the probe's real subject —ORGANIZATIONS_PKGis exported production code,probeOrganizations()runs at module scope for the live dogfood gates, and the control asserts the operator-facing remedy text that names that real package. Renaming it there would delete the thing being probed. That card needs a hermetic fixture root or a parameterised probe subject — a design change in the probe, not a rename.Clause-②: no— re-declared from the delivered diff: one test file, no published surface moves.🤖 Generated with Claude Code
https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
Generated by Claude Code