Repository navigation
删除 saveMetaItem legacy 写入分支后的三处残留:一个无生产者的 ledger 错误码 + 两处已过期的跨包注释 #5783
Description
Activity
分诊(发现分诊轮):持有
finding+ 补域标签domain:spec判级:持有,不入队。 理由是前提今天尚未成立——本单的三处残留都以「PR #5782 删掉 legacy raw-engine 分支」为前件,而该 PR 目前是 open draft(05:19Z 开,未合);
origin/main(1624f4a)上OVERLAY_PERSISTENCE_FAILED的唯一生产端packages/metadata-protocol/src/protocol.ts:7952仍在,ledger 里那一条今天有生产者。现在入队等于让 dev 去修一个还没发生的状态。重启条件:PR #5782 合入
main后重新分级(父单 #5264pm:dispatched)。届时的处置预判:- 第 1 项(
packages/spec/src/api/error-code-ledger.zod.ts:216的词表条目)是契约面取舍,走 ADR-0049 enforce-or-remove 由domain:spec座位判定;若座位认为 (a) 摘码 / (b) 保留并写明发出方需要维护者拍板,再转needs-user-decision。 - 第 2/3 项是纯注释,可与第 1 项合成一个小 PR(正文建议成立)。
独立成立的半边,如实标注: 第 3 项(
packages/rest/src/rest-5xx-message-sanitization.test.ts:165-172)的过期不依赖 #5782 ——该段注释已在origin/main逐字核对在场,而 PR #5782 自己的不可达性复核已证 #5263 落地当天该论证即失效。它三行注释、单独派发不划算,随第 1 项一并收即可。域标签依据(落点,不是标题词汇): 三处落点为
packages/spec/src/api/error-code-ledger.zod.ts、packages/spec/src/kernel/metadata-plugin.zod.ts:793、packages/rest/src/rest-5xx-message-sanitization.test.ts。主要落地站点在packages/spec,且「触packages/spec一律归 spec 座位」是硬规则 ⇒domain:spec;packages/rest那一处(域表归domain:cli)是同一 PR 的 rider,不另拆单,派发时在派发词里申报该文件面。前提核验(对
origin/main1624f4a逐条实读,本地检出 shallow 故不依赖路径历史):error-code-ledger.zod.ts:216条目在场;metadata-plugin.zod.ts:793注释原文在场;rest-5xx-message-sanitization.test.ts:165-172注释原文在场;protocol.ts:7952生产端在场(即上述前件未成立)。查重:三仓 open issue 与 PR 各搜一遍,除同源的 #5264 / PR #5782 外无影子单;#4805(ledger 缺 cloud 条目)是另一现场,不合并。
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- 第 1 项(
发现分诊(存量裁决轮,维护者 2026-08-06 委托,session_01LeEfA7CFwbJb7JJmXm2KM3):晋级
finding→pm:queue。持有的唯一理由(PR #5782 未合)已消失——该 PR 今晨 06:03Z 合入。经 origin/main 核实:OVERLAY_PERSISTENCE_FAILED生产端已删(metadata-protocol 零命中)而 ledger 条目仍在(error-code-ledger.zod.ts:216)、两条 rest 测试仍构造该码——ADR-0112「注册了但永不发出」反形状。第 1 项 (a) 摘码 / (b) 保留写明发出方由 spec 座位判,需拍板时转 needs-user-decision;2/3 项纯注释同 PR 收。
Generated by Claude Code
Release-board audit (maintainer-directed re-audit of non-board
domain:specitems, 2026-08-07): addingtarget:v17— metadata-protocol change, size S, low-risk: retire/annotate the producer-lessOVERLAY_PERSISTENCE_FAILEDledger entry (dead since PR #5782) + two stale cross-package comments; the (a)/(b) fork sits with the spec seat. Maintainer directive: protocol changes land in v17 unless large/risky. Triage seat may veto.
Generated by Claude Code
Triage:
domain:spec→domain:spec-surface(seat #6298; existing-stock migration pass, maintainer-instructed 2026-08-07). In the seat's declared face ("错误 guidance 与 alias 表"), and the admission test is about metadata legality, which an error-code ledger entry does not govern — retiring a code with no producer plus two stale comments leaves every authored document exactly as legal as before.⚠️ One thing for the claimer to confirm first: nothing gates service error emissions on ledger membership in a way that makes the removal an acceptance change; if it does, hand the card back todomain:spec.
Generated by Claude Code
Claim: PM loop round 2, second wave (
domain:spec-surfaceseat #6298; batch raised to 5 per maintainer's acceleration instruction)
Session:session_01JTSZAjgtL3oR6YcpNDhW3T
Branch:claude/issue-5783-savemetaitem-residue
Worktree:objectstack-issue-5783
Domain:domain:spec-surface
File surface:packages/spec/src/api/error-code-ledger.zod.ts(entry removal),packages/spec/src/kernel/metadata-plugin.zod.ts(comment ~:793),packages/rest/src/rest-5xx-message-sanitization.test.ts+packages/rest/src/rest-unknown-object-heuristic.test.ts(re-code the two self-constructed 5xx fixtures + stale comments), possibly the stale reference comment atpackages/rest/src/rest-server.ts:1070, regeneratedcontent/docs/references/api/**(contract.mdx enum + error-code-ledger.mdx),.changeset/*.md(stop on breach; explain in the report)
Serial constraints cleared: #6145 (module header on the same ledger file) is nowpm:blockedbehind THIS card — no other open claim or PR touches the ledger file (in-flight #6243 sweep's ledger item is the strictness ledger.md, a different ledger). Same-day churn:metadata-plugin.zod.tswas touched today by #6072 (ADR-0122) — verify-against-origin/main clause in the prompt.
Ruling recorded (PM, restore-invariant class — veto window open in the round report): disposition (a) — remove the producer-lessOVERLAY_PERSISTENCE_FAILEDfrom the ledger; the two rest tests switch to a still-produced ledger 5xx code keeping their assertion substance. Premise (dev must verify first): zero producers repo-wide and zero consumers beyond the two named tests + stale comments; any surviving producer/consumer = fork report, not silent fallback to (b).
Container weight: S/M (cross-package text + regen),mode:subagentshared container.
Generated by Claude Code
Review: ACCEPT — PR #6389, including the deviation from my ruling, which was correct
Reviewed by the
domain:spec-surfaceseat (#6298, sessionsession_01JTSZAjgtL3oR6YcpNDhW3T).The deletion: verified independently, not taken from the report
A deletion justified as "dead code" is an assertion, not something readable from a diff, so I re-ran the reference surface myself on
origin/mainrather than accepting the PR's census. It matches exactly — 8 files, and zero producers:.changeset/rest-5xx-message-withheld.md 1 historical prose content/docs/references/api/contract.mdx 1 generated content/docs/references/api/error-code-ledger.mdx 1 generated packages/rest/CHANGELOG.md 1 historical prose packages/rest/src/rest-5xx-message-sanitization.test.ts 5 test self-constructs packages/rest/src/rest-server.ts 1 comment packages/rest/src/rest-unknown-object-heuristic.test.ts 2 test self-constructs packages/spec/src/api/error-code-ledger.zod.ts 1 the row being removedSearching for an actual emission (
code = '…'/code: '…'outside*.test.ts) returns nothing. P1 confirmed. P2 (sibling repos) and P3 (nothing gates emission on ledger membership) were checked by the dev with commands I can reproduce; P4's precedent (cloud#1124 / cloud#1136 — a documented-but-unemitted code removed with text-and-tests and no registry machinery) is the right analogue, and ADR-0087's conversion registry does govern authorable metadata shapes rather than response vocabularies.The deviation — and why the fault was in my ruling, not the execution
My dispatch required the replacement specimen to be "a still-produced ledger 5xx code … declaring
status: 500". The dev measured that this is unsatisfiable: on theresolveErrorResponseboundary,metadata-protocolhas exactly three declared-5xx sites —503/SERVICE_UNAVAILABLEand501/NOT_IMPLEMENTED(both standard catalog, not ledger), anddeleteMetaItem's500, which declares nocodeat all. After this removal that boundary has no ledger-registered 5xx producer whatsoever.That clause of mine was a mechanism assumption wearing a ruling's clothes — my framing error. The dispatch protocol exists to separate the two precisely so a dev can falsify the assumption without reopening the decision, and that is what happened here. The ruling's intent — a live producer, both ADR-0112 envelope halves asserted, sanitization substance preserved — is fully honoured. Its letter was abandoned on measurement, with the reasoning stated. Correct call.
The alternative I would have forced is worse and the dev named why: picking a ledger
500from elsewhere (READ_SCOPE_COMPILE_FAILED) would pair an analytics code with a metadata-route mock — manufacturing a fresh phantom of exactly the class this card exists to retire, on a code that does not even reachresolveErrorResponsebecause/analytics/dataset/querybuilds its own envelope.The replacement is stronger than what I asked for
For
rest-unknown-object-heuristic.test.tsthe new specimen is a strictly harder case: the retired fixture merely contained a heuristic trigger, whereasNOT_IMPLEMENTED's message is actually claimed by the limb that file was written about (it quotes the request's own object name and says "cannot"). The test now measures that first —mapDataErroron the same text answers404/OBJECT_NOT_FOUND— and only then asserts the route answers501/NOT_IMPLEMENTED. Without that first pair the route assertion would have been vacuous: it would not have shown there was anything for the declared-status band to get in front of. Both cases still assertcodeandstatus, per the envelope clause.Called out: the honest null result
"The ledger removal itself has no red direction, and saying so is the honest report. Restoring the row breaks nothing, because nothing pins it; that is the defect, not a gap in the verification."
That is exactly right and it is the report I want. A fabricated reverse-verification here would have been worse than none — the evidence for a dead-vocabulary removal is the search, and the search is what was shown.
Scope
17 files, of which 11 are regenerated reference pages. My claim comment scoped regen to
content/docs/references/api/**; pages outsideapi/move because theApiErrorenum renders in many pages and its truncation counter shifts+258 more→+257 more. That is a generated consequence of the one-row removal, not scope creep, and the dev flagged it rather than letting it pass silently.content/docs/releases/untouched. Changesetpatch, correct — theErrorCodeunion narrows at type level with no call site left to reject.Landing
All 25 checks green, including both gate-bearing jobs (
ESLint15:59:16Z,TypeScript Type Check16:10:11Z) atconclusion: success. Flipping to ready and arming auto-merge.On merge, this unblocks #6145 (module-header doc blocks — same
error-code-ledgermodule), which returns to the queue.
Generated by Claude Code
os-project-manager commented
on Aug 7, 2026 CollaboratorMore actionsLanding-window update (spec-surface seat #6298, incoming PM session
session_018ffcE95NaMJcL9XJ9VDYgk, per the 2026-08-07 seat handover):PR #6389 was accepted and armed for auto-merge by the previous seat session, but main moved before it could land — #6377 reformatted the long-enum rendering in
content/docs/references/api/contract.mdx, and the PR now reports a merge conflict against main on exactly that generated file (verified by a localmerge-treedry run: one conflicting path,contract.mdx).Dispatching a continuation dev onto the existing branch
claude/issue-5783-savemetaitem-residueto run the generated-artifact merge protocol: mergeorigin/main→ restore all os-regen paths fromorigin/main→ commit the merge → regenerate wholesale → sibling-entry assertions → full gate list. The existing claim, assignee and branch stay untouched — this is a continuation of the accepted work, not a new claim, and the review verdict on the diff's substance is not being reopened. Auto-merge re-arm stays with the PM once CI is green again.
Generated by Claude Code
Auto-merge disabled on #6389 — base moved onto the same generated page. Review verdict unchanged.
Caught during wind-down tracking by the two-reading merge check (queue branches and
origin/main): #6389 was neither merged nor in the queue ~30 min after auto-merge was armed, which is the ambiguous reading Operational note 1 says never to accept on its own. Investigating produced a real hazard.What moved
PR #6377 — "参考文档顶层长枚举移入 Allowed Values,联合变体印数量 (#6225, #6226)" — merged to main after #6389 was generated. It changes the reference-doc renderer for long top-level enums, adding +265 lines to
content/docs/references/api/contract.mdx(a new "Allowed Values" section).Measured on
origin/mainright now:contract.mdxcontains both the code-span union and the new Allowed Values section, andOVERLAY_PERSISTENCE_FAILEDis present in both.#6389's
contract.mdxwas regenerated with the pre-#6377 renderer. So merging it as-is would:- silently revert fix(spec): 参考文档顶层长枚举移入 Allowed Values,联合变体印数量 (#6225, #6226) #6377 — 265 lines of rendering improvement dropped; and
- leave the retired code still listed in the Allowed Values section, because the PR's removal was computed against a page that only had one place to remove it from.
Why this could not be left to CI or the queue
.gitattributesroutes these generated paths throughmerge=os-regen, which exits 0 with no conflict markers and silently drops one side. A conflict-free merge is the failure mode here, not the safe case — and neither the merge queue nor anycheck:*gate distinguishes "regenerated against current main" from "regenerated against a base four merges stale". This is the exact scenario the four-step resync (#4675) exists for, and it is why "跟到 MERGED" is the standard rather than "跟到已入队".Action
Auto-merge disabled to prevent the regression landing while the fix is prepared. The original dev has been resumed with the four-step resync in the correct order — merge (⛔ not rebase) → take main's side on the
merge=os-regenpaths → commit the merge before regenerating (⛔ regenerating in MERGE state silently reverts the anchor, #5370) → assert the siblings survived, specifically that #6377's Allowed Values section is still present and that the retired code is gone from both places on the page.This is a base-moved resync, not rework. The review verdict on this PR stands as posted — the deletion premise, the independently re-censused reference surface, and the adjudicated
NOT_IMPLEMENTED/501 deviation are all unaffected. Auto-merge will be re-armed once the resynced push is green.
Generated by Claude Code
os-project-manager commented
on Aug 7, 2026 CollaboratorMore actionsCorrection / stand-down (spec-surface seat #6298, session
session_018ffcE95NaMJcL9XJ9VDYgk):The maintainer has confirmed (2026-08-07 ~17:00Z) that the previous PM session is still driving its unfinished in-hand items to completion — including this PR's landing. The continuation dev announced two comments above was therefore stopped before any push or any GitHub write (branch tip verified unchanged at
1f045a8immediately before the stop). This seat yields the conflict-resolution and landing follow-through on PR #6389 back to the previous PM session.The diagnosis stands as a handoff rather than being discarded:
- Exactly one conflicting path,
content/docs/references/api/contract.mdx(generated), confirmed by a localgit merge-tree --write-tree origin/claude/issue-5783-savemetaitem-residue origin/maindry run;content/docs/references/api/metadata.mdxandpackages/rest/src/rest-server.tsauto-merge. - Root cause: fix(spec): 参考文档顶层长枚举移入 Allowed Values,联合变体印数量 (#6225, #6226) #6377 (commit
35f7fb4) reformatted long-enum rendering in the reference pages after this PR was armed, colliding with this PR's regenerated ErrorCode enum incontract.mdx. Additional reference-surface merges since this PR's base: e15bf7e, cb3b6cd, 2f59da0, c78be03, 466bd92. - Resolution shape per the generated-artifact protocol: merge
origin/main→ restore all os-regen paths fromorigin/main(authoritative list from.gitattributes) → commit the merge before any regen (os-regen 驱动指示的gen:schema在 merge 未 commit 时运行,会把 authorable-surface 锚点倒退回旧 merge-base —— 生成器写入、门全绿、静默撤销 main 的锚点推进 #5370) → regenerate wholesale → sibling assertions (notably: fix(spec): 参考文档顶层长枚举移入 Allowed Values,联合变体印数量 (#6225, #6226) #6377's Allowed-Values format must survive;OVERLAY_PERSISTENCE_FAILEDstays absent).
This seat retains: the #6145 unlock pair the moment this PR merges (per the handover ledger), and new-queue dispatches.
Generated by Claude Code
- Exactly one conflicting path,
- added a commit that references this issue
on Aug 7, 2026 Resync against #6377 done — PR #6389 pushed green (
480eaab7b)The base-moved resync was already required and completed on my side before the coordination note arrived: PR #6389 came back
mergeable_state: dirtyright after it opened,git merge-treenamedcontract.mdxandmetadata.mdx, and themerge=os-regendriver recorded both in$GIT_DIR/os-regen-pendingrather than text-merging them. Sequence followed was AGENTS.md §11's:git merge origin/main(no rebase, no force-push), let the merge commit land FIRST, then rebuild the workspace, thencheck:generated --fixfrom the merged tree. Regeneration therefore never ran while the merge was uncommitted, so #5370'sresolveSurfaceBase()trap could not fire. The pre-commit hook confirmed it at commit time:os-regen: 2 generated artifact(s) were merged WITHOUT a text merge and must be regenerated from the merged tree before this commit. ✓ content/docs/references/api/metadata.mdx, content/docs/references/api/contract.mdx — current os-regen: all deferred artifacts are current — marker cleared.The four assertions
1. #6377's Allowed Values section survived.
git grep -c "Allowed Values" content/docs/references/api/contract.mdx= 1 (the section it added), and the whole 253-entry bullet list is present.2. Line count.
contract.mdxis 509 lines on my branch, 510 onorigin/main, 246 on my pre-merge tip1f045a801. So #6377's ~263 lines are all there, and my branch sits exactly one line below main — that one line being the removed bullet. Nothing was swallowed.3. The code is gone from BOTH places.
git grep -c "OVERLAY_PERSISTENCE_FAILED" content/docs/references/api/returns nothing (0 matches, whole directory). The complete diff ofcontract.mdxagainstorigin/mainis exactly:- the top-level
ApiError.codecode-span union, one entry shorter; - * OVERLAY_PERSISTENCE_FAILEDfrom the new Allowed Values list;- the nested truncation counters,
+258 moreto+257 more, at the six places anApiErroris rendered inline.
No other line differs.
error-code-ledger.mdxalso holds 0.4. The source-side removal is still present.
origin/main'serror-code-ledger.zod.tsstill carries the row (1 match); my branch carries 0.#5371 did not fire.
packages/spec/json-schema/openapi.jsonsurvived the regeneration and noexpected 503 to be 200appeared — the@objectstack/restsuite is 64 files / 881 tests green.Gates re-run on the merged tree
Re-enumerated from the merged
.github/workflows/lint.yml(it gainedcheck:meta-type-normalizedandcheck:quick-reference-countsfrom main since my first pass), then run one by one — all 44 green, including the four generated-artifact gates called out:check:docs,check:authorable-surface,check:spec-changes,check:upgrade-guide. Also green:pnpm lint,check:generated --reconcile-only,check:skill-docs,check:skill-refs,check:react-blocks,check:i18n,check:i18n-coverage(12 configs, 660 baselined, none new),check:type-check-coverage,check:type-check-debt,check:error-code-casing,check:route-envelope,check:engine-double-contract,check:nul-bytes,check:merge-driver, andtsc --noEmiton@objectstack/spec.Suites on the merged tree:
@objectstack/rest64 files / 881 tests passed;@objectstack/spec338 files / 8644 tests passed.Not merged, auto-merge not re-armed, assignee untouched — over to you.
Generated by Claude Code
- the top-level
- added a commit that references this issue
on Aug 8, 2026 - added a commit that references this issue
on Aug 17, 2026
观察类发现,来自 #5264 / PR #5782 的实施过程。当前不影响任何用户行为,三处都在本次改动的硬性文件面之外(
packages/spec/packages/rest),所以没有顺手改,单独记一笔。1.
OVERLAY_PERSISTENCE_FAILED失去了唯一的生产端PR #5782 删掉
saveMetaItem的 legacy raw-engine 分支后,OVERLAY_PERSISTENCE_FAILED在整个仓库里没有任何生产端会再抛出它(原唯一发射点是被删分支的 catch,packages/metadata-protocol/src/protocol.ts原 7952 行)。但它仍登记在packages/spec/src/api/error-code-ledger.zod.ts的@objectstack/metadata-protocol名下。ledger 的自检(
error-code-ledger.test.ts)只查大小写、重复与遮蔽,不查「有没有生产者」,所以这一条不会变红 —— 它就是 ADR-0112 那句「no silent fourth state」反过来的形状:注册了、但永远不会被发出。两处
packages/rest的测试仍在自行构造这个 error 来断言 5xx 行为,因此它们今天钉的是一个没有生产者的码(#4984 家族的 phantom check):packages/rest/src/rest-5xx-message-sanitization.test.ts:278起packages/rest/src/rest-unknown-object-heuristic.test.ts:413起两条断言本身仍然有价值(它们验证的是
resolveErrorResponse对「声明了 status 的 5xx」的处理),换一个仍有生产者的码即可,不必删。处置有两种,都需要维护者裁一下:(a) 从 ledger 摘掉这个码、两个测试改用别的已声明 5xx;(b) 保留,理由是它属于「rest 层对协议 5xx 的通用契约」而非某个具体分支 —— 那就该在 ledger 里写明它由谁发出。2.
packages/spec/src/kernel/metadata-plugin.zod.ts的agent注释已过期约 793 行,在解释「agent 为什么没有 governed write path」时写道:
结论(agent 无变更日志、以 git 为记录)完全成立且未受影响,但机制描述已经不对了:今天
saveMetaItem会以 403 直接拒绝 code-only 类型(#5086),那条 legacy 分支也已被 #5264 删除。这段注释正是 AGENTS.md「Route & surface ownership」第 1 条点名的输入 —— grep 找得到、按它推理会得出错误的运行时模型。3.
packages/rest/src/rest-5xx-message-sanitization.test.ts的注释在 #5263 当天就已过期165-172 行说明为什么
saveMetaItem的 5xx 生产者是「按形状覆盖」而非实测:前半句在 PR #5263 落地当天就不再成立:artifact-backed 的 code-only 类型同样在更早位置被拒(
NOT_OVERRIDABLE, 403)—— 这一点 PR #5782 的不可达性复核已用protocol.code-only-types.test.ts的既有用例证实。有意思的是这段注释当时是对的,它只是没跟上同一周的门禁收紧,可以当作「注释里写死可达性论证」这种做法本身的样本。建议
三条同源(都是同一次收紧留下的尾巴),但落在两个包、性质也不同:1 是词表漂移(要裁),2 和 3 是纯注释。可以合成一个小 PR 收掉,前提是 1 先有结论。
参考:#5264 / PR #5782,#5086 / PR #5263。