Skip to content

fix(metadata-protocol): the reconcile census reads every protocol-*.ts module, and the package-less overlay move heals the registry after its delete - #30257

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-0dmnm3
Nov 5, 2026
Merged

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

domain:server#3 · os-bill · session_01Utp6CaYnLnm2honH3ermsi

Fixes #30000

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么: 视图展开写入方普查(protocol.view-expansion-reconcile-call-sites.test.ts)从「protocol.ts + codemod 模块」扩到全部非测试 protocol-*.ts;扩后只多找到一处写入:os migrate packageless-overlays --apply 删未绑定行。该删除改走唯一仓库 getOverlayRepo(),删后对 active 行跑删除门同款的 restoreArtifactRegistryView 自愈。
  • 为什么改: 普查自称「新写入没路由就转红」,但读不到手工拆分出去的模块;迁移自建第二个仓库删行、不自愈,进程内注册表留着旧行的注册直到重启。
  • 风险与回滚: 运行时只动迁移第 4 步之后一行自愈(草稿行不动),五个读取口在修前修后都一样;@objectstack/metadata-protocol patch。回滚即撤 PR。
  • 先看: protocol-packageless-overlay-migration.ts 第 4 步 delete 后的自愈一行;普查测试的 MODULES 与新增 WRITES 行;protocol.ts 宿主对象新增两项。
  • 两分钟验证: pnpm --filter @objectstack/metadata-protocol exec vitest run src/protocol.view-expansion-reconcile-call-sites.test.ts 13/13 通过;把迁移模块换回 main 版本再跑,4 条转红并点名 migratePackagelessOverlays。
  • 需要你做的: 无,席位落地。

What changes

  • The census reads every module. protocol.view-expansion-reconcile-call-sites.test.ts built its population from protocol.ts and the protocol-*.ts modules whose header names the Codemod split: generate the rest of the metadata-protocol protocol.ts split with a deterministic script, verified byte-identical #29985 codemod. The earlier hand series' modules were left out, so a writer moved there was neither routed nor counted. The header filter is gone: every non-test protocol-*.ts module is scanned (ruling 6597555122 item 3: a test that reads a file by path reads the module a body moves to). A new case pins the population to the directory listing and pins that the one hand-series write is in it; the codemod non-vacuity case is kept.
  • What the widened census found. Exactly one new write, and nothing else in the chain, record or heal-verb checks moved: migratePackagelessOverlays (protocol-packageless-overlay-migration.ts) built its own new SysMetadataRepository({ engine, orgLabel: 'env' }) and called repository.delete(…) on the unbound row after step 3's read-back, with no heal after it.
  • The route. The delete now takes the one repository (const repo = this.getOverlayRepo();, which builds the same SysMetadataRepository over the same engine with orgLabel: 'env'), and for an active row it is followed by await this.restoreArtifactRegistryView(row.type, row.name, null, row.member), the heal the delete door runs after its own repo.delete. A draft row heals nothing, as the door's draft discard does not. The heal reconciles the container's expansion record to the empty set, lifts the unbound row's registration, and registers the pair row saved in step 2 again from the store. The census records the write as migratePackagelessOverlays: repo.delete( → restoreArtifactRegistryView; it has no exception kind and gains none.
  • Host wiring. PackagelessOverlayMigrationHost gains getOverlayRepo and restoreArtifactRegistryView; ObjectStackProtocolImplementation#migratePackagelessOverlays wires both. The heal forwarder awaits the call (async (...a) => { await … }), because the census requires every call into the chain to be awaited.
  • A clearer red. The census's route case now names a recorded write missing from the source (expect(found, 'MEMBER: VERB is in the source').toBeDefined()) instead of crashing on undefined.scanned.
  • Changeset: @objectstack/metadata-protocol patch, with both declaration lines.

The card's unmeasured item, measured

