Uh oh!
There was an error while loading. Please reload this page.
fix(cli): spell the multi-org runtime from its one declaration in serve's operator-facing prose - #12463
Conversation
📓 Docs Drift CheckThis PR changes 1 package(s): 16 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. 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 541f08fe323b920a7dddfa5913250936c45d4dc7 && git checkout 541f08fe323b920a7dddfa5913250936c45d4dc7
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin bb920ee089655066b6ac089779d2793d1b6d094f 78b79c54e5ea5882034db8153ab8b566056868ab && git checkout -B drift-repro bb920ee089655066b6ac089779d2793d1b6d094f && git merge --no-ff 78b79c54e5ea5882034db8153ab8b566056868ab
node scripts/docs-audit/affected-docs.mjs --json bb920ee089655066b6ac089779d2793d1b6d094f
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#12151
serveprints the multi-org runtime package name at operators in five messages. #11614 (PR #12149) single-sourced the nameserveresolves ontoServe.ORGANIZATIONS_RUNTIME_PKGand pinned that declaration against the spec-ownedPLATFORM_PLUGIN_WIRED_RUNTIMESroster, and deliberately stopped there. This is the other half: the five sentences now spell the name from the same declaration, and a new test asserts what they render.Census, re-measured on
origin/mainat8515954— the card's split is a clue, and it moved in both directionsThe card measured "eight further sites, three in comments, five in message text" at
2ba4329. Re-censused on the branch base,packages/cli/src/commands/serve.tscarried the literal on 11 lines:declared-unresolvablebranchisolatedposture descriptionThat is 4 comment lines + 6 message occurrences besides the declaration, not 3 + 5. Both halves differ from the card, in opposite directions, which is why the total still reads close:
@objectstack/organizationsspelling onto one roster-pinned declaration (#11614) #12149 itself added over the new declaration — a comment the card's own run created and did not count as a "further site".All six message occurrences are changed. All four comments stay: the card excludes the "no bare literal outside the declaration" source-scan form precisely because it would have to exclude these, and that shape is easy to get wrong.
The instrument was anchored before the count was believed:
@objectstack/service-cluster(present, not a substring of the term under test) returns 9,ORGANIZATIONS_RUNTIME_PKGreturns 2, a token known absent returns 0. A repo-wide search for any specifier extending the prefix (@objectstack/organizationsfollowed by another name character) returns nothing, so the bare-prefix grep cannot over-count here.The premise holds
Serve.ORGANIZATIONS_RUNTIME_PKGis declared as'@objectstack/organizations'— byte-identical to every literal it replaces, so the substitution is a provable no-op today.organizationsPkg字面量尚未消费 PLATFORM_PLUGIN_WIRED_RUNTIMES 的 key — roster 落地后的一行闭环 #11614's work is still in place: both loading uses read the constant (importFromHost(organizationsPkg),readHostDeclaration(organizationsPkg, hostRoot), offconst organizationsPkg = Serve.ORGANIZATIONS_RUNTIME_PKG).serve-capability-vocabulary.test.tsasserts the declared value is aPLATFORM_PLUGIN_WIRED_RUNTIMESkey and agrees on its edition, and reads no message text. The card was not already partly done.What changed
The message bodies moved into four pure formatters in
serve.ts—formatOrganizationsInstallRemedy,formatOrganizationsAbsentFatal,formatDegradedTenancyWarning,formatOrganizationsMountFatal— each interpolating the constant. Theisolatedposture description interpolates it in place.The seam is what makes the rendering reachable without spawning a boot, and it is the shape this same file already uses and documents for
resolveTenancyPostureOrRefusalandresolveAuthBaseUrl.chalkand theprocess.exitstay at the call site, so the control flow around the fatal path is unchanged and still readable there.packages/cli/src/commands/serve-organizations-message-spelling.test.tsrenders each of the five messages and compares the affected line, whitespace included, against text built fromServe.ORGANIZATIONS_RUNTIME_PKG. It asserts what the operator sees, never that the constant appears in the source. Site 6 is rendered through the realresolveTenancyPostureOrRefusalgate so the bullet's assembly is pinned too. A sweep over all five renderings asserts that every scoped package name an operator reads is the declared one — the thing the excluded source scan was reaching for, done over the rendering, where comments cannot reach and no exclusion list is needed.Evidence
Byte-identity of the rendered output (the card's stated risk). A throwaway vitest file compared each new formatter against the pre-change expressions sliced verbatim out of
origin/main'sserve.ts— no hand transcription — across every branch: both remedy kinds plus the undefined kind, both walled postures, andmountCodepresent / absent / numeric. 7 tests, all passing, including a control asserting the comparison is not vacuously equal. It is deliberately not committed: it hardcodes the pre-rename literal, which is exactly the second unchecked copy this card removes.Reverse verification — the new pin can say no. Predicted direction: red. Branch 2 of the remedy was mutated to a bare, drifted literal (the shape a roster-key rename produces). The mutation was confirmed on disk before the reading (injected text present 1, deleted anchor present 0, blob hash changed), and the run went red with 3 failures: the site-2 line pin, the scoped-name sweep (
remedy (undeclared) names '@objectstack/orgs', which is not the runtime serve resolves), and the control. Restore was trapped, absolute-pathed, and proven — restored blob equals theHEADblob andgit diff HEADis empty. No rebuild leg was needed or claimed: the subject resolves as a sibling source module under vitest, not through a packageexportsentry intodist/, which the red result confirms empirically.Tests (all at
78b79c5):packages/cliunit suite — 91 files, 1062 passed, including the new pin.test/serve-organizations-host-resolution.e2e.test.ts— 3 passed. This one spawns the real CLI and asserts on the actual rendered stderr (FATAL: tenancy posture 'isolated' was requested,to THIS APP,could not be loaded), i.e. the fatal-path text, independently of the new pin.test/serve-organizations-mount-failure.e2e.test.ts,test/serve-boot-diagnostics.e2e.test.ts,test/serve-capability-vocabulary.test.ts— 19 passed.pnpm --filter @objectstack/cli typecheck— clean, andtsc --listFilesconfirms both edited files are inside the program (1 hit each), so the green is measured rather than a config exclusion.Gates, re-derived in the worktree with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackover the actual changed files, then run as a union at78b79c5. All green, each exit code captured before any pipe:check:nul-bytes·check:cross-package-test-inputs·check:engine-double-contract·check:where-matcher·check:test-source-alias·check:type-source-resolution·check:page-declaration-shape·check:slot-lookup·check:published-files·check:route-envelope·check:query-options-erasure·check:type-check-coverage·check:type-check-debt(--re-measure, after a fullpackages/*build) ·check:i18n·check:i18n-coverage·check:objectui-changeset·check:changeset-gate-self-tests·check-ci-filter-parity·check-comment-mask-adoption·check-plugin-teardown-shape·check-empty-changeset·check-changeset-no-major·check-adr-0087-registration·release-rehearsal-clone --self-test·docs-audit/check-affected-docs·docs-audit/check-drift-comment.Repo-wide
pnpm lint(eslint . --no-inline-config) also ran in full and is green — no narrowing was needed.Not in this PR
packages/cli/src/commands/doctor.tscarries its ownTENANCY_POSTURE_FIX_HINTSwhoseisolatedvalue repeats the same sentence with the same bare literal. It is the same defect class but outside this card's declared file surface, and it is not a mechanical fix —doctor.tsdoes not readServe.ORGANIZATIONS_RUNTIME_PKG, so single-sourcing it is a coupling decision, not a substitution. Reported for triage rather than fixed here.Generated by Claude Code