Skip to content

docs(kernel): document the plural reads' failure posture (degrade vs refuse) on the metadata-service contract page - #16261

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-16090-metadata-service-plural-read-posture
Sep 6, 2026
Merged

docs(kernel): document the plural reads' failure posture (degrade vs refuse) on the metadata-service contract page#16261
baozhoutao merged 1 commit into
mainfrom
claude/issue-16090-metadata-service-plural-read-posture

Conversation

@claude

@claude claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #16090

What changed

One file, addition only. content/docs/kernel/contracts/metadata-service.mdx gains a ### list / listNames subsection placed immediately after ### load / loadDiagnosed, so the singular and the plural failure postures sit side by side and a reader can see why load answers null while listNames either degrades or refuses. The singular passage is untouched — the card's own framing is that silence is not falsehood, so nothing here rewrites what was already correct.

Also in the same file and the same claim: listDiagnosed? is added to the page's ## Interface Definition fence, because the new prose names it and the fence omitted it. Declared explicitly rather than slipped in — see Scope below.

Every claim in the passage, and where it was measured

Read off origin/main @ 0e16fc45 (this branch's base); the card's table was used as a lead, never transcribed.

the passage says measured at
A loader that cannot be read is reported once and skipped; the read resolves over the remaining loaders MetadataManager.readListUncached (packages/metadata/src/metadata-manager.ts:1172-1190) and listNames (:1627-1645) — both catch per loader and call the shared reportLoaderReadFailure; list's own docblock: "a loader that cannot be read is reported once and skipped … so this resolves with what the reachable loaders hold rather than throwing"
The caller still gets an array, nothing 500s, the set is quietly short listNames' docblock (:1608-1611), which grades the log level on exactly that sentence
listDiagnosed returns the same items plus degraded / errors; degraded is true when at least one loader could not be read; the set is known-partial, never empty and never wrong MetadataManager.listDiagnosed (:1010-1014) and the contract TSDoc in packages/spec/src/contracts/metadata-service.ts:378-412
listNames has no diagnosed counterpart measured, not quoted: listNamesDiagnosed occurs zero times in the repo; IMetadataService declares listNames with no diagnosed twin (:435-440)
Refusal: AmbiguousMetadataStemError propagates out of both plural reads the two re-raise sites, readListUncached (:1183) and listNames (:1638), each guarded by isAmbiguousMetadataStemError; pinned by filesystem-loader-ambiguous-stem.test.ts PIN 1 ("MetadataManager.listNames() PROPAGATES rather than absorbing" / "MetadataManager.list() propagates too")
Envelope: code AMBIGUOUS_METADATA_STEM, status 500, plus type, stem, and every colliding path, sorted packages/metadata/src/loaders/ambiguous-metadata-stem.ts:52 (code), :63 (status, with its own reason for 500 rather than 503 or 4xx), :76-105 (fields); pinned in PIN 1
Name derivation: extension stripped from a flat file's basename; the loser is listed but unreachable FilesystemLoader.nameFromFilename / resolvableNameForPath / resolvableNames (filesystem-loader.ts:549-627) and the module doc on ambiguous-metadata-stem.ts
Case-sensitive; registered extensions only (.js not in the default set); a nested file sharing a flat basename is not a collision resolvableNameForPath's "Case-SENSITIVE on purpose" comment (:570-574); the two CONTROL cases in filesystem-loader-ambiguous-stem.test.ts ("dual.json + dual.js is NOT ambiguous under the default set", "a NESTED file sharing a flat name is not a collision")
isAmbiguousMetadataStemError is importable by a consumer exported from packages/metadata/src/index.ts:28-33 alongside the code and status constants

Premise re-check (A3): still valid

The page at :113-125 still opens "Both read one item through the registered loaders" and still states the null collapse. The passage was placed beside it, not over it.

What the code measured that the card did not say

The card's table has one row for degrade. The code has an asymmetry inside that row, and the passage documents it: list records the degradation and offers it through listDiagnosed, while listNames has no diagnosed counterpart at all — its lost loader reaches the server log and nothing else. That is a fact about the contract a plural-read caller has to know, and it is why the passage is prose plus a table rather than the table alone.

Two things deliberately not claimed: what the singular load does on an ambiguous tree (no pin asserts it, and findFile is a different code path — unmeasured, so unwritten), and any totality claim for the plural reads beyond what the seams above do.

Gates

38 derived, 38 run, 0 NOT-MEASURED, 0 UNRUN — reconciled mechanically, not by hand:

node scripts/pm/dispatch-gates.mjs --changed --ran ran.txt --repo objectstack-ai/objectstack
✓ dispatch-gates --ran: 38 derived famil(ies) accounted for — 38 run, 0 NOT-MEASURED.

All 38 exited 0 at HEAD 6dad5a168, exit codes captured before any pipe. Sample verdict lines: 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) … equal what the registry derives today; check-nul-bytes: OK (scanned 7960 text file(s) … no raw ASCII control bytes); ✅ 228 generated files in sync with packages/spec.

check:docs-transcript-drift first answered PREREQUISITE NOT MET (exit 3 — NOT MEASURED, @objectstack/lint unbuilt); it is green above after pnpm --workspace-concurrency=2 --filter '@objectstack/spec...' --filter '@objectstack/lint...' --filter '@objectstack/client-react...' build under the shared verify lock (VERDICT command-exit 0).

Repo-wide pnpm lint is not owed here, and that is a measurement rather than a skip. eslint has no configuration matching this file: every config object in eslint.config.mjs scopes files to {ts,tsx,mts,cts,js,jsx,mjs,cjs}, the literal string mdx occurs 0 times in it, and eslint --no-inline-config --format json content/docs/kernel/contracts/metadata-service.mdx returns 1 result, 0 errors, and the single warning "File ignored because no matching configuration was supplied." Since the diff touches no linted file at all, no untouched file's verdict can move.

Scope


Generated by Claude Code

…ata-service contract page

`content/docs/kernel/contracts/metadata-service.mdx` documented the SINGULAR
read's failure posture (`### load / loadDiagnosed`: `load` collapses "no loader
has this item" and "every loader failed" into one `null`) and said nothing
about the plural reads'. `list` / `listNames` have two distinct outcomes and
the page distinguished neither:

- degrade — a loader that cannot be read is reported once and skipped, the read
  resolves over the remaining loaders (`MetadataManager.readListUncached`,
  `listNames`); `listDiagnosed` carries the `degraded`/`errors` verdict and
  `listNames` has no diagnosed counterpart at all;
- refuse — `AmbiguousMetadataStemError` propagates out of both plural reads
  (ADR-0112 envelope, `AMBIGUOUS_METADATA_STEM`, status 500, every colliding
  path named).

Addition only: the singular passage is unchanged and the two postures now sit
side by side. `listDiagnosed?` is added to the page's interface listing because
the new prose names it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 6, 2026
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Sep 6, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 6, 2026 08:54
@baozhoutao
baozhoutao enabled auto-merge September 6, 2026 08:54
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit 31baf09 Sep 6, 2026
37 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-16090-metadata-service-plural-read-posture branch September 6, 2026 09:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants