Skip to content

refactor(cli): spell the multi-org runtime from ONE declaration, with the static keeping its name (#12579) - #12763

Merged
os-litant merged 1 commit into
mainfrom
claude/issue-12579-organizations-pkg-single-source
Aug 27, 2026
Merged

refactor(cli): spell the multi-org runtime from ONE declaration, with the static keeping its name (#12579)#12763
os-litant merged 1 commit into
mainfrom
claude/issue-12579-organizations-pkg-single-source

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12579

Fork (b) only — the half the maintainer ruled on (2026-08-27, Option A). Fork (a), the note repair, landed as PR #12636 and is not redone here.

Serve.ORGANIZATIONS_RUNTIME_PKG is now assigned from the shared const in packages/cli/src/utils/tenancy-posture-hints.ts instead of spelling @objectstack/organizations a second time. One declaration, one spelling.

The four things that had to move together

  1. The static keeps its NAME; only the spelling moves.serve.ts imports the shared const under a renamed binding and assigns the static from it.
  2. Site 8 retires in the same diff. It asserted the two declarations were EQUAL; with one declaration it has no subject. ⛔ It was not deleted one diff earlier — while two declarations existed the pin had a job, which is why serve-cluster-host-resolution.test.ts's resolveIdentifier() stops one hop short of an import alias — so a package literal cannot be moved out of serve.ts without silently emptying the sweep #12533 left it alone.
  3. The four docblocks fork (a) repaired now state the decision that was taken instead of pointing at an open question: serve.ts, utils/tenancy-posture-hints.ts, serve-organizations-message-spelling.test.ts, doctor-organizations-message-spelling.test.ts.
  4. The prohibition that does not move is untouched. The ⛔ in the shared hints module rests on doctor.ts repeats serve's isolated posture sentence verbatim with its own bare @objectstack/organizations literal — and single-sourcing it is a coupling decision, not a substitution #12464's coupling ruling — os doctor must never depend on a serve command's export to spell a package name — and is kept byte-identical: that paragraph occurs once in the merge base and once here, unmodified. Its connective "instead" still has a referent, because the paragraph above it was repaired in place rather than deleted.

A fifth file, named because it is not a drive-by

serve-cluster-host-resolution.test.ts carried "NOTHING IN THE TREE USES THIS HOP YET" and gave exactly that as the reason its fixture package exists. This change makes both sentences false, so repairing them belongs in this diff: leaving them is the same defect fork (a) just fixed, running the other way. Its executable content is untouched — transpiled with removeComments, the emitted program is byte-identical to the merge base (56086b00415d946f both sides), and the instrument is calibrated in both directions (one code token moves the hash; one comment token does not).

That file is also where the ruling's accepted cost is written down once, where whoever narrows the hop will be standing: narrowing this hop is now a LIVE regression rather than a synthetic one.

The two criteria, falsified separately

The NAME did not move. Every non-comment read of the static, merge base vs this branch: 5 in serve.ts, 4 in test/serve-capability-vocabulary.test.ts, const PKG = Serve.ORGANIZATIONS_RUNTIME_PKG in the spelling test, and the sweep's fixture strings — all unchanged. The only code lines that moved are the declaration's right-hand side, the import, and site 8's own four lines.

The VALUE did not change — measured, not reasoned. A probe rendered every operator-facing string that names the runtime (both install-remedy branches, the ADR-0093 D5 fatal refusal, the degraded-boot warning, the stage-2 mount refusal, the posture fix-list, plus the static itself), on the pre-change tree and on this branch, in the same container and the same way:

BYTE-IDENTICAL (5257 bytes) sha256 before=d545248b91e52d05 after=d545248b91e52d05
control: before vs the ABLATED render -> DIFFER (the comparator is live)

The pre-change tree for that run was materialised with git restore --source of the merge base (worktree only, index untouched), so both renders came out of the same build.

Evidence that the hop's live consumer is real

Ruling 4 asked for evidence rather than an assertion. One ablation answers both halves: change ONLY the shared const's value and re-read the sweep and the renders.

ablation: mutate the SHARED const's valuepre-change treethis branch
sweep, serve.ts-derived named listgreenRED — "the sweep no longer sees the @objectstack/organizations load"
sweep, serve.ts-derived host-anchoring pingreenRED — "no load site found for @objectstack/organizations"
sweep, synthetic REAL-sibling-module caseREDRED
rendered operator strings that movedpostureRefusal onlyall six

Read together: before this change serve.ts did not consume the shared module at all (its own literal answered both the sweep and the messages) — that is the reproduction of "two live declarations", taken from the sweep's own resolution rather than from a grep count. After it, the sweep resolves serve's real organizations load through the real sibling module, and every operator-facing string is rendered from it.

Ablation hygiene: the mutation was confirmed on disk before any verdict was read (anchored counts injected=1, removed-form=0, plus the blob hash differing from the HEAD blob); restore is git checkout HEAD -- with absolute paths under trap … EXIT INT TERM; and the restore is proven by an empty git diff HEAD plus blob hashes equal to the HEAD blobs, never by an exit code.

Tests and gates — all on 3e08c4866, the final commit

  • pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 over the sweep, both spelling suites and the roster-vocabulary suite: Test Files 4 passed (4) / Tests 63 passed (63). 64 before, 63 after: exactly site 8.
  • pnpm --filter @objectstack/cli typecheck (tsc --noEmit) exit 0, and not vacuoustsc --noEmit --listFiles lists all five edited files, 1 hit each out of 1284, so the green really covers the three edited .test.ts files under src/commands/.
  • Dependency closure built first (pnpm --filter '@objectstack/cli^...' build), then packages/cli itself.
  • Gate family from node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (16 path-matched + the test-file convention kind + the 8 the changeset brings), exit codes captured before any pipe, each quoted from the gate's own verdict line — all exit 0: check:nul-bytes ("OK (scanned 7092 text file(s) … no raw ASCII control bytes)"), check:cross-package-test-inputs, check:objectql-double-limit, check:page-declaration-shape, check:published-files, check:route-envelope, check:slot-lookup ("ratchet holds: 107 unswept site(s) … none new"), check:test-source-alias, check:type-source-resolution, check:query-options-erasure ("ratchet holds … none new"), check:type-check-coverage, check:engine-double-contract ("OK — 691 pinned"), check:where-matcher ("308 matcher(s) … correctly or refuse it loudly"), check:changeset-gate-self-tests, check:objectui-changeset, check:pm-half-states, check:i18n, check-ci-filter-parity, check-comment-mask-adoption, check-cross-package-test-inputs, check-plugin-teardown-shape, check-adr-0087-registration, check-changeset-no-major, check-empty-changeset, release-rehearsal-clone --self-test, docs-audit/check-affected-docs.
  • pnpm lint (eslint . --no-inline-config) over the whole repo, exit 0 captured before any pipe. No narrowing is claimed for lint.
  • NOT MEASURED, declared rather than counted as green:check:i18n-coverage prints "COULD NOT MEASURE — 1 of 12 config(s) failed to lint" because @objectstack/connector-mcp has no build output in this worktree (it needs the full workspace built, which CI does); scripts/pm/check-half-states.mjs exits 3 ("Nothing was swept … it is no reading at all") for want of a credential this seat does not have; check:type-check-debt --re-measure needs the whole workspace closure built and was not run.

A patch changeset for @objectstack/cli is included. skip-changeset would have been wrong here: unlike fork (a) this diff does change the emitted program, and the two sibling refactors in this same family (#12464, #12151) each shipped a patch changeset while stating "no behaviour change" — the same shape as this one.


Generated by Claude Code

…ing the static's name
`Serve.ORGANIZATIONS_RUNTIME_PKG` is assigned from the shared
`utils/tenancy-posture-hints.ts` const instead of spelling the package a second
time. The static keeps its NAME — the roster pin, the host-anchoring sweep and
the rendered-message pins all address it — so only the spelling moved.
The host-anchoring sweep's import-alias hop resolves the load through that
assignment, which gives the hop its first live consumer; the equality pin that
held the two declarations together retires with its subject, and the four
docblocks that pointed at an undecided question now state the decision taken.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjujZN219uFzBhSYfMykCd
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 2 documentable anchor(s). ⚠️1 changed file(s) yielded no anchor (packages/cli/src/utils/tenancy-posture-hints.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

17 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 5700d83b3c8b1c9396de96f87611c6f128f26528.

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

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/cli/src/utils/tenancy-posture-hints.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • 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 5700d83b3c8b1c9396de96f87611c6f128f26528packageMentionDocs.

Which tree this was computed on

This run read content/docs from 2c536ac44ad9b03fca66d6dcb84f4251846e0efa — the merge of head 3e08c48661f5d73ba2be85070277985317e18187 into base 5700d83b3c8b1c9396de96f87611c6f128f26528, 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 2c536ac44ad9b03fca66d6dcb84f4251846e0efa && git checkout 2c536ac44ad9b03fca66d6dcb84f4251846e0efa
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5700d83b3c8b1c9396de96f87611c6f128f26528 3e08c48661f5d73ba2be85070277985317e18187 && git checkout -B drift-repro 5700d83b3c8b1c9396de96f87611c6f128f26528 && git merge --no-ff 3e08c48661f5d73ba2be85070277985317e18187
node scripts/docs-audit/affected-docs.mjs --json 5700d83b3c8b1c9396de96f87611c6f128f26528

⚠️ 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 5700d83b3c8b1c9396de96f87611c6f128f26528 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-litant
os-litant added this pull request to the merge queueAug 27, 2026
Merged via the queue into main with commit 07fd1acAug 27, 2026
33 of 34 checks passed
@os-litant
os-litant deleted the claude/issue-12579-organizations-pkg-single-source branch August 27, 2026 17:47
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

2 participants

@os-litant@claude