@furongjun1999/dsh-memory 0.5.1 → 0.6.0

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.
Files changed (25) hide show
  1. package/README.md +22 -2
  2. package/docs/eval/DSH/346/227/245/345/277/227/347/264/242/345/274/225v2_/345/217/202/350/200/203dsh-TUI_v1.0.md +248 -0
  3. package/docs/eval/DSH/346/227/245/345/277/227/347/264/242/345/274/225/346/225/210/346/236/234/351/252/214/350/257/201_v1.0.md +209 -0
  4. package/docs/eval/DSH/347/253/257/347/274/272/351/231/267/344/270/223/351/241/271_v1.0.md +254 -0
  5. package/docs/eval/P1b2_/350/257/273/351/235/242/344/273/243/351/231/205/344/277/256/345/244/215_v1.0.md +124 -0
  6. package/docs/eval//345/217/221/345/270/20306_/344/270/200/351/224/256/351/205/215/347/275/256/344/270/216DSH0172_v1.0.md +171 -0
  7. 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_v18.md +183 -0
  8. 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_v19.md +207 -0
  9. package/dsh/cordis-patch-profile-web.example.yml +35 -0
  10. package/lib/cli.d.ts +3 -0
  11. package/lib/cli.js +66 -0
  12. package/lib/init.d.ts +88 -0
  13. package/lib/init.js +287 -0
  14. package/md_cg/evidence.py +585 -582
  15. package/md_cg/mcp_server.py +2 -2
  16. package/md_cg/mdcg.py +89 -4
  17. package/md_cg/mdcos.py +24 -2
  18. package/md_cg/review_cli.py +49 -4
  19. package/md_cg/test_index_crossprocess_reload.py +301 -0
  20. package/md_cg/test_none_id_write_guard.py +165 -0
  21. package/md_cg/test_review_cli_attribution.py +177 -0
  22. package/md_cg/test_review_cli_visibility.py +235 -0
  23. package/package.json +6 -1
  24. package/src/cli.ts +65 -0
  25. package/src/init.ts +347 -0
