Uh oh!
There was an error while loading. Please reload this page.
chore(cli): pin the multi-org runtime name os doctor prints against the spec roster - #12496
Conversation
`doctor.ts`'s `TENANCY_POSTURE_FIX_HINTS` repeated serve's `isolated` posture sentence verbatim with its own bare `@objectstack/organizations` literal, under no check at all — so a roster-key rename would leave `os doctor` naming a package boot no longer resolves, with every gate green. The hint now interpolates a module-level `ORGANIZATIONS_RUNTIME_PKG`, pinned on two legs: the RENDERED bullet compared whitespace-included against text built from that declaration, and a roster leg asserting the declaration is a key of the spec-owned `PLATFORM_PLUGIN_WIRED_RUNTIMES`. The roster leg is the load-bearing one — without it the hint and its expectation move together under a rename and nothing goes red. This does not single-source the spelling and the const's docblock says so at the site: the literal is still declared three times, the roster cannot supply the name (keyed BY package name, no `package` field by design, "not a resolution registry"), and importing serve's export into a diagnostic command would be a worse coupling. What changes is that this copy can no longer drift in silence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjujZN219uFzBhSYfMykCd
📓 Docs Drift CheckThis PR changes 1 package(s): 5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 9bcc34487e683c0fbc2f16e6522b1f366a3cfb41 && git checkout 9bcc34487e683c0fbc2f16e6522b1f366a3cfb41
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ce6d4e9abfeac3298f06dcc8c42003289db26c22 f7c0212066640270f9546d2ecabacb48d5656c4d && git checkout -B drift-repro ce6d4e9abfeac3298f06dcc8c42003289db26c22 && git merge --no-ff f7c0212066640270f9546d2ecabacb48d5656c4d
node scripts/docs-audit/affected-docs.mjs --json ce6d4e9abfeac3298f06dcc8c42003289db26c22
|
The new `ORGANIZATIONS_RUNTIME_PKG` const and its docblock were inserted BETWEEN the `/** One-line descriptions of the accepted postures … */` block and `TENANCY_POSTURE_FIX_HINTS`, the declaration that block documents. Nearest preceding block wins, so the table was left with no docblock at all and a paragraph about posture descriptions introduced a package-name constant. That is precisely the defect class this branch exists to close — a comment describing something other than what it sits on, invisible to every gate — so it does not get to ship inside the fix for it. Pure relocation: the const and its docblock now sit immediately after the Tenancy Posture banner, ahead of the posture block, which is adjacent to its table again. No wording changed on either block, and the file's sorted line multiset is identical to the previous commit's. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjujZN219uFzBhSYfMykCd
Uh oh!
There was an error while loading. Please reload this page.
Fixes#12464
Implements the PM's ruling B (issue comment 5421042263), taken after the card's own named premise was measured false: the spec roster cannot supply the package name, so "read it from the roster" was not available.
This does not single-source the spelling, and the card's title is not achieved. The literal is still declared three times — the
PLATFORM_PLUGIN_WIRED_RUNTIMESroster key,Serve.ORGANIZATIONS_RUNTIME_PKG, and nowdoctor.ts's own const. Saying otherwise would misrepresent the change.The defect being closed is the silent every-gate-green drift, not the duplication as such — which is what the card's own Class section describes: a roster-key rename would leave
os doctornaming a package that boot no longer resolves, with every gate green, because the roster pin only ever sees the declaration and nothing read this hint table's text. A duplicate that can drift silently and one that cannot are different things; this makes it the second kind.Why the name is not read from the roster
Measured on
packages/spec/src/kernel/platform-capabilities.ts, and written into the const's docblock so the next reader inherits the measurement rather than the argument:PlatformPluginWiredRuntimehas exactly two fields,editionandnote. There is nopackagefield, and its docblock records the omission as deliberate: "here the package name is the KEY, so it cannot benulland cannot drift from a duplicate field".string, not a literal union, soObject.keys()yields no typed handle.edition: 'enterprise'— nothing machine-readable selects this one.The roster validates a name you already hold; it does not hand you one. Importing
Serve.ORGANIZATIONS_RUNTIME_PKGinto a diagnostic command was ruled out separately: a diagnostic command depending on aservecommand's export in order to spell a package name is a worse coupling than the duplication it removes.The two legs
isolatedbullet is rendered through the real gate (resolveTenancyPostureOrFinding) and compared withtoBe, whitespace included, against text built from the declaration. Per PR fix(cli): spell the multi-org runtime from its one declaration in serve's operator-facing prose #12463's shape: it asserts what the operator sees, never that a constant appears in source. A source scan is deliberately not built, for the reason fix(cli): spell the multi-org runtime from its one declaration in serve's operator-facing prose #12463 recorded — it would have to exclude comments, and the const's docblock names the package repeatedly.PLATFORM_PLUGIN_WIRED_RUNTIMES.⭐ (ii) is the load-bearing leg. Leg (i) alone pins doctor against itself: under a roster rename the hint and its expectation move together, so it stays green forever. That is not an argument — it is measured below.
Reverse verification (directions declared before running)
Two mutations, each confirmed on disk before the run and restored with
git checkout HEAD --naming an absolute path, under a trap, with restoration proven by blob-hash comparison (git diff HEADempty, final blob == HEAD blobdceaad4cc):M1 is the decisive one: leg (i) stayed green exactly as predicted, and only leg (ii) caught it. Without leg (ii) this file would be decorative against the very rename the card is about. M2 shows leg (i) catches an inline re-spelling that leg (ii) cannot see. Neither leg is redundant.
No rebuild is involved in either leg: the mutation target is same-package source reached by a relative specifier (
./doctor.js), and the roster is read from@objectstack/spec/kernel, which is untouched.Verification
All gates below ran on
841494f3f, the final commit. Exit codes were captured before any pipe; each result is the gate's own printed verdict line.pnpm --filter @objectstack/cli exec vitest run(4 files: the new pin,doctor-tenancy-posture-report,doctor-env-provenance,serve-capability-vocabulary)Test Files 4 passed (4)·Tests 53 passed (53)pnpm --filter @objectstack/cli typecheckEXIT=0pnpm check:nul-bytesOK (scanned 6906 text file(s) … no raw ASCII control bytes)pnpm check:test-source-aliasOK — 72 packages with tests scannedpnpm check:cross-package-test-inputsOK: 20 package(s) read outside themselves, all declaredpnpm check:changeset-gate-self-testsEXIT=0node scripts/check-empty-changeset.mjs✓ No empty-frontmatter changeset (1 declaring changeset added)node scripts/check-changeset-no-major.mjs✓ This diff introduces no major bumppnpm check:type-source-resolutionOK — 93 tsc program(s) across 77 packagespnpm check:published-files✓ 69 publishable package(s) …Gate families were derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackon the merged tree (no staleness warning), which reported the change set as exactly the three files in this PR.Not a vacuous typecheck:
tsc --noEmit --listFilesconfirms both edited files are in the CLI's typecheck program (the new test file anddoctor.tseach present), so the green covers this diff rather than excluding it.ESLint — narrowed, and the narrowing is measured: repo-wide
pnpm lintis CI's run. Locally the two changed TS files linted clean undereslint --no-inline-config(2 files, 0 errors, 0 warnings, count read from--format json; the population read from eslint's own--print-config, not guessed). The narrowing excludes nothing because this repo runs oneeslint.config.mjswhich, in its own words, "never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file, test or not" — so this diff cannot move any untouched file's verdict.No behaviour change. The declared value is byte-identical to the literal it replaces; the rendered bullet is unchanged.
Deletion condition
The const carries its own expiry. The whole
TENANCY_POSTURE_FIX_HINTStable is duplicated betweendoctor.tsandserve.ts—singleandgroupbyte-identical too, and those two touch no roster, so nothing could ever notice them drift. A shared hint table would single-source all three sentences and the package name at once; that work is tracked at #12492, which stays open and is not addressed here. This PR is a step toward it, not the end state.serve.tswas not touched — it is off-limits under the card's file surface.Generated by Claude Code