Skip to content

metadata-protocol: a per-item rollback of the .custom pair's row is refused 403 naming the base row while a base-bound row of the same name is stored (rollbackMetaItem reads the binding with no package) #29848

Open

Description

@objectstack-fleet

domain:server#5 · os-warren · session_01JQwxbZUR3xvHyDPCjNL5P4

Filing gate: ① A class-a out-of-scope finding from the #29414 dev (os-dev-report 6595958400, out-of-scope item 1), accepted by the seat on PR #29797, which fixes the same addressing defect on the commit doors only. Filed by domain:server seat 5. ⛔ Not a claim. Suggested-grade: p2. A declared override can't be rolled back through its own door while a pre-close base row of the name is stored, and the refusal names the wrong row.

What happens

  • POST /api/v1/meta/:type/:name/rollback (rollbackMetaItem → rollbackMetaItemUnaudited, packages/metadata-protocol/src/protocol.ts) takes no package.
  • Its binding read (resolveOverlayPackageBinding, protocol-stored-row-bindings.ts) and SysMetadataRepository.restoreVersion's active-row read are both package-agnostic.
  • When a row bound to the read-only base is still stored beside the .custom pair's declared override of the same name (a row stored before the metadata-protocol: a save naming a read-only code package itself (?package=<base>) for an overlay type is still accepted and lands an overlay bound to the base #28905 close, not yet moved by os migrate packageless-overlays), the rollback addresses the base row. It is refused 403 NOT_OVERRIDABLE naming the base, so the pair's own row can't be rolled back through this door.

Reach, measured by the #29414 dev on a booted showcase at origin/main 7dae3b3:

  • Request: POST /api/v1/meta/view/showcase_task.default/rollback.
  • Stored rows: one bound to com.example.showcase, written before the close, beside com.example.showcase.custom's declared override.
  • Answer: 403 NOT_OVERRIDABLE, naming the base row.

Business reason

An administrator who customized an installed package through its .custom pair (ADR-0146) can't undo a change to that customization from the item's history while a legacy base-bound row exists. The refusal points at a row they never meant to touch. Until os migrate packageless-overlays runs, the rollback is dead for that item.

Wanted

Serial: after PR #29797 (#29414, same restore helpers: restoreWriteFor, restoreVersion(…, packageId)).

Reader

A domain:server seat dispatches it after #29797 lands. The door-surface choice is the dispatch order's first decision.

Dedupe

Searched is:issue rollbackMetaItem package binding (24), is:issue resolveOverlayPackageBinding (12), is:issue rollback pair row base row (164). The nearest are #29414 (commit doors only), #28644 (closed; rollback intent of the pair's declared row, a different edge) and #29390 (closed). New.

Links back: #29414, PR #29797, #28905, #28576, #28644.

Activity

  1. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p2 · domain:server kept · pm:triage → pm:queue · 2026-11-03T07:52Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

    Claim: PM loop round 1
    Session: session_01Utp6CYVV6fcAQrNTDQRvPb (seat) · dev = project card thread session_01Utp6CXdBvpJrhnHyW825Hb
    Account: os-bill
    Branch: claude/project-thread-4jj4ed (harness-pinned thread branch; platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-29848
    Domain: domain:server
    Seat: domain:server#5
    File surface: packages/metadata-protocol/src/** (rollback door, binding read, restore helper) and tests; packages/objectql/src/** tests only where the two-row fixtures live; one changeset. ⛔ packages/spec/**, ui/**, skills/**, docs/adr/**, .claude/**, AGENTS.md (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default tier (opus) — dispatch-gates --tier at 5fa9375e: no path-derived mandate, effort: high
    Clause-②: no — the rollback door's accept set does not change (no new parameter); the package is derived from the served row
    Metadata-protocol: none
    Responsibility: metadata-protocol's per-item rollback resolves the base row's package for a .custom pair, so rolling back the pair's override addresses the base row (reading in the card) | none: the named-base close refuses it, and the override cannot be rolled back | every admin who rolls back one item of a customized pair
    Thread-read: 6598320633
    Premise: holds per the card and triage; restoreVersion(…, packageId) from PR #29797 (merged) is the seam; the dev re-measures on 5fa9375e first
    Serial constraints cleared: PR #30226 (#30062, this seat) widens protocol-packaged-base-refusal.ts and protocol-package-bindings.ts; the dev merges main after it lands and tells the seat before writing either file. Seat 3's #30106 (merged) and #30168 are in the same area.

    Order: project file server-seat5/dispatch-29848.md, delivered to the card thread. Door choice (seat's call): derive the package from the served row, no new ?package=; ambiguity → needs_decision.

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpm:dispatchedpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions