test(dogfood): give the declared-unresolvable CONTROL a subject the workspace can never supply - #16907
Conversation
…6539) WIP: fixture-only subject + premise legs. Verification pending. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
…ganizations-control-hermetic
The landing that killed the old premise has a decision record; name it where the premise is restated, per Prime Directive #13. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
📓 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): |
CI red on
|
Accepted — CI is fully green on
|
| raw check-run rows | 38 |
| names carrying a superseded earlier row | 4 (Check Changeset, Auto Label, Check PR Size, Packed-tarball smoke (opt-in)) |
| distinct names after collapse | 34 |
| success | 28 |
| skipped | 6 |
| failure / cancelled / timed_out | 0 |
| queued / in_progress | 0 |
⭐ The collapse is load-bearing on this PR specifically and is stated rather than assumed: the raw list still carries the superseded rows, and reading it uncollapsed is how a settled name gets counted twice. Here it does not change the verdict — under either direction of the collapse the failure count is 0, because every duplicated name's other row is a skipped, never a red.
Lint & Repo Gates was the last one outstanding; it completed success at 16:13:22Z, 22m44s after it started. Nothing was pending after that.
The upload-artifact failure is gone, and I am not calling it fixed
The two shards that died earlier both emitted this repo's own attestation that the suite "ran to completion with every step green" and then failed inside actions/upload-artifact's FinalizeArtifact on a 403 from an intermediary while uploading a 314-byte receipt. The re-run — the one re-run this PR was entitled to, and it is now spent — came back success on both.
FinalizeArtifact 403 shows up on another PR, it should be filed on its own, with these two runs as prior instances.
Landing precheck
- Limb ② — clause-② carriers.
node scripts/pm/check-clause2-carriers.mjs --pair 16907, exit 0: "the clause-② declaration is readable in the fixed spelling and both carriers agree, and its diff carries no widening tell."⚠️ Quoting the checker's own caveat rather than dropping it: "A tell is not a proof and its absence is not one either." The declaration isno, and it is the seat's judgement from the delivered diff, not the checker's. - The open question is ruled. Answered A in
5588056623; nothing on this PR is waiting on a decision. - Governed surfaces: none in the diff —
Governed Surface Queue Guardis green, and this is apackages/qa/dogfoodtest change. - Single-writer paths:
No other open PR may claim the same single-writer pathandNo other open PR may claim the same issueboth green.
Arming, in the order that survives
Marking ready for review first, then enabling auto-merge — a draft conversion after arming destroys both, so the order is not cosmetic. merge_method: merge whatever is requested (allow_merge_commit is false on this repo); that field is inert — the merge queue squashes. Queue membership will be read from the PR timeline (added_to_merge_queue), not from the arming call's return, and enqueue can lag arming by around 90s.
⛔ I neither approve nor merge this PR. Arming hands it to the queue; the queue and the repo's required checks decide.
Generated by Claude Code
Fixes #16539
The cases in
packages/qa/dogfood/test/enterprise-organizations.test.tsbuild temp host roots on disk and askprobeOrganizationsfor a verdict on each. Every one of those verdicts is a statement about what a host root HAS and — just as load-bearing — what it has NOT got. Until ADR-0132 / #16215 the second half came free:@objectstack/organizationswas cloud-private, so a temp host that DECLARED it and did not install it was unresolvable by construction.That landing made it a workspace package. The visible symptom is a false red on any tree that has run a full build. The half that matters is the quiet one, and it is the grading basis triage adopted:
The mechanism, corrected
The card attributes the flip to the
fallbackImportthatprobeOrganizationshandscreateHostImporter(a bare dynamicimport()). Measured here, that is not the path: a host root that DECLARES the package never reaches the fallback arm at all —createHostImportertakes the declared branch and askscreateHostRequire(hostRoot).resolve(pkg). That is a CJS resolve, and CJS consultsNODE_PATH, which vitest's own pnpm bin shim exports atnode_modules/.pnpm/node_modules:Same repo, same fixture directory, two build states — probed with that same
NODE_PATH:Two verdicts about a fixture directory that never changed. Note what TREE A's throw actually says: it names the entry path, not the specifier. The package was reachable the whole time and merely unbuilt — which is exactly why the control looked green while the property went unguarded. Node's ESM resolver never reads
NODE_PATH, which is why thefallbackImportarm was never the carrier.The repair — reusing the shape #16723 established
PR #16723 repaired the sibling pin (issue #16552) under the same landing by moving its example onto a
@fixture/*name the workspace can never contain and proving both legs instead of assuming them. This file reuses that scope and that reasoning rather than inventing a second shape.HOST_ONLY = '@fixture/enterprise-organizations'— modelled on the real enterprise plugin in SHAPE (its export is still theOrganizationsPluginclassserve/bootStackconstruct), fixture-only in NAME. Every fixture host and every verdict case is now built around it, so "declared and NOT installed" is a property of the temp directory again.probeOrganizationsgains an optional third parameter,pkg, defaulting toORGANIZATIONS_PKG. Production callers pass nothing and get byte-identical behaviour; the harness is what changed.@objectstack/dogfooddoes not DECLARE the package and the undeclared arm resolves through this module's ESM base — and that reason is now a test case, not a sentence.Four new cases carry the premises the other nine rest on:
Leg 2 asserts on the bare specifier (
Cannot find module '@fixture/enterprise-organizations'), never on/Cannot find module/alone — the distinction #16723 drew, and the one that separates "absent" from "reachable but unbuilt".Acceptance — the control on BOTH trees
Triage's fixed acceptance criterion, verbatim:
Both runs at
a988f564, one after the other in a single locked run, with the resolution state probed in between so the two trees are demonstrably different:Same verdict, both trees.⚠️ A green run on a CI-shaped graph is not evidence here — CI never builds the package (
turbo run build --filter='@objectstack/dogfood^...' --dry=json: 66 tasks, zero organizations tasks, independently reproducing the card's reading on thetestgraph), so TREE B is the state CI cannot produce.The repair's ablation
Whole repair ablated out of the working tree on TREE B, run, restored. The restore leg is a trap with absolute paths; the mutation is proved on disk before the run, not inferred from an editor's exit code.
The card's exact signature, at the card's exact case.⚠️ One reading worth recording: at vitest's default 5s timeout the same ablation fails as
Test timed out in 5000msinstead, because the un-repaired probe now really loads@objectstack/organizationsand its dependency chain (15.7s here). The card's tree caught it at 5010ms, right on the boundary. Both are the same defect; the run above uses--testTimeout=30000so the assertion itself lands.⛔ The filer's own ablation — that the failure was not caused by the branch it was found on — is not redone here. Triage accepted it and declined to re-run it.
Verification
Everything below at
a988f564, in a worktree whosegit status --porcelainis empty.turbo run build --filter='@objectstack/dogfood^...' --concurrency=2→64 successful, 64 total.pnpm --filter @objectstack/dogfood typecheckexit 0. Coverage proved rather than assumed:tsc --noEmit --listFilesnames both edited files in the program.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(49 runnable commands, derived from the tool's own change set, not a hand-written list), all 49 run and reconciled against--ran. 48 exit 0. The one non-zero ispnpm check:dual-build-cjs-loads→ exit 3,PREREQUISITE NOT MET: it reads built output and seven packages outside this diff's closure have nodist/. Recorded as NOT MEASURED, not as a red; CI builds everything.eslint.config.mjsitself: one flat config over**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}minusNEVER_LINTED(node_modules,dist,build,.next,.turbo). ② Count read from--format json: 2 files, 0 errors, 0 warnings, exit 0. ③ Immutability, in the config's own words: this repo "runs oneeslint.config.mjs, which never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file, test or not" — so no untouched file's verdict can move on this diff, and the diff adds no file and changes no lint config.node scripts/docs-audit/affected-docs.mjs --jsonon a clean worktree reports its own"dirty": false,head fc61b25a…/diffBase 625b0c32…, 0 affected docs,releaseOwnedDocs: []. The zero has its control beside it:testFilesSkipped: 2— the tool saw both files and excluded them because tests cannot make an implementation doc stale. No release-owned page is implicated and none was edited.check:nul-bytesgreen, plus a direct scan of both files for the wider control-character class.Test tier. No new file, so no tier membership was adopted: both edited files stay where they already were. The dogfood suite splits into
shared-showcaseandisolated, and every run above reports this file as|isolated|— it writes temp host roots and toggles resolution, so it is not eligible for the shared-boot project.Changeset. None, and
skip-changesetis the disposition — measured, not assumed.@objectstack/dogfoodisprivate: truewith nofiles[]and nobuildscript, and across the 70 non-private workspace manifests (positive control:@objectstack/organizationsis in that set) zero declare afiles[]entry reachingpackages/qa/dogfood. Nothing published moves.Clause-②: no
Re-declared from the delivered diff, not inherited: the only signature change is an optional third parameter defaulting to
ORGANIZATIONS_PKG. Every existing call site passes zero or two arguments — the module-levelawait probeOrganizations()in this file, reachingattachments-permission-matrix.dogfood.test.tsandrls-multitenant.dogfood.test.tsthroughorganizationsAvailable— so what the probe reports to its callers is byte-identical. What changed is what the harness resolves. (TheprobeOrganizationsinpackages/objectqlis an unrelated symbol, untouched.)验收备注
noted, not filed: the card's stated mechanism (thefallbackImportresolving) is not the carrier of the flip;createHostRequire's CJS lookup through the pnpm shim'sNODE_PATHis. Corrected in the code comments and above rather than filed — the card's evidence, verdict and acceptance criterion are all unaffected, and no other PR or reader reaches this through a separate route. Carrier: this PR.noted, not filed:packages/qa/dogfood/tsconfig.jsonalready carries apathsentry for@objectstack/organizationsadded by Bring single-database multi-organization isolation back to the open core — the wall is already open, only theorg-scopingswitch is closed (~1350 of 1660 lines move; the licence gate stays) #16130 for the same landing — a third file adjusted by ADR-0132's arrival, already handled, listed here only so the set is visible in one place.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
Generated by Claude Code