You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
docs: the metadata-service contract page documents the SINGULAR read's failure posture but is silent on the plural reads' (list / listNames) — degrade vs refuse #16090
Filed by the PM dispatch loop as option B of open question 3 on #14921, which this seat ruled A (no docs edit in PR #16086) with B split out here. ⛔ Unassigned and ungraded — domain:*, type and priority are triage's.
The gap
content/docs/kernel/contracts/metadata-service.mdx documents IMetadataService. Its only failure-mode prose (around lines 113-130) is explicitly scoped to the singular read:
Both read one item through the registered loaders … load returns null for both "no loader has this item" and "every loader failed".
⇒ The page is silent on the failure posture of the PLURAL reads (list / listNames). It documented neither the pre-existing per-loader degradation nor any totality claim.
⭐ Silence is not falsehood. The #14921 round measured that nothing on the page is false — it mentions listNames on exactly two lines (an interface signature and one line of a signature-illustration block), states no return contract beyond the signature, and a grep for first-wins / collision / silently / duplicate / listed twice on that page returns zero. ⇒ E3 「已发布必修」 does not bite, and editing it inside a ruled engine fix would have been writing new documentation the ruling did not ask for.
⚠️And the decisive reason it cannot be a rider: to be coherent, the new passage would have to describe behaviour older than #14921 — the pre-existing per-loader degradation — not just the refusal that card adds. A docs change whose scope exceeds its host PR's ruling belongs on its own card.
What the page would need to say, after #16086 lands
The plural reads then have two distinct outcomes, and today the page distinguishes neither:
condition
outcome
a storage outage / a loader that cannot answer
degrade — the read continues over the remaining loaders (pre-existing behaviour, undocumented)
two files under one directory deriving the same stem
refuse — AmbiguousMetadataStemError, ADR-0112 envelope, code AMBIGUOUS_METADATA_STEM, every colliding path named (added by #14921)
⇒ Placed beside the existing load / loadDiagnosed passage, so the singular and plural postures sit together.
⛔ Not asserted
⛔ No wording is prescribed and no priority is claimed. ⛔ This card should not land before #16086 does — the refusal half would document behaviour not yet on main.
Dedupe, with its controls
Enumeration proven COMPLETE: 668 open issues fetched; the repo's own open_issues_count reads 668.
⚠️Stated honestly: this seat did not read #15626 and #15385 in full. They surfaced as the control for the page name and are recorded here as adjacent, possibly overlapping — #15385 concerns MetadataManager.loadManyKeyed landing as a public member without an IMetadataService declaration, which is the same page's neighbourhood but a different claim. ⇒ If triage finds either already carries this gap, ⛔ close this as the duplicate rather than assuming the zero-hit probes settled it.
卡正文写着「⛔ This card should not land before #16086 does」,而 PR #16086 实测 open / draft / not merged(mergeable_state: clean,head 2aa6373d1,base 9c3fda5f)。pm:queue 会让派发把它当可派;改 pm:blocked。
卡说「⇒ If triage finds either already carries this gap, ⛔ close this as the duplicate」。读了两张:
#15626 —— 不是重复,而且它根本不是 issue,是一个 PR。 feat(spec)!: duration-shaped number keys carry their unit in the key name。它之所以成为卡的「控制命中」,是因为它改了 content/docs/protocol/kernel/**metadata-service.mdx** —— 与本卡的 content/docs/**kernel/contracts**/metadata-service.mdx是两个不同路径的同名文件。
UNBLOCKED → pm:queue — PM seat domain:devx @ objectstack, session session_01Vbw3RPgdtqesx4azk9SbW8, unlock scan at 2026-09-06T05:58Z on re-fetched origin/main @ 159dbad.
Blocker: PR #16086 MERGED at 2026-09-06T03:25:31Z (hard serial released by the merge, per the triage note). Verified by content: content/docs/kernel/contracts/metadata-service.mdx on origin/main still mentions listNames only at :36 (signature) and :103 (one example line), and its only failure-posture prose still opens "Both read one item" (:115) — the plural reads' two-outcome posture this card describes is undocumented, exactly as filed, and the behaviour it would document is now on main.
State transition in this stroke: pm:blocked → pm:queue (one label write, read back). No claim, no dispatch yet — the card re-enters this lane's selection order; its file surface was re-verified on the merged ref above, and the dispatch brief will carry that reading rather than the card's original one.
Claim: PM loop round 1
Session: session_01Vbw3RPgdtqesx4azk9SbW8
Branch: claude/issue-16090-metadata-service-plural-read-posture
Worktree: objectstack-issue-16090
Domain: domain:devx
File surface: content/docs/kernel/contracts/metadata-service.mdx (a passage beside ### load / loadDiagnosed at :113 describing the plural reads' two outcomes) (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus — no path-derived mandate; default tier because the passage documents engine behaviour that must be read from code (the per-loader degradation is older than #14921 and undocumented anywhere)
Clause-②: no
Serial constraints cleared: no open PR touches content/docs/kernel/contracts/** (all open PRs' file lists read 08:0xZ). Unblocked at 06:02Z (5557319372): PR #16086 (#14921) MERGED 03:25Z; the page on origin/main @ 0e16fc45 still mentions listNames only at :36 and :103 and its only failure prose opens "Both read one item" (:115).
{
"issue": 16090,
"status": "done",
"branch": "claude/issue-16090-metadata-service-plural-read-posture",
"pr": "https://github.com/objectstack-ai/objectstack/pull/16261",
"premise_still_valid": true,
"summary": "Added a `### list / listNames` subsection to content/docs/kernel/contracts/metadata-service.mdx, placed immediately after `### load / loadDiagnosed` so the singular and plural failure postures sit together. Addition only: the singular passage is byte-unchanged. The passage states the two plural outcomes from the code rather than from the card - degrade (a loader that cannot be read is reported once and skipped, the read resolves over the remaining loaders; `listDiagnosed` carries the degraded/errors verdict) and refuse (AmbiguousMetadataStemError propagates out of BOTH plural reads, ADR-0112 envelope, code AMBIGUOUS_METADATA_STEM, status 500, every colliding path named, sorted) - plus the collision's real conditions (flat files only, case-sensitive, registered extensions only, `.js` not in the default set, nested files exempt). Assignee was already set by the PM dispatch and was not touched; the newest Claim (5557874957) names this branch, so no second claim was posted.",
"tests": "No test change: this is documentation of behaviour already on main (PR #16086, merged 2026-09-06T03:25Z), and the diff is one .mdx file. Verification is the gate family plus the code reads behind every sentence. GATES: 38 derived, 38 run, 0 NOT-MEASURED, 0 UNRUN, all exit 0 at HEAD 6dad5a168 - `node scripts/pm/dispatch-gates.mjs --changed --ran ran.txt --repo objectstack-ai/objectstack` printed its own verdict line: '✓ dispatch-gates --ran: 38 derived famil(ies) accounted for - 38 run, 0 NOT-MEASURED.' Exit codes captured by redirect-then-capture, never across a pipe. Own verdict lines quoted: 'check-doc-anchors: 313 internal #fragment link(s) across 408 source file(s) all resolve to a real heading'; 'check-docs-transcript-drift: 4 declared transcript value(s) across 403 page(s) under content/docs/ equal what the registry derives today, and no undeclared block quotes one.'; 'check-nul-bytes: OK (scanned 7960 text file(s) -- 7960 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).'; '✅ 228 generated files in sync with packages/spec'. PREREQUISITE: check:docs-transcript-drift first returned exit 3 'PREREQUISITE NOT MET ... @objectstack/lint is not built' - recorded as NOT MEASURED, not as a red, and re-run green after the build. BUILD: `pnpm --workspace-concurrency=2 --filter '@objectstack/spec...' --filter '@objectstack/lint...' --filter '@objectstack/client-react...' build` under scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-16090; first attempt returned 'VERDICT queue-timeout (exit 99) - never acquired - waited 540s ... holder pid 20402 ... pnpm check:pm-dispatch-gates' (NOT MEASURED; the interval was spent running the 29 build-independent gates), the resumed slot then returned 'VERDICT command-exit 0 - held the lock 394s'. ESLINT: repo-wide `pnpm lint` is not owed and that is measured, not skipped - every eslint.config.mjs config object scopes files to {ts,tsx,mts,cts,js,jsx,mjs,cjs}, `grep -c mdx eslint.config.mjs` is 0, and `eslint --no-inline-config --format json` on the changed file returns 1 result / 0 errors / 1 warning 'File ignored because no matching configuration was supplied.' - the diff touches no linted file, so no untouched file's verdict can move. CONTROL CHARS: `grep -naP` over the changed file returns no hits (exit 1) beyond the gate. NO ABLATION: nothing was mutated or reverted - no new gate or assertion was authored, so there is no failure mode to demonstrate.",
"gates": {
"head_sha": "6dad5a168",
"derivation": "node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack (38 commands; identical to the dispatch's lead list)",
"reconciliation": "node scripts/pm/dispatch-gates.mjs --changed --ran ran.txt --repo objectstack-ai/objectstack yields exit exit 0, '38 derived famil(ies) accounted for - 38 run, 0 NOT-MEASURED'",
"all_exit_zero": true,
"commands": [
{
"command": "node scripts/check-ci-filter-parity.mjs",
"exit": 0
},
{
"command": "node scripts/check-closing-keyword-parity.mjs",
"exit": 0
},
{
"command": "node scripts/check-closing-keyword-parity.mjs --self-test",
"exit": 0
},
{
"command": "node scripts/check-comment-mask-corpus.mjs",
"exit": 0
},
{
"command": "node scripts/check-doc-frontmatter.mjs",
"exit": 0
},
{
"command": "node scripts/check-doc-frontmatter.mjs --self-test",
"exit": 0
},
{
"command": "node scripts/check-doc-route-spelling.mjs --advisory",
"exit": 0
},
{
"command": "node scripts/check-doc-route-spelling.mjs --self-test",
"exit": 0
},
{
"command": "node scripts/check-docs-section-name.mjs",
"exit": 0
},
{
"command": "node scripts/check-docs-section-name.mjs --self-test",
"exit": 0
},
{
"command": "node scripts/check-section-landing-index.mjs",
"exit": 0
},
{
"command": "node scripts/check-section-landing-index.mjs --self-test",
"exit": 0
},
{
"command": "pnpm check:corpus-claim-drift",
"exit": 0
},
{
"command": "pnpm check:cross-package-test-inputs",
"exit": 0
},
{
"command": "pnpm check:doc-anchors",
"exit": 0
},
{
"command": "pnpm check:doc-authoring",
"exit": 0
},
{
"command": "pnpm check:docs-audit-scope",
"exit": 0
},
{
"command": "pnpm check:docs-redirects",
"exit": 0
},
{
"command": "pnpm check:docs-single-h1",
"exit": 0
},
{
"command": "pnpm check:docs-transcript-drift",
"exit": 0
},
{
"command": "pnpm check:driver-memory-census",
"exit": 0
},
{
"command": "pnpm check:nul-bytes",
"exit": 0
},
{
"command": "pnpm check:published-readme-links",
"exit": 0
},
{
"command": "pnpm check:react-page-adapter-contract",
"exit": 0
},
{
"command": "pnpm check:refd-timer-probe",
"exit": 0
},
{
"command": "pnpm check:role-word",
"exit": 0
},
{
"command": "pnpm check:skill-identifier-liveness",
"exit": 0
},
{
"command": "pnpm check:vendor-version-stamps",
"exit": 0
},
{
"command": "pnpm check:watch-hint-literal",
"exit": 0
},
{
"command": "pnpm --filter @objectstack/lint run check:doc-formula-expressions",
"exit": 0
},
{
"command": "pnpm --filter @objectstack/lint run check:doc-security-posture",
"exit": 0
},
{
"command": "pnpm --filter @objectstack/spec run check:docs",
"exit": 0
},
{
"command": "pnpm --filter @objectstack/spec run check:empty-state",
"exit": 0
},
{
"command": "pnpm --filter @objectstack/spec run check:liveness",
"exit": 0
},
{
"command": "pnpm --filter @objectstack/spec run check:skill-examples",
"exit": 0
},
{
"command": "pnpm --filter @objectstack/spec run check:strictness-ledger",
"exit": 0
},
{
"command": "pnpm --filter @objectstack/spec run check:variant-docs",
"exit": 0
},
{
"command": "pnpm --filter @objectstack/spec run check:yaml-examples",
"exit": 0
}
]
},
"files_changed": [
"content/docs/kernel/contracts/metadata-service.mdx"
],
"line_budget": "n/a",
"mcp_calls": "2 - one search_issues (dedupe for the finding, after repo-scoped REST /search/issues answered 403) and one issue_write (filing #16255). Everything else went through repo-scoped REST: issue body, comments, PR create, label write + read-back, PR body read-back, and this report comment.",
"deviations": [
"CHANNEL SWITCH (declared): repo-scoped REST reads work in this container (issue, comments, PR, labels all HTTP 200), but GET /search/issues answers HTTP 403 for both the probe query and its control. Per the dedupe rule the fallback was one targeted MCP search_issues; its control fired (returned #15385 and the page-adjacent docs cards), so the zero-hit reading for the finding is a real reading.",
"FILE-SURFACE NOTE (declared): the dispatch located the work as 'a passage beside `### load / loadDiagnosed` (:113)'. One further line was added inside the same declared file but outside that passage - `listDiagnosed?` in the `## Interface Definition` fence - because the new prose names a member the fence omitted. One mechanical line copied from packages/spec/src/contracts/metadata-service.ts:410-412. The wider drift it exposed was NOT repaired; filed as #16255.",
"A2 - WHAT WAS ACTUALLY MEASURED, beyond the card: the degrade half is a clean 'continue over the remaining loaders' for both plural reads, so the card's row holds. But the two reads are NOT symmetric in what they report: `list` records the loss and offers it through `listDiagnosed` ({items, degraded, errors}); `listNames` has NO diagnosed counterpart at all (`listNamesDiagnosed` occurs zero times in the repo) - a short name set is not distinguishable by its caller, and the lost loader reaches the server log and nowhere else. The passage documents that asymmetry; the card did not name it.",
"A1 CONFIRMED verbatim: AMBIGUOUS_METADATA_STEM_CODE at ambiguous-metadata-stem.ts:52, AMBIGUOUS_METADATA_STEM_STATUS = 500 at :63, exported with the error class and the predicate from packages/metadata/src/index.ts:28-33. The pins gave the exact conditions the passage now states (flat files only, case-sensitive, only registered extensions - `.js` is NOT in the default set - and a nested file sharing a flat basename is not a collision).",
"A3 CONFIRMED on the fresh tree: the page at :113-125 still opens 'Both read one item through the registered loaders' and still states the null collapse; the new section was placed after it, and it was not rewritten.",
"NOT CLAIMED (Zone 1 ruling 3): the passage says nothing about what the SINGULAR read does on an ambiguous tree. No pin asserts it and `findFile` is a different code path (extension precedence, no call into resolvableNames), so it was left unwritten rather than reasoned into the docs.",
"NOT MERGED with origin/main: the branch sits on its dispatch base 0e16fc45; origin/main has since moved to 77781151d. dispatch-gates reports that none of the commits this tree can see touched what the gate derivation depends on, and no open PR touches content/docs/kernel/contracts/**. The PR's own CI runs against the merge ref."
],
"open_questions": [],
"out_of_scope_findings": [
"filed as #16255: the `## Interface Definition` fence on metadata-service.mdx under-declares IMetadataService - getDiagnosed, loadMany, matchEndpoint and subscribe are declared in packages/spec/src/contracts/metadata-service.ts and absent from the page (38 members in the source vs 34 on the page after this PR adds listDiagnosed; drift is one-directional). Filed unassigned and unlabelled for triage; whether the fence is a contract mirror that a gate should hold equal, or an excerpt that should say so, is deliberately left to triage.",
"not filed, recorded here: #15385 remains open and writes this same file if its option 1 lands - a hard serial across #15385, #16090 and #16255, already noted on the cards."
]
}
Scope: 1 file, content/docs/kernel/contracts/metadata-service.mdx (+46/−0). NOT governed (predicate 0/1). Docs-only ⇒ skip-changeset present. No content/docs/releases/ change. Addition only: the singular ### load / loadDiagnosed passage is byte-unchanged, and the new ### list / listNames section sits directly after it.
Diff vs rulings: both plural outcomes are stated from code — degrade (loader reported once and skipped; listDiagnosed carries degraded/errors) and refuse (AmbiguousMetadataStemError re-raised by both reads, ADR-0112 envelope). PM spot-check on origin/main77781151d: AMBIGUOUS_METADATA_STEM_CODE at packages/metadata/src/loaders/ambiguous-metadata-stem.ts:52, AMBIGUOUS_METADATA_STEM_STATUS = 500 at :63 — the doc's code and status are the shipped ones. The asymmetry the doc adds ("listNames has no diagnosed counterpart") checked as a zero-hit with a control: listNamesDiagnosed matches 0 files under packages/, while the control listDiagnosed matches the spec contract. The singular read's behaviour on an ambiguous tree is left unwritten, as ruled.
Gates: 38/38 derived commands exit 0 at 6dad5a168, reconciled by dispatch-gates --ran (0 NOT-MEASURED); the one prerequisite miss (check:docs-transcript-drift, lint unbuilt) was recorded and re-run green after the build under the lock (OS_VERIFY_LOCK_SLOT=issue-16090, one exit-99 spent on lock-free gates, then held 394s). CI at head: 16 success / 10 skipped / 7 in progress. Flip waits for Lint & Repo Gates, TypeScript Type Check, Test Core (1/6) and Build Docs on their own rows.
Process disclosures accepted: one MCP search_issues for the dedupe with its control firing (REST /search is 403 here); branch not merged with origin/main (base 0e16fc454; no open PR touches content/docs/kernel/contracts/**; CI runs the merge ref).
Landing plan: flip point armed; on all-green → ready → auto-merge (squash); on MERGED → content probe on origin/main (firing: ### list / listNames and no diagnosed counterpart in the page; negative control: ### load / loadDiagnosed still present exactly once), strip pm:dispatched, clear assignee.
LANDED — PM seat domain:devx @ objectstack, session session_01Vbw3RPgdtqesx4azk9SbW8, 2026-09-06T11:43Z (stroke delayed by the seat's usage-limit pause 09:2xZ–11:30Z; the merge itself was on time).
PR #16261 (Fixes #16090) MERGED 2026-09-06T09:39:54Z as 31baf0944 on main via the merge queue. Content probe on re-fetched origin/maindacb73f4f: ### list / listNames = 1 and no diagnosed counterpart = 1 in content/docs/kernel/contracts/metadata-service.mdx; negative control: ### load / loadDiagnosed still exactly 1. Card closed by the merge; pm:dispatched stripped and assignee cleared in this stroke.
Activity
os-zhuang commentedon Sep 6, 2026
分诊 ·
domain:devx/documentation/priority:p3/pm:blocked分诊席位。⛔ 不认领、不派发、不写代码、不合并、不裁决 decision-box 卡。
读数取自
origin/main@a4816a7,2026-09-06T02:16Z。卡的三条读数,逐条复现
content/docs/kernel/contracts/metadata-service.mdxlistNames只出现两次(签名 + 一行示例):36接口签名、:103const names = await metadataService.listNames('object');### load / loadDiagnosed一节,开头即「Both read one item through the registered loaders」first-wins/collision/silently/duplicate/listed twiceload命中 10 ⇒ grep 起火)⇒ 「沉默不是虚假」成立,E3「已发布必修」确实不咬。卡对自己的判断是准的。
车道:
domain:devx落点是
content/docs/**⇒ devx 车道(content/docs/**、apps/docs、packages/lint、sdui-parser、scripts/gates)。⛔ 不因为它描述的是 metadata 服务就归 engine——domain:*是修复落地的包,不是主题。状态改为
pm:blocked(卡自己要求,但打了pm:queue)卡正文写着「⛔ This card should not land before #16086 does」,而 PR #16086 实测 open / draft / not merged(
mergeable_state: clean,head2aa6373d1,base9c3fda5f)。pm:queue会让派发把它当可派;改pm:blocked。Blocked-by: PR #16086。⚠️ 提醒历轮教训:hard serial 由合并解除,不由「已武装 / 已 arm」解除。#16086 是 governed draft +
needs:contract-review,等的是人工合并。卡请求的去重,做了 —— 两条都不是重复,但第二条有个真约束
卡说「⇒ If triage finds either already carries this gap, ⛔ close this as the duplicate」。读了两张:
#15626 —— 不是重复,而且它根本不是 issue,是一个 PR。
feat(spec)!: duration-shaped number keys carry their unit in the key name。它之所以成为卡的「控制命中」,是因为它改了content/docs/protocol/kernel/**metadata-service.mdx**—— 与本卡的content/docs/**kernel/contracts**/metadata-service.mdx是两个不同路径的同名文件。#15385 —— 不是重复(不同主张),但与本卡在同一个文件上串行。
它讲的是
MetadataManager.loadManyKeyed作为 public member 落地却没有IMetadataService声明,落点packages/spec/src/contracts/metadata-service.ts,已挂domain:spec/pm:queue/ p3。但它正文里有一句直接命中本卡:⇒ #15385 与 #16090 会写同一个文件。两者都执行时,
content/docs/kernel/contracts/metadata-service.mdx是 hard serial。先落的那个不必等,后落的必须 rebase 后重读该节再写。已在两张卡上互相标注。优先级:p3
页面无虚假陈述(上面已实测),只有沉默;且要补的内容里有一半(refusal 那一行)在 #16086 落地前还不是
main上的行为。⇒ p3。Generated by Claude Code
claude commentedon Sep 6, 2026
UNBLOCKED →
pm:queue— PM seatdomain:devx @ objectstack, sessionsession_01Vbw3RPgdtqesx4azk9SbW8, unlock scan at 2026-09-06T05:58Z on re-fetchedorigin/main@159dbad.Blocker: PR #16086 MERGED at 2026-09-06T03:25:31Z (hard serial released by the merge, per the triage note). Verified by content:
content/docs/kernel/contracts/metadata-service.mdxonorigin/mainstill mentionslistNamesonly at:36(signature) and:103(one example line), and its only failure-posture prose still opens "Both read one item" (:115) — the plural reads' two-outcome posture this card describes is undocumented, exactly as filed, and the behaviour it would document is now onmain.State transition in this stroke:
pm:blocked→pm:queue(one label write, read back). No claim, no dispatch yet — the card re-enters this lane's selection order; its file surface was re-verified on the merged ref above, and the dispatch brief will carry that reading rather than the card's original one.Generated by Claude Code
claude commentedon Sep 6, 2026
Claim: PM loop round 1
Session:
session_01Vbw3RPgdtqesx4azk9SbW8Branch:
claude/issue-16090-metadata-service-plural-read-postureWorktree:
objectstack-issue-16090Domain:
domain:devxFile surface:
content/docs/kernel/contracts/metadata-service.mdx(a passage beside### load / loadDiagnosedat:113describing the plural reads' two outcomes) (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus— no path-derived mandate; default tier because the passage documents engine behaviour that must be read from code (the per-loader degradation is older than #14921 and undocumented anywhere)Clause-②: no
Serial constraints cleared: no open PR touches
content/docs/kernel/contracts/**(all open PRs' file lists read 08:0xZ). Unblocked at 06:02Z (5557319372): PR #16086 (#14921) MERGED 03:25Z; the page onorigin/main@0e16fc45still mentionslistNamesonly at:36and:103and its only failure prose opens "Both read one item" (:115).Generated by Claude Code
claude commentedon Sep 6, 2026
os-dev-report
{ "issue": 16090, "status": "done", "branch": "claude/issue-16090-metadata-service-plural-read-posture", "pr": "https://github.com/objectstack-ai/objectstack/pull/16261", "premise_still_valid": true, "summary": "Added a `### list / listNames` subsection to content/docs/kernel/contracts/metadata-service.mdx, placed immediately after `### load / loadDiagnosed` so the singular and plural failure postures sit together. Addition only: the singular passage is byte-unchanged. The passage states the two plural outcomes from the code rather than from the card - degrade (a loader that cannot be read is reported once and skipped, the read resolves over the remaining loaders; `listDiagnosed` carries the degraded/errors verdict) and refuse (AmbiguousMetadataStemError propagates out of BOTH plural reads, ADR-0112 envelope, code AMBIGUOUS_METADATA_STEM, status 500, every colliding path named, sorted) - plus the collision's real conditions (flat files only, case-sensitive, registered extensions only, `.js` not in the default set, nested files exempt). Assignee was already set by the PM dispatch and was not touched; the newest Claim (5557874957) names this branch, so no second claim was posted.", "tests": "No test change: this is documentation of behaviour already on main (PR #16086, merged 2026-09-06T03:25Z), and the diff is one .mdx file. Verification is the gate family plus the code reads behind every sentence. GATES: 38 derived, 38 run, 0 NOT-MEASURED, 0 UNRUN, all exit 0 at HEAD 6dad5a168 - `node scripts/pm/dispatch-gates.mjs --changed --ran ran.txt --repo objectstack-ai/objectstack` printed its own verdict line: '✓ dispatch-gates --ran: 38 derived famil(ies) accounted for - 38 run, 0 NOT-MEASURED.' Exit codes captured by redirect-then-capture, never across a pipe. Own verdict lines quoted: 'check-doc-anchors: 313 internal #fragment link(s) across 408 source file(s) all resolve to a real heading'; 'check-docs-transcript-drift: 4 declared transcript value(s) across 403 page(s) under content/docs/ equal what the registry derives today, and no undeclared block quotes one.'; 'check-nul-bytes: OK (scanned 7960 text file(s) -- 7960 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).'; '✅ 228 generated files in sync with packages/spec'. PREREQUISITE: check:docs-transcript-drift first returned exit 3 'PREREQUISITE NOT MET ... @objectstack/lint is not built' - recorded as NOT MEASURED, not as a red, and re-run green after the build. BUILD: `pnpm --workspace-concurrency=2 --filter '@objectstack/spec...' --filter '@objectstack/lint...' --filter '@objectstack/client-react...' build` under scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-16090; first attempt returned 'VERDICT queue-timeout (exit 99) - never acquired - waited 540s ... holder pid 20402 ... pnpm check:pm-dispatch-gates' (NOT MEASURED; the interval was spent running the 29 build-independent gates), the resumed slot then returned 'VERDICT command-exit 0 - held the lock 394s'. ESLINT: repo-wide `pnpm lint` is not owed and that is measured, not skipped - every eslint.config.mjs config object scopes files to {ts,tsx,mts,cts,js,jsx,mjs,cjs}, `grep -c mdx eslint.config.mjs` is 0, and `eslint --no-inline-config --format json` on the changed file returns 1 result / 0 errors / 1 warning 'File ignored because no matching configuration was supplied.' - the diff touches no linted file, so no untouched file's verdict can move. CONTROL CHARS: `grep -naP` over the changed file returns no hits (exit 1) beyond the gate. NO ABLATION: nothing was mutated or reverted - no new gate or assertion was authored, so there is no failure mode to demonstrate.", "gates": { "head_sha": "6dad5a168", "derivation": "node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack (38 commands; identical to the dispatch's lead list)", "reconciliation": "node scripts/pm/dispatch-gates.mjs --changed --ran ran.txt --repo objectstack-ai/objectstack yields exit exit 0, '38 derived famil(ies) accounted for - 38 run, 0 NOT-MEASURED'", "all_exit_zero": true, "commands": [ { "command": "node scripts/check-ci-filter-parity.mjs", "exit": 0 }, { "command": "node scripts/check-closing-keyword-parity.mjs", "exit": 0 }, { "command": "node scripts/check-closing-keyword-parity.mjs --self-test", "exit": 0 }, { "command": "node scripts/check-comment-mask-corpus.mjs", "exit": 0 }, { "command": "node scripts/check-doc-frontmatter.mjs", "exit": 0 }, { "command": "node scripts/check-doc-frontmatter.mjs --self-test", "exit": 0 }, { "command": "node scripts/check-doc-route-spelling.mjs --advisory", "exit": 0 }, { "command": "node scripts/check-doc-route-spelling.mjs --self-test", "exit": 0 }, { "command": "node scripts/check-docs-section-name.mjs", "exit": 0 }, { "command": "node scripts/check-docs-section-name.mjs --self-test", "exit": 0 }, { "command": "node scripts/check-section-landing-index.mjs", "exit": 0 }, { "command": "node scripts/check-section-landing-index.mjs --self-test", "exit": 0 }, { "command": "pnpm check:corpus-claim-drift", "exit": 0 }, { "command": "pnpm check:cross-package-test-inputs", "exit": 0 }, { "command": "pnpm check:doc-anchors", "exit": 0 }, { "command": "pnpm check:doc-authoring", "exit": 0 }, { "command": "pnpm check:docs-audit-scope", "exit": 0 }, { "command": "pnpm check:docs-redirects", "exit": 0 }, { "command": "pnpm check:docs-single-h1", "exit": 0 }, { "command": "pnpm check:docs-transcript-drift", "exit": 0 }, { "command": "pnpm check:driver-memory-census", "exit": 0 }, { "command": "pnpm check:nul-bytes", "exit": 0 }, { "command": "pnpm check:published-readme-links", "exit": 0 }, { "command": "pnpm check:react-page-adapter-contract", "exit": 0 }, { "command": "pnpm check:refd-timer-probe", "exit": 0 }, { "command": "pnpm check:role-word", "exit": 0 }, { "command": "pnpm check:skill-identifier-liveness", "exit": 0 }, { "command": "pnpm check:vendor-version-stamps", "exit": 0 }, { "command": "pnpm check:watch-hint-literal", "exit": 0 }, { "command": "pnpm --filter @objectstack/lint run check:doc-formula-expressions", "exit": 0 }, { "command": "pnpm --filter @objectstack/lint run check:doc-security-posture", "exit": 0 }, { "command": "pnpm --filter @objectstack/spec run check:docs", "exit": 0 }, { "command": "pnpm --filter @objectstack/spec run check:empty-state", "exit": 0 }, { "command": "pnpm --filter @objectstack/spec run check:liveness", "exit": 0 }, { "command": "pnpm --filter @objectstack/spec run check:skill-examples", "exit": 0 }, { "command": "pnpm --filter @objectstack/spec run check:strictness-ledger", "exit": 0 }, { "command": "pnpm --filter @objectstack/spec run check:variant-docs", "exit": 0 }, { "command": "pnpm --filter @objectstack/spec run check:yaml-examples", "exit": 0 } ] }, "files_changed": [ "content/docs/kernel/contracts/metadata-service.mdx" ], "line_budget": "n/a", "mcp_calls": "2 - one search_issues (dedupe for the finding, after repo-scoped REST /search/issues answered 403) and one issue_write (filing #16255). Everything else went through repo-scoped REST: issue body, comments, PR create, label write + read-back, PR body read-back, and this report comment.", "deviations": [ "CHANNEL SWITCH (declared): repo-scoped REST reads work in this container (issue, comments, PR, labels all HTTP 200), but GET /search/issues answers HTTP 403 for both the probe query and its control. Per the dedupe rule the fallback was one targeted MCP search_issues; its control fired (returned #15385 and the page-adjacent docs cards), so the zero-hit reading for the finding is a real reading.", "FILE-SURFACE NOTE (declared): the dispatch located the work as 'a passage beside `### load / loadDiagnosed` (:113)'. One further line was added inside the same declared file but outside that passage - `listDiagnosed?` in the `## Interface Definition` fence - because the new prose names a member the fence omitted. One mechanical line copied from packages/spec/src/contracts/metadata-service.ts:410-412. The wider drift it exposed was NOT repaired; filed as #16255.", "A2 - WHAT WAS ACTUALLY MEASURED, beyond the card: the degrade half is a clean 'continue over the remaining loaders' for both plural reads, so the card's row holds. But the two reads are NOT symmetric in what they report: `list` records the loss and offers it through `listDiagnosed` ({items, degraded, errors}); `listNames` has NO diagnosed counterpart at all (`listNamesDiagnosed` occurs zero times in the repo) - a short name set is not distinguishable by its caller, and the lost loader reaches the server log and nowhere else. The passage documents that asymmetry; the card did not name it.", "A1 CONFIRMED verbatim: AMBIGUOUS_METADATA_STEM_CODE at ambiguous-metadata-stem.ts:52, AMBIGUOUS_METADATA_STEM_STATUS = 500 at :63, exported with the error class and the predicate from packages/metadata/src/index.ts:28-33. The pins gave the exact conditions the passage now states (flat files only, case-sensitive, only registered extensions - `.js` is NOT in the default set - and a nested file sharing a flat basename is not a collision).", "A3 CONFIRMED on the fresh tree: the page at :113-125 still opens 'Both read one item through the registered loaders' and still states the null collapse; the new section was placed after it, and it was not rewritten.", "NOT CLAIMED (Zone 1 ruling 3): the passage says nothing about what the SINGULAR read does on an ambiguous tree. No pin asserts it and `findFile` is a different code path (extension precedence, no call into resolvableNames), so it was left unwritten rather than reasoned into the docs.", "NOT MERGED with origin/main: the branch sits on its dispatch base 0e16fc45; origin/main has since moved to 77781151d. dispatch-gates reports that none of the commits this tree can see touched what the gate derivation depends on, and no open PR touches content/docs/kernel/contracts/**. The PR's own CI runs against the merge ref." ], "open_questions": [], "out_of_scope_findings": [ "filed as #16255: the `## Interface Definition` fence on metadata-service.mdx under-declares IMetadataService - getDiagnosed, loadMany, matchEndpoint and subscribe are declared in packages/spec/src/contracts/metadata-service.ts and absent from the page (38 members in the source vs 34 on the page after this PR adds listDiagnosed; drift is one-directional). Filed unassigned and unlabelled for triage; whether the fence is a contract mirror that a gate should hold equal, or an excerpt that should say so, is deliberately left to triage.", "not filed, recorded here: #15385 remains open and writes this same file if its option 1 lands - a hard serial across #15385, #16090 and #16255, already noted on the cards." ] }Generated by Claude Code
claude commentedon Sep 6, 2026
ACCEPT — PR #16261 (head
6dad5a168, read 2026-09-06T08:29Z)Review checklist (read from GitHub and
origin/main, not from the report):main, body first lineFixes #16090; merge should close the card. No other issue number next to a closing keyword (docs: the## Interface Definitionblock on metadata-service.mdx under-declaresIMetadataService— 4 members declared in the contract source are absent from the page's listing #16255, fix(metadata): refuse an ambiguous metadata stem with both paths named, instead of listing one name twice and serving it by extension precedence #16086,MetadataManager.loadManyKeyedlands as a public member with noIMetadataServicedeclaration — its two siblingsloadMany?andloadDiagnosed?are declared, and the call site narrows with a local structural type instead #15385 appear in non-closing form).content/docs/kernel/contracts/metadata-service.mdx(+46/−0). NOT governed (predicate 0/1). Docs-only ⇒skip-changesetpresent. Nocontent/docs/releases/change. Addition only: the singular### load / loadDiagnosedpassage is byte-unchanged, and the new### list / listNamessection sits directly after it.listDiagnosed?(type)added to the## Interface Definitionfence because the new prose names it — copied frompackages/spec/src/contracts/metadata-service.ts(member present onorigin/main77781151d). The wider fence drift the dev noticed was not repaired here and is filed as docs: the## Interface Definitionblock on metadata-service.mdx under-declaresIMetadataService— 4 members declared in the contract source are absent from the page's listing #16255 (unlabelled, triage's).listDiagnosedcarriesdegraded/errors) and refuse (AmbiguousMetadataStemErrorre-raised by both reads, ADR-0112 envelope). PM spot-check onorigin/main77781151d:AMBIGUOUS_METADATA_STEM_CODEatpackages/metadata/src/loaders/ambiguous-metadata-stem.ts:52,AMBIGUOUS_METADATA_STEM_STATUS = 500at:63— the doc's code and status are the shipped ones. The asymmetry the doc adds ("listNameshas no diagnosed counterpart") checked as a zero-hit with a control:listNamesDiagnosedmatches 0 files underpackages/, while the controllistDiagnosedmatches the spec contract. The singular read's behaviour on an ambiguous tree is left unwritten, as ruled.6dad5a168, reconciled bydispatch-gates --ran(0 NOT-MEASURED); the one prerequisite miss (check:docs-transcript-drift, lint unbuilt) was recorded and re-run green after the build under the lock (OS_VERIFY_LOCK_SLOT=issue-16090, one exit-99 spent on lock-free gates, then held 394s). CI at head: 16 success / 10 skipped / 7 in progress. Flip waits forLint & Repo Gates,TypeScript Type Check,Test Core (1/6)andBuild Docson their own rows.search_issuesfor the dedupe with its control firing (REST/searchis 403 here); branch not merged withorigin/main(base0e16fc454; no open PR touchescontent/docs/kernel/contracts/**; CI runs the merge ref).MetadataManager.loadManyKeyedlands as a public member with noIMetadataServicedeclaration — its two siblingsloadMany?andloadDiagnosed?are declared, and the call site narrows with a local structural type instead #15385 (open, other route) writes this same file if its option 1 lands — hard serial acrossMetadataManager.loadManyKeyedlands as a public member with noIMetadataServicedeclaration — its two siblingsloadMany?andloadDiagnosed?are declared, and the call site narrows with a local structural type instead #15385 / docs: the metadata-service contract page documents the SINGULAR read's failure posture but is silent on the plural reads' (list / listNames) — degrade vs refuse #16090 / docs: the## Interface Definitionblock on metadata-service.mdx under-declaresIMetadataService— 4 members declared in the contract source are absent from the page's listing #16255, already recorded on those cards.Landing plan: flip point armed; on all-green → ready → auto-merge (squash); on MERGED → content probe on
origin/main(firing:### list / listNamesandno diagnosed counterpartin the page; negative control:### load / loadDiagnosedstill present exactly once), strippm:dispatched, clear assignee.Generated by Claude Code
claude commentedon Sep 6, 2026
LANDED — PM seat
domain:devx @ objectstack, sessionsession_01Vbw3RPgdtqesx4azk9SbW8, 2026-09-06T11:43Z (stroke delayed by the seat's usage-limit pause 09:2xZ–11:30Z; the merge itself was on time).PR #16261 (
Fixes #16090) MERGED 2026-09-06T09:39:54Z as31baf0944onmainvia the merge queue. Content probe on re-fetchedorigin/maindacb73f4f:### list / listNames= 1 andno diagnosed counterpart= 1 incontent/docs/kernel/contracts/metadata-service.mdx; negative control:### load / loadDiagnosedstill exactly 1. Card closed by the merge;pm:dispatchedstripped and assignee cleared in this stroke.Generated by Claude Code
feat(spec): declare IMetadataService.loadManyKeyed beside its plural-…