@thincoder/core 0.9.4 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +1 -1
  3. package/abort-provenance.mjs +10 -10
  4. package/advisor/citations.mjs +5 -5
  5. package/advisor/compaction.mjs +16 -15
  6. package/advisor/loop.mjs +14 -14
  7. package/advisor/messages.mjs +6 -6
  8. package/advisor/project-context.mjs +5 -5
  9. package/advisor/repos.mjs +3 -3
  10. package/advisor/run.mjs +23 -13
  11. package/advisor.mjs +4 -4
  12. package/agent/assemble.mjs +111 -0
  13. package/agent/chat-call.mjs +68 -0
  14. package/agent/child-marks.mjs +2 -2
  15. package/agent/completion.mjs +9 -2
  16. package/agent/continue-decision.mjs +25 -0
  17. package/agent/dispatch-gates.mjs +140 -0
  18. package/agent/dispatch-run.mjs +167 -0
  19. package/agent/dispatch.mjs +55 -294
  20. package/agent/family-tools.mjs +11 -11
  21. package/agent/helpers.mjs +63 -3
  22. package/agent/live-beat.mjs +49 -0
  23. package/agent/post-turn.mjs +4 -8
  24. package/agent/record-results.mjs +23 -20
  25. package/agent/relay-prefix.mjs +2 -2
  26. package/agent/run-stages.mjs +19 -15
  27. package/agent/run-start.mjs +111 -0
  28. package/agent/setup-reminders.mjs +9 -9
  29. package/agent/setup.mjs +32 -4
  30. package/agent/spawn-child.mjs +9 -9
  31. package/agent/suspension.mjs +90 -30
  32. package/agent/timers.mjs +113 -0
  33. package/agent/turn-loop.mjs +244 -0
  34. package/agent/write-gate.mjs +103 -6
  35. package/agent-tools/advisor-async.mjs +11 -11
  36. package/agent-tools/advisor-settle.mjs +17 -17
  37. package/agent-tools/advisor.mjs +38 -43
  38. package/agent-tools/async-discard.mjs +44 -1
  39. package/agent-tools/async-settle.mjs +23 -20
  40. package/agent-tools/audit-block.mjs +7 -7
  41. package/agent-tools/bash-async.mjs +281 -0
  42. package/agent-tools/batch-lifecycle.mjs +65 -34
  43. package/agent-tools/batch-paths.mjs +186 -0
  44. package/agent-tools/batch-skeleton.mjs +19 -1
  45. package/agent-tools/batch.mjs +22 -119
  46. package/agent-tools/checkpoint.mjs +121 -0
  47. package/agent-tools/child-permission.mjs +5 -3
  48. package/agent-tools/consult.mjs +58 -58
  49. package/agent-tools/context.mjs +4 -20
  50. package/agent-tools/design-token.mjs +3 -3
  51. package/agent-tools/digest-budget.mjs +1 -1
  52. package/agent-tools/eng.mjs +5 -5
  53. package/agent-tools/escalate-async.mjs +18 -13
  54. package/agent-tools/goal.mjs +3 -10
  55. package/agent-tools/panel-blocks.mjs +3 -3
  56. package/agent-tools/parent-channel.mjs +3 -13
  57. package/agent-tools/plan.mjs +3 -4
  58. package/agent-tools/read-history.mjs +12 -28
  59. package/agent-tools/recent-changes.mjs +4 -5
  60. package/agent-tools/review-facts.mjs +1 -1
  61. package/agent-tools/settings.mjs +47 -20
  62. package/agent-tools/skill.mjs +3 -5
  63. package/agent-tools/spawn-gates.mjs +3 -3
  64. package/agent-tools/subagent-actions-query.mjs +270 -0
  65. package/agent-tools/subagent-actions.mjs +29 -273
  66. package/agent-tools/subagent-async.mjs +36 -31
  67. package/agent-tools/subagent-panel.mjs +38 -9
  68. package/agent-tools/subagent-run.mjs +44 -29
  69. package/agent-tools/subagent-scheduler.mjs +29 -28
  70. package/agent-tools/subagent-spawn.mjs +57 -25
  71. package/agent-tools/subagent.mjs +46 -66
  72. package/agent-tools/task.mjs +3 -9
  73. package/agent-tools/timer.mjs +12 -12
  74. package/agent-tools/verify.mjs +14 -13
  75. package/agent-tools.mjs +0 -1
  76. package/agent.mjs +20 -351
  77. package/attachments.mjs +125 -0
  78. package/auto-think.mjs +1 -1
  79. package/config-presets.mjs +12 -7
  80. package/config-watch.mjs +83 -0
  81. package/config.mjs +17 -4
  82. package/context-degrade.mjs +81 -0
  83. package/context-echo.mjs +88 -0
  84. package/context-push.mjs +51 -0
  85. package/context.mjs +8 -187
  86. package/conventions.mjs +110 -161
  87. package/declaration.mjs +194 -0
  88. package/embedding.mjs +3 -1
  89. package/env-ref.mjs +81 -0
  90. package/explore-distill.mjs +2 -3
  91. package/file-links.mjs +53 -0
  92. package/file-refs.mjs +103 -0
  93. package/generate-title.mjs +28 -4
  94. package/git/checkpoint.mjs +4 -8
  95. package/history-window.mjs +17 -2
  96. package/i18n.mjs +2 -0
  97. package/index-discover.mjs +7 -7
  98. package/ledger-cmd.mjs +34 -126
  99. package/ledger-db.mjs +35 -7
  100. package/ledger-migrate.mjs +299 -0
  101. package/ledger-tools.mjs +188 -0
  102. package/ledger.mjs +11 -5
  103. package/log.mjs +12 -7
  104. package/manifest-discovery.mjs +131 -0
  105. package/manifest-schema.mjs +166 -0
  106. package/manifest.mjs +18 -217
  107. package/mcp.mjs +4 -1
  108. package/memory/code-search.mjs +149 -0
  109. package/memory/code-sync.mjs +89 -230
  110. package/memory/core.mjs +47 -32
  111. package/memory/docs.mjs +63 -262
  112. package/memory/file-list.mjs +89 -0
  113. package/memory/file-walk.mjs +7 -4
  114. package/memory/memory-tool.mjs +229 -0
  115. package/memory/schema.mjs +18 -3
  116. package/memory/sweep.mjs +289 -0
  117. package/memory/sync-tail.mjs +81 -0
  118. package/memory-status.mjs +69 -0
  119. package/memory.mjs +3 -0
  120. package/model-specs.mjs +131 -66
  121. package/notify-policy.mjs +51 -0
  122. package/package.json +1 -1
  123. package/peer-claims.mjs +263 -0
  124. package/peer-domains.mjs +100 -71
  125. package/peer-instances.mjs +9 -10
  126. package/process-probe-exec.mjs +187 -0
  127. package/process-probe.mjs +27 -175
  128. package/prompt-overlays.mjs +11 -10
  129. package/prompts/advisor-design.md +2 -2
  130. package/prompts/advisor-round2.md +1 -1
  131. package/prompts/advisor-round3.md +1 -1
  132. package/prompts/common.md +19 -7
  133. package/prompts/discipline-engineering.md +57 -16
  134. package/prompts/discipline-normal.md +6 -3
  135. package/prompts/persona-coder.md +0 -1
  136. package/prompts/persona-eng-coder.md +1 -5
  137. package/prompts/persona-eng-designer.md +1 -2
  138. package/prompts/persona-engineering.md +41 -11
  139. package/prompts/persona-explore.md +0 -1
  140. package/prompts/persona-normal.md +10 -1
  141. package/prompts/persona-plan.md +3 -4
  142. package/prompts/subagent-base.md +14 -0
  143. package/provider/core.mjs +11 -48
  144. package/provider/list-models.mjs +3 -0
  145. package/provider/responses-request.mjs +237 -0
  146. package/provider/responses.mjs +6 -228
  147. package/provider-flows.mjs +248 -0
  148. package/queued.mjs +90 -0
  149. package/rules.mjs +209 -2
  150. package/session-gc.mjs +67 -6
  151. package/session-guard.mjs +27 -7
  152. package/session-lifecycle.mjs +90 -18
  153. package/session-rename.mjs +5 -1
  154. package/session-slot-claims.mjs +230 -0
  155. package/session-slot-scan.mjs +299 -0
  156. package/session-slot-verify.mjs +304 -0
  157. package/session-slot-write.mjs +74 -8
  158. package/session-slots-manifest.mjs +163 -236
  159. package/session-slots.mjs +99 -71
  160. package/session.mjs +30 -10
  161. package/shell-candidates.mjs +78 -0
  162. package/text-budget.mjs +2 -2
  163. package/think-off.mjs +49 -0
  164. package/token-ttl.mjs +15 -4
  165. package/tool-args.mjs +73 -0
  166. package/tool-docs/advisor.md +9 -0
  167. package/tool-docs/batch.md +1 -0
  168. package/tool-docs/code_search.md +1 -0
  169. package/tool-docs/consult_start.md +5 -0
  170. package/tool-docs/consult_stop.md +3 -0
  171. package/tool-docs/context.md +7 -0
  172. package/tool-docs/doc_search.md +1 -0
  173. package/tool-docs/edit.md +2 -13
  174. package/tool-docs/eng.md +1 -0
  175. package/tool-docs/execute.md +1 -9
  176. package/tool-docs/git.md +25 -51
  177. package/tool-docs/goal.md +6 -0
  178. package/tool-docs/ledger_add.md +1 -0
  179. package/tool-docs/ledger_close.md +1 -0
  180. package/tool-docs/ledger_count.md +1 -0
  181. package/tool-docs/ledger_query.md +1 -0
  182. package/tool-docs/ledger_update.md +1 -0
  183. package/tool-docs/main_history.md +1 -0
  184. package/tool-docs/memory.md +10 -0
  185. package/tool-docs/notify_parent.md +9 -0
  186. package/tool-docs/peer_instances.md +1 -0
  187. package/tool-docs/plan.md +5 -0
  188. package/tool-docs/process.md +7 -3
  189. package/tool-docs/question.md +1 -0
  190. package/tool-docs/read_history.md +7 -0
  191. package/tool-docs/recent_changes.md +1 -0
  192. package/tool-docs/repo_outline.md +1 -0
  193. package/tool-docs/settings.md +6 -0
  194. package/tool-docs/skill.md +5 -0
  195. package/tool-docs/subagent.md +31 -0
  196. package/tool-docs/task.md +7 -0
  197. package/tool-docs/timer.md +6 -0
  198. package/tool-docs/verify.md +1 -0
  199. package/tool-docs/wait_for.md +2 -1
  200. package/tools/bash.mjs +28 -4
  201. package/tools/edit-batch.mjs +10 -11
  202. package/tools/edit-diff.mjs +33 -25
  203. package/tools/exec-run.mjs +102 -4
  204. package/tools/execute.mjs +5 -2
  205. package/tools/file.mjs +11 -12
  206. package/tools/git-run.mjs +1 -1
  207. package/tools/git.mjs +0 -24
  208. package/tools/glob-dialect.mjs +3 -3
  209. package/tools/index.mjs +2 -1
  210. package/tools/linter.mjs +2 -1
  211. package/tools/node-child.mjs +21 -0
  212. package/tools/ops.mjs +50 -13
  213. package/tools/patch.mjs +4 -2
  214. package/tools/repomap.mjs +20 -9
  215. package/tools/search.mjs +2 -2
  216. package/tools/shared.mjs +9 -7
  217. package/tools/web.mjs +5 -2
  218. package/tools/write-path.mjs +1 -1
  219. package/traces/trace-store.mjs +17 -14
  220. package/vision-reader.mjs +76 -0
  221. package/agent-tools/batch-segment.mjs +0 -18
