fix(metadata-protocol): revertCommit's soft-remove limb states its write intent per item, so a commit that CREATED an object can be reverted - #6768
Merged
Conversation
…ite intent per item (#6620) The limb that undoes an artifact a commit CREATED stated its intent as the constant 'override-artifact'. SysMetadataRepository.delete opens with assertAllowed(ref.type, opts.intent), which refuses every type that is not allowOrgOverride — 'object' among them — so a commit that created an object could not be reverted at all, and the first-build undo left the package half-reverted with success: false. The intent is now derived PER ITEM from isArtifactBacked, exactly as the sibling delete caller deleteMetaItem and the sibling revert caller rollbackMetaItem already derive it, so all three agree. The repository's gate is untouched: a genuinely artifact-backed item still resolves to 'override-artifact' and is still refused with NOT_OVERRIDABLE. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W6bLax4KMrSfnE1ydFU8Dw
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 4 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Aug 8, 2026
os-zhuang
marked this pull request as ready for review
August 8, 2026 15:04
This was referenced Aug 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6620
What was wrong
ObjectStackProtocolImplementation.revertCommithas two limbs. PR #6642 (#6563) fixed the one that RESTORES an edited artifact, where the intent was unstated and fell through torestoreVersion's?? 'override-artifact'default. The other limb — an artifact the commit CREATED, which the revert soft-removes — stated the same intent as a literal constant:SysMetadataRepository.deleteopens withthis.assertAllowed(ref.type, opts.intent)— the same gateputuses — which refuses every type whose registry entry is notallowOrgOverride.objectis exactly such a type, so a commit that created an object could not be reverted at all:This is the first-build undo — the Studio / AI flow that publishes a brand-new app and then undoes it. Every object the commit created stayed behind, the call answered
success: falsewith a populatedfailed[], and the package was left half-reverted: its overlay-allowed items removed, its objects not.rollbackToPackageCommitreverts through the same loop and inherited it, and there the symptom was quieter still — a per-item refusal never throws, so the rollback recorded the commit as reverted and answeredsuccess: truewhile the created object was untouched.Two different causes, one rhyming symptom: #6563 was an unstated intent, #6620 is a literal the caller wrote down.
The fix
The limb derives the intent per item from
isArtifactBacked, the way the sibling callers already do. The repository's gate and default are untouched.Premise check 2 — which delete caller was mirrored, and why
The issue asked that
deleteMetaItem's derivation be read first so the two delete callers agree. Read on the merge base:deleteMetaItemprotocol.ts:10716artifactBacked ? 'override-artifact' : 'runtime-only'(onsingularTypeForRepo)rollbackMetaItemprotocol.ts:10395artifactBacked ? 'override-artifact' : 'runtime-only'(onsingularType)revertCommitrestore limb (#6642)protocol.ts:10245this.isArtifactBacked(it.type, it.name) ? … : 'runtime-only'The two delete callers already agreed with each other and with the restore limb — there was nothing to reconcile, so mirroring is unambiguous. The one deliberate difference from
deleteMetaItemis the type spelling:deleteMetaItemfolds throughcanonicalizeMetaRequestType/PLURAL_TO_SINGULARbecause it takes a caller-suppliedrequest.type(#4432), whilerevertCommitreadsit.typeoff a stored commit item and already uses that same rawit.typeto buildref. Introducing a second spelling for one of the two facts inside one loop iteration would be the actual hazard, so this follows the region's existing convention (identical to #6642's restore limb). Canonicalizing the whole loop is a separate question and is not in this PR's ruled file surface.Also updated: the note #6642 left in the restore limb saying the soft-remove limb "still cannot be reverted (#6620)". That sentence is now false, so it is rewritten rather than left to mislead the next reader; the still-open #6621 half of that note is kept verbatim.
Tests
Extended
packages/objectql/src/protocol-commit-history.test.tsin place (the #6215 / #6642 pin suite, same harness, same realSysMetadataRepositoryover the in-memorysys_metadata/sys_metadata_history/sys_metadata_commitdouble). Six new cases:revertedCount 1,failed [], row gone, and an append-onlyoperation_type: 'delete'tombstone withmetadata: null(ADR-0067 §5: soft, recoverable, not a vanished lineage)code: 'NOT_OVERRIDABLE'plus the condition's own first sentence, row keptrollbackToPackageCommitinherits it through the same loopEnvelope split follows #6642:
revertCommitconverts a per-item throw into afailed[]record whose declared shape is{ type, name, error, code? }with nostatus, socode+ the message's first sentence are asserted here, and the full{ code, status }pair belongs to the throwing surface.Reverse verification — and one honest inversion
Directions were predicted before running, and one of them is not before-green/after-red. Reporting it as measured rather than forcing the template:
'override-artifact''runtime-only'Pin 3 cannot go red by removing the fix, because removing the fix refuses everything — the constant was accidentally right for exactly this one case. Its real red direction is the wrong fix, i.e. the one-line "just make objects work" edit that swaps one constant for the other and would let a revert tombstone an artifact a code package genuinely ships. Both directions were run: 5 red / 18 pass on the old constant, 2 red / 21 pass on the inverted constant, 23/23 only on the per-item derivation. A pin that reads as coverage but cannot go red in any direction would not be coverage; this one goes red in the direction that actually threatens it.
Commands
Every
check:*enumerated from.github/workflows/lint.yml's ESLint job was run one by one, all pass:slot-lookup,query-options-erasure,verify-stand-in,nul-bytes,doc-authoring,docs-audit-scope,role-word,quick-reference-counts,adr-anchors,org-identifier,authz-resolver,service-providers,route-envelope,error-code-casing,wildcard-fallthrough,meta-type-normalized,init-service-contract,durability-log-level,startup-registry-verdict,objectui-changeset,release-notes,release-body,node-version,workflow-status-functions,shard-attestation,published-files,engine-double-contract,kernel-hook-pairs,resume-authority-declared,driver-memory-census,merge-driver,spec-parsed-alias. Plus from the type-check job:type-check-coverage,driver-conformance,stall-guard,skill-frame-sync,skill-compatibility, and speccheck:generated --reconcile-only.origin/mainmoved 12 commits during the work (including #6708 / #6709 on other regions of this same file); merged clean, reinstalled, rebuilt, and the numbers above are the post-merge run.No new fake engine was added — the harness reuses the existing double, which already routes both write verbs through
assertEngineDeleteDispatch/assertEngineUpdateDispatch.Scope
packages/metadata-protocol/src/protocol.ts(revertCommitsoft-remove limb only) +packages/objectql/src/protocol-commit-history.test.ts+ one changeset. #6621 (registry refresh after revert) targets the same region and was deliberately not absorbed.中文摘要
revertCommit的两条支路里,#6642 修好了「恢复被编辑的产物」那条;另一条——「软删除本次 commit 新建的产物」——把写入 intent 写成了常量'override-artifact'。仓库层delete开头就是assertAllowed(ref.type, opts.intent),会拒绝一切非allowOrgOverride的类型,object正是其一,于是新建了对象的 commit 根本无法回滚:这正是「发布一个全新应用再撤销」的首次构建撤销路径,对象全部留在原地,success: false,包处于半回滚状态;rollbackToPackageCommit走同一循环,症状更隐蔽——它照样返回success: true。现在按每一项从
isArtifactBacked推导 intent,与deleteMetaItem、rollbackMetaItem以及 #6642 的恢复支路完全一致(三处调用方口径统一)。仓库层的 gate 一行未改:真正由代码包提供的产物仍然解析为'override-artifact',仍然被NOT_OVERRIDABLE拒绝,这一条也一并钉住。反向验证有一条方向是反的,如实记录而非套模板:artifact-backed 拒绝那条用例修前修后都绿——因为修改前是「全都拒绝」,它恰好被常量蒙对了;它真正会变红的方向是错误的修法(把常量换成
'runtime-only')。两个方向都实测过:旧常量 5 红,反向常量 2 红,只有 per-item 推导 23/23 全绿。🤖 Generated with Claude Code
https://claude.ai/code/session_01W6bLax4KMrSfnE1ydFU8Dw
Generated by Claude Code