Repository navigation
Description
Found on a real isolated-posture dogfood boot (epic #12701 program; PM session session_01SVYmuhHW6qZmNBqciaS7BN, 2026-08-27).
Measured
- hotcrm@main built with its pinned released toolchain (
@objectstack/cli17.1.0) emitsdist/objectstack.jsoncarrying 75 occurrences ofallowPurge/allowRestore— hotcrm source contains zero; the released builder injects the (then-legal) permission bits. - Booting that artifact on the current framework main (
packages/spec17.2.0 — still the v17 line) through cloud's composed-artifact mode (OS_COMPOSED_ARTIFACT_URL) refuses the boot:invalid_typeatpermissions[5].objects.crm_campaign_member.allowPurge, the spec: retire theallowRestore/allowPurgepermission props (ruled 2026-08-26; M2 anchor stays open, keys return with M2) #12497retiredKeytombstone message verbatim ("Runos migrate meta --from 17…"). - The mechanical fix EXISTS in the platform already:
packages/spec/src/conversions/registry.ts(~line 8123) declares theallowRestore/allowPurgestripKeys conversion for stored permissions. The stored-metadata read path runs conversions (packages/metadata-protocol/src/protocol.ts, stored-row conversion machinery ~line 3916). The artifact-ingestion doors do not: neither the frameworkOS_ARTIFACT_URLpath (whoseartifact-boot-migration.tscovers physical schema drift only) nor cloud's composed path applies key conversions before the strict parse. - Workaround used to unblock the dogfood run (proving the conversion is sufficient): stripping the two keys from a copy of the artifact makes the same boot succeed and the whole multi-org battery pass.
Why this is a v17-line compatibility break, not a v18 migration story
The removal shipped inside the v17 line (spec 17.1.0 → 17.2.0). Every already-built marketplace/composed artifact — including the one cloud's own acceptance builds at its pin — becomes unbootable the moment a deployment's runtime moves to ≥17.2, with no operator action available except hand-editing an artifact JSON (os migrate meta targets sources, not built artifacts). The epic-#12150 line's own upgrade promise ("smooth upgrade for existing installs") is what breaks.
Suggested shape (dev/triage to confirm)
Run the ADR-0087 conversions over a versioned artifact's metadata at every artifact-ingestion door (the artifact carries its authored specVersion/protocol handshake, so "older 17.x, convert forward" is decidable), the same way the stored-row read path already does — one policy, not per-door copies. Whether the seam lands in the framework (artifact parse) or each consumer (cloud composed loader) is the routing decision; the framework parse is the single place all doors share.
Repro is cheap: build hotcrm at its released toolchain, boot on current main via either artifact door.
Activity
huangyiirene commentedon Aug 27, 2026
Triage:
domain:cli· type Bug ·pm:queue·target:v17·priority:p0. This is the first p0 and the first release-board entry this seat has set in days, so both are justified explicitly below rather than asserted.Routing, measured not guessed. The ingestion door the fix has to run at is
packages/cli/src/utils/artifact-boot-migration.ts— located by search, with its consumer atpackages/cli/src/commands/serve.tsand two existing self-tests beside it.packages/cliisdomain:cliby the lane table. The other two files the card names are reference, not landing surface:packages/spec/src/conversions/registry.tsalready declares theallowRestore/allowPurgestripKeys conversion (⇒ ⛔ no spec change expected), andpackages/metadata-protocol/src/protocol.tsis the model to copy — the stored-row read path that already applies conversions.packages/metadata-protocol(engine lane) rather than just calling into it, that is a cross-domain PR — declare the full file surface in the claim and take the cross-domain exception path, ⛔ don't widen silently.packages/spec/src/**change after all, Clause-② fires on the path leg and it is not enqueueable below the contract-review tier. Expected not to, but check the diff rather than the plan.Why
priority:p0— the binary question is not "is it severe" but "is it live", and it is:packages/spec17.2.0 is published, not pending: objectui'spackages/coredepends on"^17.2.0"and resolves it. So the breaking runtime is already out.@objectstack/cli17.1.0 emits 75 occurrences of keys its own source contains zero of, because the released builder injects them.os migrate metatargets sources, not built artifacts; the only unblock found was hand-editing artifact JSON.⇒ Live, released-surface, no workaround, self-inflicted by our own release. That is what p0 is for. ⛔ p0 does not exempt the same-file serial queue or the claim protocol.
target:v17— three of the four blocking classes are met, not one: ① a defect users hit today on the released surface; ③ already-built artifacts in the wild, unreachable by any migration tool we ship; ④ unambiguously an apology in the release notes. The epic-#12150 line's own "smooth upgrade for existing installs" promise is the thing that breaks.Scope note that should shape the fix — read #1883 before choosing strip-vs-convert. #12497 retired these keys with the M2 anchor left open: the keys return with M2 (#1883,
pm:on-hold). So the conversion must be a versioned forward conversion keyed off the artifact's authoredspecVersion, ⛔ not an unconditional strip — an unconditional strip becomes wrong again the moment M2 lands and the keys are legal once more. The card's own suggested shape says this ("the artifact carries its authoredspecVersion/protocol handshake, so 'older 17.x, convert forward' is decidable"); the M2 return is the reason it is load-bearing rather than stylistic.Cross-repo half, recorded not filed: cloud's composed-artifact path (
OS_COMPOSED_ARTIFACT_URL) needs the same treatment and is a second door. ⛔ Not filed by this seat — cloud is outside this session's repo scope, and by the standing rule the consuming-side follow-up is filed by the seat that accepts this PR. Whoever does that should file it in cloud with aBlocked-by:pointing here, since the framework parse is the shared policy the cloud door should consume rather than reimplement.Repro is cheap and stated: build hotcrm at its released toolchain, boot on current main via either artifact door. The workaround already run (stripping the two keys from an artifact copy → boot succeeds, full multi-org battery passes) is a positive control proving the conversion is sufficient, which is what makes this a queue card rather than an investigation.
Dedup run over the retired-key / artifact-conversion family: no twin. #12497 (closed) is the retirement that caused it; #1883 is the M2 project that will reverse it.
Generated by Claude Code
os-zhuang commentedon Aug 28, 2026
Claim: PM loop round 2 — maintainer direct-dispatch channel (2026-08-27/28 live PM chat: 「继续」 following the dogfood report that filed this card; p0 jump per the triage grading)
Session:
session_01SVYmuhHW6qZmNBqciaS7BNBranch:
claude/issue-12772-artifact-conversion-doorWorktree:
objectstack-issue-12772Domain:
domain:cliFile surface:
packages/cli/src/utils/(artifact ingestion door + its self-tests),packages/cli/src/commands/serve.ts(consumer),.changeset/;packages/specandpackages/metadata-protocolare REFERENCE surfaces per the triage comment — touching either is a declared stop-and-report boundary, not a quiet widening (stop on breach; explain in the report)Container & model: M,
mode:subagent,model: claude-fable-5— clause-② content-limb judgment: accepting previously-refused 17.x artifacts changes accept behaviour, so the card dispatches at the contract-review tier regardless of the expected spec-free path (dispatch-gates run at current main reports no path-derived mandate for the cli surface, derived not recalled)Clause-②: yes
Serial constraints cleared: none — no open in-flight claim or PR touches
packages/cli/src/utils/(checked the lane's openpm:dispatchedset andgit log origin/main -5 -- packages/cli/src/utils/); sibling fix #12803 (plugin-auth logger receiver) is merged and disjointGenerated by Claude Code
os-zhuang commentedon Aug 28, 2026
Dev claim: dispatched os-dev subagent of PM session
session_01SVYmuhHW6qZmNBqciaS7BN(round 2 dispatch) — this comment is the dev seat's own claim, distinct from the PM claim above.Branch:
claude/issue-12772-artifact-conversion-door(pushed, empty probe)Worktree:
objectstack-issue-12772File surface:
packages/cli/src/utils/(artifact ingestion door + self-tests),packages/cli/src/commands/serve.ts(consumer),.changeset/.packages/specandpackages/metadata-protocolare reference surfaces per triage — a diff touching either is a stop-and-report boundary.Generated by Claude Code
os-zhuang commentedon Aug 28, 2026
os-dev-report
{ "issue": 12772, "status": "done", "branch": "claude/issue-12772-artifact-conversion-door", "pr": "https://github.com/objectstack-ai/objectstack/pull/12843", "premise_still_valid": true, "summary": "Fixed by a versioned ADR-0087 forward conversion at the framework artifact-ingestion door. Measured first: the parse point is NOT packages/cli - every framework artifact door (OS_ARTIFACT_URL, OS_ARTIFACT_PATH, dist convention, HMR reload, environment-artifact envelope) funnels to MetadataPlugin._parseAndRegisterArtifact in packages/metadata, confirmed by booting the real 17.1-built hotcrm artifact on main f75a38a (tombstone invalid_type at permissions[0].objects.crm_lead.allowRestore, 'Plugin startup failed: com.objectstack.metadata'). New policy applyArtifactForwardConversions in packages/metadata-core (beside the protocol handshake; spec-only dependency so the cloud follow-up can consume it): declared engines.protocol floor below the running spec version replays the full conversion chain retired-entries-included before the strict parse; floor at-or-above converts nothing and the tombstone stays the authority; undeclared range replays (stored-row posture); unresolvable runtime version closes the window. Notices surface operator-visibly and deduped, one summary line per conversion per artifact, modeled on the stored-row pass. Neither stop boundary crossed: zero packages/spec and zero packages/metadata-protocol changes (the conversions registry is consumed, not extended).", "tests": "Union at head 6d097a6, clean tree: metadata-core typecheck (src+test tsc programs; new files confirmed in-program via --listFiles) green; metadata-core suite 14/14 files, metadata suite 36/36 files green (incl. new plugin-artifact-forward-conversion.test.ts pinning accept on the real derived fixture - 75+75 occurrences verbatim from the 17.1-built artifact with a premise-guard test - refuse at the tombstone with prescription text for a manifest floored at the installed spec version, dedupe of the operator summary, and the envelope branch). dispatch-gates.mjs derived at 983ca39/6d097a6 (no paths, merge-base f75a38a): all 24 runnable derived+convention gates exit 0 at their own verdict lines (changeset-gate-self-tests, cross-package-test-inputs, durability-log-level, objectql-double-limit, objectui-changeset, page-declaration-shape, pm-half-states, published-files, slot-lookup, stack-collection-maps, test-source-alias, type-source-resolution, adr-0087-registration, changeset-no-major, ci-filter-parity, comment-mask-adoption, empty-changeset, plugin-teardown-shape, release-rehearsal-clone self-test, query-options-erasure, type-check-coverage, engine-double-contract, where-matcher, nul-bytes). check:type-check-debt --re-measure green after full packages closure build ('none above its recorded number'; an earlier +1 drift in @objectstack/metadata from my own test file's extensionless import was fixed, not ledgered). Full-repo pnpm lint (eslint --no-inline-config) exit 0 in 70s - full run, no narrowing needed. docs-audit check-affected-docs and check-drift-comment exit 0. scripts/pm/check-half-states.mjs NOT MEASURED locally (exit 3 PREREQUISITE NOT MET, container token is the proxy placeholder, not a credential) - CI owns it. End-to-end positive control: OS_ARTIFACT_URL boot of the real hotcrm artifact refuses on main and completes on the branch ('Migration complete (37 plugins started against configured DB)', exit 0) with exactly one conversion summary warn covering 150 sites.", "gates": "all derived + convention families green at 6d097a6 except check-half-states (NOT MEASURED locally, prerequisite: no GitHub credential in container; CI-owned)", "files_changed": [ "packages/metadata-core/src/artifact-forward-conversion.ts (new policy)", "packages/metadata-core/src/artifact-forward-conversion.test.ts (new, 16 tests)", "packages/metadata-core/src/protocol-handshake.ts (export keyword on resolveDeclaredRange)", "packages/metadata-core/src/index.ts (export)", "packages/metadata/src/plugin.ts (door wiring + deduped operator surfacing)", "packages/metadata/src/plugin-artifact-forward-conversion.test.ts (new, 5 tests)", "packages/metadata/src/__fixtures__/hotcrm-17.1-built-permissions.artifact.json (real derived fixture, 30KB)", ".changeset/artifact-forward-conversion-door.md (patch: metadata-core, metadata)" ], "deviations": [ "Landing surface differs from the claimed file surface (packages/cli/src/utils + serve.ts): the PM mechanism assumption named packages/cli as the landing zone with instruction to measure; measured, the strict-parse funnel is packages/metadata (MetadataPlugin) and the policy home is packages/metadata-core. This comment amends the claim's file surface accordingly. Zero cli changes needed - serve.ts consumes the door unchanged.", "check-half-states not runnable in this container (no GitHub credential) - reported NOT MEASURED, not red.", "eslint executed as the full repo run (fits the foreground budget at 70s light-load), so no narrowing declaration is needed." ], "mcp_calls": "6 - claim comment, create_pull_request, dedup search + positive control search, issue_write (finding #12844), this report comment; issue body + comments were read zero-quota via the public page payload", "open_questions": [], "out_of_scope_findings": [ "filed as #12844 (label: finding): AppPlugin registers artifact security metadata in memory from the RAW bundle via registerInMemory, bypassing the door conversion - a second unconverted reader of the same bytes; no reachable defect today (measured green boot; the retired bits gate nothing), graded for triage.", "NOT filed per triage ruling: the cloud composed-artifact door (OS_COMPOSED_ARTIFACT_URL) follow-up card belongs to the seat that accepts PR #12843, filed in cloud with a Blocked-by link; the policy was landed in metadata-core so that door can consume it." ] }Generated by Claude Code
os-zhuang commentedon Aug 28, 2026
Review of record — PM ACCEPT + clause-② contract review PASS, both for PR #12843 at head
6d097a6.Contract-review chain (
needs:contract-reviewhung on both carriers this stroke, reviewed, cleared same-stroke below): eligibility per the downgrade fuse's fresh machine reading — this seat'sexternal_metadata.last_served_model=claude-fable-5=CONTRACT_REVIEW_TIER(dispatch-seat self-review path). The clause-② path limb did NOT fire (zeropackages/specchanges — the registry is consumed, not extended); the content limb (previously-refused 17.x artifacts now accepted) is what this review covers, read from the diff:engines.protocolfloor vs the running spec (fullx.y.z). Floor < runtime → full ADR-0087 chain replay (retired entries included) before the strict parse; floor ≥ runtime → nothing replayed, tombstones stay the authority (pinned: a current-version manifest with the same keys still refuses, and the pin asserts the tombstone's prescription text verbatim, not a bare throw); undeclared range → replay (stored-row posture); unresolvable runtime version → window closed. The M2 return case ([security][立项位] M2 权限生命周期(undelete/purge)功能与 allowRestore/allowPurge RBAC 同批建设(evaluator 已 fail-closed,allowTransfer 已 enforced) #1883) is structurally protected — artifacts authored at ≥ the retirement version are never stripped by history.applyArtifactForwardConversionslives inmetadata-core(spec-only dependency, deliberately consumable by cloud's composed door), the ADR-0087 registry in spec remains the sole conversion table, and the door (MetadataPlugin._parseAndRegisterArtifact) is the single funnel all framework artifact doors share — measured, not assumed (the triage'spackages/clirouting was a located neighbour; the dev's producer-side landing is evidenced by the main-vs-branch boot control and is accepted as the real landing zone; noted: the landing packages sit in the engine lane while the card is routeddomain:cli— recorded here as the sanctioned producer-side deviation).PM checklist: draft ✓ base
main✓Fixes #12772first line, no stray closure keywords ✓ 8 files = the report's list; changeset present ✓ nocontent/docs/releases/edits ✓ real derived fixture with a premise-guard pin (75+75 occurrences) ✓ end-to-end positive control (real 17.1-built hotcrm artifact: refuses on mainf75a38a, boots clean on the branch with one summary line, exit 0) ✓ both stop boundaries held (zero spec, zero metadata-protocol; the one adjacent edit is anexportkeyword onresolveDeclaredRangein metadata-core's own handshake) ✓ out-of-scope finding filed as #12844 (AppPlugin raw-bundleregisterInMemorysecond reader — measured non-defect today, graded for triage).Landing: waits on full CI green on
6d097a6; on green this seat flips ready → merge queue (clause-② enqueue gate satisfied: dispatched AND reviewed atCONTRACT_REVIEW_TIER). At acceptance-time per the triage record, the cloud follow-up card (composed door consumes this policy) is filed by this seat with aBlocked-by:link — next comment.Generated by Claude Code
4 remaining items
os-zhuang commentedon Aug 28, 2026
⛔ Contract-review gate RE-ARMED on both carriers — this seat stands down on landing, and says why rather than leaving the card looking merely idle.
What changed. PR #12843's head moved to
7eff896(patch round 3:git merge origin/main+ a re-aimed refuse-direction pin) after the PASS conclusion recorded above, which coveredb02ec81. Percontract-review.md's re-hang rule — "仅当 head 在结论之后移动、或根本无结论时才重挂" — the gate re-arms for the new head.Why this seat cannot clear it. The downgrade fuse's machine reading (
get_session, taken this stroke) now returnsexternal_metadata.last_served_model=claude-opus-5;CONTRACT_REVIEW_TIERisclaude-fable-5. Reading ≠ tier ⇒ the review is skipped wholesale and the label stays put; a card waiting outside the queue is the safe state. ⛔ Contract review does not take the quota-exhaustion downgrade exemption — that exemption covers dispatch, and the review exists precisely to compensate a dispatch below the floor.State of the PR, so the next tier-eligible seat starts from facts, not a re-derivation:
7eff896: 32/32 checks green,mergeable_state: clean, ready (not draft), not enqueued — the merge queue dequeued it atb02ec81(CI_FAILURE) and auto-merge did not survive.expect.unreachablefired on the queue's merge tree. Reproduced on the locally merged tree first, then re-aimed — not weakened: the pin now probes a NON-default value (allowRestore: true, authorable on the 17.1 surface, never emitted by a default) and still demands the tombstone plus prescription; a new pin holds the ruling's other half at the door (current-version pure residue parses clean, keys stripped by the schema, door window CLOSED with zero conversion notices).git diff b02ec81...7eff896restricted to this PR's own authorship is exactlypackages/metadata/src/plugin-artifact-forward-conversion.test.ts(+73/−…); every other path in that range arrived via the merge of main. The policy module, the door wiring, and the accept/refuse boundary are byte-unchanged from the reviewed head. That is a fact for the reviewer to verify, not a self-clearance — the tier gate is not this seat's to waive.To land it, one of: a seat running at
CONTRACT_REVIEW_TIERreviews the delta and clears both carriers (then ready → queue); or the maintainer rules on it directly (the human floor is always above this gate).Generated by Claude Code
os-zhuang commentedon Aug 28, 2026
🤝 HANDOVER — claim RELEASED, contract review + landing pass to the incoming PM seat
Maintainer order, 2026-08-28, live PM chat, verbatim and untranslated: 「更新你的 epic 卡片,我让新人接手。 12843 也让新项目经理用 fable 审核」.
The chartering PM session
session_01SVYmuhHW6qZmNBqciaS7BNstands down on this card. The assignee is cleared deliberately so the incoming seat can claim it cleanly — this is a released handover, not a stale claim and not a reclaim.pm:dispatchedstays because the dev work IS delivered and in flight toward landing; only the tier-gated review and the landing remain.Do these in order
1. Prove your tier before anything else.
get_session(claude-code-remote MCP, no args) →external_metadata.last_served_modelmust equalCONTRACT_REVIEW_TIER(claude-fable-5, single-sourced inscripts/pm/dispatch-gates.mjs). ⛔ Self-reported tier is not a reading. If it is anything else, stop and leave both labels hung — a card waiting outside the queue is the safe state, and contract review does not take the quota-downgrade exemption.2. Claim it. Assign yourself + a
Claim:comment carrying YOUR session id. This is a review-and-land claim, not a dev claim: the branchclaude/issue-12772-artifact-conversion-dooralready exists and needs no new worktree unless you decide changes are required.3. Review the delta — it is small, and its boundary is measured. The gate re-armed only because the head moved (
b02ec81→7eff896) after the previous PASS.git diff b02ec81...7eff896looks large because the range contains agit merge origin/main; this PR's own authorship in that range is exactly one file:packages/metadata/src/plugin-artifact-forward-conversion.test.ts. Everything else arrived from main (#12845, #12841, #12847, …). What to verify rather than take on trust:packages/metadata-core/src/artifact-forward-conversion.ts), the door wiring (packages/metadata/src/plugin.ts) and the accept/refuse boundary are byte-unchanged from the reviewed head;allowRestore: true— authorable on the 17.1 surface, never emitted by a default) and still demands the spec: retire theallowRestore/allowPurgepermission props (ruled 2026-08-26; M2 anchor stays open, keys return with M2) #12497 tombstone plus the prescription text;Prior conclusions, so you review the increment and not the whole card again: PASS at
b02ec81· delta addendum · gate re-armed / stand-down.4. On PASS, clear the gate on BOTH carriers (this card and PR #12843) with the read-modify-write + read-back discipline, then land: fresh
mergeable_stateread → closing-keyword scan (the body's first line isFixes #12772; confirm no second closing keyword pointing at another open card) → true-green check on the current head → the PR is already ready, so enable auto-merge → queue.5. If the queue dequeues it again, read the merge-queue triage comment on the PR for the failure signature before deciding. ⛔ Do not blind re-enqueue — that rebuilds every PR behind it. The last dequeue was not this PR's defect: main landed #12845 mid-flight and moved the semantics under a pin; it was reproduced on the locally merged tree, then the pin was re-aimed rather than weakened.
6. After merge: confirm this card closed by that PR and that no other card was closed alongside it; objectstack-ai/cloud#1688 (
Blocked-by:this card) then unblocks on its own — cloud consumes the framework via workspace links, so merged means installable there, no release wait. Epic #12701 closes on this merge.State facts you should not have to re-derive
7eff896: 32/32 CI checks green,mergeable_state: clean, ready (not draft), not enqueued (auto-merge did not survive the dequeue).packages/specand zeropackages/metadata-protocolchanges; the conversions registry is consumed (applyConversionswithincludeRetired: true), never extended. The one adjacent edit is anexportkeyword onresolveDeclaredRangein metadata-core's own handshake.registerInMemoryis a second, unconverted reader — no reachable defect today) and dispatch-gates:check:dispatcher-error-vocabularyis invisible to path derivation — a new error/notice code literal ships a green local union and a red CI (measured on the #12843 patch rounds) #12850 (dispatch-gates cannot see a new error-code literal, which is why this PR shipped a green local union and a red CI twice).check:type-check-debt --re-measureunderNODE_OPTIONS=--max-old-space-size=4096(CI's tsc heap ceiling; the container default hides the failure).Generated by Claude Code
hotlong commentedon Aug 28, 2026
Claim: incoming PM seat (epic #12701 takeover) — review-and-land claim
Session:
local_6f4b2700-a5fd-43ae-98a2-65e25e1dfe95Branch:
claude/issue-12772-artifact-conversion-door(already exists; review-only claim — no new worktree unless changes prove necessary)Domain:
domain:cli(landing surface recorded as metadata-core/metadata, sanctioned deviation per the triage record)Tier reading (machine): session model =
claude-fable-5=CONTRACT_REVIEW_TIER✓ — taken this stroke via session metadata, not self-reportAuthority: handover comment above + maintainer order, 2026-08-28, live PM chat, verbatim: 「更新你的 epic 卡片,我让新人接手。 12843 也让新项目经理用 fable 审核」 and 「12843 你也帮我审核一下,没问题就合并」
Scope: review the unreviewed delta
b02ec81→7eff896at tier; on PASS clearneeds:contract-reviewon BOTH carriers (this card + PR #12843), then ready → merge queue; follow to MERGED; epic #12701 closes on this merge.Serial constraints cleared: none — the branch is the only in-flight work on this surface; the prior claim was deliberately RELEASED by the handover.
hotlong commentedon Aug 28, 2026
Contract review — PASS at tier (delta
b02ec81→7eff896), both carriers cleared this stroke.Reviewer: incoming PM seat, session
local_6f4b2700-a5fd-43ae-98a2-65e25e1dfe95, machine tier readingclaude-fable-5=CONTRACT_REVIEW_TIER. Prior PASS atb02ec81stands; this review covers only the re-armed delta. Every handover assertion was re-measured, not trusted:origin/mainmerge (f4a6b0c) and one test-only commit (7eff896, touches onlypackages/metadata/src/plugin-artifact-forward-conversion.test.ts, +64/−9). Verified viagit rev-list b02ec81..7eff896 --not origin/main.metadata-core/src/artifact-forward-conversion.ts), door wiring (metadata/src/plugin.ts), and the metadata-core policy suite are byte-identical across the delta (empty diffs, main side included).acceptRetiredDefaultResidue, confirmed present onorigin/mainspec); the delta adapts the pins to the ruled contract. The refuse pin is re-aimed, not weakened: current-version manifest + NON-default probe (allowRestore: true, premise-guarded — the fixture value is assertedfalsebefore flipping) still demands the spec: retire theallowRestore/allowPurgepermission props (ruled 2026-08-26; M2 anchor stays open, keys return with M2) #12497 tombstone and the prescription text, and the message assertion is tightened (toMatch(/allowRestore|allowPurge/)→toContain(allowRestore)).conversion '${conversionId}'(plugin.ts summary emission), and the accept-direction test pins that pathway positively.Proceeding per the landing sequence: fresh mergeable read → closing-keyword scan → true-green on
7eff896→ queue. Maintainer order on record (2026-08-28, live PM chat): 「12843 你也帮我审核一下,没问题就合并」.docs(metadata): correct the retiredKey tombstone's minor version in t…
docs(adr-0087): point the pre-addendum retirement claim at its correc…
docs(metadata): correct the retiredKey tombstone's minor version in t…
docs(spec): state the authoring-surface jurisdiction retiredFromLoadP…
fix(metadata-core,spec): the artifact door stops replaying the defaul…
docs(adr-0087): point the superseded retirement claim at its correcti…
feat(spec,metadata-core)!: every retired ADR-0087 conversion carries …