Skip to content

A successful revertCommit never refreshes the SchemaRegistry — the runtime keeps dispatching the reverted-away body until restart (the #4521 rule, missing on the batch path) #6621

Closed

Description

@baozhoutao

Found while implementing #6563. Recording only, per Prime Directive #10 — a different cause (a missing call) on a different line than the write intent #6563 fixes.

Evidence

rollbackMetaItem — the per-item revert — ends its restore with a registry write-through, and #4521 states why in that file:

// #4521 — a rollback is a live write like any other: the restored
// body must be the one the runtime dispatches on immediately, not
// after someone lists the type.
this.applyRegistryWriteThrough({
    type: singularType, name: request.name,
    item: result.item.body, packageId: rollbackPackageId,
});

revertCommit — the batch revert over the same repository call — has no equivalent. Its restore limb awaits repo.restoreVersion(...), pushes { action: 'restored' } and moves on; nothing re-registers the restored body.

Measured on #6563's branch (where an object can reach the restore at all — on origin/main @ e39dd66 it fails earlier with NOT_OVERRIDABLE), real SysMetadataRepository over the #6215 in-memory double: an object saved twice through saveMetaItem (v2 adds a due_date field), then reverted through revertCommit:

revertCommit  ->  { success: true, revertedCount: 1, failed: [] }
stored sys_metadata row fields  ->  ["name","amount"]            # reverted
SchemaRegistry.getObject(...) fields ->  [... ,"name","amount","due_date"]  # NOT reverted

Ownership survives (getObjectOwner(...).packageId === 'app.myapp', _provenance: 'org'), so this is purely the body going stale.

This is type-agnostic and pre-dates #6563: an overlay-allowed type (view, page, dashboard, …) has always been able to reach this limb and has always come back with the same split — #6215's pins assert the stored row and are silent on the registry. object merely makes it loud, because the registry copy is what CRUD dispatches on.

Why it matters

revertCommit answers success: true while the runtime keeps serving the body the operator just reverted away — a revert that looks done and is not, healing only at the next restart or re-hydration. That is exactly the failure mode #4521 named for the single-item path, one seam over. rollbackToPackageCommit reverts through the same loop, so a whole package rollback can report success and change nothing the running process can see.

Suggested direction (not a ruling)

Mirror rollbackMetaItem inside revertCommit's loop: use the restoreVersion result's body plus the row's own ownership key (resolveOverlayPackageBinding, per item, read the way #4636 requires so the re-register does not clash with already owned by package ...). The soft-remove limb needs the symmetric decision made deliberately rather than by omission — whether an unregister belongs there is a real question, not an obvious yes.

Worth deciding together with #6602 (which gates write-through on environmentId and never on org): a batch revert of org-scoped rows would inherit whatever that issue rules.

Related: #6563, #4521, #4636, #6215, #6602, ADR-0067.

Activity

  1. added
    pm:queueReady for the PM dispatch loop
    target:v17Release blocker for v17 — on the board until fixed, dropped as no longer valid, or accepted for GA
    on Aug 8, 2026
  2. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    Contributor

    Triage: pm:queue + domain:metadata + target:v17.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    Contributor

    Successor-pricing note from PR #6768 (#6620, same revertCommit region), recorded before this card is dispatched — domain:metadata PM, session session_01W6bLax4KMrSfnE1ydFU8Dw:


    Generated by Claude Code

  4. self-assigned this
    on Aug 8, 2026
  5. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    Contributor

    Claim: PM loop round 3 (domain:metadata seat, sticker #6367)
    Session: session_01W6bLax4KMrSfnE1ydFU8Dw
    Branch: claude/issue-6621-revert-registry-refresh
    Worktree: objectstack-issue-6621
    Domain: domain:metadata
    File surface: packages/metadata-protocol/src/protocol.ts (revertCommit region only — both limbs' registry-refresh insertion) + the #6215/#6642/#6768 pin suite (packages/objectql/src/protocol-commit-history.test.ts, extend in place) + one .changeset/*.md
    Serial constraints cleared: base must include 75e6871bf (PR #6768, same blocks — its pricing note mandates rebase-not-parallel) and 4bb6f01af (PR #6779 — applyRegistryWriteThrough now takes a required organizationId; any new call this card adds must use the new signature and pass the row's own org). Both are on origin/main as of this claim. #6562 rides in the same batch on disjoint regions (getMetaItems/getMetaItem read exits vs revertCommit). The card's own scoping note stands: the soft-remove limb's symmetric unregister is a deliberate decision to make and document, not an obvious yes — STOP → needs_decision if it changes contract semantics.


    Generated by Claude Code

  6. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    Contributor

    Review verdict: ACCEPT — PM domain:metadata seat, session session_01W6bLax4KMrSfnE1ydFU8Dw.

    PR #6807: the batch revert now honours #4521 on both limbs. Restore limb mirrors rollbackMetaItem's write-through with the ownership key read per item BEFORE the restore (#4636/#4867) and the row's own organizationId through #6779's required parameter — org-scoped rows inherit #6602's refusal by construction, not by a re-taken decision. The soft-remove limb's symmetric question was answered by measurement, exactly as the card demanded: "do nothing" IS the measured defect, a flat removeOverlayEntry was measured actively wrong (it deletes names a code package still ships), so the limb reuses deleteMetaItem's three-tier heal — org-gated, because reverse-verification run 4 showed the ungated form reproduces #6780's env-wide eviction from a second path. No contract movement beyond ADR-0067 §5, so no escalation was needed, and the reasoning lives in the PR body and code.

    Four-way reverse verification with per-run predictions, all exact; the two pins that cannot go red on removal are explained in the test file itself rather than in a report footnote. CI converged green (both required jobs by conclusion). Out-of-scope discipline: #6808 filed (the heal reaches the generic map but never objectContributors — distinct from #6725's write side), and the #6780 reproduction was commented onto the existing card after searching, not twinned.

    Flipping ready + queueing now. This clears the third target:v17 entry of the revertCommit family.


    Generated by Claude Code

  7. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    Contributor

    Landing confirmed — PR #6807 merged as 271cee131. A successful revertCommit now refreshes the SchemaRegistry on both limbs; the runtime dispatches the reverted body immediately, per the #4521 rule. This clears the target:v17 entry. Follow-up #6808 (map-vs-objectContributors on the heal) awaits triage; #6780 keeps the helper-level org-blindness. — domain:metadata PM, session session_01W6bLax4KMrSfnE1ydFU8Dw


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

pm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch looptarget:v17Release blocker for v17 — on the board until fixed, dropped as no longer valid, or accepted for GA

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions