diff --git a/.changeset/serve-organizations-pkg-single-sourced.md b/.changeset/serve-organizations-pkg-single-sourced.md new file mode 100644 index 0000000000..1764d46e4f --- /dev/null +++ b/.changeset/serve-organizations-pkg-single-sourced.md @@ -0,0 +1,31 @@ +--- +"@objectstack/cli": patch +--- + +chore(cli): spell the multi-org runtime from ONE declaration, with the static keeping its name (#12579) + +`Serve.ORGANIZATIONS_RUNTIME_PKG` carried a second copy of +`@objectstack/organizations`, beside the declaration in +`utils/tenancy-posture-hints.ts` that `os doctor` reads. That duplication was +deliberate: the host-anchoring sweep in `serve-cluster-host-resolution.test.ts` +resolved serve's organizations `import()` through that static and needed a +LITERAL in that file — written as a re-export, the specifier stopped resolving +and the load dropped OUT of the swept population instead of failing inside it +(#11614's silent-vacuity mode, which #12492 hit and measured). That constraint +died at `1ca763b60` (#12533), which taught the sweep to follow an import alias +into a sibling module of the same package. + +The static is now assigned from the shared const. It keeps its NAME — the roster +pin (`test/serve-capability-vocabulary.test.ts`), the sweep and the +rendered-message pins all address `Serve.ORGANIZATIONS_RUNTIME_PKG`, and only the +spelling moved — and the equality assertion that kept the duplication CHECKED +(site 8 of `serve-organizations-message-spelling.test.ts`) retires with its +subject, in the same change. The gap is still never closed in the other +direction: `os doctor` must not depend on a `serve` export to spell a package +name (#12464's coupling ruling, untouched). + +**No behaviour change, measured rather than argued.** 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 and the +tenancy-posture fix list — was rendered before and after the change and compared: +byte-identical, 5257 bytes, sha256 `d545248b91e52d05…`. diff --git a/packages/cli/src/commands/doctor-organizations-message-spelling.test.ts b/packages/cli/src/commands/doctor-organizations-message-spelling.test.ts index 4555c41ce3..30ceed751f 100644 --- a/packages/cli/src/commands/doctor-organizations-message-spelling.test.ts +++ b/packages/cli/src/commands/doctor-organizations-message-spelling.test.ts @@ -26,19 +26,21 @@ * measure did not change: what `os doctor` RENDERS. Leg (ii) especially, which * is the load-bearing one (see below). * - * ⚠️ The literal is still declared three times, not two: the roster key, the - * shared module this file now reads, and `Serve.ORGANIZATIONS_RUNTIME_PKG` in - * `serve.ts`. ⛔ That third copy is no longer REQUIRED, and this paragraph is - * the fourth place that said it was. It used to read: it must stay a string - * LITERAL or the host-anchoring sweep in `serve-cluster-host-resolution.test.ts` - * can no longer resolve which package that command's `import()` names. ⭐ That - * died at `1ca763b60` (#12533), which taught the sweep to follow an import alias - * into a sibling module; whether to end the duplication is now an open, - * maintainer-facing decision at #12579. ⛔ None of it changes what THIS file - * measures. What holds either way is that no copy can drift in silence: the - * serve↔shared pair is pinned equal by site 8 of - * `serve-organizations-message-spelling.test.ts`, and each copy is separately - * pinned as a roster key. + * ⚠️ The literal is declared twice now, not three times: the roster key, and + * the shared module this file reads. `Serve.ORGANIZATIONS_RUNTIME_PKG` was the + * third copy; since #12579 it is ASSIGNED FROM that shared module instead of + * spelling the package again. ⛔ It had stopped being REQUIRED before that, and + * this paragraph is the fourth place that said it was. It used to read: it must + * stay a string LITERAL or the host-anchoring sweep in + * `serve-cluster-host-resolution.test.ts` can no longer resolve which package + * that command's `import()` names. ⭐ That died at `1ca763b60` (#12533), which + * taught the sweep to follow an import alias into a sibling module; ending the + * duplication was then ruled on by the maintainer (2026-08-27, #12579, Option + * A). ⛔ None of it changes what THIS file measures. What holds throughout is + * that no copy can drift in silence: the equality pin that held serve's literal + * to this module's — site 8 of `serve-organizations-message-spelling.test.ts` — + * retired with its subject, and each surviving declaration keeps the roster-key + * leg that made the pair safe in the first place. * * ── Three legs, and the second is the point ────────────────────────────── * diff --git a/packages/cli/src/commands/serve-cluster-host-resolution.test.ts b/packages/cli/src/commands/serve-cluster-host-resolution.test.ts index 737ec1ab92..2e0149c891 100644 --- a/packages/cli/src/commands/serve-cluster-host-resolution.test.ts +++ b/packages/cli/src/commands/serve-cluster-host-resolution.test.ts @@ -96,8 +96,11 @@ const HERE = dirname(fileURLToPath(import.meta.url)); * lives in (what `./x.js` is relative TO) and the package that file belongs to * (the fence the hop may not cross). Both are parameters rather than constants so * the synthetic sources below can be scanned against a temp package instead of - * this one — the hop has no live site in the tree yet, so a fixture package is - * the only way to exercise it at all. + * this one: the REFUSAL cases need siblings that must never exist inside + * `packages/cli` — a non-literal export, a module outside the package, a + * re-export, a missing file — so a fixture package is the only way to exercise + * them. The hop's live site (#12579) proves what it RESOLVES; it can say nothing + * about what it refuses. */ type ScanContext = { /** Directory of the scanned file — the base a relative specifier resolves against. */ @@ -1145,25 +1148,41 @@ function makeScannedPackage(files: Record): ScanContext { } /** - * The import-alias hop (#12533), proven against synthetic source. + * The import-alias hop (#12533), proven against synthetic source — and, since + * #12579, carrying a LIVE site. * - * ── Why synthetic, and why that is stated here rather than assumed ────────── + * ── The live consumer, and what it costs whoever narrows this hop ─────────── * - * ⚠️ NOTHING IN THE TREE USES THIS HOP YET. That is deliberate: the hop is the - * capability, and its consumer — single-sourcing a package spelling out of - * `serve.ts` — is a follow-up. A capability and the reversal it licenses do not - * land together, because `Serve.ORGANIZATIONS_RUNTIME_PKG` is deliberately a - * duplicated LITERAL today, with the reasoning written at both ends - * (`utils/tenancy-posture-hints.ts` and `serve-organizations-message-spelling.test.ts`), - * and a diff that both adds the safety net and removes the thing it protects - * cannot be reviewed as either. + * ⭐ `serve.ts` declares `static readonly ORGANIZATIONS_RUNTIME_PKG = + * SHARED_ORGANIZATIONS_RUNTIME_PKG;` — an import alias of the const in + * `packages/cli/src/utils/tenancy-posture-hints.ts` — so the + * `@objectstack/organizations` load the sweep above judges is resolved THROUGH + * this hop rather than beside it. Until then the branch was reached only from + * the cases below, which is the state the maintainer ruling of 2026-08-27 + * (#12579, Option A) deliberately ended: a capability nothing live reaches is + * the defect class this lane keeps filing, one level down. * - * ⛔ So do NOT read a resolver branch with no live caller as dead code. It is - * reached from every case below, and deleting it puts the tree back in the state - * where single-sourcing a spelling silently empties the sweep — which is how - * this file lost the `@objectstack/organizations` load once already. + * ⚠️ The cost came with it, and this paragraph is where it is written down. + * NARROWING THIS HOP IS NOW A LIVE REGRESSION, not a synthetic one. Refuse a + * spelling these cases accept and the result is no longer "a fixture case went + * red": `serve.ts`'s organizations load drops out of the judged population and + * the NAMED half of the vacuity guard above fires by package name — the loud + * path, but a whole command's load rather than a fixture. ⛔ So do not narrow + * this hop to make some other case pass. Read what the live site spells first, + * and move both or neither. * - * The first case is the strongest anchor available without a live site: the + * ⛔ And should that site ever change shape again, still do not read the branch + * as dead code: deleting it puts the tree back where single-sourcing a spelling + * silently empties the sweep — which is how this file lost the + * `@objectstack/organizations` load once already. + * + * ── Why the cases stay synthetic ──────────────────────────────────────────── + * + * The live site proves what the hop RESOLVES; it can prove nothing about what + * the hop REFUSES, and the refusals are the fence. Those cases need siblings + * that must never exist in `packages/cli` — a non-literal export, a module + * outside the package, a re-export, a missing file — so they are written against + * a temp package. The first case is the anchor between the two halves: its * source is synthetic, but the sibling it follows is the REAL * `packages/cli/src/utils/tenancy-posture-hints.ts`, read through `serve.ts`'s * own scan context. @@ -1178,10 +1197,10 @@ describe('os serve → the resolver follows ONE import alias into a sibling modu it('resolves an alias to a literal in a REAL sibling module of packages/cli', () => { // No fixture: `SERVE_CONTEXT`, so the sibling read is the live - // `packages/cli/src/utils/tenancy-posture-hints.ts` — the module a - // single-sourcing refactor would actually import from. If that export is - // renamed or stops being a string literal, this goes red HERE, naming the - // hop, instead of the sweep quietly judging one load fewer. + // `packages/cli/src/utils/tenancy-posture-hints.ts` — the module `serve.ts` + // imports the spelling from since #12579. If that export is renamed or stops + // being a string literal, this goes red HERE, naming the hop, instead of the + // sweep quietly judging one load fewer. expect( shape([ "import { ORGANIZATIONS_RUNTIME_PKG } from '../utils/tenancy-posture-hints.js';", @@ -1205,7 +1224,9 @@ describe('os serve → the resolver follows ONE import alias into a sibling modu // The static keeps its NAME (separate pins read it as a roster key); only // the spelling moves to the shared module. This is the exact rewrite that // came back as `the sweep no longer sees the @objectstack/organizations - // load` before this hop existed. + // load` before this hop existed — and, since #12579, the shape `serve.ts` + // itself carries. Kept synthetic anyway: the live site is judged by the + // sweep above, and this case is what names the SHAPE when it moves. const context = makeScannedPackage({ 'pkg/src/utils/spelling.ts': SIBLING }); expect( shape([ diff --git a/packages/cli/src/commands/serve-organizations-message-spelling.test.ts b/packages/cli/src/commands/serve-organizations-message-spelling.test.ts index d63be1fb6b..0371c128ee 100644 --- a/packages/cli/src/commands/serve-organizations-message-spelling.test.ts +++ b/packages/cli/src/commands/serve-organizations-message-spelling.test.ts @@ -48,22 +48,31 @@ * included — the two entries that touch no roster and that, before #12492, * nothing at either command ever read. * - * **Site 8** covers what the shared table could NOT absorb. - * `Serve.ORGANIZATIONS_RUNTIME_PKG` is still a string LITERAL in `serve.ts`, so - * the spelling is declared twice inside `packages/cli`, and site 8 is what keeps - * that duplication CHECKED instead of silent — it asserts the two declarations - * are equal. Each is separately pinned as a roster key, here via - * `test/serve-capability-vocabulary.test.ts` and there via doctor's leg (ii). + * **Site 8 retired WITH its subject (#12579).** It asserted that + * `Serve.ORGANIZATIONS_RUNTIME_PKG` and the shared hints module declared the + * same package — the assertion that kept a deliberate duplication CHECKED + * instead of silent. The maintainer ruling of 2026-08-27 (Option A) ended the + * duplication: the spelling is declared once in + * `../utils/tenancy-posture-hints.ts` and serve's static is assigned from it, so + * an equality pin between two declarations has nothing left to compare. ⛔ It + * was deleted in the diff that removed its subject and not one diff earlier — + * while two declarations existed the pin had a job, which is why #12533 left it + * alone. * - * ⚠️ This paragraph used to say the literal HAD to stay, because + * ⛔ Nothing it measured went with it. Its second assertion — that the + * `isolated` hint an operator reads interpolates that same spelling — is what + * site 6 renders through the real gate and compares hard-coded against `PKG`; + * and both ends of the vanished pair are still pinned as a roster KEY, serve's + * via `test/serve-capability-vocabulary.test.ts` and the shared const's via + * doctor's leg (ii). + * + * ⚠️ This paragraph used to say the literal HAD to stay in `serve.ts`, because * `serve-cluster-host-resolution.test.ts` resolved the organizations `import()` * through that static and needed the literal in that file or the load dropped * out of the host-anchoring sweep silently. ⭐ That reason died at `1ca763b60` - * (#12533): the sweep now follows an import alias into a sibling module. Site 8 - * never rested on it — while there are two declarations the pin has a subject, - * whichever way the open decision at #12579 goes — so ⛔ do not delete site 8 to - * "finish" that decision. It falls with its subject or not at all. The reading - * of what survives the hop is on `Serve.ORGANIZATIONS_RUNTIME_PKG`'s docblock. + * (#12533): the sweep now follows an import alias into a sibling module — and + * serve's static is the live site it follows. The reading of what survives the + * hop is on `Serve.ORGANIZATIONS_RUNTIME_PKG`'s docblock. */ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; @@ -71,11 +80,10 @@ import type { HostDeclaration } from '@objectstack/types/node'; import { TENANCY_POSTURES } from '@objectstack/spec/security'; // The table `doctor` renders too (#12492). Read here so site 7 measures the -// SHARING, not this command talking to itself. -import { - ORGANIZATIONS_RUNTIME_PKG as SHARED_ORGANIZATIONS_RUNTIME_PKG, - TENANCY_POSTURE_FIX_HINTS, -} from '../utils/tenancy-posture-hints.js'; +// SHARING, not this command talking to itself. The spelling itself is no longer +// imported beside it: since #12579 there is one declaration, and `PKG` below +// reaches it through the handle these pins are actually about. +import { TENANCY_POSTURE_FIX_HINTS } from '../utils/tenancy-posture-hints.js'; import Serve, { formatDegradedTenancyWarning, @@ -90,9 +98,10 @@ import Serve, { * * Reading it as `Serve.…` is deliberate: what these pins are about is what THIS * COMMAND puts in front of an operator, and the static is the seam the boot path - * and the roster pin already address. It is a literal in `serve.ts`, not a - * re-export of the shared hints module — site 8 below is what holds the two - * equal. + * and the roster pin already address. Since #12579 that static is assigned from + * the shared hints module rather than being a second literal, so this reads the + * ONE declaration through serve's own handle — which is what these pins want, + * the value an `os serve` message actually interpolates. */ const PKG = Serve.ORGANIZATIONS_RUNTIME_PKG; @@ -248,24 +257,6 @@ describe('serve — the posture description an operator reads names the declarat expect(TENANCY_POSTURES).toContain('group'); expect(TENANCY_POSTURES).toContain('isolated'); }); - - // #12492. The one thing the shared table could not absorb: the package name is - // declared twice inside `packages/cli` on purpose — see - // `Serve.ORGANIZATIONS_RUNTIME_PKG`'s docblock, and the shared module's. This - // is the assertion that makes that duplication a CHECKED one. Drift either - // copy and `os serve` and `os doctor` start naming different packages at - // operators; without this, nothing anywhere would say so. - it('site 8 — serve\'s literal and the shared hints module declare the SAME package', () => { - expect( - PKG, - 'Serve.ORGANIZATIONS_RUNTIME_PKG and the shared tenancy-hints module disagree about the ' - + 'multi-org runtime, so `os serve` and `os doctor` now name different packages at operators', - ).toBe(SHARED_ORGANIZATIONS_RUNTIME_PKG); - - // …and the isolated hint an operator reads is built from the shared copy, so - // the equality above is load-bearing rather than a spare assertion. - expect(TENANCY_POSTURE_FIX_HINTS.isolated).toContain(SHARED_ORGANIZATIONS_RUNTIME_PKG); - }); }); describe('#12151 CONTROL — these pins can say no', () => { diff --git a/packages/cli/src/commands/serve.ts b/packages/cli/src/commands/serve.ts index 4b51811ed7..911b9a77f4 100644 --- a/packages/cli/src/commands/serve.ts +++ b/packages/cli/src/commands/serve.ts @@ -52,8 +52,16 @@ import { LOG_LEVELS, resolveLogLevel, readLogLevelEnv } from '../utils/log-level import { BootLogCapture, isVerboseBootLevel } from '../utils/boot-log-capture.js'; import { graftAuthoredRuntimeMembers, isAppPluginLike } from '../utils/graft-runtime-hooks.js'; import { redactConnectionUrl, describeDriverConnection } from '../utils/connection-display.js'; -// The posture prose `os serve` and `os doctor` BOTH print, declared once (#12492). -import { TENANCY_POSTURE_FIX_HINTS } from '../utils/tenancy-posture-hints.js'; +// The posture prose `os serve` and `os doctor` BOTH print, declared once +// (#12492) — and, since #12579, the multi-org runtime SPELLING those two +// commands put in front of the same operator, declared once with it. +// +// Renamed on import so `Serve.ORGANIZATIONS_RUNTIME_PKG` below keeps its own +// NAME: separate pins address that static by name, and only the spelling moved. +import { + ORGANIZATIONS_RUNTIME_PKG as SHARED_ORGANIZATIONS_RUNTIME_PKG, + TENANCY_POSTURE_FIX_HINTS, +} from '../utils/tenancy-posture-hints.js'; // Shared with @objectstack/verify and the dogfood multi-org probes (#4700) — // node-only, hence the `/node` subpath rather than the edge-safe root export. import { @@ -993,7 +1001,7 @@ export default class Serve extends Command { * Exposed as a static for the same reason ALWAYS_ON_CAPABILITIES above is: * one declaration, two readers — the boot path and the pin. * - * ── A ⛔ stood here, and the constraint it named no longer exists ───────── + * ── The ⛔ that stood here, and how it ended ────────────────────────────── * * ⚠️ Until #12579 this read: MUST stay a string LITERAL in this file, because * `serve-cluster-host-resolution.test.ts` sweeps `serve.ts` for every dynamic @@ -1013,41 +1021,47 @@ export default class Serve extends Command { * `../utils/tenancy-posture-hints.ts` read through this file's own scan * context, in the case named "resolves an alias to a literal in a REAL sibling * module of packages/cli", and again in "resolves `static readonly MEMBER = - * ` — the shape the refactor writes". + * ` — the shape the refactor writes", which is the shape this + * declaration now carries. + * + * ⛔ Both halves stay recorded rather than the note deleted, because a reader + * who meets neither re-derives the dead constraint from the shape and puts the + * ⛔ back. + * + * ── What this declaration is now ───────────────────────────────────────── * - * ⛔ Both halves are recorded rather than the whole note deleted, because a - * reader who meets neither re-derives the dead constraint from the duplication - * and puts the ⛔ back. ⛔ Nor is that hop dead code for having no live caller: - * delete it and this file is one refactor away from emptying the sweep again. + * ⭐ The spelling is declared ONCE, in `../utils/tenancy-posture-hints.ts` — + * the neutral module `os doctor` reads too — and this static is assigned from + * it (#12579, maintainer ruling 2026-08-27, Option A). It is the same shape + * `ALWAYS_ON_CAPABILITIES` above already carries: a stable handle over a + * declaration that lives elsewhere. Until that ruling this was a second + * LITERAL held EQUAL by an assertion — site 8 of + * `serve-organizations-message-spelling.test.ts`, retired in the same diff, + * because with one declaration an equality pin has no subject. * - * ── What holds today, which is less than a prohibition ─────────────────── + * Two things outlive that decision, and neither moved: * - * The spelling is declared twice inside this package — here, and in the shared - * hints module `os doctor` also reads. That duplication is CHECKED, not - * silent: `serve-organizations-message-spelling.test.ts` site 8 asserts the two - * are EQUAL, and each is independently pinned as a key of the spec-owned - * roster (here via `test/serve-capability-vocabulary.test.ts`, there via - * `doctor-organizations-message-spelling.test.ts`). All three read this - * static's VALUE, never this file's source text, so none of them is a reason - * for the literal — and #12579 measured that the sweep was the only thing in - * the tree that ever read this file's source for a package spelling. + * · This static keeps its NAME. `test/serve-capability-vocabulary.test.ts` + * pins it as a roster KEY and the sweep resolves the load THROUGH it, both + * addressing `Serve.ORGANIZATIONS_RUNTIME_PKG`; only the spelling moved + * out. ⛔ Rename the member and those read something that is not there. + * · ⛔ The gap is NEVER closed in the other direction — see the ⛔ on + * `ORGANIZATIONS_RUNTIME_PKG` in `../utils/tenancy-posture-hints.ts`. + * `os doctor` must not depend on a `serve` command's export in order to + * spell a package name (#12464's coupling ruling, which none of this + * touches). * - * ⚠️ So the honest state is that nothing forbids single-sourcing this any - * more, and ⛔ that is still not a licence to do it in passing. PR #12532 - * shipped the duplication deliberately with the reasoning at both ends, which - * makes ending it a maintainer-facing decision — open at #12579, where what - * would have to move together is written down. Two things outlive that - * decision either way: this static keeps its NAME (the roster pins address - * `Serve.ORGANIZATIONS_RUNTIME_PKG`, and only a spelling may move), and the - * gap is ⛔ never closed in the other direction — see the ⛔ on - * `ORGANIZATIONS_RUNTIME_PKG` in `../utils/tenancy-posture-hints.ts`, whose - * reason (#12464) the hop did not touch. + * ⚠️ This assignment is the alias hop's FIRST LIVE CONSUMER, which is a cost + * as well as the point: narrowing that hop is now a LIVE regression rather + * than a synthetic one. It is written down once, where whoever narrows it will + * be standing — the suite docblock over the hop's own cases in + * `serve-cluster-host-resolution.test.ts`. * * This single-sources the SPELLING and nothing else. Which postures load the * runtime, and what each of the two failure stages means, stay exactly where * they are; the roster is deliberately not a resolution registry. */ - static readonly ORGANIZATIONS_RUNTIME_PKG = '@objectstack/organizations'; + static readonly ORGANIZATIONS_RUNTIME_PKG = SHARED_ORGANIZATIONS_RUNTIME_PKG; /** * Auto-registered plugin tiers. Plugins explicitly listed in diff --git a/packages/cli/src/utils/tenancy-posture-hints.ts b/packages/cli/src/utils/tenancy-posture-hints.ts index 0038ea3170..e3aeaef77c 100644 --- a/packages/cli/src/utils/tenancy-posture-hints.ts +++ b/packages/cli/src/utils/tenancy-posture-hints.ts @@ -19,9 +19,10 @@ * here together. Single-sourcing only `isolated` would have closed the half that * was already covered and left the other two exactly as silent as before. * - * What moved is the TABLE. The package name the `isolated` sentence interpolates - * is declared here too, but `serve.ts` still holds its own copy for a reason - * recorded at that const below — read it before "finishing the job". + * What moved is the TABLE — and, since #12579, the package name the `isolated` + * sentence interpolates moved with it. It is declared once, at the const below, + * and `serve.ts`'s `Serve.ORGANIZATIONS_RUNTIME_PKG` is assigned from it. Read + * that const's docblock before touching either end. * * ── Why this is CLI-INTERNAL and not `packages/spec` ───────────────────── * @@ -57,37 +58,45 @@ /** * The `plugins[]`-wired multi-org runtime both commands NAME in their posture - * advice, spelled once FOR THIS TABLE (#11614 → #12464 → #12492). - * - * ⚠️ This is not the only declaration of the literal inside `packages/cli`: - * `serve.ts` keeps its own `Serve.ORGANIZATIONS_RUNTIME_PKG` literal. ⛔ Do not - * read that as a live constraint on this file. It WAS one — the host-anchoring - * sweep in `serve-cluster-host-resolution.test.ts` resolved the organizations - * load site through that static to a LITERAL IN THAT FILE, so rewriting the - * static as a re-export of this const stopped the specifier resolving and - * dropped that load OUT of the sweep instead of failing inside it (#11614's - * silent-vacuity mode; #12492 tried it and the sweep's named vacuity guard - * refused it, by name). ⭐ That reason died at `1ca763b60` (#12533, PR #12582): - * the sweep now follows an import alias into a sibling module of the same - * package, and it pins that hop against THIS FILE by name. The full reading - * lives on `Serve.ORGANIZATIONS_RUNTIME_PKG`; ⛔ do not restate it here — it is - * one reason, and #12579 exists because it had four copies. - * - * ⭐ So the spelling is declared twice in this package, and both copies are - * CHECKED rather than silent — a duplicate that can drift unnoticed and one - * that cannot are different things: - * - * · `serve-organizations-message-spelling.test.ts` asserts the two are EQUAL. - * · this one is a key of the spec-owned `PLATFORM_PLUGIN_WIRED_RUNTIMES` + * advice — and, since #12579, the one place that package is spelled at all + * inside `packages/cli` (#11614 → #12464 → #12492 → #12579). + * + * ⚠️ Since #12579 this is the ONLY declaration of the spelling inside + * `packages/cli`. `serve.ts` no longer holds a second literal: its + * `Serve.ORGANIZATIONS_RUNTIME_PKG` is assigned from this const, keeping the + * NAME the roster pins address while the spelling lives here. ⛔ That is not a + * reason to move this declaration somewhere more central — it sits in a module + * NEITHER command owns for the reason the ⛔ below states. + * + * It was a duplicate until then, and the reason it HAD to be one is dead rather + * than forgotten: the host-anchoring sweep in + * `serve-cluster-host-resolution.test.ts` resolved the organizations load site + * through that static to a LITERAL IN THAT FILE, so rewriting the static as a + * re-export of this const stopped the specifier resolving and dropped that load + * OUT of the sweep instead of failing inside it (#11614's silent-vacuity mode; + * #12492 tried it and the sweep's named vacuity guard refused it, by name). + * ⭐ That reason died at `1ca763b60` (#12533, PR #12582): the sweep now follows + * an import alias into a sibling module of the same package, and it pins that + * hop against THIS FILE by name. The full reading lives on + * `Serve.ORGANIZATIONS_RUNTIME_PKG`; ⛔ do not restate it here — it is one + * reason, and #12579 exists because it had four copies. + * + * ⭐ So: one declaration, and the pins that used to hold two copies equal are + * now pins over this one — + * + * · it is a key of the spec-owned `PLATFORM_PLUGIN_WIRED_RUNTIMES` * (`doctor-organizations-message-spelling.test.ts`, leg (ii)). - * · serve's is a key of the same roster (`test/serve-capability-vocabulary.test.ts`). - * - * Ending the duplication for real is now POSSIBLE — the further hop it was - * waiting on landed, and single-sourcing the spelling into this module is the - * only live consumer it would have. ⛔ Possible is not decided: PR #12532 - * shipped this duplication deliberately with the reasoning at both ends, so - * reversing it is a maintainer-facing call. It is open at #12579, which carries - * the list of what would have to move in one diff. + * · the same value read as `Serve.ORGANIZATIONS_RUNTIME_PKG` is pinned as a + * key of that roster again (`test/serve-capability-vocabulary.test.ts`), and + * is what every operator-facing `os serve` message renders + * (`serve-organizations-message-spelling.test.ts`). + * · the equality assertion that kept the duplication CHECKED — site 8 of that + * file — retired WITH its subject, in the same diff. + * + * ⛔ Ending the duplication was a maintainer-facing call rather than a refactor, + * because PR #12532 shipped it deliberately with the reasoning at both ends. It + * was ruled on 2026-08-27 (#12579, Option A: single-source the spelling here, + * the static keeps its NAME) and taken in one diff. * * ⛔ Do NOT close the gap by importing `Serve.ORGANIZATIONS_RUNTIME_PKG` here * instead: this module is read by `os doctor`, and a diagnostic command taking a