@a9i5k4/dsh-auto-memory 3.0.1 → 3.1.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 (124) hide show
  1. package/README.md +13 -8
  2. package/README.zh-CN.md +13 -8
  3. package/docs/HANDBOOK.md +88 -52
  4. package/docs/USER-GUIDE.en.md +9 -9
  5. package/docs/USER-GUIDE.zh-CN.md +9 -9
  6. package/docs/screenshots/promo/promo-0-banner-v4.png +0 -0
  7. package/docs/screenshots/promo/promo-1b-auto-recall.png +0 -0
  8. package/lib/activation-host.js +6 -1
  9. package/lib/client.js +819 -272
  10. package/lib/context-host.js +7 -1
  11. package/lib/episodic-store.js +90 -16
  12. package/lib/fact-store.js +463 -41
  13. package/lib/hub-io.js +217 -0
  14. package/lib/index.js +224 -45
  15. package/lib/intent-clean-safe.js +1 -1
  16. package/lib/memory-hub.js +37 -5
  17. package/lib/note-status.js +9 -1
  18. package/lib/procedure-store.js +252 -31
  19. package/lib/procedure-switch.js +38 -0
  20. package/lib/python-sidecar-client.js +285 -8
  21. package/package.json +6 -2
  22. package/docs/internal/ACCEPT-35-LIVE.md +0 -143
  23. package/docs/internal/ACCEPTANCE-20260914.md +0 -90
  24. package/docs/internal/ARCH-REVIEW-BRIEF.md +0 -411
  25. package/docs/internal/ARCH-REVIEW-REQUEST.md +0 -201
  26. package/docs/internal/ARCH-REVIEW-ROUND2.md +0 -169
  27. package/docs/internal/ARCH-REVIEW-ROUND3.md +0 -206
  28. package/docs/internal/ARCHITECTURE-FOR-ZCODE-20260920.md +0 -397
  29. package/docs/internal/ART-DIRECTION-DEEPSEEK-20260920.md +0 -351
  30. package/docs/internal/ART-DIRECTION-WIREFRAME.md +0 -191
  31. package/docs/internal/ART-DIRECTION-WIREFRAME.md.bak-superseded +0 -181
  32. package/docs/internal/AUDIT-WB-GRAPH-FULL-20260916.md +0 -314
  33. package/docs/internal/BATTLE-PLAN-20260917.md +0 -871
  34. package/docs/internal/CONCURRENCY-INVESTIGATION-20260917.md +0 -192
  35. package/docs/internal/CROSS-SESSION-SEARCH-PATH-DECISION.md +0 -72
  36. package/docs/internal/CROSS-SESSION-SEARCH-RESEARCH.md +0 -131
  37. package/docs/internal/CUA-VISION-FIX-NOTES.md +0 -78
  38. package/docs/internal/DECISIONS-20260914-SESSION.md +0 -269
  39. package/docs/internal/DESIGN-OVERHAUL-PRE-RESEARCH.md +0 -292
  40. package/docs/internal/DESIGN-P1-STATE-COMMIT-20260915.md +0 -219
  41. package/docs/internal/DIRECTION-CHECK-WB-GRAPH-20260916.md +0 -132
  42. package/docs/internal/FEATURE-INVENTORY.md +0 -531
  43. package/docs/internal/FEEDBACK-TO-DSHAPI-RELAY.md +0 -13
  44. package/docs/internal/G-SERIES-EXECUTION-20260917.md +0 -248
  45. package/docs/internal/G3-DESIGN-20260918.md +0 -82
  46. package/docs/internal/G3-DISK-FORMAT-GAP-20260919.md +0 -92
  47. package/docs/internal/GH-DISCUSSION-5732-COMMENT.md +0 -74
  48. package/docs/internal/GPT-ACCEPTANCE-PROMPT-20260916.md +0 -352
  49. package/docs/internal/GPT-REVIEW-PROMPT.md +0 -216
  50. package/docs/internal/GROUP-DIGEST-SETUP.md +0 -62
  51. package/docs/internal/GROUP-LISTENER-SETUP.md +0 -49
  52. package/docs/internal/GROUP-WEBHOOK-SETUP.md +0 -93
  53. package/docs/internal/HANDOFF-TO-ZCODE-20260920.md +0 -309
  54. package/docs/internal/HANDOFF-TO-ZCODE.md +0 -168
  55. package/docs/internal/HERMES-DATA-VERIFICATION-20260919.md +0 -120
  56. package/docs/internal/HERMES-LEGACY-STATUS-20260919.md +0 -74
  57. package/docs/internal/ISSUE-55-58-VERIFICATION-20260918.md +0 -175
  58. package/docs/internal/ISSUE10-FIX-EXECUTION-20260919.md +0 -389
  59. package/docs/internal/ISSUE10-PLAN-20260919.md +0 -254
  60. package/docs/internal/ISSUE10B-FORENSICS-20260919.md +0 -468
  61. package/docs/internal/ISSUE9-PURGE-AND-R1-PLAIN-20260919.md +0 -150
  62. package/docs/internal/ISSUE9-RESIDUAL-FORENSICS-20260919.md +0 -114
  63. package/docs/internal/KICKOFF-P0.md +0 -254
  64. package/docs/internal/LESSON-TO-CANDIDATE-STATUS-20260919.md +0 -79
  65. package/docs/internal/MASTER-PLAN-3.0.md +0 -411
  66. package/docs/internal/MEMORY-GOVERNANCE-20260917.md +0 -309
  67. package/docs/internal/MEMORY-MUTATION-AND-INDEX-DESIGN.md +0 -85
  68. package/docs/internal/MERGE-CONFLICT-SCAN-20260914.md +0 -222
  69. package/docs/internal/NEXT-VERSION-TODO.md +0 -95
  70. package/docs/internal/OFFICIAL-DISCUSSION-DRAFT.md +0 -80
  71. package/docs/internal/PENDING-FIXES-20260916.md +0 -289
  72. package/docs/internal/PRE-FRONTEND-CHECKLIST-20260919.md +0 -705
  73. package/docs/internal/PRE-FRONTEND-CHECKLIST-20260919.md.bak-s10 +0 -649
  74. package/docs/internal/PROCEDURAL-MEMORY-AND-APPROVAL-DESIGN-20260918.md +0 -225
  75. package/docs/internal/PROGRESS-20260917.md +0 -93
  76. package/docs/internal/PROMPT-GAP-AUDIT-20260920.md +0 -128
  77. package/docs/internal/R1-DEGRADE-AUDIT-20260918.md +0 -163
  78. package/docs/internal/R1-READABILITY-FORENSICS-20260919.md +0 -127
  79. package/docs/internal/R2-EVIDENCE-DEEP-AUDIT-20260918.md +0 -140
  80. package/docs/internal/R3-DEGRADE-LEDGER-DESIGN-20260918.md +0 -138
  81. package/docs/internal/R4-RECALL-QUOTA-PLAN-20260918.md +0 -218
  82. package/docs/internal/RAG-KARPATHY-PROGRAM.md +0 -229
  83. package/docs/internal/RELEASE-PROCESS.md +0 -99
  84. package/docs/internal/REPORT-P0-NIGHTLY.md +0 -212
  85. package/docs/internal/REPORT-P5-ACCEPTANCE.md +0 -31
  86. package/docs/internal/REPORT-WB-GRAPH-NIGHTLY.md +0 -153
  87. package/docs/internal/RESUME-20260918.md +0 -171
  88. package/docs/internal/RESUME-20260919.md +0 -104
  89. package/docs/internal/REVIEW-WB-GRAPH-SELF.md +0 -81
  90. package/docs/internal/RHINELAB-TO-DEEPSEEK-FEASIBILITY.md +0 -198
  91. package/docs/internal/ROADMAP-20260917-WEEK.md +0 -439
  92. package/docs/internal/ROADMAP.md +0 -106
  93. package/docs/internal/RUN-P0-NIGHTLY.md +0 -227
  94. package/docs/internal/S10-CONSTRUCTION-HANDOFF-20260917.md +0 -185
  95. package/docs/internal/S10-GAP-INVENTORY-20260917.md +0 -239
  96. package/docs/internal/S10-GAPS-PLAIN-20260917.md +0 -125
  97. package/docs/internal/SEMANTIC-ARCHITECTURE-SPEC.md +0 -360
  98. package/docs/internal/SESSION-FILE-REPAIR-PROTOCOL.md +0 -90
  99. package/docs/internal/SUBAGENT-REPORT-ROUTING-PRE-RESEARCH.md +0 -261
  100. package/docs/internal/T6-EXECUTION-20260920.md +0 -130
  101. package/docs/internal/TELEMETRY-EFFECT-REPORT-DESIGN-20260918.md +0 -146
  102. package/docs/internal/THESIS-GAP-ANALYSIS-20260918.md +0 -89
  103. package/docs/internal/THESIS-OUTLINE-20260918.md +0 -147
  104. package/docs/internal/THREE-LAYER-CONTRACT.md +0 -219
  105. package/docs/internal/TODO-BACKLOG.md +0 -263
  106. package/docs/internal/TODO-GRAPH.html +0 -715
  107. package/docs/internal/TODO-GRAPH.html.bak-20260914-v2 +0 -493
  108. package/docs/internal/TODO-GRAPH.html.bak-20260915-alsfix +0 -710
  109. package/docs/internal/TODO-GRAPH.html.bak-20260915-p1 +0 -710
  110. package/docs/internal/TODO-GRAPH.html.bak-20260915-p6a-rev +0 -703
  111. package/docs/internal/TODO-GRAPH.html.bak-20260915-wshint +0 -710
  112. package/docs/internal/TODO-GRAPH.html.bak-20260916-batch +0 -715
  113. package/docs/internal/UPSTREAM-ISSUE-PR-TRIAGE-20260919.md +0 -297
  114. package/docs/internal/UPSTREAM-ISSUES-3RD-AUDIT-20260920.md +0 -104
  115. package/docs/internal/WB-FORMAT-CONVENTION.md +0 -112
  116. package/docs/internal/WB-GRAPH-DECISIONS-20260914.md +0 -71
  117. package/docs/internal/WB-GRAPH-INTEGRATION-PLAN.md +0 -386
  118. package/docs/internal/WB-GRAPH-RESEARCH-BRIEF.md +0 -118
  119. package/docs/internal/WB-GRAPH-RESEARCH-EXTERNAL.md +0 -228
  120. package/docs/internal/WB-GRAPH-RESEARCH-LOCAL.md +0 -190
  121. package/docs/internal/reviews/CLAIM-VERIFICATION-20260914.md +0 -56
  122. package/docs/internal/reviews/PLAN-gpt6astra-round2-20260914.md +0 -787
  123. package/docs/internal/reviews/REVIEW-gpt6astra-20260914.md +0 -112
  124. package/docs/internal/reviews/ROUND3-REVIEW-INTEGRATION-20260914.md +0 -230
