Skip to content

revertCommit's soft-remove limb hard-codes intent: 'override-artifact', so a commit that CREATED an object cannot be reverted either #6620

Description

@baozhoutao

Found while implementing #6563 (the RESTORE half of the same loop). Recording only, per Prime Directive #10 — a different line with a different cause (a hard-coded literal, not an unstated default), and outside #6563's ruled file surface.

Evidence

ObjectStackProtocolImplementation.revertCommit (packages/metadata-protocol/src/protocol.ts) has two limbs. #6563 fixes the existedBefore one. The OTHER limb — an artifact the commit CREATED, which the revert soft-removes — states its intent as a constant:

if (!it.existedBefore) {
    // Created by this commit -> soft-remove (metadata only; table stays).
    if (current) {
        await repo.delete(ref, {
            parentVersion: current.hash,
            actor,
            source: 'protocol.revertCommit',
            intent: 'override-artifact',
            state: 'active',
        });
    }
    reverted.push({ type: it.type, name: it.name, action: 'removed' });
}

SysMetadataRepository.delete opens with this.assertAllowed(ref.type, opts.intent) — the same gate put uses — which refuses any type whose registry entry is not allowOrgOverride. object is exactly such a type, so a commit that created an object cannot be undone.

Measured on origin/main @ e39dd66 against a real SysMetadataRepository over an in-memory sys_metadata / sys_metadata_history / sys_metadata_commit double (the #6215 harness in packages/objectql/src/protocol-commit-history.test.ts): an object created once through saveMetaItem under a Studio package workspace, recorded in a commit as existedBefore: false, then reverted through revertCommit:

{
 "success": false, "revertedCount": 0, "failedCount": 1, "reverted": [],
 "failed": [{ "type": "object", "name": "myapp_invoice",
   "error": "[NOT_OVERRIDABLE] 'object' is not allowOrgOverride in the registry. ...",
   "code": "NOT_OVERRIDABLE" }]
}

…and the sys_metadata row is still there afterwards (1 row remaining). The same measurement re-run on #6563's branch is byte-identical: fixing the restore limb does not touch this one.

Why it matters

This is the first-build revert — the AI/Studio flow that publishes a brand-new app and then undoes it. Every object the commit created stays behind, success is false with a populated failed[], and the package is left half-reverted (its overlay-allowed items removed, its objects not). rollbackToPackageCommit reverts through the same loop, so it inherits this.

Suggested direction (not a ruling)

Same shape as #6563's ruling, applied to the other limb: derive the intent from the artifact per item (isArtifactBacked -> 'override-artifact' : 'runtime-only'), exactly as the sibling caller rollbackMetaItem does, rather than stating a constant. A genuinely artifact-backed item must still be refused. The repository's gate is right; the caller is what never says which of the two cases it is.

Note the delete-limb equivalent of #6563's premise check is worth re-running before implementing: deleteMetaItem (the per-item delete path) should be read first to see which intent it derives, so the two delete callers agree.

Related: #6563 (the restore limb, same loop), ADR-0067, ADR-0005.

Activity

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