@@ -0,0 +1,187 @@
1
+ /**
2
+ * process-probe-exec.mjs — 探测执行面(#590① 自 `process-probe.mjs` 外提——CORE-UNIFICATION
3
+ * 行 10 预案兑现;语义逐字零变)。
4
+ *
5
+ * 归本档:双超时常量(`ALIVE_EXEC_TIMEOUT_MS` / `CMDLINE_EXEC_TIMEOUT_MS`)· 模块级测试注入缝
6
+ * (`_testImpl` + 两 setter——唯一读取点 = 本档四批量函数,同档免回引 / 免环)· `uniqPids` ·
7
+ * 输出解析族 · `execFileP` · 四批量探测函数(`batchAlive` / `batchAliveAsync` / `probeCmdlines` /
8
+ * `probeCmdlinesAsync`)。
9
+ * 留 `process-probe.mjs`:身份判据 · 三态判据 · 束 API · 单 pid 兼容面 · 清理面适配——该档对
10
+ * 四批量名与缝名 **import + re-export**(导入面逐字零改;`isProcessAlive` 经 import 取用本档
11
+ * `parseTasklistPids`,并自持 `execFileSync` import——win32 分支直调,不设转口)。
12
+ *
13
+ * 成本纪律与两形态语义(同步 / 异步共用同一解析族与同一注入缝)= `process-probe.mjs` 头注
14
+ * (单源——本档不重述)。
15
+ */
16
+ import { execFile, execFileSync } from "node:child_process"
17
+
18
+ const ALIVE_EXEC_TIMEOUT_MS = 10_000
19
+ const CMDLINE_EXEC_TIMEOUT_MS = 15_000
20
+
21
+ // 模块级测试注入缝(default null = 生产实现;测试注入 + finally 恢复——见测试文件)
22
+ let _testImpl = null
23
+
24
+ /** 注入测试实现。aliveFn(pids) → Set<pid>|null;cmdlineFn(pids) → Map<pid,cmdline>|null
25
+ * (两缝均**批量**语义——一次调用拿全量)。返回前值便于测试保存恢复;**未传的槽置 null**(传 `{}` = 两槽同清,等价 reset)。 */
26
+ export function _setProcessProbeTestImpl({ aliveFn = undefined, cmdlineFn = undefined } = {}) {
27
+ const prev = _testImpl
28
+ _testImpl = { aliveFn: aliveFn ?? null, cmdlineFn: cmdlineFn ?? null }
29
+ return prev
30
+ }
31
+
32
+ export function _resetProcessProbeTestImpl() {
33
+ _testImpl = null
34
+ }
35
+
36
+ /** pid 清单归一:去重 + 正整数(两探测共用——空清单调用面零 exec)。 */
37
+ export function uniqPids(pids) {
38
+ return [...new Set(pids.map(Number).filter((n) => Number.isInteger(n) && n > 0))]
39
+ }
40
+
41
+ // ── 输出解析族(同步 / 异步两形态**共用**——单源,防两形态漂移) ─────────────────
42
+ /** Windows tasklist CSV(`"name","pid"…`)→ 全量 PID 集合。 */
43
+ export function parseTasklistPids(out) {
44
+ const alive = new Set()
45
+ for (const line of out.split(/\r?\n/)) {
46
+ const m = line.match(/^"([^"]*)","(\d+)"/)
47
+ if (m) alive.add(Number(m[2]))
48
+ }
49
+ return alive
50
+ }
51
+
52
+ /** Unix `ps -eo pid=` → 全量 PID 集合。 */
53
+ function parsePsPids(out) {
54
+ return new Set(out.split(/\r?\n/).map((l) => Number(l.trim())).filter((n) => Number.isInteger(n)))
55
+ }
56
+
57
+ /** Windows `Get-CimInstance … ConvertTo-Json` → `Map<pid, cmdline>`。 */
58
+ function parseCimCmdlines(out) {
59
+ const rows = JSON.parse(out.trim())
60
+ const map = new Map()
61
+ for (const r of Array.isArray(rows) ? rows : [rows]) {
62
+ if (r && Number.isInteger(r.ProcessId) && typeof r.CommandLine === "string") {
63
+ map.set(Number(r.ProcessId), r.CommandLine)
64
+ }
65
+ }
66
+ return map
67
+ }
68
+
69
+ /** Unix `ps -eo pid=,args=` → `Map<pid, cmdline>`。 */
70
+ function parsePsArgs(out) {
71
+ const map = new Map()
72
+ for (const line of out.split(/\r?\n/)) {
73
+ const m = line.match(/^\s*(\d+)\s+(.*)$/)
74
+ if (m) map.set(Number(m[1]), m[2])
75
+ }
76
+ return map
77
+ }
78
+
79
+ /** execFile → Promise<string>(stdout;exec / 超时失败 ⇒ reject——调用面按「探测失败」降级)。 */
80
+ function execFileP(cmd, args, timeout) {
81
+ return new Promise((resolve, reject) => {
82
+ execFile(cmd, args, { encoding: "utf8", timeout, windowsHide: true }, (err, stdout) => {
83
+ if (err) reject(err); else resolve(stdout)
84
+ })
85
+ })
86
+ }
87
+
88
+ /**
89
+ * 批量判活:单次 tasklist(Windows 全量 CSV)/ ps(Unix)拿全量 PID 集合 → 一次 exec
90
+ * 比对(修复 isProcessAlive 每 pid 一次 execSync 的成本——每回合 N 次 = 贵,探索 §4)。
91
+ * 返回存活 pid 的 Set;exec/解析失败 → null(调用方区分"探测失败"与"全死"——只读面按
92
+ * 无活伴降级;域面(peer-domains 死清理)在 null 时不得执行删除——探测失败 ≠ 死)。
93
+ * **纯存在性**:身份判据不在此层(D-MI11——过滤落点在读面 `peerInstances()` 与清理面
94
+ * `filterDeadOwners`;`peer-domains.mjs` 的域残留清理语义保持零变)。
95
+ */
96
+ export function batchAlive(pids, { timeoutMs = ALIVE_EXEC_TIMEOUT_MS } = {}) {
97
+ const uniq = uniqPids(pids)
98
+ if (uniq.length === 0) return new Set()
99
+ if (_testImpl?.aliveFn) return _testImpl.aliveFn(uniq)
100
+ try {
101
+ if (process.platform === "win32") {
102
+ const output = execFileSync("tasklist", ["/FO", "CSV", "/NH"], {
103
+ encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"],
104
+ })
105
+ const alive = parseTasklistPids(output)
106
+ return new Set(uniq.filter((pid) => alive.has(pid)))
107
+ }
108
+ const output = execFileSync("ps", ["-eo", "pid="], {
109
+ encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"],
110
+ })
111
+ const alive = parsePsPids(output)
112
+ return new Set(uniq.filter((pid) => alive.has(pid)))
113
+ } catch {
114
+ return null // 探测失败(区别于全死)——调用方不得据此执行删除/判死副作用
115
+ }
116
+ }
117
+
118
+ /**
119
+ * 异步对偶(TUI 假死批 · MULTI-INSTANCE-COLLAB §3.1):与 `batchAlive` 同语义(返回集 / null
120
+ * 降级一致),但 `execFile` + Promise 化——**读面**(每回合的 `peerInstances`)不得用同步 exec
121
+ * 占住事件循环。**同一注入缝** `_setProcessProbeTestImpl`(注入值可能是同步函数——经 await
122
+ * 消费);**清理面继续用同步版**(会话起点 / 认领路径,非每回合面——D-MI14 登记)。
123
+ */
124
+ export async function batchAliveAsync(pids) {
125
+ const uniq = uniqPids(pids)
126
+ if (uniq.length === 0) return new Set()
127
+ if (_testImpl?.aliveFn) return _testImpl.aliveFn(uniq)
128
+ try {
129
+ if (process.platform === "win32") {
130
+ const alive = parseTasklistPids(await execFileP("tasklist", ["/FO", "CSV", "/NH"], ALIVE_EXEC_TIMEOUT_MS))
131
+ return new Set(uniq.filter((pid) => alive.has(pid)))
132
+ }
133
+ const alive = parsePsPids(await execFileP("ps", ["-eo", "pid="], ALIVE_EXEC_TIMEOUT_MS))
134
+ return new Set(uniq.filter((pid) => alive.has(pid)))
135
+ } catch {
136
+ return null // 探测失败(区别于全死)——只读面按「无活伴」降级
137
+ }
138
+ }
139
+
140
+ /**
141
+ * 批量 cmdline 探测(决策③ A——一次 exec 拿全部 pid+cmdline):返回 Map<pid, cmdline>
142
+ * 或 null(exec 失败/解析失败)。Windows = 一次 Get-CimInstance(PowerShell);Unix =
143
+ * 一次 ps。仅在 manifest mtime 变化后跑一次(~百 ms 级——设计已接受);清理面同一批量
144
+ * 结果逐条消费(D-MI3 / N-MI3——不做每 pid 一次 exec)。
145
+ */
146
+ export function probeCmdlines(pids, { timeoutMs = CMDLINE_EXEC_TIMEOUT_MS } = {}) {
147
+ const uniq = uniqPids(pids)
148
+ if (uniq.length === 0) return new Map()
149
+ if (_testImpl?.cmdlineFn) return _testImpl.cmdlineFn(uniq)
150
+ try {
151
+ let out
152
+ if (process.platform === "win32") {
153
+ // 一次 Get-CimInstance 拿全表 → node 侧过滤目标 pid(避免 shell 引号注入面)
154
+ out = execFileSync("powershell.exe",
155
+ ["-NoProfile", "-NonInteractive", "-Command",
156
+ "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress"],
157
+ { encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"] })
158
+ return parseCimCmdlines(out)
159
+ }
160
+ out = execFileSync("ps", ["-eo", "pid=,args="], {
161
+ encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"],
162
+ })
163
+ return parsePsArgs(out)
164
+ } catch {
165
+ return null // 探测失败 → 端字段/身份缺省(调用方降级:保守保留)
166
+ }
167
+ }
168
+
169
+ /** 异步对偶(同 `probeCmdlines` 语义——`execFile` + Promise;同一注入缝;读面在用)。 */
170
+ export async function probeCmdlinesAsync(pids) {
171
+ const uniq = uniqPids(pids)
172
+ if (uniq.length === 0) return new Map()
173
+ if (_testImpl?.cmdlineFn) return _testImpl.cmdlineFn(uniq)
174
+ try {
175
+ if (process.platform === "win32") {
176
+ const out = await execFileP("powershell.exe",
177
+ ["-NoProfile", "-NonInteractive", "-Command",
178
+ "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress"],
179
+ CMDLINE_EXEC_TIMEOUT_MS)
180
+ return parseCimCmdlines(out)
181
+ }
182
+ const out = await execFileP("ps", ["-eo", "pid=,args="], CMDLINE_EXEC_TIMEOUT_MS)
183
+ return parsePsArgs(out)
184
+ } catch {
185
+ return null // 探测失败 → 端字段/身份缺省(调用方降级:保守保留)
186
+ }
187
+ }
package/process-probe.mjs CHANGED
@@ -20,11 +20,17 @@
20
20
  * 2 s + 三态)。`filterDeadOwners` = `ownerState` 薄适配(`alive` 三态,缺省 = 未知)。
21
21
  * 方向不对称(D-MI10):探测失败(null)/ 该 pid 缺行 ⇒ **保守保留/不删**——误保留 = 噪音,
22
22
  * 误删活实例 = 破坏存储隔离(双进程同槽),两者代价不同级。
23
+ *
24
+ * 执行面拆分(#590① · CORE-UNIFICATION 行 10 兑现):注入缝 + 解析族 + 四批量探测函数
25
+ * (`batchAlive` / `batchAliveAsync` / `probeCmdlines` / `probeCmdlinesAsync`)+ `uniqPids`
26
+ * 住拆分件 `process-probe-exec.mjs`(迁移账 = 该档头注);本档 import 自用 + re-export
27
+ * 保导入面逐字零改(VSC `session-io.mjs` 与核七消费档不动)。
23
28
  */
24
- import { execFile, execFileSync } from "node:child_process"
25
-
26
- const ALIVE_EXEC_TIMEOUT_MS = 10_000
27
- const CMDLINE_EXEC_TIMEOUT_MS = 15_000
29
+ import { execFileSync } from "node:child_process"
30
+ import {
31
+ batchAlive, batchAliveAsync, probeCmdlines, probeCmdlinesAsync,
32
+ uniqPids, parseTasklistPids,
33
+ } from "./process-probe-exec.mjs"
28
34
 
29
35
  /** 同步束的**紧界**(F-MI7 · SESSION.md §6.2):同步探测阻塞事件循环 ⇒ 单次 exec ≤ 2 s;
30
36
  * 超时 / 失败 ⇒ 未知(三态判据)⇒ 保守保留(D-MI10)。单源常量——调用面不得自行取值。 */
@@ -40,187 +46,30 @@ const VSC_END_RE = /--extensionDevelopmentPath|--type=extensionHost|extensionHos
40
46
  * 「误删方向」——消解路径 = 实测出现时补进本族,单源改点仅此一处)。 */
41
47
  const CLI_ENTRY_RE = /thincoder\.cjs|thincoder\.mjs|thincoder-cli/i
42
48
 
43
- // 模块级测试注入缝(default null = 生产实现;测试注入 + finally 恢复——见测试文件)
44
- let _testImpl = null
45
-
46
- /** 注入测试实现。aliveFn(pids) → Set<pid>|null;cmdlineFn(pids) → Map<pid,cmdline>|null
47
- * (两缝均**批量**语义——一次调用拿全量)。返回前值便于测试保存恢复;**未传的槽置 null**(传 `{}` = 两槽同清,等价 reset)。 */
48
- export function _setProcessProbeTestImpl({ aliveFn = undefined, cmdlineFn = undefined } = {}) {
49
- const prev = _testImpl
50
- _testImpl = { aliveFn: aliveFn ?? null, cmdlineFn: cmdlineFn ?? null }
51
- return prev
52
- }
53
-
54
- export function _resetProcessProbeTestImpl() {
55
- _testImpl = null
56
- }
57
-
58
- /** pid 清单归一:去重 + 正整数(两探测共用——空清单调用面零 exec)。 */
59
- function uniqPids(pids) {
60
- return [...new Set(pids.map(Number).filter((n) => Number.isInteger(n) && n > 0))]
61
- }
62
-
63
- // ── 输出解析族(同步 / 异步两形态**共用**——单源,防两形态漂移) ─────────────────
64
- /** Windows tasklist CSV(`"name","pid"…`)→ 全量 PID 集合。 */
65
- function parseTasklistPids(out) {
66
- const alive = new Set()
67
- for (const line of out.split(/\r?\n/)) {
68
- const m = line.match(/^"([^"]*)","(\d+)"/)
69
- if (m) alive.add(Number(m[2]))
70
- }
71
- return alive
72
- }
73
-
74
- /** Unix `ps -eo pid=` → 全量 PID 集合。 */
75
- function parsePsPids(out) {
76
- return new Set(out.split(/\r?\n/).map((l) => Number(l.trim())).filter((n) => Number.isInteger(n)))
77
- }
78
-
79
- /** Windows `Get-CimInstance … ConvertTo-Json` → `Map<pid, cmdline>`。 */
80
- function parseCimCmdlines(out) {
81
- const rows = JSON.parse(out.trim())
82
- const map = new Map()
83
- for (const r of Array.isArray(rows) ? rows : [rows]) {
84
- if (r && Number.isInteger(r.ProcessId) && typeof r.CommandLine === "string") {
85
- map.set(Number(r.ProcessId), r.CommandLine)
86
- }
87
- }
88
- return map
89
- }
90
-
91
- /** Unix `ps -eo pid=,args=` → `Map<pid, cmdline>`。 */
92
- function parsePsArgs(out) {
93
- const map = new Map()
94
- for (const line of out.split(/\r?\n/)) {
95
- const m = line.match(/^\s*(\d+)\s+(.*)$/)
96
- if (m) map.set(Number(m[1]), m[2])
97
- }
98
- return map
99
- }
100
-
101
- /** execFile → Promise<string>(stdout;exec / 超时失败 ⇒ reject——调用面按「探测失败」降级)。 */
102
- function execFileP(cmd, args, timeout) {
103
- return new Promise((resolve, reject) => {
104
- execFile(cmd, args, { encoding: "utf8", timeout, windowsHide: true }, (err, stdout) => {
105
- if (err) reject(err); else resolve(stdout)
106
- })
107
- })
108
- }
109
-
110
- /**
111
- * 批量判活:单次 tasklist(Windows 全量 CSV)/ ps(Unix)拿全量 PID 集合 → 一次 exec
112
- * 比对(修复 isProcessAlive 每 pid 一次 execSync 的成本——每回合 N 次 = 贵,探索 §4)。
113
- * 返回存活 pid 的 Set;exec/解析失败 → null(调用方区分"探测失败"与"全死"——只读面按
114
- * 无活伴降级;域面(peer-domains 死清理)在 null 时不得执行删除——探测失败 ≠ 死)。
115
- * **纯存在性**:身份判据不在此层(D-MI11——过滤落点在读面 `peerInstances()` 与清理面
116
- * `filterDeadOwners`;`peer-domains.mjs` 的域残留清理语义保持零变)。
117
- */
118
- export function batchAlive(pids, { timeoutMs = ALIVE_EXEC_TIMEOUT_MS } = {}) {
119
- const uniq = uniqPids(pids)
120
- if (uniq.length === 0) return new Set()
121
- if (_testImpl?.aliveFn) return _testImpl.aliveFn(uniq)
122
- try {
123
- if (process.platform === "win32") {
124
- const output = execFileSync("tasklist", ["/FO", "CSV", "/NH"], {
125
- encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"],
126
- })
127
- const alive = parseTasklistPids(output)
128
- return new Set(uniq.filter((pid) => alive.has(pid)))
129
- }
130
- const output = execFileSync("ps", ["-eo", "pid="], {
131
- encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"],
132
- })
133
- const alive = parsePsPids(output)
134
- return new Set(uniq.filter((pid) => alive.has(pid)))
135
- } catch {
136
- return null // 探测失败(区别于全死)——调用方不得据此执行删除/判死副作用
137
- }
138
- }
139
-
140
- /**
141
- * 异步对偶(TUI 假死批 · MULTI-INSTANCE-COLLAB §3.1):与 `batchAlive` 同语义(返回集 / null
142
- * 降级一致),但 `execFile` + Promise 化——**读面**(每回合的 `peerInstances`)不得用同步 exec
143
- * 占住事件循环。**同一注入缝** `_setProcessProbeTestImpl`(注入值可能是同步函数——经 await
144
- * 消费);**清理面继续用同步版**(会话起点 / 认领路径,非每回合面——D-MI14 登记)。
145
- */
146
- export async function batchAliveAsync(pids) {
147
- const uniq = uniqPids(pids)
148
- if (uniq.length === 0) return new Set()
149
- if (_testImpl?.aliveFn) return _testImpl.aliveFn(uniq)
150
- try {
151
- if (process.platform === "win32") {
152
- const alive = parseTasklistPids(await execFileP("tasklist", ["/FO", "CSV", "/NH"], ALIVE_EXEC_TIMEOUT_MS))
153
- return new Set(uniq.filter((pid) => alive.has(pid)))
154
- }
155
- const alive = parsePsPids(await execFileP("ps", ["-eo", "pid="], ALIVE_EXEC_TIMEOUT_MS))
156
- return new Set(uniq.filter((pid) => alive.has(pid)))
157
- } catch {
158
- return null // 探测失败(区别于全死)——只读面按「无活伴」降级
159
- }
160
- }
49
+ /** 桌面端(Electron 主进程)判别标记族(台账 #584 实读定形——`electron .` 启动形态):真机捕获
50
+ * 命令行 = `"…\thincoder-desktop\node_modules\electron\dist\electron.exe" .`——产品树路径段可判
51
+ * (打包件 exe 名同族)。补入本族 = §3.1「标记族假阴性」已知局限的实测消解路径(原族外形态)。
52
+ * #707 扩(2026-09-30 实测 · 台账 #707):桌面 dev 启动形 = `node_modules\electron\dist\electron.exe
53
+ * --remote-debugging-port=9222 --remote-allow-origins=* .`(**相对路径**,无 `thincoder-desktop` 段)
54
+ * ⇒ 原族假阴性(活属主判 dead——误删方向)⇒ 补 `node_modules[\\/]electron[\\/]dist[\\/]` 段;
55
+ * 超集面(他 electron dev 应用)与 VSC 宿主族同向——宁可多留(误保留=噪音/误删=双进程同槽破坏级)。 */
56
+ const DESKTOP_END_RE = /thincoder-desktop|node_modules[\\/]electron[\\/]dist[\\/]/i
161
57
 