@@ -1,228 +0,0 @@
1
- # R2 外部资产调研:dsh-graph 与 MRAgent(只借鉴范式,不搬代码)
2
-
3
- > 预研子代理 R2 产出 · 2026-09-13。配套任务书:`WB-GRAPH-RESEARCH-BRIEF.md`。
4
- > 本文所有 dsh-graph 结论均附仓库内文件路径(:行号)证据;MRAgent 附 文件:行号 或论文段落。
5
-
6
- ---
7
-
8
- ## 获取方式与诚实声明
9
-
10
- | 资产 | 获取方式 | 结果 | 缺口 |
11
- | --- | --- | --- | --- |
12
- | dsh-graph | `git clone --depth 50 https://github.com/miuzel/dsh-graph.git` 到临时目录 | 成功。`main` 分支,commit `6809942a9a8efa5a90b82bb90ebaeedd2cbce275`(2026-09-12),tag `v0.10.0`。含完整 TS 源码(`core/`)、编译产物(`dsh-graph-host/`)、`DESIGN.md`、`schema/SCHEMA.md`、60+ 测试文件 | 仓库根目录**无 LICENSE 文件**(MIT 许可文本在 `dsh-graph-host/LICENSE`,随 npm 包分发);`package.json` 无 license 字段,host 包 package.json 有 `"license": "MIT"` |
13
- | MRAgent | `git clone --depth 50 https://github.com/Ji-shuo/MRAgent.git` 到临时目录 | 成功。`main` 分支,commit `7441506db984b7c4da32e8dbeb2527f2e351270a`(2026-06-08)。32 个文件,Python 源码齐全,`data/dataset_LM.json` 经 Git LFS 正常拉取 | **仓库完全无 LICENSE 文件**,README 亦无任何许可声明 |
14
- | 论文 arXiv:2606.06036 | WebFetch 摘要页 `https://arxiv.org/abs/2606.06036` + 全文 HTML `https://arxiv.org/html/2606.06036v1` | 成功。标题 *"Memory is Reconstructed, Not Retrieved: Graph Memory for LLM Agents"*,作者 Shuo Ji, Yibo Li, Bryan Hooi,提交 2026-06-04,Comments: **"Accepted at ICML 2026"**,cs.AI/cs.IR。全文(公式、算法、成本表)均可取,无需降级 | 论文为 v1 快照,与代码 commit(6-08)可能存在细微滞后;下文「与任务书描述的出入」第 6 条记录了论文与代码的一处命名不一致 |
15
-
16
- 两仓库均已完整克隆、逐文件核读;本报告未使用任何推测性内容。临时克隆目录位于 `C:\Users\JH Z\AppData\Local\Temp\wb-research\`(未触碰 dsh-auto-memory 仓库)。
17
-
18
- ---
19
-
20
- ## dsh-graph 范式提炼
21
-
22
- dsh-graph 是 DeepSeek Harness 的"目标看板"插件(npm 包 `dsh-graph` v0.10.0),把工作组织成 Backlog → Version → Goal 三层图。设计源流是工业应急 SOP 图模型(`DESIGN.md:5-17`)。核心哲学:**"结构化的是生命周期与求值语义,非结构化的是内容"**(`DESIGN.md:22`)。
23
-
24
- ### 1. 判据机制:"判据先于执行"
25
-
26
- **判据定义——不是 JSON schema,是 Markdown 小节 + 纯文本行。** 判据存放在 goal.md 正文的 `## 质量判据` 小节中,每条是一行编号文本,两种形式可混合:自然语言判据(review 执行者对照产出判断)与检查脚本判据(`[script] scripts/check_xxx.sh` 约定)(`DESIGN.md:119-122`;`schema/SCHEMA.md:87-91`)。代码里"判据"的解析是按行过滤:`criteriaItems()` 取小节内非空、非 HTML 注释、非模板占位行(`core/model.ts:146-153`;占位符集合在 `core/model.ts:134-138`)。没有 criterion_type / evidence_required / confidence_level 之类的结构化字段——任务书 Q1 的猜想 schema 在 dsh-graph 中**不存在对应物**。
27
-
28
- **判据如何校验——三条硬性门槛 + 一条软性确认路径。** 引擎在状态迁移时强制(`core/machine.ts:80-90`):进入 `in_progress` 必须同时满足 ①`meta.rules_snapshot` 已记录(规则库版本快照);②`## 质量判据` 小节非空(`criteriaPresent`);③事件流中存在该目标的 `criteria.confirmed` 事件。`transition()` 在执行迁移前从事件流重放检查 `criteria.confirmed` 是否存在(`core/ops.ts:1770-1780`)。登记判据走 `setCriteria()`:覆盖写判据小节、快照规则库版本、追加 `criteria.confirmed` 事件(`core/ops.ts:1456-1486`)。
29
-
30
- **登记与编辑是两条不同信任级别的路径(值得借鉴的细节):**
31
- - `setCriteria`(agent 登记):写 `criteria.confirmed`,触发执行门槛(`core/ops.ts:1477-1485`);
32
- - `updateCriteria`(GUI 人工编辑,g-170):**始终只写 `criteria.updated`,"绝不自动记录 criteria.confirmed,不触碰 rules_snapshot"**(`core/ops.ts:1508-1509, 1569`)——人工改判据不等于确认判据,确认必须走显式路径;
33
- - `updateCriteria` 还做乐观并发控制:`base_items` 与服务器当前判据不一致即抛 `GraphConflictError`(REST 409),force 覆盖时把 `conflicted=true` 记入事件可审计(`core/ops.ts:1536-1552`);空判据列表在 `in_progress/review/delivered` 状态下拒绝清空(D3,`core/ops.ts:1532-1535`)。
34
-
35
- **不通过时的行为——拒绝 + 零副作用,不降级不等待。** `assertTransition` 抛 `GraphError` 使迁移失败(`core/machine.ts:46-91`);`graph_start_attempt` 工具描述明确"派发前先执行准入门禁(backlog/draft/blocked/delivered 及无判据/未确认判据/状态不允许的目标直接拒绝,零副作用:不建 attempt、不启动子代理)"(`dsh-graph-host/index.js:1521` 附近)。GUI 拖动是唯一旁路:`force=true` 跳过 in_progress 门槛,语义是"人工拖动视为授权"(`core/machine.ts:41-43`;`core/machine.ts:26-27` 注释)。
36
-
37
- **判据的公平性用途(PK 模式):** N 路并行 attempt 对同一份登记判据独立核验,"判据先于执行是 PK 公平性的保证"(`DESIGN.md:97-102`)。
38
-
39
- ### 2. 状态机
40
-
41
- **真实阶段名与任务书一致:`draft → planning → collecting → ready → in_progress → review → delivered`,任意阶段可入 `blocked`**(`core/machine.ts:10-19`)。但实际迁移图远比线性链复杂(`core/machine.ts:24-33`):
42
-
43
- ```
44
- draft → planning, blocked
45
- planning → collecting, ready, blocked, in_progress # planning→ready 无收集需求直达;planning→in_progress 直接派发
46
- collecting → ready, planning, blocked, in_progress # collecting→in_progress 跳过 ready(人工拖动视为授权)
47
- ready → in_progress, collecting, blocked
48
- in_progress → review, blocked, collecting # →collecting 中断回退重新收集
49
- review → delivered, in_progress, blocked # →in_progress 打回
50
- delivered → review # 交付后仍可回 review 补充/修 bug
51
- blocked → (只能回 blocked_from)
52
- ```
53
-
54
- **迁移条件(每条边不是都设防,只在关键门口设防):**
55
- - 唯一硬判据门是 `ready/collecting/planning → in_progress`:rules_snapshot + 判据非空 + criteria.confirmed 事件三者齐备(`core/machine.ts:80-90`)。其余阶段迁移无判据要求;
56
- - 进 `blocked` 必须给 `reason`(`core/machine.ts:74-78`),且解除只能回 `blocked_from` 原状态(`core/machine.ts:64-69`,`blocked_from` 写入在 `core/ops.ts:1781-1784`);
57
- - backlog 中的草稿目标禁止阶段迁移,须先排期(`core/ops.ts:1766-1768`);
58
- - 完成声明 ≠ 完成:"任何执行者都只是发起完成声明,触发 review。工作没实际做,review 自然不通过。**状态不是证据,产出物才是**"(`DESIGN.md:104`)。
59
-
60
- **迁移由谁触发:模型/人发起,代码校验强制。** 迁移入口是 `graph_transition` 工具(模型调用)或看板 REST 端点(人工拖动)——两者都汇入核心层 `transition()` → `assertTransition()`,核心层不依赖模型自觉(`dsh-graph-host/index.js:1130-1135` 工具定义:"状态机与不变式由核心层强制";`core/ops.ts:1759-1797` 实现)。状态推进由事件流重放可重建:`goal.created→draft/planning、goal.transition→details.to`(`core/events.ts:163-194`)。
61
-
62
- **回边语义(打回不清空):** 打回时受影响的证据标记 `stale`(freshness 概念),未受影响的证据保留(`DESIGN.md:161`)。review 结论按预登记处置分支路由(完全实现/部分实现/遗留问题/未实现),"不是执行者临场决定"(`DESIGN.md:152-159`)。
63
-
64
- ### 3. 数据存储
65
-
66
- `.dsh-graph/` 目录布局(`schema/SCHEMA.md:12-40`;骨架创建在 `core/ops.ts:234-252` `init()`):
67
-
68
- ```
69
- .dsh-graph/
70
- ├── project.yaml # 项目配置(supervisor 自动化边界等)
71
- ├── rules.md # 整体工作规则库(frontmatter 带 version)
72
- ├── backlog/<id>.md # 草稿目标
73
- ├── goals/<id>/goal.md + attempts/ # 独立目标
74
- ├── versions/<slug>/version.md + goals/<id>/goal.md + attempts/<att>/attempt.md + delivery/
75
- ├── memory/long-term/<slug>.md # 长期记忆条目(每条带 source_goal 引用)
76
- ├── memory/memory.jsonl # 结构化记忆事件流(含文件锁,core/events.ts:215-253)
77
- ├── shared-cards/ attachments/
78
- ├── events.jsonl # ★ 全系统唯一事实源(append-only)
79
- └── index.json # 派生缓存,可重建
80
- ```
81
-
82
- **文件格式三件套**(`schema/SCHEMA.md:3-7`):
83
- - 叙事内容:**Markdown + JSON frontmatter**(frontmatter 用 JSON——YAML 子集,零依赖可直接 `JSON.parse`,`core/model.ts:1-3, 45-83`)。goal.md 的 meta 受引擎管(status/version/depends_on…),正文小节里引擎只管 `## 质量判据`、`## 证据台账` 等受管小节,其余人/LLM 自由编辑(`schema/SCHEMA.md:48`);
84
- - 运行履历:**JSONL 事件流**。事件结构 `{ts, actor, event, goal?, details}`(`core/events.ts:8-14`),`appendFileSync` 单行追加(`core/events.ts:46-62`)。事件类型全集约 35 种(`schema/SCHEMA.md:284-292`);
85
- - 派生索引:JSON(`index.json`、看板缓存)。
86
-
87
- **事件流是真相源,文件是投影:** "任何状态迁移必须伴随事件;events.jsonl 是唯一真相源,goal.md 的 frontmatter 状态可视为事件流的物化投影,允许从事件流重建"(`schema/SCHEMA.md:298-299`)。`graph_rebuild` 工具即从事件流重建状态并与 frontmatter 对账输出 drift(`dsh-graph-host/index.js:1385-1390`);重放逻辑在 `core/events.ts:92-194`(版本泳道 + 目标状态两个 replay 函数)。
88
-
89
- **写路径安全:** 一律原子写(`atomicWrite`,temp+rename),多写者叠加文件锁;`withTx` 事务模板"锁保护下读-改-写,事件先行"(`core/ops.ts:285-333` 注释及实现);graph root 拒绝符号链接、限制路径逃逸(`core/ops.ts:102-118`;`core/root.ts:68-75`)。
90
-
91
- **graph root 解析:** 默认 `<workspace>/.dsh-graph`,跟随会话 cwd 而非服务进程 cwd;git linked worktree 会归一到主工作树的 canonical root(`core/root.ts:220-300`)。
92
-
93
- ### 4. graph_handoff 交接
94
-
95
- `generateHandoff()`(`core/ops.ts:339-443`)——任务书所述"三元结构"确认存在,实为四段组装,全部从磁盘数据生成、**不依赖会话上下文**(不读 session):
96
-
97
- 1. **board 投影**:`boardProjection(root)` 产出目标看板,交接文档按"版本泳道 → 独立目标 → backlog"罗列每个目标一行(id、title、status、blocked_reason、最新 status_line),随后再按"进行中(下一步就干)→ 已交付 → 阻塞"重排一份行动视角摘要(`core/ops.ts:358-395`);
98
- 2. **关键环境事实(固定段)**:硬编码的项目环境事实列表(executor 注册名、root 覆盖约定、冻结脚本规则等五条,`core/ops.ts:396-404`)——注意这是**该仓库自身 dogfood 的固定文案**,不是可配置的环境事实 API;
99
- 3. **长期记忆**:可选 `query` 参数触发 `recallMemory` 关键词检索,输出结构化记忆(`memory/memory.jsonl` 的条目,带 kind/importance/source_goal 标注,经 `safeMemory` 清洗控制字符/伪指令,4000 字符截断),另附 `memory/long-term/` 下文件清单(`core/ops.ts:406-439`);
100
- 4. 落盘与归档:写 `<root>/HANDOFF.md`,旧版内容不同则先归档到 `handoffs/HANDOFF-<ts>.md`(`core/ops.ts:448-458`)。
101
-
102
- 配套 `claimSupervisor()`:新会话把 `project.yaml` 的 supervisor.session 换成自己的 sessionId、记 `supervisor.claimed` 事件、返回 HANDOFF 全文(`core/ops.ts:476-492`;`DESIGN.md:21`)。设计意图:交接文档是"新会话的启动上下文",职责指南以 skill 形式另行注入(`core/ops.ts:357`)。
103
-
104
- ### 5. 图遍历工具
105
-
106
- dsh-graph **没有通用的"图遍历"工具**——它的"图查询"是一组面向实体的 CRUD + 校验工具,39 个 `graph_*` 工具按功能分组(`README.md:44-63`;工具定义在 `dsh-graph-host/index.js`)。与"查询/遍历/对账"最相关的接口:
107
-
108
- | 工具 | 输入 → 输出 | 实现要点 |
109
- | --- | --- | --- |
110
- | `graph_validate` | 无参 → `problems: string[]` | 全量不变式校验:状态合法性、文件位置与 version 字段一致性、判据(脚本路径必须在 scope 外)、依赖环(`depends_on` DFS)、卡片引用(`dsh-graph-host/index.js:1376-1383`;实现 `core/ops.ts:1816-1970`:`cycleProblems` DFS 环检测 + `locationProblems` 位置一致性) |
111
- | `graph_rebuild` | 无参 → `drift: string[]` | 从 events.jsonl 重放各目标状态,与 frontmatter 对账(`dsh-graph-host/index.js:1384-1390`;`core/events.ts:163-194`) |
112
- | `graph_memory_recall` | `query, kind?, limit?` → `{total, matches}` | 按关键词/类型检索持久记忆条目(`dsh-graph-host/index.js:1505-1522`) |
113
- | `graph_transition` | `goal, to, reason?` → `{ok}` | 唯一的状态迁移入口,核心层强制校验(`dsh-graph-host/index.js:1130-1135`) |
114
- | `graph_move_goal` | 排期移动 | 移动文件即改变归属(backlog ↔ goals ↔ versions),"git 历史天然记录全部排期变迁"(`schema/SCHEMA.md:44-47`) |
115
-
116
- 依赖边不是手工声明的图 API:目标 B 声明"需要 A 的产出"时边 A→B 自然诞生,门控语义是"逐规则部分匹配"——B 只等它实际声明的 A 的结论(`DESIGN.md:86-91`)。
117
-
118
- ### 可迁移范式清单(→ 单对话白板场景)
119
-
120
- **① 判据集 schema 结构。** dsh-graph 的判据 = "命名小节内的一行文本列表 + 确认事件"。可迁移的最小范式:(a) 判据是**写操作的前置门槛**而非事后检查——白板场景即"交接笔记写入前,state/goals/dead ends/progress 四部分各自有非空且非占位的判据行";(b) **登记与确认分离**——模型生成判据只产生 `updated` 事件,`confirmed` 必须由显式动作产生,这防止"自评自确认";(c) 判据条目天然支持两种形态:自然语言(LLM review 判定)+ 脚本(确定性判定),白板场景可以先用"自然语言判据 + LLM 自检一次"的轻量版。简化方向:不需要 rules_snapshot / 乐观并发 token / PK 公平性那一整套——单对话单写者,保留"小节非空 + 占位符过滤 + 确认事件"三点即可;占位符过滤(`CRITERIA_PLACEHOLDERS`)是防"形式主义判据"的关键小技巧,值得照搬。
121
-
122
- **② 状态迁移强制逻辑。** 迁移范式 = "枚举合法边表 + 迁移函数内联校验 + 每次迁移必须伴随事件"。白板场景若引入状态机(如 `draft → filled → reviewed → handed_off`),简化要点:(a) dsh-graph 只有 in_progress 一个判据门——白板同理只在"写入/交付"一个门上设防,其余边放开;(b) blocked 的 `blocked_from` 记忆原状态的模式可复用于白板的"暂停/恢复";(c) 迁移触发者是模型但校验者是代码(`assertTransition` 抛错拒绝),模型永远不能直接改状态字段。更进一步:单对话白板可能连显式状态机都不需要,只需要"事件行 + 从事件重放当前状态"这一半范式(append-only JSONL 为真相源,Markdown 文件为投影,可 rebuild 对账)——这是比状态机更低成本、对前缀缓存更友好的部分。
123
-
124
- **③ 图存储持久化方式。** 范式 = "Markdown(人读) + JSONL(机读真相源) + 派生索引(可丢弃)"三层分工,git 友好。白板场景简化:dsh-graph 为多目标/多版本/多写者设计了目录树 + 文件锁 + 原子写 + canonical root;单对话白板是单写者,可以砍掉锁与 canonical root,保留 (a) 每次写先追加一行事件再更新投影文件;(b) 投影文件允许损坏——可从事件流重建;(c) 事件结构固定为 `{ts, actor, event, target, details}` 五字段。若白板后续接入 Cue–Tag–Content 图(见下),事件流可以承载全部图变更(node.added/edge.added…),使"图重建"与"白板重建"共用同一机制。
125
-
126
- ---
127
-
128
- ## MRAgent 机制提炼
129
-
130
- MRAgent(arXiv:2606.06036,ICML 2026)是面向 LoCoMo/LongMemEval 长对话 QA 的记忆系统:Phase 1 把对话建成为"联想记忆图",Phase 2 用 LLM 工具调用循环在图上**主动重建**(而非一次检索)答案。
131
-
132
- ### 1. Cue–Tag–Content 图
133
-
134
- **论文形式化**:异构图 ℳ=(𝒞,𝒱,ℛ)。**Cue 节点**=细粒度关键词(实体、属性、时间、地点);**Content 节点**=记忆条目(情节事件、语义事实、主题摘要);**Tag 不是节点,是边的属性**——三元组 (c, g, v)∈ℛ 表示"cue c 经语义桥 g 连到 content v"(论文 HTML v1,Section 3;⚠ 任务书把它当成三种节点,见出入第 5 条)。
135
-
136
- **代码中的实现**(`memory/system.py`):
137
- - **Cue** = `KeyNode`(`memory/system.py:16-35`):`key_id`(关键词原文)、`tag_list`(该 key 出现过的所有 tag)、`tag_dict: tag → [episode_id]`——即 Cue→Tag 的**倒排索引**;
138
- - **Content** = `EpisodeEvent`(情节事件,`memory/system.py:78-96`):`event_id`(如 `D1:1-1`)、`text`、`origin`(原始对话轮 id)、`time`(绝对日期)、`embedding`、`conversation_time`;另有 `Topic`(主题摘要节点,`memory/system.py:38-43`:`topic_id` 如 `D1:t1`、`text`、`event_list`)和 `Persona`(按人聚合的个人事实,`memory/system.py:54-75`:`person → tag(aspect) → PersonalEvent[]`)——对应论文的"情节层 / 抽象层 / 语义层";
139
- - **Tag** = 字符串属性,挂在 `Link(key_id, event_id, event_type, tag)` 上(`memory/system.py:99-104`),同时冗余索引到 `by_tag: tag → [edge_id]`、`event_to_keys`、`key_to_values`(`memory/system.py:112-119`)。
140
-
141
- **边类型与语义**:
142
- - **Cue→Tag→Content(正向)**:`key_to_values[key] = {(episode_id, origin)}` + `KeyNode.tag_dict[tag]=[episode_id]`——"实体 c 的 g 方面体现在事件 v";由 `store_event_new` 建(`agent/agent.py:848-861`);
143
- - **Content→(Cue,Tag)(反向)**:`event_to_keys[event_id] = {key_id}` + `EpisodeEvent.tag_dict`——事件 v 上可以反查它涉及哪些 cue 与 tag;这是 reverse traversal 的数据基础;
144
- - **Topic→Content**:`topic_to_event` / `Topic.event_list`(`memory/system.py:119, 273-279`)——主题节点挂它辖下的情节事件;
145
- - **Timeline**:`timeline: date → [event_id]`(`memory/system.py:118, 132-134`)——统一时间轴,temporal 工具的数据基础;
146
- - **Persona 边**:`Persona.tag_dict[aspect] = [PersonalEvent]`(`memory/system.py:59-66`)。
147
-
148
- 图本身是**纯内存 Python 对象**,不落盘;持久化靠 Phase 1 的中间产物(rewrite JSONL / keyword JSONL / embedding pkl),每次运行从缓存重建图(`run.py:165, 184-205`)。
149
-
150
- ### 2. 主动重建循环
151
-
152
- **论文算法**(Algorithm 1,HTML v1):维护重建状态 𝒮⁽ᵗ⁾=(𝒵⁽ᵗ⁾, ℋ⁽ᵗ⁾)——活动集(当前激活的 cue/tag/content)+ 累积上下文。每步四操作:①从问题抽取 cue,匹配库存 cue 得初始 𝒵⁽⁰⁾;②LLM 选动作 𝒜⁽ᵗ⁾=f_select(x, ℋ, 𝒵);③受控遍历(应用映射算子);④LLM 路由剪枝 f_route 丢弃无关候选,ℋ ∪= 新证据。Stop(x, ℋ) 由 LLM 判断证据是否足够——**"累积证据"不是数值分数,是 LLM 的模式判定**(answer 模式输出 answer/supports/confidence;否则 navigate 模式继续调工具)。
153
-
154
- **遍历动作集(论文三算子 → 代码落地)**:
155
- - **forward Cue→Tag**:`edges_by_tag(key, tag, note)`——从一个 cue 沿指定 tag 展开其下事件(`agent/tools.py:5-31`;实现 `memory/controller.py:29-48`);论文的 Π_{c→g};
156
- - **forward (Cue,Tag)→Content**:即 edges_by_tag 的返回值(事件文本+origin+id);超过 `RERANK_LIMIT=20` 条时用问题 embedding 余弦取 top-K2(`memory/controller.py:33-47`);
157
- - **reverse Content→(Cue,Tag)**:`query_event_keywords(event_id)`——从一个事件反查它的全部 keyword 与 tags(`memory/system.py:309-310` → `{"key": k, "tags": [...]}`);prompt 明示"Often followed by query_event_context"(`agent/tools.py:51-53`)。论文的 Π_{v→(c,g)},即"检索到的内容派生新 cue/tag 以便转向";
158
- - 辅助动作:`query_event_context`(事件前后原文轮)、`query_conversation_time`(事件发生的会话时间)、`query_topic_events`(主题下辖事件)、`query_personal_information` / `query_personal_aspect`(人物 aspect 两级下钻)。
159
-
160
- **循环骨架**(`llm/controller.py:92-234` `chat_with_tools_once`):assistant→批量执行 tool_calls→assistant 多轮,直到消息解析出 `{"mode":"answer", answer, supports, confidence}`;**防爆炸的硬约束**:`MAX_ROUNDS=8` 轮(最后一轮强制切换为"只准回答"的 FINAL prompt,`llm/controller.py:140-143`)、`MAX_TOOL_CALLS=50` 总调用安全帽(`llm/controller.py:186-190`)、温度 0(`agent/agent.py:37`)。工具描述里内嵌剪枝纪律:"Do NOT repeat the same key–tag combination"、"When exploring, choose at least one tag from each related keyword"(`agent/tools.py:9, 15`);已查过的 keyword 记入 `queried_keyword` 集合去重(`memory/controller.py:60-64`)。
161
-
162
- **剪枝条件总结**:①LLM 自判证据充分(answer/navigate 二选一,confidence 自报);②同 (key,tag) 不可重复查询 + 已查 keyword 集合去重;③单工具返回超阈值走 embedding rerank 截断(RERANK_LIMIT=20 → K2=20);④tag 过多时 LLM 给 tag 打 0-1 相关分取前 TAG_LIMIT=10(`EVENT_KEYWORDS_SYSTEM_PROMPT`,`prompts/prompts.py:6-13`;`memory/controller.py:66-92`);⑤轮数与调用数硬帽。
163
-
164
- ### 3. 工具集(agent/tools.py 的 schema)
165
-
166
- 7 个工具(README "Tool inventory (7 tools)";另有一个被注释禁用的 `score_event_relevance`,`agent/tools.py:135-153`):
167
-
168
- | 工具 | 参数(required 加粗) | 返回 |
169
- | --- | --- | --- |
170
- | `edges_by_tag`(工具面只此一个"遍历"工具) | **tag, key**, note(8-80字决策注记,防跳步) | 该 (key,tag) 下事件 `[id:text]`、origin、ids;>20 条时 rerank 取 top-20(`tools.py:5-31` + `controller.py:29-48`) |
171
- | `query_conversation_time`(temporal) | **event_id** | `Conversation_time:{id}:{date}`(`system.py:303-307`) |
172
- | `query_event_keywords`(keyword,反向遍历入口) | **event_id** | `[{"key","tags":[...]}]`,tags>15 时 LLM 打分选前 10(`controller.py:97-116`) |
173
- | `query_event_context`(context) | **event_id** | 该事件前后各一轮的原始对话文本 JSON(`system.py:319-351`) |
174
- | `query_personal_information`(personal 一级) | **person** | `{"person", "aspects":[tag...]}`(`system.py:312-313`) |
175
- | `query_personal_aspect`(personal 二级) | **person, aspect** | 该 aspect 下 `[origin:text]` 列表(`system.py:315-316`) |
176
- | `query_topic_events`(topic) | **topic** | 主题下辖 `[id:text]` + origins(`system.py:355-367`) |
177
-
178
- 问题侧入口不在 tools.py 而在 agent 主循环:`extract_question_keys`(LLM 抽问题关键词+同义词/时态变体+时间窗,`prompts/prompts.py:206-219`)→ `evaluate_relations_over_graph`(纯文本匹配把问题 key 映射到图 KeyNode:规范化相等 / token 子集 / Jaccard≥0.6,`memory/controller.py:213-260`)→ AND/OR 分组求值 full/partial match(`memory/controller.py:263-370`)→ 种子证据 + keys_candidates + 相似 topic 一起塞进首轮 user 消息(`agent/agent.py:539-545`)。
179
-
180
- ### 4. 图构建流程(Phase 1)
181
-
182
- 编排(`run.py:184-205`;`agent/agent.py:623-744`):**rewrite → embed → extract_keyword → store**,各阶段产物按 `data/<ds>/rewrite_<model>/<id>_rewrite.json` 等路径缓存、存在即跳过(README §4/§7)。
183
-
184
- - **rewrite**(`agent/agent.py:592-621`,`REWRITE_SYSTEM_PROMPT` `prompts/prompts.py:18-55`):逐句改写为自足句。prompt 原文要点(摘录):*"1. Replace ALL pronouns … with explicit entities … 3. Use a short concrete noun to describe what the speaker is talking about in 'tag', e.g. Movie Preference, Hobbies. No more than two words. 4. If a sentence uses a relative time … compute the absolute calendar date based on conversation_time and output 'YYYY-MM-DD'. … - Topics: derive at least ten concrete topics overall (short sentences). Assign topic IDs (t1..tn) …"*——**tag 与 topic 都在这一步附加**;输出 schema 含 `sentence[]`(id/text/tag/origin/topic[]/time)、`topics{}`、`personal_sentences[]`(人/事实/aspect)。校验失败带错误信息重试至 3 次(`agent/agent.py:601-619`);
185
- - **extract_keyword**(`agent/agent.py:669-744`,`KEYWORD_SYSTEM_PROMPT` `prompts/prompts.py:69-87`):*"For each input sentence, extract 2–30 keywords DIRECTLY from the original text … Do not invent, paraphrase, or generalize … Keyword types: entity | topic | verb | time | location | task | event | people … 'sentence_id' must be same with 'id' in TEXT"*。输入刻意只喂 `{id, text}`,防 rewrite 的 tag/topic 泄漏进关键词(`agent/agent.py:734-743` 注释);
186
- - **store**(`agent/agent.py:772-862` `store_event_new`):为每个 keyword 建/取 `KeyNode`,`Link(k, sentence_id, "episode", tag)` 记边,双向写 `key_to_values` / `event_to_keys` / 双侧 tag_dict;**speaker 自动追加为 keyword**(`agent/agent.py:845-848`);topics 与 personal sentences 分别入 `add_topics` / `add_personal_information`。语义层(summary→semantic memory)代码中已被移除并注明原因"summary is never queried"(`agent/agent.py:780-781`)——论文描述的 Cue–Tag–Semantic 层在当前代码里由 Persona 部分事实承载。
187
-
188
- ### 5. 计算成本
189
-
190
- 论文报告(LongMemEval 每 sample,含构建+检索,HTML v1 成本表):
191
- - **Token**:MRAgent 118k,对比 Mem0 245k、MemoryOS 273k、A-Mem 632k、LangMem 3,268k——最低;
192
- - **运行时间**:MRAgent 586s,Mem0 533s(唯一比它快的),A-Mem 1122s、LangMem 1210s、MemoryOS 3136s——第二快;
193
- - **精度**:LoCoMo Gemini 骨干 84.21 vs 最强基线 Mem0 68.31(相对提升 23.3%,摘要的"up to 23%"即此);LongMemEval 72.95 vs MemoryOS 54.92(相对 32%)。
194
-
195
- **节省的具体环节分解**(论文 + 代码对照):
196
- 1. **遍历剪枝**(最大头):一次只展开一个 (key,tag) 面,>20 条即 embedding 截断返回 top-20——避免整库 top-k 检索把大量无关文本塞进上下文;
197
- 2. **图规模控制 / 结构化注入**:首轮注入的是"种子证据句子 + key/tag 候选 + 相似主题"(`agent/agent.py:539-545`),而非全文;8 轮上限保证上下文线性增长受限;
198
- 3. **构建期缓存**:rewrite/keyword/embedding 三阶段产物落盘复用,多轮实验零重复构建(README §7);
199
- 4. 代价:token 省了但轮数多(最多 8 轮 × 多工具调用),所以**墙钟时间并没有比最简单的 Mem0 快**——精度换时间,token 是靠"只注入相关子图"省的。
200
-
201
- ### 最小机制集(→ 白板场景)
202
-
203
- - **节点 schema(简化)**:白板只需两种 Content(state 条目 / dead-end 条目,可加 goals 作第三种)+ 一种 Cue(关键词/文件路径/组件名)。Tag 保留为**字符串边属性**而非节点(任务书的"三种节点"提法需要修正)——tag 用确定性映射:dead-end 条目自动挂 tag `dead-end:<方案名>`,state 条目挂 `topic:<主题>`,文件/模块名自动成 Cue。MRAgent 的 speaker 自动入 keyword(`agent/agent.py:845-848`)对应白板里"涉及文件路径自动入 Cue"。
204
- - **两个遍历动作(简化)**:`expand_tag(tag) → [Content]`(对应 edges_by_tag 的 Cue→Tag→Content 正向,但白板可省去 note 参数与 LLM 打分,直接返回该 tag 全部条目);`trace_back(content_id) → {cues, tags, 邻接条目}`(对应 query_event_keywords + query_event_context 的反向,返回该条目关联的 cue/tag 及同 tag 邻居)。工具描述里写死剪枝纪律("不要重复同一 tag")照搬——这是 MRAgent 防爆炸最便宜的一招。
205
- - **剪枝信号(简化)**:白板是单图小规模(几十条),不需要 embedding rerank 与 8 轮循环;最小信号集 = ①LLM 自判"已能续上工作"即停(answer/navigate 二分,对应 MRAgent 的 Stop 判定);②已展开 tag 集合去重;③每轮返回条目数硬帽(如 ≤10)。前缀缓存约束下:首轮只注入"白板投影 + 入口 Cue 命中的固定边界段",遍历结果全部进动态尾部。
206
-
207
- ---
208
-
209
- ## 许可证核对
210
-
211
- | 仓库 | LICENSE 实况 | 结论 |
212
- | --- | --- | --- |
213
- | miuzel/dsh-graph | 仓库根**无** LICENSE 文件;MIT 许可全文位于 `dsh-graph-host/LICENSE`("MIT License, Copyright (c) 2026 miuzel",21 行标准文本),且 `dsh-graph-host/package.json:5` 声明 `"license": "MIT"`;README.md:99 亦写 "MIT(Copyright © 2026 miuzel)"。npm 包(含 LICENSE)即此目录 | MIT 属实,可合法借鉴;但注意许可文本挂在发布子包而非仓库根 |
214
- | Ji-shuo/MRAgent | **无任何 LICENSE 文件**(根目录及全部子目录均无),README/requirements 无许可声明,package.json 不适用(Python 项目) | 默认版权保留:**代码不可复制/再分发**;论文(arXiv)思想与范式借鉴不受限。这也与"只借鉴范式不搬代码"的既定约束一致——对 MRAgent 是硬约束而非偏好 |
215
-
216
- ---
217
-
218
- ## 与任务书描述的出入
219
-
220
- 1. **dsh-graph 判据不是结构化 schema。** 任务书 Q1 猜想"schema 结构是 criterion_type + evidence_required + confidence_level?"——实际判据只是 `## 质量判据` 小节里的编号文本行(自然语言 + `[script]` 脚本约定),无任何字段化 schema(`core/model.ts:146-153`、`schema/SCHEMA.md:87-91`)。R3 设计白板判据时应以"小节 + 行列表 + 占位过滤 + 确认事件"为参照,而非字段级 schema。
221
- 2. **"每个阶段是否有强制判据"——实际只有一个门。** 任务书状态机调查项暗示逐阶段检查;实际只有进入 `in_progress` 一处设判据门(rules_snapshot + 小节非空 + criteria.confirmed 三条件,`core/machine.ts:80-90`),其余迁移只查边表合法性。另外真实迁移图含大量非线性边(planning→ready、planning→in_progress、collecting→in_progress、in_progress→collecting、review→in_progress、delivered→review,`core/machine.ts:24-33`),与任务书书写的中断式线性链 `draft→planning→collecting→ready→in_progress→review→delivered`(§B 表格)不完全一致。
222
- 3. **graph_handoff 的"环境事实"段是硬编码文案。** 任务书说"board 投影 + 长期记忆 + 环境事实"三元结构——结构属实(`core/ops.ts:339-443`),但"关键环境事实"是写死在代码里的五条本项目 dogfood 事实(executor 注册名、root 覆盖约定等,`core/ops.ts:396-404`),不是可配置或自动采集的机制;长期记忆注入也有 4000 字符截断与 `safeMemory` 防注入清洗这两处任务书未提的细节。
223
- 4. **dsh-graph 没有独立"图遍历工具"。** 任务书调查项问"是否有可复用的图查询/遍历逻辑"——实际没有通用遍历 API;最接近的是 `graph_validate`(全量不变式校验 + 依赖环 DFS)与 `graph_rebuild`(事件流对账),以及 `depends_on` 的"声明即建边 + 部分匹配门控"。白板的 expand_tag/trace_back 设计只能借鉴 MRAgent,不能借鉴 dsh-graph。
224
- 5. **MRAgent 的 Tag 不是节点。** 任务书 C 项写"三种节点类型的具体定义;Cue→Tag、Tag→Content 的边关系"——论文与代码中 Tag 均为**边属性**(三元组 (cue, tag, content),代码中为 `Link` 的 tag 字段 + KeyNode/Event 的 tag 倒排),图里真实存在的第三类节点是 **Topic**(主题摘要)与 Persona 事实,而非 Tag。R3 的"当前字段 → 节点类型 → 边关系"映射表应按 cue/tag-边/content/topic 四元修正。
225
- 6. **论文与代码的工具命名不一致。** 论文 HTML 列举的工具含 `query_tag_events`,代码实际是 `edges_by_tag`(`agent/tools.py:8`);论文称"up to 10 tool invocations per turn",代码是每会话总量 `MAX_TOOL_CALLS=50`(`common/config.py:67`),无 per-turn 限制;论文的 Cue–Tag–Semantic 语义层在代码里已部分移除(summary→semantic 块被删,`agent/agent.py:780-781`),由 Persona 承载。以代码为准。
226
- 7. **成本数字有反例需要如实转述。** 任务书 C 项问"论文声称减少 token 和耗时"——token 确实全场最低(118k vs 245k~3268k),但**运行时间不是最低**:Mem0 比它快(533s vs 586s),MRAgent 仅第二快(论文成本表)。节省主要来自遍历剪枝 + 结构化注入,代价是推理轮数增加。
227
- 8. **MRAgent 许可风险任务书未覆盖。** 任务书仅确认了 dsh-graph 的 MIT(经核属实,但 LICENSE 文件在 `dsh-graph-host/` 子包而非仓库根);MRAgent 仓库无任何许可证——本轮新增确认,R3 方案中对 MRAgent 必须保持"零代码复制"。
228
- 9. **(核实无误项)** 任务书对 dsh-graph 状态机阶段名、`.dsh-graph` 目录/事件流描述、MRAgent 的 7 工具分组(keyword/topic/personal/temporal/context 五类 + 遍历工具)、"rewrite→extract_keyword→store"三段式流程,均与代码一致,无出入。
@@ -1,190 +0,0 @@
1
- # R1·白板接续本地代码审计报告
2
-
3
- > 任务书:`docs/internal/WB-GRAPH-RESEARCH-BRIEF.md`(2026-09-13)。
4
- > 审计对象:`lib/index.js`(8281 行)、`lib/handoff-anchor-pre.js`、`lib/l0-extract-pre.js`、`lib/client.js` 等。
5
- > 所有行号均为 2026-09-13 工作树实测(grep/read 验证,非臆造)。仅读代码,未改任何文件。
6
-
7
- ---
8
-
9
- ## 一、白板数据模型(PLAN.md 磁盘存储)
10
-
11
- **路径解析**
12
-
13
- - `lib/index.js:1549-1579` — `resolvePaths(agent)` 返回全部记忆路径对象;`handoffDir = projectDir/handoff`(1573)、`planPath = projectDir/handoff/PLAN.md`(1574)。解析前强制加载配置(1552),工作区锁定防 cwd 漂移(1559-1561)。
14
- - `lib/index.js:1504-1515` — `projectDirOf(ws)`:集中式记忆根 `config.memoryRoot`(默认 `dshHome()/memory/workspaces`,1513)+ 每工作区一个子目录(1514)。
15
- - `lib/index.js:1498-1501` — `wsKey(ws)`:工作区路径 → `--<特殊字符替换为->--` 目录名,'default' 兜底。
16
- - `lib/index.js:1530-1543` — `migrateLegacy`:旧分散结构 `{ws}/.dsh-memory` → 集中式根的复制式迁移。
17
-
18
- **文件结构约定(无 schema)**
19
-
20
- - PLAN.md 与账本均为自由 Markdown,代码中不存在任何 schema/结构校验。唯一的"结构约定"有三处且都不做校验:
21
- - `lib/index.js:1641` — P7 老化只按标题正则 `/^## .*(历史|踩坑|流水线要点)/` 分类顶层节;
22
- - 四段式标题字符串只出现在 prompt 文案(`lib/index.js:445`、`2506`、`3703`)与权重表(`lib/handoff-anchor-pre.js:18-23`),写入端无任何代码消费它们;
23
- - `lib/index.js:1674` — 账本统一由写入函数加系统头部 `# 交接账本 · <日期> <HH:MM>`。
24
-
25
- **归档与老化**
26
-
27
- - `lib/index.js:1620-1662` — `writePlanSnapshot(projectDir, content)`:整篇重写 PLAN.md;旧内容非空且与新内容不同 → 先归档到 `handoff/archive/PLAN-<ts>.md`(1626-1631)。
28
- - `lib/index.js:1633-1656` — P7 白板老化(2026-09-09):新内容中标题命中「历史|踩坑|流水线要点」的顶层 `##` 节移入 `handoff/archive/PLAN-history-<ts>.md`;**只移动不删除**;全部节都命中时 fail-soft 放弃老化原样写入(1645)。
29
- - `lib/index.js:1657` + `lib/index.js:3798-3801` — 落盘走 `writeFullRaw`(原始整篇写,**不经 anchor 分流**;契约上白板属"非记忆文件"排除项)。
30
- - `lib/index.js:464-467` — `handoffStamp()`:`YYYYMMDD-HHMMSS`,文件名字典序=时间序(同秒多写用 `-b/-c` ASCII 后缀防撞,`1672`、`1650`)。
31
-
32
- **历史版本查询**
33
-
34
- - `lib/index.js:1682-1686` — `listHandoffLedgers(dir, limit)`:白名单正则 `/^(?:handoff-\d{8}-\d{6}(-[a-z])?|PLAN-\d{8}-\d{6})\.md$/`,字典序倒序=新→旧。
35
- - `lib/index.js:2795-2874` — `handoffPanelData(fileQ, sessionId)`:GUI 白板面板——当前 PLAN 全文(2864)、归档历史版本列表(2806-2812,最多 30 条)、账本时间线(2813-2820,最多 60 条);`fileQ` 非空时按**严格白名单**(2800:`PLAN.md|handoff-*|archive/PLAN-*`)返回指定文件文本 → 白板历史版本查询的唯一入口(HTTP:`lib/index.js:7370-7377`,GET `/handoff-state`,loopback-only)。
36
- - `lib/index.js:1603-1615` — `readLatestHandoff(handoffDir)`:按 mtime 取最新账本(同秒 `-b` 后缀参与竞争)。
37
- - `lib/index.js:1584-1600` — `findLatestGlobalHandoff()`:跨工作区扫描全部 ws 的 `handoff/handoff-*.md`,取 mtime 最新一篇(工作区未绑定时的降级材料源)。
38
-
39
- ---
40
-
41
- ## 二、交接账本(生成路径与四段式映射点)
42
-
43
- **生成路径共 3 条**
44
-
45
- 1. 工具显式写:`lib/index.js:7069-7112` — `memory_note_pre` 工具,`kind` 枚举 `note|handoff|plan`(7072);`kind=handoff/plan` 分支在 7078-7091:`sanitizeForWrite`(plan 20 万字 / handoff 8000 字上限,7079)→ `writePlanSnapshot`(7083)或 `writeHandoffLedger`(7084)→ 同步 `state.planText` / `state.latestHandoffText`(7086-7087)。注意:**此分支不检查 `handoffEnabled`**(其余读写路径均有该开关门禁,见 1691/1869/2679/2796/3137/3616)。
46
- 2. 水位自动骨架账本:`lib/index.js:2002-2024` — `checkWaterLevel` 内 `waterLevelAutoHandoff!==false` 且本会话未写过时触发(每会话一次,`rt.waterLevelAutoHandoffDone`);骨架从"已策展源"抽取(今日日志尾部 16 行 2008、失败行去重 4 条 2009、反思摘要 2010、笔记头部 6 行 2011),**四段标题在代码里硬编码**(2014-2017:`## 任务状态` / `## 目标` / `## 已试方案与失败原因` / `## 进度与下一步`),`slice(0,4000)`(2018)后**直调 `writeHandoffLedger`(2019)——完全绕过工具层门禁**。
47
- 3. 接续前刷新仪式:`lib/index.js:2502-2509` — `refreshRitualPrompt()` 明文要求旧会话执行 `memory_note_pre(kind=plan)`(2505)与 `memory_note_pre(kind=handoff)` 四段式(2506)。宿主侧 `hostRefreshRitual`(2540-2574)经 `sessionController.prompt` 发回旧会话并轮询 `handoffMaterialStamp`(2524-2533,PLAN mtime+最新账本名指纹)等待材料变化;client 路径 `refreshOldSession`(`lib/client.js:2117`)+ `waitForRefresh`(2145-2166)同序。
48
-
49
- **四段式标题在代码里的全部映射点**
50
-
51
- - prompt 约束(唯一的"生成质量约束"载体):`lib/index.js:445`(水位建议 `snapshotWaterBody`,四段各≤5行的完整描述)、`2506`(刷新仪式)、`3703`(静态纪律 `renderMemoryStatic` 中的账本质量纪律:每段≤5行、失败项写「方案→失败原因」、下一步必须可直接执行)。
52
- - 结构消费:`lib/handoff-anchor-pre.js:18-23` — 四段权重表 `HANDOFF_LEDGER_WEIGHT_VERSION='handoff_ledger_weight_pre_v1'`:已试方案与失败原因 `.35` > 进度与下一步 `.30` > 目标 `.20` > 任务状态 `.15`;未知 `##` 段权重 `.05`(27)。
53
- - 水位骨架硬编码:`lib/index.js:2014-2017`。
54
- - 权重化截断实现:`lib/handoff-anchor-pre.js:41-58`(`parseHandoffLedgerPre`:行级切分 `## ` 标题,无任何段 → 返回 null fail closed);`91-114`(`weightedTrimHandoffLedgerPre`:预算充足逐字节原样返回;不足从最低权重段截 body、保标题、截点带 `TRIM_MARK`(28);仍超回落 `slice(0,budget)`)。
55
-
56
- **账本命名**
57
-
58
- - `lib/index.js:1667-1679` — `writeHandoffLedger`:每篇新文件 `handoff-<handoffStamp()>.md`(1669,即 `handoff-YYYYMMDD-HHMMSS.md`);同秒 `-b..-v` 后缀(1672);模型 content 自带的 `# 交接账本` 标题行一律剔除(1673),正文经返回值 `clean` 带回(1675)供 state 同步(7087)。
59
-
60
- ---
61
-
62
- ## 三、注入/检索路径(新窗口如何唤醒)
63
-
64
- **接续材料组装:`buildContinueCarry`(`lib/index.js:2677-2792`)**
65
-
66
- - 2682-2688 — 读 PLAN 全文 + 最新账本;工作区无账本 → 降级全局最近账本(`ledgerFrom='global'`,2687)。
67
- - 2689 — `buildPrevSessionPack`(2592-2671):定位旧会话 `~/.dsh/sessions/<ws>/<sid>/session.jsonl[.zstd]`,逐帧 zstd 解压折叠,转写落盘 `handoff/prev-session-<sid8>-<HHMMSS>.md`(2638);单条截断 2000 字符、总长上限 60000(2649、2655)。
68
- - 2712 — **第0层**:`【第0层 · 白板 PLAN.md(节选)】` + `plan.slice(0, 3000)`。
69
- - 2713-2723 — **第1层**:账本 8000 预算 `weightedTrimHandoffLedgerPre(ledger, 8000)`(2720,动态 import,失败 fail-soft 回落位置截断 `slice(0,8000)` 2717——注释明示 I4 绝不阻塞接续,2715);预算 8000 与动态快照的 `handoffLedgerChars`(800) **不同源不复用**(2716 注释)。
70
- - 2725-2726 — **第2层**:近期线程 `tailText`=最近 20 条消息、单条 700 字(2667)。
71
- - 2727-2737 — **第3层**:转写文件绝对路径 + 按需 read 指令("仅在第0-2层不足以推进时再 read",2730)。
72
- - 2739-2760 — P5 接续锚点表:`buildL0IndexPre(body, {maxChars:48})` 对工作区笔记/今日日志各取 10 条 `mem_<32hex>` 锚点摘要,置于 parts 末尾(预算超限时先被截掉=自动回退平铺,2742-2743)。
73
- - 2774 — `carryText = parts.join(NL+NL).slice(0, 18000)`(材料总预算 18000 字符)。
74
- - 消费端两条:宿主兜底 `hostAutoContinue`(2264)在 2279 取材料、2305 `sc.prompt` 直接作为新会话首条消息;client 路径 HTTP POST `/handoff-continue`(7381-7393,7390 调 `buildContinueCarry`)→ `lib/client.js:2226` → `executeContinue`(2167-2213)在 2201 `rf.session.prompt` 注入 `d.carryText`。
75
-
76
- **动态快照注入(每轮 user-role 快照,非接续场景也在注入)**
77
-
78
- - `lib/index.js:3593-3690` — `renderMemoryDynamic`:白板注入点 3616-3618(`handoffPlanChars` 预算,默认 1200,配置定义 255-256)+最新账本注入点 3619-3620(`handoffLedgerChars` 预算,**默认 800**,配置定义 257-258);均经 `stripSensitiveSections`+`sanitizeForInjection`+`truncateLinesBounded`(行边界截断,482-487);位于动态快照**首位**(在日志/笔记段之前,3614 注释)。
79
- - 3623-3624 — 工作区未绑定时注入全局最近账本**绝对路径指针**(模型自行 read)。
80
- - 3627-3628 — 水位越阈(≥0.75 且 10 分钟内)注入 `snapshotWaterTitle/Body`(445)。
81
- - state 来源:`_doRefresh`(3107-3205)在 3137-3146 读 PLAN+最新账本入 `state.planText/latestHandoffText`;写入工具同步同字段(7086-7087)。
82
- - 注册:`lib/index.js:6958-7024` — `ctx.systemPrompt.context({name:'dsh:auto-memory-pre'})`(user-role 快照追加历史尾部);频率控制(日志段指纹+`snapshotMinGapRounds`,6971-7019);压缩后强制重注入(6984-6989)。
83
-
84
- **"检索"逻辑现状**
85
-
86
- - 唯一的检索是**词法行级匹配**:`searchHandoffCorpus`(1689-1719)对 `白板 PLAN.md`(每文件最多 3 命中行)+最新 12 篇账本(2 行)+20 篇归档(2 行)做 `line.toLowerCase().includes(term)` 计分,平铺直返;`handoffEnabled===false` 时返回空(1691)。
87
- - 路由:`recall()`(3827-3853)`scope='handoff'` 走 3838-3844;`scope='all'` 时白板语料并入头部(3875-3881);`scope='sessions'` 走 `searchSessionHistory`(1722-1735,sessionQuery 部署时可用);工具参数面在 `lib/index.js:7169`(`memory_recall_pre` 的 `scope` 枚举)。
88
- - 结论:**主注入路径是平铺**(固定分层+预算截断+一张锚点表),没有图导航/主动重建;P5 锚点表是唯一的"地图"形态(也只覆盖笔记与日志,不覆盖白板与账本本身)。
89
-
90
- ---
91
-
92
- ## 四、前缀缓存约束(I1 字节级稳定的实现)
93
-
94
- **不变量出处**:`docs/CONTINUITY-FLOW.md:159-168` 定义 I1-I5(I1=前缀缓存字节级稳定:只动动态快照层,静态纪律层字节不碰;I3=凭证永不进提示词;I4=绝不阻塞接续)。代码中显式引用 I4 的注释在 `lib/index.js:2715`。
95
-
96
- **静态/动态分离(I1 的实现点)**
97
-
98
- - `lib/index.js:3590-3592` — 关键注释:动态记忆 → `ctx.systemPrompt.context()`(user-role 快照追加在历史尾部,内容变化才追加、由 dsh-agent-loop `project()` 去重);system prompt 不含动态内容 → 字节级稳定 → DeepSeek 前缀缓存全程命中。
99
- - `lib/index.js:3692-3715` — `renderMemoryStatic`:固定纪律文本(含白板/账本写入纪律 3703),同步、零状态依赖;注册为 `ctx.systemPrompt.section`(7026-7032,`text: () => ...` 无动态输入)——字节稳定锚。
100
- - `lib/index.js:6956-6958` — 动态快照注册为 `ctx.systemPrompt.context`(6958-7024)。
101
- - `lib/index.js:3134` — refresh 注释:白板/账本"内容属动态层,不碰静态字节"。
102
- - 动态快照内部结构:`<memory_system>` 头(3608)…铭文行(3686,含日期,注释明示"秒级时间戳也不再击穿 system prompt 前缀")…`</memory_system>`(3687)。
103
- - 防意外击穿:`neutralizePromptTemplateVars`(6050-6052)无 `{{` 时恒等变换(3713-3714 注释);白板/账本块注入前统一清洗(3617、3619)。
104
-
105
- **接续链路的缓存影响**
106
-
107
- - `carryText` 是新会话**首条 user 消息**(`lib/client.js:2201`、`lib/index.js:2305`),含材料 mtime 时间戳(2692-2703、2712-2723)——单次接续内写定后不再变化(对该新会话的前缀稳定);跨接续会话间不共享。
108
- - P5 锚点表被明确设计为字节稳定:"同输入 → 同抽取+同排序+同格式"(2740 注释)——这是新检索方案必须保持的性质。
109
-
110
- ---
111
-
112
- ## 五、写入路径与校验
113
-
114
- **触发条件汇总**(写入白板/账本的仅此三条通路)
115
-
116
- | 通路 | 代码位置 | 门禁 |
117
- | --- | --- | --- |
118
- | 工具 `memory_note_pre(kind=plan/handoff)` | `lib/index.js:7078-7091` | sanitizeForWrite(7079);**无 handoffEnabled 检查** |
119
- | 水位自动骨架账本 | `lib/index.js:2002-2024`(写 2019) | `waterLevelAutoHandoff` 开关 + 每会话一次;**直调 writeHandoffLedger,绕过工具层** |
120
- | 刷新仪式(旧会话自己调工具) | `2505-2506` + `hostRefreshRitual` 2540-2574 + client 2117/2145 | 仅 prompt 约束,产物仍走通路 1 |
121
-
122
- **写入前校验现状("判据校验缺失"证据链)**
123
-
124
- - 已有校验=通用卫生闸门 `sanitizeForWrite`(`lib/index.js:6059-6083`):空内容/乱码密度>0.001/复读退化(hasStutter)/raw JSON envelope/base64 行/连续同文行≥3,超长截断(6070-6073);原因表 `WRITE_GATE_REASON`(6092);保留语法改写 `sanitizeReservedSyntax`(6088-6090,`<!-- memory:` → `<!--memory:` 防锚点冲突)。返回 `{ok, reason, clean}` 模式(6063-6082)。
125
- - `writeHandoffLedger`(1667-1679)对 content 做的唯一结构处理 = 剔除 `# 交接账本` 标题行(1673)+ 加系统头(1674)。
126
- - `writePlanSnapshot`(1620-1662)对 content 做的唯一结构处理 = P7 节分类老化(1633-1656)。
127
- - **判据(四段齐全、每段≤5行、下一步可执行、失败项含报错词)无任何代码校验**:
128
- - 这些纪律只存在于 prompt 文案(445、2506、3703),是纯"模型自觉"约束;
129
- - `parseHandoffLedgerPre`(handoff-anchor-pre.js:41-58)虽能解析四段,但只在**注入端**被调用(2720);解析失败返回 null → 注入端 fail-soft 回落位置截断(2722)——是读取端的容忍,不是写入端的拦截;
130
- - 水位骨架(2019)绕过工具层,即使把校验加在工具里也覆盖不到该通路 → 校验必须下沉到 `writeHandoffLedger`/`writePlanSnapshot` 才能全覆盖。
131
-
132
- ---
133
-
134
- ## 六、最小改动点清单
135
-
136
- ### P1 判据校验中间件(写入路径上加校验函数)
137
-
138
- | # | 改动点 | 位置 | 上游(谁调它) | 下游(它调谁) | 改动量 |
139
- | --- | --- | --- | --- | --- | --- |
140
- | 1 | 新建纯函数校验模块(建议 `lib/ledger-criteria-pre.js`,仿 handoff-anchor-pre.js 契约:纯函数、零 IO、fail closed) | 新文件 | index.js 两处写入函数 | 复用 `parseHandoffLedgerPre`(handoff-anchor-pre.js:41)判段结构;自身判段数/段名/段行数/下一步特征 | 新增 ~100 行 |
141
- | 2 | `writeHandoffLedger` 入口插校验(或 1674 `writeFullRaw` 之前) | `lib/index.js:1667-1679` | ① `memory_note_pre` 7084;② 水位骨架 2019 | `writeFullRaw`(3798)、`handoffStamp`(464)、`memToday`(3208)、`nowHm`(462) | ~5 行 |
142
- | 3 | `writePlanSnapshot` 入口插校验 | `lib/index.js:1620-1662` | 仅 `memory_note_pre` 7083 | `writeFullRaw`(1657) | ~5 行 |
143
- | 4 | 工具层拒绝文案:把判据 gate 并入 7079-7081 的 `sanitizeForWrite` 判定处,仿 `WRITE_GATE_REASON`(6092) 模式返回可执行的改写指引 | `lib/index.js:7078-7091` | harness 工具分发 | 新校验函数 | ~10 行 |
144
- | 5 | 水位骨架路径的 fail-soft 策略:校验不过时照写+diag+骨架内加警示行(或仅记日志跳过),**不得阻塞接续**(I4,`docs/CONTINUITY-FLOW.md:165`) | `lib/index.js:2002-2024` | `checkWaterLevel`(1867) ← pre-step 钩子 2049 与 `agent/turn-stopping` 钩子 6868/6884 | 通路 2 | ~5 行 |
145
- | 6 | 回归测试:仿 `tests/smoke/smoke-test-handoff-anchor-pre.mjs`(fixture 锁定+源码守卫);注意 `tests/smoke/smoke-test-handoff-pre.mjs` 的 G0 源码守卫断言了写入/注入块的存在与顺序(文件头 13-17),插校验后需同步 | tests/smoke | — | — | ~60 行 |
146
-
147
- ### P2 结构化存储改造(Markdown → 附加结构,增量兼容)
148
-
149
- | # | 改动点 | 位置 | 说明 |
150
- | --- | --- | --- | --- |
151
- | 1 | 结构化元数据写入钩子 | `writeHandoffLedger` 返回值(`lib/index.js:1675` `{ok,path,clean}`)与 `writePlanSnapshot` 返回值(1658) | sidecar JSON(如 `handoff/index.json` 或每账本同名 `.json`)在此落盘;现有 Markdown 读取方全部兼容 |
152
- | 2 | 结构化读取视图 | `handoffPanelData`(2795-2874) | GUI 增加 tag/段落级视图;`fileQ` 白名单(2800)需放行新文件名 |
153
- | 3 | 结构化检索 | `searchHandoffCorpus`(1689-1719)与 `recall` scope 路由(3838-3844、7169) | 词法匹配 → 段/Tag 级命中;scope 枚举扩展 |
154
- | 4 | 注入端导航层 | `buildContinueCarry`(2739-2760 锚点表、2708-2711 分层说明) | `expand_tag`/`trace_back` 产物作为新"层"或锚点表扩展;总预算 18000(2774)与字节稳定约束(2740)不变 |
155
- | 5 | 白板/账本条目加锚点(可选,图化前提) | `writeHandoffLedger`/`writePlanSnapshot` 写入时为节/条目生成 `<!-- memory:mem_<32hex> -->` 锚 | 复用 `parseMemoryItemsPre`(l0-extract-pre.js:54)切分;注意与 `sanitizeReservedSyntax`(6088)的豁免写法约定 |
156
-
157
- ### 完全不用动
158
-
159
- - `lib/client.js` 接续流程(`refreshOldSession` 2117 / `waitForRefresh` 2145 / `executeContinue` 2167-2213 / `runContinueFlow` 2214-2232)——只透传 `carryText`。
160
- - 宿主接续执行器 `hostAutoContinue`(2264-2335)与接续序号持久计数器(2106-2160)。
161
- - `lib/memory-writer.js` / `memory-writer-pre.js`(自动沉淀完全不涉白板/账本,grep 零命中)。
162
- - `lib/water-window-pre.js`、`lib/l0-extract-pre.js`(纯函数,原样复用)。
163
- - `DEFAULT_PROMPT_LAYERS`(434-455)与静态纪律 `renderMemoryStatic`(3693-3715)——**I1 禁止改动其字节**;判据纪律如果要进 prompt 只能加在动态层或另开 section(需评估)。
164
- - `readLatestHandoff`(1603)/`listHandoffLedgers`(1682)/`findLatestGlobalHandoff`(1584)/`handoffMaterialStamp`(2524)——sidecar 增量方案下原样可用。
165
-
166
- ---
167
-
168
- ## 七、现有可复用件盘点
169
-
170
- | 组件 | 位置 | 能力 | 与新方案重叠度 |
171
- | --- | --- | --- | --- |
172
- | `parseHandoffLedgerPre` | lib/handoff-anchor-pre.js:41-58 | 四段账本解析(preamble+sections+权重),无段 fail closed | **最高**——判据校验与结构化解析的地基,可直接复用为校验前置解析 |
173
- | `weightedTrimHandoffLedgerPre` | lib/handoff-anchor-pre.js:91-114 | 权重化预算截断(.35/.30/.20/.15),预算内逐字节原样 | 高——新检索方案下注入预算控制仍必需 |
174
- | P7 白板老化 | lib/index.js:1633-1656 | 确定性节分类+移动不删除+fail-soft | 中——"节级 lifecycle"范式可推广(如 dead-end 节自动下沉归档) |
175
- | 归档机制 | lib/index.js:1626-1631 | 旧版白板 `archive/PLAN-<ts>.md` 版本历史 | 中——结构化方案的版本链可直接挂在现有归档上 |
176
- | `buildL0IndexPre`/`extractL0Pre`/`parseMemoryItemsPre` | lib/l0-extract-pre.js:118/84/54 | 锚点切分+确定性 L0 摘要(同输入同输出) | 中——"条目地图"地基;但 PLAN/账本当前**无锚点**(锚点仅存在于开启 memoryAnchorEnabled 的 notes/log),图化需写入端新增锚点注入 |
177
- | `searchHandoffCorpus`+scope 路由 | lib/index.js:1689-1719 / 3838-3844 | 白板语料词法检索(行级 includes 计分) | 中——可原地升级为结构化检索的工具面 |
178
- | `handoffMaterialStamp` | lib/index.js:2524-2533 | 材料变化指纹(PLAN mtime+最新账本名) | 中——判据校验失败重试、仪式等待、sidecar 失效检测可复用 |
179
- | `sanitizeForWrite`+`WRITE_GATE_REASON` | lib/index.js:6059-6092 | 写闸门 `{ok,reason,clean}` 模式与拒绝文案模式 | 中——判据中间件照此模式实现,工具层文案零新概念 |
180
- | `stripSensitiveSections`/`sanitizeForInjection` | lib/index.js:5987/6054 | 注入端敏感段清洗 | 中——任何新注入块必须复用(I3) |
181
- | `handoffPanelData`+`/handoff-state` | lib/index.js:2795-2874 / 7370-7377 | 面板数据+严格白名单文件读取 | 低-中——结构化视图挂载点 |
182
-
183
- ---
184
-
185
- ## 八、给 R3 的关键代码事实(一句话版)
186
-
187
- 1. 四段式结构目前**只活在 prompt 文案里**,写入端零校验、解析端仅注入时消费——P1 校验中间件的两个咽喉是 `writeHandoffLedger`(index.js:1667) 与 `writePlanSnapshot`(1620),其中账本咽喉可同时覆盖工具与水位骨架两条通路。
188
- 2. 注入是"固定分层平铺"而非检索;唯一的结构化地图是 P5 锚点表(2739-2760,且不覆盖白板/账本);检索仅 `searchHandoffCorpus` 词法行匹配(1689)。
189
- 3. 前缀缓存纪律已工程化:静态纪律 section(7026)/动态快照 context(6958)分离,白板/账本/水位全部位于动态层(3614-3628);新方案的图导航产物必须放动态层并保持"同输入同字节"(2740)。
190
- 4. 结构化存储的最小路径是 sidecar JSON(写入钩子在 1675/1658 返回值处),现有全部 Markdown 读者(1603/1682/1584/2795)可原样兼容。
@@ -1,56 +0,0 @@
1
- # 主张核实结论表 · 2026-09-14
2
-
3
- > **用途**:第一轮外部评审(`docs/internal/reviews/REVIEW-gpt6astra-20260914.md`)A 节列出的 12 条「文件:行号」主张,逐条对照**当前代码**核实后的结论。
4
- > **这是第二轮投喂的输入之一**:告诉对方哪些主张**已被证实**(可当前提)、哪些**行号有误但实质成立**、哪些是**架构级必改项**。
5
- > **核实方式**:只读核实(读文件 + grep),未修改/创建/删除任何文件,未运行任何测试,未执行 git 写操作。
6
- > **我方独立复核**:C8(最严重的一条)已由本会话自行打开 `tests/smoke/smoke-test-c5-tier-inject-pre.mjs:115-137` 与 `docs/internal/THREE-LAYER-CONTRACT.md:178-189` 逐行复核确认。
7
-
8
- ---
9
-
10
- ## 结论摘要
11
-
12
- **12 / 12 条成立**(真 10 + 部分真 2),**假 0 条,无法判定 0 条。**
13
-
14
- - **行号有误但实质成立**:C5(`125` 是函数签名行,实际行为在 `126–127`;`138` 正确)、C10(`323` 是注释行,实际行为在 `327–335`)。
15
- - **行号在可接受范围**(指向路径首行/字面量首行,非错引):C9(`3894` 守卫首行,复用赋值在 `3896`、拼装在 `3901`)、C12(`149` 为 `FIELDS` 字面量首行,条目在 `150–157`)。
16
- - 其余 8 条行号与当前文件逐一一致。
17
-
18
- **因此:第一轮评审不是「看着像真」,是真的。其 A1–A3 应按事实指控处置,而非按意见讨论。**
19
-
20
- ---
21
-
22
- ## 逐条核实表
23
-
24
- | 编号 | 判定 | 证据(当前文件:行号) | 说明 |
25
- |---|---|---|---|
26
- | **C1** | 真 | `lib/semantic-js-pre.js:60` | `fused: D6_FUSION_WEIGHTS_PRE_V1.dense * denseN + D6_FUSION_WEIGHTS_PRE_V1.lexical * lexN`;`denseN`/`lexN` 由 `normOf` 归一化(:46–51)⇒ 确为**归一化分数空间加权**,与 S3.2「禁止分数空间加权」相悖。 |
27
- | **C2** | 真 | `python/worker_semantic_pre_v1.py:486`;调用点 `:907` | `:486 \| c['fusedScore'] = round(w * dn + (1 - w) * ln, 6)`(`dn`/`ln` 来自 `:479–481` 的 `_minmax`);`:907 \| candidates = self.hybrid_rank(candidates, query, …)`。两行号均准。 |
28
- | **C3** | 真 | `lib/context-host-pre.js:378` | `keptList = fuseD6Pre([...poolMap.values()].map((k) => ({ …`)—— JS 侧 D6 加权的唯一生产调用点。 |
29
- | **C4** | 真 | `lib/activation-inbox-pre.js:255` | `const sorted = [...items].sort((a, b) => b.score - a.score \|\| …)`;`items` 的 `score` 源自 `c.score`(`:357`),JS 路径=裸稠密分(`context-host-pre.js:505`)、Python 路径=裸 `c['score']`(worker `:637`)⇒ **融合序在尾注渲染时被重排覆盖**。 |
30
- | **C5** | **部分真** | `lib/l0-index-pre.js:126–127`(主张写 125,实为函数签名行);`:138` 正确 | `:126 texts = items.map(it => it.l0)`、`:127 await embedder.embedPassages(texts)` ⇒ **先全量嵌入**;`:138 const reused = prev && prev.l0Hash === l0Hash` 才判复用(`:141` 应用)。「增量不省嵌入算力」**实质成立**;`update → assemble(:213)` 对全部 items 嵌入。 |
31
- | **C6** | 真 | `lib/l0-index-pre.js:219` | `recomputed: changed,`;`changed` 在 `:204–210` 按 `l0Hash` 分类计数(新增/变化),**与实际嵌入次数(`items.length`)不同**。(`:179` 的 `buildFull` 路径才是 `recomputed: entries.length`。) |
32
- | **C7** | 真 | `python/m7_embedding_pre_v1.py:75` | `def chunk_id_for(memory_id, record_digest, ordinal)`,哈希串含 `record_digest`(`:77`);调用处 worker `:317`/`:338` 传入整条 `rec['recordDigest']`(来源 `memory-anchor-pre.js:131`/`:211`)⇒ **改一个块则整条记录所有块 ID 全变**,块级复用无从谈起。 |
33
- | **C8** | 真 | `tests/smoke/smoke-test-c5-tier-inject-pre.mjs:127,133`;契约 `docs/internal/THREE-LAYER-CONTRACT.md:183` | `:127` 放入 `status: 'superseded'` 候选;`:133 eq(lines.length, 3, 'Tier-1 条数=命中数(≤K)')` 断言三条**全留**;`:137` 更显式断言 `lines[2]`(即那条 superseded)带 `0.55` 分。I5 原文(契约 `:183`):**「非 `current` 的条目在检索结果与注入内容两处都被过滤」** ⇒ **我方测试把违反 I5 的现状锁成了正确行为**。本会话已逐行独立复核确认。 |
34
- | **C9** | 真 | `lib/index.js:3894`(守卫首行)、`:3895`、`:3896`、`:3901` | `:3894 fresh = !!gh && Date.now() - gh.at < 30*60000`(**只查时间**)、`:3895` 只查 session、`:3896` 直接复用 `gh.hits`、`:3901` 与**当前** sources 拼装;`_tierGateHits` 投影本身不含 `miv`(`activation-host-pre.js:151–165`)⇒ 全路径**无处比对 `miv`**,I6(契约 `:184`「三层来自同一份快照(同一 `miv`),混版视为错误」)失守。(若按"复用赋值行"口径,正确行号是 `:3896`。) |
35
- | **C10** | **部分真** | `lib/tier-layer-inject-pre.js:327–335`(主张写 323,实为注释行) | `:323` 只是注释「只裁下探段,目录层与降级行永不裁」;实际裁剪循环在 `:329` 仅 `lines.pop()`(`drillParts`),`headParts`(目录+降级行)从不动;`:332–334` 在裁剪**之后**把 `[降级] 下探段超注入预算,已裁剪 N 行…` push 进 `headParts`,`:336` 拼入 `text` ⇒ **最终长度可超 `maxTotal`**。 |
36
- | **C11** | 真 | `lib/index.js:3986` | `const catalogCost = s.tier0LayerText ? Math.min(String(s.tier0LayerText).length + 2, Math.floor(budget * 0.35)) : 0` —— 封顶的是 **35% 的扣账成本**(供 `:3987` 的 `sub` 计算);实际注入用全文 `s.tier0LayerText`(`:3957`),**不参与 `used` 记账** ⇒ 账面与实际双口径。 |
37
- | **C12** | 真 | `tests/smoke/smoke-test-doc-code-consistency-pre.mjs:149–157` | `const FIELDS = [` 下恰好 **7 项**:`l0IndexEnabled` / `tier0CatalogEnabled` / `injectBudgetChars` / `tier0MaxTokens` / `tier0BudgetShare` / `B0` / `B2`(`:150–157`;`TRACKED_NAMES:89` 同七项);`TIER_BUDGET_PRE_V1`(`tier-layer-inject-pre.js:32–40`)里的 **`L1` / `K` / `projectRatio` / `floorRatio` / `maxTier2Blocks` 均未覆盖**。 |
38
-
39
- ---
40
-
41
- ## 必须进施工方案的 5 项(架构级,非测试写法)
42
-
43
- 1. **C5 + C6 ·「增量索引」名实不符**:`assemble` 每次全量 `embedPassages`,`recomputed` 又是"变化计数"而非真实编码次数 ⇒ 要么做**真增量**(只嵌入 hash 变化的条目),要么把指标改成诚实口径。**否则 3.0 的「增量索引」是空头承诺。**
44
- 2. **C7 · 块 ID 含整条 `recordDigest`** ⇒ **块级向量缓存必须先改成按「块内容摘要」做键**,否则 3.0 目标④「块级向量缓存」根本无法兑现。
45
- 3. **C9 · I6 失守**:命中投影不带 `miv`,跨版本复用旧命中并与当前 sources 拼装 ⇒ 投影需带 `miv`,并在 compose 处比对(**属接口改动**)。
46
- 4. **C11 + C10 · 预算账本双口径**:扣账成本被 35% 封顶、目录却按全文注入;裁剪之后又追加降级行 ⇒ **`injectBudgetChars` 目前不是硬上限**,需收敛为单一口径记账。
47
- 5. **C1 / C2 / C4 · 排序语义未定**:minmax 分数空间融合(D6)+ 尾注按裸 `score` 重排 ⇒ 必须先裁定「**融合分是否决定展示顺序**」,并与 `lib/recall-fusion-pre.js` 的 rank-space RRF **存并取舍**一并解决。
48
-
49
- ---
50
-
51
- ## 第二轮如何使用本表(纪律)
52
-
53
- - 本表中判**真 / 部分真**的条目,在第二轮提示词里**直接认证为事实**,要求对方**不必再论证**,只需给出落地方案——省下的篇幅全部用于 Phase 设计与验收断言。
54
- - 判**部分真**的两条(C5、C10),第二轮须显式给出**修正后的行号**,并要求对方按修正后的位置写改动点。
55
- - 对方在第二轮若**重新引用已被本表修正的行号**或**把本表已认证的事实重新论证一遍**,视为未承接,退回。
56
- - 对方若**指出本表某条核实有误**,必须写明"我方哪一步错了"(读了哪个文件的哪一行、得出什么相反结论);接受反驳,但反驳同样要落到 `文件:行号`。