@@ -0,0 +1,254 @@
1
+ # DSH 端缺陷专项修复报告 v1.0
2
+
3
+ - **日期**:2026-09-26
4
+ - **范围**:DSH 端(插件另一端)在役实测报告四组缺陷的修复落地。定性由编排会话读码完成,本轮为修复与验证的如实汇编;修复落点全部在本仓 `md_cg/`(记忆引擎/工具面)与 `scripts/`(工具孪生件),不改 DSH 侧代码。
5
+ - **基线**:工作区 HEAD `2ae15a63`(main)之上的未提交改动——`git status --short` 实测:修改 7 件(`md_cg/evidence.py`、`md_cg/mdcg.py`、`md_cg/mdcos.py`、`md_cg/review_cli.py`、`scripts/linkref_backfill.py`、`scripts/migrate_restricted.py`、`scripts/review_cli.py`),新增守卫 3 件(`md_cg/test_review_cli_visibility.py` 235 行、`md_cg/test_review_cli_attribution.py` 177 行、`md_cg/test_none_id_write_guard.py` 165 行)。未 git commit(编排会话负责)。
6
+ - **材料来源**:第二节逐项修复的 target/前后对比/语义变化/验证主体来自编排会话交付的三份修复材料(修复轮实跑记录);本报告撰写会话对全部关键引证做了逐条读码复核,**材料行号与当前工作区实测有漂移处已按实测修正**(校勘明细见附录清单二末尾),三个守卫套件在本会话重跑全绿(实测输出见第三节、第四节)。
7
+
8
+ ---
9
+
10
+ ## 一、总览
11
+
12
+ DSH 端在役实测报告四组缺陷,经编排会话读码定性后,处置矩阵如下:
13
+
14
+ | 组 | 标签 | 缺陷一句话定性 | 处置 | 结果 |
15
+ |---|---|---|---|---|
16
+ | 可见性 | **P1b** | review_cli 裁决落盘节点在 MCP 读面不可见——autoflush 兜底的第二落点遗漏(server 级 2026-09-16 已修,工具面四处构造漏补) | 代码修复(4 处构造补 `autoflush=1` + 红守卫) | 已修;守卫 10/0(本会话重跑 EXIT=0) |
17
+ | 归因 | **P1** | review_cli 随机会话 + accept 落盘 writer 被 designer-cli 覆盖,原始写入者不可追溯 | 代码修复(`--session` + 归因保留 + 红守卫) | 已修;守卫 10/0(本会话重跑 EXIT=0) |
18
+ | 归因(环境侧) | **P1-1** | DSH 会话目录若不在 `_normalize_session` 校验面内(`MDCG_DSH_SESSIONS_ROOT` / `~/.dsh/sessions`),该会话在 MCP 写入面降级 anonymous,CLI 与 MCP 两侧归因对不上 | 环境侧回告(按要求非代码修) | 回告,复验清单见第五节 |
19
+ | 脏节点 | **P3** | `knowledge/orphan/None.md` 脏节点写入源头——node_id 字符串化污染形态(`"None"`/`"null"`)在写入边界放行 | 代码修复(写入边界整串精确等值闸 + 红守卫)+ 存量清理建议回告 | 已修;守卫 17/0(本会话重跑 EXIT=0) |
20
+ | 待排期 | **P4 / P5** | 本轮材料未含其定性细节(如实注明,不入虚构内容) | 入缺陷池 | 不入本轮,见第六节 |
21
+
22
+ 四组归并:①可见性组(P1b)②归因组(P1 + 其环境侧面 P1-1)③脏节点组(P3)④其余(P4/P5 入池)。三项代码修复共用同一条工程链路:读码定性 → 修复落点 → 红守卫先红后绿 → 定向回归 → 组全量/门禁。
23
+
24
+ ---
25
+
26
+ ## 二、逐项修复
27
+
28
+ ### 2.1 P1b——review_cli 裁决落盘节点在 MCP 读面不可见(autoflush 兜底第二落点遗漏)
29
+
30
+ **target**:短命裁决/工具进程被 kill 时,索引增量停留在内存 `_dirty`、分片日志 `_index_log/` 从未落盘,其它进程读面不可见(DSH 在役实测 read null)。server 级同款兜底 2026-09-16 已修(`md_cg/mcp_server.py:3695-3702`,`autoflush=1`),本项补齐**工具面第二落点**。
31
+
32
+ **落点**(全部本会话读码复核在位):
33
+ - `md_cg/review_cli.py:96-106`——`_cg` 返回 `MdCGSecure(..., autoflush=1)`(`autoflush=1` 在 :106),注释写明根因与 mcp_server 对照;
34
+ - `md_cg/evidence.py:509-512`——`_open_cg` 同款补参(`autoflush=1` 在 :512);
35
+ - `scripts/linkref_backfill.py:195-198`——构造补 `autoflush=1` + 注释;
36
+ - `scripts/migrate_restricted.py:41-46`——构造补 `autoflush=1` + 注释;
37
+ - `md_cg/test_review_cli_visibility.py`——新增红守卫(235 行)。
38
+ - `scripts/_mdcg_reindex_code.py:62` 经读码判定**不改**:批式重索引工具(逐文件 add 可达数千条),:128 有显式 `cg.close()` 作为确定性提交边界(flush+compact 落快照),收尾语义与「写完即退的短命工具」不同——per-write `autoflush=1` 只会增加数千次逐条日志 append 的开销而无可见性收益,维持 `autoflush=64`(本会话实测 :62 为 `autoflush=64`、:128 为 `cg.close()`,在位)。
39
+
40
+ **前后对比**:
41
+ - 修复前:`python -X utf8 -m md_cg.test_review_cli_visibility` → PASS=5 FAIL=5。红项:③落账契约「进程 A'(经生产构造 `_cg` 调库裸 add 2 条 → `os._exit(0)` 模拟被杀,close/atexit 均不执行)退出后盘面 `_index_log/` 已含全部写入记录」实测 `log_ids=[]`(autoflush=64 未达阈值,分片日志从未落);④四处构造 autoflush 回显均为 64/缺参。
42
+ - 修复后:同命令 → PASS=10 FAIL=0(③ `log_ids=['vis-node-1','vis-node-2']`;④ review_cli `_cg` 与 evidence `_open_cg` 行为断言 `autoflush==1`,两个 scripts 源级断言命中)。
43
+ - 读面断言①②(进程 A subprocess 跑 review_cli accept/调库落盘 → 进程 B 全新子进程 `_load_index`+read 断言业务节点与审计记录节点可见)修复前后均绿——如实说明:本仓 HEAD 上正常路径已由 09-16 的 review_decide 显式 flush(`md_cg/mdcos.py` 实测三处 `self.flush()` 在 :2258/:2309/:2330)+ review_cli finally `cg.close()`(`md_cg/review_cli.py:204-211`)+ 库层 atexit(`md_cg/mdcg.py:104-118`,`atexit.register` 在 :117,仅覆盖正常退出)覆盖;kill 形态的重开读面已由批次 39(9102df88,2026-09-25)`_load_index` 指纹失配→全扫自愈(`md_cg/mdcg.py:959-970`:指纹不符或旧快照无 `_fingerprint` 键即 `idx=None` 回退 `_scan_nodes` 全扫)兜住,该自愈晚于 09-16 的 autoflush=1 修复(17699ec5)落地——DSH 在役实测的 read null 对应**旧读面**(0.4.x 副本 `_load_index` 盲信既有快照无指纹校验)+ **旧写入面**(无显式 flush/autoflush)的叠加形态,本次补齐的正是写入面兜底在短命工具侧的第二落点(与 server 同款);①②保留为回归钉,防上述两层兜底未来被移除时旧症复发。
44
+
45
+ **语义变化**:`autoflush=1` 仅改变索引增量的落账时机:写入经 `_stage` 后立即 flush() 落分片日志 `_index_log/`(`md_cg/mdcg.py:2079-2087` 阈值判定 `len(self._dirty) >= self.autoflush` 在 :2086-2087,1 条即落),任意退出形态(含被 kill)下写入对其它进程立即可见;flush 幂等(`_dirty` 空即 no-op,`md_cg/mdcg.py:1080-1081`),节点文件/索引条目内容、读面结果、权限闸均零变化。四处均为构造参数增补,无 API/文件格式变更。`_mdcg_reindex_code.py` 维持 autoflush=64 是读码判定后的刻意不动(见落点末段理由),非遗漏。
46
+
47
+ **验证**:
48
+ 1. 守卫红→绿:`python -X utf8 -m md_cg.test_review_cli_visibility`,修复前 PASS=5 FAIL=5(③ `log_ids=[]`、④ autoflush=64×2 + 源级断言×2 未命中),修复后 PASS=10 FAIL=0。**本报告撰写会话重跑:PASS=10 FAIL=0,EXIT=0**(③ `log_ids=['vis-node-1','vis-node-2']`、④ `autoflush==1`×2 + 源级断言×2,实测输出见第三节)。
49
+ 2. 回归(修复轮实跑):`test_review_cli_attribution` 10/0;`test_review_conformance` 38/0;`test_review_onepass` 10/0;`test_p24_evidence` 29 passed(evidence.py 改动的直接回归)。
50
+ 3. `python -X utf8 -m py_compile` 五个改动文件 → COMPILE_OK;`scripts/migrate_restricted.py --root <临时库>` dry-run 冒烟 exit=0、`scripts/linkref_backfill.py --root <临时库>` 扫描冒烟 exit=0(修复轮实跑)。本会话另对 11 个改动面+新守卫+reindex 工具整体 py_compile → **COMPILE_OK**。
51
+ 4. 全量测试由编排会话跑(修复轮未跑,本报告会话亦未跑——见第四节门禁与第六节申报)。
52
+ 5. 实验与测试全程临时目录 + 哑主密钥(`MDCG_MASTER_KEY=ab*32`),实验目录(系统临时目录下 `mdcg_p1b_exp` 及 `clivis_*` 等临时残留)已清理(余量 0)。
53
+ 6. 读码依据(行号为本会话实测):`mdcg.py:922`(autoflush 缺省 64)、`mdcg.py:104-118`(`_atexit_flush_all`,atexit 仅正常退出)、`mdcg.py:2079-2087`(`_stage` 阈值落账)、`mdcg.py:1079-1098`(flush 落分片日志)、`mdcg.py:948-975`(`_load_index`=快照+日志重放,:959-970 指纹失配全扫自愈)、`mdcos.py:2258/2309/2330`(review_decide 显式 flush)、`mcp_server.py:3695-3702`(server 级 autoflush=1 对照)、git 时间线 17699ec5(09-16 autoflush)vs 9102df88(批次 39 指纹自愈,09-25)。未触真实令牌库/真实 serve/在役数据目录。
54
+
55
+ ### 2.2 P1——裁决面身份归因退化:review_cli 随机会话 + accept 落盘 writer 被 designer-cli 覆盖(原始写入者不可追溯)
56
+
57
+ **target**:同一批裁决落盘节点会话 id 各自随机(DSH 实录 5 节点 5 个 `sess_*`),且 accept/edit 落盘节点 `frontmatter.writer` 被裁决者 designer-cli 覆盖,提案原始写入者(如 dsh-memory)不可追溯。
58
+
59
+ **落点**(本会话读码复核在位):
60
+ 1. `md_cg/review_cli.py`——模块 docstring :31-44(`--session` 用法 + 落盘归因回告,含 `_normalize_session` 的 DSH 前置条件注记);:75-80 新增 `_session_of`(`--session` > 环境变量 `MDCG_SESSION` > None);:83-106 `_cg` 改造(None 时 stderr 告警一行后维持随机会话);:175-178 argparse common 新增 `--session`,所有子命令通用(`parents=[common]` 挂全部 8 个子命令)。
61
+ 2. `md_cg/mdcos.py:2240-2249`——review_decide accept/edit 分支:提案记录的原始 actor(propose rec 的 `"actor"`,propose 时库端快照,`md_cg/mdcos.py:1770`)经 `extra.setdefault` 记为落盘节点 `frontmatter.writer`,裁决者 `self.actor` 记入 `frontmatter.reviewer`。
62
+ 3. `scripts/review_cli.py`——孪生件同款同步(`_session_of` :66、`--session` argparse :153、docstring :11/:26);本会话实测两文件同步点在位。
63
+ 4. `md_cg/test_review_cli_attribution.py`——新增红守卫(①~⑥ 共 10 断言,177 行)。
64
+
65
+ 注:git status 中 `md_cg/mdcg.py` 的改动与 `md_cg/test_none_id_write_guard.py` 为并行修复员的 P3(None.md)改动,非本目标所改,未触碰。
66
+
67
+ **前后对比**:
68
+ - 修复前(红守卫实测,`python -m md_cg.test_review_cli_attribution`):PASS=2 FAIL=8——`accept --session` 报 argparse "unrecognized arguments" 退出码 2;提案人以 actor=dsh-memory propose、designer-cli accept 后节点 `frontmatter.writer="designer-cli"`(原始写入者被覆盖,DSH 缺陷直接复现);设 `MDCG_SESSION` 亦被无视(落 `sess_41d73208c113` 随机值);无告警。
69
+ - 修复后:同一守卫 PASS=10 FAIL=0——`accept --session sess_decide_01` → `frontmatter.session=="sess_decide_01"`、`writer=="dsh-memory"`(原始写入者在位)、`reviewer=="designer-cli"`(裁决者换字段保留);同一 `--session` 两次裁决两节点归属一致;仅设 `MDCG_SESSION` 时兜底生效(sess_env_03);两者皆缺省时 stderr 恰一行随机会话告警且退出码 0、仍落 `sess_*` 随机值(现状兼容)、writer 归因同样在位。
70
+ - 孪生件 `scripts/review_cli.py` 端到端(修复轮,临时库+哑密钥):rc=0,`session='sess_twin_01'`、`writer='dsh-memory'`、`reviewer='designer-cli'`;`_session_of`/`_cg` 两文件逐字一致已 diff 验证(修复轮实跑;本会话复核同步点在位,未重跑孪生端到端——见第六节申报)。
71
+
72
+ **语义变化**(向后兼容):
73
+ 1. `frontmatter.writer` 含义变化——accept/edit 落盘节点从「最后落盘者=裁决者(designer-cli)」变为「保留提案原始写入者」(propose 时库端快照的 actor,非裁决期客户端输入,不可伪造面);裁决者身份不丢失,移入 `frontmatter` 新增字段 `reviewer`。该实现落在引擎层 `mdcos.review_decide`,故 MCP 面 op=review 的 accept/edit 走同一代码路径同样受益。兼容性:提案 extra 里已带 writer 的存量提案不覆盖(`setdefault`,保留既有「库层调用方可显式传值覆盖」通道,`_attribution` 语义不变);旧格式提案无 actor 时 writer 仍回落裁决者(现状不变);merge/reject/noop 路径不动。
74
+ 2. review_cli 全部子命令新增 `--session`:缺省取环境变量 `MDCG_SESSION`,仍无则维持旧随机会话行为、仅多一行 stderr 告警(不拒绝、不阻断);现有调用形态(不传 `--session`)全部照旧。
75
+ 3. `frontmatter.session` 语义本身不变(写入时会话归属),变化仅是取值来源受控——DSH 端同一批裁决传同一 `--session` 即得同一落盘归属。
76
+ 4. **DSH 前置条件回告(P1-1,按要求为回告非代码修)**:MCP 写入面对 DSH 形态会话 id(`session-<8>-<4>-<4>-<4>-<12>`)有 `_normalize_session` 防编造校验(`md_cg/mcp_server.py:3381-3405`,目录根可用 `MDCG_DSH_SESSIONS_ROOT` 覆盖、缺省 `~/.dsh/sessions`,:3397)——会话目录须真存在于其下,否则该会话在 MCP 面降级 anonymous;review_cli 直构 Principal 不经该校验。此注记已写入两份 review_cli 模块 docstring(`md_cg/review_cli.py:39-44`),DSH 端环境侧需自行核对目录在位,否则 CLI 与 MCP 两侧归因对不上。
77
+ 5. 未做 git commit(按纪律,编排会话负责)。
78
+
79
+ **验证**(修复轮全部实跑):
80
+ - 修复前红:`python -m md_cg.test_review_cli_attribution` → PASS=2 FAIL=8(关键红:`--session` 未识别退出码 2;writer=designer-cli;`MDCG_SESSION` 被无视落随机值;无告警)。
81
+ - 修复后绿:同命令 → PASS=10 FAIL=0。**本报告撰写会话重跑:PASS=10 FAIL=0,EXIT=0**(session/writer/reviewer 三归因逐项断言 + 稳定性 + `MDCG_SESSION` 兜底 + 告警一行 + 随机兼容,实测输出见第四节抽验)。
82
+ - 孪生件端到端(修复轮,子进程,临时库 + `MDCG_MASTER_KEY=ab*32` 哑密钥):`scripts/review_cli.py accept --session sess_twin_01` → rc=0,frontmatter 三归因在位。
83
+ - 定向回归全绿(修复轮实跑):test_review_conformance 38/0、test_index_durability 16/0、test_identity_attribution 19/0、test_propose_tail_index 8/0、test_review_onepass 10/0、test_merge_upsert 11/0、test_n131_merge_gate 17/0、test_p2_mcp 64/0、test_p21_tokens 38/0、test_p2 37/0、test_action_derive 46/0、test_mr_m4 88/0。
84
+ - 组全量:`python scripts/run_tests.py md_cg --jobs 4` → 159/159 通过、3 跳过(白箱语料依赖,既有跳过)、exit 0,清单含 PASS `md_cg.test_review_cli_attribution`。
85
+ - `python -m md_cg.review_cli accept --help` → `--session` 在列;`python scripts/run_tests.py --list` → 新守卫已入发现面。
86
+
87
+ ### 2.3 P3——knowledge/orphan/None.md 脏节点写入源头(md_cg 写入边界 node_id 污染形态放行)
88
+
89
+ **target**:DSH 在役库出现 `knowledge/orphan/None.md` 并进入检索与 route 候选(route op 即 `cg.search`,`md_cg/mcp_server.py:2117-2119`,本会话实测)。定性:`str(None)`(Python)/`String(null)`(TS)字符串化污染形态在写入边界放行。
90
+
91
+ **落点**(本会话读码复核在位):
92
+ - `md_cg/mdcg.py:909-916`——新增模块级 `_NODE_ID_FORBIDDEN = frozenset({"None", "null"})` 及成因注释,紧邻 `_NODE_ID_RE`(:907);
93
+ - `md_cg/mdcg.py:1316-1321`——add 既有白名单闸(None 本体/空串/`..` 穿越/形状)位置;
94
+ - `md_cg/mdcg.py:1322-1326`——add() 写入边界新增整串精确等值闸:`nid_s in _NODE_ID_FORBIDDEN` → 结构化 ValueError(消息点名 P3 缺陷与改法);
95
+ - `md_cg/test_none_id_write_guard.py:1-165`——新红守卫,套件自动发现。
96
+
97
+ **前后对比**:
98
+ - 修前(修复轮,临时库实弹复现):`MdCGOS.add("None", …)` 返回 `'None'` 并落盘 `knowledge/orphan/None.md`;`add("null")` 同(白名单 `_NODE_ID_RE` 形状合法放行——str(None)/String(null) 字符串化污染形态);该文件经 `_scan_nodes` 收录为 id="None",search_rrf 实测命中 `('None', 0.016)`,与 DSH 在役『None.md 出现在检索与 route 候选』病理吻合。
99
+ - 修后:add 在唯一写入边界拒 `"None"`/`"null"`(ValueError),盘面无 None.md、索引无该条目;防误杀与既有语义不回退(`'NoneBot_1'`/`'nullify_test'` 子串 id 正常写入;`add(None)`/`''`/`0` 仍 P0-1 ValueError)。
100
+ - 红→绿:守卫修前 9 通过/7 失败,修后 17 通过/0 失败。**本报告撰写会话重跑:17 通过/0 失败,EXIT=0**([1] 字符串 "None" 拒写 4 断言、[2] "null" 拒写、[3] 防误杀、[4] 既有语义不回退、[5] writepipe 链不落盘、[6] 存量脏数据收录取证与清理路径实证,实测输出见第四节抽验)。
101
+
102
+ **语义变化**:
103
+ 1. 行为变化仅一处:node_id 与 `"None"`/`"null"` **整串精确等值**(大小写敏感、不扩子串)的写入由『成功落盘』变『ValueError 拒绝』。全部写入链(writepipe 执行器、review accept、identity、consolidate、branches)最终经 add 单点落盘,行为同步收敛;已持有存量 id="None" 节点的库再覆写该 id 将被拒——预期 fail-closed。
104
+ 2. 定性(取证结论):None.md 出自两段叠加——①批次 24(2a57c2a2,2026-09-24)之前 add 无任何校验、`f"{node_id}.md"` 直拼,`add(None)` 直落 None.md(修复轮 `git show 2a57c2a2^:md_cg/mdcg.py` 实证:旧 add 无校验、path 直拼,DSH 在役库的存量 None.md 属该历史脏数据);②现行代码 `add(None)` 本体已拒,但字符串形态 `add("None")` 仍可造出 None.md——这是现行残留洞而非纯历史问题,故本次修复成立,非『修不存在的缺陷』。
105
+ 3. 存量清理建议(未代执,不触真实库):删除 `<root>/knowledge/orphan/None.md` 后 `rebuild_index()` 即从索引与检索/route 候选消失(守卫 [6] 三断言实证该路径有效);勿用 `add("None")` 覆写清理(修后被拒)。
106
+ 4. 相邻发现(如实报告,未修,超本目标):①`add(123)` 等非字符串真值 id 以原值入索引键,flush 落快照后重开 `_load_index` 的 `sorted(nodes)` 混型键 TypeError 整库打不开(修复轮探针实测);②`propose(None)` 抛裸 TypeError 非 ValueError(`md_cg/mdcos.py:1770` 附近,不产生 None.md,未动)。建议入池,见第六节。
107
+ 5. 工作区内 mdcos.py/review_cli.py 等另有并行改动(P1/P1b 修复员所为),未触碰;未 git commit(编排会话负责)。
108
+
109
+ **验证**(修复轮实跑):
110
+ 1. 红态:`python -X utf8 -m md_cg.test_none_id_write_guard` → 9 通过/7 失败 exit 1([1][2][5] 污染形态红,[3][4][6] 防误杀/既有语义/取证面绿)。
111
+ 2. 绿态:同命令 → 17 通过/0 失败 exit 0。本会话重跑一致(EXIT=0)。
112
+ 3. 回归:`test_security_audit` 13/0;`test_security_audit_v21` 49/0;`test_session_isolation` 19/0。
113
+ 4. 全量:`python scripts/run_tests.py --jobs 4` → 「SUMMARY 213/213 通过,5 跳过(依赖缺失/平台不符)」exit 0,且 `_discover()` 列表(218 件)含 `md_cg.test_none_id_write_guard`。
114
+ 5. 取证:grep 写入路径构造点(`_node_disk_path`、add path 派生、`_scan_nodes`、writepipe execute/executor)+ `git show 2a57c2a2^:md_cg/mdcg.py` + 临时库探针(add 13 种 None-ish 调用形态枚举,仅 `add("None")` 复现 None.md);探针与守卫临时目录均已删除,未触真实令牌库/真实在役数据。
115
+
116
+ ---
117
+
118
+ ## 三、跨进程可见性红转绿证据(P1b)
119
+
120
+ **红态**(修复前,`python -X utf8 -m md_cg.test_review_cli_visibility`):PASS=5 FAIL=5。
121
+
122
+ 关键红项③的契约:进程 A' 经生产构造 `_cg` 调库裸 add 2 条后 `os._exit(0)`(模拟被杀,close/atexit 均不执行),退出后盘面 `_index_log/` 必须已含全部写入记录。修复前 `log_ids=[]`——缺省 `autoflush=64`(`md_cg/mdcg.py:922`)未达阈值,分片日志从未落;此时另一进程 B 重开读面(`_load_index`=快照+日志重放,`md_cg/mdcg.py:948-975`)自然读不到这两条,即 DSH 在役 read null 的机理。
123
+
124
+ **绿态**(修复后):PASS=10 FAIL=0。本报告撰写会话重跑实测输出(节选):
125
+
126
+ ```
127
+ 【①③】… 进程 B 读回业务节点 vis-acc-node 可见 · READ 1
128
+ 进程 B 侧审计记录节点可见(review_records 命中 1 条)
129
+ 【②③】进程 A' 调库落 2 条即 os._exit(模拟被杀)→ 落账契约 + 进程 B 读
130
+ [PASS] 进程 A' 经生产构造 _cg(autoflush=1 回显) · staged=0 autoflush=1
131
+ [PASS] ③ 落账契约:A' 被杀退出后 _index_log 已含全部 2 条写入记录
132
+ · log_ids=['vis-node-1', 'vis-node-2']
133
+ [PASS] ② 进程 B 读回 kill 形态的两条节点均可见 · READ 1 1
134
+ 【④】同病工具面 autoflush=1
135
+ [PASS] review_cli._cg 构造 autoflush==1 · 1
136
+ [PASS] evidence._open_cg 构造 autoflush==1 · 1
137
+ [PASS] scripts/linkref_backfill.py 构造语句带 autoflush=1(源级断言)
138
+ [PASS] scripts/migrate_restricted.py 构造语句带 autoflush=1(源级断言)
139
+ ================================================================
140
+ PASS=10 FAIL=0 EXIT=0
141
+ ```
142
+
143
+ **证据链要点**:
144
+ - 红转绿的唯一变量是四处构造的 `autoflush=1`(写入侧落账时机),读面 `_load_index` 重放逻辑一行未改——同一读面在红态读不到、绿态读到,证明缺陷在写入面,与定性一致。
145
+ - ①②读面断言修复前后均绿:正常退出路径在本仓 HEAD 已有三层既有兜底(mdcos review_decide 显式 flush :2258/:2309/:2330;review_cli finally `cg.close()` :204-211;库层 atexit `mdcg.py:104-118`);kill 形态重开读面另有批次 39 指纹失配→全扫自愈(`mdcg.py:959-970`)。DSH 在役实测命中的是**旧读面(0.4.x 无指纹校验)×旧写入面(无 autoflush)**叠加,两层兜底均晚于其在役版本。①②保留为回归钉。
146
+ - 时间线佐证:17699ec5(2026-09-16,server 级 autoflush=1)早于 9102df88(批次 39,2026-09-25,指纹自愈),故在役 0.4.x 两侧皆旧。
147
+
148
+ ---
149
+
150
+ ## 四、门禁
151
+
152
+ 编排会话门禁执行记录(本报告撰写会话**未重跑门禁全套**,如实注明):
153
+
154
+ | 门禁项 | 结果 |
155
+ |---|---|
156
+ | python 全量测试 | 通过(退出码 0) |
157
+ | 注入套件 | 18/18,与登记一致(EXIT=0) |
158
+ | gate | 1 |
159
+
160
+ **本报告撰写会话抽验(本会话实跑,均 EXIT=0)**:
161
+
162
+ | 命令 | 结果 |
163
+ |---|---|
164
+ | `python -X utf8 -m md_cg.test_review_cli_visibility` | PASS=10 FAIL=0 |
165
+ | `python -X utf8 -m md_cg.test_review_cli_attribution` | PASS=10 FAIL=0 |
166
+ | `python -X utf8 -m md_cg.test_none_id_write_guard` | 17 通过 / 0 失败 |
167
+ | `python -X utf8 -m py_compile`(11 件:7 改动面 + 3 新守卫 + `scripts/_mdcg_reindex_code.py`) | COMPILE_OK |
168
+
169
+ ---
170
+
171
+ ## 五、回告 DSH 端复验清单(复现步骤+预期)
172
+
173
+ **通用前置**:拉取含本轮修复的 md_cg 构建(三项修复均在本仓未提交工作区,随编排会话提交/发布后生效;在役 0.4.x 副本不含 09-16 autoflush 与批次 39 指纹自愈,须升级方可复验);复验一律临时库 + 哑密钥,勿触在役数据。
174
+
175
+ **P1b(跨进程可见性)**
176
+ - 步骤 A(守卫):`python -X utf8 -m md_cg.test_review_cli_visibility`。
177
+ 预期:PASS=10 FAIL=0,退出码 0;③项 `log_ids=['vis-node-1','vis-node-2']`。
178
+ - 步骤 B(手工复现):进程 A 以生产构造(review_cli accept 或 `_cg` 裸 add)写入 2 条后立即 `os._exit(0)` 模拟被杀;随后检查 `<root>/_index_log/`。
179
+ 预期:A' 退出后分片日志立即含全部 2 条写入记录;进程 B 全新进程 `_load_index`+read 可见业务节点与审计记录节点。
180
+
181
+ **P1(裁决归因)**
182
+ - 步骤 A(守卫):`python -X utf8 -m md_cg.test_review_cli_attribution`。
183
+ 预期:PASS=10 FAIL=0,退出码 0。
184
+ - 步骤 B(端到端):以身份 X(如 dsh-memory)propose 一条 → `python -m md_cg.review_cli accept <pid> --session <同一会话id> --reason "…"`(裁决者 designer-cli)。
185
+ 预期:退出码 0;落盘节点 `frontmatter.session==<传入值>`、`writer==X`(原始写入者在位)、`reviewer=="designer-cli"`;同一 `--session` 再裁一条,两节点 session 一致。
186
+ - 步骤 C(兼容性):不传 `--session` 亦不设 `MDCG_SESSION` 裁决一条。
187
+ 预期:stderr 恰一行随机会话告警、退出码 0、落 `sess_*` 随机值、writer 归因同样在位(不阻断)。
188
+ - **P1-1 环境侧核对(回告项)**:核对 DSH 会话目录真实存在于 `MDCG_DSH_SESSIONS_ROOT` 或 `~/.dsh/sessions` 之下。预期:目录在位后,MCP 写入面 `_normalize_session`(`md_cg/mcp_server.py:3381-3405`)不再将该会话降级 anonymous,MCP 面与 CLI 面归因一致;目录不在位时 MCP 面降级 anonymous 属既有防编造设计,CLI 侧(直构 Principal 不经该校验)不受影响,但两侧归因会对不上——需环境侧保证目录在位。
189
+
190
+ **P3(None.md 脏节点)**
191
+ - 步骤 A(守卫):`python -X utf8 -m md_cg.test_none_id_write_guard`。
192
+ 预期:17 通过 / 0 失败,退出码 0。
193
+ - 步骤 B(写入拒绝):修复版库 `add("None", …)` / `add("null", …)`。
194
+ 预期:ValueError(消息点名字符串化污染形态与 P3),盘面无 None.md/null.md,索引无该条目;`add("NoneBot_1")`/`add("nullify_test")` 等子串 id 不受影响。
195
+ - 步骤 C(存量清理,DSH 端自执,本轮未代执):核对 `<root>/knowledge/orphan/None.md` 是否存在;存在则删除该文件后 `rebuild_index()`。
196
+ 预期:id="None" 从索引、检索结果与 route 候选消失(守卫 [6] 实证该路径)。**勿用 `add("None")` 覆写清理(修后被拒)。**
197
+
198
+ ---
199
+
200
+ ## 六、遗留
201
+
202
+ 1. **读路径自愈长效兜底与升级要求**:批次 39(9102df88,2026-09-25)`_load_index` 指纹失配→全扫自愈(`md_cg/mdcg.py:959-970`)已在库层兜住 kill 形态重开读面;但 0.4.x 旧副本读面盲信快照无指纹校验——DSH 侧**必须升级构建**才能同时获得旧读面自愈与本轮写入面兜底,升级列为第五节复验前置。
203
+ 2. **P1b 读面断言①②为回归钉**:正常路径与 kill 重开路径的既有兜底(显式 flush / close / atexit / 指纹自愈)若未来被移除,守卫即红。
204
+ 3. **P3 相邻发现未修**(如实,建议入池):`add(123)` 等非字符串真值 id 混型索引键可致重开 `_load_index` 的 `sorted(nodes)` TypeError 整库打不开(修复轮探针实测);`propose(None)` 抛裸 TypeError 非 ValueError(`md_cg/mdcos.py:1770` 附近,无脏文件产生)。
205
+ 4. **P4/P5 入池**:编排决策不入本轮;定性细节本轮材料未含,本报告不作描述(不虚构)。
206
+ 5. **存量 None.md 清理在 DSH 真实库的执行**由 DSH 端自执(本轮未代执、未触真实库),路径见第五节 P3 步骤 C。
207
+ 6. **批式工具 autoflush 取舍**:`scripts/_mdcg_reindex_code.py` 维持 `autoflush=64`(:62)+ 显式 `cg.close()`(:128)——读码判定后的刻意不动(批式数千条逐条落日志无收益,:128 close 即确定性提交边界),非遗漏。
208
+ 7. **本报告会话未重跑项**:门禁全套(python 全量/注入套件/gate,编排会话执行记录见第四节)、组全量与定向回归套件(修复轮实跑记录见第二节各验证栏)、孪生件端到端(修复轮实跑,本会话仅复核同步点源码在位)。以上均为如实申报,未以本会话名义重报为已跑。
209
+
210
+ ---
211
+
212
+ ## 七、纪律合规声明
213
+
214
+ 1. **实验环境**:全程系统临时目录 + 哑主密钥(`MDCG_MASTER_KEY=ab*32`);未触真实令牌库、真实 serve、真实在役数据目录。
215
+ 2. **清理**:修复轮实验残留(`mdcg_p1b_exp`、`clivis_*`、`cliattrib_*`、`twin_attr_*`、`mdcg_conf_*` 及 P3 探针/守卫临时目录)均已清理,复查余量 0。
216
+ 3. **提交纪律**:未 git commit(编排会话负责);工作区未提交改动即本轮修复落点(7 修改 + 3 新守卫,见前言基线)。
217
+ 4. **引证纪律**:结论均带 `path:line`;验证结果区分「修复轮实跑(材料记录)」与「本报告会话实跑(第四节抽验)」;未跑项如实标注(第六节第 7 条),无以未跑充已跑之处。
218
+ 5. **路径表述**:本报告不写本机绝对路径,实验目录一律以「系统临时目录下 …」表述。
219
+ 6. **无法满足项如实列出**:P4/P5 定性细节本轮材料未提供,总览矩阵与遗留仅记「入池」,不作内容描述;材料个别行号与当前工作区实测有漂移(如 review_decide 显式 flush 实测在 `mdcos.py:2258/2309/2330` 而非材料所记 2248/2299/2320),报告已全部按本会话实测修正,未沿用漂移值。
220
+
221
+ ---
222
+
223
+ ### 附:自查双清单
224
+
225
+ **清单一:结论引证自查(关键结论 → 引证 → 验证方式)**
226
+
227
+ | 结论 | 引证 | 验证方式 |
228
+ |---|---|---|
229
+ | P1b 四处落点 `autoflush=1` | `md_cg/review_cli.py:106`、`md_cg/evidence.py:512`、`scripts/linkref_backfill.py:198`、`scripts/migrate_restricted.py:46` | 本会话逐处读码在位 |
230
+ | 缺省 autoflush=64、atexit 仅正常退出 | `md_cg/mdcg.py:922`、`mdcg.py:104-118` | 本会话读码 |
231
+ | `_stage` 阈值落账 / flush 幂等落分片日志 | `md_cg/mdcg.py:2079-2087`、`mdcg.py:1079-1098` | 本会话读码 |
232
+ | `_load_index` 快照+重放、指纹失配全扫自愈 | `md_cg/mdcg.py:948-975`(:959-970) | 本会话读码 |
233
+ | P1 三归因改造点 | `md_cg/review_cli.py:31-44/75-80/83-106/175-178`、`md_cg/mdcos.py:2240-2249`、propose actor 快照 `md_cg/mdcos.py:1770` | 本会话读码 |
234
+ | P1-1 校验面 | `md_cg/mcp_server.py:3381-3405`(:3397 目录根) | 本会话读码 |
235
+ | P3 写入边界闸 | `md_cg/mdcg.py:909-916`、`mdcg.py:1322-1326` | 本会话读码 |
236
+ | route op 即 cg.search | `md_cg/mcp_server.py:2117-2119` | 本会话读码 |
237
+ | reindex 工具刻意不动 | `scripts/_mdcg_reindex_code.py:62/:128` | 本会话读码 |
238
+ | 孪生件同步 | `scripts/review_cli.py:66/:153` 等 | 本会话读码 |
239
+ | 三守卫全绿 | 第四节抽验表 | 本会话实跑(EXIT=0) |
240
+ | 11 件 py_compile | 第四节抽验表 | 本会话实跑(COMPILE_OK) |
241
+ | 门禁三项、组全量、定向回归、孪生端到端、红态 5/5 与 9/7 | 第二节各「验证」栏、第四节门禁表 | 修复轮/编排实跑记录(材料),本会话未重跑 |
242
+
243
+ **清单二:未实跑/未尽事项申报**
244
+
245
+ - 门禁全套(python 全量、注入套件 18/18、gate=1):编排会话执行,本会话未重跑——按记录转述,未冒充本会话结果。
246
+ - 全量 213/213、组全量 159/159、定向回归 12 套、孪生件端到端、红态(PASS=5 FAIL=5 / PASS=2 FAIL=8 / 9 通过 7 失败):修复轮实跑(材料记录),本会话未重跑红态(绿态三守卫已重跑)。
247
+ - 存量 None.md 真实库清理:未代执(DSH 端自执)。
248
+ - P4/P5:无定性材料,未描述。
249
+ - P3 相邻两项(混型索引键 TypeError、propose(None) 裸 TypeError):未修,已申报入池建议。
250
+ - 材料行号校勘:`_stage` 2066-2073→实测 2079-2087;flush 1070-1089→实测 1079-1098;`_load_index`/指纹自愈 939-973/958-961→实测 948-975/959-970;autoflush 缺省 913→实测 922;atexit 104-119→实测 104-118;review_decide 显式 flush 2248/2299/2320→实测 2258/2309/2330;review_cli finally close「196 区」→实测 204-211;argparse --session 165-168→实测 175-178。其余引证与实测一致。
251
+
252
+ ---
253
+
254
+ *报告撰写会话(报告员)2026-09-26 · 全部抽验命令与输出见第四节;材料与实跑来源已在正文逐处标注。*
@@ -0,0 +1,124 @@
1
+ # P1b-2 修复报告:读面缓存代际——常驻进程索引跨进程写入不可见(v1.0)
2
+
3
+ - **日期**:2026-09-26
4
+ - **缺陷编号**:P1b-2(DSH 端在役复验 dsh_restart_recheck_20260926 定性)
5
+ - **改动面**:`md_cg/mdcg.py`、`md_cg/mdcos.py`、`md_cg/mcp_server.py` 三改 + `md_cg/test_index_crossprocess_reload.py` 一加(git diff --stat:3 files changed, +90/-7;未 commit,由编排会话负责)
6
+ - **行号口径**:本报告全部 path:line 经报告会话读码逐一核对;与修复会话材料有出入的 3 处已按读码实测修正并在「三、修复」末尾的核对注记中标明。
7
+
8
+ ---
9
+
10
+ ## 一、缺陷与证据链(DSH 端在役复验时间戳锁死证据)
11
+
12
+ **缺陷**:MdCG 常驻进程的索引只在启动时装载一次,此后 read/get/search 全走内存态——其它进程(典型:review_cli,autoflush=1)写入对常驻进程**不可见**。
13
+
14
+ **证据链(DSH 端在役复验,探针 dsh_restart_recheck_20260926,时间戳锁死)**:
15
+
16
+ | 时刻 | 事件 |
17
+ |---|---|
18
+ | 22:13:16 | 常驻进程启动装载索引快照:14135 节点 |
19
+ | 22:14:37 | review_cli(autoflush=1)写入探针节点 |
20
+ | 22:14:43 | 磁盘 `_index.json` 已含探针(写侧落盘成功) |
21
+ | 同一常驻进程 | `read` 探针 → **null**(读面不可见——病灶) |
22
+ | 旁路新起进程 | `read` 探针 → 命中(新进程装载时读盘即得) |
23
+
24
+ **关键反差**:写侧账本是平的(盘面有、新进程能读),唯独**常驻进程**的内存态停留在启动代——这不是写入丢失,是**读面代际停滞**。
25
+
26
+ ## 二、根因(启动装载一次 + 批次39 指纹仅在装载时)
27
+
28
+ 1. **架构根因**:`MdCG.__init__` 仅启动时调一次 `_load_index()`(mdcg.py:939),此后 `self.index` 常驻内存,读路径(`get`/`search`/`search_rrf`/`list_goals`)不再碰盘——他进程写快照对常驻进程永不生效。
29
+ 2. **演进根因**:批次39 为 issue #33 加的 `_index.json` 指纹校验(`fp = idx.get("_fingerprint")` 与 `_dir_fingerprint()` 比对,mdcg.py:973-974;修前行号 966-967)**也在 `_load_index` 内部**——它防的是「快照写入后盘面有增删」的陈旧快照误信,只在启动装载那一瞬间执行一次,不防「进程存活期间盘面变化」。
30
+ 3. 二者叠加:指纹校验给了「索引新鲜」的错觉,实际保鲜窗口只有启动瞬间;DSH 复验链中 22:14:43 的盘面真相到不了 22:13:16 装载的常驻进程。
31
+
32
+ ## 三、修复
33
+
34
+ **【机制】**(mdcg.py)
35
+
36
+ - **:946**(`__init__` 装载后):`self._index_sig = self._index_signature()`——记录装载时的文件签名作基线。
37
+ - **:992-1004** 新增 `_index_signature()`:`os.stat(index_path)` 取 `(st_mtime_ns, st_size)` 二元组(微秒级、无 open 无解析);文件不存在或 stat 抛 `OSError` 时返回 `None` 静默——签名探测绝不阻塞读。`(mtime_ns, size)` 在 NTFS 100ns / ext4 ns 粒度下足以识别「他进程写快照」;`None→非 None` 同样可判(他进程首次落快照)。
38
+ - **:1006-1047** 新增 `_maybe_reload_index()`:签名对比(相等含双侧 None → 返回 False 零开销);变化才调 `_load_index()` 重载(`OSError/ValueError` 静默放弃、沿用旧内存态——重载失败不阻塞读);重载后把 `self._dirty` 重放回新索引(`None`=tombstone pop,与 `_load_index` 日志重放同语义)并重算 buckets——本实例未 flush 的写入不因重载从检索面消失;顺带失效 hotcache(默认关)与 readcache(默认开)两个旧索引代派生缓存。
39
+ - **:1139-1141** `compact_index` 写快照后主动刷签名;**:1308-1309** `rebuild_index` 同——**自写不自载**(防止把自己的 compact/rebuild 误判为他进程写入而做无谓重载循环)。
40
+ - **flush 无改动**:flush 只在 `FileLock` 下向 `_index_log` 追加分片、不改 `_index.json`(mdcg.py:1145 def,:1153-1164 追加临界区),签名不变——自身写入/落账**永不**触发重载,正合适。
41
+
42
+ **【接线清单(读码定案,5 处,写路径不接)】**
43
+
44
+ | # | 位置 | 覆盖链 |
45
+ |---|---|---|
46
+ | 1 | mdcg.py:2054 `list_goals` | op=goal list/active 链(MdCGOS.list_goals 走 super 同源) |
47
+ | 2 | mdcg.py:2319 `get` | op=read node_id 链;MdCGSecure.get(mdcos.py:4027-4031,读隔离判定后 `super().get`)同源覆盖 |
48
+ | 3 | mdcg.py:2523 基类 `search` | 空查询早退后、读索引前 |
49
+ | 4 | mdcos.py:767 `MdCGOS.search`(:745 def) | 生产检索 + op=route 链(mcp_server.py:2117-2119 `if op == "route":` → `cg.search(...)`) |
50
+ | 5 | mdcos.py:1315 `MdCGOS.search_rrf` | 置于热路径 query 缓存查询(`hc = _hc.get(self)`,mdcos.py:1322)**之前**——旧索引代缓存命中会静默错答;op=read budget_tokens→recall→search_rrf 链同覆盖 |
51
+
52
+ 写路径一律不接(写入面经 `_dirty`/flush 自有账本,接了反而引入自重载循环)。
53
+
54
+ **【maintain reload(显式探活面)】**
55
+
56
+ - mdcos.py:2689-2698 `MAINTAIN_ACTIONS` 登记 `"reload"`(:2690);
57
+ - mdcos.py:2752-2755 maintain 分派 reload 分支:`return {"ok": True, "op": "maintain", "action": "reload", "reloaded": bool(self._maybe_reload_index())}`(:2754)——签名未变时是零开销 stat + False,幂等;
58
+ - mdcos.py:2735 契约注释(生效条件):reload 只触发 `_maybe_reload_index`;
59
+ - mcp_server.py:2688-2698 `_maintain_call` 文档面:action 枚举补 reload,标注只读档(「reload 读面代际刷新 → 不额外拦截」),免 admin 天然放行。
60
+
61
+ **【红守卫】** `md_cg/test_index_crossprocess_reload.py`(新增,run_tests.py 的 `glob("md_cg/test_*.py")` 发现机制自动纳入 CI——scripts/run_tests.py:45;跳过规则 `_SKIPS` 在 :139-146,本守卫不在跳过清单)。守卫六组,全部临时库 + 哑主密钥(进程内 `os.environ['MDCG_MASTER_KEY']='ab'*32` 先行 :134,子进程同哑密钥 :95,`tempfile.mkdtemp` :138/:182/:196/:248,rmtree 清理 :291)。
62
+
63
+ **before / after**
64
+
65
+ - **修复前**:`MdCG.__init__` 仅启动调一次 `_load_index`(mdcg.py:939 旧行),此后 read/get/search 全走内存态;复验证据链(第一节)写侧落盘成功、同常驻进程 read null、旁路新进程可读。
66
+ - **修复后**:读路径入口每调用一次 stat(微秒级)对比签名,他进程写快照(compact/rebuild,典型在 close)即触发重载 + 日志重放,写入/删除(tombstone)对常驻进程可见;自身 flush/写入/compact/rebuild 全程零真实重载(自写不自载)。
67
+ - **守卫红转绿实测**:同解释器双实例(临时库+哑密钥),B 实例 add+close 落快照后——A 禁用 reload 时 read null(修前病灶形态)→ 恢复后 read/search 命中;C 实例 forget+close 后 A read 归 null(tombstone 可见);goal=list 经 list_goals 可见;maintain action=reload 幂等探活(未变 False / 他写后 True 且读面立即可见);旁路子进程 READ 1 0(修前已绿,钉住盘面账本)。
68
+ - **性能粗测**:接线态 vs 重载桩空转态——修复会话实测 157.4µs/读 vs 140.7µs/读(增幅 ~11.9%,即一次 stat 摊入 ~150µs 的 get;断言上界 ×3+30µs 内通过);报告会话复跑 164.0µs/读 vs 149.0µs/读(增幅 10.1%,同断言通过)。
69
+
70
+ **semantics_note(语义边界,如实)**
71
+
72
+ 1. **探测面边界**:签名只盯 `_index.json`——他进程**仅追加分片日志、尚未写新快照**(存活写方未 close/rebuild)不在自动探测面内。这正合适:自身 flush 也是纯日志追加,签名不变故零自重载循环;DSH 复验链中 review_cli 退出即 close→compact 落快照,落在探测面内。此类场景可用 maintain action=reload 显式探活(显式 reload 的真重载同样走 `_load_index` 的快照+日志全量口径,日志记录仍须快照重写才并入)。
73
+ 2. **mtime 粒度**:NTFS 100ns 粒度下同 tick 同尺寸的两次快照写入理论上可能漏检一次,下次任何签名变化自愈(启发式哨兵的诚实边界)。
74
+ 3. **重载保留本实例未 flush 写入**:`_dirty` 重放回新索引(None=tombstone pop,与 `_load_index` 日志重放同语义)并重算 buckets——`_stage` 双写 index+_dirty 的「检索看得到未落盘写入」语义不变。
75
+ 4. **派生缓存失效**:重载成功时顺带失效 hotcache(默认关)与 readcache(默认开)——旧索引代的 query 结果与同 path 重写的解析产物不陈旧化;重载是稀疏事件,不构成热路径开销。
76
+ 5. **未接线的读面(读码盘点)**:op=edges(provenance.all_edges→index_edges)、protect action=check(mcp_server.py:1142-1146 直读 `cg.index`)、service_info/insight/stats(mcp_server.py:3074-3079 `len(cg.index["nodes"])`)等低频读面仍走启动快照——高频面(get/search/search_rrf/route/recall/list_goals)已全覆盖,低频面如需可后续按同款单行接入。
77
+ 6. **stat 成本 vs 常驻不可见取舍**:无跨进程写入时每次读多一次 `os.stat`(实测 ~11-16µs 量级,占 get 的 ~11%,search 毫秒级面摊薄至 <1%),换得跨进程写入可见——读热路径无重载(重载仅在签名变化时发生,重载成本=一次 `_load_index`,与进程重启装载同量级但免去重启)。
78
+
79
+ **核对注记(报告会话读码实测 vs 修复会话材料的 3 处修正)**
80
+
81
+ | 材料 | 实测(本报告采用) |
82
+ |---|---|
83
+ | `MdCGSecure.get:4075` | **mdcos.py:4027-4031**(`MdCGSecure` 类在 mdcos.py:3655 起,不在 mdcg.py;:4075 附近实为 forget 区) |
84
+ | `_maintain_call` 2689-2692 | **mcp_server.py:2688-2698**(def 在 :2688,reload 只读档说明在 docstring :2689-2698) |
85
+ | 批次39 指纹校验 mdcg.py:966-967 | 修复插入后位移为 **mdcg.py:973-974**(报告第一节/第二节按修后行号引用,旧号在括注保留) |
86
+
87
+ ## 四、验证(守卫红转绿 + 全量回归 + 门禁)
88
+
89
+ **本会话(报告员)实际复跑**(cmd:`python -X utf8`,工作目录仓库根):
90
+
91
+ | 检查 | 命令 | 结果 |
92
+ |---|---|---|
93
+ | 新守卫 | `python -X utf8 -m md_cg.test_index_crossprocess_reload` | **PASS=21 FAIL=0**(六组全绿:红转绿/旁路子进程/自身写入零重载+自写不自载/maintain reload/goal 链路/性能粗测;⑥性能 接线态 164.0µs vs 桩 149.0µs、增幅 10.1%,上界 ×3+30µs 通过) |
94
+ | ask 点名回归① | `python -X utf8 -m md_cg.test_readcache_prodpath` | **PASS 12 / FAIL 0** |
95
+ | ask 点名回归② | `python -X utf8 -m md_cg.test_index_durability` | **PASS=16 FAIL=0** |
96
+ | 改动面核对 | `git diff --stat` | mcp_server.py 4 行、mdcg.py 77 行、mdcos.py 16 行——合计 **3 files changed, 90 insertions(+), 7 deletions(-)**,与材料一致,未 commit |
97
+
98
+ **修复会话记录(材料转录,本会话未复跑、如实标注来源)**:
99
+
100
+ - 点名回归:`test_p2` 通过 37/失败 0。
101
+ - 扩面(均 0 失败):`test_p2_mcp` 64 通过、`test_readcache_default_on` PASS 10、`test_readcache_precise_inval` PASS 23、`test_i28_hotcache_prodpath` ALL OK 8 项、`test_snapshot_autoclose` PASS=10/FAIL=0、`test_review_cli_visibility` PASS=10/FAIL=0、`test_p30_maintain` 55 通过、`test_p32_backfill` 59 通过、`test_p36_kp_align` PASS=34/FAIL=0。
102
+ - 全量:`python -X utf8 scripts/run_tests.py md_cg --jobs 4 --timeout 600`(后台运行)→ **exit 0,"===== SUMMARY 161/161 通过,3 跳过(依赖缺失/平台不符)====="**;经 `--list` 核对发现 164 目标含 `test_index_crossprocess_reload`,跳过 3 项均为 `_SKIPS` 数据面依赖(test_p44_md_whitebox / test_md_access_parity / test_wisdom_md_store,与 scripts/run_tests.py:139-146 清单一致),与新守卫无关。
103
+
104
+ ## 五、DSH 端复验指引(探针 dsh_restart_recheck_20260926)
105
+
106
+ 修复部署后,探针应立即可读——**无需重启在役进程**:
107
+
108
+ 1. **新起进程**:装载时经 `_load_index`(快照+日志全量口径)天然并入,探针直接可见。
109
+ 2. **常驻旧进程**(修复前启动、仍持 22:13:16 旧内存态):二选一——
110
+ - 调一次 `cg(op="maintain", action="reload")`:签名对比必变(review_cli 退出即 close→compact 落了新快照)→ 真重载 → 探针立即可见,返回 `"reloaded": true`;再调一次返回 `false`(幂等探活);
111
+ - 或待其自然重启,重启后装载即含探针。
112
+ 3. **注意语义边界①**:若某存活写方只追加了分片日志、尚未写新快照,maintain reload 的真重载也读不到该增量——日志记录须快照重写才并入。DSH 复验链的 review_cli 已退出(close→compact 已落快照),不受此限。
113
+
114
+ ## 六、纪律合规声明
115
+
116
+ 1. **实验隔离**:全部测试/守卫运行于 `tempfile.mkdtemp` 临时库 + 哑主密钥(进程内 `os.environ['MDCG_MASTER_KEY']='ab'*32` 先行,子进程 env 同哑密钥),绝不触真实 `~/.mdcg` 令牌库(守卫源码 md_cg/test_index_crossprocess_reload.py:56/:95/:134/:291 可复核)。
117
+ 2. **在役面零接触**:DSH 在役库与探针 dsh_restart_recheck_20260926 本会话未触碰(仅读代码与跑临时库测试);在役复验按第五节指引执行。
118
+ 3. **临时产物清理**:本会话运行守卫产生的 `p1b2_*` 临时目录已清理归零(清理前 8 个 → 清理后 0,已实测复核);`clivis_*` 8 个系批次57 遗留、非本会话产物,按材料注记未动。
119
+ 4. **版本管理**:未 git commit(编排会话负责);工作区改动面 = 三改一加,与本报告第一节改动面记录一致。
120
+ 5. **报告口径**:本报告不写本机绝对路径;所有 path:line 经本会话读码核对;本会话实跑的检查与修复会话材料转录的检查在第四节分列注明来源,无任何未实跑检查被报告为已通过。
121
+
122
+ ---
123
+
124
+ *报告员(工程报告撰写)· 2026-09-26 · P1b-2 v1.0*
@@ -0,0 +1,171 @@
1
+ # 0.6.0 发布准备报告 v1.0 —— 一键配置 init · DSH 0.17.2 声明 · 版本发布准备
2
+
3
+ > 批次:0.6.0 发布准备 | 报告:v1.0 | 环境:win32,仓库 `dsh-memory-main`(工作区 HEAD `d7c6e347`)
4
+ > 本报告所引全部检查为本会话(报告撰写会话)**实跑复验**的结果;引用行号均经读取核对。未跑项在第四节与自查双清单中如实声明。
5
+
6
+ ---
7
+
8
+ ## 一、总览
9
+
10
+ 本次 0.6.0 发布准备含三个任务,全部落在工作区未提交改动中(编排会话负责 git commit):
11
+
12
+ | # | 任务 | 交付物 | 本会话复验 |
13
+ |---|---|---|---|
14
+ | 1 | **init 一键配置** | 新增 `src/init.ts`(lingshu-init CLI)、`src/cli.ts`(lingshu 主命令分发)、`test/init.test.ts`(6 用例);`package.json` 补 bin 字段;README 快速开始头部加「一键配置(推荐)」节;WORKSPACE_INDEX.md 重生成 | build/单测/E2E/gate 全过(见第四节) |
15
+ | 2 | **README DSH 0.17.2 声明** | README.md:14 徽章换为「DSH 适配-0.17.2 已验证」;README.md:106 前置行追加「已验证至 DSH 0.17.2」 | 徽章解码 + grep 双计数核过(见第三节) |
16
+ | 3 | **版本 0.6.0 发布准备** | 发布清单就绪(第五节);`package.json` version 当前仍为 0.5.1(`package.json:3`),0.6.0 bump 属编排待执行动作 | 未执行 version bump / pack / publish(如实声明) |
17
+
18
+ git 工作区快照(`git status --short` 实测):` M README.md`、` M WORKSPACE_INDEX.md`、` M package.json`、` A src/cli.ts`、` A src/init.ts`、` A test/init.test.ts`(intent-to-add 登记,与「git add -N」口径一致)、`?? lingshu-mcp-snippet.json`(见第六节第 6 条)。
19
+
20
+ ---
21
+
22
+ ## 二、init 命令设计与实现
23
+
24
+ ### 2.1 目标
25
+
26
+ 一键配置命令:`npx @furongjun1999/dsh-memory init`。新增 `src/init.ts`(lingshu-init CLI 主实现)、`src/cli.ts`(lingshu 主命令分发)、`test/init.test.ts`;`package.json` 补 bin 字段;README.md 快速开始节头部加「一键配置(推荐)」小节;WORKSPACE_INDEX.md 重生成。**未动** `src/index.ts`(DSH 插件入口)与 `src/lib/`(仅 import 复用)。未 git commit(编排会话负责;三个新文件已登记进 git 索引面)。
27
+
28
+ ### 2.2 实现要点(行号均经本会话读取核对)
29
+
30
+ **src/init.ts(新增,348 行)**:导出 `runInit(argv, opts)`(src/init.ts:239)、`buildSnippet`(src/init.ts:106)、`defaultMemoryRoot`(src/init.ts:101)。文件尾 `import.meta === pathToFileURL(resolve(process.argv[1]))` 判定直跑,win32 下做大小写兜底比对(src/init.ts:328-338)。
31
+
32
+ - **交互模式**(无参数/参数不全且 stdin TTY,src/init.ts:256-288):`node:readline/promises` 三问——①接入端 1=DSH 插件 2=Claude Code 3=Codex CLI 4=通用 MCP ②记忆根目录(缺省 `<home>/.lingshu/memory`,src/init.ts:101-103)③Python 解释器(缺省 `defaultPython()` 按平台 python/python3,src/lib/python_path.ts:42-49)。
33
+ - **非交互**:`--end/--root/--python` 齐即跳过问答;解析容忍 `--flag value` 与 `--flag=value`、跳过一个前导 `--` 分隔符(src/init.ts:216,221);非 TTY 缺参报错退出 1、点名缺失 flag(src/init.ts:259-266),不挂死。
34
+ - **产出**:①打印所选端 mcp.json 片段——`command`=解释器、`args=['-m','md_cg.mcp_server']`、env 含 `MDCG_ROOT`、`MDCG_PYTHON`、`PYTHONPATH`=包根(`repoRoot()`,src/init.ts:118)、`PYTHONIOENCODING`/`PYTHONUTF8` 编码硬约束、`MDCG_MCP_SURFACE=kernel`、`MDCG_ACTOR` 按端(src/init.ts:109-129)②写 `<cwd>/lingshu-mcp-snippet.json`(src/init.ts:297-298)③后续步骤指引(片段各端放哪、令牌签发 `python -m md_cg.tokens issue ...` 与 README「写入凭据」对应,src/init.ts:136-144、大脑自检、PYTHONPATH 换常驻仓库的提示 src/init.ts:312)。
35
+ - **边界**:不创建目录/不启动服务/不写库(src/init.ts:11-16 头注;输出尾行如实声明 src/init.ts:313);无时间戳类易变内容(幂等)。零运行时依赖:仅 `node:fs`/`node:os`/`node:readline/promises`/`node:path`/`node:url` + 包内 lib 模块(src/init.ts:29-36 import 清单)。
36
+ - **各端口径**:dsh 端诚实标注「DSH 无全局 mcp.json——推荐走插件形态;下方 JSON 为 MCP 直挂等价形态」(src/init.ts:58),并给出 cordis.yml 对应关系 `config.python` / `config.mdcg.root`(src/init.ts:152-154)。
37
+
38
+ **src/cli.ts(新增,66 行)**:`lingshu <cmd>` 分发——`'init'` → `runInit`(src/cli.ts:35-38);无/未知命令打印用法退出 1(src/cli.ts:39-43);导出 `main` 供测试(src/cli.ts:33)。头注说明三 bin 设计动机(src/cli.ts:5-10)。
39
+
40
+ **package.json**:`bin = { lingshu→./lib/cli.js, lingshu-init→./lib/init.js, dsh-memory→./lib/cli.js }`(package.json:8-12)。第三个 bin 是 npx 硬要求——包有多 bin 时 npx 只自动运行与包名末段(dsh-memory)同名的 bin,缺它则 `npx @furongjun1999/dsh-memory init` 直接报错(npm npx 多 bin 同名匹配规则)。`files` 数组已含 `lib/` 与 `md_cg/`(package.json:19-29),未改。git diff 实测 package.json 仅 +5 行(bin 块)。
41
+
42
+ **test/init.test.ts(新增,6 用例,src 行号 :41/:82/:102/:128/:151/:173)**:非交互四端端到端(各端 MDCG_ROOT/MDCG_PYTHON/command/args/actor/编码键逐条断言 + snippet 落盘可 JSON.parse 且与返回一致 + 输出含令牌指引 + 记忆根目录未被创建);幂等(同参重跑 stdout 逐字节一致 + 文件一致);交互 mock stdin(PassThrough)走通 DSH 分支与缺省分支;非 TTY 缺参报错点名缺失 flag。测试注释记录 readline 预缓冲坑的实测结论(test/init.test.ts:97-100)。
43
+
44
+ **README.md:48-66**:「### 一键配置(推荐)」置于「按宿主选择入口」引用块(README.md:68,即原手工三步)之前:npx 命令(:51)、三问与三项产出说明(:54-58)、非交互直跑形态(:63)、init 边界与幂等(:66)、「或继续阅读下方手工步骤」(:66)。
45
+
46
+ **WORKSPACE_INDEX.md**:`scripts/workspace_index.py --write` 重生成(`--check` 过,见第四节⑤⑥)。快照行实测:HEAD `d7c6e347` · 追踪 1998 件(WORKSPACE_INDEX.md:8),`src/` 14 件(WORKSPACE_INDEX.md:20)。
47
+
48
+ ### 2.3 前 / 后对比
49
+
50
+ - **前**:部署灵枢需手工多步——读 README 选流程、自己定记忆根目录、手写各端 mcp.json 的 env(README 各端样例里 PYTHONPATH 是 REPLACE_WITH_ABSOLUTE_PATH_TO_dsh-memory 占位符)、自行理解令牌签发;无任何 bin,包不可执行。
51
+ - **后**:一条命令三问即产出所选端可直接复制的 mcp.json 片段 + 落盘文件 + 后续步骤(含按端 actor 的令牌签发命令);参数齐可非交互直跑;重跑输出一致。DSH 端诚实标注「无全局 mcp.json,JSON 为直挂等价形态,推荐插件形态三步」并给出 cordis.yml 对应关系(config.python / config.mdcg.root)。
52
+
53
+ ### 2.4 语义注记(口径对齐,均已读取核对)
54
+
55
+ 1. 片段 env 口径与 `mdcgChildEnv()`(src/lib/mdcg_client.ts:115-129)对齐——`PYTHONIOENCODING`/`PYTHONUTF8` 是硬约束非可选(中文记忆场景,该函数头注 2026-09-20 读线程崩溃现场);`MDCG_MCP_SURFACE=kernel` 与既有直挂样例同口径——kernel 面无 `mdcg_*` 细粒度工具,直挂端写入走 `cg(op=write)`+凭据,与插件端「必须 full」的场景(src/lib/mdcg_client.ts:68-74:唯一写入通道是 `mdcg_remember`,kernel 面下写入静默失败)不同,**刻意不跟插件对齐**。
56
+ 2. 缺省记忆根 `~/.lingshu/memory` 是本命令的显式缺省并写进 MDCG_ROOT,与插件运行时缺省(`~/.dsh/.dsh-memory`,src/lib/datapath.ts stateRoot())不同源——片段显式指定即无歧义,两套默认各安其位。
57
+ 3. `PYTHONPATH=repoRoot()`(import.meta 向上找 package.json,src/lib/datapath.ts:41-56):npx 场景指向 npx 缓存中的包根(files 含 md_cg/ 故随包可用),输出含「建议换 git clone 常驻仓库路径(免疫 npx 缓存清理)」的诚实提示(src/init.ts:312)。
58
+ 4. `MDCG_PYTHON` 键对直挂端是文档性(解释器由 command 决定),对 DSH 插件端真实生效(`defaultPython()` 读 env 覆盖,src/lib/python_path.ts:46-47)——输出文案如实区分(src/init.ts:122)。
59
+ 5. readline 对「attach 前预缓冲且不 end」的 PassThrough 只消费第一行(隔离实验结论:首问 OK 次问 ERR_USE_AFTER_CLOSE/question 永久 pending),可靠形态是先启动 runInit 再逐问 write——该结论已写进测试注释(test/init.test.ts:97-100),后来者不必重踩。
60
+
61
+ ---
62
+
63
+ ## 三、README 更新(DSH 0.17.2 声明)
64
+
65
+ ### 3.1 改动(两处)
66
+
67
+ 1. **README.md:14 徽章行**:DSH 适配徽章 URL 由 `badge/DSH%20%E9%80%82%E9%85%8D-%3E%3D0.1.2--rc.1-4E9BF1` 换为 `badge/DSH%20%E9%80%82%E9%85%8D-0.17.2%20%E5%B7%B2%E9%AA%8C%E8%AF%81-4E9BF1`。
68
+ 2. **README.md:106 前置行**:「DSH 内核 ≥ 0.1.2-rc.1」后插入「· **已验证至 DSH 0.17.2**」。
69
+
70
+ - 前:`![DSH 适配](https://img.shields.io/badge/DSH%20%E9%80%82%E9%85%8D-%3E%3D0.1.2--rc.1-4E9BF1)` / 后:`![DSH 适配](https://img.shields.io/badge/DSH%20%E9%80%82%E9%85%8D-0.17.2%20%E5%B7%B2%E9%AA%8C%E8%AF%81-4E9BF1)`
71
+ - 前置行 前:`- **前置**:Node ≥ 22.19 · DSH 内核 ≥ 0.1.2-rc.1 · **大脑零安装**…` / 后:`- **前置**:Node ≥ 22.19 · DSH 内核 ≥ 0.1.2-rc.1 · **已验证至 DSH 0.17.2** · **大脑零安装**…`
72
+
73
+ ### 3.2 行号勘误与兼容性小节说明
74
+
75
+ - **行号勘误**:任务书写「:86 前置行」,实际前置行在 README.md:106(:86 现为 cordis.yml 示例的 `mdcg:` 配置行,本会话读取核实)——系工作区已有未提交的「一键配置」init 段(+20 行)导致行号漂移,已按文案「DSH 内核 ≥ 0.1.2-rc.1」精确定位修改。
76
+ - **兼容性/版本对照小节**:README 全文检索无独立小节(仅文档导航表指向外部历史文档),按任务书第 3 条未新建。
77
+ - 语义上「≥ 0.1.2-rc.1」的最低要求声明**保留不动**,仅追加「已验证至 0.17.2」的上界事实;徽章与前置行两处表述一致(徽章版本表述与正文一致性纪律)。
78
+ - **未触碰**:Release 历史段与 COGMAP 标记段——git diff 实测 README.md 全部 3 个 hunk 为:①`@@ -11,7` 徽章行 ②`@@ -45,6` 一键配置段(+20 行,会话开始前工作区已有的 init 功能未提交改动,非本任务所改)③`@@ -83,7` 前置行。两目标行各在其 hunk 内,无越界改动。
79
+
80
+ ### 3.3 本会话复验输出
81
+
82
+ - `node -e "console.log(decodeURIComponent('DSH%20%E9%80%82%E9%85%8D-0.17.2%20%E5%B7%B2%E9%AA%8C%E8%AF%81-4E9BF1'))"` → **`DSH 适配-0.17.2 已验证-4E9BF1`**——徽章文字与颜色逐一核对无误。
83
+ - `grep -n "0\.1\.2" README.md` → 仅 README.md:106 一行(最低要求项;旧徽章形态 `>=0.1.2--rc.1` 已消失);`grep -n "0\.17\.2" README.md` → 恰为 README.md:14 与 README.md:106 两处。
84
+ - `grep -c "COGMAP:BEGIN" README.md` → **1**,标记段完好。
85
+ - `python scripts/cogmap_sync.py check`(gate 内实跑)→「cogmap check: 通过(cg 36 op / stg 4 op / whitebox 6 action / mdcg_* 31 个…全部一致)」——gate 未因本次修改报错。
86
+ - **未做**:真机 DSH 0.17.2 配合验证(任务书声明用户已实测,本任务只做文档声明)。
87
+
88
+ ---
89
+
90
+ ## 四、验证(本会话全部实跑复验)
91
+
92
+ | # | 检查 | 命令 | 实测结果 | 判定 |
93
+ |---|---|---|---|---|
94
+ | ① | build | `npm run build` | exit=0;`ls` 确认 lib/init.js、lib/cli.js 生成;`head -1` 两文件均为 `#!/usr/bin/env node`(shebang 保留) | **通过** |
95
+ | ② | init 单测 | `node --import tsx --test --test-timeout=15000 test/init.test.ts` | tests 6 / pass 6 / fail 0(四端 E2E、幂等、交互 DSH 分支、交互缺省分支、非 TTY 缺参、缺省根口径) | **通过** |
96
+ | ③ | 全量测试 | `npm test` | exit 0;tests 59 = pass 58 + skipped 1 + fail 0(skip 为既有 python-utf8-mode 环境性用例「内层 locale 已是 utf-8,无从构造 gbk 现场」,与本次改动无关) | **通过** |
97
+ | ④ | init E2E 真跑 | 临时目录实跑(见下) | 编译产物直跑全链 | **通过** |
98
+ | ⑤ | gate | `npm run gate` | exit 0:check_unreachable VERDICT=PASS(FAIL 规则=0)/ cogmap check 通过 / link_check 228 OK·0 BROKEN / workspace_index --check [OK] 顶层 20·追踪 1998 / verify_discipline 结论 9/9 目标一致 | **通过** |
99
+ | ⑥ | 索引守卫单跑 | `python scripts/workspace_index.py --check` | `[OK ] 工作区索引与仓库一致(顶层目录 20 · 追踪 1998 件)`,exit 0 | **通过** |
100
+
101
+ **④ E2E 细节**(临时目录,测毕已清理,不触真实 `~/.lingshu` 与在役库):
102
+
103
+ - `node lib/init.js --end claude --root <tmp>/mem --python python3` → exit=0;stdout 含 MDCG_ROOT×2、`md_cg.tokens issue`、`README「写入凭据」`、「建议换成 git clone」提示;`lingshu-mcp-snippet.json` 落临时目录且 `JSON.parse` 通过(MDCG_ROOT 指向 `<tmp>/mem`、ACTOR=claude-code);`<tmp>/mem` **未创建**(test 判定)。
104
+ - 同参数重跑 → `diff run1.txt run2.txt` 为空(**stdout 幂等**)。
105
+ - `node lib/cli.js init -- --end dsh --root <tmp>/mem2 --python python` → exit=0(分发 + `--` 分隔符 + dsh 端 `MDCG_ACTOR: "dsh-memory"` 与令牌 actor dsh-memory 实测)。
106
+ - `node lib/cli.js`(无命令)→ 打印用法 exit=1;`node lib/cli.js badcmd` → exit=1。
107
+ - `node lib/init.js`(非 TTY 缺参)→ exit=1,stderr:`lingshu-init:非交互环境(stdin 非 TTY)必须给齐参数:--end --root --python。示例:…`。
108
+
109
+ **未验证(如实声明)**:
110
+
111
+ - a) `npx @furongjun1999/dsh-memory init` **联网安装全链路未跑**——需发布含 bin 的新版本后 npx 才可能从 registry 拉到;本会话验证的是其依赖的机制(bin 声明 + lib/cli.js、lib/init.js 直跑行为)与 npm npx 多 bin 同名匹配规则。
112
+ - b) **真实 TTY 下人工交互问答未跑**(本会话无交互终端),由单测 mock stdin 覆盖同一代码路径(DSH 分支 + 缺省分支,test/init.test.ts:102/:128)。
113
+ - c) `npm version 0.6.0` / `npm pack --dry-run` / `npm publish` **未执行**——属第五节编排待执行清单。
114
+
115
+ **另注(WORKSPACE_INDEX 陈化)**:索引重生成把此前的陈化欠账一并刷新(快照 HEAD `2e9bfe8`→`d7c6e347`、追踪 1828→1998 件,src/ 12→14 中本次贡献 2 件)——该陈化在本次改动前已存在(此前提交未重生成索引),不回滚(回滚会使 gate 的索引守卫在当前树上亮红)。
116
+
117
+ ---
118
+
119
+ ## 五、发布清单(编排待执行)
120
+
121
+ 以下动作**本会话未执行**,按序待编排/使用者完成:
122
+
123
+ 1. `npm version 0.6.0`——当前 version 仍为 0.5.1(package.json:3 实读)。
124
+ 2. `npm pack --dry-run`——核对 files 集合(lib/、md_cg/、skills/ 等,package.json:19-29)与 bin 三键(package.json:8-12)入包形态。
125
+ 3. `npm publish`——需使用者终端凭据;`prepublishOnly` 钩子(package.json:88)将自动串联 `npm run build && npm test && npm run gate`(三者本会话均已预验通过)。
126
+ 4. 发布后补验:`npx @furongjun1999/dsh-memory init`(清 npx 缓存后)——registry 拉到含 bin 版本后的一键配置全链路终验。
127
+ 5. git commit 由编排会话统一负责(本会话未 commit,遵守纪律)。
128
+
129
+ ---
130
+
131
+ ## 六、纪律合规声明
132
+
133
+ 1. **零运行时依赖**:init/cli 仅 import `node:fs`/`node:os`/`node:readline/promises`/`node:path`/`node:url` + 包内 lib 模块(src/init.ts:29-36、src/cli.ts:14-16 实读),未加 prompts/inquirer 等第三方依赖。
134
+ 2. **实验只在临时目录**:E2E 全部落 `mktemp -d` 临时目录并以 `--root` 指向临时路径,未触真实 `~/.lingshu`、未触在役库;测毕 `rm -rf` 并复核不存在。单测同口径(test/init.test.ts:14-15 头注「不 spawn python、不触碰 ~/.lingshu、不触真实记忆库」)。
135
+ 3. **不写本机绝对路径**:本报告路径一律仓库相对形态(临时目录以 `<tmp>` 代指)。
136
+ 4. **未 git commit**:工作区保持编排会话负责提交的形态。
137
+ 5. **诚实报告**:第四节列出全部未验证项(npx 联网链路/真 TTY 交互/version+pack+publish/真机 DSH 0.17.2 配合验证),无一项填充为「已通过」。
138
+ 6. **现场遗留说明**:仓库根有一份前会话遗留的未跟踪 `lingshu-mcp-snippet.json`(MDCG_ROOT 指向 `.lingshu-init-e2e-root`,该目录确认不存在)。本会话复验时曾因在仓库根直跑 init 被 `process.cwd()` 语义覆盖此文件(init 按设计把片段写到当前目录),已当即用同参数重跑恢复(恢复后 MDCG_ROOT 实读核对为 `.lingshu-init-e2e-root`)。该文件属前会话 E2E 遗留产物、未跟踪,是否清理由编排会话决定——**注意**:其 MDCG_ROOT 指向的仓库内路径不可作为真实记忆根使用(勿把记忆写进插件包目录,README cordis.yml 示例同此警示)。
139
+
140
+ ---
141
+
142
+ ## 附:自查双清单
143
+
144
+ ### 清单一:已验证清单(本会话实跑,全部有命令+输出)
145
+
146
+ | 检查 | 命令/方式 | 结果 |
147
+ |---|---|---|
148
+ | 构建 | `npm run build` | exit 0;lib/init.js + lib/cli.js 生成,shebang 保留 |
149
+ | init 单测 | `node --import tsx --test --test-timeout=15000 test/init.test.ts` | 6/6 pass,0 fail |
150
+ | 全量测试 | `npm test` | exit 0;59 = 58 pass + 1 skip(既有环境性)+ 0 fail |
151
+ | E2E 非交互 | `node lib/init.js --end claude --root <tmp>/mem --python python3`(临时目录) | exit 0;片段+落盘+JSON 可解析+记忆根未创建 |
152
+ | E2E 幂等 | 同参重跑 `diff run1.txt run2.txt` | 空(逐字节一致) |
153
+ | E2E 分发 | `node lib/cli.js init -- --end dsh --root <tmp>/mem2 --python python` | exit 0;端=DSH 插件;actor=dsh-memory |
154
+ | E2E 用法/错误 | `node lib/cli.js`、`badcmd`、`node lib/init.js`(非 TTY 缺参) | 各 exit 1;报错点名 --end --root --python |
155
+ | gate | `npm run gate` | exit 0;五守卫全绿 |
156
+ | 索引守卫 | `python scripts/workspace_index.py --check` | [OK] exit 0 |
157
+ | 徽章文字 | `node -e decodeURIComponent(...)` | 「DSH 适配-0.17.2 已验证-4E9BF1」 |
158
+ | README 计数 | `grep -n "0\.1\.2"` / `"0\.17\.2"` / `grep -c "COGMAP:BEGIN"` | 仅 :106 最低要求项 / 恰 :14+:106 两处 / 标记段=1 |
159
+ | diff 范围 | `git diff README.md` hunk 清点 | 3 hunks:徽章 / 一键配置段(会话前已有)/ 前置行 |
160
+ | 引用行号 | 读取 src/init.ts、src/cli.ts、test/init.test.ts、package.json、README、src/lib 三件 | 全部核对一致 |
161
+
162
+ ### 清单二:未验证与欠账清单(如实声明,编排可循)
163
+
164
+ | 项 | 状态 | 原因 / 后续 |
165
+ |---|---|---|
166
+ | `npx @furongjun1999/dsh-memory init` 联网全链路 | 未跑 | registry 需发布含 bin 版本;发布后按第五节 4 补验 |
167
+ | 真实 TTY 人工交互问答 | 未跑 | 本会话无交互终端;单测 mock stdin 覆盖同一代码路径 |
168
+ | `npm version 0.6.0` / `npm pack --dry-run` / `npm publish` | 未执行 | 编排待执行;publish 需使用者终端凭据;当前 version=0.5.1 |
169
+ | 真机 DSH 0.17.2 配合验证 | 未跑 | 任务书声明用户已实测,本次仅文档声明 |
170
+ | python-utf8-mode 1 例 skip | 维持 skip | 既有环境性用例(内层 locale 已是 utf-8),与本次改动无关 |
171
+ | 仓库根遗留 `lingshu-mcp-snippet.json` | 待编排决定 | 前会话 E2E 遗留、未跟踪;本会话已恢复原内容;MDCG_ROOT 指向路径不可作真实记忆根 |