162
- /**
163
- * 批量 cmdline 探测(决策③ A——一次 exec 拿全部 pid+cmdline):返回 Map<pid, cmdline>
164
- * 或 null(exec 失败/解析失败)。Windows = 一次 Get-CimInstance(PowerShell);Unix =
165
- * 一次 ps。仅在 manifest mtime 变化后跑一次(~百 ms 级——设计已接受);清理面同一批量
166
- * 结果逐条消费(D-MI3 / N-MI3——不做每 pid 一次 exec)。
167
- */
168
- export function probeCmdlines(pids, { timeoutMs = CMDLINE_EXEC_TIMEOUT_MS } = {}) {
169
- const uniq = uniqPids(pids)
170
- if (uniq.length === 0) return new Map()
171
- if (_testImpl?.cmdlineFn) return _testImpl.cmdlineFn(uniq)
172
- try {
173
- let out
174
- if (process.platform === "win32") {
175
- // 一次 Get-CimInstance 拿全表 → node 侧过滤目标 pid(避免 shell 引号注入面)
176
- out = execFileSync("powershell.exe",
177
- ["-NoProfile", "-NonInteractive", "-Command",
178
- "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress"],
179
- { encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"] })
180
- return parseCimCmdlines(out)
181
- }
182
- out = execFileSync("ps", ["-eo", "pid=,args="], {
183
- encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"],
184
- })
185
- return parsePsArgs(out)
186
- } catch {
187
- return null // 探测失败 → 端字段/身份缺省(调用方降级:保守保留)
188
- }
189
- }
190
-
191
- /** 异步对偶(同 `probeCmdlines` 语义——`execFile` + Promise;同一注入缝;读面在用)。 */
192
- export async function probeCmdlinesAsync(pids) {
193
- const uniq = uniqPids(pids)
194
- if (uniq.length === 0) return new Map()
195
- if (_testImpl?.cmdlineFn) return _testImpl.cmdlineFn(uniq)
196
- try {
197
- if (process.platform === "win32") {
198
- const out = await execFileP("powershell.exe",
199
- ["-NoProfile", "-NonInteractive", "-Command",
200
- "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress"],
201
- CMDLINE_EXEC_TIMEOUT_MS)
202
- return parseCimCmdlines(out)
203
- }
204
- const out = await execFileP("ps", ["-eo", "pid=,args="], CMDLINE_EXEC_TIMEOUT_MS)
205
- return parsePsArgs(out)
206
- } catch {
207
- return null // 探测失败 → 端字段/身份缺省(调用方降级:保守保留)
208
- }
209
- }
210
-
211
- /** 本产品身份判据(单源):命令行命中 CLI 入口族或 VSC 扩展宿主族 ⇒ 本产品进程。
58
+ /** 本产品身份判据(单源):命令行命中 CLI 入口族 ∕ VSC 扩展宿主族 ∕ 桌面端族 ⇒ 本产品进程。
212
59
  * 命令未知(undefined / 空串)⇒ `false`——**调用方须自行区分「未知」与「明确不符」**
213
60
  * (未知 = 保守保留,明确不符 = 剔除/可删;两态判据见 `filterDeadOwners` 与读面落点)。 */
214
61
  export function isProductProc(cmdline) {
215
62
  if (typeof cmdline !== "string" || cmdline.length === 0) return false
216
- return CLI_ENTRY_RE.test(cmdline) || VSC_END_RE.test(cmdline)
63
+ return CLI_ENTRY_RE.test(cmdline) || VSC_END_RE.test(cmdline) || DESKTOP_END_RE.test(cmdline)
217
64
  }
218
65
 
219
- /** cmdline → 端标签:扩展宿主标记 → vscode;其余(node/thincoder CLI)→ cli。
66
+ /** cmdline → 端标签:扩展宿主标记 → vscode;桌面端族 → desktop;其余(node/thincoder CLI)→ cli。
220
67
  * 命令不可得(探测失败 / 缺行)⇒ `undefined`(端字段缺省——既有降级语义)。 */
221
68
  export function classifyEnd(cmdline) {
222
69
  if (typeof cmdline !== "string" || cmdline.length === 0) return undefined
223
- return VSC_END_RE.test(cmdline) ? "vscode" : "cli"
70
+ if (VSC_END_RE.test(cmdline)) return "vscode"
71
+ if (DESKTOP_END_RE.test(cmdline)) return "desktop"
72
+ return "cli"
224
73
  }
225
74
 
226
75
  /**
@@ -313,3 +162,6 @@ export function filterDeadOwners(pid, { alive = undefined, cmdline = undefined }
313
162
  const cmds = typeof cmdline === "string" && cmdline.length > 0 ? new Map([[n, cmdline]]) : null
314
163
  return ownerState(n, { aliveSet, cmds }) === "dead"
315
164
  }
165
+
166
+ // ── 执行面 re-export(拆分件接口——导入面逐字零改:#590①) ──
167
+ export { batchAlive, batchAliveAsync, probeCmdlines, probeCmdlinesAsync, _setProcessProbeTestImpl, _resetProcessProbeTestImpl } from "./process-probe-exec.mjs"
@@ -3,9 +3,9 @@
3
3
  * 施工② G1+G2, 2026-09-10). Slot constants are loaded ONCE (byte-stable, module scope);
4
4
  * assemblePrompt composes them per PROMPT-SYSTEM.md §3.2 装配矩阵 (D1 内联表——设计锚).
5
5
  *
6
- * Slot model (PROMPT-SYSTEM.md §1/§2): persona → common → discipline → [4] other
7
- * (AGENTS/skills ride the existing tail logic). explore/coder/plan reuse PERSONA_NORMAL
8
- * as their persona slot (蓝图 §3.1 同槽位复用——变体差异归人格层覆写; design D1 G1 note).
6
+ * Slot model (PROMPT-SYSTEM.md §1/§2): persona → common → discipline → [4] subagent-base
7
+ * (AGENTS/skills ride the existing tail logic — tail block, no numbered slot). explore/coder/plan
8
+ * reuse PERSONA_NORMAL as their persona slot (蓝图 §3.1 同槽位复用——变体差异归人格层覆写; design D1 G1 note).
9
9
  */
10
10
 
11
11
  import { loadSlot, applyPromptInjections } from "./prompt-files.mjs"
@@ -22,6 +22,7 @@ const SLOT_CONTENTS = {
22
22
  "common.md": loadSlot("common.md"),
23
23
  "discipline-engineering.md": loadSlot("discipline-engineering.md"),
24
24
  "discipline-normal.md": loadSlot("discipline-normal.md"),
25
+ "subagent-base.md": loadSlot("subagent-base.md"),
25
26
  }
26
27
 
27
28
  // ── G1 六件导出(评审 #1 计数口径:人格 2 + 公共 1 + 纪律 2 + consult 自含基底;
@@ -40,11 +41,11 @@ export const CONSULT_BASE = loadSlot("consult-base.md")
40
41
  export const SCENARIO_SLOT_FILES = {
41
42
  engineering: ["persona-engineering.md", "common.md", "discipline-engineering.md"],
42
43
  normal: ["persona-normal.md", "common.md", "discipline-normal.md"],
43
- "eng-coder": ["persona-eng-coder.md", "common.md", "discipline-engineering.md"],
44
- "eng-designer": ["persona-eng-designer.md", "common.md", "discipline-engineering.md"],
45
- explore: ["persona-explore.md", "common.md", "discipline-normal.md"],
46
- coder: ["persona-coder.md", "common.md", "discipline-normal.md"],
47
- plan: ["persona-plan.md", "common.md", "discipline-normal.md"],
44
+ "eng-coder": ["persona-eng-coder.md", "common.md", "discipline-engineering.md", "subagent-base.md"],
45
+ "eng-designer": ["persona-eng-designer.md", "common.md", "discipline-engineering.md", "subagent-base.md"],
46
+ explore: ["persona-explore.md", "common.md", "discipline-normal.md", "subagent-base.md"],
47
+ coder: ["persona-coder.md", "common.md", "discipline-normal.md", "subagent-base.md"],
48
+ plan: ["persona-plan.md", "common.md", "discipline-normal.md", "subagent-base.md"],
48
49
  consult: null, // §3.3 特殊模块——consult-base.md 自含基底,不入主装配链(CONSULT_BASE 直出)
49
50
  }
50
51
 
@@ -55,8 +56,8 @@ export function slotWarning(fileName) {
55
56
 
56
57
  /**
57
58
  * G2: the single prompt-assembly function — table-driven (D1), fixed order
58
- * persona → common → discipline (§3.1 四槽位固定序;[4] AGENTS/skills 由既有尾部
59
- * 逻辑承担). A missing slot file is SKIPPED with a prominent warning (蓝图 §3.4 降级链);
59
+ * persona → common → discipline → [4] subagent-base (§3.1 四槽位固定序;AGENTS/skills
60
+ * 由既有尾部逻辑承担——槽位外尾块,无编号). A missing slot file is SKIPPED with a prominent warning (蓝图 §3.4 降级链);
60
61
  * AGENTS.md missing = silent skip in the caller's existing tail logic. Byte-stable
61
62
  * per scenario (D3): fixed slot contents + fixed order — no timestamps here.
62
63
  *
@@ -1,5 +1,5 @@
1
1
  <!-- slot:special-advisor-design consumers:[advisor(type='design') injection — self-contained, NOT part of the main assembly chain] -->
2
- You are an independent design reviewer for an engineering-mode project. ## Your role (identity — read before the criteria) You are an INDEPENDENT REVIEWER — authority in judgment, not in decisions. 1. **Stance**: you judge the design/code on its own merits against the review criteria. You are not the author, not the implementer, not the editor — you FIND and REPORT; the parent agent (and the user) decides what changes. Do NOT write replacement text or patch code in your findings — the suggestion column stays advisory guidance (the parent agent decides what changes; you evidence and recommend, you do not rewrite). 2. **Evidence discipline**: every factual/behavioral assertion you make MUST be verified from the documents/files in scope (read them, cite file:line) — or explicitly marked `unverified`. NEVER assert "Known behavior…", "I'm confident…", or rely on remembered API semantics when the source is readable in scope — a behavioral question is an EVIDENCE question, not a reasoning question. 3. **Boundary**: your review target = the review-object declaration (type / target / status / reason / exclude) + the documents in the review scope. Do NOT expand it. With no object declaration (legacy calls) your target = the review scope only. Findings that touch something outside this scope (parent-side docs, other modules) go in a trailing "out-of-scope note" — NO severity assigned to them. 4. **Neutrality**: no git diff, no conversation-history archaeology — the state of the files/documents as you read them is the truth. Do not guess author intent. The agent has written a design document and is asking you to review it before any code is written. ## Review Criteria Evaluate the design against these dimensions: 1. **Requirements coverage** — Does the design address every requirement? Are there gaps?
2
+ You are an independent design reviewer for a design review inside an engineering-mode session. ## Your role (identity — read before the criteria) You are an INDEPENDENT REVIEWER — authority in judgment, not in decisions. 1. **Stance**: you judge the design/code on its own merits against the review criteria. You are not the author, not the implementer, not the editor — you FIND and REPORT; the parent agent (and the user) decides what changes. Do NOT write replacement text or patch code in your findings — the suggestion column stays advisory guidance (the parent agent decides what changes; you evidence and recommend, you do not rewrite). 2. **Evidence discipline**: every factual/behavioral assertion you make MUST be verified from the documents/files in scope (read them, cite file:line) — or explicitly marked `unverified`. NEVER assert "Known behavior…", "I'm confident…", or rely on remembered API semantics when the source is readable in scope — a behavioral question is an EVIDENCE question, not a reasoning question. 3. **Boundary**: your review target = the review-object declaration (type / target / status / reason / exclude) + the documents in the review scope. Do NOT expand it. With no object declaration (legacy calls) your target = the review scope only. Findings that touch something outside this scope (parent-side docs, other modules) go in a trailing "out-of-scope note" — NO severity assigned to them. 4. **Neutrality**: no git diff, no conversation-history archaeology — the state of the files/documents as you read them is the truth. Do not guess author intent. The agent has written a design document and is asking you to review it before any code is written. ## Review Criteria Evaluate the design against these dimensions: 1. **Requirements coverage** — Does the design address every requirement? Are there gaps?
3
3
 
4
4
  2. **Feasibility** — Given the project's architecture and constraints, can this design be implemented? Are there obvious blockers?
5
5
  3. **Methodology compliance** — Does it follow the project's document norms and the 4-step workflow?
@@ -26,7 +26,7 @@ You are an independent design reviewer for an engineering-mode project. ## Your
26
26
  - Do NOT run git diff or look for code changes — there are none at this stage.
27
27
 
28
28
  ## 批次档 §3 落档(仅设计评审——工具已挂载时)
29
- 设计评审专用(**仅当本评审为设计评审、且工具面里已挂载 `batch`(过渡别名 `batch_segment`)时**——代码评审无此工具,本节不适用):在报告之外,用 `batch({action:"append", segment:"§3", text})` 把本轮**发现表 + VERDICT + 计数逐字**写进批次档 §3(不给路径参数;工具自带 `### 轮次 N(评审子代理)` 来源戳,勿自写标题)。
29
+ 设计评审专用(**仅当本评审为设计评审、且工具面里已挂载 `batch` 时**——代码评审无此工具,本节不适用):在报告之外,用 `batch({action:"append", segment:"§3", text})` 把本轮**发现表 + VERDICT + 计数逐字**写进批次档 §3(不给路径参数;工具自带 `### 轮次 N(评审子代理)` 来源戳,勿自写标题)。
30
30
  写不进去(被拒/失败)→ 报告里明说「§3 未写入」——不得静默略过,也不得假装写过(父侧代写必须打标)。
31
31
 
32
32
  ## Judgment Rules (apply directly — do not re-derive) Apply each rule to the extent it matches the review type: design review — doc-state rules (R1, R7a-e) apply; code review — all rules apply. R1 Doc contradiction / state inconsistency → 🟡 (report-and-fix by the parent doc layer — NOT 🔴; exception: the same mechanism described differently in two places = Document ownership 🔴 — keep this convention — do not downgrade)
@@ -27,7 +27,7 @@ You have a budget of 15 tool rounds (chat turns). Hard mechanical cap: 100 round
27
27
  - Stop calling tools once you are ready to produce the review table.
28
28
 
29
29
  ## 批次档 §3 落档(仅设计评审——工具已挂载时)
30
- 设计评审专用(**仅当本评审为设计评审、且工具面里已挂载 `batch`(过渡别名 `batch_segment`)时**——代码评审无此工具,本节不适用):在报告之外,用 `batch({action:"append", segment:"§3", text})` 把本轮**发现表 + VERDICT + 计数逐字**写进批次档 §3(不给路径参数;工具自带 `### 轮次 N(评审子代理)` 来源戳,勿自写标题)。
30
+ 设计评审专用(**仅当本评审为设计评审、且工具面里已挂载 `batch` 时**——代码评审无此工具,本节不适用):在报告之外,用 `batch({action:"append", segment:"§3", text})` 把本轮**发现表 + VERDICT + 计数逐字**写进批次档 §3(不给路径参数;工具自带 `### 轮次 N(评审子代理)` 来源戳,勿自写标题)。
31
31
  写不进去(被拒/失败)→ 报告里明说「§3 未写入」——不得静默略过,也不得假装写过(父侧代写必须打标)。
32
32
 
33
33
  ## Judgment Rules (apply directly — do not re-derive) Apply each rule to the extent it matches the review type: design review — doc-state rules (R1, R7a-e) apply; code review — all rules apply. R1 Doc contradiction / state inconsistency → 🟡 (report-and-fix by the parent doc layer — NOT 🔴; exception: the same mechanism described differently in two places = Document ownership 🔴 — keep this convention — do not downgrade)
@@ -23,7 +23,7 @@ You have a budget of 15 tool rounds (chat turns). Hard mechanical cap: 100 round
23
23
  - Stop calling tools once you are ready to produce the review table.
24
24
 
25
25
  ## 批次档 §3 落档(仅设计评审——工具已挂载时)
26
- 设计评审专用(**仅当本评审为设计评审、且工具面里已挂载 `batch`(过渡别名 `batch_segment`)时**——代码评审无此工具,本节不适用):在报告之外,用 `batch({action:"append", segment:"§3", text})` 把本轮**发现表 + VERDICT + 计数逐字**写进批次档 §3(不给路径参数;工具自带 `### 轮次 N(评审子代理)` 来源戳,勿自写标题)。
26
+ 设计评审专用(**仅当本评审为设计评审、且工具面里已挂载 `batch` 时**——代码评审无此工具,本节不适用):在报告之外,用 `batch({action:"append", segment:"§3", text})` 把本轮**发现表 + VERDICT + 计数逐字**写进批次档 §3(不给路径参数;工具自带 `### 轮次 N(评审子代理)` 来源戳,勿自写标题)。
27
27
  写不进去(被拒/失败)→ 报告里明说「§3 未写入」——不得静默略过,也不得假装写过(父侧代写必须打标)。
28
28
 
29
29
  ## Judgment Rules (apply directly — do not re-derive) Apply each rule to the extent it matches the review type: design review — doc-state rules (R1, R7a-e) apply; code review — all rules apply. R1 Doc contradiction / state inconsistency → 🟡 (report-and-fix by the parent doc layer — NOT 🔴; exception: the same mechanism described differently in two places = Document ownership 🔴 — keep this convention — do not downgrade)
package/prompts/common.md CHANGED
@@ -4,6 +4,7 @@
4
4
  Reply, reason, and ask in the user's language. If they switch languages mid-session, switch with them — this applies to your replies, thinking, progress notes, and questions.
5
5
  Keep code, commands, identifiers, file paths, and technical terms in their original form.
6
6
  Artifacts written to the repository (comments, commit messages, docs) follow the project's conventions, not the conversation language.
7
+ **Speak plainly**: replies and reports to the user (for a subagent, the parent is the user) must be **readable and repeatable back** — **internal terms / codenames / jargon must not be used bare** (follow each with a one-line plain explanation); ground complex mechanisms in plain words first, then give precise details as needed. Docs / code / review tables follow their own conventions.
7
8
 
8
9
  ## 人机分工(Who you are)
9
10
  Programming is collaborative labor between you and the human.
@@ -29,6 +30,7 @@ Every factual/behavioral assertion you make MUST be verified from the code/docs
29
30
  — read them, cite `file:line` — or explicitly marked `unverified`.
30
31
  NEVER assert "Known behavior…" or "I'm confident…", and never rely on remembered API semantics
31
32
  when the source is readable — a behavioral question is an EVIDENCE question, not a reasoning question.
33
+ **Take evidence only for the assertions you are about to make**: facts the task book or context already gave you are cited as-is — no re-reading to verify them; do not pre-read unrelated code (no surveying forever "just to be safe").
32
34
 
33
35
  ## 文档写作纪律(Document writing discipline — semantic merge, no script ghost-writing)
34
36
  - **NEVER batch-rewrite documents with scripts/programs**: scripted section splicing, regex bulk replacement, whole-file appending, "verbatim porting" via script — all count. Every document content change must be made by YOU, reading each spot, understanding the semantics, writing it yourself. (Twice, scripted merges/moves of documents were rejected by the user on the spot — lost semantics costs more than the effort saved.)
@@ -48,13 +50,19 @@ Conflict, gap, can't-do — stop and report; never silently adapt, never silentl
48
50
 
49
51
  ## 上行通道(Upstream channel — subagents and their parent)
50
52
 
51
- A subagent has a channel to its parent for decision-grade questions — the `notify_parent` tool. The parent is not a
52
- user: it cannot confirm anything and it may be busy. Pass every message through this filter first:
53
+ A subagent has a channel to its parent for decision-grade questions — the `notify_parent` tool. The parent is not a confirmation gate: it cannot approve anything, and it may be busy.
54
+ Pass every message through this filter first:
53
55
 
54
56
  - **Ask only when both hold**: (1) the answer changes your next step, and (2) the answer cannot be found in the
55
57
  materials you can read (task book, design doc, repo code/docs). Otherwise decide yourself and write the call into your report.
56
58
  - **In scope**: a stated premise the facts contradict; two requirements that conflict and you cannot arbitrate;
57
- whether an action is inside your task domain; a choice that would waste work already done.
59
+ whether an action is inside your task domain; a choice that would waste work already done;
60
+ **two documents (requirements / design / batch record) describing the same mechanism differently ⇒ judged the same as a conflict**:
61
+ lay out both sides' `file:line` + your leaning; never pick one and implement it yourself.
62
+ - **A conflicting requirement ⇒ ask at once — never keep weighing**: two requirements that conflict ⇒ **that IS "cannot arbitrate"** —
63
+ **send an `ask` (`notify_parent`) at once** — one line naming both sides of the conflict and your leaning;
64
+ **never settle it by picking a side yourself**; **never keep weighing it, never "do one round first and then see"**;
65
+ **never defer it to your final report**.
58
66
  - **Out of scope**: naming / implementation / structure / wording details; anything a read or a command answers;
59
67
  a trade-off the task book already states; reassurance-seeking.
60
68
  - **Non-blocking**: send it and keep working on the unaffected parts — the affected part stays pending until a reply
@@ -101,6 +109,7 @@ Batch independent read-only tool calls into a single reply (they run concurrentl
101
109
  | `bash` | package-manager/CLI subprocesses, servers, TTY programs, one-off pipelines no dedicated tool expresses | see table — dedicated tools first |
102
110
  | `git` | ALL git operations | `git` in bash |
103
111
  | `process` / `get_current_time` / `wait_for` | list processes / current time / condition waits | `tasklist`/`ps`, `date`, `sleep` hacks |
112
+ | `timer` | set a thinking-budget reminder — non-blocking; a system reminder lands at the deadline | `sleep` hacks / pushing work to later turns |
104
113
  | `verify` | pre-completion gate (you declare verification.status; it gates mechanically — it does not run checks) | expecting it to run your tests |
105
114
  | `memory` | long-term memory (search/put/list/delete/clear) | session notes |
106
115
  | `fetch` / `websearch` / MCP search | fetch a URL (explicit proxy) / Bing fallback / technical lookups primary | `curl` scraping |
@@ -148,11 +157,14 @@ Batch independent read-only tool calls into a single reply (they run concurrentl
148
157
  **Six states**: 待讨论 → 待设计 → 在途 → 待核销 (the four **unsettled states**); **已核销 / 已废弃 = archive states** (soft delete — settled entries leave the unsettled surface).
149
158
  **Who reads / writes**: **reads = every role** (`ledger_query` / `ledger_count`; `cwd` defaults to the session project root — pass an absolute path to touch another project); **writes = the main agent only** (subagent tool assemblies carry no write command).
150
159
  **Subagent view**: the ledger is your **input** (this batch's entries come with the spawn), **not your write surface** — new findings go back to your parent, which books them.
160
+ **A note is not a ledger**: an **unresolved** gap / difference / limitation must not rest in prose, comments or inline notes — fix it, surface it upward, or get it booked, so it lands on a **visible todo surface**; 「登记」-style in-doc registration is valid **only for ruled items** (judgment + reasons + resolution window).
151
161
  **Anchors & detail**: entries hang on **pointers** (requirement-doc section / batch-record section / evidence line).
152
162
 
153
- ## 批次档常识(Batch record — the carrier of engineering-mode task flow)
154
- **What it is**: the **batch record** = the **carrier** of an engineering-mode task (the single file threading one implementation round from start to closeout); the path takes the form `docs/batches/<batch>-<topic>.md` — the actual location is the `batchDoc` passed at spawn, never hard-coded.
163
+ ## 批次档常识(Batch record — the carrier of task flow)
164
+ **What it is**: the **batch record** = the **carrier** of task flow (the single file threading one implementation round from start to closeout); the path takes the form `docs/batches/<batch>-<topic>.md` — the actual location is the `batchDoc` passed at spawn, never hard-coded.
165
+ **Mode scope**: the documentation system is the same across all modes; the difference from engineering mode = whether strong process constraints apply.
155
166
  **Six-segment map (one segment, one author)**: §1 discussion = the main agent · §2 batch task & design = eng-designer · §3 design review findings = the review subagent (advisor) · §4 user approval = the main agent · §5 implementation record = eng-coder · §6 verification & closeout = the main agent (the parent, as seen by subagents).
156
- **Writing means**: the `batch` tool (transition alias `batch_segment` — same append executor) — **no path parameter** (the target record is bound to you at spawn); **the segment number follows from your identity**; if the write does not land ⇒ say "§× 未写入" plainly in your report.
167
+ **Writing means**: the `batch` tool — **no path parameter** (the target record is bound to you at spawn); **the segment number follows from your identity**; if the write does not land ⇒ say "§× 未写入" plainly in your report.
157
168
  **The task book itself**: `batchDoc` = the batch-record path = **your task book** (mandatory on every eng-designer / eng-coder spawn; **unreadable ⇒ refused** — do not execute, bounce it back).
158
- **Structure authority**: segment structure / gates / lifecycle are defined by the project's own batch-record mechanism (this section gives the map only — no mechanism restatement).
169
+ **Structure authority**: segment structure / gates / lifecycle are defined by the batch-record mechanism (this section gives the map only — no mechanism restatement).
170
+ **Per repo, its own record**: batch records live in their own repo — the base root resolves from the session anchor's project declaration; **a cross-repo create is refused — that is the correct behavior** (never work around it); a task spanning several repos ⇒ **each repo gets its own record and its own batch**.