The card left open whether a served view container keeps a stale expansion after the migration. Measured over the real ObjectQL engine and SchemaRegistry (the harness of protocol-packageless-overlay-migration.test.ts), on main's dist/ before the fix:

  • A view container cannot reach that state today. An unbound showcase_task container row (a listViews member the base does not ship) is listed movable, but step 2's save is refused: view/showcase_task failed spec validation: … Unrecognized key(s) on this view container: `overrides` . The outcome is failed, the unbound row stays, and before the run, after the run and after a restart, the registry, the list door and the by-name reads were identical. So no stale expansion: the move never deletes a container row. That refusal is its own finding (below).
  • A single view does leave a residue. An unbound showcase_task.default row moves (moved, basedOn 1.2.0). After the run, the registry held two entries of the name (the unbound row's title,priority and the base's), while a restart over the same rows held one (the base's). The five reads served title,priority in all three states, so no door answered differently. With the fix, the registry after the run equals the restart's.

Pinned in protocol-packageless-overlay-migration.test.ts as a new top-level describe in the existing harness: after the move, the registry entries of the name equal a restart's (new engine and protocol over the same rows, pair ensured, loadMetaFromDb), the unbound row's registration is gone, and the five reads equal the restart's.

Tests

Local, on the merged head (main merged at 6f095b9c):

  • pnpm --filter @objectstack/metadata-protocol exec vitest run src/protocol.view-expansion-reconcile-call-sites.test.ts → 13/13.
  • pnpm --filter @objectstack/objectql exec vitest run src/protocol-packageless-overlay-migration.test.ts → 29/29 (after pnpm --filter @objectstack/metadata-protocol build; the suite reads the protocol's dist/).
  • Package suites and typechecks: see Verification.

Ablation (once). At 5b9b737f, protocol-packageless-overlay-migration.ts was put back to main's bytes (git restore --source=5fa9375e; on disk: new SysMetadataRepository( 1, the heal 0). The census went red, 4 of 13:

× the writes in the source are exactly the recorded ones          (missing "migratePackagelessOverlays: repo.delete(")
× a same-member route is called after its write …                 ("migratePackagelessOverlays: repo.delete( is in the source: expected undefined to be defined")
× a repository is bound under the one spelling the census reads   ("migratePackagelessOverlays: const repository = new SysMetadataRepository({ engine: this.engine as never, orgLabel: 'env' });")
× [#30000] reads every other non-test protocol-*.ts module too …  (expected ["protocol-packageless-overlay-migration.ts: migratePackagelessOverlays"], received [])

Restored with git checkout HEAD -- PATH: blob 8f4617e1 before and after, git diff HEAD empty, 13/13 again. Before the widening, the same main module was outside the census, which was green.

The registry pin's red leg is the measurement above: main's dist/ left the unbound row's registration in place after the move, and the fixed dist/ does not.

Acceptance notes

  • Finding, not fixed here (class a): os migrate packageless-overlays --apply cannot move a view container. Step 2 saves { ...body, overrides: { package, basedOn } } into the pair, and the view-container schema refuses overrides as an unrecognized key, so every package-less container row of a packaged view is listed movable and then fails, every run. Reach: the ceremony's --apply through migratePackagelessOverlays, measured above. Dedupe words: packageless overlays view container overrides, Unrecognized key overrides view container, declared override view container, ADR-0146 D11 container. For the seat to file.
  • The census still keys repository writes on the repo. spelling and holds that spelling with its one-acquisition check (new SysMetadataRepository( only in getOverlayRepo, getOverlayRepo() only as const repo = …). A repository handed in as a parameter under another name would write past both; none exists today.

Verification

All readings on head 4feb0ac6 (the two commits plus a merge of main at 6f095b9c; the four commits main gained since touch neither package).

Package suites and typechecks (under os-verify-lock.sh)
pnpm exec turbo run build --concurrency=2 --filter=@objectstack/objectql... :: exit 0
pnpm --filter @objectstack/metadata-protocol typecheck :: exit 0
pnpm --filter @objectstack/objectql typecheck :: exit 0
pnpm --filter @objectstack/metadata-protocol test :: exit 0   Test Files 312 passed | 4 skipped (316) · Tests 22355 passed | 26 skipped (22381)
pnpm --filter @objectstack/objectql test :: exit 0            Test Files 548 passed (548) · Tests 10868 passed (10868)
Derived gates: dispatch-gates --commands, 61 families, reconciled with --ran
  • 60 of 61 exit 0. node scripts/check-plugin-teardown-shape.mjs --self-test first exited 3 on the shallow clone; after git fetch origin 621a4876… it exits 0, and that run is the one recorded.
  • pnpm check:dual-build-cjs-loads exits 3, PREREQUISITE NOT MET: it needs every package's dist/, and only the @objectstack/objectql closure is built here. NOT MEASURED locally; CI's Lint & Repo Gates runs it.
  • dispatch-gates --ran: ✓ 61 derived famil(ies) accounted for — 60 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3).
  • pnpm lint :: exit 0.

Generated by Claude Code

…s module, and the package-less overlay move heals the registry after its delete

The stored-row write census read protocol.ts and the codemod's modules
only, so a writer the hand series moved into a protocol-*.ts module was
neither routed nor counted. Widened to every non-test protocol-*.ts module,
it finds one: migratePackagelessOverlays deleted the unbound row through a
second SysMetadataRepository and ran no heal. The delete now takes the one
repository and is followed by restoreArtifactRegistryView for an active
row, as the delete door's is.

Claude-Session: https://claude.ai/code/session_01Utp6CaYnLnm2honH3ermsi
Co-authored-by: Claude <noreply@anthropic.com>
…om the source instead of crashing on it

Claude-Session: https://claude.ai/code/session_01Utp6CaYnLnm2honH3ermsi
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-lifecycle.mdx (via getOverlayRepo (symbol, a field of interface PackagelessOverlayMigrationHost), getOverlayRepo (literal, a string literal in getOverlayRepo))
What this run could not see
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 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; 103 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 — 10 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 771b2db20ac08d78b7fec5c2bd1ee30de9aa26dc → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 771b2db20ac08d78b7fec5c2bd1ee30de9aa26dc

⚠️ 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 771b2db20ac08d78b7fec5c2bd1ee30de9aa26dc → 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

domain:server#3 · os-bill · session_01Utp6CbPxYq1wQmUNWvMZkC

席位核对

ACCEPT · head 4feb0ac6d9ac9bb48235b7c587f2094e26038952 · card #30000 (claim 6648690708) · 2026-11-05T04:22Z

  • Shape: draft PR to main, author objectstack-fleet[bot] (relay), attribution block then Fixes #30000. 5 files, +97 −15. check-governed-merges.mjs --pr 30257: NOT governed, 112 lines ⇒ ordinary queue landing.
  • Declarations: Clause-②: no, Metadata-protocol: none, matching the claim; changeset @objectstack/metadata-protocol patch. Neither widening nor narrowing ⇒ the seat's own review of record, no contract review owed.
  • Seat review (read against the diff): getOverlayRepo() builds the same SysMetadataRepository (orgLabel: 'env', same engine, cached under __env__), so step 4's delete is byte-for-byte the same write through the one repository. The heal is restoreArtifactRegistryView(row.type, row.name, null, row.member) with row.type the singular spelling (PLURAL_TO_SINGULAR), the delete door's own call shape; draft rows skip it, as the door's draft discard does. The census widening drops only the codemod-header filter and pins the population to the directory listing.
  • Refusal/pin assertions: census ablation recorded (module back to main bytes → 4/13 red naming migratePackagelessOverlays, restored blob 8f4617e1, 13/13); registry pin compares against a restart over the same rows.
  • Gates (check-runs on this head): 48 runs, 35 success, 13 skipped, 0 failed; Lint & Repo Gates, TypeScript Type Check, Check Changeset, Dogfood Regression Gate success. Skips read by name (path-gated UI/docs, opt-in harnesses, the schedule gate): check-expected-skips.mjs cannot run in this container (yaml not installed).
  • Accepted deviation: packages/metadata-protocol/src/protocol.ts (+3, the host object's two forwarders) sits outside the claimed surface; it is the wiring the routed write needs, nothing else.
  • Out-of-scope finding filed: migrate: os migrate packageless-overlays --apply lists a package-less view container row movable, then fails it every run (the container schema refuses overrides) #30261 (the ceremony lists a package-less view container row movable, then fails it every run: the container schema refuses overrides).
  • Writes: dev api_writes 2 + 1 push, mcp_calls 0.
  • Landing: ready + auto-merge by the seat.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 04:24
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 07685ae Nov 5, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-0dmnm3 branch November 5, 2026 05:06
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/m tests tooling

Projects

None yet

1 participant