You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 07685ae
Browse filesBrowse the repository at this point in the historyBrowse files
fix(metadata-protocol): the reconcile census reads every protocol-*.ts module, and the package-less overlay move heals the registry after its delete (#30257)
<!-- claude-projects-attribution: {"github_login":"os-bill"} -->
_Requested by **Bill** · [project
thread](https://claude.ai/code/project/chan_016iMXWSZDzqWRWvWzdNo8wg?thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgVRDQFEPQMGJupEwoJymrzt)_
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 #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).
<details><summary>Package suites and typechecks (under
os-verify-lock.sh)</summary>
```
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)
```
</details>
<details><summary>Derived gates: dispatch-gates --commands, 61 families,
reconciled with --ran</summary>
- 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 621a487…` 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`.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Utp6CaYnLnm2honH3ermsi)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
fix(metadata-protocol): `os migrate packageless-overlays --apply` heals the registry after it deletes the unbound row, as the delete door does
6
+
7
+
Clause-②: no
8
+
Metadata-protocol: none
9
+
10
+
-**Now.** After the ceremony saves a row into its base's `<base>.custom` pair and reads it back, it deletes the unbound row through the protocol's one repository and then runs the delete door's registry heal for an active row. The unbound row's registration leaves with the row, and the pair's row is registered again, so the process serves what a restart serves. Before, the delete went through a second repository and no heal followed, so the unbound row stayed registered beside the base's entry until the next restart. The five reads of the item already served the pair's row, so no read answered differently.
11
+
-**Unchanged.** A draft row's removal heals nothing, as a draft discard does not. Outcomes, receipts and the rows written are the same.
0 commit comments