Skip to content

Commit 04acdcc

Browse files
huangyiireneclaude
andauthored
docs(core,rest): one adjective for what cloud mounts — licence-gated, not cloud-private (#18759)
Fixes #18542 Clause-②: no #17379's census (comment 5704455410) left one five-sentence family that is neither "now false" nor "still true". Five measurement narratives in `packages/core/src/security` and `packages/rest/src` call the thing cloud's own app mounted "the real cloud-private `@objectstack/organizations`" (or "`Organizations` plugin"). Under ADR-0132 D3 — one name, two packages — that adjective never says which of the two it means: it is true of the licence-gated subclass a commercial deployment resolves the name to, and false of the package name, which in this workspace resolves to an Apache-2.0 member of it. Triage kept the five on one card so the adjective is chosen once. This is that choice. ## The adjective: `licence-gated` It is the tree's own established spelling for cloud's variant, and it is the property that DISCRIMINATES: the open-core build in this repository is not gated, the subclass cloud resolves the same name to is. Measured on this head: 16 live occurrences of `licence-gated` and zero of `license-gated` — `packages/plugins/plugin-security/README.md`, `packages/plugins/plugin-auth/src/tenancy-service.ts`, `packages/services/service-cluster/src/multi-node-gate-mount.ts`, `packages/spec/src/kernel/platform-capabilities.ts` (a runtime string), `packages/cli/src/commands/serve.ts` and four CHANGELOGs, all reading "a private, licence-gated subclass". Where the old adjective sat on the bare package NAME (the three `packages/rest` sites), the referent noun `subclass` is added with it, so the gate attaches to the variant and never to the Apache-2.0 package — otherwise the repair would trade one false reading ("this package is private") for another ("this package needs a licence"). The two `packages/core` sites already had a noun denoting the mounted instance ("`Organizations` plugin"), so they take the adjective alone. ### Why the alternatives lose * **`enterprise`** — it does not discriminate. `packages/spec/src/security/tenancy-posture.ts` uses "the enterprise `@objectstack/organizations` runtime" for the package required to ACTIVATE a multi-org posture, which since ADR-0132 is the open-core one. Picking it would relabel the ambiguity instead of removing it, and add five lines to the 107-line population the census already flagged as true-but-for-a-stale-reason. * **Keeping `cloud-private`** (the census's option C) — true of what cloud mounted, but it states the package is private: the exact claim ADR-0132 falsified and that a six-PR campaign (#16215, #16539, #16552, #17371, #17369, #17910) removed everywhere else. Five surviving instances re-seed that population. * **Dropping the adjective entirely** — right for an operator-facing message, and already ruled so for one: `packages/cli/src/commands/serve-organizations-message-spelling.test.ts` records "a commercial deployment resolves the same package name to a private licence-gated subclass, so any adjective this message picks is wrong for one of the two installs reading it". That ruling is about a message whose READER is unknown. These five are measurement narratives whose subject is named (`apps/objectos-ee`, cloud#1982): two of them contrast "objectstack#15163 measured it on the framework" with "cloud#1982 reproduced it with the real X", and with no adjective that contrast collapses into the same artifact twice — which is exactly the distinction ADR-0132 D3 exists to keep readable. * **`entitled`** — already carries a different job: `tenancy-posture.ts` uses it for ACTIVATION ("Open code, entitled activation") and doctor's roster pin for "the ENTITLED variant" of a roster row. ## The five sites Taken from #17379's census, comment 5704455410 — not from the card body, and no fresh sweep was run. Line numbers are readings at this head, not identifiers. | # | file | the adjective as it stood | |--:|:--|:--| | 1 | `packages/core/src/security/resolve-authz-context.ts:483` | "the real cloud-private `Organizations` plugin" | | 2 | `packages/core/src/security/resolve-authz-context.test.ts:1604` | same sentence, the test twin | | 3 | `packages/rest/src/rest-api-plugin.ts:300` | "the real, cloud-private `@objectstack/organizations`" | | 4 | `packages/rest/src/single-kernel-isolated-api-key-matrix.test.ts:21` | "the REAL cloud-private `@objectstack/organizations`" | | 5 | `packages/rest/src/single-kernel-isolated-session-org-claim-matrix.test.ts:11` | same shape | File surface as declared in the claim: `packages/core/src/security/` and `packages/rest/src/`. The diff is those 5 files, both halves of the cross-domain surface, nothing outside it. No other session's edit was found in either file. ## Prose, not pins The three machine assertions the card names are NOT in this diff. `serve-capability-vocabulary.test.ts:92` and `doctor-organizations-message-spelling.test.ts:197` (`edition === 'enterprise'`) and `harness.host-resolution.test.ts:288,319` (operator strings in a regex) are class-3 `enterprise` sites in `packages/cli` and `packages/verify`; this card is the class-1-ambiguous `cloud-private` family in `packages/core` + `packages/rest`. `doctor-organizations-message-spelling.test.ts:188-194` was read before deciding, as the card requires. It argues that the roster row `edition: 'enterprise'` records the ENTITLED variant and is "deliberately not relaxed to match the prose reword". Nothing here disturbs it, and its reasoning is the one this diff applies — one name, two packages, and the roster names the entitled variant. No argument to move it is made. All five edited sites are comment prose. Nothing asserts their text: the three tests that read these two source files as TEXT (`authz-store-unavailable.test.ts`, `execctx-authz-input-seam-reachability.test.ts`, `objectql-slot-consumer-census.test.ts`) assert code shapes only — imports, catch patterns, and field reads whose scanner excludes comment lines by construction. ## Evidence * `pnpm --filter '@objectstack/rest...' --filter '@objectstack/core...' build` — exit 0 (both dependency closures). * `pnpm --filter @objectstack/core test` — 51 files / 1316 tests passed. * `pnpm --filter @objectstack/rest test` — 194 files / 3236 passed, 1 skipped. * `pnpm --filter @objectstack/core typecheck && pnpm --filter @objectstack/rest typecheck` — exit 0, each including its test layer. * `pnpm lint` (repo-wide `eslint . --no-inline-config`) — exit 0 in 86s, so no narrowing was needed and none is declared. * Gate families derived by `scripts/pm/dispatch-gates.mjs --commands` at this head: 53 derived, 51 run green, 2 NOT MEASURED — `check:dual-build-cjs-loads` and `check:type-check-debt` both exit 3, `PREREQUISITE NOT MET`: they read a whole-repo `dist/` this worktree does not have, and CI builds before running them. Reconciled with `--ran`: "53 derived famil(ies) accounted for — 51 run, 2 NOT-MEASURED". ### `skip-changeset`, measured rather than assumed Both packages publish, so the label was measured. After the build, `grep -rn "licence-gated" packages/core/dist packages/rest/dist` returns **zero** hits (exit 1, captured before any pipe). Positive control on the same channel: comment prose DOES reach published output — `cloud#1982` appears at `packages/rest/dist/index.d.ts:421`, out of a JSDoc on an exported declaration in `rest-server.ts` — and the exported symbols (`resolveAuthzContext`, `RestApiPlugin`) hit in `dist` as expected. The channel exists, the control fires on it, and none of the five edited comments is on it: nothing published moves. `Clause-②: no` holds for the same reason — no accept set, no authorable key and no export moves. ## Acceptance notes * **noted, not filed** — `packages/rest/src/single-kernel-isolated-api-key-matrix.test.ts` calls the same mounted runtime "the enterprise plugin" two lines below the edited sentence. That is a class-3 line (true under ADR-0132 D3, stale reason), and #17379's class-3 population is out of this card's scope by the triage ruling, so it is left exactly as it was. Successor: none queued — the census's class-3 lane table lists no `packages/rest` lane. * **noted, not filed** — two shorter siblings of the same family already carry no adjective at all: `packages/rest/src/rest-server.ts` (the published JSDoc above) and `packages/core/src/security/api-key.ts`. Both are correct as written — they name the package, not a variant — and are unchanged. Recorded so the next reader does not read that absence as drift from this ruling. * The census's own out-of-scope finding about ADR-0132's `Status: Proposed` line is already reported on #17379 and is not refiled here. --- _Generated by [Claude Code](https://claude.ai/code/session_01CqmCgU5RGDoJYhHUMVp2af)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 95e1745 commit 04acdcc

5 files changed

Lines changed: 13 additions & 12 deletions

‎packages/core/src/security/resolve-authz-context.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1601,7 +1601,7 @@ describe('the in-memory ObjectQL double honours `limit` (#10978)', () => {
16011601
*
16021602
* ## What was measured, before the guard existed
16031603
*
1604-
* On a live `isolated` boot with the real cloud-private `Organizations` plugin
1604+
* On a live `isolated` boot with the real licence-gated `Organizations` plugin
16051605
* and a file-backed sqlite store, a session whose owner had been removed
16061606
* through better-auth's OWN `/organization/remove-member` — driven by the org
16071607
* owner, 200, the `sys_member` row really deleted — went on READING that

‎packages/core/src/security/resolve-authz-context.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ export async function resolveAuthzContext(input: ResolveAuthzInput): Promise<Res
480480
// The block above asks "is this stamped organization still backed by a
481481
// membership?" and, until this card, asked it ONLY of an API key. A browser
482482
// session's `activeOrganizationId` reached `ctx.tenantId` unread: measured on
483-
// a live `isolated` boot with the real cloud-private `Organizations` plugin,
483+
// a live `isolated` boot with the real licence-gated `Organizations` plugin,
484484
// a session whose owner had been removed through better-auth's OWN
485485
// `/organization/remove-member` (driven by the org owner, 200, the
486486
// `sys_member` row really deleted) went on READING that organization's rows

‎packages/rest/src/rest-api-plugin.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,9 @@ export function createRestApiPlugin(config: RestApiPluginConfig = {}): Plugin {
297297
// organization's rows (GET 200) and WROTE a new one into it (POST
298298
// 201, the row read back from the store carrying the other
299299
// organization's id). objectstack#15163 measured it on the
300-
// framework; cloud#1982 reproduced it with the real, cloud-private
301-
// `@objectstack/organizations` mounted, which adds no request-time
302-
// refusal of its own.
300+
// framework; cloud#1982 reproduced it with the real, licence-gated
301+
// `@objectstack/organizations` subclass mounted, which adds no
302+
// request-time refusal of its own.
303303
//
304304
// ## Why this is NOT `authServiceProvider`'s catch-all
305305
//

‎packages/rest/src/single-kernel-isolated-api-key-matrix.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
* | organization-less key | **200 total 0 (silent) · 403** | **401** |
1919
*
2020
* objectstack#15163 measured it on the framework; cloud#1982 reproduced it on
21-
* `apps/objectos-ee` with the REAL cloud-private `@objectstack/organizations`
22-
* mounted, reading the written row back out of the sqlite file — the enterprise
23-
* plugin adds no request-time refusal, so the blast radius was every walled
24-
* deployment.
21+
* `apps/objectos-ee` with the REAL licence-gated `@objectstack/organizations`
22+
* subclass mounted, reading the written row back out of the sqlite file — the
23+
* enterprise plugin adds no request-time refusal, so the blast radius was
24+
* every walled deployment.
2525
*
2626
* ## Why the fixture is shaped the way it is
2727
*

‎packages/rest/src/single-kernel-isolated-session-org-claim-matrix.test.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
* ## What was measured, before this guard existed
99
*
1010
* On a real `objectstack serve` of cloud's `apps/objectos-ee` — 44 plugins, the
11-
* REAL cloud-private `@objectstack/organizations`, `Tenancy: isolated`,
12-
* `SqlDriver(better-sqlite3)` on a FILE — a browser session whose
13-
* `activeOrganizationId` pointed at an organization its owner had LEFT:
11+
* REAL licence-gated `@objectstack/organizations` subclass,
12+
* `Tenancy: isolated`, `SqlDriver(better-sqlite3)` on a FILE — a browser
13+
* session whose `activeOrganizationId` pointed at an organization its owner
14+
* had LEFT:
1415
*
1516
* | after the membership ended | GET | POST | the row, read back from the sqlite file |
1617
* |:--|:--|:--|:--|

0 commit comments

Comments
 (0)