Skip to content

refactor(metadata-protocol): move the sealed overlay boot report out of protocol.ts into protocol-sealed-overlay-report.ts (#25467 cut 21) - #26404

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-ybjm3h
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-ybjm3h

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Part of #25467
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6270097840

Cut 21 of the protocol.ts split (#24586 ruling A, recorded in 6270097840). Cuts 1–20 landed as #25494, #25518, #25538, #25589, #25601, #25623, #25642, #25719, #25731, #25740, #25797, #25939, #25972, #26014, #26104, #26168, #26264, #26317, #26350 and #26366. The claim is 6431131976. It took the cut 21 proposal of the cut 20 report (6430425388): the pair findServedOverlayRow + reportSealedOverlayRows, with the fallback that a member which fails the screen stays and the other moves alone.

This cut moves reportSealedOverlayRows alone. Both members passed the AST screen. Moving findServedOverlayRow was built and measured in full, and it would have taken three read seams out of check-durability-degradation-log-level's census (82 → 79) while the gate stayed green. That loss is not move-only, so findServedOverlayRow stays in protocol.ts. The section "Why findServedOverlayRow stays" has the numbers.

One ObjectStackProtocolImplementation member moves unedited into a new module, packages/metadata-protocol/src/protocol-sealed-overlay-report.ts (#23050 shape B):

  • reportSealedOverlayRows: the boot warning [metadata_sealed_overlay_unserved]. It names the stored environment sys_metadata rows that no read serves because they overlay sealed managed content (ADR-0131 D6): per type, a capped sample of names, and the two remedies.

protocol.ts loses 20 net lines (29,210 to 29,190 before the merge of main; 29,213 to 29,193 after it). The cut is move-only, and the card stays open for later cuts.

Branch claude/project-thread-ybjm3h is the harness-pinned branch for this thread. It was restarted from main 61c079862a after cut 20 merged. It names no issue, so the claim comment is the identity record. It holds the move commit be992bfe9d, the merge of main e931621a1 (ce32d8d5be), and the merge of main abb9046057 (f488ca9c46). The second merge brought in #26244, which edits packages/metadata-protocol/ (seedFile); it auto-merged in protocol.ts, far from the stub, and every reading after it is labelled "merged" below.

Why findServedOverlayRow stays

check-durability-degradation-log-level's read-seam rule finds a try that guards a storage read. It recognises a read by vocabulary (find/findOne/count) or through a same-file wrapper up to MAX_READ_WRAPPER_DEPTH = 2 hops. findServedOverlayRow is such a wrapper: its body reads through this.findEnvironmentRow(...). Three try blocks guard calls to it, and the gate counts each as a read seam:

packages/metadata-protocol/src/protocol.ts  guards findServedOverlayRow() in readMetaItem()  → no invented answer
packages/metadata-protocol/src/protocol.ts  guards findServedOverlayRow() in readMetaItem()  → no invented answer
packages/metadata-protocol/src/protocol.ts  guards findServedOverlayRow() in getMetaItemLayered()  → no invented answer

With the pair moved (a full build, the AST compare at 0 differ, the package suite 21,568/21,568 and all ten hand gates green), the stub's body is a .call into another file, so the wrapper hop ends there. The gate still exited 0, but its --list lost exactly those three lines and its verdict went from 82 read seam(s) to 79 read seam(s). Moving reportSealedOverlayRows alone leaves the census at 82, with an identical --list once [@:][0-9]+ is stripped.

So moving findServedOverlayRow waits for one of two things: the gate following an imported wrapper across files, or a ruling that accepts the census loss. Either is a gate decision, outside this move-only card. The cut 22 proposal on #25467 lists the members that a census trial showed to be neutral.

Serial with the file's in-flight PRs

Measured on the pre-merge head, of the 21 open PRs then, one edited protocol.ts: #26244. Its git merge-tree was clean against this head and against origin/main alone. It has since merged, and this branch merged it in (f488ca9c46, auto-merged, no conflict).

Measured again just before this PR was opened, against the merged head f488ca9c46: of the 29 open PRs, none edits protocol.ts and none names reportSealedOverlayRows. Two touch packages/metadata-protocol/src/ test files: #26337 (3 files) and #26138 (7 files). Each conflicts with this head only where it already conflicts with origin/main alone. No open PR edits the new module.

The order's screen (private statics, new Error()

Both members of the pair passed:

  • No private-static read. The AST walk found no access on the ObjectStackProtocolImplementation identifier and none on this.constructor.
  • No error constructed. Neither body has new Error(, so no PRODUCERS row is owed.

What moves

Member Base line Visibility Body lines AST tokens (params + return + body)
reportSealedOverlayRows :28657 private 26 208

The shape (#23050 shape B, as cuts 1–20 apply it)

  • The class keeps the member. It keeps its name, docblock, signature and position. Its body now delegates, marked [#25467 cut 21].
  • No host. The body reads no member of the class (AST walk over this accesses, with as casts, parentheses and ! stripped, and bare this checked). So the stub is return reportSealedOverlayRows(rows);, a plain call, as namesWithOwnStoredRow's stub is in protocol-stored-overlay-reads.ts (cut 16). No member changes visibility.
  • The caller still calls the class method. The one this.reportSealedOverlayRows(sealedOverlays) site (:28549 at base) does not change.

Module-level declarations, by reader

An AST walk over the body's identifiers, against protocol.ts's top-level declarations and import bindings, finds none. The body uses only console.warn, Map and Array.from. The new module imports nothing, nothing gains export, and no import of protocol.ts changes. The one new statement in protocol.ts is the import of the new module.

The durability --list

check-durability-degradation-log-level exits 0 at both ends. Its --list is identical once [@:][0-9]+ is stripped (158 lines; 82 read seam(s) at both ends, and no entry changes file). The moved body has no try, no catch and no engine call. Its console.warn is not a catch handler, so the log-level rule does not grade it. After the merge of main, the verdict still reads 82 read seam(s).

The published .d.ts

The series bar is ruling 6348891160: sorted-union identity, measured first, with raw identity reported when it holds. Both hold for this cut.

Base 61c079862a and head be992bfe9d were built with pnpm --filter @objectstack/metadata-protocol build against one dependency tree. The closure was built at head by a full pnpm build (119/119). For the base build, the package's src/ was swapped tree-only to base and restored from HEAD under a trap. Afterwards git diff HEAD was empty, git status was clean, and both touched blobs equalled HEAD's.

  • Normalised sha256 (the ruling's bar): f7f9306ed46a5605c5e4c59bbc212a7d5288061760ec85d473d2f3047a836ba5 at base and at head, on dist/index.d.ts and dist/index.d.cts. The normaliser sorted 80 string-literal union runs. Sorted-union identity holds.
  • Raw sha256: 1b134685a474f83cfa85a743e49a811d607fcaeed53ae8856dd62db65ad132f5 at base and at head, 776,660 bytes. Raw identity holds. (Cut 20's getDiscovery union reorder did not recur.)
  • Again after the merge, against the main it merged. feat(spec,metadata-protocol,service-storage,runtime,cli)!: a seed record can carry a file the package ships, seedFile(path) #26244 changed this package's published surface, so the merged tree was measured against main abb9046057, not against 61c079862a. The closure was rebuilt by a full pnpm build (119/119) on the merged tree f488ca9c46; the main leg is the package src/ swapped tree-only to abb9046057 and restored from HEAD under a trap. Both give raw 059fa1189ac13c65c36d3718584495bebaa48cc545e1526adb37cd95ef7dfaa3 (777,645 bytes) and normalised 0a7694d518b570981c5cdcbcdd8fcb2dec9f1c7d74a7287299fe9e19e32a4183, on .d.ts and .d.cts. Both identities hold on the merged tree too.
  • The two builds differ where they should.
    • index.js hashes 7be270d93afd5f1e… at base and f109362a448bc4d8… at head (index.cjs b9cafecb264ca263… → 48b8964ac889f026…).
    • function reportSealedOverlayRows( appears 0 times in the base bundle and once in the head bundle. On the merged pair, index.js is 94affd7c88eb31be… on main and dbff5f6c5cde69ca… merged, with the same 0 → 1.
  • Positive control on the head tree. private was dropped from reportSealedOverlayRows on disk (restored from HEAD under a trap) and the package rebuilt. Both .d.ts files move to f631a07ad5d36220… (normalised c2f3625e27675e2a…, which differs from the bar's f7f9306e…) and print reportSealedOverlayRows(rows: ReadonlyArray<{ type: string; name: string; }>): void;. So the bar can see a change on this member.
  • Export tables. The ESM and CJS bundles, loaded at runtime from the package, each export the same 78 names at base, head and merged. The sorted list hashes to 9f18a0177ac736ed… on all of them. The moved function does not appear.

No changeset: skip-changeset, per ruling 6348891160.

Proof of move-only

Body, by AST token. The compare reads the TypeScript compiler's AST leaf tokens, with JSDoc skipped, over the parameters, the return type and the body. It compares base protocol.ts at 61c079862a against the head module:

reportSealedOverlayRows: base tokens 208, head tokens 208, differ 0, text-equal(ws-collapsed, comments incl.) true
TOTAL tokens=208 differ=0 members=1
CLASS members base=301 head=301 order-same=true byte-identical=300 stubs-with-identical-docblock+signature=1 other=[]
TOP base=249 head=250 byte-identical=249

The same compare against origin/main abb9046057 and the merged head gives the same four lines, so the merge added nothing to the cut.

Negative control. One token was changed in an in-memory copy of the head module: SAMPLE_PER_TYPE = 5 became SAMPLE_PER_TYPE = 6.

MUTATED packages/metadata-protocol/src/protocol-sealed-overlay-report.ts "SAMPLE_PER_TYPE = 5" -> "SAMPLE_PER_TYPE = 6"
reportSealedOverlayRows: base tokens 208, head tokens 208, differ 1, text-equal(ws-collapsed, comments incl.) false
TOTAL tokens=208 differ=1 members=1
  • Class side. There are 301 members at base and at head, in the same order. The 300 that did not move are byte-identical to base, leading comments included. The stub sits at its base position and keeps its docblock and signature byte-identical.
  • Top level of protocol.ts. All 249 base statements are byte-identical, leading comments included. Head adds one statement, the new module's import.
  • Size. +60 / −24 over 2 files, 84 changed lines. protocol-sealed-overlay-report.ts has 56 lines.

Readers of the moved lines

The tests and scripts that read protocol.ts by path (33 files that name protocol.ts and call readFileSync) were grepped for the member name and for its body's text: SAMPLE_PER_TYPE, metadata_sealed_overlay_unserved, sealed against overlays, linkage-free clone. So were docs/qa/ and the @objectstack/spec liveness ledgers.

  • By path: none reads the moved body. The one textual hit is the gitignored, build-written packages/spec/src/migrations/registry.ts, whose migration prose names the log code; it reads no file.
  • Behaviour through the class method, no edit owed. protocol.reads-environment-only.test.ts (metadata-protocol) and packages/objectql/src/protocol-object-overlay-layer.test.ts assert the [metadata_sealed_overlay_unserved] warning through boot. The first passes at both ends in the package suite.
  • The liveness ledgers. No row points at reportSealedOverlayRows. Carried forward: manifest.json's dependencies row → protocol.ts#resolveWritePackageScope (cut 19) and seed.json → protocol.ts#applySeedBodies (cut 14) still quote lines that moved to a seam module. check:liveness checks only that the #symbol is named in the file, and both stubs keep it. Re-pointing them is a packages/spec/** edit, outside this card.
  • ADR anchors. check-adr-anchors exits 0 at both ends. The warning text's (ADR-0131 D6) moves out of the anchored protocol.ts, so the decision-letter count goes 1,877 → 1,876. The citation count goes 59,313 → 59,314 and the file count 8,371 → 8,372 (the new module's header names ADR-0131 D6 once more). The moved line is the warning's text, not an anchor entry paragraph's mechanism, so no anchor entry is owed (as in cuts 8 and 12). The protocol.ts anchor's governing ADRs are all still referenced.
  • Engine-double contract (check-engine-double-contract): exits 0 at both ends. The updateData/deleteData seams keep their entries at new line numbers (13896 → 13898, 13921 → 13923, from the two import lines). productionFiles is 3,751 at base and 3,753 at head: the new file, and the gitignored registry.ts the spec build writes in the head tree (as in cut 20).
  • Identical output at both ends: check-adr-symbol-anchors, check-system-context-census, check-tenant-audit-census, check-query-options-erasure-ratchet, check-filter-alias-parity and check-durability-degradation-log-level. check-nul-bytes differs only in the scanned file count, +1. The base-end readings were taken in a clean detached worktree at 61c079862a.

Verification

  • tsc --noEmit in packages/metadata-protocol exits 0 at head and on the merged tree.
  • pnpm exec eslint --no-inline-config packages/metadata-protocol exits 0 with no output (the whole touched package, with the root lint script's flags).
  • packages/metadata-protocol suite, base and head, compared per test name and status. Each leg ran vitest --maxWorkers=2 with the JSON reporter under the shared verify lock.
    • Base 61c079862a: 21,568 tests (21,549 passed, 0 failed, 19 skipped), 270 files.
    • Head be992bfe9d: 21,568 tests (21,549 passed, 0 failed, 19 skipped), 270 files.
    • None lost, none added, none changed status. The base leg is the package src/ swapped tree-only from 61c079862a (the new module removed) and restored from HEAD under a trap.
    • Again after the merge, since feat(spec,metadata-protocol,service-storage,runtime,cli)!: a seed record can carry a file the package ships, seedFile(path) #26244 changed the package: main abb9046057 (the same tree-only swap) and the merged head f488ca9c46 both give 21,584 tests (21,565 passed, 0 failed, 19 skipped), 271 files; 0 lost, 0 added, 0 changed.
  • check-platform-checklist --changed-from origin/main (merged tree): OK. 1 anchored file is in the change set, 29 anchors were judged, and 0 were lost.
  • Governed surface. check-governed-merges --branch claude/project-thread-ybjm3h finds 0 of 2 paths in the register (13 surfaces), so the PR is NOT governed. It has 84 changed lines, under the 3,000-line human-merge bar.
  • Derived gates. dispatch-gates --commands --repo objectstack-ai/objectstack derives 51 gates on 2 changed paths from the merged tree f488ca9c46, with no stale-tree warning; all 51 exit 0 after the full pnpm build (119/119). dispatch-gates --ran: 51 derived, 51 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. (On the first merge, ce32d8d5be, the same 51 also exited 0, but the derivation flagged scripts/engine-double-contract.pinned.json as stale against main; main was merged again and everything after the merge was re-run.)
  • check-durability-degradation-log-level --list on the merged tree: 82 read seam(s).
  • When this PR was opened, main was 6 commits past the merge. dispatch-gates names 10 changed files there (workflow files and scripts/ci/** self-tests). None of them is named by any of this cut's 51 derived gate commands, and none is under packages/metadata-protocol/, so main was not merged a third time.

Carry-forward

These stay in protocol.ts:

  • New this cut: the read-seam members. A member that appears in a protocol.ts read-seam entry of check-durability-degradation-log-level --list (guards X() in Y()), as the guarded wrapper X or as the guarding member Y, takes a seam out of the census when it moves. It stays until the gate follows imported wrappers or a ruling accepts the loss. A census trial (each body moved alone to a scratch module, the gate re-run) measured findServedOverlayRow 82 → 79, packagelessAddress 82 → 78, and unmanagedImportCollisions, rollBackUnmanagedImport, readRevertableCommit and applyObjectRegistryMutation 82 → 81 each.
  • Public members whose return type is inferred.
  • The bodies kept by a reader: probeRecord, getMetaItemCached, findReferencesToMeta, refuseUnmintableMetaType, metaTypeNamespaceExists, viewContainerNameCollisionRefusal with overlaidShippedContainerViewNames, reportPageRequiresAbsentAtLoad, the 7 applyRegistryWriteThrough callers, readCodeLayerForCarryForward and ensureObjectStorage.
  • The private-static readers: refusePackagedBaseRemoval, packagedBaseRefusal, runPublishSideEffects, promoteDraftForPublish, publishPackageDrafts and discardPackageDrafts.
  • The field writers: warnClosureReadUnavailable.

🤖 Generated with Claude Code

https://claude.ai/code/session_016SP1spj613tyGCgxoD1An6

…of protocol.ts (#25467 cut 21)

Move the body of reportSealedOverlayRows unedited into
protocol-sealed-overlay-report.ts. It reads no member of the class, so
the method's stub calls the plain function directly. The class keeps the
method, its docblock and declared signature; its one caller keeps
calling the class method.

Claude-Session: https://claude.ai/code/session_016SP1spj613tyGCgxoD1An6
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 55 of 207 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json a66c9ff423f49afe1dadfff271a1152f8cdd60fa → packageMentionDocs.

Which tree this was computed on

This run read content/docs from f30799bc13d50462fa13fac3c8c0226f31579e77 — the merge of head f488ca9c464c6e91febc40c5501f572e11429342 into base a66c9ff423f49afe1dadfff271a1152f8cdd60fa, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin f30799bc13d50462fa13fac3c8c0226f31579e77 && git checkout f30799bc13d50462fa13fac3c8c0226f31579e77
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a66c9ff423f49afe1dadfff271a1152f8cdd60fa f488ca9c464c6e91febc40c5501f572e11429342 && git checkout -B drift-repro a66c9ff423f49afe1dadfff271a1152f8cdd60fa && git merge --no-ff f488ca9c464c6e91febc40c5501f572e11429342

node scripts/docs-audit/affected-docs.mjs --json a66c9ff423f49afe1dadfff271a1152f8cdd60fa

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · domain:server seat 1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC · 2026-10-27T02:42Z

Head f488ca9c46, report 6432160640 (on #25467), claim 6431131976. No contract review is owed, because there is no packages/spec/** diff and no narrowing.

item reading
Shape Draft, base main, Part of #25467 (cut 21), skip-changeset (no published behaviour moves).
Files 2 files, +60/−24. reportSealedOverlayRows moves into protocol-sealed-overlay-report.ts as a plain function (it reads no member), and its class stub calls it directly, as in cuts 3 and 16. Its one caller still calls the class member. Not governed.
Scope Narrowed under the claim's own fallback. findServedOverlayRow passed the AST screen, but moving it dropped check-durability-degradation-log-level's read-seam census from 82 to 79 while the gate stayed green. The gate follows only same-file wrappers, so the three try blocks guarding it in readMetaItem and getMetaItemLayered drop out of view. That is a coverage loss, not a move, so the member stays in the class. The seat's call: it stays there. No gate change is ordered for it, and the series moves on to members whose census stays at 82.
Fix A pure move. Seat probe at fresh origin/main: all 24 removed protocol.ts lines reappear in the new module, identical once the class indent is stripped, 0 missing. Dev: AST compare 0 differ (the negative control gives 1).
.d.ts bar Ruling 6348891160: both raw and sorted-union identity hold, base→head and main→merged.
Order questions No body constructs an error, so no PRODUCERS row is owed. The durability --list is identical at 82. check-adr-anchors decision letters go 1,877 → 1,876 (the warning's ADR-0131 D6 text moved), and the gate exits 0; disclosed, accepted as a move.
Evidence 35 check runs succeeded and 16 were expected skips on this head, 0 failed; mergeable clean. The suite ran 21,568 of 21,568 before the merge and 21,584 of 21,584 against main after #26244 was merged in. Derived gates: 51 of 51 exit 0, and --ran reconciles. No other open PR touches protocol.ts.
Carried The liveness pointer disclosure stays for the series' close-out. Cut 22 goes to the same thread once this lands: resolveRowlessExpandedView (:10306) paired with expandUnderOwnName (:18159); both keep the census at 82 with an identical list.
Landing 「按照 pm-dispatch 规则复核过的都授权」 (Leehom, 2026-10-25T11:35Z): ready and auto-merge through the relay, once every check on this head is green or an expected skip.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 02:43
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 02:43
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit dada291 Oct 27, 2026
52 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-ybjm3h branch October 27, 2026 03:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants