@furongjun1999/dsh-memory 0.6.0 → 0.6.1
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 +588 -572
- package/codebuddy/CODEBUDDY.md +10 -10
- package/data/policy.json +27 -0
- package/docs/discipline/harnesses.yaml +31 -0
- package/docs/discipline/templates/full.md.tmpl +1 -1
- package/docs/discipline/templates/rules.mdc.tmpl +1 -1
- package/docs/eval/N225_/347/264/242/345/274/225/346/227/245/345/277/227/351/235/236/345/257/271/350/261/241/350/243/205/350/275/275/351/235/242/347/261/273/345/236/213/351/227/270_v1.0.md +418 -0
- package/docs/eval/issue43_/351/273/230/350/256/244/347/255/226/347/225/245/344/270/216/351/224/256/347/261/273/345/236/213/351/227/270_v1.1.md +441 -0
- package/docs/eval/issue43_/351/273/230/350/256/244/347/255/226/347/225/245/345/212/240/350/275/275/344/270/216/345/207/255/346/215/256/346/230/216/346/226/207/351/230/237/345/210/227_v1.0.md +398 -0
- package/docs/eval//344/274/230/345/214/226/347/254/254/344/270/200/346/211/271_/346/216/245/347/272/277/344/270/216/347/255/211/344/273/267/345/217/230/346/215/242_v1.0.md +498 -0
- package/docs/eval//344/274/230/345/214/226/347/254/254/344/270/211/346/211/271_/351/227/250/347/246/201/350/275/254/346/255/243/344/270/216/350/260/203/345/272/246/346/255/242/350/241/200/344/270/216/351/227/250/346/216/247/346/224/266/345/217/243_v1.0.md +669 -0
- package/docs/eval//344/274/230/345/214/226/347/254/254/344/272/214/346/211/271_/344/276/235/350/265/226/351/200/217/344/274/240/344/270/216/345/257/271/346/213/215/345/217/243/345/276/204/344/270/216/350/264/237/347/274/223/345/255/230_v1.0.md +683 -0
- package/docs/eval//345/217/221/345/270/20307_/345/244/226/351/203/250/346/212/245/345/221/212/345/233/233/346/211/271/344/277/256/345/244/215/344/270/216/346/217/222/344/273/266/351/235/242/345/212/240/345/233/272_v1.0.md +205 -0
- package/docs/eval//346/200/247/350/203/275/344/270/223/351/241/271_/345/206/267/346/237/245/350/257/242/344/270/216/345/206/205/345/255/230_v1.0.md +218 -0
- package/docs/eval//346/225/205/351/232/234/346/263/250/345/205/245/345/256/236/346/265/213_v1.0.md +10 -0
- package/docs/eval//347/274/226/347/240/201/351/235/242/345/211/215/347/275/256_/345/205/245/345/217/243/350/207/252/344/277/235/350/257/201UTF8/344/270/216/346/226/207/346/234/254open/345/256/210/345/215/253_v1.0.md +646 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v20.md +283 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v21.md +224 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v22.md +223 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v23.md +293 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v24.md +303 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v25.md +230 -0
- package/docs/hive//346/243/200/347/264/242/350/267/257/345/276/204/344/270/216/350/256/244/347/237/245/347/273/223/346/236/204/345/245/221/347/272/246_v0.1.md +20 -0
- package/docs/hive//350/234/202/345/267/242M6_ingest/345/256/236/346/226/275/350/256/241/345/210/222_v0.1.md +1 -1
- package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.10.md +12 -0
- package/docs/mdcg//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +40 -40
- package/docs/mdcg//345/217/221/345/270/203/351/227/250/347/246/201/351/223/276_v0.1.md +21 -9
- package/docs/mdcg//347/201/265/346/236/242/350/256/260/345/277/206/345/212/250/350/257/215/345/215/217/350/256/256_v1.0-draft.md +7 -2
- package/docs/mdcg//350/256/244/347/237/245/345/233/276_/347/264/242/345/274/225/344/270/216/345/267/245/347/250/213/350/247/204/350/214/203/345/214/226_/350/256/241/345/210/222_v0.1.md +39 -3
- package/docs/plans//345/205/250/344/270/255/346/226/207/347/274/226/347/240/201/344/270/216/350/234/202/345/267/242/344/273/273/345/212/241/346/240/207/350/257/206/345/245/221/347/272/246_v2.0.md +220 -0
- package/docs/plans//347/234/237/346/272/220/347/264/242/345/274/225_/351/200/232/347/224/250/346/234/272/345/210/266_v0.3.md +227 -0
- package/docs/plans//350/234/202/345/267/242/346/250/241/345/236/213/345/257/206/351/222/245/351/205/215/347/275/256/351/235/242_v1.0.md +385 -0
- package/docs//345/267/245/344/275/234/347/272/252/345/276/213_/350/256/244/347/237/245/345/233/276/346/235/241/347/233/256_v1.1.json +460 -460
- package/lib/bridge.js +24 -2
- package/lib/hooks.d.ts +31 -2
- package/lib/hooks.js +219 -16
- package/lib/init.js +16 -1
- package/lib/lib/prompt_safety.d.ts +52 -1
- package/lib/lib/prompt_safety.js +76 -1
- package/md_cg/audit.py +652 -379
- package/md_cg/bench6_arms.py +1 -1
- package/md_cg/bench_p0.py +1 -1
- package/md_cg/branches.py +2 -2
- package/md_cg/ccgc.py +1071 -1006
- package/md_cg/chain.py +1 -1
- package/md_cg/consistency.py +97 -9
- package/md_cg/consolidate.py +34 -9
- package/md_cg/crypto.py +59 -26
- package/md_cg/docindex.py +16 -1
- package/md_cg/evolution.py +17 -1
- package/md_cg/export.py +1 -1
- package/md_cg/forgetting.py +309 -13
- package/md_cg/fsutil.py +454 -8
- package/md_cg/identity.py +3 -3
- package/md_cg/insight.py +24 -3
- package/md_cg/linkref.py +1 -1
- package/md_cg/logref.py +327 -0
- package/md_cg/mcp_server.py +4152 -3818
- package/md_cg/mdcg.py +4208 -3547
- package/md_cg/mdcos.py +4491 -4159
- package/md_cg/mreview/pipeline.py +15 -1
- package/md_cg/nodefile.py +639 -575
- package/md_cg/protect.py +158 -11
- package/md_cg/protocol.py +41 -2
- package/md_cg/provenance.py +22 -4
- package/md_cg/reach.py +4 -4
- package/md_cg/readcache.py +76 -15
- package/md_cg/reconcile.py +1 -1
- package/md_cg/refindex.py +246 -37
- package/md_cg/refine.py +1 -1
- package/md_cg/routing.py +32 -5
- package/md_cg/run_tests.py +17 -0
- package/md_cg/scrub.py +30 -3
- package/md_cg/security.py +47 -1
- package/md_cg/self_state.py +5 -5
- package/md_cg/sources.py +23 -6
- package/md_cg/srcindex.py +352 -0
- package/md_cg/stg.py +47 -3
- package/md_cg/subgraph.py +2 -2
- package/md_cg/sustain.py +1299 -1168
- package/md_cg/tasks.py +469 -470
- package/md_cg/test_b1_auto_id_multiproc.py +277 -0
- package/md_cg/test_b1b2_write_face.py +417 -0
- package/md_cg/test_b2_sensitivity_landing.py +223 -0
- package/md_cg/test_b3_merge_keeps_content.py +576 -0
- package/md_cg/test_b4_shard_dir_selfheal.py +843 -0
- package/md_cg/test_b4_shard_dir_selfheal_guard.py +238 -0
- package/md_cg/test_c3_transient_read_negative.py +793 -0
- package/md_cg/test_c8_search_rrf_gates.py +502 -0
- package/md_cg/test_ccg_form_parity.py +188 -0
- package/md_cg/test_ccgc.py +28 -3
- package/md_cg/test_govern_directread.py +17 -4
- package/md_cg/test_h2_session_view_norm.py +233 -0
- package/md_cg/test_h4_sustain_snapshot.py +1377 -0
- package/md_cg/test_hive_ingest.py +285 -285
- package/md_cg/test_issue39_utf8_stdio.py +307 -16
- package/md_cg/test_issue43_default_policy.py +865 -0
- package/md_cg/test_legacy_p3_node_id_type.py +375 -0
- package/md_cg/test_linkref.py +10 -3
- package/md_cg/test_lock.py +2 -2
- package/md_cg/test_logref.py +1109 -0
- package/md_cg/test_m3_h9_bucket_health_protect_mark.py +301 -0
- package/md_cg/test_m3_h9_semantic_guard.py +525 -0
- package/md_cg/test_mr_m2.py +15 -3
- package/md_cg/test_n130_verify_falsified_protect.py +1 -1
- package/md_cg/test_n139_dek_provision_failclosed.py +185 -0
- package/md_cg/test_n176_link_trust.py +221 -0
- package/md_cg/test_n178_units_jobid_gate.py +212 -0
- package/md_cg/test_n184_keys_concurrent_provision.py +307 -0
- package/md_cg/test_n195_writepath_reload.py +283 -0
- package/md_cg/test_n196_stale_gate_skip.py +169 -0
- package/md_cg/test_n197_n208_write_face_gates.py +471 -0
- package/md_cg/test_n198_tokens_corrupt_failclosed.py +225 -0
- package/md_cg/test_n199_tokens_concurrent_write.py +378 -0
- package/md_cg/test_n201_proposal_visibility.py +382 -0
- package/md_cg/test_n202_session_notes_visibility.py +461 -0
- package/md_cg/test_n204_n205_n226_n227_n228_n229_exit_gates.py +542 -0
- package/md_cg/test_n206_stdio_jsonrpc_type.py +403 -0
- package/md_cg/test_n209_verify_write_face_gates.py +461 -0
- package/md_cg/test_n212_n213_n224_generation_gates.py +513 -0
- package/md_cg/test_n214_n215_n221_n222_write_face_gates.py +563 -0
- package/md_cg/test_n225_nonobject_load.py +1110 -0
- package/md_cg/test_n62_tenant_bind_failclosed.py +200 -0
- package/md_cg/test_neg_condition_hits.py +333 -0
- package/md_cg/test_neg_tail_honesty.py +663 -0
- package/md_cg/test_none_id_write_guard.py +4 -3
- package/md_cg/test_opt_batch1_md_cg.py +451 -0
- package/md_cg/test_p1.py +5 -5
- package/md_cg/test_p11_consistency.py +161 -12
- package/md_cg/test_p26_refindex.py +2 -2
- package/md_cg/test_p27_docindex.py +777 -774
- package/md_cg/test_p28_refcheck.py +740 -13
- package/md_cg/test_p29_session_ingest_export.py +2 -2
- package/md_cg/test_p2_mcp.py +12 -1
- package/md_cg/test_p30_maintain.py +33 -2
- package/md_cg/test_p31_insight.py +1 -0
- package/md_cg/test_p9c_dedup_hints.py +276 -0
- package/md_cg/test_policy_required_ccg.py +426 -0
- package/md_cg/test_protocol.py +22 -0
- package/md_cg/test_rank_parity_score_mode.py +516 -0
- package/md_cg/test_read_face_input_gates.py +363 -0
- package/md_cg/test_read_face_semantics.py +489 -0
- package/md_cg/test_recall_face_guards.py +812 -0
- package/md_cg/test_rejected_credential_forms.py +188 -0
- package/md_cg/test_rejected_redact.py +146 -0
- package/md_cg/test_retr_s1b.py +2 -2
- package/md_cg/test_retr_s5.py +20 -7
- package/md_cg/test_review_conformance.py +21 -3
- package/md_cg/test_security_audit_v21.py +2 -2
- package/md_cg/test_server_version.py +67 -0
- package/md_cg/test_srcindex.py +171 -0
- package/md_cg/test_tenant_env_override_warn.py +63 -50
- package/md_cg/test_token_lowercase_form.py +315 -0
- package/md_cg/test_v21r1_package_version.py +310 -0
- package/md_cg/test_writepipe.py +10 -4
- package/md_cg/tokens.py +225 -95
- package/md_cg/tool_face.py +4 -4
- package/md_cg/trust.py +49 -5
- package/md_cg/units.py +204 -6
- package/md_cg/weights.py +4 -4
- package/md_cg/whitebox_kb/wisdom/knowledge_points.py +1 -1
- package/md_cg/writelimit.py +42 -8
- package/md_cg/writepipe.py +651 -554
- package/package.json +3 -2
- package/skills/plugin.json +1 -1
- package/src/bridge.ts +25 -2
- package/src/hooks.ts +229 -16
- package/src/init.ts +15 -1
- package/src/lib/prompt_safety.ts +88 -1
- package/zcode/AGENTS.md +10 -10
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# issue #43 默认策略加载与凭据明文队列 · 修复工程报告 v1.0
|
|
2
|
+
|
|
3
|
+
> 修订:v1.0-r1(2026-09-29,按读者反馈逐条修订;改动记于 §8)。范围:写入策略来源(默认策略
|
|
4
|
+
> 加载)+ 其下游「非 ACCEPT/REJECT 出口」把正文(含凭据)明文落 `hippocampus/inbox.jsonl` 的链路。
|
|
5
|
+
> 本文只写有机械证据的事。**本文不含任何明文凭据/令牌**:复现所用凭据为合成哑值,一律以
|
|
6
|
+
> `mdcg1.<role>.<id>.<secret>` 掩码形态书写。
|
|
7
|
+
|
|
8
|
+
## 0. 阅读约定(先读这一段)
|
|
9
|
+
|
|
10
|
+
### 0.1 证据来源标签(全文只有这三种)
|
|
11
|
+
|
|
12
|
+
| 标签 | 含义 | 可复跑性 |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| **本报告亲跑** | 我在本次会话中用所列命令实测过,命令与输出见对应表格 | 可复跑;命令逐条给出 |
|
|
15
|
+
| **工作流采集** | 由本修复工作流采集、**本报告未重跑** | 本报告内不可复跑;只给工作流给的字符串 |
|
|
16
|
+
| **工作流采集·独立复核** | 独立复核者的结论,来源、装置、命令均由工作流转述 | 本报告内**不可复跑**(装置 `%TEMP%\i43rev` 已删,无命令、无产物) |
|
|
17
|
+
|
|
18
|
+
### 0.2 三棵树(全文所有行号都标明树)
|
|
19
|
+
|
|
20
|
+
| 简称 | 是什么 | 怎么得到 |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| **HEAD 树** | 修前代码(`git archive HEAD` 导出的整树,`%TEMP%\i43head2_*\tree`) | 只读导出;HEAD 无 `resolve_rulebook`(实测 `hasattr(audit,'resolve_rulebook')` → `False`) |
|
|
23
|
+
| **现行树** | 修复后的工作树 `D:\program\dsh-memory-main` | 本报告 §4.2 的实测均在其上 |
|
|
24
|
+
| **工作流树** | 工作流做复现时所用(未由本报告取得) | — |
|
|
25
|
+
|
|
26
|
+
### 0.3 两个计数单位(不混淆)
|
|
27
|
+
|
|
28
|
+
- **断言数**:单个测试模块内部 `ok(...)` 的条数。例:新守卫 `md_cg/test_issue43_default_policy.py` 一个模块内 **47 条断言**(`47 通过 / 0 失败`)。
|
|
29
|
+
- **模块数**:`scripts/run_tests.py` 的最小计入单位是**测试文件(模块)**,每个模块一条 `PASS/FAIL` 行;其汇总行代码为
|
|
30
|
+
`print(f"\n===== SUMMARY {len(runnable) - len(bad)}/{len(runnable)} 通过,{len(skipped)} 跳过…")`(`scripts/run_tests.py` 汇总段)。
|
|
31
|
+
故 `191/191` = md_cg 组 191 个模块通过;`258/258` = 全量 258 个模块通过(本报告实测该日志中 `PASS` 行 258 条、`SKIP` 5 条、`FAIL` 0 条)。
|
|
32
|
+
**一个含 47 条断言的新文件对模块数只贡献 +1**(190→191,257→258),两个量纲不可直接比较。
|
|
33
|
+
|
|
34
|
+
### 0.4 rc 的参照对象(每条 rc 都写明是哪个进程)
|
|
35
|
+
|
|
36
|
+
- `rc=<N>`(server):`python -X utf8 -m md_cg.mcp_server` **进程自身**的退出码。
|
|
37
|
+
- `rc=<N>`(probe):探针脚本(`python -X utf8 -c <code>`)的退出码;写链探针**成功跑完即 0**,与写入被拒/入队无关。
|
|
38
|
+
- `rc=<N>`(guard/suite):`python -X utf8 -m md_cg.test_issue43_default_policy` / `python -X utf8 scripts/run_tests.py` 的退出码(0 = 全绿)。
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
| 项 | 值 |
|
|
43
|
+
|---|---|
|
|
44
|
+
| 缺陷 | 策略「未配置」被静默折叠成空规则库 → `text` 恒 `DEFER` → 正文(含凭据)**明文**入审核队列且不脱敏 |
|
|
45
|
+
| 真实站点(修前=HEAD 树行号) | `md_cg/audit.py:64-74`(`load_rulebook` 无默认回落)→ `:98-99`(空规则 → DEFER)→ `md_cg/writepipe.py:235`(DEFER 走 `cg.propose`)→ `md_cg/mdcos.py:1781-1792`(`rec["content"] = content` + `append_jsonl`) |
|
|
46
|
+
| 修复面 | 9 个既有文件(audit / writepipe / mcp_server / package.json / README + 4 个既有测试)+ 1 个新守卫模块 |
|
|
47
|
+
| 写入面判定(**本报告亲跑 3 项** + **工作流采集 2 项**) | 修复成立:合法输入逐位一致(工作流采集)、守卫 47/47(亲跑)、8 处定点变异恰好命中(亲跑)、python 全量 258/258 模块(亲跑)、容器两栈 rc=0(工作流采集,**本报告未跑**) |
|
|
48
|
+
| **总判定(工作流采集·独立复核)** | **DEFER** —— 本 issue 的写入面已修好,但本轮**引入**「策略键为标量时」的新缺陷:启动面崩(server rc=1)+ 写入面退回 `DEFER → review_queue` 明文入队(见 §5.2,**本报告亲跑**),故不判通过 |
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 1. 缺陷定义与真实站点
|
|
53
|
+
|
|
54
|
+
### 1.1 缺陷定义
|
|
55
|
+
|
|
56
|
+
修前 `MDCG_POLICY_FILE` 未设时,`load_rulebook()` 返回 `{}`(空规则库),**没有任何默认回落**。
|
|
57
|
+
空规则库在 `_rule_check` 里被判 `DEFER`(「无规则不能假装合规」——这个判断本身是对的),
|
|
58
|
+
但写入闸门把 `DEFER` 归入「非 ACCEPT/REJECT 出口」→ `cg.propose` → 正文**逐字**进
|
|
59
|
+
`hippocampus/inbox.jsonl`;而脱敏(`redact_forbidden`)**只在 REJECT 分支**执行。
|
|
60
|
+
|
|
61
|
+
于是后果不是「少一条规则」,而是**安全面翻转**:那些在**有规则时**会被禁表拦下的凭据没被拦,
|
|
62
|
+
原样进了审核队列落盘。措辞精确化(读者反馈):修前**并不存在任何生效的规则表**——修前既没有
|
|
63
|
+
包内默认回落、`MDCG_POLICY_FILE` 也未设,所以「禁表命中」这一说法描述的是**修复后的默认策略
|
|
64
|
+
(包内 `data/policy.json` 的 11 条 forbidden,清单见 §4.3)本该拦下的形态**,不是修前已加载的规则。
|
|
65
|
+
修前的实证形态就是「凭据原样入 `inbox.jsonl`」(§2 腿3、§4.2)。
|
|
66
|
+
|
|
67
|
+
### 1.2 三条独立成因(任一成立即足以成病)
|
|
68
|
+
|
|
69
|
+
1. **实现面无回落**:`load_rulebook` 只认 env,未设即 `{}`,且坏路径也静默 `{}`(不 fail-closed)。
|
|
70
|
+
- HEAD 树 `md_cg/audit.py:66` `path = path or os.environ.get("MDCG_POLICY_FILE")`
|
|
71
|
+
- HEAD 树 `md_cg/audit.py:67-68` `if not path or not os.path.exists(path): return {}`
|
|
72
|
+
- HEAD 树 `md_cg/audit.py:72-73` `except (OSError, ValueError): return {}`(目录不可读、非法 JSON 一并吞掉)
|
|
73
|
+
- HEAD 树 `md_cg/audit.py:74` `return rules if isinstance(rules, dict) else {}`
|
|
74
|
+
2. **发布面拿不到默认策略**:`package.json` 的 `files` 不含 `data`,`lingshu-init` 也不生成该 env
|
|
75
|
+
——「默认 `data/policy.json`」对默认安装的用户不成立。
|
|
76
|
+
3. **「未配置」与「不可用」同码**:两种完全不同的处境折叠成同一个 `{}`,坏路径静默降级、
|
|
77
|
+
无任何可观测面报出「你的策略从哪来」。
|
|
78
|
+
|
|
79
|
+
### 1.3 病态出口的落点(现行树行号)
|
|
80
|
+
|
|
81
|
+
- 空规则 → `DEFER`:`md_cg/audit.py:197-198`
|
|
82
|
+
- 未知 `content_kind` → `BLINDSPOT`(同样走非 ACCEPT/REJECT 出口):`md_cg/audit.py:462-463`
|
|
83
|
+
- 非 ACCEPT/REJECT 出口:`md_cg/writepipe.py:252-256`(`cg.propose(...)`,响应 `moved_to: "review_queue"`)
|
|
84
|
+
- 明文落点:`md_cg/mdcos.py:1781-1792`(`rec = {... "content": content ...}`;`:1792` `append_jsonl(self.inbox_log, rec)`)
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 2. 修前现场(复现腿)
|
|
89
|
+
|
|
90
|
+
> 来源:本修复工作流采集的 8 条复现腿(`reproduced: true`)。「本报告复跑」列写明**跑在哪棵树**、
|
|
91
|
+
> 用什么形态;`—` 表示本报告未重跑。
|
|
92
|
+
> **字节数不可跨树直接对比**(读者反馈第5条):同一 TEXT 字面在 HEAD 树上重复跑两次即得
|
|
93
|
+
> 468 / 469 两种字节数——抖动来自 `t`(`time.time()` 浮点 repr)长度,实测 `t_repr` 为
|
|
94
|
+
> `1790649083.657689`(16 字符,468B)与 `1790649083.7671993`(17 字符,469B),其余字段相同。
|
|
95
|
+
> 故「字节数差」必须按**同树同字面**比较,§4.2 已按此口径重测。
|
|
96
|
+
|
|
97
|
+
| 腿 | payload | 观察 | 崩溃点 | 本报告复跑 |
|
|
98
|
+
|---|---|---|---|---|
|
|
99
|
+
| 腿1 构造期装载 | pop 掉 env 后调 `load_rulebook()` / `_rule_check(TEXT, {}, 'text')` / `audit.audit('text', …)`;另跑 cwd=仓库根做对照 | 无异常。`load_rulebook() == {}`;`_rule_check -> DEFER evidence='未配置合规/纪律规则(MDCG_POLICY_FILE),无法判定'`;`audit()` 同。cwd 下就有 `data/policy.json`(exists=True)时仍为 `{}` —— 既无包根回落也无 cwd 相对默认 | 无异常(**静默**,病根) | ✅ **HEAD 树**:`load_rulebook {}`、`('DEFER', '未配置合规/纪律规则(MDCG_POLICY_FILE),无法判定', None)`、`resolve_rulebook` 不存在 |
|
|
100
|
+
| 腿2 发布面 | `npm.cmd pack --dry-run --json --ignore-scripts`;另读 `package.json` 的 `files` | RC=0,清单 1457 条;`data/*` 条目 `[]`;`policy.json present = False`;`files` 不含 `'data'` | 无异常 | —(只读现行树 `package.json:19-30`:修后 `files` 含 `"data/policy.json"`@`:23`) |
|
|
101
|
+
| 腿3 默认写入链 | `install_default_gates(WritePipeline())` + `{'content_kind':'text','content':TEXT,'layer':'knowledge'}`;env 未设;随后**二进制**读 `inbox.jsonl` 反查 | 无异常。`committed=False` `ok=True` `moved_to='review_queue'` `state=DEFER`;`inbox.jsonl` **490 字节**,原始字节同时命中合成凭据与明文标记;`rec` 含 `content` 键、值为**原文逐字**,`sensitivity='internal'` | 无异常 | ✅ **HEAD 树**:`moved_to=review_queue / DEFER`,`inbox.jsonl` 468/469 字节(同字面重复跑两次),凭据与标记字节面**双命中**,`content` 与原文逐字相等 |
|
|
102
|
+
| 腿3b 对照(显式指向真源 policy) | 同一写入 + `MDCG_POLICY_FILE=<仓库>/data/policy.json`(只读引用) | `moved_to='rejected'`、`state=REJECT`(`命中禁止规则:mdcg1\.[A-Za-z0-9._\-]{20,}`);`inbox.jsonl` 不存在;负记忆 `rejected/rej_*.md` 中凭据已被替换为占位符、非凭据标记保留 —— **脱敏只在 REJECT 分支** | 无异常 | ✅ **现行树 + env 未设**(等价档,因现行树未设 env 即加载包内默认):`moved_to=rejected`、全库凭据字节命中 `0`、负记忆含 `[已过滤:` 占位符 |
|
|
103
|
+
| 腿4 显式坏路径 | env 依次指向 ① 不存在路径 ② 目录 ③ 非法 JSON ④ 合法 JSON 但顶层非对象 | 四形态全部 `load_rulebook() == {}`、`_rule_check -> DEFER`;**无异常、无 stderr、不 fail-closed**。不可读分支取证:`open(<目录>)` 抛 `PermissionError`(属 `OSError`),被 `except` 吞掉后仍 `{}` | 异常被吞(HEAD 树 `audit.py:72-73`) | ✅ 同四形态在**现行树**实测(§4.2)已转结构化不可用 |
|
|
104
|
+
| 腿4b 坏路径下的写入 | env 指向非法 JSON + 同一写入 | `moved_to='review_queue'` `state=DEFER`;`inbox.jsonl` **490 字节**,凭据与标记均命中 | 无异常 | ✅ **现行树**:`moved_to=policy_unavailable`、落盘仅 `_keys.json`/`.lock`、凭据字节命中 `0`(§4.2) |
|
|
105
|
+
| 腿5 `content_kind` 省略 | 同一写入,未传 `content_kind` | `moved_to='review_queue'` `state=BLINDSPOT`(`未知内容类型:''`);`inbox.jsonl` **460 字节**,凭据与标记均命中 | 无异常 | ✅ **现行树 + 显式空策略 `{}`**(见 §6.3-1 的实验设计与依据):`BLINDSPOT` → `review_queue`,468/469 字节、凭据字节命中 `True` |
|
|
106
|
+
| 腿6 三处不符 | 读 `package.json` / `src/init.ts` env 块 / `README.md`;`git grep` 全仓 `MDCG_POLICY_FILE` 与 `--show-config`;实跑 `python -m md_cg.mcp_server` 抓 stderr | ① `files` 无 `data`;② `src/init.ts:134-141` 只写 `MDCG_ROOT/MDCG_PYTHON/MDCG_MCP_SURFACE/MDCG_ACTOR/MDCG_TENANT/MDCG_CLEARANCE/MDCG_TOKEN`,全文件 `MDCG_POLICY_FILE` 零命中;③ `README.md:131-132`、`:159` 把「默认 `data/policy.json`」写成既成事实;④ **启动 stderr 全量共 3 行**(不是「前三行」也不是「相邻三行」,工作流原话即「stderr 全量三行」),`policy/POLICY/规则库/规则` 四个关键词在该次 **stdout+stderr 全体**中命中均为 `False`;全仓 `--show-config` 只在 `hive/serve_start.py:658` 与 hive 文档 | 无异常(**不可观测**) | ✅ **HEAD 树**(本报告亲跑复现该行,命令与输出见 §4.2 #6):`server rc=0`、stderr 恰 3 行、`policy/POLICY/规则库/规则` 四关键词在所有输出中零命中 |
|
|
107
|
+
| 腿7 迁移面 | env 指向真源 policy 后复跑既有断言;另用临时空策略 `{}` 做保原口径对照 | 基线 7 模块全绿。翻转:`test_writepipe.py:97-105`「DEFER 入队」成立→**False**;`test_linkref.py:242-249` 同样 True→False;`test_mr_m2.py:350-358` C4b 测试体内 `pop` env 期望 `review_queue`,修复后 pop ≠ 空规则库,必须显式改指。保原口径可行:`{}` 时三场景仍 `review_queue/DEFER`(evidence 与 unset 档逐字一致) | 无崩溃(**断言翻转**) | ✅ **现行树**读码:`test_writepipe.py:97-108`、`test_linkref.py:48-54`、`test_mr_m2.py:351-361`、`test_p2_mcp.py:260-271` 四处已按语义迁移 |
|
|
108
|
+
| 腿8 自证 | 全部库根/策略文件建在 `tempfile.mkdtemp`;末了比对 `gettempdir()` 前缀、walk 落盘清单、`git status --porcelain`、`data/policy.json` 对 HEAD 的 sha256 | `UNDER_SYSTEM_TEMP = True`;6 个临时根均在系统 temp;`git status --porcelain` 与会话起始快照逐字一致;`data/policy.json` 磁盘 sha256 = HEAD sha256;未 `git add/commit/push`。**两处足迹**:① `npm pack --dry-run` 触发 `package.json:87` 的 `prepare` 脚本重建 gitignored 的 `lib/`(13 文件 mtime 刷新,随后改用 `--ignore-scripts`);② 启动探针刷新本机默认 aux 根的 `sustain/heartbeat.md_cg.stamp`(无法剔除本机常驻 mcp 进程自刷的可能) | 无 | ✅ 本报告侧:所有探针根均在 `%TEMP%\i43*`,`MDCG_ROOT` 全程为临时目录 |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 3. 修法契约与落点
|
|
113
|
+
|
|
114
|
+
### 3.1 契约分层(回应读者反馈第1条)
|
|
115
|
+
|
|
116
|
+
先定义术语:**「生效条件注释」= 本仓公开函数上方那一行 `# 生效条件:…` 注释**,是这个代码库
|
|
117
|
+
既有的**函数级契约声明**惯例(每个公开函数一条,写在 `def` 行**上一行**;例:现行树
|
|
118
|
+
`md_cg/audit.py:55`、`:81`、`:87`、`:93`、`:112`、`:142`、`:164` 各一条)。它声明的是**该函数在
|
|
119
|
+
任何输入下都成立的行为**(含「恒不抛异常」这类总括断言),不是「只在某个前置成立时才成立」的
|
|
120
|
+
条件式描述。本文 §3.1 因此把契约分两层,两层**都**是「本轮修复声明成立」的东西:
|
|
121
|
+
|
|
122
|
+
| 层 | 内容 | 本轮状态 |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| **C1-C4(本次修复的核心契约,硬契约)** | 来源单点 / 未设回落包内默认 / 显式坏路径不掩蔽 / 不可用则在提案前 fail-closed | **成立**(§4.2 实测) |
|
|
125
|
+
| **补充契约(生效条件注释已声明的既有行为)** | `resolve_rulebook` 恒不抛、`load_rulebook` 不可用仍返 `{}` 不抛、`policy_report()` 六键恒不抛 | **C1-C4 成立,但「恒不抛」被本轮新缺陷打破**:标量键策略下 `policy_report()` 抛 `TypeError`(§5.2),违反其生效条件注释(现行树 `md_cg/audit.py:142` 明文写「恒不抛异常」)。故 §5.2 属**违约**(生效条件注释被违反),不是「边界内的正常降级」。 |
|
|
126
|
+
|
|
127
|
+
因此读者反馈里「两种读法结论相反」的问题有确定答案:**§3.1 的补充契约是本轮修复自身声明成立的行为,
|
|
128
|
+
`policy_report()` 的崩是违约**。§6.1 原先写的「应先定契约再落码」措辞不当(契约已在
|
|
129
|
+
`audit.py:142` 声明),已改为「先补齐声明契约的行为,再谈收口」,见 §6.1。
|
|
130
|
+
|
|
131
|
+
### 3.2 核心契约(硬契约)
|
|
132
|
+
|
|
133
|
+
| # | 契约 | 落点 |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| C1 **来源单点** | 来源判定(env / 包内默认 / 不可用)只有一处实现——闸门、自描述面、守卫读同一个函数 | `md_cg/audit.py:113-139` `resolve_rulebook()` |
|
|
136
|
+
| C2 **未设/空串 → 回落包内默认** | 包根由 `__file__` 反推,**不依赖 cwd**;env 为空串与未设同路 | `md_cg/audit.py:82-90`、`:125-139` |
|
|
137
|
+
| C3 **显式坏路径不得被默认掩蔽** | env(或显式 path)给了就按它算;不可用即返回结构化 error,**不回落包内默认** | `md_cg/audit.py:127-132` |
|
|
138
|
+
| C4 **不可用 → 提案入队前 fail-closed** | 返回 `ok=False / moved_to="policy_unavailable"` + 结构化 error,**不进 audit、不 propose、不落任何节点,响应体不含正文** | `md_cg/writepipe.py:210-217`;错误码枚举 `md_cg/audit.py:94-109`(`policy_not_found / policy_unreadable / policy_invalid_json / policy_not_object`) |
|
|
139
|
+
|
|
140
|
+
### 3.3 落点清单(现行树行号)
|
|
141
|
+
|
|
142
|
+
| 文件:行 | 改动 |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `md_cg/audit.py:63-78` | 新增 `POLICY_ENV` / `DEFAULT_POLICY_REL=("data","policy.json")` / `POLICY_HINT` |
|
|
145
|
+
| `md_cg/audit.py:82-90` | 新增 `package_root()` / `default_policy_path()` |
|
|
146
|
+
| `md_cg/audit.py:94-109` | 新增 `_read_policy_file()`:读单文件 → `(rules\|None, (错误码, 原因)\|None)` |
|
|
147
|
+
| `md_cg/audit.py:113-139` | 新增 `resolve_rulebook()`:来源解析单点,恒不抛 |
|
|
148
|
+
| `md_cg/audit.py:143-161` | 新增 `policy_report()`:来源自描述单点(`source/path/available/forbidden/required/error`) |
|
|
149
|
+
| `md_cg/audit.py:165-173` | `load_rulebook()` 改为委托 `resolve_rulebook`(不再自行读 env) |
|
|
150
|
+
| `md_cg/writepipe.py:197-217` | `_gate_audit` 前置策略可用性闸(C4);规则经 `ctx["rules"]` 下传验证器(`:226-232`) |
|
|
151
|
+
| `md_cg/mcp_server.py:3771-3786` | 新增 `_policy_stderr_note(rep)`:启动 stderr 策略来源单行(内容单点,便于定点变异) |
|
|
152
|
+
| `md_cg/mcp_server.py:3790-3801` | 新增 `_show_config()`:只读诊断面,不解析 root、不建 cg、不落盘 |
|
|
153
|
+
| `md_cg/mcp_server.py:3807-3808`、`:3907` | `--show-config` 分派(先于 root 解析);`main()` 启动写策略 stderr 行 |
|
|
154
|
+
| `md_cg/mcp_server.py:2143` | `cg(op=info)` 返回体新增 `write_policy` |
|
|
155
|
+
| `package.json:23` | `files` 新增 `"data/policy.json"` |
|
|
156
|
+
| `README.md:131`、`:132`、`:159` | 三处文档对齐:默认策略随包发布、env 覆盖、不可用 fail-closed 不进队列、来源三处可查 |
|
|
157
|
+
| `md_cg/test_linkref.py:48-54`、`:249`;`md_cg/test_writepipe.py:97-108`;`md_cg/test_p2_mcp.py:260-271` | 三处「DEFER 出口」用例改为**显式设空规则库**,保持原断言口径(断言一字未改) |
|
|
158
|
+
| `md_cg/test_mr_m2.py:351-361` | C4b 按语义更新为「策略不可用 → fail-closed」,并 monkeypatch 包内默认路径使其真触发 |
|
|
159
|
+
| `md_cg/test_issue43_default_policy.py`(571 行) | 新守卫 G1-G7 共 47 条断言 + 8 处定点变异自证;锚点漂移报 `ANCHOR-MISS`、退出码 2 |
|
|
160
|
+
|
|
161
|
+
**本轮未触及的面(明确排除)**:§3.3 无一条改动 `DEFER`/`BLINDSPOT` 出口的脱敏或落盘行为——
|
|
162
|
+
即「非 ACCEPT/REJECT 出口仍原样 `cg.propose` → 明文入 `inbox.jsonl`」这一事实**未被本轮修改**,
|
|
163
|
+
只被**收窄了触发面**(策略不可用时不再走该出口)。见 §6.1。
|
|
164
|
+
|
|
165
|
+
### 3.4 可观测面(契约④「不得静默」)
|
|
166
|
+
|
|
167
|
+
三处共用同一个 `policy_report()`,无第二套口径:启动 stderr(`mcp_server.py:3907`)、
|
|
168
|
+
`python -m md_cg.mcp_server --show-config`(`:3790-3801`)、`cg(op=info).write_policy`(`:2143`)。
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 4. 验证数字
|
|
173
|
+
|
|
174
|
+
### 4.1 门禁数字(含来源标注与量纲)
|
|
175
|
+
|
|
176
|
+
| 项 | 命令 | 结果 | 量纲 | 来源 |
|
|
177
|
+
|---|---|---|---|---|
|
|
178
|
+
| 守卫正向 | `python -X utf8 -m md_cg.test_issue43_default_policy` | 47 通过 / 0 失败,rc=0(G1=6 G2=3 G3=6 G4=9 G5=6 G6=8 G7=9) | **断言数**(单模块内) | **本报告亲跑** |
|
|
179
|
+
| 变异自证 | 同命令加 `--mutation-baseline` | 未变异基线红项 0;M1 13/13、M2 3/3、M3 6/6、M4 9/9、M5 12/12、M6 2/2、M7 5/5、M8 1/1,rc=0 | 断言数 | **本报告亲跑** |
|
|
180
|
+
| 删断言探针 | `probe_drop_group.py`(仅内存改 `T._GROUPS`,文件未动) | 摘掉 g1 → M1 红 7/13、M5 红 7/12 与声明不符、返回 1;摘掉 g5 → M1 红 10/13、M5 红 6/12、返回 1 | 断言数 | 工作流采集(**未重跑**) |
|
|
181
|
+
| python 全量 | `python -X utf8 scripts/run_tests.py --jobs 4` | `SUMMARY 258/258 通过,5 跳过`,进程 exit code **0**(日志内 `PASS` 258 行、`SKIP` 5 行、`FAIL` 0 行) | **模块数** | **本报告亲跑** |
|
|
182
|
+
| 目标套件 | `python -X utf8 scripts/run_tests.py md_cg --jobs 4` | 191/191 通过,3 跳过,rc=0(改动前基线 190/190,**+1 个模块** = 新守卫) | **模块数** | 工作流采集(**未重跑**) |
|
|
183
|
+
| 只读门禁 | `check_unreachable.py` / `workspace_index.py --check` / `link_check.py` | 475 文件命中 0 rc=0 / OK rc=0 / OK=228 BROKEN=0 rc=0 | — | 工作流采集(**未重跑**) |
|
|
184
|
+
| 容器栈一 | `docker … scripts/linux_verify.sh`(含 `smoke_test (linux)`) | 退出码 **0**;`结果: 21 pass / 0 fail`;`[PASS] smoke_test (linux)`;`汇总: 38 pass / 0 fail` | 脚本内记录项数 | 工作流采集(**本报告未跑**) |
|
|
185
|
+
| 容器栈二 | node 侧容器测试栈 | 退出码 **0**;`# cancelled 0`、`# skipped 3`、`# todo 0`、`# duration_ms 5308.138373` | node 测试项数 | 工作流采集(**本报告未跑**) |
|
|
186
|
+
|
|
187
|
+
**未跑原因(容器两栈)**:本机存在 `python:3.12` / `rust:bookworm` / `node:22-*` 镜像(`docker images` 列表实测),
|
|
188
|
+
但重跑 `scripts/linux_verify.sh`、`scripts/newuser_sim.sh` 会向仓库写入构建产物(`target/`、`lib/`、`node_modules`),
|
|
189
|
+
超出本次「只改报告文件,不得动源码」的硬边界,故只转述工作流数字。
|
|
190
|
+
|
|
191
|
+
### 4.2 本报告亲跑的复现(每条含命令与 rc 参照对象)
|
|
192
|
+
|
|
193
|
+
| # | 命令(要点) | 树 | 结果 |
|
|
194
|
+
|---|---|---|---|
|
|
195
|
+
| 1 | `python -X utf8 -c "from md_cg import audit; print(audit.load_rulebook(), audit._rule_check('正文',{}, 'text'), hasattr(audit,'resolve_rulebook'))"`(cwd=HEAD 树,env 未设) | **HEAD** | `{}` / `('DEFER','未配置合规/纪律规则(MDCG_POLICY_FILE),无法判定',None)` / `False`;**无异常** |
|
|
196
|
+
| 2 | 写链探针(`MdCGSecure(<临时root>)` + `install_default_gates(WritePipeline())` + `{'content_kind':'text', content:TEXT}`),随后二进制读 `inbox.jsonl` | **HEAD** | probe rc=0;`moved_to=review_queue`、`state=DEFER`;`inbox.jsonl` 468 与 469 字节(同字面跑两次);凭据与标记**字节面双命中**;`content` 与原文逐字相等 |
|
|
197
|
+
| 3 | 同 #2,**env 未设** | 现行 | probe rc=0;`moved_to=rejected`、`state=REJECT`;`inbox.jsonl` 不存在;全库扫描凭据字节命中 `0`;负记忆含 `[已过滤:` 占位符 |
|
|
198
|
+
| 4 | 同 #2,`MDCG_POLICY_FILE=<非法 JSON 文件>` | 现行 | probe rc=0;`moved_to=policy_unavailable`、`state=None`;落盘文件仅 `_keys.json` / `_keys.json.lock`;凭据字节命中 `0` |
|
|
199
|
+
| 5 | `python -X utf8 -m md_cg.mcp_server`(临时 root + 临时 aux + 真签发令牌,stdin 送 EOF) | 现行,env 未设 | **server rc=0**;stderr `[mdcg-mcp] 写入策略:来源=package_default path=D:\program\dsh-memory-main\data\policy.json(forbidden=11 required=6)` |
|
|
200
|
+
| 6 | 同 #5 | **HEAD**,env 未设 | **server rc=0**;**stderr 恰 3 行**(路径 / 自报 / 自报文件);`policy`、`POLICY`、`规则库`、`规则`、`策略` 五关键词在 stdout+stderr 全体中命中均 `False` |
|
|
201
|
+
| 7 | 同 #5 + `MDCG_POLICY_FILE={"forbidden": 1}` | 现行 | **server rc=1**;stderr 末行 `TypeError: 'int' object is not iterable`(`mcp_server.py:3907` → `audit.py:157`);策略行**未打印** |
|
|
202
|
+
| 8 | 同 #7 换 `{"required": true}` 走 `python -X utf8 -m md_cg.mcp_server --show-config` | 现行 | **rc=1**、stdout 空;traceback `mcp_server.py:3799 → audit.py:159`(`'bool' object is not iterable`) |
|
|
203
|
+
| 9 | 同 #2 + `MDCG_POLICY_FILE={"forbidden": 1}` / `{"required": true}`(各一次) | 现行 | probe rc=0(两次);`moved_to=review_queue`、`state=DEFER`、`evidence='验证器异常:TypeError: 'int'/'bool' object is not iterable'`;`inbox.jsonl` 均 469 字节、`content` 与原文**逐字相等**、凭据与标记字节面双命中 |
|
|
204
|
+
| 10 | 同 #2 + `MDCG_POLICY_FILE={"forbidden": "sk-…(哑值,23 字符)"}` | 现行 | probe rc=0;`policy_report()` **不抛**,但报 `forbidden=23`(把字符串长度当规则数);写入侧以**单字符规则**判定,`evidence='命中禁止规则:s'` → `REJECT`、`inbox` 不存在(详见 §5.2 末) |
|
|
205
|
+
| 11 | 读 `data/policy.json` 计数 | 现行 | `forbidden` 11 条、`required` 6 条、`required_kinds=['text']`(清单见 §4.3) |
|
|
206
|
+
|
|
207
|
+
### 4.3 包内默认策略的实际内容(回应读者反馈第8条)
|
|
208
|
+
|
|
209
|
+
`data/policy.json` 顶层键:`_comment`、`_note`、`forbidden`、`required`、`required_kinds`、`required_labels`。
|
|
210
|
+
下表逐条列出**文件内实际存在的**模式(本报告用 `json.load` 读出后原样转写),故读者判断
|
|
211
|
+
「默认策略上线后会不会拒掉自己的正文」**不必再打开该文件**;模式之外的设计口径随记在 `_note` 里
|
|
212
|
+
(该文件自述:只放高精度模式、宁可漏报不误伤;`tk_[0-9a-f]{8,}` 是灵枢令牌 id 形态,
|
|
213
|
+
且该模式不误伤 `tk_fake`/`tk_zzzz` 一类非 hex 哑值):
|
|
214
|
+
|
|
215
|
+
**forbidden(11 条,任意 `content_kind`,命中即 REJECT)**
|
|
216
|
+
|
|
217
|
+
| # | 模式 |
|
|
218
|
+
|---|---|
|
|
219
|
+
| 1 | `-----BEGIN [A-Z ]*PRIVATE KEY-----` |
|
|
220
|
+
| 2 | `\bsk-[A-Za-z0-9_\-]{20,}` |
|
|
221
|
+
| 3 | `\bAKIA[0-9A-Z]{16}\b` |
|
|
222
|
+
| 4 | `\bgh[pousr]_[A-Za-z0-9]{20,}\b` |
|
|
223
|
+
| 5 | `(?i)\bapi[_-]?key\s*[:=]\s*[A-Za-z0-9_\-]{16,}` |
|
|
224
|
+
| 6 | `(?i)\b(app[_-]?secret|appsecret|client[_-]?secret|secret[_-]?key|access[_-]?key)\s*[:=]?\s*[A-Za-z0-9]{16,}` |
|
|
225
|
+
| 7 | `(?i)\b(password|passwd|pwd|密码|口令)\s*[:=:]\s*\S{6,}` |
|
|
226
|
+
| 8 | `(?i)\bBearer\s+[A-Za-z0-9._\-]{20,}` |
|
|
227
|
+
| 9 | `[a-z][a-z0-9+.\-]*://[^/\s:@]+:[^/\s@]+@` |
|
|
228
|
+
| 10 | `\btk_[0-9a-f]{8,}\b` |
|
|
229
|
+
| 11 | `mdcg1\.[A-Za-z0-9._\-]{20,}` |
|
|
230
|
+
|
|
231
|
+
**required(6 条,`required_kinds=['text']` 收窄,缺失即 REJECT;`required_labels` 为报错展示名)**
|
|
232
|
+
|
|
233
|
+
| # | 模式 | label |
|
|
234
|
+
|---|---|---|
|
|
235
|
+
| 1 | `(?m)^#\s*功能名` | 功能名 |
|
|
236
|
+
| 2 | `(?m)^#\s*生效条件` | 生效条件 |
|
|
237
|
+
| 3 | `(?m)^#\s*子功能` | 子功能 |
|
|
238
|
+
| 4 | `(?m)^#\s*执行` | 执行 |
|
|
239
|
+
| 5 | `(?m)^#\s*验证方式` | 验证方式 |
|
|
240
|
+
| 6 | `(?m)^#\s*不适用条件` | 不适用条件 |
|
|
241
|
+
|
|
242
|
+
即**「CCG 六要素」= 上表 6 个 `# 要素名` 行**(形态口径:`^#\s*<要素名>` 不要求冒号;
|
|
243
|
+
二级标题 `## <要素名>` 与缩进形态同判不认——`_note` 已声明该边界)。工程含义:
|
|
244
|
+
`text` 类写入若不带这 6 行,默认策略上线后会由修前的「DEFER 入队」变为 **REJECT**(记入负记忆)。
|
|
245
|
+
部署影响见 §7-Q3。
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## 5. 独立复核判定与它列出的 uncovered
|
|
250
|
+
|
|
251
|
+
### 5.1 判定与来源(回应读者反馈第5条 unsupported)
|
|
252
|
+
|
|
253
|
+
**独立复核结论 = DEFER。** 来源标签:**工作流采集·独立复核**——本报告只拿到工作流转述的装置描述
|
|
254
|
+
(全部在 `%TEMP%\i43rev`,跑完已删;修前 = `git archive HEAD` 整树 2058 文件;现行 = 工作树;
|
|
255
|
+
隔离:`MDCG_AUX_ROOT/STATE/DATA` 指向临时目录;跑前后对在役 aux 根 `本机 aux 根(~/.mdcg)` 快照,
|
|
256
|
+
9 文件零增/零删/零改)。**本报告无法复跑该复核**:没有可执行的复核命令、没有留下产物、装置目录已删;
|
|
257
|
+
仅有转述文字。故 §5.1 的「PASS/PASS/PASS」三条与 §5.2 的判定归属均按此标签计——**未由本报告验证**。
|
|
258
|
+
|
|
259
|
+
复核认可的部分(三条 PASS,本报告在 §4.2 对**写入面与守卫面**做了同口径复跑,结论一致):
|
|
260
|
+
|
|
261
|
+
1. **各腿不崩 + 退化/容错路径真的发生**(修前 5 条分支腿 + valid 共 24 次运行无异常;判据用独立
|
|
262
|
+
`os.walk` 文件系统扫描而非只看响应;含正对照 `unset+code → ACCEPT` 证明扫描有判别力)。
|
|
263
|
+
2. **合法输入逐位一致**(HEAD vs 现行,4 条腿响应体逐字段一致、落盘文件集同集且逐字节一致,
|
|
264
|
+
仅掩码时间戳/session/pid/iid/密钥 nonce 等易变字段)。
|
|
265
|
+
3. **守卫本体与变异自证**(47/47、8 处变异恰好命中、删断言探针返回 1)。
|
|
266
|
+
|
|
267
|
+
### 5.2 它列出的 uncovered(本轮修复**新引入**的缺陷;本报告已逐条亲跑确认)
|
|
268
|
+
|
|
269
|
+
前提:策略文件是**合法 JSON 对象**,但 `forbidden`/`required` 的值是标量(`1` / `true`)。
|
|
270
|
+
|
|
271
|
+
| # | uncovered | 本报告亲跑结果(命令见 §4.2 #7/#8/#9) |
|
|
272
|
+
|---|---|---|
|
|
273
|
+
| (a) | `audit.policy_report()` 抛 `TypeError`(`md_cg/audit.py:156-161` 对取值无类型闸) | ✅ `{"forbidden": 1}` → `audit.py:157` `TypeError: 'int' object is not iterable`;`{"required": true}` → `audit.py:159` `'bool' object is not iterable` |
|
|
274
|
+
| (b) | 启动面崩:`mcp_server.py:3907` 的 `policy_report()` 调用不在任何 try 内 | ✅ **server rc=1**(真进程、临时 root/aux + 真签发令牌),stderr 末行 `TypeError`,策略行未打印 |
|
|
275
|
+
| (c) | `--show-config` rc=1、无 JSON 输出,与其 docstring「恒退出 0、不可用时 `available=false`」(`mcp_server.py:3789`)相反 | ✅ rc=1、stdout 空、traceback |
|
|
276
|
+
| (d) | 守卫盲区:G4 只测「顶层非对象」,**未测键类型为标量** | ✅ 读码确认:`md_cg/test_issue43_default_policy.py:242-272`(G4 组)覆盖 `policy_not_found/invalid_json/not_object/unreadable/空 dict`,**无键类型用例**;故该缺陷对 47 条断言全绿无感 |
|
|
277
|
+
|
|
278
|
+
### 5.3 对复核「边界说明」的一处纠正(本报告实测)
|
|
279
|
+
|
|
280
|
+
复核原文:**「写入路径不受该缺陷影响——两侧同为 DEFER→inbox,且修前根本不调用 `policy_report`,
|
|
281
|
+
故属启动/诊断面回归,不是写入语义回归。」**
|
|
282
|
+
|
|
283
|
+
结论方向(本轮新引入的缺陷)成立,但「写入路径不受影响」的措辞会掩盖事实:
|
|
284
|
+
**标量键策略下的写入侧也已退化,且退化的正是本 issue §1 的病态出口。**
|
|
285
|
+
机制:`_rule_check` 对 `(rules.get("forbidden") or [])` 的标量取值迭代时抛 `TypeError`,
|
|
286
|
+
被 `md_cg/audit.py:467-470` 的验证器兜底 `except Exception → DEFER` 吞掉(`_verdict(DEFER, kind,
|
|
287
|
+
f"验证器异常:…")`),于是 `moved_to=review_queue`、正文逐字(含凭据)落 `inbox.jsonl`。
|
|
288
|
+
本报告实测(§4.2 #9):`{"forbidden": 1}` 与 `{"required": true}` 各 469 字节、`content` 与原文
|
|
289
|
+
逐字相等、凭据与标记字节面双命中。即**部署方只要写出一个「合法 JSON 对象但键为标量」的策略文件,
|
|
290
|
+
启动面与写入面同时失去防护**,而不是仅丢诊断面。
|
|
291
|
+
|
|
292
|
+
**字符串键不是同一回事(本报告实测,回应读者反馈 unsupported 第4条)**:
|
|
293
|
+
`{"forbidden": "sk-…(23 字符哑值)"}` 因可迭代**不抛** `TypeError`,但静默退化为「按字符逐条建规则」:
|
|
294
|
+
`policy_report()` 报 `forbidden=23`(把字符串长度当规则数),写入侧以单字符规则判定,
|
|
295
|
+
实测 `evidence='命中禁止规则:s'` → `REJECT`(即误伤式判定,而非「不触发本缺陷」的豁免)。
|
|
296
|
+
故该形态的问题从「抛异常」变成「静默错判 + 计数失真」,同样需要键类型闸。
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## 6. 边界与未覆盖
|
|
301
|
+
|
|
302
|
+
### 6.1 本轮明确不修、留池的面
|
|
303
|
+
|
|
304
|
+
| 留池面 | 现状(本报告实测/读码) | 为何留池 |
|
|
305
|
+
|---|---|---|
|
|
306
|
+
| **策略键类型闸**(`forbidden`/`required` 非列表) | `md_cg/audit.py:157-160` 列表推导对取值无类型闸 → §5.2 (a)(b)(c);写入侧 `_rule_check` 同抛 `TypeError` 被 `audit.py:467-470` 吞成 `DEFER` → 明文入队(§4.2 #9);字符串键 → 计数失真 + 单字符误判(§5.3) | 属独立面(键类型校验),与「默认策略加载」根因不同。**因它同时波及启动面与写入面,修它时应同时覆盖 `policy_report()` 与 `_rule_check()` 两处键类型闸,并补守卫(G4 现无此用例)**——即先补齐 §3.1 已声明契约(「恒不抛」)的实际行为,再谈本 issue 收口 |
|
|
307
|
+
| **非 ACCEPT/REJECT 出口的脱敏**(本 issue 的病态出口本体) | `md_cg/writepipe.py:252-256` 仍原样 `cg.propose`;`md_cg/mdcos.py:1781-1792` 仍逐字落 `inbox.jsonl`。本轮**未改**该出口的落盘/脱敏逻辑(§3.3 无一条落点触及),只**收窄了触发面** | 收窄触发面(不可用 → fail-closed)已消除本 issue 的**默认**触发路径;是否给 `inbox.jsonl` 也做脱敏(或改为密文/封套)是**新的语义决策**(脱敏会削弱审核者看到原文的能力),须单独立项 |
|
|
308
|
+
| **显式空策略 `{}` 的残留通路** | 空 dict 是**可用**策略(`audit.py:93`/`:105` 契约),此时 `text` → `DEFER`、省略 `content_kind` → `BLINDSPOT`,两者仍 → `review_queue` → `inbox.jsonl` 逐字明文(§4.2 #9 同形;§6.3-1 实测设计) | 「无规则不能假装合规」是既有契约,空策略是**部署方的显式选择**(与修前「静默空白」不同),本轮不改其语义;但它是明文入队的残留通路,须在文档/运维面提示(见 §7-Q1) |
|
|
309
|
+
| `policy_report()` 三处调用无 try 包裹 | `mcp_server.py:3907`(启动,不在 try 内)、`:3799`(`_show_config`)、`:2143`(`op=info`,标量键下被 `_serve_line` 宽 except 兜成 `-32603`,但该场景下 server 已在启动期崩,实际到不了) | 随类型闸一并收口;「诊断面不得因策略畸形而拒绝启动/应答」的先定契约已在 §3.1 建立 |
|
|
310
|
+
| `_show_config` docstring 与实现的落差 | `mcp_server.py:3789` 写「恒退出 0」,标量键下实测 rc=1 | 随类型闸一并收口 |
|
|
311
|
+
| 守卫 G4 的键类型盲区 | `test_issue43_default_policy.py:242-272` 无键类型用例 | 随类型闸补断言 |
|
|
312
|
+
| `hive/serve_start.py:658` 的同名 `--show-config` 与 `md_cg.mcp_server --show-config` 是否需统一口径 | 全仓 `--show-config` 现出现在 hive 与其文档、`md_cg/mcp_server.py:3807`、`README.md:131` | 跨模块命名/语义统一不是本 issue 范围 |
|
|
313
|
+
| `src/init.ts:134-141` 不生成 `MDCG_POLICY_FILE` | 读码确认该 env 块无此项;README/init 文案未就此对齐 | **本报告无证据判断是否有意**(读码只能证明「缺席」,不能证明「有意」;未找到设计说明/注释/提交记录支撑)。故按事实陈述为「缺席且未在文档对齐」,**不断言动机**;是否补 env 由发布决策定 |
|
|
314
|
+
|
|
315
|
+
### 6.2 本报告未覆盖 / 未验证
|
|
316
|
+
|
|
317
|
+
- **容器两栈、目标套件 191/191、只读门禁三件、删断言探针**:均未由本报告重跑(§4.1 已逐行标注来源与未跑原因)。
|
|
318
|
+
- **`npm pack` 发布面**未由本报告重跑;只读 `package.json:19-30` 确认 `files` 含 `"data/policy.json"`@`:23`。
|
|
319
|
+
- **独立复核**(§5.1):无可复跑命令与产物,未由本报告验证。
|
|
320
|
+
- **HEAD 树的 `-m md_cg` 探针**:本报告的 HEAD 探针均以 `cwd=导出树` + `PYTHONPATH=导出树` 运行;
|
|
321
|
+
早期一次误将 cwd 留在仓库根,导致导入到现行树(已作废重跑,结论表内数字均来自 cwd=导出树 的运行)。
|
|
322
|
+
- **工作流腿3/腿5 的 490/460 字节**:其 TEXT 字面未由本报告取得,**两者差 30 字节的归因(是否同字面)
|
|
323
|
+
本报告无法核对**,故不采信「仅因 TEXT 字面差异」的因果说法(见 §6.3-3)。
|
|
324
|
+
|
|
325
|
+
### 6.3 语义边界(真源驱动)
|
|
326
|
+
|
|
327
|
+
1. **显式空策略 `{}` 是「可用」策略**(`audit.py:93`/`:105`)。实验设计(回答读者反馈第3条):
|
|
328
|
+
**两次独立实验、均跑在现行树**——① `MDCG_POLICY_FILE=<内容 '{}'`> + `content_kind='text'`;
|
|
329
|
+
② 同一策略文件 + **省略** `content_kind`。结果:① `moved_to=review_queue`、`state=DEFER`;
|
|
330
|
+
② `moved_to=review_queue`、`state=BLINDSPOT`;两次 `inbox.jsonl` 均为 468/469 字节
|
|
331
|
+
(同字面抖动,§2 注),合成凭据**字节命中 `True`**。**不是**「未设 env 之外的同一写入」——
|
|
332
|
+
原文措辞有误,已改正。
|
|
333
|
+
**限定口径(改正)**:「凭据不明文入队」只对**有合法规则列表的策略**成立——`forbidden`/`required`
|
|
334
|
+
为空列表(本项)、为标量(§4.2 #9)、为字符串(§5.3)三种形态都不在此列。
|
|
335
|
+
2. `REJECT` 的脱敏只作用于正文与 `tags`(`writepipe.py:238-244`),截断在脱敏之后(`:245`)——**本轮未改该口径**。
|
|
336
|
+
3. 字节数的可比性:本报告内所有字节数均在**同树同 TEXT 字面**下测得,且已实测 ±1 字节的同树抖动
|
|
337
|
+
来自 `t` 浮点 repr 长度(§2 注)。**TEXT 全文不在此给出**(本文硬边界:不得出现明文凭据),
|
|
338
|
+
故「工作流 490/460 与本报告 468/469 的差值是否全部来自 TEXT 字面」**只能标为推断、未核对**。
|
|
339
|
+
|
|
340
|
+
### 6.4 足迹披露(如实)
|
|
341
|
+
|
|
342
|
+
- 工作流侧:首次 `npm pack --dry-run` 触发 `package.json:87` 的 `prepare` 脚本重建 gitignored 的 `lib/`
|
|
343
|
+
(13 文件 mtime 刷新,随后改用 `--ignore-scripts`);启动面探针刷新了本机默认 aux 根的
|
|
344
|
+
`sustain/heartbeat.md_cg.stamp`(无法剔除本机常驻 mcp 进程自刷的可能)。
|
|
345
|
+
- 本报告侧:`git status --porcelain` 中 `package-lock.json` 显示为 `M`,但 `git diff --raw` /
|
|
346
|
+
`--numstat` 对它输出**零内容差异**(stat 脏,非内容改动)。本报告只改了
|
|
347
|
+
`docs/eval/issue43_默认策略加载与凭据明文队列_v1.0.md` 一个文件;**未改任何源码、未
|
|
348
|
+
`git add/commit/push`、未写入任何在役记忆数据根**(全部复现库根在 `%TEMP%\i43*`)。
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## 7. 读者问答(逐条回应 nextQuestions)
|
|
353
|
+
|
|
354
|
+
| # | 问题 | 回答 | 证据/状态 |
|
|
355
|
+
|---|---|---|---|
|
|
356
|
+
| Q1 | 空策略 `{}` 的残留算不算收口?总判定与结论为何不算它? | **不算本 issue 的违约,但算「明文入队」的残留通路**,已列入 §6.1 留池表。理由:修前的问题是「**静默**空白」(用户不知道策略为空),修后空策略是部署方**显式**写出 `{}` 的选择,且 `audit.py:197-198` 的「无规则不能假装合规 → DEFER」是既有契约;本轮收窄的是「策略不可用」这一支。结论句已限定为「已被取代」针对默认链路(未设 env / 坏路径),并在 §6.1 明确 `{}` 仍明文入队 | §4.2 #9、§6.3-1(本报告亲跑) |
|
|
357
|
+
| Q2 | 非 ACCEPT/REJECT 出口的脱敏本轮到底改没改?「DEFER 出口明文入库」是留池还是收口? | **没改**:§3.3 落点清单无一条触及 DEFER/BLINDSPOT 出口的落盘与脱敏;`writepipe.py:252-256` 仍原样 `cg.propose`。它是**留池面**,现已明确列入 §6.1(此前漏列,已补) | 读码(现行树)+ §6.1 |
|
|
358
|
+
| Q3 | 升级影响面:未设 env 的部署会不会突然被 REJECT?有无迁移/公告?历史明文凭据怎么办? | 会。未设 env 的部署从「恒 DEFER → 全部入审核队列」变为「按包内默认策略判定」:`text` 缺六要素 → `REJECT`;命中 11 条 forbidden 之一 → `REJECT`;其余(如 `code`)不变。规则清单与形态见 §4.3,可据此自测。**迁移/公告与历史 `inbox.jsonl` 里已落明文凭据的处置,本报告无法决定、也未发现任何相关动作**——按发布决策留给运行方;本报告只能指出这一支属未处置 | §4.2 #3、§4.3、§6.1;迁移动作=**未验证/未发现** |
|
|
359
|
+
| Q4 | 「独立复核」是谁、什么口径?能给出可复跑记录吗? | **不能**。本报告只有工作流转述的装置描述(`%TEMP%\i43rev`,跑完已删),无命令、无产物、无记录。已按 §0.1 建立「工作流采集·独立复核」标签并标注不可复跑(§5.1) | §5.1;**不可复跑** |
|
|
360
|
+
| Q5 | `47/47`、`191/191`、`258/258` 的计数单位? | 47/47 = **断言数**(单模块内);191/191、258/258 = **模块(测试文件)数**;新守卫含 47 条断言但只贡献 **+1 模块**(190→191)。判据:`scripts/run_tests.py` 汇总行按 `runnable` 模块计数,本报告实测日志 `PASS` 258 行 | §0.3;**本报告亲跑** |
|
|
361
|
+
| Q6 | 标量键缺陷的修复排期与发布影响?有临时防护/回滚建议吗? | **排期不在本报告可及范围**(本报告只做取证与归档,不做发布决策);当前发布版本是否已带上该缺陷,本报告**未验证**。可自测的临时防护:升级/配置后用 `python -X utf8 -m md_cg.mcp_server --show-config` 验证策略文件(正常路径 rc=0 并输出 JSON;标量键会 rc=1),或先跑 `python -X utf8 -m md_cg.test_issue43_default_policy`。注意:这两项都**未覆盖**标量键面(守卫按 §5.2(d) 是盲区),故「临时防护」的可靠口径目前只能是人工检查策略文件的 `forbidden`/`required` 是否为数组 | 建议=**未验证的运维建议**,非本报告实测结论;排期=**未定** |
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## 8. 本次修订记录(v1.0 → v1.0-r1)
|
|
366
|
+
|
|
367
|
+
| 反馈 | 处置 |
|
|
368
|
+
|---|---|
|
|
369
|
+
| §3.1「生效条件注释」未定义;补充契约与 §5.2 的关系不明 | 新增 §0.1/§3.1:定义该注释为函数级契约声明惯例并给出行号例证;**明确 §5.2 属违约**(违反 `audit.py:142` 的「恒不抛」);§6.1 相关措辞由「先定契约再落码」改为「先补齐已声明契约的行为」 |
|
|
370
|
+
| 计数单位不明 | 新增 §0.3(断言数 vs 模块数,给 `scripts/run_tests.py` 汇总行代码为判据),§4.1 增「量纲」列 |
|
|
371
|
+
| §6.3「空策略」实验句法不成立 | 重写 §6.3-1 为「两次实验、均现行树」并给各自结果 |
|
|
372
|
+
| §1.1「禁表命中」指代不明 | 改为「**修复后的**默认策略本该拦下的形态」,并指向 §4.3 清单 |
|
|
373
|
+
| §2「亲跑」列混树 | 列名改「本报告复跑」,逐行标注树与形态;新增 §0.2 三棵树定义;§2 表头新增字节数不可跨树对比的说明与实测依据 |
|
|
374
|
+
| rc 参照对象不明 / §4.2 行缺命令 | 新增 §0.4(rc 参照对象);§4.2 改为「命令 + 树 + 结果」逐行给命令 |
|
|
375
|
+
| §2 腿6「三行内」歧义 | 改为「启动 stderr **全量共 3 行**」,并由本报告在 HEAD 树复现(§4.2 #6:rc=0、3 行、五关键词零命中) |
|
|
376
|
+
| 缺六要素与 forbidden 清单 | 新增 §4.3 全量清单(11 + 6 + labels + kinds) |
|
|
377
|
+
| 「有规则的策略」被反例排除 | §6.3-1 补回限定口径并改正措辞为「有**合法规则列表**的策略」,并列出三种反例形态(空列表 / 标量 / 字符串);§6.1 空策略行同步指向该口径 |
|
|
378
|
+
| `init.ts` 动机归因无支撑 | §6.1 该行改为「本报告无证据判断是否有意」,只陈述缺席事实 |
|
|
379
|
+
| TEXT 字面未给出 → 字节差归因 | §6.3-3 明确 TEXT 因硬边界不载,归因降级为**推断/未核对**;并实测同树抖动来源(`t` 浮点 repr) |
|
|
380
|
+
| 字符串键「不触发」无实测 | §5.3 新增实测:不抛但退化为按字符建规则(`forbidden=23`、`命中禁止规则:s` → REJECT) |
|
|
381
|
+
| §1 判定行混入未亲跑数字 | 判定行改为逐项标注「本报告亲跑 / 工作流采集」,并注明容器两栈未跑及原因 |
|
|
382
|
+
| 独立复核来源无标签、不可追溯 | 新增 §0.1 第三档标签;§5.1 明写不可复跑;§4.1/§2 逐行标注 |
|
|
383
|
+
| nextQuestions 6 条 | 新增 §7 逐条回答(含 3 条标注为「未验证/未发现/未定」) |
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## 9. 结论
|
|
388
|
+
|
|
389
|
+
- **写入面修复成立**:修前的「未设 env → 空规则 → 恒 DEFER → 正文(含凭据)明文入
|
|
390
|
+
`hippocampus/inbox.jsonl` 且不脱敏」链路已被「包内默认回落 + 显式坏路径不掩蔽 +
|
|
391
|
+
不可用则在提案前 fail-closed」取代;证据:修前/修后写入链字节面(§4.2 #2/#3/#4,亲跑)、
|
|
392
|
+
合法输入逐位一致(§5.1-2,工作流采集·独立复核)、守卫 47/47 与 8 处定点变异恰好命中(§4.1,亲跑)。
|
|
393
|
+
- **总判定为 DEFER**(来源:工作流采集·独立复核;本报告以亲跑复现其依据):本轮**新引入**
|
|
394
|
+
「策略键为标量时」的缺陷,且**不止于诊断面**——启动面 server rc=1 + `TypeError`,
|
|
395
|
+
**写入面**退化为 `DEFER → review_queue → inbox.jsonl` 逐字明文(含凭据),守卫该面为盲区。
|
|
396
|
+
修好键类型闸(`policy_report` + `_rule_check` 两处)与三处调用兜底、补上相应断言后方可转通过。
|
|
397
|
+
- **未收口的三支**(如实列出,见 §6.1):① 非 ACCEPT/REJECT 出口本身未改脱敏(只收窄了触发面);
|
|
398
|
+
② 显式空策略 `{}` 仍明文入队;③ 键类型闸与启动/诊断面兜底留池。
|