/** * S-157④ —— SQL file-history 店的 **typed 永久失败**披露座:散文一个铸点 + 机器面一只 sink。 * * ## 病(基线 v7.79.0 逐行亲核 + 亲跑) * `plugins/blob-backend.ts:55` 的 `BlobTooLargeError` 带 typed 判别码 `blob_too_large_for_sql`,头注 * 原话「绝不能与 transient 写失败混在一类(那类是可重试的 502;这类重试永远不会成功)」。可它一旦被 * `plugins/file-history-store-sql.ts` 的任何一条 `catch` 折进 `FileHistoryResult.error.message`,判别码 * 就只剩散文,**机器一位都读不到**。两条腿各自的后果: * * · **snapshot(边界捕获)腿**:引擎把失败包成一句散文交给 `RunnerDeps.onError({phase:"rewind"})` * (core 7.20.0 `dist/core/runner/prepare-file-history.js` 的 `wrapFileHistoryEntry`),服务那条臂从文本里抠**第一个** * 括号词当 metric label —— 而第一个括号词恒是引擎自己的闭集码。实测:「这个文件永远存不进这口店」 * 与「连接被重置」在 `rewind_snapshot_failed_total` 上**同形** `code="snapshot_failed"`。 * · **trackEdit(首触备份)腿**:本服务用引擎默认的 `onTrackFailure:"refuse"`(`boot/runner-deps.ts` 亲读), * 那条路 **完全不走 onError** —— 引擎把 `(track_failed: <本店 message>)` 念给模型,末句是 * 「Retry when the file-history store is healthy.」(`prepare-file-history.js`)。对这枚码那是永远 * 不会到来的一天,而**运维面一个计数都没有**:这条腿今天在遥测上完全不可见。 * * ## 修法(源头留痕,不走文本通道) * · **散文面**({@link discloseCaughtFailure} 的返回值):把 caught error 的 typed `code` 写成一个括号词 * 前置到它自己的 message 前 —— **不 `instanceof` 单挑任何一个类**(身上有字符串 `code` 就折),该店 * 11 处折点全部改走这一只函数。人与模型因此看得到「这是哪一类失败」。 * · **机器面**(同一只函数的副作用):码取自 typed 错**对象**、对闭集词表判定,**绝不从散文里抠**。 * 🔴 这一条是 codex R1-[high] 验真后的定形:文本通道**天然可伪造** —— 一个名叫 * `(blob_too_large_for_sql).txt` 的文件读不出来时,它的路径就在 `snapshot_failed` 的散文里,任何 * 「从散文里取在册括号词」的读法都会把它当成一枚永久码(继而抑制掉真故障的告警)。闭集只约束基数, * 不能证明来源。所以机读判定搬回**唯一知道真相的那一层**=铸错的地方,`rewind` 臂的读法一字不动。 * · 留痕三件套与 `observability/fail-open.ts` / `observability/secret-env-scrub.ts` 同律:**计数逐次** * (`file_history_permanent_failure_total{code}`)、**每进程每码一条 warn**(第二条起零新增内容,而 * 20G 树的用户每 turn 撞一次)、装配前累计的次数在 install 时补进 metrics。 */ import type { Logger } from "./observability/logger.js"; import type { Metrics } from "./observability/metrics.js"; interface Sink { logger?: Logger; metrics?: Metrics; } /** * 把一个 caught error 折进店侧散文,**并**在它是一枚 typed 永久失败时留痕。 * * 返回值 = 散文形:带 typed `code` 的前缀一个括号词(`(blob_too_large_for_sql) blob of … exceeds …`), * 不带的**逐字**是原 message(transient 失败的文案因此与本批之前一字不差)。 * 副作用 = 机器形:码取自错误对象,逐次计数 + 每进程每码一条 warn。观测绝不抛。 */ export declare function discloseCaughtFailure(err: unknown): string; /** 装配层把 logger/metrics 接上(main.ts,与 `installFailOpenRecorder` 同一处)。 */ export declare function installFileHistoryFailureRecorder(deps: Sink): void; /** 本进程逐码累计(测试/诊断读面)。 */ export declare function fileHistoryPermanentFailureCounts(): ReadonlyMap; /** 测试用重置(进程级状态在同一个 vitest worker 里跨用例存活)。 */ export declare function resetFileHistoryFailureRecorderForTest(deps?: Sink): void; export {}; //# sourceMappingURL=file-history-disclosure.d.ts.map