@thincoder/core 0.9.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 (203) hide show
  1. package/abort-provenance.mjs +116 -0
  2. package/advisor/citations.mjs +139 -0
  3. package/advisor/compaction.mjs +174 -0
  4. package/advisor/convergence.mjs +80 -0
  5. package/advisor/history.mjs +77 -0
  6. package/advisor/loop.mjs +288 -0
  7. package/advisor/messages.mjs +299 -0
  8. package/advisor/notice.mjs +141 -0
  9. package/advisor/project-context.mjs +197 -0
  10. package/advisor/repos.mjs +150 -0
  11. package/advisor/run.mjs +190 -0
  12. package/advisor/truncate.mjs +57 -0
  13. package/advisor.mjs +281 -0
  14. package/agent/child-marks.mjs +24 -0
  15. package/agent/completion.mjs +145 -0
  16. package/agent/dispatch.mjs +493 -0
  17. package/agent/family-tools.mjs +174 -0
  18. package/agent/helpers.mjs +412 -0
  19. package/agent/post-turn.mjs +70 -0
  20. package/agent/record-results.mjs +174 -0
  21. package/agent/relay-prefix.mjs +39 -0
  22. package/agent/run-stages.mjs +244 -0
  23. package/agent/setup-reminders.mjs +199 -0
  24. package/agent/setup.mjs +234 -0
  25. package/agent/spawn-child.mjs +258 -0
  26. package/agent/suspension.mjs +240 -0
  27. package/agent/write-gate.mjs +87 -0
  28. package/agent-tools/advisor-async.mjs +481 -0
  29. package/agent-tools/advisor-settle.mjs +240 -0
  30. package/agent-tools/advisor.mjs +280 -0
  31. package/agent-tools/async-discard.mjs +143 -0
  32. package/agent-tools/async-settle.mjs +299 -0
  33. package/agent-tools/batch-segment.mjs +265 -0
  34. package/agent-tools/child-permission.mjs +45 -0
  35. package/agent-tools/consult.mjs +471 -0
  36. package/agent-tools/design-token.mjs +117 -0
  37. package/agent-tools/digest-budget.mjs +76 -0
  38. package/agent-tools/eng.mjs +102 -0
  39. package/agent-tools/escalate-async.mjs +302 -0
  40. package/agent-tools/goal.mjs +119 -0
  41. package/agent-tools/panel-blocks.mjs +24 -0
  42. package/agent-tools/parent-channel.mjs +231 -0
  43. package/agent-tools/plan.mjs +86 -0
  44. package/agent-tools/read-history.mjs +309 -0
  45. package/agent-tools/recent-changes.mjs +24 -0
  46. package/agent-tools/review-facts.mjs +31 -0
  47. package/agent-tools/settings.mjs +268 -0
  48. package/agent-tools/skill.mjs +63 -0
  49. package/agent-tools/spawn-gates.mjs +109 -0
  50. package/agent-tools/subagent-actions.mjs +496 -0
  51. package/agent-tools/subagent-async.mjs +456 -0
  52. package/agent-tools/subagent-panel.mjs +160 -0
  53. package/agent-tools/subagent-run.mjs +208 -0
  54. package/agent-tools/subagent-scheduler.mjs +446 -0
  55. package/agent-tools/subagent-spawn.mjs +478 -0
  56. package/agent-tools/subagent.mjs +419 -0
  57. package/agent-tools/task.mjs +87 -0
  58. package/agent-tools/timer.mjs +46 -0
  59. package/agent-tools/verify.mjs +295 -0
  60. package/agent-tools.mjs +23 -0
  61. package/agent.mjs +430 -0
  62. package/auto-think.mjs +115 -0
  63. package/compress-form.mjs +24 -0
  64. package/config-io.mjs +277 -0
  65. package/config-migrate.mjs +178 -0
  66. package/config-presets.mjs +49 -0
  67. package/config.mjs +419 -0
  68. package/context.mjs +495 -0
  69. package/conventions.mjs +223 -0
  70. package/embedding.mjs +120 -0
  71. package/escape.mjs +152 -0
  72. package/expand-home.mjs +16 -0
  73. package/explore-distill.mjs +152 -0
  74. package/generate-title.mjs +123 -0
  75. package/git/checkpoint.mjs +448 -0
  76. package/git/gitmem.mjs +100 -0
  77. package/history-window.mjs +179 -0
  78. package/hooks.mjs +108 -0
  79. package/i18n.mjs +106 -0
  80. package/index-bin.mjs +48 -0
  81. package/index-discover.mjs +176 -0
  82. package/ledger-cmd.mjs +209 -0
  83. package/ledger-db.mjs +86 -0
  84. package/ledger-surface.mjs +76 -0
  85. package/ledger.mjs +202 -0
  86. package/log.mjs +195 -0
  87. package/manifest.mjs +338 -0
  88. package/markdown.mjs +106 -0
  89. package/mcp/helpers.mjs +51 -0
  90. package/mcp/transport-http.mjs +248 -0
  91. package/mcp/transport-stdio.mjs +140 -0
  92. package/mcp/transport-ws.mjs +122 -0
  93. package/mcp.mjs +295 -0
  94. package/memory/code-index.mjs +219 -0
  95. package/memory/code-sync.mjs +427 -0
  96. package/memory/core.mjs +318 -0
  97. package/memory/delete.mjs +242 -0
  98. package/memory/docs.mjs +431 -0
  99. package/memory/file-walk.mjs +109 -0
  100. package/memory/origin.mjs +24 -0
  101. package/memory/scan.mjs +177 -0
  102. package/memory/schema.mjs +460 -0
  103. package/memory.mjs +21 -0
  104. package/model-ref.mjs +66 -0
  105. package/model-specs.mjs +277 -0
  106. package/package.json +32 -0
  107. package/peer-domains.mjs +265 -0
  108. package/peer-instances.mjs +178 -0
  109. package/permission.mjs +79 -0
  110. package/process-probe.mjs +315 -0
  111. package/prompt-files.mjs +113 -0
  112. package/prompt-overlays.mjs +78 -0
  113. package/prompts/advisor-design.md +43 -0
  114. package/prompts/advisor-round1.md +41 -0
  115. package/prompts/advisor-round2.md +46 -0
  116. package/prompts/advisor-round3.md +42 -0
  117. package/prompts/common.md +158 -0
  118. package/prompts/consult-base.md +19 -0
  119. package/prompts/discipline-engineering.md +123 -0
  120. package/prompts/discipline-normal.md +206 -0
  121. package/prompts/persona-coder.md +21 -0
  122. package/prompts/persona-eng-coder.md +41 -0
  123. package/prompts/persona-eng-designer.md +80 -0
  124. package/prompts/persona-engineering.md +160 -0
  125. package/prompts/persona-explore.md +15 -0
  126. package/prompts/persona-normal.md +35 -0
  127. package/prompts/persona-plan.md +27 -0
  128. package/provider/anthropic.mjs +225 -0
  129. package/provider/core.mjs +491 -0
  130. package/provider/errors.mjs +101 -0
  131. package/provider/google.mjs +257 -0
  132. package/provider/index.mjs +7 -0
  133. package/provider/list-models.mjs +163 -0
  134. package/provider/normalize.mjs +81 -0
  135. package/provider/rate.mjs +168 -0
  136. package/provider/responses.mjs +495 -0
  137. package/provider/retry.mjs +88 -0
  138. package/provider/sse.mjs +264 -0
  139. package/provider/wait-status.mjs +59 -0
  140. package/proxy.mjs +274 -0
  141. package/rules.mjs +53 -0
  142. package/session-gc.mjs +248 -0
  143. package/session-guard.mjs +59 -0
  144. package/session-lifecycle.mjs +305 -0
  145. package/session-migrate.mjs +48 -0
  146. package/session-rename.mjs +38 -0
  147. package/session-segments.mjs +99 -0
  148. package/session-slot-write.mjs +168 -0
  149. package/session-slots-manifest.mjs +264 -0
  150. package/session-slots.mjs +298 -0
  151. package/session-store.mjs +441 -0
  152. package/session.mjs +244 -0
  153. package/skills.mjs +234 -0
  154. package/text-budget.mjs +79 -0
  155. package/token-ttl.mjs +285 -0
  156. package/tool-docs/apply_patch.md +15 -0
  157. package/tool-docs/bash.md +38 -0
  158. package/tool-docs/delete.md +13 -0
  159. package/tool-docs/edit.md +30 -0
  160. package/tool-docs/execute.md +21 -0
  161. package/tool-docs/fetch.md +12 -0
  162. package/tool-docs/file_ops.md +17 -0
  163. package/tool-docs/get_current_time.md +8 -0
  164. package/tool-docs/git.md +54 -0
  165. package/tool-docs/glob.md +11 -0
  166. package/tool-docs/grep.md +19 -0
  167. package/tool-docs/hashline_edit.md +14 -0
  168. package/tool-docs/insert_after.md +15 -0
  169. package/tool-docs/lint.md +10 -0
  170. package/tool-docs/ls.md +12 -0
  171. package/tool-docs/lsp.md +10 -0
  172. package/tool-docs/process.md +10 -0
  173. package/tool-docs/question.md +16 -0
  174. package/tool-docs/read.md +20 -0
  175. package/tool-docs/read_image.md +8 -0
  176. package/tool-docs/tree.md +14 -0
  177. package/tool-docs/wait_for.md +22 -0
  178. package/tool-docs/websearch.md +16 -0
  179. package/tool-docs/write.md +11 -0
  180. package/tools/bash.mjs +276 -0
  181. package/tools/edit-batch.mjs +204 -0
  182. package/tools/edit-diff.mjs +388 -0
  183. package/tools/exec-run.mjs +43 -0
  184. package/tools/execute.mjs +243 -0
  185. package/tools/file.mjs +464 -0
  186. package/tools/git-checkpoint.mjs +143 -0
  187. package/tools/git-ext.mjs +173 -0
  188. package/tools/git.mjs +415 -0
  189. package/tools/glob-dialect.mjs +130 -0
  190. package/tools/index.mjs +76 -0
  191. package/tools/linter.mjs +120 -0
  192. package/tools/lsp.mjs +335 -0
  193. package/tools/ops.mjs +293 -0
  194. package/tools/patch.mjs +290 -0
  195. package/tools/question.mjs +26 -0
  196. package/tools/repomap.mjs +314 -0
  197. package/tools/search.mjs +248 -0
  198. package/tools/shared.mjs +467 -0
  199. package/tools/tree.mjs +81 -0
  200. package/tools/web.mjs +224 -0
  201. package/tools/write-path.mjs +191 -0
  202. package/traces/trace-store.mjs +303 -0
  203. package/undo-stack.mjs +47 -0
