Skip to content

refactor(metadata-protocol): move the registry materialize/strip pair out of protocol.ts into protocol-registry-materialize.ts (#25467 cut 11) - #25797

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-f0qmfa
Oct 25, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-f0qmfa

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 11 of the protocol.ts split (#24586 ruling A, recorded in 6270097840). Cuts 1–10 landed as #25494, #25518, #25538, #25589, #25601, #25623, #25642, #25719, #25731 and #25740. The claim is 6397346543; the scope is the registry materialize/strip group the cut 10 report (6393303455) proposed from Release 6385903828, as the server seat approved it in its dispatch order.

Four ObjectStackProtocolImplementation members move unedited into a new module, packages/metadata-protocol/src/protocol-registry-materialize.ts (#23050 shape B, one engine getter). protocol.ts loses 22 net lines (29,791 to 29,769). The cut is move-only, and the card stays open for later cuts.

Branch claude/project-thread-f0qmfa is the harness-pinned branch for this thread. It names no issue, so the claim comment is the identity record. It holds one commit, f081d777, on main 0790d0a469 (cut 10's merge 2c86039515 is in its history).

Serial with the file's in-flight PRs. Measured just before this PR was opened: none of the 12 open PRs touches packages/metadata-protocol/src/ (each file list read in full; the release PR #25640's 226 files include the package's CHANGELOG.md and package.json only). git log 0790d0a469..origin/main is empty at that moment.

What moves

Member Base line Visibility AST tokens (type params + params + return + body)
materializeFromRegistry :9071 private 119
governServedObject :9109 private 28
stripMaterializedFromRegistry :9134 private 119
stripServedObjectColumns :9165 private 28

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

  • The class keeps every member with its name, docblock, signature and position. Each body is now one host literal plus a .call, marked [#25467 cut 11].
  • Typing. RegistryMaterializeHost types every member as the class member itself (an indexed access on ObjectStackProtocolImplementation). Each moved function keeps the method's type parameter T and types its this as the Pick of the host over exactly the members its body reads. Each stub annotates its literal as ThisParameterType of typeof fn. No cast, and no member changes visibility.
  • One new spelling, forced by the generics. The four members are generic (T in, T out). Function.prototype.call on a generic function erases T to unknown, which tsc rejects against the method's declared T return (TS2322). So each stub instantiates the function first: the stub calls .call on the parenthesised instantiation expression of the function at T (tsc refuses an instantiation expression followed directly by a property access, TS1477, hence the parentheses). The emitted JavaScript is a plain .call(host, …); cuts 1–10 moved no generic member, so no earlier cut needed it.
  • Members read. materializeFromRegistry and stripMaterializedFromRegistry read the field engine, handed over as a GETTER so the read happens when the body makes it (tests replace engine on a constructed protocol). governServedObject calls materializeFromRegistry and stripServedObjectColumns calls stripMaterializedFromRegistry, each through a late-bound closure on the protocol. No private-static member, and nothing on the Release's stay list. The order's hypothesis holds.
  • Every other caller still calls the class method: the five governServedObject read exits (one in readFlattenedMetaItems, three in readMetaItem, one on getMetaItemLayered's effective layer) and the one stripServedObjectColumns write site in saveMetaItem.

Module-level declarations, by reader

  • Two gain export, none moves. The bodies read three module-level functions of protocol.ts: canonicalMetaType (already exported), and governServedItem and stripServedSystemColumns, which gain export in place, as cut 8 exported permissionSetOwnerBindingRefusal and runtimeHookWithoutBodyRefusal. The new module imports all three from protocol.js; the cycle is evaluation-safe because each is read only inside a function body. index.ts names what it publishes from protocol.ts one by one, so the published surface does not move (measured below).
  • Measured, not assumed: after the move, the two exported helpers have no caller left in protocol.ts; the moved bodies were their only callers at base too. They stay there because the class's read and write paths cite them by name in prose (for example the getMetaItemLayered comment on why governServedItem never runs on overlay). Moving them is noted below, not done here.
  • Imports in protocol.ts. No import loses its last reader. The one new statement is the import of the new module.

The published .d.ts

The series bar is ruling 6348891160, sorted-union identity, measured first. Base 0790d0a469 and head f081d777 were built with pnpm --filter @objectstack/metadata-protocol build against one dependency tree (the closure built once at head, then the package's src/ swapped tree-only to base for the base build and restored from HEAD under a trap; git diff HEAD empty and git status clean after, both blobs equal to HEAD's).

  • Raw sha256: 325970c1661efaa6a3762ce7de9e6bb205ea16099784f2b6c5950d62d175b331 at base and at head, on both dist/index.d.ts and dist/index.d.cts, 772,838 bytes each (the hash cuts 8–10 reported). cmp exits 0 on both pairs. Raw identity holds, so sorted-union identity holds too. The four members are private, so each stays a private NAME; line.
  • Normalised sha256: 9562749edf7b8bba67ed2f1779799a68b3d6934a8605074934572d9507c55ba6 at both ends (109 string-literal union runs sorted; this run's own normaliser).
  • The two builds differ where they should: index.js hashes b99c5148b08d3fc5… at base (cut 10's head hash) and bbe1a227b80604e0… at head; the four functions' .call( sites occur 0 times in the base bundles and 4 times in the head bundles (ESM and CJS alike).
  • Positive control on the head tree (on disk, restored from HEAD under a trap, hash equal to HEAD's after): private dropped from stripServedObjectColumns and the package rebuilt. Both .d.ts files move to 06884ae21a4b72b4… and print the member's full generic signature (type parameter T, then (type: string, item: T): T;), 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 and head; the sorted list hashes to 9f18a0177ac736ed… on all four. None of the moved functions, the two newly exported helpers or the host type appears.

No changeset: skip-changeset, per ruling 6348891160.

Proof of move-only

  • Bodies, by AST token (the TypeScript compiler's AST leaf tokens, JSDoc skipped, over type parameters, parameters less the added this, return type and body): 294 tokens compared, 0 differ. The type-parameter, parameter, return-type and body text, comments included, is also equal with whitespace collapsed, for all four. Negative control: one changed token in stripMaterializedFromRegistry's body (item === null to item !== null, on a copy) makes the check report differ=1.
  • Class side. 299 members at base and at head, in the same order. The 295 that did not move are byte-identical to base, leading comments included. Each of the 4 stubs sits at its base position and keeps its docblock and signature byte-identical.
  • Top level of protocol.ts. 236 of the 238 base statements other than the class are byte-identical, leading comments included. The other two are governServedItem and stripServedSystemColumns, which gain the export keyword and nothing else. Head adds one statement, the new module's import.
  • Size. +165 / −53 over 2 files, 218 changed lines. protocol-registry-materialize.ts has 134 lines.

Readers of the moved lines

Every test and script that reads protocol.ts by path, and every test file in packages/ and scripts/, was grepped for the four member names, for governServedItem and stripServedSystemColumns, and for the four registry seam names the bodies call (materializeServedObjectOnto, provisionSearchCompanionOnto, stripMaterializedStampsFrom, stripProvisionedSearchCompanionFrom).

  • No by-path reader. Outside protocol.ts the member names occur only in prose: metadata-diagnostics.ts (a comment naming governServedObject's docblock), two CHANGELOG.md entries, and comments in packages/objectql and packages/rest tests. No gate, baseline or ADR anchor names them.
  • The partial-registry-double fallbacks. Six test files name the seam methods: packages/objectql/src/protocol-meta-effective-schema.test.ts, registry-deployment-platform-global.test.ts, registry-tenant-index-author-declared-column.test.ts, registry-tenant-index-follows-wall.test.ts, meta-object-primary-designation-roundtrip.test.ts, and packages/rest/src/meta-object-materialization-agreement.test.ts. Each builds the protocol over a REAL SchemaRegistry (which defines all four seam methods, packages/objectql/src/registry.ts) or calls the registry directly; none reads protocol.ts by path, and none builds a double that answers only the narrower provisionSearchCompanionOnto / stripProvisionedSearchCompanionFrom spelling. Verdict: the fallback arms move verbatim and no in-repo test reaches them (see Acceptance notes). At head, those six files plus meta-object-search-companion-roundtrip.test.ts pass: objectql 6 files / 57 tests, rest 1 file / 12 tests, each VERDICT command-exit 0.
  • Bracketed-refusal pin (PRODUCERS): no row owed. The four bodies hold no new Error( (0 by count in base lines 9032–9167); protocol.ts keeps its 74 such sites.
  • ADR anchors and decision letters. check-adr-anchors exits 0 at both ends with identical output: 85 anchored files, 52,707 citations across 6,638 files, 1,846 → 1,846 decision-letter citations. The protocol.ts anchor file names none of the four members, and the moved bodies are not the mechanism of any entry paragraph, so the new module gets no entry.
  • Durability checker (check-durability-degradation-log-level --list): identical at both ends once [@:][0-9]+ is stripped (159 lines). The gate exits 0 at both ends with identical output. The moved try/catch arms wrap registry seam calls, not storage reads, so neither rule reads them.
  • Engine-double contract (check-engine-double-contract): exits 0 at both ends. The updateData/deleteData seams keep their entries at new line numbers (14044 → 14022, 14069 → 14047). productionFiles is 3,690 at base and 3,692 at head: +1 is the new file, +1 is the gitignored packages/spec/src/migrations/registry.ts, which the spec build writes in the head tree and the clean base tree had not built.
  • 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. check-nul-bytes differs only in the scanned file count, +1. The base-end readings were taken in a clean detached worktree at 0790d0a469.
  • Platform checklist. check-platform-checklist --changed-from origin/main: OK, 29 anchors judged, 0 lost.
  • Governed surface. check-governed-merges --branch claude/project-thread-f0qmfa: 0 of 2 paths hit the register, NOT governed; 218 changed lines, under the 3,000-line human-merge bar.

Verification

All at head f081d777.

  • tsc --noEmit in packages/metadata-protocol exits 0. pnpm exec eslint --no-inline-config on the two changed files exits 0, no output.
  • packages/metadata-protocol suite, base and head, compared per test name and status (vitest --maxWorkers=2, JSON reporter, each run under the shared verify lock, VERDICT command-exit 0 both legs):
    • Base 0790d0a469: 21,509 tests (21,490 passed, 0 failed, 19 skipped), 266 files.
    • Head f081d777: 21,509 tests (21,490 passed, 0 failed, 19 skipped), 266 files.
    • None lost, none changed status, none added. The base leg is the package src/ swapped tree-only from 0790d0a469 (the new module removed from the tree) and restored from HEAD under a trap.
  • Derived gates. dispatch-gates --commands --repo objectstack-ai/objectstack derives 53 gates on 2 changed paths; all 53 exit 0. check:dual-build-cjs-loads and check:lean-entry-closure first answered PREREQUISITE NOT MET (exit 3, unbuilt dist/) and exit 0 after a full pnpm build (76/76 tasks, VERDICT command-exit 0); the plugin-teardown fixture commit was fetched before the first run. dispatch-gates --ran: 53 derived, 53 run, 0 NOT-MEASURED, 0 UNRUN, exit 0.

Acceptance notes

  • The fallback arms of materializeFromRegistry and stripMaterializedFromRegistry (the narrower provisionSearchCompanionOnto / stripProvisionedSearchCompanionFrom spellings for partial registry doubles) are reached by no test in this repository: every protocol in the six named test files is built over a real SchemaRegistry, which answers the wider seam first. They move verbatim; whether to keep them is not this cut's question. Not class a/b/c.
  • governServedItem and stripServedSystemColumns now have their only callers in protocol-registry-materialize.ts. A later cut could move them there; it would re-point the {@link} references in the class docblocks and the prose comments that name them.

Carry-forward

Unchanged from cut 10: public members whose return type is inferred, the bodies kept by a reader (probeRecord, getMetaItemCached, findReferencesToMeta, packagelessAddress, refuseUnmintableMetaType, metaTypeNamespaceExists, viewContainerNameCollisionRefusal with overlaidShippedContainerViewNames, reportPageRequiresAbsentAtLoad, the 7 applyRegistryWriteThrough callers, readCodeLayerForCarryForward), and the private-static readers stay in protocol.ts.


Generated by Claude Code

… out of protocol.ts (#25467 cut 11)

Move-only. The bodies of materializeFromRegistry, governServedObject,
stripMaterializedFromRegistry and stripServedObjectColumns move into the
new protocol-registry-materialize.ts (host shape B: an `engine` getter and
callbacks for materializeFromRegistry / stripMaterializedFromRegistry).
Every member keeps its name, docblock and signature in the class as a stub
that delegates. governServedItem and stripServedSystemColumns gain an
`export` in place so the new module can import them; index.ts names its
exports one by one, so the published surface does not move.

Claude-Session: https://claude.ai/code/session_011WCU5ZbGMk9LUhayKvYPsx
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 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

9 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
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 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 0790d0a4696a26135f350d39b1cf4cc6a1af860f → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 4dfbb2ade5e334085db6347a55de7a23602dbd21 — the merge of head f081d777722712adf55e165517987501e6e8df1f into base 0790d0a4696a26135f350d39b1cf4cc6a1af860f, 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 4dfbb2ade5e334085db6347a55de7a23602dbd21 && git checkout 4dfbb2ade5e334085db6347a55de7a23602dbd21
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0790d0a4696a26135f350d39b1cf4cc6a1af860f f081d777722712adf55e165517987501e6e8df1f && git checkout -B drift-repro 0790d0a4696a26135f350d39b1cf4cc6a1af860f && git merge --no-ff f081d777722712adf55e165517987501e6e8df1f

node scripts/docs-audit/affected-docs.mjs --json 0790d0a4696a26135f350d39b1cf4cc6a1af860f

⚠️ 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-25T12:56Z

Head f081d77772, report 6397905786, claim 6397346543.

item reading
Shape draft, base main, Part of #25467 (no closing keyword), skip-changeset label, Clause-②: no line-initial.
Files (2, +165/−53) protocol.ts and the new protocol-registry-materialize.ts: exactly the claim's surface; no test file, no packages/spec/**, no packages/objectql/**, no docs/adr/**. Not governed (check-governed-merges --pr 25797), 218 changed lines.
Move-only report's AST proof: 294 leaf tokens, 0 differ; one-token negative control reads DIFFERENT. Seat's own read on the diff: the four class methods keep name, docblock and signature; each is a host literal plus .call — engine as a live getter for the two registry bodies, a protocol-bound closure for the one member each wrapper calls (materializeFromRegistry, stripMaterializedFromRegistry), so a spy on the instance is still reached. (fn<T>).call keeps T (plain .call erases it, TS2322, per the report).
Exports governServedItem and stripServedSystemColumns gain export in place in protocol.ts (cut 8's precedent); index.ts names its re-exports one by one, so the published export table is unchanged (78 names both ends).
.d.ts raw byte-identical at base and head (325970c1…); normalised identical. Ruling 6348891160 holds.
Gates dev 53/53 derived exit 0, --ran 0 gaps; hand gates identical at both ends; package suite 21,509 tests both ends, 0 lost/changed/added. No new Error( moved, so no PRODUCERS row. CI at this act: in progress; Lint & Repo Gates and Type Check owed success before ready.
Noted, not filed the two partial-registry-double fallback arms (provisionSearchCompanionOnto, stripProvisionedSearchCompanionFrom) are reached by no test (the seam tests build a real SchemaRegistry). Pre-existing; this cut moves them unchanged.
Landing ready + auto-merge through the relay once every check on this head is green or an expected skip. Cut 12 per the report (backfill pair + listDrafts + discardDraftInItsScope) is the next claim.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 25, 2026 13:26
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 25, 2026 13:26
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 25, 2026
Merged via the queue into main with commit 4893a2f Oct 25, 2026
53 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-f0qmfa branch October 25, 2026 13:58
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