Skip to content

fix(cli): spell the multi-org runtime from its one declaration in serve's operator-facing prose - #12463

Merged
os-litant merged 2 commits into
mainfrom
claude/issue-12151-organizations-pkg-prose
Aug 26, 2026
Merged

fix(cli): spell the multi-org runtime from its one declaration in serve's operator-facing prose#12463
os-litant merged 2 commits into
mainfrom
claude/issue-12151-organizations-pkg-prose

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12151

serve prints the multi-org runtime package name at operators in five messages. #11614 (PR #12149) single-sourced the name serveresolves onto Serve.ORGANIZATIONS_RUNTIME_PKG and pinned that declaration against the spec-owned PLATFORM_PLUGIN_WIRED_RUNTIMES roster, 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/main at 8515954 — the card's split is a clue, and it moved in both directions

The 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.ts carried the literal on 11 lines:

linekind
336comment — host-anchored import doc
503comment — inside the declaration's own JSDoc
519the declaration
2787comment — the OrganizationsPlugin pairing note
2809comment — the two-stage note
2865message — install remedy, declared-unresolvable branch
2870message — install remedy, undeclared branch
2881message — stage-1 FATAL (ADR-0093 D5)
2899message — degraded-boot warning
2929message — stage-2 mount FATAL
4317message — the isolated posture description

That 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:

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_PKG returns 2, a token known absent returns 0. A repo-wide search for any specifier extending the prefix (@objectstack/organizations followed by another name character) returns nothing, so the bare-prefix grep cannot over-count here.

The premise holds

  • Serve.ORGANIZATIONS_RUNTIME_PKG is declared as '@objectstack/organizations' — byte-identical to every literal it replaces, so the substitution is a provable no-op today.
  • [finding] serve.ts 的 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), off const organizationsPkg = Serve.ORGANIZATIONS_RUNTIME_PKG).
  • The roster pin covers the declaration onlyserve-capability-vocabulary.test.ts asserts the declared value is a PLATFORM_PLUGIN_WIRED_RUNTIMES key 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.tsformatOrganizationsInstallRemedy, formatOrganizationsAbsentFatal, formatDegradedTenancyWarning, formatOrganizationsMountFatal — each interpolating the constant. The isolated posture 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 resolveTenancyPostureOrRefusal and resolveAuthBaseUrl. chalk and the process.exit stay 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.ts renders each of the five messages and compares the affected line, whitespace included, against text built from Serve.ORGANIZATIONS_RUNTIME_PKG. It asserts what the operator sees, never that the constant appears in the source. Site 6 is rendered through the real resolveTenancyPostureOrRefusal gate 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's serve.ts — no hand transcription — across every branch: both remedy kinds plus the undefined kind, both walled postures, and mountCode present / 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 the HEAD blob and git diff HEAD is empty. No rebuild leg was needed or claimed: the subject resolves as a sibling source module under vitest, not through a package exports entry into dist/, which the red result confirms empirically.

Tests (all at 78b79c5):

  • packages/cli unit 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, and tsc --listFiles confirms 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/objectstack over the actual changed files, then run as a union at 78b79c5. 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 full packages/* 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.ts carries its own TENANCY_POSTURE_FIX_HINTS whose isolated value 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.ts does not read Serve.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

@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation tests tooling labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 7 documentable anchor(s).

16 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json bb920ee089655066b6ac089779d2793d1b6d094f.

4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json bb920ee089655066b6ac089779d2793d1b6d094fpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 541f08fe323b920a7dddfa5913250936c45d4dc7 — the merge of head 78b79c54e5ea5882034db8153ab8b566056868ab into base bb920ee089655066b6ac089779d2793d1b6d094f, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs bb920ee089655066b6ac089779d2793d1b6d094f → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-litant
os-litant marked this pull request as ready for review August 26, 2026 04:51
@os-litant
os-litant added this pull request to the merge queueAug 26, 2026
Merged via the queue into main with commit 27b6902Aug 26, 2026
34 checks passed
@os-litant
os-litant deleted the claude/issue-12151-organizations-pkg-prose branch August 26, 2026 05:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

serve.ts still spells @objectstack/organizations in operator-facing prose, outside the roster pin

2 participants

@os-litant@claude