@@ -0,0 +1,315 @@
1
+ /**
2
+ * process-probe.mjs — 进程探测族 + 本产品身份判据(**判据单源**——
3
+ * MULTI-INSTANCE-COLLAB §3.1 F-MI6 / N-MI6 · D-MI9–D-MI12;批 1 CORE-DEFECT-FIXES)。
4
+ *
5
+ * 两个消费面共用本档(零第二套标记正则——V3):
6
+ * - **读面** `peer-instances.mjs` `peerInstances()`:存在性判活(`batchAlive`)× 命令行身份
7
+ * 复核(`isProductProc`)⇒ 端字段(`classifyEnd`);
8
+ * - **清理面** `session-slots.mjs` `cleanDeadOwners()`:pid 存在性之外加身份复核
9
+ * (`filterDeadOwners`——pid 复用 ⇒ 陈旧属主条目可删;判据面 = SESSION.md §6.2)。
10
+ *
11
+ * 成本纪律(D-MI3 / N-MI3):判活与命令行探测**各一次 exec 拿全量**——本档只提供批量形态,
12
+ * 调用面逐条消费同一批量结果,**不做每 pid 一次 exec**(注入缝 `cmdlineFn(pids)` 即此批量语义)。
13
+ * 每族两形态(TUI 假死批 2026-09-18):**同步**(`execFileSync`——清理面 `cleanDeadOwners` 在用,
14
+ * 非每回合面,D-MI14)与**异步**(`execFile` + Promise——**读面** `peerInstances` 每回合调用,
15
+ * 不得占住事件循环);两形态**共用同一解析族与同一注入缝**,语义(返回集 / null 降级)逐条对齐。
16
+ *
17
+ * 束 API(init-block 批 · F-MI7):清理 / 认领 / 恢复四面**零自有探测**——入口一次
18
+ * `probeOwnersSync` / `probeOwnersAsync` 拿束(≤1 判活 + ≤1 cmdline),判据经 `ownerState`
19
+ * 三态(`"dead" | "alive" | "unknown"`)逐条查表;`isProcessAlive` = 单 pid 兼容面(有界
20
+ * 2 s + 三态)。`filterDeadOwners` = `ownerState` 薄适配(`alive` 三态,缺省 = 未知)。
21
+ * 方向不对称(D-MI10):探测失败(null)/ 该 pid 缺行 ⇒ **保守保留/不删**——误保留 = 噪音,
22
+ * 误删活实例 = 破坏存储隔离(双进程同槽),两者代价不同级。
23
+ */
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
28
+
29
+ /** 同步束的**紧界**(F-MI7 · SESSION.md §6.2):同步探测阻塞事件循环 ⇒ 单次 exec ≤ 2 s;
30
+ * 超时 / 失败 ⇒ 未知(三态判据)⇒ 保守保留(D-MI10)。单源常量——调用面不得自行取值。 */
31
+ export const SYNC_PROBE_MS = 2000
32
+
33
+ /** VS Code 扩展宿主判别标记(决策③ A——cmdline 探测):扩展宿主进程 argv 必带其一
34
+ * (Windows:Code.exe --type=extensionHost / --extensionDevelopmentPath;Unix 同)。 */
35
+ const VSC_END_RE = /--extensionDevelopmentPath|--type=extensionHost|extensionHostProcess/i
36
+
37
+ /** 本产品 CLI 入口标记族(D-MI9):命令行含入口路径段即认本产品——启动形态多
38
+ * (`node bin/thincoder.cjs` / `--inspect` / 卷路径大小写),故用「族」而非全等;
39
+ * 缺该段的自持入口形态(包装器)属已登记已知局限(MULTI-INSTANCE-COLLAB §3.1
40
+ * 「误删方向」——消解路径 = 实测出现时补进本族,单源改点仅此一处)。 */
41
+ const CLI_ENTRY_RE = /thincoder\.cjs|thincoder\.mjs|thincoder-cli/i
42
+
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
+ }
161
+
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 扩展宿主族 ⇒ 本产品进程。
212
+ * 命令未知(undefined / 空串)⇒ `false`——**调用方须自行区分「未知」与「明确不符」**
213
+ * (未知 = 保守保留,明确不符 = 剔除/可删;两态判据见 `filterDeadOwners` 与读面落点)。 */
214
+ export function isProductProc(cmdline) {
215
+ if (typeof cmdline !== "string" || cmdline.length === 0) return false
216
+ return CLI_ENTRY_RE.test(cmdline) || VSC_END_RE.test(cmdline)
217
+ }
218
+
219
+ /** cmdline → 端标签:扩展宿主标记 → vscode;其余(node/thincoder CLI)→ cli。
220
+ * 命令不可得(探测失败 / 缺行)⇒ `undefined`(端字段缺省——既有降级语义)。 */
221
+ export function classifyEnd(cmdline) {
222
+ if (typeof cmdline !== "string" || cmdline.length === 0) return undefined
223
+ return VSC_END_RE.test(cmdline) ? "vscode" : "cli"
224
+ }
225
+
226
+ /**
227
+ * 三态判据(**单源** · F-MI7 · SESSION.md §6.2):`ownerState(pid, { aliveSet, cmds })`。
228
+ * 顺序即语义(判据面唯一——调用面**不得**复刻本判据):
229
+ * ① `aliveSet == null` ⇒ `"unknown"`(**探测失败 ≠ 全死**——D-MI10);
230
+ * ② pid 不在存活集 ⇒ `"dead"`(明确不存在);
231
+ * ③ 存活但命令行缺行(cmdline 探测失败 / 未发)⇒ `"unknown"`(保守保留);
232
+ * ④ 命令行明确可得且非本产品 ⇒ `"dead"`(pid 复用——D-MI11);
233
+ * ⑤ 命令行命中本产品标记族 ⇒ `"alive"`。
234
+ * 方向不对称:`"unknown"` ⇒ 不认领 / 不判死 / 不删(保守保留)。
235
+ */
236
+ export function ownerState(pid, { aliveSet = null, cmds = null } = {}) {
237
+ if (!aliveSet) return "unknown"
238
+ const n = Number(pid)
239
+ if (!n || !aliveSet.has(n)) return "dead"
240
+ const cmdline = cmds?.get?.(n)
241
+ if (typeof cmdline !== "string" || cmdline.length === 0) return "unknown"
242
+ return isProductProc(cmdline) ? "alive" : "dead"
243
+ }
244
+
245
+ /** 探测束(同步 · F-MI7):一次调用 = 全量待判 pid 的判活 +(必要时的)命令行批量结果。
246
+ * `pids` 空 ⇒ **零 exec 早退**(`aliveSet` null = 未探测——判据层按未知保守处理)。
247
+ * ≤1 批量判活 + ≤1 批量 cmdline(D-MI3 / N-MI3——零逐 pid exec;**cmdline 仅对存活 pid**
248
+ * 发——死者判据只需存在性。**不得按本进程 pid 排除**:属主条目 pid == 本进程 pid 而会话 id
249
+ * 不同 = pid 复用场景,身份复核(D-MI11)正是靠 cmdline 区分——排除即退化为「未知 ⇒ 保留」。
250
+ * 同步形态以 `SYNC_PROBE_MS` 紧界(超时 ⇒ `aliveSet` null ⇒ 未知)。
251
+ * cmdline **仅存在存活属主时**发(全死 ⇒ 省发)——束内自决,调用面零探测零判据。 */
252
+ export function probeOwnersSync(pids) {
253
+ const uniq = uniqPids(Array.isArray(pids) ? pids : [])
254
+ if (uniq.length === 0) return { aliveSet: null, cmds: null }
255
+ const aliveSet = batchAlive(uniq, { timeoutMs: SYNC_PROBE_MS })
256
+ let cmds = null
257
+ if (aliveSet) {
258
+ const alivePids = uniq.filter((pid) => aliveSet.has(pid))
259
+ if (alivePids.length > 0) cmds = probeCmdlines(alivePids, { timeoutMs: SYNC_PROBE_MS })
260
+ }
261
+ return { aliveSet, cmds }
262
+ }
263
+
264
+ /** 异步对偶(同语义 / 同一注入缝):恢复 / 异步清理路径用——不阻塞事件循环,
265
+ * 沿用 10 s / 15 s 量级(`batchAliveAsync` / `probeCmdlinesAsync`)。 */
266
+ export async function probeOwnersAsync(pids) {
267
+ const uniq = uniqPids(Array.isArray(pids) ? pids : [])
268
+ if (uniq.length === 0) return { aliveSet: null, cmds: null }
269
+ const aliveSet = await batchAliveAsync(uniq)
270
+ let cmds = null
271
+ if (aliveSet) {
272
+ const alivePids = uniq.filter((pid) => aliveSet.has(pid)) // 同同步版:不按本进程 pid 排除
273
+ if (alivePids.length > 0) cmds = await probeCmdlinesAsync(alivePids)
274
+ }
275
+ return { aliveSet, cmds }
276
+ }
277
+
278
+ /** 单 pid 兼容面(F-MI7——原住 `session-slots.mjs`,本批外提判据单源):**有界同步** ——
279
+ * 单次 exec ≤ `SYNC_PROBE_MS`;**三态**:`true` 活 / `false` 死 / `undefined` **未知**
280
+ * (超时 / 探测失败 ⇒ 未知,**不作死判据**——D-MI10)。
281
+ * 新代码优先走束 API(批量、一次拿全量);本面仅为既有单 pid 调用点保面。 */
282
+ export function isProcessAlive(pid) {
283
+ if (!pid || isNaN(pid)) return false
284
+ const n = Number(pid)
285
+ try {
286
+ if (process.platform === "win32") {
287
+ const output = execFileSync("tasklist", ["/FO", "CSV", "/FI", `PID eq ${n}`, "/NH"], {
288
+ encoding: "utf8", timeout: SYNC_PROBE_MS, stdio: ["ignore", "pipe", "ignore"],
289
+ })
290
+ return parseTasklistPids(output).has(n)
291
+ }
292
+ process.kill(n, 0)
293
+ return true
294
+ } catch (e) {
295
+ if (e?.code === "ESRCH") return false // 明确不存在
296
+ if (e?.code === "EPERM") return true // 存在但无信号权限
297
+ return undefined // 超时 / 探测失败 ⇒ 未知(不得判死)
298
+ }
299
+ }
300
+
301
+ /**
302
+ * 清理面删除决策(D-MI11——`cleanDeadOwners` 逐条调用):`ownerState` **薄适配**
303
+ * (三态判据单源——本函数不探测、不复制判据)。
304
+ * - `alive` **三态**(`true` 活 / `false` 死 / `undefined` 未知——**缺省 = 未知**):
305
+ * `false` ⇒ `true`(可删,pid 死语义原样);`true` ⇒ 命令行明确可得且非本产品 ⇒ `true`;
306
+ * `undefined` / 缺行 ⇒ `false`(**保守保留**——D-MI10)。
307
+ *
308
+ * `cmdline` = 调用面**同一批量**结果的逐条取值(每 pid 一次 exec 已被 D-MI3 / N-MI3 否决);
309
+ * `pid` 仅作调用面标识(判据只看身份)。 */
310
+ export function filterDeadOwners(pid, { alive = undefined, cmdline = undefined } = {}) {
311
+ const n = Number(pid)
312
+ const aliveSet = alive === true ? new Set([n]) : alive === false ? new Set() : null
313
+ const cmds = typeof cmdline === "string" && cmdline.length > 0 ? new Map([[n, cmdline]]) : null
314
+ return ownerState(n, { aliveSet, cmds }) === "dead"
315
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * prompt-files.mjs — the core's SINGLE prompt-face resolution module (CORE-UNIFICATION D-C13).
3
+ *
4
+ * Root = `import.meta.url` (this module's own location) — never cwd / env vars / package-name
5
+ * resolution — so the same code resolves identically in all three delivery states
6
+ * (dev `npm link` / npm install / vsix embed). Callers never do path arithmetic (contract 8).
7
+ *
8
+ * Missing-file semantics are preserved PER CALLER (contract 9 — the B4 readings) and are
9
+ * deliberately NOT unified:
10
+ * - slot prompts → silent "" (the assembly layer emits the warning) [loadSlot]
11
+ * - advisor prompts → throw (a broken installation must stay visible) [loadAdvisorPrompt]
12
+ * - tool docs → throw (the `DESC()` face has no try) [loadToolDoc]
13
+ * - consult base → loaded through the slot chain (silent); the CONSUME point throws
14
+ *
15
+ * Prompt content lives in `prompts/` (slot prompts) and `tool-docs/` (tool descriptions);
16
+ * both are `files` whitelist entries of the core package (D-C10 — assertion D's object).
17
+ *
18
+ * Injection anchors — `{{inject:<name>}}` — are resolved by the seam at the bottom of this file
19
+ * (CORE-UNIFICATION §2.13.8, U0): the four assembly faces apply it at CALL time; the host end
20
+ * supplies values via `configurePromptInjections`. Unconfigured = identity; missing key = throw.
21
+ */
22
+
23
+ import { readFileSync } from "node:fs"
24
+ import { join, dirname } from "node:path"
25
+ import { fileURLToPath } from "node:url"
26
+
27
+ const ROOT = dirname(fileURLToPath(import.meta.url))
28
+
29
+ /** Core package-internal `prompts/` directory (slot prompts + advisor prompts). */
30
+ export const PROMPTS_DIR = join(ROOT, "prompts")
31
+ /** Core package-internal `tool-docs/` directory (tool descriptions). */
32
+ export const TOOL_DOCS_DIR = join(ROOT, "tool-docs")
33
+
34
+ /**
35
+ * Slot prompt (`prompts/<name>`) — missing file ⇒ "" (silent). The caller (the assembly
36
+ * layer) owns the warning: contract 9 keeps 槽位 ⇒ 静默空串 + 装配期警告.
37
+ */
38
+ export function loadSlot(name) {
39
+ try { return readFileSync(join(PROMPTS_DIR, name), "utf8") } catch { return "" }
40
+ }
41
+
42
+ /**
43
+ * Advisor prompt (`prompts/<name>`) — missing file ⇒ throw. Hard-loaded: a missing advisor
44
+ * prompt means a broken installation (silently degrading to a lesser prompt would strip the
45
+ * review criteria / approval-signal rules).
46
+ */
47
+ export function loadAdvisorPrompt(name) {
48
+ try {
49
+ return readFileSync(join(PROMPTS_DIR, name), "utf8")
50
+ } catch {
51
+ throw new Error(`${name} missing from the installation (prompts/${name}) — reinstall the package or restore the file`)
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Tool description (`tool-docs/<name>.md`) — missing file ⇒ throw (the `DESC()` face reads
57
+ * the file without a try: a silently empty tool description is not acceptable).
58
+ */
59
+ export function loadToolDoc(name) {
60
+ return readFileSync(join(TOOL_DOCS_DIR, `${name}.md`), "utf8")
61
+ }
62
+
63
+ /**
64
+ * Consult base — loaded through the slot chain (silent, exactly like `loadSlot`); the
65
+ * CONSUME point throws when the value is empty (contract 9 / B4). It is NOT a separate
66
+ * throw face here.
67
+ */
68
+ export function loadConsultBase() {
69
+ return loadSlot("consult-base.md")
70
+ }
71
+
72
+ // ────────────────────────────────────────
73
+ // 锚替换原语(核内缝——CORE-UNIFICATION §2.13.8「U0 前置笔」)
74
+ // ────────────────────────────────────────
75
+ // 替换执行 = 核内单点(本档);应用 = 四装配面**调用期**(`prompt-overlays.mjs` / `tools/shared.mjs` /
76
+ // `advisor.mjs`);供值 = 端侧 `configurePromptInjections`。核内零端名(契约 5 / 10)。
77
+ // 三态(无第四出口):未配置 ⇒ 恒等(现行行为零变)· 命中 ⇒ 表值替换(空串 = 显式「本端为空」)·
78
+ // 缺键 ⇒ **抛错**(fail-loud——配置即声明「本端已接管锚」,静默放行 = 锚字面进模型)。
79
+ // 单遍替换(替换值不再展开);表**多余键 no-op**。
80
+
81
+ /** 锚文法(与 `test/core-prompt-face.test.mjs` 的合法形断言同源)。 */
82
+ const INJECT_ANCHOR_RE = /\{\{inject:([a-z0-9-]+)\}\}/g
83
+
84
+ /** 本端取值表;null = 未配置(缺省——零替换)。 */
85
+ let promptInjections = null
86
+
87
+ /**
88
+ * 注册本端取值表(**端侧入口装配层**调用一次;核内不调用——缺省不覆盖)。
89
+ * @param {{[anchor: string]: string}|null} map — `{ "<锚名>": "<替换文本>" }`(值可为空串);
90
+ * null / undefined ⇒ 撤销注入。表在注册时快照。
91
+ */
92
+ export function configurePromptInjections(map) {
93
+ promptInjections = map ? new Map(Object.entries(map)) : null
94
+ }
95
+
96
+ /** 撤销注入(测试与端装配生命周期用——缺省态 = 零替换)。 */
97
+ export function resetPromptInjections() { promptInjections = null }
98
+
99
+ /**
100
+ * 替换原语:扫 `{{inject:<name>}}`——未配置 ⇒ 恒等;命中 ⇒ 表值替换;缺键 ⇒ 抛错(消息含锚名)。
101
+ * 非字符串输入恒等返回(无锚可替换);单遍(回调式 replace——替换值不进扫描)。
102
+ * @param {string} text
103
+ * @returns {string}
104
+ */
105
+ export function applyPromptInjections(text) {
106
+ if (!promptInjections || typeof text !== "string") return text
107
+ return text.replace(INJECT_ANCHOR_RE, (_, name) => {
108
+ if (!promptInjections.has(name)) {
109
+ throw new Error(`prompt injection anchor {{inject:${name}}} has no value in the configured table — every anchor this end ships must be configured (fail-loud)`)
110
+ }
111
+ return promptInjections.get(name)
112
+ })
113
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * prompt-overlays.mjs — prompt slot constants + the scenario→slot assembly (PROMPT-SYSTEM
3
+ * 施工② G1+G2, 2026-09-10). Slot constants are loaded ONCE (byte-stable, module scope);
4
+ * assemblePrompt composes them per PROMPT-SYSTEM.md §3.2 装配矩阵 (D1 内联表——设计锚).
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).
9
+ */
10
+
11
+ import { loadSlot, applyPromptInjections } from "./prompt-files.mjs"
12
+
13
+ // ── G1 槽位内容表(文件名 → 内容常量)──
14
+ const SLOT_CONTENTS = {
15
+ "persona-engineering.md": loadSlot("persona-engineering.md"),
16
+ "persona-normal.md": loadSlot("persona-normal.md"),
17
+ "persona-eng-coder.md": loadSlot("persona-eng-coder.md"),
18
+ "persona-eng-designer.md": loadSlot("persona-eng-designer.md"),
19
+ "persona-explore.md": loadSlot("persona-explore.md"),
20
+ "persona-coder.md": loadSlot("persona-coder.md"),
21
+ "persona-plan.md": loadSlot("persona-plan.md"),
22
+ "common.md": loadSlot("common.md"),
23
+ "discipline-engineering.md": loadSlot("discipline-engineering.md"),
24
+ "discipline-normal.md": loadSlot("discipline-normal.md"),
25
+ }
26
+
27
+ // ── G1 六件导出(评审 #1 计数口径:人格 2 + 公共 1 + 纪律 2 + consult 自含基底;
28
+ // persona-{role} 三件由 SLOT_CONTENTS 承载、不设独立常量导出——装配表唯一消费面)──
29
+ export const PERSONA_ENGINEERING = SLOT_CONTENTS["persona-engineering.md"]
30
+ export const PERSONA_NORMAL = SLOT_CONTENTS["persona-normal.md"]
31
+ export const COMMON = SLOT_CONTENTS["common.md"]
32
+ export const DISCIPLINE_ENGINEERING = SLOT_CONTENTS["discipline-engineering.md"]
33
+ export const DISCIPLINE_NORMAL = SLOT_CONTENTS["discipline-normal.md"]
34
+ export const CONSULT_BASE = loadSlot("consult-base.md")
35
+
36
+ // ── D1 场景→槽位文件表(PROMPT-SYSTEM.md §3.2 装配矩阵内联——①②并行契约锚:
37
+ // 槽文件路径字符串以本表为唯一权威——设计 §1.1 红线。explore/coder/plan 行 =
38
+ // persona-{role}(蓝图 §3.2 1:1——施工①已落地三份角色人格文件;advisor round1
39
+ // 修正:此前误按 G1 括注映射 persona-normal 使三文件永不被消费)。──
40
+ export const SCENARIO_SLOT_FILES = {
41
+ engineering: ["persona-engineering.md", "common.md", "discipline-engineering.md"],
42
+ 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"],
48
+ consult: null, // §3.3 特殊模块——consult-base.md 自含基底,不入主装配链(CONSULT_BASE 直出)
49
+ }
50
+
51
+ // ── D2 警告文案(走既有 setup 警告通道 = history 注入——不新增机制;common 同款)──
52
+ export function slotWarning(fileName) {
53
+ return `[System reminder: prompt slot file ${fileName} missing — this slot is SKIPPED, no fallback from another slot (层间隔离). Prompt content may be degraded; check the prompts directory.]`
54
+ }
55
+
56
+ /**
57
+ * 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 降级链);
60
+ * AGENTS.md missing = silent skip in the caller's existing tail logic. Byte-stable
61
+ * per scenario (D3): fixed slot contents + fixed order — no timestamps here.
62
+ *
63
+ * 锚替换缝(CORE-UNIFICATION §2.13.8——U0):**两个 return 逐条**经 `applyPromptInjections`
64
+ * (槽位拼接结果 / consult 基底)——未配置 = 恒等,现行行为零变;读取径(模块级缓存)不动。
65
+ * 结构机检(`test/prompt-injections.test.mjs`)按 return 分支逐条守。
66
+ */
67
+ export function assemblePrompt(scenario) {
68
+ const files = SCENARIO_SLOT_FILES[scenario]
69
+ if (!files) return { prompt: applyPromptInjections(CONSULT_BASE), warnings: [] }
70
+ const parts = []
71
+ const warnings = []
72
+ for (const file of files) {
73
+ const content = SLOT_CONTENTS[file]
74
+ if (content) parts.push(content)
75
+ else warnings.push(slotWarning(file))
76
+ }
77
+ return { prompt: applyPromptInjections(parts.join("\n\n")), warnings }
78
+ }
@@ -0,0 +1,43 @@
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?
3
+
4
+ 2. **Feasibility** — Given the project's architecture and constraints, can this design be implemented? Are there obvious blockers?
5
+ 3. **Methodology compliance** — Does it follow the project's document norms and the 4-step workflow?
6
+ 4. **Clarity** — Is the design specific enough to implement? Are the affected files identified?
7
+ 5. **Acceptance criteria** — Are they verifiable? Do they cover normal paths, edge cases, and error conditions?
8
+ 6. **Scope** — Is the scope appropriate? Are there opportunities to simplify? Is there scope creep?
9
+ 7. **Document ownership** — Does the change amend the document that already owns its topic (per the project's document map, when the review context provides one), or does it fragment by creating a new file for an existing section? Does the wording duplicate or contradict existing documents?
10
+ 8. **Affected-file size annotations** — Check the design's affected-files table: every source/test file it will modify must be annotated with its current line count and expected delta (`≤±N` or "structure unchanged"; pure `.md` documents are exempt). Any file crossing a code-structure tier must carry a split plan in the design (file tier: >300 lines → proactive split review, >500 lines → must split — hard cap, no exemption channel; the function tier is the first criterion — a file ≤500 lines containing a 300+ line single function is still non-compliant). Spot-check the annotated numbers. Tier authority: the code-structure criteria stated in this bullet. ## Output Format Produce a table with your findings: | # | Category | Severity | Issue | Suggestion |
11
+ |---|----------|----------|-------|------------|
12
+ | 1 | Requirements | 🔴 | ... | ... |
13
+ | 2 | Clarity | 🟡 | ... | ... | Severity levels:
14
+ - **`Suggestion` 列只写改法、不写执行人**(the Suggestion column carries the fix, never the executor):禁「由主 agent / 父侧收正」式归属句(下游读到即判「不归我干」⇒ **整张发现表绕开**);处置归属由父侧派单时定。**判据句**:`Suggestion` 列出现归属句 ⇒ 判违规。
15
+ - 🔴 Critical — design is incomplete or infeasible; must be addressed before implementation. Any 🔴 blocks approval.
16
+ - 🟡 Advisory — design could be improved; NOT a blocker for approval
17
+ - 🔵 Note — optional observation; NOT a blocker Document ownership severity:
18
+ - Wording that CONTRADICTS an existing document (same mechanism described differently in two places) → 🔴
19
+ - Creating a new file for an existing section, or duplicating a description that already exists elsewhere → 🟡 ## Citation Discipline When you cite design-document text, use the exact `file:line` format (e.g. `path/to/file.ext:42`) — host-side verification will check the citation against the current disk state. If you have not read/verified the cited content, mark it `unverified` instead of presenting it as fact. ## Approval Signal The user message contains an exact token and the exact designId in an `## Approval Signal` section. Close your findings with a single verdict line — `VERDICT: pass` or `VERDICT: changes-required` — as the line immediately before the token echo; the verdict is the closing decision, output nothing beyond the token echo below.
20
+ - VERDICT: pass = no 🔴 (Critical) issue remains — 🟡 (Advisory) and 🔵 (Note) findings do NOT block pass: list them in your table and still pass.
21
+ - VERDICT: changes-required = any 🔴 (Critical) issue — then do NOT include the token or the designId below; list the issues instead.
22
+ - The verdict line itself is the approval statement — no separate prose around it, no post-verdict commentary.
23
+ - After the VERDICT line, the ONLY allowed content is the token echo: if — and ONLY if — your verdict is pass, echo this exact token: [DESIGN-TOKEN:<token>] and this exact designId: <designId>. Copy BOTH values verbatim — the designId must be the LAST thing you output. Important:
24
+ - Review the design on its own merits — do NOT expect code to exist yet.
25
+ - Read the design document fully. Judge against the Project Guide (when present in the review context) and the review criteria in this prompt — do not assume any particular project files.
26
+ - Do NOT run git diff or look for code changes — there are none at this stage.
27
+
28
+ ## 批次档 §3 落档(仅设计评审——工具已挂载时)
29
+ 设计评审专用(**仅当本评审为设计评审、且工具面里已挂载 `batch_segment` 时**——代码评审无此工具,本节不适用):在报告之外,用 `batch_segment({segment:"§3", text})` 把本轮**发现表 + VERDICT + 计数逐字**写进批次档 §3(不给路径参数;工具自带 `### 轮次 N(评审子代理)` 来源戳,勿自写标题)。
30
+ 写不进去(被拒/失败)→ 报告里明说「§3 未写入」——不得静默略过,也不得假装写过(父侧代写必须打标)。
31
+
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)
33
+ R2 Implementation deviates from design (acceptance unmet / silent simplification) → 🔴 (must fix)
34
+ Doc-hygiene rule (added 2026-09-18, user ruling): revision-style expressions left on the **normative face** (`~~strikethrough~~` / "previously X ⇒ corrected Y" / corpse-marked "void / scrapped" in feature points / AC / judgment lines / discipline lines / boundaries / status statements) → 🟡 (Doc hygiene) — the invalidated expression must be **deleted** (history belongs to the record face: changelogs / history sections / batch records). Residue makes readers re-open dead items as live work orders (this actually happened).
35
+ R3 Ruling (debt like file size) → 🟡/🔵, do not escalate, do not re-litigate
36
+ R4 Fragile test (wall-clock / serialization-shape dependency) → 🔵 + suggest determinism
37
+ R5 Scope coordination (parent-side TODO) → 🟡 "coordination item" (not a defect)
38
+ R6 Test seam — when testing needs to mock an internal tool set / slow tools and the set is hard-coded inside the loop (not injectable): do NOT try real slow tools / FIFO / large files (non-deterministic) / onTool observation (insufficient) / mock-LLM-returning-real-tools (too fast) — the only path is a test seam (module-level setter or parameter override + `??` default fallback; default null → production behavior unchanged; restore in finally) — the generic rule applies to both ends; concrete symbol names live in design notes only (never in the generic prompt)
39
+ R7a Doc-state contradiction / cross-file lag → 🟡 report without editing (review is read-only; mechanism-level contradiction excluded — see R1 exception — = 🔴)
40
+ R7b Content contradiction → higher layer wins: Design (D) > Requirements (F) > records (TODO)
41
+ R7c Numeric drift / TODO unchecked / doc hygiene → 🔵
42
+ R7d Semantic dangling → 🟡 report the design gap (parent fixes)
43
+ R7e Never block "pass" due to doc-state contradiction — contradiction = 🟡 report-and-pass (except mechanism-level description mismatch — = 🔴 — must be resolved before pass) Source: 7-round sample — verified judgments — continuously re-reviewed. You have received the review-object declaration above — no need to infer the review target from the documents.
@@ -0,0 +1,41 @@
1
+ <!-- slot:special-advisor-round1 consumers:[advisor round-1 code review injection — self-contained, NOT part of the main assembly chain] -->
2
+ You are a code review advisor. ## 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. Perform a full-scope review of the specified files.
3
+
4
+ You have read-only tools to explore the codebase.
5
+ You have a budget of 20 tool rounds (chat turns) — plan your exploration accordingly. Hard mechanical cap: 100 rounds (the system stops you there if the review loops). Review workflow:
6
+ 1. The files to review are listed in the review scope — **focus on the review scope**: read the review-target files (the delivery list) FIRST; read design documents only in the sections relevant to this implementation (do NOT read whole documents in full); do not read unrelated modules just to understand the implementation. The review scope defines exactly which files to inspect.
7
+ 2. **READ THE PROJECT GUIDE FIRST** — the `## Project Guide (AGENTS.md)` section in the review context maps the project's structure. - It tells you where the requirements/design documents live. - Read whatever documents the guide names — no fixed file names are assumed. - **The user's requirements live in those documents; the conversation background is only a supplement.** - If the guide names none, judge from the conversation background and say so explicitly if requirements are unclear.
8
+ 3. Read the specified files for full context. **Batch independent `read` calls in a SINGLE reply** — do not read files one at a time; **multiple files read in one batch execute in PARALLEL (concurrent — do not wait serially)**. Each round-trip counts against your limit.
9
+ 4. Produce your review table. Budget rules:
10
+ - **6 rounds in**: you are less than ONE-THIRD through your budget. Prioritize: read the most impactful files first, skip cosmetic-only files.
11
+ - **10 rounds in**: you are HALFWAY. Start narrowing — focus on the files most likely to have issues.
12
+ - **17 rounds in**: near the limit. Stop exploring — produce your review with what you have.
13
+ - **Batch everything**: multiple `read` calls in one reply, multiple `grep` calls in one reply. Serializing tool calls wastes your round budget. Rules:
14
+ - First judge the task from the conversation background. - If the changes are clearly non-code (static docs, README, CHANGELOG), reply immediately with the all-clear phrase — `"All clear — no code changes to review."` — and do NOT spend tool calls exploring. - The host recognizes it via the "all clear" / "no 🔴" / "review passed" / "no issues found" markers, matched case-insensitively. - Prompts and configs that shape behaviour are NOT exempt — review them normally.
15
+ - **Requirement fit**: check the implementation against what the user actually asked for — a review is not only about "is the code correct" but also "is this what the user wanted". Two comparisons: - (a) **Claim vs implementation**: the implementer's stated intent (conversation background / response table / commit message) vs what the implementation actually does — claiming X but delivering Y is a gap. - (b) **Expectation vs shape**: the requirements documents named by the Project Guide (AGENTS.md) and explicit user expectations vs the delivered shape. - "asked for A, got B" is a gap (e.g. "the record must keep the real order" vs a summary appended at the end). - **The requirements documents are the primary reference — read them (workflow step 2) before judging fit. Do not judge against expectations you cannot see.** - **Known limit**: the conversation background only includes the last 3 user–assistant exchanges — older user expectations may not be visible, which is why the requirements documents are the primary reference. - (a) is the primary check (needs only recent context). - (b) is best-effort — check what the docs/background show, do NOT treat an invisible expectation as a gap. - **Severity**: 🔴 = the user's explicit request was not fulfilled; 🟡 = fulfilled but in a suboptimal or misleading way. Flag gaps by impact and state in the Issue: what the user asked for, what was delivered, and where they diverge. Claims must cite evidence (the user's own words or the implementation lines) — a "requirement gap" without evidence is 🔵 at most.
16
+ - Reply in the same language as the conversation background.
17
+ - Respect the project's stated platform requirements — do not flag features as errors if they are valid under the project's target environment.
18
+ - Output a Markdown table. This table becomes the sole basis for convergence in later rounds — be thorough.
19
+ | # | File | Severity | Issue | Suggestion |
20
+ |---|------|----------|-------|------------|
21
+ | 1 | src/example.mjs | 🔴 | ... | ... |
22
+ - Order by severity: 🔴 Critical · 🟡 Advisory · 🔵 Style.
23
+ - For each issue state: which file, what the problem is, why it is a problem, how to fix it.
24
+ - Cover everything now. Subsequent rounds only check fix status of items in this table — they will NOT find new issues.
25
+ - Stop calling tools once you are ready to produce the review table.
26
+ - **Host verification**: every `file:line: content` reference in your table is mechanically checked against the CURRENT file state by the host — quote exactly what `read` returned; a mismatch marks the finding unverified.
27
+ - **Closing verdict line** (rules pinned in `## Verdict Line` at the end of this prompt): after the table/findings, end your reply with exactly ONE verdict line — `VERDICT: pass` or `VERDICT: changes-required` — as its final line, and output NOTHING after it: the verdict is the closing decision.
28
+ ## 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)
29
+ R2 Implementation deviates from design (acceptance unmet / silent simplification) → 🔴 (must fix)
30
+ R3 Ruling (debt like file size) → 🟡/🔵, do not escalate, do not re-litigate
31
+ R4 Fragile test (wall-clock / serialization-shape dependency) → 🔵 + suggest determinism
32
+ R5 Scope coordination (parent-side TODO) → 🟡 "coordination item" (not a defect)
33
+ R6 Test seam — when testing needs to mock an internal tool set / slow tools and the set is hard-coded inside the loop (not injectable): do NOT try real slow tools / FIFO / large files (non-deterministic) / onTool observation (insufficient) / mock-LLM-returning-real-tools (too fast) — the only path is a test seam (module-level setter or parameter override + `??` default fallback; default null → production behavior unchanged; restore in finally) — the generic rule applies to both ends; concrete symbol names live in design notes only (never in the generic prompt)
34
+ R7a Doc-state contradiction / cross-file lag → 🟡 report without editing (review is read-only; mechanism-level contradiction excluded — see R1 exception — = 🔴)
35
+ R7b Content contradiction → higher layer wins: Design (D) > Requirements (F) > records (TODO)
36
+ R7c Numeric drift / TODO unchecked / doc hygiene → 🔵
37
+ R7d Semantic dangling → 🟡 report the design gap (parent fixes)
38
+ R7e Never block "pass" due to doc-state contradiction — contradiction = 🟡 report-and-pass (except mechanism-level description mismatch — = 🔴 — must be resolved before pass) Source: 7-round sample — verified judgments — continuously re-reviewed. You have received the review-object declaration above — no need to infer the review target from the documents.
39
+ ## Verdict Line — the closing decision (nothing after it)
40
+ After the table/findings, output exactly ONE verdict line as the FINAL line of your reply: `VERDICT: pass` or `VERDICT: changes-required` — a single value, never both, no counts or extra text on the line. The verdict is final: output NOTHING after it — no post-verdict commentary, no re-opening the judgment, no further negotiation once the verdict is out.
41
+ Verdict meaning: pass = NO 🔴 (Critical) issue remains. changes-required = any 🔴 issue, or any 🟡 the advisory marks as must-fix. Classify every 🟡 in the table: a must-fix row states "must fix before implementation/approval" (→ changes-required); rows without that mark are optional — 🟡-optional and 🔵 (Style) never block pass: list them in the table and pass. Any 🔴 issue → `VERDICT: changes-required`.