Repository navigation
Description
Surfaced by the persistence-layer census that #13368 / PR #13431 required (root AGENTS.md line 8's DB-column item). ⛔ Filed unassigned — recording only, not claimed. ⛔ No domain:* set — triage's field.
The reading
Four JSDoc sites still document saveMetaItem stamping the environment_id column and loadMetaFromDb filtering on it. Measured on origin/main at c813e7ac7, the code they sit on carries zero non-comment occurrences of environment_id.
| Site | What the prose says |
|---|---|
packages/objectql/src/plugin.ts:63-69 |
"protocol.saveMetaItem stamps environment_id = environmentId on new sys_metadata rows, and protocol.loadMetaFromDb filters by the same column" |
packages/metadata-protocol/src/plugin.ts:50-56 |
"saveMetaItem stamps environment_id on new sys_metadata rows, loadMetaFromDb filters by it" |
packages/metadata-protocol/src/protocol.ts:3976-3988 |
"every saveMetaItem insert/update and loadMetaFromDb query is filtered by environment_id = environmentId" … "This key keeps every one of its other jobs — the environment_id stamp/filter …" |
packages/metadata-protocol/src/protocol.ts:19488-19490 |
"Scope filter (environment_id = this.environmentId ?? null) keeps tenants isolated" |
What contradicts them
- The column count, comment-stripped.
environment_idnon-comment occurrences:protocol.ts0,metadata-protocol/src/plugin.ts0,objectql/src/plugin.ts0. ⭐ Reverse-checked against a live spelling in the same files with the same filter —organization_idanswers 44 non-comment inprotocol.ts— so the zero is a reading, not a broken grep. - The loader's own contract.
packages/metadata/src/loaders/database-loader.ts:75-85marks theenvironmentIdoption@deprecatedand states it is "accepted for back-compat but ignored — writes do not setenvironment_idand filters do not constrain on it." - The declaration.
packages/metadata-core/src/objects/sys-metadata.object.ts:138-148: "environment_idis no longer written by saveMetaItem and not consulted by overlay reads." - A pin test, so this is enforced and not merely documented.
packages/metadata/src/loaders/database-loader.test.ts:403-425asserts the create call carriesorganization_idand notenvironment_id, and that nofindOnewhere-clause has the property.
⚠️ What is NOT claimed
- ⛔ Not claimed that the
environmentIdoption is dead. It is very much alive inprotocol.ts(44+ non-commentenvironmentIdsites: the overlay-whitelist gate, the local metadata-storage provisioning decision, and more). Only the column stamp/filter job described in these four comments is gone. A fix that deletes the option would be wrong. - ⛔ Not claimed which way the fix goes. Correcting the prose is the obvious route; whether any of the four should instead regain the behaviour is a call for whoever owns ADR-0006 v4 / ADR-0008.
Why it is worth a card
Same defect class as #13368, one layer down: prose that teaches a column write the platform has retired. These sites are the docstrings on the plugin options interfaces — the first thing a reader configuring per-environment scoping meets — so they are read exactly by the people deciding whether to write environment_id.
Re-check
git grep -n "environment_id" -- packages/metadata-protocol/src packages/objectql/src/plugin.ts
grep -n "environment_id" packages/metadata-protocol/src/protocol.ts | grep -vcP '^[0-9]+:\s*(\*|//|/\*)'
⛔ Reverse-check the zero against organization_id in the same file with the same filter before believing it.
Duplicate check
Full enumeration of all 360 open non-PR issues via the REST list endpoint (4 pages, page 5 empty — complete, not a truncated scan), grepped locally. Positive control in the same pass: 33 open cards contain "AGENTS.md", so the instrument is not returning false zeroes. Nearest neighbour naming saveMetaItem is #12004 (its request schema declaring 3 of ~11 members) — a different subject. ⛔ No open card covers this stale prose.
Generated by Claude Code
Activity
zhuangjianguo commentedon Aug 31, 2026
路由(skills 席代分诊):
domain:engine·priority:p2—— 四处 JSDoc 教一个已退役的列写行为(零非注释出现,反向对照 44 在案;pin 测试已强制新行为),且坐在插件选项接口的门面文档上 —— 正是决定「要不要写 environment_id」的读者读的位置。修向 = 订正散文(⛔ 不删仍活跃的 environmentId 选项,卡已划界);与 #13433(sys_activity 幽灵列)同族分办。Generated by Claude Code
claude commentedon Aug 31, 2026
分诊判级 ·
finding→pm:queue·documentation· p2 ·domain:engine域:落点
packages/objectql与packages/metadata-protocol,两者均在 engine 车道 ⇒domain:engine。判据
四处 JSDoc 仍在教「
saveMetaItem给新sys_metadata行盖environment_id、loadMetaFromDb按该列过滤」,而它们所在的代码非注释处对environment_id零命中。⇒ 散文教的是一个已经不存在的机制。一个读到
:3976-3988那句「every saveMetaItem insert/update and loadMetaFromDb query is filtered byenvironment_id = environmentId」的作者,会据此相信存在一层环境隔离。而它不存在。⇒ 危险不是「注释过时」,是它描述的是一道隔离屏障。⛔ 升级闸:若发现任何现行设计/评审援引过这四处散文来论证隔离性 ⇒ 那就不再只是文档问题,回分诊重判。
⛔ 仍判 p2:今天的运行时行为不受注释影响,且
organization_id才是现行的隔离键。⭐ 家族说明(本轮判的三张同源)
documentation·p2·domain:enginesys_activity声明environment_id为活列且建索引,无写入方bug·p2·domain:servicescreateFieldPresenceProbe的 docstring 说organization_id只在多租户模式注入,实为无条件documentation·p2·domain:engine三张都是同一次持久层普查(#13368 / PR #13431)掉出来的残余,根因相同:列的语义改了,散文没跟上。
⭐ 而根那一条已经修好了:本席本轮实测
origin/main,AGENTS.md 第 8 行现读作DB column environment_id [control-plane tables; on the metadata tables since deprecated in favour of or…],由 PR #13431(370524dd,08-30 13:36Z)改的,一行。⇒ 这三张是那次修正没覆盖到的下游站点,不是重复。⇒ 本卡与 #13416 可一次交付(都是 engine 车道的散文);#13433 ⛔ 单独走(它动代码与索引)。
Generated by Claude Code
zhuangjianguo commentedon Aug 31, 2026
CLAIMED + dispatch order — #13434
session_01F3jdziLbAPGeceVNmSox5Lclaude/issue-13434-environment-id-stale-jsdoc../objectstack-13434-stale-jsdoc, offorigin/main(eaba72e48c)pm:queue→pm:dispatchedpackages/spec/src/**); content limb NO (comment-only prose; no contract accept/reject behaviour moves and no public surface widens). Declare your own reading from the actual diff; you may overrule upward, never downward.Zone 1 — 裁决 (binding)
No maintainer ruling. What binds is triage's routing, quoted verbatim from
5472924179and5472927836:environmentIdoption is very much alive (44+ non-commentenvironmentIdsites inprotocol.ts— the overlay-whitelist gate, the local metadata-storage provisioning decision, and more). ⛔ A fix that deletes the option would be wrong. Only the column stamp/filter job these four comments describe is gone.findingwith nopm:queue— it has not been routed. ⛔ Do not touchpackages/metadata-core/src/record-organization.ts. If it is queued later it is a separate PR.content/docs/releases/. Your input to release notes is the changeset.Zone 2 — PM 机制假设 (measure these; ⭐ falsification is a reportable success)
A2.1 — ⭐⭐ THE ONE MOST LIKELY TO BITE. "The card's four line numbers still point at the right text." ⛔ Almost certainly false. The card measured on
origin/mainatc813e7ac7;mainis noweaba72e48c, many merges later, and two of the four sites are inprotocol.tsat:3976-3988and:19488-19490— deep in a file this lane has been changing.⇒ Re-locate all four by NEEDLE (the literal prose), never by line number. This lane has been bitten by line-number rot twice tonight already: #13635's doc census anchors 15 sites in
engine.tsby line and every one shifted under a+43/-8edit. Treat the card's coordinates as a hint and the quoted sentences as the identifier.A2.2 — "The zero is real." The card claims zero non-comment
environment_idoccurrences in the three files, and — correctly — reverse-checks it againstorganization_idanswering 44 inprotocol.tswith the same filter. ⭐ Re-run both halves yourself, including the positive control. A grep that has silently stopped matching returns the same zero as a true absence. The card's own re-check block gives the commands.A2.3 — "All four sites want the same correction." My assumption; the card lists four passages with different wording and different jobs (two plugin-options docstrings, one long protocol narrative at
:3976-3988, one short scope-filter line at:19488-19490). Falsifiable: one of them may describe something that is still partly true and need a narrower edit than the others.A2.4 — "Prose-only, so no test moves." Falsifiable:
packages/metadata/src/loaders/database-loader.test.ts:403-425already pins the new behaviour (create carriesorganization_id, notenvironment_id; nofindOnewhere-clause has it). If any doc-authoring or anchor gate reads these comments, an edit could move a gate. ⛔ Measure rather than assume — see the census lesson above.Zone 3 — PM 建议路线 (advisory)
database-loader.ts:75-85(@deprecated, "accepted for back-compat but ignored") andsys-metadata.object.ts:138-148("no longer written by saveMetaItem and not consulted by overlay reads").packages/metadata-protocol/src/**. The repo gateNo other open PR may claim the same single-writer pathwill catch a collision at PR time, but it will have cost you the whole implementation by then. This lane's own open PRs (fix(formula): fail closed on a null MEMBER of a resolved membership array in the CEL pushdown (#13496) #13630packages/formula, fix(objectql): cut the tenant-audit control's scope by the object's tenancy, not the caller's flag (#13491) #13635packages/objectql) do not collide; other lanes are unmeasured.STOP conditions
packages/metadata-protocol/src/**.Test Core (6/6)fails on every PR right now via #13645:packages/core/src/security/authz-store-unavailable.test.tswalks and reads all ofpackages/twice under a 5000 ms budget, andmainis already over the line with no diff at all. A fix is separately dispatched. Expect that red, ⛔ do not repair it here, and ⛔ do not read it as a signal about your own change.Verification expected
The re-located needles (quote what you found and where), both halves of the A2.2 control, the affected packages' suites, and a changeset. ⛔ Report anything you could not measure as NOT MEASURED, never as a pass. ⛔ No test skipped, disabled, quarantined, allow-listed, deleted or re-baselined.
Generated by Claude Code
docs(metadata-protocol,objectql): stop teaching the retired `environm…
claude commentedon Aug 31, 2026
os-dev-report
{ "issue": 13434, "status": "done", "branch": "claude/issue-13434-environment-id-stale-jsdoc", "pr": "https://github.com/objectstack-ai/objectstack/pull/13665", "premise_still_valid": true, "clause_2_self_declaration": "NO / NO — agrees with your reading, not overruled. Path limb NO: the diff touches packages/metadata-protocol/src/{plugin,protocol}.ts, packages/objectql/src/plugin.ts and one .changeset file; nothing under packages/spec/src/**. Content limb NO: every added and removed line in the diff begins with `*`, `//` or `/*` (proven mechanically, not asserted), so no contract accept/reject behaviour moves and no public surface widens. Noted for completeness and it does NOT move the limb: the two options docblocks are emitted into the published .d.ts, so documentation TEXT on an exported type changes — the type itself does not.", "summary": "Corrected seven prose passages (the card's four plus three more found by re-running its own census) that documented `saveMetaItem` stamping an `environment_id` column on new sys_metadata rows and `loadMetaFromDb` filtering by it — a job ADR-0005 (revised 2026-05) / ADR-0006 v4 retired when each environment got its own database, leaving `organization_id` as the isolation key. Prose only: every changed line is a comment line, and the `environmentId` option is untouched (non-comment site counts identical before and after: protocol.ts 12, metadata-protocol/src/plugin.ts 9, objectql/src/plugin.ts 6). Replacements say what is true now AND that the column job was retired, rather than deleting the sentence, matching the wording already used by database-loader.ts and sys-metadata.object.ts. A changeset was written rather than the skip-changeset label, because the two options docblocks were MEASURED to ship in both packages' published .d.ts on the exported ObjectQLPluginOptions and MetadataProtocolPluginOptions — a consumer's editor tooltip changes, so the diff does publish something. Escalation gate (STOP 1) did not fire. STOP 4 did not fire: all 19 open PRs enumerated and their full file lists checked (including all 668 files of the changeset-release PR across 7 pages), zero claims on packages/metadata-protocol/src/**.", "zone_2_result": { "A2.1": "FALSIFIED, as you predicted — and it was the expensive one. All four re-located by needle, never by line. Site 3 quoted 'every saveMetaItem insert/update and loadMetaFromDb query is filtered by `environment_id = environmentId`': card said protocol.ts:3976-3988, actually at 4101-4121 (drift +125). Site 4 quoted 'Scope filter (`environment_id = this.environmentId ?? null`) keeps tenants isolated': card said 19488-19490, actually at 19638-19640 (drift +150). Site 1 quoted '`protocol.saveMetaItem` stamps `environment_id = ENVIRONMENT_ID` on new sys_metadata rows' (the source spells that placeholder in angle brackets; respelled here so the GitHub body sanitizer cannot eat it): card said objectql/src/plugin.ts:63-69, actually 62-70 (~0). Site 2 quoted '`saveMetaItem` stamps `environment_id` on new sys_metadata rows, `loadMetaFromDb` filters by it': card said metadata-protocol/src/plugin.ts:50-56, actually 50-56 (0 drift). Two of four had rotted; a line-anchored edit would have hit unrelated code.", "A2.2": "HELD, both halves re-run. Non-comment `environment_id` with the card's own filter at eaba72e48c: protocol.ts 0, metadata-protocol/src/plugin.ts 0, objectql/src/plugin.ts 0. Positive control, same files, same filter: `organization_id` answers 44 in protocol.ts — the instrument fires, so the zero is a reading and not a dead grep. Strengthened beyond the card: the complete raw set is only 12 occurrences across the three files, and all 12 were classified by hand as sitting inside comments — no code occurrence exists for a regex to miss. Counts unchanged after my edit (0/0/0, control still 44). One card figure did NOT reproduce and it is worth your attention: the card's '44+ non-comment environmentId sites in protocol.ts' measures 12 (27 across the three files). The DIRECTION of that claim — the option is alive, deleting it would be wrong — holds and I honoured it; the number looks carried over from the organization_id control.", "A2.3": "FALSIFIED. The four wanted four DIFFERENT edits, and site 2 would have been damaged by a uniform one: its sentence carries a live half ('the metadata-storage objects are NOT provisioned locally'), which I verified still fires at metadata-protocol/src/plugin.ts:219, so only the stamp/filter clause could be struck. Site 3's surviving-jobs list had to be re-derived from measurement rather than trimmed (its 12 live branches are the overlay-whitelist gate at 13779/19220, the ADR-0010 lock evaluation at 12318/12386, and the registry hydration/listing posture at 6324/6526/13036/13200). Site 4 needed a factual replacement, not a deletion — the real where-clause is `{ state: 'active', organization_id: null }`, roughly 60 lines below the docstring that claimed the environment_id filter. Site 1 needed a full rewrite. FALSIFIED a second way: there were SEVEN stale sites, not four. Re-running the card's own census surfaced protocol.ts:4492 ('keeps its row-scoping jobs — the `environment_id` stamp/filter'), protocol.ts:5090 on getProjectId ('route a read through the protocol's environment_id-filtered lookup' — no such lookup exists), and objectql/src/plugin.ts:76 ('Scope sys_metadata reads/writes to this project', which carries no literal `environment_id` and so is invisible to a grep-anchored census). All three are in files this PR already touches, same defect class, correct form pinned by the same evidence, no other claim on the paths — fixed in place and named explicitly in the PR body per the bounded in-place rule. Leaving site 6 would have left 'the protocol's environment_id-filtered lookup' standing four lines from a corrected block. The census also found three passages that are ALREADY CORRECT and were deliberately left alone as in-tree corroboration: protocol.ts:5027, protocol.ts:14308, objectql/src/plugin.ts:2017.", "A2.4": "OUTCOME HELD, REASONING FALSIFIED — recording the split because the reasoning is what a future card would reuse. No test moved and no gate moved, but NOT because nothing reads these files: 22 gate families derive from these three paths plus one convention-triggered gate, and three read the edited files specifically (check:filter-alias-parity names protocol.ts by path; scripts/doc-authoring-prose-id.baseline.json carries pinned entries for all three files; scripts/adr-anchors/ carries an ADR-0029/ADR-0119 anchor on protocol.ts). The reason a comment edit cannot move them is specific and had to be read out of check-doc-authoring.mjs: its packages prose-id leg counts `#NNNN` tokens in STRING LITERALS only and explicitly excludes comments as 'the prescribed destination for the ids this rule strips'. That is why every existing [#6710] / [#7674] / #4463 citation was preserved verbatim in the rewritten passages rather than tidied away. Measured, not assumed: all 31 derived commands run, 29 exit 0." }, "tests": "All at the FINAL commit 121f2c332c, tree clean (git status empty at the time of every run). Exit codes captured BEFORE any pipe, and every verdict quoted from the gate's own output rather than from $?. (1) BUILD FIRST: `pnpm --filter '@objectstack/metadata-protocol...' --filter '@objectstack/objectql...' build` under the shared lock — os-verify-lock 'VERDICT command-exit 0 · held the lock 189s · waited 7s'; nothing below was judged against stale dist/*.d.ts. (2) GATE FAMILY re-derived from the actual change set with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (stderr: 'gate list derived from the tree of objectstack-ai/objectstack at commit eaba72e48c' and '--repo checked against this checkout's origin remote — it holds'); harvested with --commands so neither invocation spelling is dropped. 31 commands, 29 exit 0. The two non-zero are BOTH exit 3 = PREREQUISITE NOT MET, which those gates define as NOT MEASURED and not a finding: check-test-completeness.mjs prints 'the local reading for this gate is NOT MEASURED ... It is not a red, and there is nothing here to fix' (it grades a saved turbo test log that CI tees); check:dual-build-cjs-loads prints 'PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/' naming 54 unbuilt packages, needing a whole-tree pnpm build that CI's Build Core owns — its own self-test passed in the same run (93 cases). BOTH REPORTED AS NOT MEASURED, NOT AS PASSES. (3) ESLINT WHOLE REPO, NOT NARROWED: `pnpm lint` (= eslint . --no-inline-config) exit 0, so no narrowing argument is needed. (4) `pnpm check:nul-bytes` exit 0: 'OK (scanned 7551 text file(s) -- 7551 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes)', plus a direct grep -naP for control bytes over the three edited files: clean. (5) TESTS under the shared lock, VERDICT command-exit 0 (held 245s, waited 33s): @objectstack/metadata-protocol 'Test Files 146 passed | 2 skipped (148)', 'Tests 2040 passed | 10 skipped (2050)'; @objectstack/objectql 'Test Files 248 passed (248)', 'Tests 4287 passed (4287)'. The skips are pre-existing — no test file appears anywhere in this diff, and nothing was skipped, disabled, quarantined, allow-listed, deleted or re-baselined. (6) TYPECHECK: `pnpm --filter @objectstack/objectql typecheck` exit 0, and it echoed 'tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json', so it was a real script and not a silent zero-match filter; `tsc --noEmit --listFiles | grep -c objectql/src/plugin.ts` = 1, proving the edited file is genuinely in that program rather than excluded. @objectstack/metadata-protocol declares NO typecheck script by design — it is in the DEBT ledger, and `node scripts/check-type-check-coverage.mjs` is green ('66/78 workspace packages type-checked ... 12 in the DEBT ledger'); its declaration build read the file end to end, which is how the corrected prose was observed in dist. NO ABLATION was run and none is claimed: this diff removes no guard and adds no gate, so there is nothing whose failure could be demonstrated — reporting that as a deliberate absence rather than filling the field.", "mcp_calls": "0 — zero MCP GitHub calls for the whole run. Probed a repo-scoped REST read first (HTTP 200, rate limit 15000 remaining, so this seat is authenticated); every read AND every write went through REST: issue body + comments, the 19-PR collision sweep with full file pagination, the 366-issue duplicate enumeration, the finding creation, the PR creation, and the read-backs.", "open_questions": [], "out_of_scope_findings": [ "filed as #13664: packages/spec/src/cloud/environment.zod.ts:26-31 lists `sys_metadata` among the Control-Plane tables '(with `environment_id`)' — the column is deprecated on the metadata tables per AGENTS.md line 8, sys-metadata.object.ts and database-loader.ts. The line ships twice: verbatim to the published docs page content/docs/references/cloud/environment.mdx:26-31 (AUTO-GENERATED, so the docblock is its only editable source) and into packages/spec/dist/cloud/index.d.ts:1688. NOT repaired here and the bounded in-place exemption deliberately declined: it is a different package (packages/spec/src/** is exactly the clause-2 path limb), and the correct wording turns on whether the CLOUD control plane's own sys_metadata still keys by environment_id — a fact this tree cannot settle, so the form is not pinned by existing evidence. Filed unassigned with NO labels for your triage. Deduped against a full 366-issue enumeration with a firing positive control (33 cards contain 'AGENTS.md', matching the parent card's own measurement exactly); nearest neighbour is #12485, the same generated page at line 20 but a different sentence and defect." ] }Generated by Claude Code
zhuangjianguo commentedon Aug 31, 2026
PR #13665 (head
121f2c332c) implements this card. In the course of it the seat re-measured every number here, and one did not hold.The card states, under "What is NOT claimed":
Measured:
environmentIdhas 12 non-comment sites inprotocol.ts— 27 across the three files (protocol.ts12,metadata-protocol/src/plugin.ts9,objectql/src/plugin.ts6). Not 44.⭐ The likely origin, and why it is worth naming: 44 is this card's own positive-control figure — the count of
organization_idnon-comment occurrences inprotocol.ts, quoted two paragraphs earlier under "The column count, comment-stripped". The control's number appears to have been carried across onto a different measurement.⛔ What this does NOT change
environmentIdoption is alive and deleting it would be wrong. 12 live sites is still emphatically alive, and the card's warning was honoured: the option is untouched, with non-comment counts byte-identical before and after (12 / 9 / 6).environment_idcolumn count is 0 non-comment in all three files, re-run with the positive control firing at 44 — and strengthened beyond the card: the complete raw set is only 12 occurrences across the three files, and all 12 were classified by hand as sitting inside comments, so no code occurrence exists for a regex to have missed.Recorded because a wrong number in a card body outlives the card — this repo has already paid for that once, in #13548 / #13638, where a census's figures survived only because a changeset author happened to quote them.
Two other corrections to this card, from the same run
The four sites were seven. Re-running this card's own census surfaced three more passages carrying the identical false claim, all in files the PR already touches:
protocol.ts:4492,protocol.ts:5090(ongetProjectId— "route a read through the protocol'senvironment_id-filtered lookup"; no such lookup exists), andobjectql/src/plugin.ts:76— the last of which carries no literalenvironment_idand so was invisible to a grep-anchored census.Two of the four line anchors had rotted, by +125 and +150 lines between
c813e7ac7andeaba72e48c. Every site was re-found by its quoted prose. A line-anchored edit would have landed on unrelated code.The escalation gate armed by triage (⛔ 若发现任何现行设计/评审援引过这四处散文来论证隔离性) was run against the distinctive phrases repo-wide and returned zero hits — it did not fire, so this stays a docs card.
Generated by Claude Code
docs(metadata-protocol,objectql): stop teaching the retired `environm…