Skip to content

refactor(metadata-protocol): move the row-less expanded view read and the own-name container expansion out of protocol.ts into protocol-view-expansion-names.ts (#25467 cut 22) - #26449

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

objectstack-fleet[bot] merged 2 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 22 of the protocol.ts split (#24586 ruling A, recorded in 6270097840). Cuts 1–21 landed as #25494, #25518, #25538, #25589, #25601, #25623, #25642, #25719, #25731, #25740, #25797, #25939, #25972, #26014, #26104, #26168, #26264, #26317, #26350, #26366 and #26404. The claim is 6432922305. The scope is the cut 22 proposal of the cut 21 report (6432160640), which the server seat approved in its dispatch order, with the order's condition that the read-seam census stays at 82 with an identical list. Both members passed the AST screen and the census trial, and both moved unedited.

Two ObjectStackProtocolImplementation members move unedited into a new module, packages/metadata-protocol/src/protocol-view-expansion-names.ts (#23050 shape B):

protocol.ts loses 39 net lines (29,193 to 29,154). 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 9a0108cdc2 after cut 21 merged. It names no issue, so the claim comment is the identity record. It holds the move commit c2087b4c49 and the merge of main (95673433d1). That merge touches no file of packages/metadata-protocol/: the package's src/ tree is ac8309b0 at the move commit and at the merge.

Serial with the file's in-flight PRs

Measured just before this PR was opened, against the merged head 95673433d1: of the 20 open PRs, none edits protocol.ts and none names either member. Two touch packages/metadata-protocol/src/ test files: #26337 (3 files) and #26138 (7 files). Each was checked with git merge-tree: both are clean against this head and against origin/main alone. No open PR edits the new module.

The order's screen (private statics, new Error(, the read-seam census)

Both members 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.
  • Read-seam census (added to the screen after cut 21). Each member was moved alone to a scratch module in a clean worktree and check-durability-degradation-log-level --list re-run: 82 read seams and an identical list for each. The real cut measures the same; see "The durability --list".

What moves

Member Base line Visibility Body lines AST tokens (params + return + body)
resolveRowlessExpandedView :10306 private async 27 232
expandUnderOwnName :18159 private 28 297

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

  • The class keeps both members. Each keeps its name, docblock, signature and position. Each body now delegates, marked [#25467 cut 22].
  • Typing. ViewExpansionNamesHost types each of its six members as the class member itself (ObjectStackProtocolImplementation['…']). Each function types its this as the Pick of the members it reads. Each stub annotates its literal as ThisParameterType<typeof fn>. No member changes visibility.
  • Members read, measured by AST walk (property and element accesses on this, with as casts, parentheses and ! stripped, and bare this checked too). All are methods, each handed over as a closure (...a) => this.x(...a):
    • resolveRowlessExpandedView reads readActiveOverlayRows, rethrowUnlessMetadataStoreUnprovisioned, namesWithOwnStoredRow, expandStoredViewContainers and storedOverlayEntries. Three of these are themselves cut 16 stubs, so the call reaches protocol-stored-overlay-reads.ts through the class, as before. expandStoredViewContainers is generic (<E …>); its closure is contextually typed by the class member, and tsc accepts it with no annotation.
    • expandUnderOwnName reads isShippedByAnotherPackage.
  • Every caller still calls the class method. There are four this.resolveRowlessExpandedView( sites (:11041, :11556, :22145, :27291 at base) and one this.expandUnderOwnName( site (:18008, inside expandRuntimeViewContainer). None changes.

Module-level declarations, by reader

Measured by an AST walk over each body's identifiers, against protocol.ts's top-level declarations and import bindings (property names excluded):

Name Where protocol.ts has it Other readers in protocol.ts What this cut does
RowlessExpandedView non-exported interface, :6254 yes (2) gains export in place; the new module imports it as a type
servedViewExpansion non-exported generic function, :2872 yes (2) gains export in place; the new module imports it from ./protocol.js
PLURAL_TO_SINGULAR the @objectstack/spec/shared import yes (59) stays; the new module imports it from there too
expandViewContainer the @objectstack/spec/ui import yes (2) stays; imported from there too

index.ts names its exports one by one, so the two in-place exports do not reach the published surface (cuts 8, 11, 18 and 19 did the same). 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 with identical output: 82 read seam(s) at both ends, the order's condition. Its --list is identical once [@:][0-9]+ is stripped (158 lines, and no entry changes file). No entry of the base --list names either member. resolveRowlessExpandedView's try guards this.readActiveOverlayRows(...), which is already a cut 16 stub, and the gate did not count it as a read seam at base either. Its catch rethrows through rethrowUnlessMetadataStoreUnprovisioned (#5532), unedited. On the merged tree 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 9a0108cdc2 and head c2087b4c49 were built with pnpm --filter @objectstack/metadata-protocol build against one dependency tree. The closure was built at head by a full pnpm build (118/118). 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): 0a7694d518b570981c5cdcbcdd8fcb2dec9f1c7d74a7287299fe9e19e32a4183 at both ends, on dist/index.d.ts and dist/index.d.cts. The normaliser sorted 80 string-literal union runs. Sorted-union identity holds.
  • Raw sha256: 059fa1189ac13c65c36d3718584495bebaa48cc545e1526adb37cd95ef7dfaa3 at both ends, 777,645 bytes. Raw identity holds.
  • Again on the merged tree 95673433d1 (closure rebuilt by a full pnpm build, 118/118): the same raw and normalised sha256 and the same index.js/index.cjs hashes as the pre-merge head.
  • The two builds differ where they should.
    • index.js hashes dbff5f6c5cde69ca… at base and 14cc3ee05da5713f… at head (index.cjs 919b8129becaf027… → c75e50d7513a93c9…).
    • Each function's .call(host site occurs 0 times in the base bundles and once in the head bundles (ESM and CJS alike), and each function NAME( declaration goes 0 → 1.
  • Positive control on the head tree. private was dropped from expandUnderOwnName on disk (restored from HEAD under a trap) and the package rebuilt. Both .d.ts files move to 796c09174d25bb97… (normalised 9f5a865eb21c4211…, which differs from the bar's 0a7694d5…) and print expandUnderOwnName(type: string, object: string, container: Record<string, any>, ownPackageId: string | undefined): Record<string, unknown>[];. So the bar can see a change on these members.
  • Export tables. The ESM and CJS bundles, loaded at runtime from the package, each export the same 78 names at base and head. The sorted list hashes to 9f18a0177ac736ed… on all of them. None of the moved functions, ViewExpansionNamesHost, servedViewExpansion or RowlessExpandedView appears.

No changeset: skip-changeset, per ruling 6348891160.

Proof of move-only

Bodies, by AST token. The compare reads the TypeScript compiler's AST leaf tokens, with JSDoc skipped, over the parameters (less the added this), the return type and the body. It compares base protocol.ts at 9a0108cdc2 against the head module:

resolveRowlessExpandedView: base tokens 232, head tokens 232, differ 0, text-equal(ws-collapsed, comments incl.) true
expandUnderOwnName: base tokens 297, head tokens 297, differ 0, text-equal(ws-collapsed, comments incl.) true
TOTAL tokens=529 differ=0 members=2
CLASS members base=301 head=301 order-same=true byte-identical=299 stubs-with-identical-docblock+signature=2 other=[]
TOP base=250 head=251 byte-identical=248

The two top-level statements that are not byte-identical are servedViewExpansion and RowlessExpandedView, which gained export and nothing else.

Negative controls, one per member, each one token changed in an in-memory copy of the head module:

MUTATED … "!== 'view') return undefined" -> "=== 'view') return undefined"
resolveRowlessExpandedView: base tokens 232, head tokens 232, differ 1, text-equal(ws-collapsed, comments incl.) false
expandUnderOwnName: base tokens 297, head tokens 297, differ 0, text-equal(ws-collapsed, comments incl.) true
MUTATED … "item.name = under;" -> "item.name = ownName;"
resolveRowlessExpandedView: base tokens 232, head tokens 232, differ 0, text-equal(ws-collapsed, comments incl.) true
expandUnderOwnName: base tokens 297, head tokens 297, differ 1, text-equal(ws-collapsed, comments incl.) false
  • Class side. There are 301 members at base and at head, in the same order. The 299 that did not move are byte-identical to base, leading comments included. Both stubs sit at their base positions and keep their docblocks and signatures byte-identical.
  • Top level of protocol.ts. Of the 250 base statements, 248 are byte-identical, leading comments included; the other two gained export. Head adds one statement, the new module's import.
  • Size. +143 / −57 over 2 files, 200 changed lines (the proposal's estimate was 200–260). protocol-view-expansion-names.ts has 125 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 both member names and for the bodies' text: servedViewExpansion, RowlessExpandedView, expandViewContainer(, standInRows, underIsShipped, bareSpelled. So were the test files in packages/, docs/qa/ and the @objectstack/spec liveness ledgers.

  • By path: none reads the moved bodies. No by-path reader matches any of those strings.
  • No test names either member. The behaviour is driven through the class by the view-container expansion tests in the package suite, which pass at both ends.
  • The liveness ledgers. No row points at either member. 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 gives identical output at both ends (1,876 decision letters, 59,344 citations across 8,385 files). Neither body nor the new header cites an ADR.
  • Engine-double contract (check-engine-double-contract): exits 0 at both ends. The updateData/deleteData seams keep their entries at new line numbers (13898 → 13882, 13923 → 13907). productionFiles is 3,764 at base and 3,766 at head: the new file, and the gitignored packages/spec/src/migrations/registry.ts the spec build writes in the head tree (as in cuts 20 and 21).
  • Identical output at both ends: check-adr-anchors, 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 9a0108cdc2.

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 9a0108cdc2: 21,584 tests (21,565 passed, 0 failed, 19 skipped), 271 files.
    • Head c2087b4c49: 21,584 tests (21,565 passed, 0 failed, 19 skipped), 271 files.
    • None lost, none added, none changed status. The base leg is the package src/ swapped tree-only from 9a0108cdc2 (the new module removed) and restored from HEAD under a trap.
    • The merged head 95673433d1 gives the same 21,584 tests, per test identical to the pre-merge head (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 200 changed lines, under the 3,000-line human-merge bar.
  • Derived gates. dispatch-gates --commands --repo objectstack-ai/objectstack derives 46 gates on 2 changed paths from the merged tree 95673433d1, with no stale-tree warning; all 46 exit 0 after the full pnpm build (118/118). dispatch-gates --ran: 46 derived, 46 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. When this PR was opened, main was one commit past the merge (ui/packages/app-shell and a changeset), with no stale-tree warning.
  • check-durability-degradation-log-level --list on the merged tree: 82 read seam(s), the list identical to the pre-merge head's.

Carry-forward

These stay in protocol.ts:

  • The read-seam members (cut 21): a member named in a protocol.ts read-seam entry of check-durability-degradation-log-level --list, as guarded wrapper or guarding member, stays until the gate follows imported wrappers or a ruling accepts the loss. The census trial on this base measured findEnvironmentRows 82 → 66, packagelessAddress → 78, findServedOverlayRow → 79, and foldStoredCollection, applyObjectRegistryMutation, unmanagedImportCollisions, rollBackUnmanagedImport, readRevertableCommit and storedFlowBindingAgrees → 81. rethrowUnlessMetadataStoreUnprovisioned cannot move at all: moved alone, the gate exits 1 and finds 3 read seams.
  • 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

… the own-name container expansion out of protocol.ts (#25467 cut 22)

Move the bodies of resolveRowlessExpandedView and expandUnderOwnName
unedited into protocol-view-expansion-names.ts, behind a
ViewExpansionNamesHost that hands over the six members they read as
closures. RowlessExpandedView and servedViewExpansion gain export in
place. The class keeps both methods, their docblocks and declared
signatures; their callers keep calling the class methods.

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

This PR changes 1 package(s): @objectstack/metadata-protocol, touching 18 documentable anchor(s).

17 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 26e7c75e9ae232237ae5f7caf6c23164652853b5.

⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

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 26e7c75e9ae232237ae5f7caf6c23164652853b5 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 81e98b5a46dfd409d8e0f824ee16dbe96306746e — the merge of head 95673433d159caf29670a2584bc0b58d83047cf4 into base 26e7c75e9ae232237ae5f7caf6c23164652853b5, 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 81e98b5a46dfd409d8e0f824ee16dbe96306746e && git checkout 81e98b5a46dfd409d8e0f824ee16dbe96306746e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 26e7c75e9ae232237ae5f7caf6c23164652853b5 95673433d159caf29670a2584bc0b58d83047cf4 && git checkout -B drift-repro 26e7c75e9ae232237ae5f7caf6c23164652853b5 && git merge --no-ff 95673433d159caf29670a2584bc0b58d83047cf4

node scripts/docs-audit/affected-docs.mjs --json 26e7c75e9ae232237ae5f7caf6c23164652853b5

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 26e7c75e9ae232237ae5f7caf6c23164652853b5 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

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

Head 95673433d1, report 6433520681 (on #25467), claim 6432922305. 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 22), skip-changeset (no published behaviour moves).
Files 2 files, +143/−57. resolveRowlessExpandedView and expandUnderOwnName move into protocol-view-expansion-names.ts behind ViewExpansionNamesHost, with six closures handed over. RowlessExpandedView and servedViewExpansion gain export in place. The class members stay as delegating stubs, so all five callers still call the class members. Not governed.
Fix A pure move. Seat probe at fresh origin/main: of the 57 removed protocol.ts lines, 55 reappear in the new module, identical once the class indent is stripped. The other 2 are the servedViewExpansion and RowlessExpandedView declaration lines, which stay in protocol.ts and only gain export. Dev: AST compare 529 tokens, 0 differ (two negative controls give 1 each).
.d.ts bar Ruling 6348891160: both raw and sorted-union identity hold, pre-merge and merged.
Order questions The claim's condition holds. The read-seam census is 82 at base, head and merged, and the durability --list is identical once line numbers are stripped. No body constructs an error, so no PRODUCERS row is owed. check-adr-anchors is fully identical.
Evidence CI on head 9567343 is green: 36 checks succeeded, 16 were skipped, and none failed or are pending. The suite ran 21,584 of 21,584, with 0 lost, 0 added and 0 changed. Derived gates: 46 of 46 exit 0, --ran reconciles, and the tree is not stale. No other open PR touches protocol.ts.
Carried The liveness pointer disclosure stays for the series' close-out. Cut 23 goes to the same thread once this lands: convertStoredItem (:6415) paired with convertStoredItemDetailed (:6451), with storedConversionWarned (a Set that is only read with .has and .add) handed over as a getter.
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 04:32
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 04:32
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit d0852dd Oct 27, 2026
53 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-ybjm3h branch October 27, 2026 05:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m 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