From 121f2c332c2c6a3b090f2b0367af2b0fadffd565 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 05:29:58 +0000 Subject: [PATCH] docs(metadata-protocol,objectql): stop teaching the retired `environment_id` column stamp/filter Seven JSDoc/comment passages across three files still documented `saveMetaItem` stamping an `environment_id` column on new `sys_metadata` rows and `loadMetaFromDb` filtering by it. ADR-0005 (revised 2026-05) / ADR-0006 v4 retired that job when each environment got its own physical database; `organization_id` is the isolation key that survived. Measured on this tree: zero non-comment `environment_id` occurrences in the three files, with `organization_id` answering 44 in `protocol.ts` under the same filter as the positive control. `loadMetaFromDb`'s real where-clause is `{ state: 'active', organization_id: null }`. Prose only. The `environmentId` option is untouched: its non-comment site counts are identical before and after (12 / 9 / 6). Part of #13434 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01F3jdziLbAPGeceVNmSox5L --- .changeset/environment-id-stale-jsdoc.md | 37 ++++++++++++++ packages/metadata-protocol/src/plugin.ts | 13 +++-- packages/metadata-protocol/src/protocol.ts | 58 +++++++++++++--------- packages/objectql/src/plugin.ts | 27 +++++++--- 4 files changed, 101 insertions(+), 34 deletions(-) create mode 100644 .changeset/environment-id-stale-jsdoc.md diff --git a/.changeset/environment-id-stale-jsdoc.md b/.changeset/environment-id-stale-jsdoc.md new file mode 100644 index 0000000000..695a213c13 --- /dev/null +++ b/.changeset/environment-id-stale-jsdoc.md @@ -0,0 +1,37 @@ +--- +"@objectstack/metadata-protocol": patch +"@objectstack/objectql": patch +--- + +docs(metadata-protocol,objectql): stop teaching the retired `environment_id` column stamp/filter (#13434) + +Prose only — no behaviour changes, and the `environmentId` option is untouched +and still very much alive. What changes is what the docstrings on the two +plugin options interfaces teach, and those docstrings ship: they are emitted +into both packages' published `.d.ts` on the exported `ObjectQLPluginOptions` +and `MetadataProtocolPluginOptions`, so this is the tooltip a consumer +configuring per-environment scoping actually reads. + +Seven passages still described `saveMetaItem` stamping an `environment_id` +column on new `sys_metadata` rows and `loadMetaFromDb` filtering by it. That +job was retired by ADR-0005 (revised 2026-05) / ADR-0006 v4 when each +environment got its own physical database: `organization_id` is the isolation +key that survived. The three files carry **zero** non-comment occurrences of +`environment_id` (positive control in the same files under the same filter: +`organization_id` answers 44 in `protocol.ts`), `loadMetaFromDb`'s actual +where-clause is `{ state: 'active', organization_id: null }`, and +`SysMetadataObject.environment_id`, `DatabaseLoaderOptions.environmentId` and +`DatabaseLoader`'s pin test already say so. + +The danger was not staleness but subject: the prose described an **isolation +barrier**, so an author reading it would believe an environment-level boundary +existed inside `sys_metadata`. The replacements say what is true now and say +that the column job was retired, rather than deleting the sentence — a bare +deletion loses the signal for the next reader who wonders whether environment +scoping was ever there. + +`environmentId` keeps every job it actually has, and the corrected prose now +names them from measurement: the ADR-0005 overlay-whitelist gate, the ADR-0010 +metadata-lock evaluation, the SchemaRegistry hydration/listing posture, the +metadata-service bridge skip, and the local metadata-storage provisioning +decision. diff --git a/packages/metadata-protocol/src/plugin.ts b/packages/metadata-protocol/src/plugin.ts index 250c42ef47..ed025382fd 100644 --- a/packages/metadata-protocol/src/plugin.ts +++ b/packages/metadata-protocol/src/plugin.ts @@ -48,10 +48,15 @@ import type { MetadataAuthoringChannel } from './protocol.js'; export interface MetadataProtocolPluginOptions { /** - * Per-project scope (cloud per-env kernels). When set, `saveMetaItem` - * stamps `environment_id` on new sys_metadata rows, `loadMetaFromDb` - * filters by it, and the metadata-storage objects are NOT provisioned - * locally (per-project kernels source metadata from the control plane). + * Per-environment scope (cloud per-env kernels). Setting it does NOT put an + * `environment_id` column on anything: `saveMetaItem` no longer stamps that + * column and `loadMetaFromDb` no longer filters by it (ADR-0005 revised + * 2026-05 — each environment owns its own database, and `organization_id` + * is the isolation key that survived). What it still decides is that the + * metadata-storage objects are NOT provisioned locally (per-environment + * kernels source metadata from the control plane), plus the other topology + * postures listed on `ObjectStackProtocolImplementation`'s own + * `environmentId` field. * Mirrors `ObjectQLPluginOptions.environmentId` — pass the same value. */ environmentId?: string; diff --git a/packages/metadata-protocol/src/protocol.ts b/packages/metadata-protocol/src/protocol.ts index 6a08f3244a..7e3ddb6d97 100644 --- a/packages/metadata-protocol/src/protocol.ts +++ b/packages/metadata-protocol/src/protocol.ts @@ -4099,18 +4099,20 @@ export class ObjectStackProtocolImplementation implements private engine: MetadataHostEngine; private getServicesRegistry?: () => Map; /** - * Project scope applied to sys_metadata reads/writes. When undefined - * (single-kernel deployments), rows land in / come from the - * platform-global bucket (`environment_id IS NULL`). When set, every - * saveMetaItem insert/update and loadMetaFromDb query is filtered by - * `environment_id = environmentId`, so per-project kernels see only their own - * metadata even if several projects share the same physical database. - * - * [#6710] Row scoping ONLY. This key keeps every one of its other jobs — - * the `environment_id` stamp/filter, the ADR-0005 overlay-whitelist gate, - * the local metadata-storage provisioning decision — but it no longer - * decides whether the #4463 runtime authoring rules run. - * See {@link authoringChannel}. + * Environment scope this protocol instance is bound to. It is a topology + * declaration, not a row filter. ADR-0005 (revised 2026-05) gave every + * environment its own physical database, so no saveMetaItem insert/update + * writes an `environment_id` column and no loadMetaFromDb query constrains + * on one — the isolation key that survived is `organization_id`, which is + * what `ensureOverlayIndex` below indexes and what `loadMetaFromDb`'s own + * where-clause names. Legacy rows may still carry the column; nothing here + * reads it. + * + * [#6710] Topology ONLY. This key keeps every one of its other jobs — the + * ADR-0005 overlay-whitelist gate, the ADR-0010 metadata-lock evaluation, + * the SchemaRegistry hydration/listing posture, the local metadata-storage + * provisioning decision — but it no longer decides whether the #4463 + * runtime authoring rules run. See {@link authoringChannel}. * * [#7674] …and no longer whether the #3050 pre-persistence authoring gate * runs either. The sentence above used to list "the #3050 authoring-gate @@ -4489,12 +4491,14 @@ export class ObjectStackProtocolImplementation implements // mechanism, because the failure mode being designed out is precisely // "a new assembly variant nobody thought about". // - // `environmentId` keeps its row-scoping jobs — the `environment_id` - // stamp/filter and the ADR-0005 overlay-whitelist gate. [#7674] It no - // longer keys the #3050 authoring gate below either: #6710 re-keyed - // this activation and left that one on the retired proxy, which cost - // the ADR-0090 D11 object posture gate every host-config deployment - // until #7674 finished the move. + // `environmentId` keeps its topology jobs — the ADR-0005 + // overlay-whitelist gate and the registry / provisioning postures. It + // never stamps or filters an `environment_id` column: ADR-0005 (revised + // 2026-05) retired that job when each environment got its own database. + // [#7674] It no longer keys the #3050 authoring gate below either: + // #6710 re-keyed this activation and left that one on the retired + // proxy, which cost the ADR-0090 D11 object posture gate every + // host-config deployment until #7674 finished the move. if (this.authoringChannel === 'package-author') return []; if (evt.state !== 'active') return []; // `os migrate meta --stored --apply` rewrites rows that ALREADY EXIST @@ -5084,10 +5088,12 @@ export class ObjectStackProtocolImplementation implements } /** - * Exposes the project scope the protocol is bound to. Consumers like + * Exposes the environment scope the protocol is bound to. Consumers like * the HTTP dispatcher use this to decide whether to trust the process- - * wide SchemaRegistry or whether they must route a read through the - * protocol's environment_id-filtered lookup. + * wide SchemaRegistry (an unscoped kernel owns it) or whether they must + * route the read through this protocol's own sys_metadata lookup — which + * is scoped by `organization_id`, never by an `environment_id` column + * (ADR-0005 revised 2026-05). */ getProjectId(): string | undefined { return this.environmentId; @@ -19635,9 +19641,13 @@ export class ObjectStackProtocolImplementation implements * Loads all active metadata records and registers them in the in-memory registry. * Safe to call repeatedly — idempotent (latest DB record wins). * - * Per ADR-0005, project-kernel mode ALSO hydrates from sys_metadata — - * customization overlay rows must survive restart. Scope filter - * (`environment_id = this.environmentId ?? null`) keeps tenants isolated. + * Per ADR-0005, environment-kernel mode ALSO hydrates from sys_metadata — + * customization overlay rows must survive restart. The where-clause below + * is `{ state: 'active', organization_id: null }`: env-wide rows only, with + * per-org overlays left to `getMetaItem`'s on-demand read so one org's + * customization never lands in the process-wide SchemaRegistry. There is no + * `environment_id` constraint — ADR-0005 (revised 2026-05) gave each + * environment its own database and `organization_id` is the isolation key. * * #3903 — two contract duties run per row, and their split is deliberate: * diff --git a/packages/objectql/src/plugin.ts b/packages/objectql/src/plugin.ts index 2c05d109f4..b3cbf1ad4c 100644 --- a/packages/objectql/src/plugin.ts +++ b/packages/objectql/src/plugin.ts @@ -62,18 +62,33 @@ function hasLoadMetaFromDb(service: unknown): service is ProtocolWithDbRestore { /** * Options for ObjectQLPlugin. * - * `environmentId` scopes all metadata writes + reads to a specific project. - * When set, `protocol.saveMetaItem` stamps `environment_id = ` on - * new sys_metadata rows, and `protocol.loadMetaFromDb` filters by the same - * column. Leave undefined in single-kernel / self-hosted mode — rows land - * in the platform-global scope (environment_id IS NULL). + * `environmentId` declares that this kernel serves ONE environment. It is a + * topology declaration, not a row filter. ADR-0005 (revised 2026-05) gave every + * environment its own physical database, so `protocol.saveMetaItem` stamps no + * `environment_id` column on new sys_metadata rows and `protocol.loadMetaFromDb` + * constrains on none — the isolation key that survived is `organization_id`. + * The column is deprecated on the metadata tables and new writes leave it NULL; + * `SysMetadataObject.environment_id` and `DatabaseLoaderOptions.environmentId` + * both carry that note, and `DatabaseLoader`'s own pin test asserts the write + * does not set it. + * + * What setting it still decides is topology: this kernel skips the boot-time + * `sys_metadata` hydration unless `hydrateMetadataFromDb` opts back in, skips + * bridging the process-wide SchemaRegistry into the metadata service (that + * registry is shared, so the bridge would leak sibling environments' objects), + * and does not provision the metadata-storage platform objects locally. Leave + * undefined in single-kernel / self-hosted mode. */ export interface ObjectQLPluginOptions { /** Optional pre-built engine. When absent, one is lazily created in init. */ ql?: ObjectQL; /** Passed to `new ObjectQL(...)` when `ql` is not supplied. */ hostContext?: Record; - /** Scope sys_metadata reads/writes to this project. */ + /** + * Declare that this kernel serves one environment. Topology only — it does + * not scope sys_metadata rows by an `environment_id` column. See the + * interface docblock above. + */ environmentId?: string; /** * [#6710] Which authoring channel this kernel's metadata writes arrive on —