dsh-log-contract 0.3.14 → 0.3.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -8
- package/README.zh.md +9 -7
- package/bin/dsh-log-contract.mjs +2 -2
- package/docs/CONTRACTS.md +5 -4
- package/lib/archaeology.js +2 -2
- package/lib/checks.js +5 -5
- package/lib/contracts.js +6 -6
- package/lib/log-reader.js +2 -2
- package/lib/repair.js +2 -2
- package/lib/validate.js +4 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -15,8 +15,7 @@ offline health check + pre-write validation. The business layer's **doctor**.
|
|
|
15
15
|
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
|
-
Formerly `log-contract-validator
|
|
19
|
-
per the OfferKuai three-piece plan.
|
|
18
|
+
Formerly `log-contract-validator`; now **`dsh-log-contract`**.
|
|
20
19
|
|
|
21
20
|
A fuse for DSH session logs (`*.jsonl` / `*.jsonl.zstd`): format drift that humans
|
|
22
21
|
cannot see but parsers crash on is caught and reported here. It does **not** judge
|
|
@@ -289,17 +288,16 @@ if (!verdict.ok) {
|
|
|
289
288
|
## Tests
|
|
290
289
|
|
|
291
290
|
```bash
|
|
292
|
-
pnpm check && pnpm test # syntax check +
|
|
291
|
+
pnpm check && pnpm test # syntax check + contract-doc drift gate + all unit tests
|
|
293
292
|
```
|
|
294
293
|
|
|
295
294
|
- **Synthetic fixtures** (in-repo): legal session / seq gap / empty sourceEventSeqs
|
|
296
295
|
/ turn-null append / unknown type / bad chunk row / torn tail frame / unknown
|
|
297
296
|
marker prefix / self-shadowing etc.
|
|
298
|
-
- **Real fossils** (not in-repo, contain user data): run locally
|
|
299
297
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
298
|
+
> **Scope of the published package**: it contains the runtime code and documentation only.
|
|
299
|
+
> Local maintainer tooling is not part of the public repository, so `pnpm check` and `pnpm test`
|
|
300
|
+
> work from a clean clone.
|
|
303
301
|
|
|
304
302
|
Known truth table (updated 2026-08-31 — after 0.3.5 added I1, `spliced-orphan`
|
|
305
303
|
now FAILs; the old PASS row was stale):
|
|
@@ -336,7 +334,11 @@ MIT © OfferKuai Team
|
|
|
336
334
|
|
|
337
335
|
---
|
|
338
336
|
|
|
339
|
-
## ⚠️ Upgrading to 0.3.
|
|
337
|
+
## ⚠️ Upgrading to 0.3.15 — behaviour changes you must know (0.3.12 → 0.3.15)
|
|
338
|
+
|
|
339
|
+
> **0.3.15 itself changes no behaviour.** It removes two internal identifiers that had leaked into the
|
|
340
|
+
> 0.3.14 published artifact (a README naming note and one source comment) and widens the pre-publish
|
|
341
|
+
> leak gate vocabulary. Everything below applies unchanged when going from 0.3.12 to 0.3.15.
|
|
340
342
|
|
|
341
343
|
**1. New exit codes `3` / `4` — a defect fix that is also a breaking change.**
|
|
342
344
|
- `3` = **not assessable on this host**: the file's `header.version` is higher than the host supports
|
package/README.zh.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
> DSH(DeepSeek Harness)会话日志的**结构契约保险丝**:离线体检 + 写前校验。
|
|
21
|
-
>
|
|
21
|
+
> 本包名 **`dsh-log-contract`**;早期工作名 `log-contract-validator`。
|
|
22
22
|
|
|
23
23
|
给 DSH 会话日志(`*.jsonl` / `*.jsonl.zstd`)装一条保险丝:人眼看不出、程序解析会崩的日志格式漂移,在它这里被拦下并告警。它不判断日志**内容**对不对,只守护日志**结构**是否破坏了下游消费者(Harness 读路径、客户端引擎、插件 marker 语义)的预期。
|
|
24
24
|
|
|
@@ -257,15 +257,13 @@ if (!verdict.ok) {
|
|
|
257
257
|
## 测试
|
|
258
258
|
|
|
259
259
|
```bash
|
|
260
|
-
pnpm check && pnpm test # 语法检查 +
|
|
260
|
+
pnpm check && pnpm test # 语法检查 + 契约文档漂移闸 + 全部单测(含事故回归用例)
|
|
261
261
|
```
|
|
262
262
|
|
|
263
263
|
- **合成夹具**(入库):合法会话 / seq 缺口 / 空 sourceEventSeqs / turn=null append / 未知 type / 坏 chunk 行 / 撕裂尾帧 / 未知 marker 前缀 / 自指 shadowed 等。
|
|
264
|
-
- **真实化石**(不入库,含用户隐私):本地跑
|
|
265
264
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
```
|
|
265
|
+
> **发布包范围**:只含运行时代码与文档。维护者本地工具不在公开仓内,
|
|
266
|
+
> 因此 `pnpm check` / `pnpm test` 在干净的克隆里即可通过。
|
|
269
267
|
|
|
270
268
|
已知真值表(2026-08-31 实测更新——0.3.5 加 I1 后,`spliced-orphan` 已可报错,旧表 PASS 过时):
|
|
271
269
|
|
|
@@ -297,7 +295,11 @@ MIT © OfferKuai Team
|
|
|
297
295
|
|
|
298
296
|
---
|
|
299
297
|
|
|
300
|
-
## ⚠️ 升级到 0.3.
|
|
298
|
+
## ⚠️ 升级到 0.3.15 —— 必须知道的行为变更(0.3.12 → 0.3.15)
|
|
299
|
+
|
|
300
|
+
> **0.3.15 本身不含行为变更。** 它只做两件事:清掉 0.3.14 已发布产物里的两处内部标识
|
|
301
|
+
> (README 的定名说明 + 一处源码注释),并补齐发布前泄漏闸门的词表。
|
|
302
|
+
> 从 0.3.12 升到 0.3.15 时,下面各条与原来完全一致。
|
|
301
303
|
|
|
302
304
|
**1. 新增退出码 `3` / `4`:本质是缺陷修复,形式上也是破坏性变更。**
|
|
303
305
|
- `3` = **不可在本宿主评估**:被检文件的 `header.version` 高于当前宿主支持的上限
|
package/bin/dsh-log-contract.mjs
CHANGED
|
@@ -106,7 +106,7 @@ const USAGE = `dsh-log-contract —— 日志契约守护(DSH session log cont
|
|
|
106
106
|
|
|
107
107
|
dsh-log-contract extract <session-log> --pattern <regex> [--out DIR] [--min-size N] [--json]
|
|
108
108
|
考古提取:按命令正则导出工具输出(只读)。--out 写到目录(保留原始文本),
|
|
109
|
-
否则打印前 3 条摘要。--min-size 过滤小输出(默认 50
|
|
109
|
+
否则打印前 3 条摘要。--min-size 过滤小输出(默认 50)。
|
|
110
110
|
|
|
111
111
|
dsh-log-contract audit-report <session-log> [--json]
|
|
112
112
|
考古审计报告:调用数 / 配对率 / 孤儿数 / 命令分布。
|
|
@@ -407,7 +407,7 @@ function cmdFix(args) {
|
|
|
407
407
|
const trimBudget = budgetIdx >= 0 && args[budgetIdx + 1] ? Number(args[budgetIdx + 1]) : undefined;
|
|
408
408
|
const compactIdx = args.indexOf('--compact-last');
|
|
409
409
|
const compactLast = compactIdx >= 0 && args[compactIdx + 1] ? Number(args[compactIdx + 1]) : undefined;
|
|
410
|
-
// L4 新原语(2026-08-30
|
|
410
|
+
// L4 新原语(2026-08-30 收编外部验证工具)
|
|
411
411
|
const tailIdx = args.indexOf('--tail-renumber');
|
|
412
412
|
const tailRenumberDelta = tailIdx >= 0 && args[tailIdx + 1] ? Number(args[tailIdx + 1]) : undefined;
|
|
413
413
|
const neutralizeOrphan = args.includes('--neutralize-orphan');
|
package/docs/CONTRACTS.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
> **自动生成**(2026-09-06 起):本文件由 `node scripts/gen-contracts-doc.mjs`
|
|
4
4
|
> 从 `lib/contracts.js` 的 `CONTRACT_RULES` 注册表生成——**勿手改**,规则只增不减,
|
|
5
5
|
> 新增规则后跑一次生成即同步(此前手工维护滞后 15+ 条,外部审计指出)。
|
|
6
|
+
> **漂移闸**:`pnpm check` 会跑 `--check` 逐字节比对,改了注册表没重生成 ⇒ 直接红。
|
|
6
7
|
>
|
|
7
8
|
> DSH 会话日志契约的**可执行 spec**。每条规则在 `lib/checks.js`(逐事件判定)
|
|
8
9
|
> 与 `lib/prewrite.js`(写前校验)中有对应实现;离线体检(`lib/validate.js`)
|
|
@@ -231,7 +232,7 @@
|
|
|
231
232
|
### E7 — ignorable 未知 type 合法性(带被忽略标记的未知事件须有消费者)
|
|
232
233
|
|
|
233
234
|
- **层级**: persistence | **严重度**: warning
|
|
234
|
-
- **出处**:
|
|
235
|
+
- **出处**: 对抗性复查 2026-09-09 T2(E3 ignorable 无合法性校验 = 后门)
|
|
235
236
|
- **契约**: 未知 type + ignorable:true 被读路径接纳但无人消费 = 静默垃圾。排除已知消费者白名单(retrace/marker、retrace/goal-marker、message-editor/ 前缀等 retrace 客户端消费的插件 marker)后,其余 ignorable 未知事件报 warning。
|
|
236
237
|
|
|
237
238
|
### E8 — 事件信封键白名单(多余键:seed/restore 路径会拒)
|
|
@@ -255,19 +256,19 @@
|
|
|
255
256
|
### Z3 — 空会话文件(有 header 无事件)显式报出
|
|
256
257
|
|
|
257
258
|
- **层级**: framing | **严重度**: warning
|
|
258
|
-
- **出处**:
|
|
259
|
+
- **出处**: 对抗性复查 2026-09-09 T3(36 条规则全来自有内容事故,空态无覆盖)
|
|
259
260
|
- **契约**: 有 header 但零事件 = 异常空会话(新建即空或写入未落盘)。空态不在任何有内容规则的覆盖下,显式 warning 供人判断。
|
|
260
261
|
|
|
261
262
|
### P3 — tool/call ↔ tool/result 配对完整性(考古 B1)
|
|
262
263
|
|
|
263
264
|
- **层级**: plugin | **严重度**: warning
|
|
264
|
-
- **出处**:
|
|
265
|
+
- **出处**: 考古方法 §2/§4.2(callId 配对,不可用"上一个 call"推断)
|
|
265
266
|
- **契约**: 每个 tool/call 的 data.callId 必须能在 tool/result 的 data.message.source.callId 中找到配对;孤儿 call(无 result)告警——中断/失败轮次可能产生孤儿(合法但要审计),考古提取将缺该输出。
|
|
266
267
|
|
|
267
268
|
### P4 — tool/result 输出结构可解析(考古 B2)
|
|
268
269
|
|
|
269
270
|
- **层级**: plugin | **严重度**: warning
|
|
270
|
-
- **出处**:
|
|
271
|
+
- **出处**: 考古方法 §2/§4.2(content 递归 text 结构)
|
|
271
272
|
- **契约**: tool/result 的 data.message.content 必须可递归解析(list[dict{type:text,text}] 或等价);不可解析片段 = 考古提取将漏数据。空 content(失败/无输出)合法。
|
|
272
273
|
|
|
273
274
|
### M1 — turn/step 为 null 的 assistant/message 只能 replace,不能 append
|
package/lib/archaeology.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dsh-log-contract · lib/archaeology.js
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 会话日志考古 —— 两插件共享:
|
|
5
5
|
* retrace 的考古界面/导出(A1-A4)与 log-contract 的 extract/audit-report
|
|
6
6
|
* (B3/B4)都消费这里的纯函数。**只读不写**(纪律 §8.1)。
|
|
7
7
|
*
|
|
@@ -100,7 +100,7 @@ export function indexToolCalls(events) {
|
|
|
100
100
|
*
|
|
101
101
|
* @param events - 会话事件数组。
|
|
102
102
|
* @param pattern - 命令正则(字符串或 RegExp;字符串按子串匹配,空则全量)。
|
|
103
|
-
* @param opts.minSize - 输出最小字节数过滤(默认 0
|
|
103
|
+
* @param opts.minSize - 输出最小字节数过滤(默认 0;CLI 默认用 50 过滤噪声)。
|
|
104
104
|
* @returns {{ pairs: Array<{ callId, command, text, size }>, matched: number, total: number }}
|
|
105
105
|
*/
|
|
106
106
|
export function extractToolOutputs(events, pattern = '', { minSize = 0 } = {}) {
|
package/lib/checks.js
CHANGED
|
@@ -480,7 +480,7 @@ export function turnEndReasonViolations(events) {
|
|
|
480
480
|
}
|
|
481
481
|
|
|
482
482
|
/**
|
|
483
|
-
* E7 —— ignorable 未知 type 合法性(2026-09-09
|
|
483
|
+
* E7 —— ignorable 未知 type 合法性(2026-09-09 T2 增量)。
|
|
484
484
|
*
|
|
485
485
|
* 盲区:E3 对"未知 type + ignorable:true"直接放行(容忍更新版本 harness 写入),
|
|
486
486
|
* 但 ignorable 标记无合法性校验 = 后门——被读路径接纳却无人消费的未知事件 =
|
|
@@ -509,7 +509,7 @@ export function ignorableTypeViolations(events, version) {
|
|
|
509
509
|
}
|
|
510
510
|
|
|
511
511
|
/**
|
|
512
|
-
* P3 —— tool/call ↔ tool/result
|
|
512
|
+
* P3 —— tool/call ↔ tool/result 配对完整性(考古 B1)。
|
|
513
513
|
* 每个 tool/call 的 `data.callId` 必须能在 tool/result 的
|
|
514
514
|
* `data.message.source.callId` 中找到配对;孤儿 call(无 result)告警——
|
|
515
515
|
* 中断/失败轮次可能产生孤儿(合法但要审计)。warning 级:不破坏日志。
|
|
@@ -517,7 +517,7 @@ export function ignorableTypeViolations(events, version) {
|
|
|
517
517
|
export function toolPairingViolations(events) {
|
|
518
518
|
const out = [];
|
|
519
519
|
const calls = new Map(); // callId → { command, loc }
|
|
520
|
-
const results = new Map(); // callId → loc(双向:孤儿 result 也要指认,2026-09-09
|
|
520
|
+
const results = new Map(); // callId → loc(双向:孤儿 result 也要指认,2026-09-09 T1)
|
|
521
521
|
for (const { event, lineNo } of events) {
|
|
522
522
|
if (event.type === 'tool/call') {
|
|
523
523
|
const callId = event.data?.callId;
|
|
@@ -539,7 +539,7 @@ export function toolPairingViolations(events) {
|
|
|
539
539
|
out.push(violation('P3', loc, `tool/call ${callId}(命令 ${command || '(未知)'})没有配对的 tool/result——孤儿调用(中断/失败未落结果),考古提取将缺该输出`));
|
|
540
540
|
}
|
|
541
541
|
}
|
|
542
|
-
//
|
|
542
|
+
// 双向(T1 增量):孤儿 result = result 无对应 tool/call。
|
|
543
543
|
// 折叠后 wire 流中无主 tool 消息 = provider 拒绝风险(W1/W2 同族、不同层);
|
|
544
544
|
// 与孤儿 call 同为 warning 级——合法场景(中断/修复产物)不破坏日志。
|
|
545
545
|
for (const [callId, loc] of results) {
|
|
@@ -579,7 +579,7 @@ function findUnparsableContent(node, path) {
|
|
|
579
579
|
}
|
|
580
580
|
|
|
581
581
|
/**
|
|
582
|
-
* P4 —— tool/result
|
|
582
|
+
* P4 —— tool/result 输出结构契约(考古 B2)。
|
|
583
583
|
* `data.message.content` 必须可递归解析(list[dict{type:text,text}] 或等价);
|
|
584
584
|
* 不可解析片段 = 考古提取将漏数据。空 content(失败/无输出)合法。warning 级。
|
|
585
585
|
*/
|
package/lib/contracts.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* DSH 会话日志契约规则目录(spec)。
|
|
5
5
|
*
|
|
6
6
|
* 规则集来源:
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
7
|
+
* - 早期契约审计发现(59 条 / F1–F7 / N1–N6 / R1–R3)
|
|
8
|
+
* - 会话修复事故复盘(三层契约:持久化 / 客户端引擎 / 插件语义)
|
|
9
9
|
* - `@deepseek-ai/dsh-session@0.1.0-rc.7` 官方源码逐行核对(见每条 `source`)
|
|
10
10
|
*
|
|
11
11
|
* 每条规则只描述"契约是什么";具体判定逻辑在 `lib/validate.js`(离线体检)
|
|
@@ -290,7 +290,7 @@ export const CONTRACT_RULES = [
|
|
|
290
290
|
title: 'ignorable 未知 type 合法性(带被忽略标记的未知事件须有消费者)',
|
|
291
291
|
layer: LAYER.PERSISTENCE,
|
|
292
292
|
severity: SEVERITY.WARNING,
|
|
293
|
-
source: '
|
|
293
|
+
source: '对抗性复查 2026-09-09 T2(E3 ignorable 无合法性校验 = 后门)',
|
|
294
294
|
description: '未知 type + ignorable:true 被读路径接纳但无人消费 = 静默垃圾。排除已知消费者白名单(retrace/marker、retrace/goal-marker、message-editor/ 前缀等 retrace 客户端消费的插件 marker)后,其余 ignorable 未知事件报 warning。',
|
|
295
295
|
},
|
|
296
296
|
{
|
|
@@ -329,7 +329,7 @@ export const CONTRACT_RULES = [
|
|
|
329
329
|
title: '空会话文件(有 header 无事件)显式报出',
|
|
330
330
|
layer: LAYER.FRAMING,
|
|
331
331
|
severity: SEVERITY.WARNING,
|
|
332
|
-
source: '
|
|
332
|
+
source: '对抗性复查 2026-09-09 T3(36 条规则全来自有内容事故,空态无覆盖)',
|
|
333
333
|
description: '有 header 但零事件 = 异常空会话(新建即空或写入未落盘)。空态不在任何有内容规则的覆盖下,显式 warning 供人判断。',
|
|
334
334
|
},
|
|
335
335
|
{
|
|
@@ -337,7 +337,7 @@ export const CONTRACT_RULES = [
|
|
|
337
337
|
title: 'tool/call ↔ tool/result 配对完整性(考古 B1)',
|
|
338
338
|
layer: LAYER.PLUGIN,
|
|
339
339
|
severity: SEVERITY.WARNING,
|
|
340
|
-
source: '
|
|
340
|
+
source: '考古方法 §2/§4.2(callId 配对,不可用"上一个 call"推断)',
|
|
341
341
|
description: '每个 tool/call 的 data.callId 必须能在 tool/result 的 data.message.source.callId 中找到配对;孤儿 call(无 result)告警——中断/失败轮次可能产生孤儿(合法但要审计),考古提取将缺该输出。',
|
|
342
342
|
},
|
|
343
343
|
{
|
|
@@ -345,7 +345,7 @@ export const CONTRACT_RULES = [
|
|
|
345
345
|
title: 'tool/result 输出结构可解析(考古 B2)',
|
|
346
346
|
layer: LAYER.PLUGIN,
|
|
347
347
|
severity: SEVERITY.WARNING,
|
|
348
|
-
source: '
|
|
348
|
+
source: '考古方法 §2/§4.2(content 递归 text 结构)',
|
|
349
349
|
description: 'tool/result 的 data.message.content 必须可递归解析(list[dict{type:text,text}] 或等价);不可解析片段 = 考古提取将漏数据。空 content(失败/无输出)合法。',
|
|
350
350
|
},
|
|
351
351
|
// ── M · 客户端引擎层 ────────────────────────────────────────────────────
|
package/lib/log-reader.js
CHANGED
|
@@ -243,8 +243,8 @@ export function loadSessionLog(path) {
|
|
|
243
243
|
*
|
|
244
244
|
* 帧布局:帧 1 = header 行(单行 JSON,~几百字节),帧 2+ = 事件流。
|
|
245
245
|
* 短码推导(工作区 createdAt 序号)需要扫全部会话但只需要 header——
|
|
246
|
-
* 读文件前缀 64KiB 足够覆盖完整帧 1
|
|
247
|
-
*
|
|
246
|
+
* 读文件前缀 64KiB 足够覆盖完整帧 1,避免全量读大文件(大会话压缩后可达
|
|
247
|
+
* 数百 MiB)。失败返回 null(调用方降级)。
|
|
248
248
|
*
|
|
249
249
|
* @param {string} path 会话日志文件路径(.jsonl.zstd 或明文 .jsonl)。
|
|
250
250
|
* @returns {object|null} header 对象;无法解析返回 null。
|
package/lib/repair.js
CHANGED
|
@@ -1245,10 +1245,10 @@ export function repairSession(file, opts = {}) {
|
|
|
1245
1245
|
issues.push({ kind: 'trim-budget', detail: `估算 ${r.estimatedTokens} tokens ≤ 预算 ${opts.trimBudget}——无需裁剪(${r.kept} 条消息全保留)` });
|
|
1246
1246
|
}
|
|
1247
1247
|
}
|
|
1248
|
-
// L4 新原语(2026-08-30
|
|
1248
|
+
// L4 新原语(2026-08-30 收编外部验证工具)
|
|
1249
1249
|
if (typeof opts.tailRenumberDelta === 'number') {
|
|
1250
1250
|
// 起点自动推导:从首个可平移的 seq 起(即所有事件都平移)。
|
|
1251
|
-
// fix-tail 原工具是 <startLine> <delta>
|
|
1251
|
+
// fix-tail 原工具是 <startLine> <delta> 双参;L4 简化为单参 delta
|
|
1252
1252
|
// (尾部全部平移)。若需部分平移,传 --tail-renumber 前先 --keep-ranges。
|
|
1253
1253
|
const r = tailRenumberText(plain, 0, opts.tailRenumberDelta);
|
|
1254
1254
|
if (r.error) {
|
package/lib/validate.js
CHANGED
|
@@ -106,7 +106,7 @@ export function validateSessionLog(log, opts = {}) {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
// ── Z3 · 空文件体检(2026-09-09
|
|
109
|
+
// ── Z3 · 空文件体检(2026-09-09 T3 增量)──────────────────────────
|
|
110
110
|
// 空态无覆盖:36 条规则全来自"有内容"事故,空会话文件应显式报(不管 DSH 编排层怎么处理)。
|
|
111
111
|
// 有 header 但零事件 = 异常空会话(warning,不破坏 ok);连 header 都没有 → H1 已报。
|
|
112
112
|
if (header !== null && events.length === 0) {
|
|
@@ -159,7 +159,7 @@ export function validateSessionLog(log, opts = {}) {
|
|
|
159
159
|
violations.push(...nullTurnStepViolations(events));
|
|
160
160
|
violations.push(...turnEndReasonViolations(events));
|
|
161
161
|
|
|
162
|
-
// ── E7 · ignorable 未知 type
|
|
162
|
+
// ── E7 · ignorable 未知 type 合法性(T2)────────────────────
|
|
163
163
|
violations.push(...ignorableTypeViolations(events, formatVersion));
|
|
164
164
|
|
|
165
165
|
// ── I1 · inbox seed 相对重放(fork 边界孤儿;交接书 L1)──────────────
|
|
@@ -488,7 +488,7 @@ export function resumeVerdict(result) {
|
|
|
488
488
|
violationsByTier: { structural: [], inbox: [], tokenMeter: [] },
|
|
489
489
|
};
|
|
490
490
|
}
|
|
491
|
-
//
|
|
491
|
+
// 三档各自的「阻断规则集」——按 L3 档位定义:
|
|
492
492
|
// 可加载:结构层(PERSISTENCE/FRAMING)+ 引擎层非 I1/T1/T2/T3/T4 的 error;
|
|
493
493
|
// 可继续:+ I1(inbox 重放);
|
|
494
494
|
// 可压缩:+ T1/T2/T3/T4(token-meter 配对 + 渲染层节点 key/turn)。
|
|
@@ -500,7 +500,7 @@ export function resumeVerdict(result) {
|
|
|
500
500
|
const has = (id) => (byId[id]?.length ?? 0) > 0;
|
|
501
501
|
|
|
502
502
|
// 可加载阻断 = 除 I1/T1/T2/T3/T4 外的所有 error 违规(结构/信封/物理序/工具配对等)。
|
|
503
|
-
// 注意:不能用 result.ok(它把 T1/T2/T3/T4/I1 也计为 error
|
|
503
|
+
// 注意:不能用 result.ok(它把 T1/T2/T3/T4/I1 也计为 error)——三档判定按 L3 定义,
|
|
504
504
|
// T1/T2/T3/T4 只影响「可压缩」档、I1 只影响「可继续」档。
|
|
505
505
|
const loadableBlockers = violations.filter(
|
|
506
506
|
(v) => v.severity === 'error' && !['I1', 'T1', 'T2', 'T3', 'T4', 'T5'].includes(v.id),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-log-contract",
|
|
3
3
|
"description": "日志契约守护 — DSH session log contract guard: offline health check (CLI) + pre-write validation for DeepSeek Harness session logs",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.15",
|
|
5
5
|
"packageManager": "pnpm@11.7.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"README.zh.md"
|
|
30
30
|
],
|
|
31
31
|
"scripts": {
|
|
32
|
-
"check": "node scripts/check-syntax.mjs",
|
|
32
|
+
"check": "node scripts/check-syntax.mjs && node scripts/gen-contracts-doc.mjs --check",
|
|
33
33
|
"test": "vitest run",
|
|
34
|
-
"prepublishOnly": "node scripts/check-pkg-meta.mjs && node scripts/
|
|
34
|
+
"prepublishOnly": "node scripts/check-pkg-meta.mjs && node scripts/prepublish-gate.mjs && pnpm check && pnpm test"
|
|
35
35
|
},
|
|
36
36
|
"keywords": [
|
|
37
37
|
"dsh",
|