docs(organizations): stop calling the open multi-org runtime enterprise/closed-source (#16718) - #17371
Conversation
…se/closed-source (#16718) ADR-0132 moved the `org-scoping` registrar into open core: `packages/plugins/organizations` is Apache-2.0, carries no licence check of any kind, and declares `['group','isolated']` as its own constant. #16137's acceptance proved an open-only composition raises the `isolated` wall with no licence check reachable. The prose did not follow — 65 occurrences across 45 files still described that package as enterprise / closed-source / cloud-private. Prose only. Every non-comment hunk is a string literal (an operator message, a flag description, a conformance-matrix note, a test expectation); no control flow, no identifier and no behaviour changes anywhere. Corrected (population 1), led by the two sites the card names: - `plugin-auth/src/tenancy-service.ts` — open core's own authority for resolving the posture. Its header said both walled postures require the *enterprise* package and that multi-org operation is *a commercial capability*; `isolationActive` argued "ENABLING a multi-organization posture is an entitlement". Rewritten around ADR-0132 D2/D3/D4: the mechanism (both postures probe the same registrar) is unchanged, the answer to "can an open install run isolated?" is now yes, and the `probeEntitledPostures` seam is explained as existing for the commercial subclass rather than as a tier. - `cli/src/commands/serve.ts` — the mount block no longer says the runtime "lives in the closed-source package", and the operator-facing install remedy no longer ends "this runtime is closed-source and is NOT on the public npm registry ... Without one this bullet is not followable". That sentence told an open-source operator the remedy they had just been given was unusable. - `cli/src/utils/tenancy-posture-hints.ts` — the `isolated` hint both `os serve` and `os doctor` render. Not in the card's count: its regex cannot see `the enterprise ${ORGANIZATIONS_RUNTIME_PKG}`. - `plugin-dev/src/dev-plugin.ts` — two more operator-facing strings (the degraded-tenancy warning and the stage-2 mount refusal). Because one name now denotes two packages (ADR-0132 D3), every rewritten site names which one it means; where a site was load-bearing on ADR-0105 D12 the citation is repointed at D12-as-amended rather than deleted. Pin tests updated deliberately, with the reason recorded in each file: `serve-organizations-message-spelling.test.ts` (5 hits) and `doctor-organizations-message-spelling.test.ts`. The pins were not relaxed — they still compare whole rendered lines built from `Serve.ORGANIZATIONS_RUNTIME_PKG`, and both CONTROL blocks still differ from their expectation only in spacing. The doctor roster assertion still pins `edition: 'enterprise'`, which stays true: the roster records the ENTITLED variant. Left alone on purpose, and why, in the PR body: 2 hits that describe the genuinely commercial same-named package, and 15 that state the package is absent/not installable in this workspace as the reason a fixture fakes or skips a proof — the live capability question the card dispatches as report-only. Refs ADR-0132, ADR-0105 D12 (as amended), #16130, #16137, PR #16215. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…he card's pattern could not see (#16718) The card's measurement is a case-sensitive regex over `enterprise (@objectstack/organizations|multi-org|organizations)` plus two `closed-source`/`cloud-private` forms. Re-running it case-INSENSITIVELY, and widening to the neighbouring `cloud-private` / `enterprise package` spellings inside the two files the card names as its worst sites, turns up nine more population-1 statements. All nine are in files the previous commit already corrects; every one is a comment. - `cli/src/commands/serve.ts` (×5) · the host-importer docblock used `@objectstack/organizations` as THE example of "a cloud-private package the framework cannot see" — the same reasoning #16539 already applied to the dogfood probe: it is no longer a name this repo can use as an example of something a host root does not have. The example is now framed on what actually decides resolution (the host app's declaration), with ADR-0132's two-packages fact named. · the cloud#1013 narrative said the load "never found the cloud-private package" — true of 2026-07, asserted in the present tense. Now "then-cloud-private": the history is kept, the present claim is dropped. · the live Stage-1 mount comment said "This package is cloud-private" in the boot path itself. Now APP-DECLARED, citing ADR-0132 D3 — which build of the name a deployment gets is the host manifest's call, not `serve`'s. · the ADR-0093 D5 fail-fast comment said "the enterprise package can't provide tenant isolation". - `plugin-dev/src/dev-plugin.ts` — `ENTERPRISE @objectstack/organizations`, in capitals. The card's pattern is case-sensitive, so this hit was invisible to the 65-occurrence count while sitting two lines above one that was counted. - `plugin-auth/src/auth-plugin.ts` — "so the enterprise package needs no change to light it up", on the `org-scoping` presence probe. - `qa/dogfood/test/enterprise-organizations.ts` — the doc heading on `ORGANIZATIONS_PKG` still opened "The enterprise multi-org package", directly contradicted by the⚠️ #16539 note two lines below it. - `qa/dogfood/test/rls-multitenant.dogfood.test.ts` — "moved to the ENTERPRISE package ... not part of this open workspace". Both halves false since #16215. The skip is unchanged and its real reason is now stated: the gate needs the runtime resolvable FROM THE APP UNDER TEST, which is what `organizationsAvailable` probes. ⛔ No behaviour change: every hunk is a comment. Populations 2 and 3 are untouched — see the PR body for the per-site accounting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…erprise-only (#16718) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 12 package(s): 26 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 71 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 3e0e00eee1b4c598bccf4416190749c0cdd23743 && git checkout 3e0e00eee1b4c598bccf4416190749c0cdd23743
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b11bfb9a8a130deef53f0336083ce7ad47900fb9 076f1d54ebc4a7d51fb920ebc9157b394c68a751 && git checkout -B drift-repro b11bfb9a8a130deef53f0336083ce7ad47900fb9 && git merge --no-ff 076f1d54ebc4a7d51fb920ebc9157b394c68a751
node scripts/docs-audit/affected-docs.mjs --json b11bfb9a8a130deef53f0336083ce7ad47900fb9
|
Clause-②: no — this PR puts no new key on any published payload. Declared by the PM seat rather than the author, and measured rather than assumed: every changed line under
packages/**/src/**is a comment or contains a string literal — filtering the diff for lines that are neither returns nothing, so no control flow moved and no identifier changed. The changed strings are operator-facing message text, not payload keys.Fixes #16718
ADR-0132 moved the
org-scopingregistrar into open core. The prose did not follow: the open tree still called@objectstack/organizationsenterprise / closed-source / cloud-private in the file that decides the tenancy posture, in the CLI that mounts it, and in the operator message that tells you how to install it.Text only. Every hunk is a comment or a string literal — no control flow, no identifier, no behaviour change anywhere.
The two sites the card named
packages/plugins/plugin-auth/src/tenancy-service.ts— open core's own authority for resolving the posture. Its header said both walled postures require the enterprise package and that multi-organization operation is a commercial capability (ADR-0105 D12);isolationActiveargued "Open code, entitled activation ... ENABLING a multi-organization posture is an entitlement". Rewritten around ADR-0132 D2/D3/D4. The mechanism is unchanged and says so — both walled postures still probe the same registrar, and both still degrade without it. What changed is the answer to "can an open install have it?", which is the question a reader opens this file to ask.packages/cli/src/commands/serve.ts— the mount block no longer says the runtime "lives in the closed-source@objectstack/organizationspackage", and the operator-facing install remedy no longer ends:That sentence told an open-source operator the remedy they had just been given was unusable. Verified before rewriting it rather than assumed:
npm view @objectstack/organizations version license→17.4.0,Apache-2.0.Every rewritten site says WHICH package it means
ADR-0132 D3 makes one name denote two packages, so a sentence ambiguous between them is not corrected, it is differently wrong. Each rewritten site names the open Apache-2.0 build, or the commercial private subclass, or explicitly both. Where a site was load-bearing on ADR-0105 D12, the citation is repointed at D12 as amended by ADR-0132 rather than deleted — the amendment is on ADR-0105's own status line, dated 2026-09-06.
The accounting — what was left alone, and why
This is the number the card asked for, because "I edited fewer files than the count" is otherwise indistinguishable from missing some.
The card's own pattern, re-run at the merge base
e669e5eee(the card measured 65 at2e6a2ea4c;mainhas drifted +1 since):e669e5eee076f1d54ePlus 9 statements the card's pattern cannot see, all in files the sweep was already editing — five neighbouring
cloud-private/enterprise packagespellings inserve.ts, oneENTERPRISEin capitals indev-plugin.ts(the pattern is case-sensitive, and this hit sat two lines above one that was counted), one inauth-plugin.ts, and two in the dogfood probe. 57 statements corrected in total.The 18 that remain — deliberately
Population 2 — still true, about the OTHER package (2 hits, 2 files). Both describe cloud's genuinely private same-named subclass, mounted in cloud's own
apps/objectos-ee, inno-framework-dependents.pin.test.ts's terms:packages/rest/src/single-kernel-isolated-api-key-matrix.test.ts:21— "cloud#1982 reproduced it onapps/objectos-eewith the REAL cloud-private@objectstack/organizationsmounted"packages/rest/src/single-kernel-isolated-session-org-claim-matrix.test.ts:11— same shapePopulation 3 — the "not installable in this workspace" cluster (15 hits, 6 files).
packages/verify/src/harness.ts(6),harness.posture.test.ts(2),harness.posture-only.test.ts(1),harness.host-resolution.test.ts(1),packages/cli/test/serve-organizations-host-resolution.e2e.test.ts(3), and two dogfood files (2). Reported, not changed — and the reporting turned up something the card could not have known:One more (1 hit, 1 file).
serve-organizations-message-spelling.test.ts:72is a hit on this PR's own rationale block, which quotes the old sentence it replaced. Correct as a historical quotation.⛔
packages/plugins/organizations/**is untouched — its prose is already right.The pin tests were updated deliberately, and are not relaxed
serve-organizations-message-spelling.test.ts(5 hits) anddoctor-organizations-message-spelling.test.tsexist to pin operator wording, so they are part of the change; each carries a dated in-file block giving the reason. They still compare whole rendered lines built fromServe.ORGANIZATIONS_RUNTIME_PKG, and both CONTROL blocks still differ from their expectation only in spacing.Proven by ablation rather than asserted, on the committed tree, with the mutation confirmed on disk by occurrence count both ways and the restore proved by
git diff HEADand blob-hash equality withHEAD(both files, all four checkpoints). Both pin tests resolve their subject through relative source imports insidepackages/cli(./serve.js,../utils/tenancy-posture-hints.js), so nodist/is in the path and no rebuild leg applies.enterpriseinto theisolatedposture hintCONTROL — these pins can say noenterpriseinto serve's install-remedy bullet#12151 CONTROL — these pins can say noVerification
All run on this branch at
076f1d54e, real output watched.pnpm build --concurrency=2→Tasks: 73 successful, 73 total@objectstack/core1268 ·@objectstack/plugin-auth2245 ·@objectstack/plugin-security2103 ·@objectstack/runtime3532 ·@objectstack/plugin-sharing910 ·@objectstack/dogfood1064 (+3 skipped) ·@objectstack/types597 ·@objectstack/service-storage593 ·@objectstack/service-settings578 ·@objectstack/service-datasource676 ·@objectstack/platform-objects561 ·@objectstack/mcp292 ·@objectstack/plugin-dev72 ·@objectstack/cli3015 — all green except the 10@objectstack/clifailures below.pnpm turbo run typecheckover the same 14 packages →Tasks: 78 successful, 78 totalpnpm lint(the repo's only style authority,eslint . --no-inline-config, whole repo — not narrowed) → exit 0, no outputcheck:nul-bytes("scanned 8173 text file(s) ... no raw ASCII control bytes"),check:auth-mount-ledger("19 ObjectStack auth mount(s), all accounted for"),check:verify-stand-in("2 stand-in check(s) guarded ... 10 call site(s) reached"),check:cross-package-test-inputs("28 package(s) read outside themselves, all declared"),check:test-source-alias("73 packages with tests scanned"),check:org-identifier("2653 author-facing source file(s)"), pluscheck:tenant-chokepoint,check:single-claim-paths,check:watch-hint-literal,check:error-code-casing. The gate family was derived withnode scripts/pm/dispatch-gates.mjs(which reported 60+ families for this path set); this is a declared narrowing to the ones this diff can plausibly move — CI runs the farm regardless.The 10
@objectstack/clifailures are a macOS artifact, proven, not waved away3 files, 10 tests:
test/published-subpath-console.pin.test.ts,test/published-subpath-hook-body.pin.test.ts,test/serve-runtime-state-project-key.test.ts. Every assertion is a path comparison of the same shape:/tmpis a symlink toprivate/tmpon macOS; the tests build scratch dirs frommkdtempSync(join(tmpdir(), …))and compare the unresolved path against a child process's realpath-resolved one. Controlled experiment rather than an assertion: re-running exactly those three files withTMPDIR=/private/tmpand nothing else changed →Test Files 3 passed (3) · Tests 41 passed (41). No source change can put/privateon one side of that comparison and not the other. CI runs on Linux, where/tmpis not a symlink.Provenance — this branch salvages an earlier attempt
Two dispatched devs died on this card before pushing anything, and the branch they left on the remote (
claude/organizations-is-open-core-prose-16718, tip05374bee6) really was empty. But the local worktree of attempt 2 held an unpushed commit,78cc23f8d, carrying a complete 39-file sweep. It is the first commit here, cherry-picked onto currentorigin/main; three conflicts (sharing-plugin.ts,admin-routes.ts,storage-service-plugin.ts) weremain's #16013 comment refactor, resolved tomain's text with the prose fix reapplied — those three now name the package instead of calling it "the enterprise organizations runtime". Every factual claim in the salvaged commit was re-verified againstmainbefore it was kept: ADR-0132 D2/D3/D4 exist and say what it cites, ADR-0105's status line carries the 2026-09-06 amendment,organizations-plugin.ts:194declares['group', 'isolated'],index.ts:31states the no-licence-check boundary, and the npm publication is real.Changeset
.changeset/organizations-open-core-prose.md, patch for@objectstack/cliand@objectstack/plugin-dev— the only two packages where a published, user-visible string moved (serve's install remedy, theisolatedposture hint rendered byserveanddoctor,os verify's--org-scopedflag description, and the dev stack's degraded-tenancy warning + stage-2 mount refusal). Every other edited package changed comments or test expectations only.🤖 Generated with Claude Code