fix(scripts): check:durability-log-level 学会「故障已答给调用方」——显式声明的传播词汇,清空基线 (#5241) - #5616
Merged
Merged
Conversation
…pagation vocabulary (#5241) The gate had two legal answers for a catch guarding a durability-critical callee — rethrow, or log at `error` — and no way to express the third: the failure was handed to the CALLER. AGENTS.md's own judgment question ("does the system still look normal from the outside?") answers NO for that shape, so it is not a degradation at all. #4754 hit this the moment `saveMetaItem` entered the vocabulary: three of its four hits were propagation, not degradation, and had to be parked in `durability-degradation.baseline.json` — entries for CORRECT code, in a shrink-only ledger whose header calls every line debt. Worse, the cheapest way out at `meta.ts` was to bolt on a `logger.error` on a path whose common case is a rejected request: one durability `error` per bad keystroke, the mirror-image failure AGENTS.md names. The vocabulary is DECLARED, never guessed, and the declaration only supplies the NAME — the checker still proves the STRUCTURE (every path out of the catch delivers the failure), so a declaration cannot become a baseline entry wearing a friendlier name: - FAILURE_PROPAGATION_CALLEES — repo-wide names (`errorFromThrown`), split by how they deliver: `via: 'return'` (the value IS the answer, so it only counts inside a `return`) vs `via: 'effect'` (the call IS the delivery). - FAILURE_PROPAGATION_SITES — function-scoped names for the second shape: a batch whose contract is a per-item outcome report, delivering through `record(...)` / `failed.push(...)`. Keyed by file plus enclosing FUNCTION, not by file: `protocol.ts` is nine thousand lines and a file-wide licence for `saveMetaItem` would hide the next #4669. Entries are staleness-checked. `catchDeliversFailure()` is conservative by construction: it can carry a delivery forward but never invent one, and any exit it cannot prove delivered counts as an escape — "cannot prove" reads as "does not deliver", which judges the seam instead of excusing it. Both #5241 baseline entries went stale and are deleted; the ledger is now empty, which is its intended steady state. No production code changed — the three sites were correct all along. Self-test grows 19 -> 35 cases, pinning both halves of the rule: partial propagation, an envelope built but never returned, a delivery in a later callback, a report written on one branch only, an undeclared report shape, and a site declaration keyed to a different function all still flag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V7WetGmnfoXNn8cLieKKmx
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
Author
|
CI 红说明(engine-core 车道 PM):ESLint job 的失败是 Generated by Claude Code |
This was referenced Aug 5, 2026
…ability-gate-propagation
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 #5241
⏸️ 停放状态(PM 维护,2026-08-05)
复核已通过(ACCEPT),draft 停放中,等待 base 恢复。
预期红清单(签名级,逐条比对,出现不在此列的签名才是新问题):
ESLintjob →check-engine-double-contract: 1 problem(s)→x PINNED: packages/runtime/src/action-execution-calldata-not-found.test.ts declares 2 engine double(s) whose delete() does not route through assertEngineDeleteDispatch (lines 69, 102)—— base 分支自身的红(main 全仓红:check:engine-double-contract挂在 #5584 刚落地的action-execution-calldata-not-found.test.ts(2 个 double 未接assertEngineDeleteDispatch,基线无条目)——所有新 PR 的 ESLint job 都过不去 #5604 立案,cli 车道在修);dev 已用单文件隔离实验钉死因果,且本 PR 自己的 durability 门在同一次 CI 里是绿的(20:32:26✓ self-test: 35 case(s)、20:32:28✓ ... (3 propagating, declared))。解除条件:#5604 的修复合入 main → 本分支
git merge origin/main→ CI 重跑红清零(并确认 durability 门在重跑里仍绿)→ 转 ready → 挂 auto-merge 入队。取 方向 1(PM 裁决):给闸门加一份显式声明的故障传播词汇,而不是放宽基线语义、也不是维持现状。验收标准直接来自两条基线条目
closes字段自己写的关闭条件。问题
闸门只认两种合法应答 —— rethrow 或
error日志 —— 表达不了第三种:故障已经答给了调用方。按 AGENTS.md 自己的判据问「降级之后系统从外面看还正常吗?」,这种形状答案是否:请求方被明确告知这次写入没有落盘。它根本不是降级。#4754 把
saveMetaItem加进词表后立刻撞上:4 处命中里 3 处是传播而非降级,只能进durability-degradation.baseline.json—— 给正确代码在一份「每行都是待还的债」的 shrink-only 账本里写条目。更糟的是meta.ts那处最省事的「修法」是补一句logger.error,而那条路径最常见的情况是作者提交了不合 spec 的 body:每一次校验拒绝打一条持久性error,正是 AGENTS.md 点名的镜像错误。做法:声明只给名字,结构仍由闸门证明
这是「词汇」和「改名后的基线条目」的分界线,而且是机械的、不是编辑口径上的:
FAILURE_PROPAGATION_CALLEES—— 全仓专名(errorFromThrown),按交付方式分两类:via: 'return'—— 调用构造答复,值本身才是交付,所以只在return里算数(const env = errorFromThrown(e)之后warn一句,什么都没交付);via: 'effect'—— 调用本身就是交付(把故障写进调用方会收到的报告),裸表达式语句即可。FAILURE_PROPAGATION_SITES—— issue 作者存疑的第二形状(结构化逐项结果报告)的答案。record(...)/failed.push(...)这种名字全仓无意义,声明全仓等于按名字猜 —— 所以按函数作用域声明,键是「文件 + 所在函数名」,不是行号(行号每次无关改动都churn)、也不是整文件(protocol.ts九千行,给saveMetaItem发全文件许可证的盲区大到能藏下一个 permission-set backfill (ADR-0094 D4) 现在 100% 失败:行里的active存储列喂进了 #4001 之后严格化的 permission spec #4669)。每条带理由,并做失效检测。关键在于:声明只提供名字,
catchDeliversFailure()仍要证明 catch 的每一条出口路径都交付了故障。它按结构建模(顺序、块、if/else、throw、return、break/continue),对未建模的形状保守回退 —— 只能把已有交付向前带,绝不凭空造一个;凡是证明不了已交付的出口都算逃逸。也就是「证不出来」读作「没交付」,判它而不是放它,这是闸门唯一安全的方向。闸门没有因新词汇变瞎(反向验证,方向均为事先预测)
service-automation/plugin.ts的error降成warnquiet-log @931packages.tsADR-0045 可见性翻转的error降成debugpackages.ts:243errorFromThrownmeta.ts:161FAILURE_PROPAGATION_SITESprotocol.ts:7741+:9045B 是其中最要紧的一条:
packages.ts那处把unhideError写进了响应体 —— 表面上很像「已答给调用方」,但那是一次赋值,不是声明过的传播调用,闸门拒绝放它过。新词汇没有把 #4754 刚修好的真丢失重新弄绿。> 说明一处与派单模板不符的地方:模板要求「把 baseline 里任一真降级条目临时移除时闸门必须红」。本 PR 之后基线已空,不存在真降级条目可移除 —— 空账本正是它应有的稳态。实验 A 与 B 是诚实的等价证明:直接把真降级站点弄哑,闸门照样红。
自测
--self-test从 19 条扩到 35 条,两个方向都钉住。新增负例包括:部分路径传播(一支答错误信封、另一支答成功值 / 另一支直接落到块尾)、信封构造了但没 return、交付写在稍后才跑的回调里、声明过的报告只在一支分支写、同样的报告形状但没有声明、站点声明键在另一个函数上、以及一个「证明不到」的保守回退样例。另加expectSitesUsed断言,钉住失效检测所依赖的记账。文件面
scripts/check-durability-degradation-log-level.mjs—— 词汇 + 结构分析 + 失效检测 + 自测;scripts/durability-degradation.baseline.json—— 删掉两条check:durability-log-level分不清「故障已答给调用方」和「故障被吞掉」—— 词表加saveMetaItem后逼出 2 条基线,记的全是正确代码 #5241 条目,账本清空;表头写明这是稳态而非休眠,以及「红了先想想是不是传播,别给正确代码写条目」;AGENTS.md—— 规则现在有第三种合法应答,不写下来的话下一个作者照样会去补logger.error(这正是 issue 点名的「立了坏范例」之害)。⛔ 未改动
packages/**任何生产代码 —— 那 3 处站点一直是对的,不是修复对象。Changeset
不需要:scripts-only + AGENTS.md,发布产物不变。也没有加空 frontmatter 的占位 changeset(Check Changeset 门的日志明说那是 last resort 且有 #4898 风险)。
skip-changeset标签已由 PM 挂上,门已过。🤖 Generated with Claude Code
https://claude.ai/code/session_01V7WetGmnfoXNn8cLieKKmx