Skip to content

revertCommit cannot restore an object overlay at all — it passes no write intent, so restoreVersion defaults to override-artifact and the repository answers NOT_OVERRIDABLE #6563

Description

@baozhoutao

Found while implementing #6215 (the package-scoping half of the same call). Recording only, per Prime Directive #10 — it is a different cause on a different line, outside that issue's ruled file surface.

Evidence

ObjectStackProtocolImplementation.revertCommit (packages/metadata-protocol/src/protocol.ts, the existedBefore limb) restores an edited artifact with:

await repo.restoreVersion(ref, it.prevVersion, {
    actor,
    source: 'protocol.revertCommit',
    message: `revert commit ${request.commitId}`,
});

No intent. SysMetadataRepository.restoreVersion therefore defaults to intent: opts.intent ?? 'override-artifact', and put opens with assertAllowed(ref.type, opts.intent), which refuses any type whose registry entry is not allowOrgOverride. object is exactly such a type, so every object item in a reverted commit comes back in failed[]:

[NOT_OVERRIDABLE] 'object' is not allowOrgOverride in the registry.
Overlay-allowed: view, page, dashboard, app, action, report, dataset,
translation, email_template, book, permission, position, tool, skill.
Set OS_METADATA_WRITABLE to enable additional types at runtime.

Measured on a real SysMetadataRepository over an in-memory sys_metadata / sys_metadata_history double while writing #6215's pin tests: an object saved twice through saveMetaItem and then reverted through revertCommit returns revertedCount: 0, failedCount: 1 with that message. Package binding is irrelevant — a package-less object fails identically.

Why the sibling caller works

rollbackMetaItem derives the intent instead of defaulting it:

const artifactBacked = this.isArtifactBacked(singularType, request.name);
const intent: 'override-artifact' | 'runtime-only' = artifactBacked
    ? 'override-artifact' : 'runtime-only';

A runtime-created object (_provenance: 'org', the shape Studio's package workspace produces) is not artifact-backed, so the per-item rollback passes runtime-only and the same repository line admits it. revertCommit never computes that, so the two user-facing revert paths disagree about what is revertable — for the metadata type Studio creates most.

Why it matters

revertCommit is the package-commit "undo" (ADR-0067). A commit that edited an object — the common case for an AI-built or Studio-built app — reports partial failure and leaves the edit in place, while the same edit reverts fine one artifact at a time through the version-history revert. The failure is per item, so the revert also reports success overall with a populated failed[], which reads as a flaky revert rather than a systematic refusal.

rollbackToPackageCommit reverts through the same loop, so it inherits this.

Suggested direction (not a ruling)

Compute the intent from the artifact the same way rollbackMetaItem does, rather than defaulting it at the repository — the repository's default is right for callers that genuinely mean "override an artifact", and the bug is that this caller never states which of the two it is. Note revertCommit reverts a batch, so the intent is per item, not per call.

Related: #6215 (the package-scoping defect on the same restoreVersion call; its pin tests use view precisely because object cannot reach the scoping line), ADR-0067, ADR-0048.

Activity

  1. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    Contributor

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

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


    Generated by Claude Code

  2. self-assigned this
    on Aug 8, 2026
  3. baozhoutao commented on Aug 8, 2026

    @baozhoutao
    ContributorAuthor

    Claim: PM loop round 4 (domain:metadata seat, sticker #6367)
    Session: session_01KDU3qAuJyajAQm3GkUXdfA
    Branch: claude/issue-6563-revert-commit-write-intent
    Worktree: objectstack-issue-6563
    Domain: domain:metadata
    File surface: packages/metadata-protocol/src/protocol.ts — the revertCommit restore limb (per-item intent derivation, the rollbackMetaItem idiom) — + tests (the #6215 pin files in packages/objectql are the natural home: protocol-commit-history.test.ts already drives revertCommit against the real repository). Stop on breach; explain in the report.
    Serial constraints cleared: the triage serialization note said "land #6215 first" — PR #6574 MERGED 06:11Z; the other protocol.ts in-landing PR #6600 MERGED 07:1xZ (loadMetaFromDb region — different limb, now on main); #5839 (engine-core seat) still holds its claim on the ensureOverlayIndex region with no remote branch — no overlap with the revertCommit limb, merge-main discipline applies; #6479 (same file, updateData region) deliberately held for the NEXT slot — never same-batch with this card.
    Container class: M — mode:subagent, shared container.


    Generated by Claude Code

  4. baozhoutao commented on Aug 8, 2026

    @baozhoutao
    ContributorAuthor

    ACCEPT (PM review, domain:metadata seat, session session_01KDU3qAuJyajAQm3GkUXdfA) — PR #6642, now ready + queued.

    Shipped: revertCommit's restore limb derives the write intent per item (isArtifactBacked → override-artifact, else runtime-only), exactly as rollbackMetaItem does; the repository's ?? 'override-artifact' default is untouched per the ruling, so the gate is stated rather than widened — the artifact-backed refusal is pinned alongside the fix and still answers NOT_OVERRIDABLE.

    Review verified against GitHub: 3 files exactly on the claimed surface (changeset, protocol.ts +28 confined to the restore limb, the #6215 pin suite extended in place); ESLint and TypeScript Type Check completed: success, 24 checks zero failures; patch changeset present. Reverse verification 4-red/13-green in the predicted direction, and the rollbackToPackageCommit case correctly asserts the stored body rather than success — because success: true was already true on the defect, so a status assertion would have been green on it. That is the right instinct and worth naming.

    Two caveats reported honestly rather than papered over, both accepted:

    1. The dispatch asked for "the restored registry state carries the right body" — measured not true today for any type (revertCommit has no registry write-through), so the pins assert the persisted row instead of manufacturing the claim. Filed as 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.
    2. The sibling soft-remove limb hard-codes override-artifact, so a commit that created an object still cannot be reverted — measured identical before/after, left untouched per the restore-limb-only surface. Filed as revertCommit's soft-remove limb hard-codes intent: 'override-artifact', so a commit that CREATED an object cannot be reverted either #6620.

    Both are on the lane's handoff ledger (seat sticker #6367) as the natural next cards on this seam.

    The issue closes on merge via the Fixes line.


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions