Repository navigation
Commit 713beba
<!-- claude-projects-attribution: {"github_login":"os-bill"} -->
_Requested by **Bill** · [project
thread](https://claude.ai/code/project/chan_016iMXWSZDzqWRWvWzdNo8wg?thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgHf6TbfHAaaj7ZxnraVYYjb)_
domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb
## 维护者速读
- **改了什么:** 单项回滚(`POST
/api/v1/meta/:type/:name/rollback`)现在回滚「用户看到的那一行」:定制包(`BASE.custom`)里声明了覆盖的行,即使旁边还躺着一条关停前写进只读基包的旧行,也能正常回滚,旧行不动。
- **为什么改:**
旧逻辑按「存储里第一条同名行」取包,常常取到基包旧行,于是被「点名基包写入已关闭」拒绝(403),报错还指向用户根本没打算碰的行;在 `os
migrate packageless-overlays` 跑之前,这一项的历史回滚等于废了。
- **风险与回滚:** 不加参数、不改错误码与状态;只有同名多行时取行结果会变(取被服务的那一行)。只有基包旧行被服务时仍照旧拒绝。回滚即撤
PR。
- **先看:**
`packages/metadata-protocol/src/protocol-package-commits.ts`(`rollbackMetaItemUnaudited`
里取包的那一段与 `restoreVersion` 的
`packageId`),`packages/objectql/src/protocol-packageless-overlay-migration.test.ts`(S5
pin 按新寻址重判)。
- **两分钟验证:** `pnpm --filter @objectstack/metadata-protocol exec vitest
run src/protocol.named-base-overlay-write.test.ts -t
29848`:三条全绿;定制行回滚成功、基包行原样,删掉定制行后回滚基包行仍是 403 `NOT_OVERRIDABLE`。
- **需要你做的:** 无,席位落地。
Fixes #29848
Clause-②: no — the door's accept set does not change (no new parameter,
no new value); the package is derived from the row the by-name read
serves. ADR-0146 D2: an item of the pair that declares `overrides`
"replaces the base's item **whole** for every read that serves the
base's name", and its revert ("Restore package original" = delete the
overriding item from `.custom`, so the base's document is served again)
is the pair's own row's; neither ever addressed the base row. The 403
refused the pair's own row because the binding read named the base row,
a refusal those texts already deny.
Metadata-protocol: none
## What changes
`packages/metadata-protocol/src/protocol-package-commits.ts`
(`rollbackMetaItemUnaudited`), two lines of behaviour:
1. **The row the rollback addresses is the row the read serves.** The
package is now `findServedOverlayRow` at `packagelessReadAddress` with
`servedOverride: true`: the same resolution `getMetaItem` naming no
package uses (ADR-0146 D8 item 2, the pair's declared override first,
`isServedOverrideRowOf` the one predicate). Before,
`resolveOverlayPackageBinding` answered the first active row of the name
in store order. When no row is served (a row only another package binds
under the name), the binding read answers as before.
2. **`restoreVersion` restores into that row** (`packageId:
rollbackPackageId`, the parameter PR #29797 added for the commit doors).
Before, the restore's active-row read was package-agnostic too, so even
with the right binding the parent hash and the write could land on the
base row.
Every gate after the binding read is unchanged and now reads the served
row's package: the package-less close, the named-base close,
`restoreWriteFor` (the pair's row is a runtime-only write judged by the
override write gate, ADR-0146 D2), the registry write-through.
`protocol.ts` gains two docblock lines on `rollbackMetaItem`. No new
parameter, no new error code, no spec change.
## Tests
`packages/metadata-protocol/src/protocol.named-base-overlay-write.test.ts`,
new top-level describe `[#29848]` over the file's existing harness:
- **Red first, the measured two-row state.** A row bound to the base
stored first, then the pair's declared override saved twice. On
`5fa9375e` the rollback to the pair's first version answered `403
NOT_OVERRIDABLE` with the named-base sentence ("a rollback of its row
bound to the read-only base is closed"), the card's reading. Now:
success, the pair row holds `PairFirst` with its `overrides`
declaration, the base row is untouched, and both the read naming no
package and the read naming the base serve `PairFirst`.
- The pair's rollback to the base row's version (a body that declares no
override) is the override write gate's `403 NOT_OVERRIDABLE`, not the
named-base close's; nothing is written.
- **The named-base close still holds.** Once the pair's row is deleted,
the read serves the base row again, and its rollback is refused with the
close's envelope (`status` 403, `code` `NOT_OVERRIDABLE`, the sentence
naming the pair); the row is unchanged.
`packages/objectql/src/protocol-packageless-overlay-migration.test.ts`,
the shadowed (S5) case re-judged: it pinned "the rollback's row is the
first active row of the name in store order", the package-less row,
while the read served the pair's declared row beside it. That is this
card's defect on a package-less row instead of a base-bound one. The
rollback now addresses the served pair row; its version 1 is the
package-less row's undeclared body, so the D2 gate refuses it and both
rows stay as they were. The save and DELETE halves of the pin are
unchanged; the final read now serves the package-less row's current
body, since it is no longer rolled back.
### Ablations
`scripts/ablation-replace.mjs` (wrap mode), each leg restored with blob
== HEAD (`35ffb243`) and `git status --porcelain` empty.
| # | Mutation | Result |
|:--|:--|:--|
| A1 | the served-row derivation off (`rollbackPackageId` back to the
binding read) | 2 / 34 red: the two-row rollback, the D2-gate pin |
| A2 | `restoreVersion` without `packageId` | 1 / 34 red: the two-row
rollback |
## Acceptance notes
- **S5 behaviour change, named.** With a package-less row and the pair's
declared row both stored (the census's `shadowed`), the per-item
rollback used to restore the package-less row; it now restores the
pair's row, which is the one every read serves. A rollback of the
package-less row itself is not reachable through this door any more; its
remedies are unchanged (a DELETE of the pair's row, after which the row
is `movable` and `os migrate packageless-overlays` moves it).
- PR #30226 (#30062) is in flight in the same area
(`protocol-packaged-base-refusal.ts`, `protocol-package-bindings.ts`);
this PR touches neither file. The served-row resolution it reads
(`findServedOverlayRow`, `isServedOverrideRowOf`) is not in #30226's
diff.
- `content/docs/concepts/metadata-lifecycle.mdx:171` says a write naming
no package (a rollback included) onto a packaged item is refused; it
speaks of package-less rows, and a rollback of a served pair row already
succeeded on `main` when the pair row was the only one. Left as is.
## Verification
Head `abd5d69f` (base `5fa9375e`, `origin/main` `771b2db2` merged in
once, no conflict, no rebase). The merge brought 8 commits (core
dispatch loop, objectql lifecycle service, the spec i18n resolver, ui);
none touches this diff's files. The affected closure was rebuilt after
the merge under the shared lock.
- `pnpm --filter @objectstack/metadata-protocol test` (pre-merge head
`2d469b09`): 312 files passed (4 skipped), 22 357 tests passed (26
skipped). `typecheck`: exit 0 pre- and post-merge (`--listFiles`
includes the new test file).
- `pnpm --filter @objectstack/objectql test` (pre-merge head
`2d469b09`): 548 / 548 files, 10 863 / 10 863 tests.
- Post-merge, scoped (§10): `protocol.named-base-overlay-write.test.ts`
34 / 34; objectql `protocol-packageless-overlay-migration.test.ts` +
`protocol-publish-rollback.test.ts` 40 / 40; `pnpm --filter
@objectstack/spec check:generated`: all 14 artifacts up to date.
- `pnpm lint`: exit 0 (pre-merge head `2d469b09`; the merge adds no
lintable change of this PR's).
- Red on `5fa9375e`: the two-row pin answered the named-base sentence,
`403 NOT_OVERRIDABLE`; the D2-gate pin read that same sentence.
<details><summary>Gates: dispatch-gates --commands, each exit code
captured before any pipe</summary>
61 derived families (head `2d469b09`; the list derived at `abd5d69f` is
byte-identical): 60 exit 0, 1 NOT MEASURED. `dispatch-gates --ran`: exit
0, "61 derived famil(ies) accounted for — 60 run, 1 NOT-MEASURED".
- NOT MEASURED: `pnpm check:dual-build-cjs-loads` exit 3, "Run `pnpm
build` first. This is NOT a pass: nothing was measured" (70 packages
outside this diff's closure have no `dist/`); left to CI.
- `pnpm check:lean-entry-closure` first exited 3 (no
`packages/objectql/dist`); after `turbo run build
--filter=@objectstack/objectql` it exits 0 ("Admitted set held exactly
(15 packages)").
- `node scripts/check-plugin-teardown-shape.mjs --self-test` ran after
`git fetch origin 621a487…` (the pinned fixture commit): exit 0.
- Re-run on the merged head `abd5d69f`: `check:type-check-coverage`,
`check:type-check-debt`, `check:durability-log-level`,
`check:nul-bytes`, `check:cross-package-test-inputs`,
`check-empty-changeset`, `check-adr-0087-registration`,
`check-changeset-no-major`: all exit 0.
</details>
Change set: 5 paths, +134 / −8 against `5fa9375e` (two source files +33
/ −2, two test files +93 / −6, one changeset +8).
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(not touched)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: n/a.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the maintainer brief
is Chinese by the seat's convention).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01Utp6CXdBvpJrhnHyW825Hb
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Utp6CXdBvpJrhnHyW825Hb)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 343ddb2 commit 713beba
5 files changed
Lines changed: 134 additions & 8 deletions
File tree
- .changeset
- packages
- metadata-protocol/src
- objectql/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1906 | 1906 | | |
1907 | 1907 | | |
1908 | 1908 | | |
1909 | | - | |
| 1909 | + | |
1910 | 1910 | | |
1911 | 1911 | | |
1912 | 1912 | | |
| |||
2025 | 2025 | | |
2026 | 2026 | | |
2027 | 2027 | | |
2028 | | - | |
| 2028 | + | |
| 2029 | + | |
| 2030 | + | |
| 2031 | + | |
| 2032 | + | |
| 2033 | + | |
| 2034 | + | |
| 2035 | + | |
| 2036 | + | |
| 2037 | + | |
| 2038 | + | |
| 2039 | + | |
| 2040 | + | |
| 2041 | + | |
| 2042 | + | |
| 2043 | + | |
| 2044 | + | |
| 2045 | + | |
| 2046 | + | |
| 2047 | + | |
| 2048 | + | |
| 2049 | + | |
| 2050 | + | |
| 2051 | + | |
| 2052 | + | |
2029 | 2053 | | |
2030 | 2054 | | |
2031 | 2055 | | |
| |||
2081 | 2105 | | |
2082 | 2106 | | |
2083 | 2107 | | |
| 2108 | + | |
| 2109 | + | |
| 2110 | + | |
2084 | 2111 | | |
2085 | 2112 | | |
2086 | 2113 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
648 | 648 | | |
649 | 649 | | |
650 | 650 | | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13088 | 13088 | | |
13089 | 13089 | | |
13090 | 13090 | | |
| 13091 | + | |
| 13092 | + | |
| 13093 | + | |
| 13094 | + | |
13091 | 13095 | | |
13092 | 13096 | | |
13093 | 13097 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
612 | 612 | | |
613 | 613 | | |
614 | 614 | | |
615 | | - | |
616 | | - | |
617 | | - | |
618 | | - | |
619 | | - | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
620 | 626 | | |
621 | 627 | | |
622 | 628 | | |
| |||
630 | 636 | | |
631 | 637 | | |
632 | 638 | | |
633 | | - | |
| 639 | + | |
634 | 640 | | |
635 | 641 | | |
636 | 642 | | |
| |||
0 commit comments