@thincoder/core 0.10.12 → 0.11.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 (174) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/advisor/messages.mjs +4 -2
  3. package/advisor/notice.mjs +1 -1
  4. package/advisor/project-context.mjs +121 -11
  5. package/agent/completion.mjs +13 -1
  6. package/agent/digest-account.mjs +73 -46
  7. package/agent/dispatch-run.mjs +1 -1
  8. package/agent/dispatch.mjs +29 -21
  9. package/agent/family-tools.mjs +9 -5
  10. package/agent/helpers.mjs +1 -94
  11. package/agent/offload.mjs +88 -0
  12. package/agent/record-results.mjs +1 -6
  13. package/agent/run-stages.mjs +74 -49
  14. package/agent/run-start.mjs +8 -8
  15. package/agent/setup.mjs +6 -11
  16. package/agent/suspension.mjs +1 -1
  17. package/agent/turn-loop.mjs +35 -3
  18. package/agent/write-face.mjs +99 -0
  19. package/agent/write-gate.mjs +354 -5
  20. package/agent-tools/advisor.mjs +127 -11
  21. package/agent-tools/async-settle.mjs +2 -2
  22. package/agent-tools/bash-async.mjs +8 -2
  23. package/agent-tools/batch-closeout.mjs +206 -0
  24. package/agent-tools/batch-gate.mjs +287 -73
  25. package/agent-tools/batch-ledger-bind.mjs +183 -0
  26. package/agent-tools/batch-ledger-link.mjs +73 -0
  27. package/agent-tools/batch-lifecycle.mjs +113 -354
  28. package/agent-tools/batch-paths.mjs +45 -40
  29. package/agent-tools/batch-read.mjs +1 -1
  30. package/agent-tools/batch-reopen.mjs +142 -0
  31. package/agent-tools/batch-skeleton.mjs +283 -80
  32. package/agent-tools/batch-unit-test-gate.mjs +107 -0
  33. package/agent-tools/batch.mjs +243 -50
  34. package/agent-tools/context.mjs +6 -3
  35. package/agent-tools/design-slots.mjs +24 -24
  36. package/agent-tools/design-token.mjs +96 -15
  37. package/agent-tools/doc-check.mjs +18 -2
  38. package/agent-tools/eng.mjs +1 -1
  39. package/agent-tools/escalate-async.mjs +3 -3
  40. package/agent-tools/manifest.mjs +8 -5
  41. package/agent-tools/read-history.mjs +5 -5
  42. package/agent-tools/review-facts.mjs +31 -13
  43. package/agent-tools/spawn-gates.mjs +10 -14
  44. package/agent-tools/ssh-session.mjs +120 -111
  45. package/agent-tools/ssh-transfer.mjs +210 -0
  46. package/agent-tools/ssh-transport.mjs +277 -46
  47. package/agent-tools/subagent-actions-query.mjs +1 -1
  48. package/agent-tools/subagent-actions.mjs +1 -1
  49. package/agent-tools/subagent-async.mjs +3 -2
  50. package/agent-tools/subagent-scheduler.mjs +43 -9
  51. package/agent-tools/subagent-spawn.mjs +68 -9
  52. package/agent-tools/subagent.mjs +16 -14
  53. package/agent.mjs +1 -0
  54. package/client-identity.mjs +70 -0
  55. package/config-presets.mjs +5 -3
  56. package/config.mjs +19 -13
  57. package/context.mjs +39 -8
  58. package/declaration.mjs +2 -2
  59. package/doc-check/anchors.mjs +128 -28
  60. package/doc-check/index.mjs +93 -27
  61. package/doc-check/line-refs.mjs +112 -0
  62. package/doc-check/paragraph.mjs +24 -8
  63. package/embedding.mjs +2 -0
  64. package/explore-distill.mjs +1 -1
  65. package/generate-title.mjs +3 -2
  66. package/i18n.mjs +1 -1
  67. package/index-discover.mjs +4 -1
  68. package/ledger-close-proof.mjs +62 -0
  69. package/ledger-cmd.mjs +79 -194
  70. package/ledger-db.mjs +29 -28
  71. package/ledger-migrate.mjs +4 -4
  72. package/ledger-read.mjs +10 -8
  73. package/ledger-surface.mjs +1 -1
  74. package/ledger-tools.mjs +10 -10
  75. package/ledger-variant-notice.mjs +1 -1
  76. package/ledger-write-ops.mjs +373 -0
  77. package/ledger.mjs +2 -2
  78. package/manifest-discovery.mjs +2 -2
  79. package/manifest-schema.mjs +4 -0
  80. package/memory/memory-tool.mjs +5 -2
  81. package/memory/schema.mjs +5 -26
  82. package/model-specs.mjs +6 -5
  83. package/package.json +1 -1
  84. package/path-resolve.mjs +217 -0
  85. package/permission.mjs +4 -0
  86. package/prompt-overlays.mjs +5 -0
  87. package/prompts/advisor-design.md +9 -6
  88. package/prompts/advisor-round1.md +4 -3
  89. package/prompts/advisor-round2.md +6 -5
  90. package/prompts/advisor-round3.md +6 -5
  91. package/prompts/common.md +23 -10
  92. package/prompts/consult-base.md +2 -1
  93. package/prompts/discipline-engineering.md +29 -48
  94. package/prompts/discipline-normal.md +10 -5
  95. package/prompts/persona-eng-coder.md +8 -0
  96. package/prompts/persona-eng-designer.md +22 -12
  97. package/prompts/persona-engineering.md +81 -66
  98. package/prompts/persona-plan.md +2 -1
  99. package/prompts/persona-tester.md +55 -0
  100. package/provider/anthropic.mjs +12 -7
  101. package/provider/core.mjs +21 -5
  102. package/provider/errors.mjs +41 -0
  103. package/provider/google.mjs +25 -9
  104. package/provider/list-models.mjs +4 -3
  105. package/provider/rate.mjs +11 -3
  106. package/provider/responses.mjs +17 -7
  107. package/provider/sse.mjs +15 -4
  108. package/session-identity.mjs +21 -0
  109. package/session-lifecycle.mjs +12 -4
  110. package/session-slots.mjs +9 -14
  111. package/session.mjs +5 -0
  112. package/skip-dirs.mjs +48 -0
  113. package/team-ledger.mjs +4 -4
  114. package/text-budget.mjs +4 -0
  115. package/token-window.mjs +107 -10
  116. package/tool-docs/advisor.md +2 -2
  117. package/tool-docs/apply_patch.md +2 -0
  118. package/tool-docs/bash.md +4 -0
  119. package/tool-docs/batch.md +85 -1
  120. package/tool-docs/browser.md +3 -1
  121. package/tool-docs/code_search.md +3 -1
  122. package/tool-docs/consult_start.md +6 -2
  123. package/tool-docs/consult_stop.md +3 -1
  124. package/tool-docs/delete.md +3 -0
  125. package/tool-docs/doc_check.md +43 -8
  126. package/tool-docs/doc_search.md +5 -1
  127. package/tool-docs/edit.md +22 -1
  128. package/tool-docs/execute.md +7 -1
  129. package/tool-docs/fetch.md +3 -1
  130. package/tool-docs/file_ops.md +3 -1
  131. package/tool-docs/git.md +9 -1
  132. package/tool-docs/goal.md +2 -0
  133. package/tool-docs/grep.md +1 -1
  134. package/tool-docs/hashline_edit.md +2 -0
  135. package/tool-docs/insert_after.md +3 -1
  136. package/tool-docs/ledger.md +5 -5
  137. package/tool-docs/lint.md +1 -0
  138. package/tool-docs/manifest-read.md +2 -1
  139. package/tool-docs/manifest.md +9 -2
  140. package/tool-docs/memory.md +11 -1
  141. package/tool-docs/notify_parent.md +1 -1
  142. package/tool-docs/plan.md +2 -0
  143. package/tool-docs/question.md +1 -1
  144. package/tool-docs/read_history.md +15 -3
  145. package/tool-docs/read_image.md +3 -1
  146. package/tool-docs/recent_changes.md +3 -1
  147. package/tool-docs/repo_outline.md +3 -1
  148. package/tool-docs/settings.md +10 -2
  149. package/tool-docs/skill.md +2 -0
  150. package/tool-docs/ssh.md +30 -17
  151. package/tool-docs/subagent.md +50 -13
  152. package/tool-docs/task.md +2 -0
  153. package/tool-docs/team_ledger.md +1 -1
  154. package/tool-docs/timer.md +7 -0
  155. package/tool-docs/verify.md +13 -1
  156. package/tool-docs/websearch.md +3 -1
  157. package/tool-docs/write.md +2 -0
  158. package/tools/bash.mjs +12 -2
  159. package/tools/edit-batch.mjs +40 -33
  160. package/tools/edit-diff.mjs +69 -1
  161. package/tools/execute.mjs +10 -4
  162. package/tools/file.mjs +17 -27
  163. package/tools/git.mjs +27 -1
  164. package/tools/ops.mjs +2 -1
  165. package/tools/patch.mjs +12 -3
  166. package/tools/process-doc-mark.mjs +139 -0
  167. package/tools/search.mjs +15 -6
  168. package/tools/shared.mjs +24 -1
  169. package/tools/ssh.mjs +48 -7
  170. package/tools/tree.mjs +4 -1
  171. package/tools/write-path.mjs +64 -3
  172. package/traces/trace-store.mjs +4 -3
  173. package/agent/light-round.mjs +0 -63
  174. package/agent/unbooked-pen.mjs +0 -62
@@ -0,0 +1,217 @@
1
+ /**
2
+ * path-resolve.mjs — 相对路径 → 绝对路径的**统一解析层**(一个函数、四态结果)。
3
+ *
4
+ * 机制单源 = `docs/core/design/PATH-RESOLUTION.md` §2(本档只实现,不重述口径)。
5
+ * 各吃路径的面只留自己的**受理判据**(存不存在 / 是不是文档 / 能不能读 / 落不落基底内);
6
+ * 「一条路径指向哪里」只由本层回答。
7
+ *
8
+ * 叶档(KD-1):只 import `node:*` + `manifest.mjs`(`projectRootView`:取根单源)+
9
+ * `declaration.mjs`(`declaredPublicRoots`:声明仓根集单源——`manifest.mjs` 的转口不含它,取它须直引)。
10
+ *
11
+ * 四态(§2.1):`resolved{abs}` · `ambiguous{candidates}` · `dangling{missing, sameName, level, tried}` ·
12
+ * `crossRepo{repo, declared, relPath, candidates}`。`tried` = 基根序下试过的绝对候选(面报「解析 = …」用;
13
+ * 只作读数,不作判据)。
14
+ */
15
+ import { existsSync, readdirSync, statSync } from "node:fs"
16
+ import { basename, dirname, isAbsolute, join, resolve } from "node:path"
17
+
18
+ import { declaredPublicRoots } from "./declaration.mjs"
19
+ import { projectRootView } from "./manifest.mjs"
20
+
21
+ /** 同名候选扫描界(KD-7):每根各自起算深 ≤3、命中上限 20(只在 `dangling` 路径上跑)。 */
22
+ const SAME_NAME_MAX_DEPTH = 3
23
+ const SAME_NAME_MAX_HITS = 20
24
+ /** 跨仓未声明形的同级目录枚举上限(§2.7:一次 `readdir`、不递归、按名排序)。 */
25
+ const SIBLING_MAX = 20
26
+ /** 同名候选扫描跳过的目录名(归档区不是「你是不是指这个」的候选面)。 */
27
+ const SAME_NAME_SKIP = new Set(["_archive"])
28
+
29
+ /** 路径同一性(win32 大小写不敏感——同 `batch-paths.mjs` 越基底判据口径)。 */
30
+ const pathKey = (p) => (process.platform === "win32" ? p.toLowerCase() : p)
31
+
32
+ /** 现存判据(`kind` = 存在判口径;`"none"` 恒真——只做调度、不校验存在性)。 */
33
+ function existsAs(abs, kind) {
34
+ if (kind === "none") return true
35
+ let st = null
36
+ try { st = statSync(abs, { throwIfNoEntry: false }) } catch { return false }
37
+ if (!st) return false
38
+ if (kind === "dir") return st.isDirectory()
39
+ if (kind === "file") return st.isFile()
40
+ return true // "any"
41
+ }
42
+
43
+ /** 去重保序(按路径同一性;基根重合时不产伪 `ambiguous`)。 */
44
+ function uniquePaths(list) {
45
+ const seen = new Set()
46
+ const out = []
47
+ for (const p of list) {
48
+ const k = pathKey(p)
49
+ if (seen.has(k)) continue
50
+ seen.add(k)
51
+ out.push(p)
52
+ }
53
+ return out
54
+ }
55
+
56
+ /** 目录 → 直接子目录名(按名排序、上限 `SIBLING_MAX`;不可读 ⇒ 空集)。 */
57
+ function siblingDirs(dir) {
58
+ let entries
59
+ try { entries = readdirSync(dir, { withFileTypes: true }) } catch { return [] }
60
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort().slice(0, SIBLING_MAX)
61
+ }
62
+
63
+ /** 同名档扫描(**每根各自起算**:递归深 ≤ `SAME_NAME_MAX_DEPTH`、命中上限 `SAME_NAME_MAX_HITS`、
64
+ * 跳过隐藏目录与 `_archive`;宽度优先——浅处的同名档先入列)。 */
65
+ function collectSameName(root, name, out) {
66
+ const queue = [{ dir: root, depth: 0 }]
67
+ while (queue.length > 0 && out.length < SAME_NAME_MAX_HITS) {
68
+ const { dir, depth } = queue.shift()
69
+ let entries
70
+ try { entries = readdirSync(dir, { withFileTypes: true }) } catch { continue }
71
+ for (const e of entries) {
72
+ if (out.length >= SAME_NAME_MAX_HITS) break
73
+ if (e.name.startsWith(".") || SAME_NAME_SKIP.has(e.name)) continue
74
+ const abs = join(dir, e.name)
75
+ if (e.isDirectory()) {
76
+ if (depth + 1 <= SAME_NAME_MAX_DEPTH) queue.push({ dir: abs, depth: depth + 1 })
77
+ continue
78
+ }
79
+ if (e.isFile() && e.name === name) out.push(abs)
80
+ }
81
+ }
82
+ }
83
+
84
+ /** 第一个不存在的路径段(`dangling.missing` 的取值单源——AC-4)。 */
85
+ function firstMissing(base, segs) {
86
+ let cur = base
87
+ for (const s of segs) {
88
+ cur = join(cur, s)
89
+ if (!existsSync(cur)) return s
90
+ }
91
+ return segs[segs.length - 1] ?? ""
92
+ }
93
+
94
+ /** 跨仓识别(§2.7,只在 `crossRepo === "visible"` ∧ 输入多段 ∧ 第 4–5 步零命中时调):
95
+ * 已声明形 = 首段 ∈ 声明仓名集(声明根**不 stat**——位置因人而异);未声明形 = 首段是项目根的同级目录名;
96
+ * 形态不合法(含 `..` / `.` 段 / 余段空)⇒ 不是跨仓形 ⇒ `null`(落 `dangling`,KD-11)。 */
97
+ function crossRepoOf(start, pathText, view) {
98
+ const segs = pathText.split("/").filter(Boolean)
99
+ if (segs.length < 2) return null
100
+ if (segs.some((s) => s === ".." || s === ".")) return null
101
+ const repo = segs[0]
102
+ const relPath = segs.slice(1).join("/")
103
+ if (!relPath) return null
104
+ const declaredNames = new Set(declaredPublicRoots(start).map((r) => basename(r)))
105
+ if (declaredNames.has(repo)) return { state: "crossRepo", repo, declared: true, relPath, candidates: [] }
106
+ const parent = view.state === "ok" ? dirname(view.root) : dirname(start)
107
+ const siblings = siblingDirs(parent)
108
+ if (siblings.includes(repo)) return { state: "crossRepo", repo, declared: false, relPath, candidates: siblings }
109
+ return null
110
+ }
111
+
112
+ /** 前两级基根(§2.2 第 4 步——缺哪级就跳过):`root-first` = 项目根 → 所指目录;
113
+ * `near-first` = 所指目录 → 项目根。「所指目录」= `referrer` 的目录,无 `referrer` ⇒ 会话锚
114
+ * (§1.2 偏离 ③——不兜则第二级在「手上没有引用方档」的面整段消失)。
115
+ * 解析不出项目根时:`workspace` = 这一级不成立(§2.3 判 `ambiguous`);`none` = 会话锚即该级
116
+ * (`batchProjectRoot` / `docRootBase` 的既有兜底同式——锚自己就是手上唯一的基根)。 */
117
+ function coreLegs(start, view, referrer, order) {
118
+ const refDir = referrer ? dirname(referrer) : start
119
+ const rootLeg = view.state === "ok" ? view.root : view.state === "none" ? start : null
120
+ const legs = order === "near-first" ? [refDir, rootLeg] : [rootLeg, refDir]
121
+ return uniquePaths(legs.filter((p) => typeof p === "string" && p !== ""))
122
+ }
123
+
124
+ /**
125
+ * 一条路径 → 四态之一(判据正文 = `docs/core/design/PATH-RESOLUTION.md` §2.2)。
126
+ * @param {string} anchor 会话锚(相对路径的最终兜底基根;`projectRootView` 的查询起点)
127
+ * @param {string|undefined|null} given 参数原值(`undefined` = 参数缺省——与空串分野,§2.2 第 1 步)
128
+ * @param {{referrer?:string, bases?:string[], scanBases?:string[],
129
+ * kind?:"file"|"dir"|"any"|"none", order?:"root-first"|"near-first",
130
+ * crossRepo?:"dangling"|"visible"}} [opts]
131
+ * @returns {{state:"resolved", abs:string}
132
+ * | {state:"ambiguous", candidates:string[]}
133
+ * | {state:"dangling", missing:string, sameName:string[], level:"base"|"path", tried:string[]}
134
+ * | {state:"crossRepo", repo:string, declared:boolean, relPath:string, candidates:string[]}}
135
+ */
136
+ export function resolvePathTarget(anchor, given, opts = {}) {
137
+ const o = opts ?? {}
138
+ const start = resolve(anchor ?? ".")
139
+ const kind = o.kind === "dir" || o.kind === "any" || o.kind === "none" ? o.kind : "file"
140
+ const order = o.order === "near-first" ? "near-first" : "root-first"
141
+ const wantCrossRepo = o.crossRepo === "visible"
142
+ const referrer = typeof o.referrer === "string" && o.referrer.trim() !== "" ? resolve(o.referrer) : null
143
+ const bases = (Array.isArray(o.bases) ? o.bases : []).filter((b) => typeof b === "string" && b.trim() !== "")
144
+ const scanBases = (Array.isArray(o.scanBases) ? o.scanBases : []).filter((b) => typeof b === "string" && b.trim() !== "")
145
+ const view = projectRootView(start)
146
+
147
+ // ── 第 1 步:参数缺省(undefined)≠ 空串 —— 没有路径可判,只查锚根
148
+ if (given === undefined || given === null) {
149
+ if (view.state === "ok") return { state: "resolved", abs: view.root }
150
+ if (view.state === "workspace") return { state: "ambiguous", candidates: [...view.candidates] }
151
+ return { state: "dangling", missing: "", sameName: [], level: "base", tried: [] }
152
+ }
153
+ const raw = typeof given === "string" ? given.trim() : ""
154
+ if (raw === "") return { state: "dangling", missing: typeof given === "string" ? given : String(given), sameName: [], level: "path", tried: [] }
155
+ const pathText = raw.replace(/\\/g, "/")
156
+
157
+ // ── 第 2 步:绝对路径 —— 照用,零基根走查(各面受理判据照旧)
158
+ if (isAbsolute(pathText)) return { state: "resolved", abs: resolve(pathText) }
159
+
160
+ const segs = pathText.split("/").filter(Boolean)
161
+ const legs = coreLegs(start, view, referrer, order)
162
+ // 第三级基底:与前两级重合者不重复(重合即同一基根,不是第二个落点)
163
+ const baseRoots3 = uniquePaths(bases).filter((b) => !legs.some((l) => pathKey(l) === pathKey(b)))
164
+ const roots = [...legs, ...baseRoots3]
165
+
166
+ // ── 第 3 步:kind:"none" —— 只做调度、不校验存在性:候选序首个即 resolved,恒不产其余三态
167
+ if (kind === "none") return { state: "resolved", abs: resolve(roots[0] ?? start, pathText) }
168
+
169
+ // ── 第 4 步(续):工作区锚 —— 项目根这一级不成立 ⇒ 不猜(§2.3:列下辖项目、停下要人挑)
170
+ if (view.state === "workspace") return { state: "ambiguous", candidates: [...view.candidates] }
171
+
172
+ // ── 第 5 步:判态 —— 前两级按 `order` **首命中即 resolved**;第三级 `bases` 内多份现存 ⇒ `ambiguous` 全列。
173
+ // 依据:KD-2 / KD-3 的「翻转次序会改变判定结果」+ AC-11 两条断言(两个基根各有一份同名现存档 ⇒
174
+ // 锚面取项目根那份、批档面取近基根那份)——只有首命中择一才成立(§2.2 第 5 步「≥二 ⇒ ambiguous」
175
+ // 在两级基根上与 AC-11 不能同时成立,取验收判据这一面)。
176
+ const tried = uniquePaths(roots.map((b) => resolve(b, pathText)))
177
+ for (const leg of legs) {
178
+ const abs = resolve(leg, pathText)
179
+ if (existsAs(abs, kind)) return { state: "resolved", abs }
180
+ }
181
+ const baseHits = uniquePaths(baseRoots3.map((b) => resolve(b, pathText)).filter((abs) => existsAs(abs, kind)))
182
+ if (baseHits.length === 1) return { state: "resolved", abs: baseHits[0] }
183
+ if (baseHits.length > 1) return { state: "ambiguous", candidates: baseHits }
184
+
185
+ // ── 第 6 步:跨仓识别(仅文档锚面开;仓内缺档仍阻断)
186
+ if (wantCrossRepo) {
187
+ const cross = crossRepoOf(start, pathText, view)
188
+ if (cross) return cross
189
+ }
190
+
191
+ // ── 第 7 步:dangling —— 报缺的那一段 + 列同名候选(两层基根 + bases + scanBases 各根,每根各自起算)
192
+ const name = segs[segs.length - 1] ?? pathText
193
+ const hits = []
194
+ for (const root of uniquePaths([...roots, ...scanBases])) collectSameName(root, name, hits)
195
+ const sameName = uniquePaths(hits)
196
+ return {
197
+ state: "dangling",
198
+ missing: firstMissing(roots[0] ?? start, segs),
199
+ sameName,
200
+ level: "path",
201
+ tried,
202
+ }
203
+ }
204
+
205
+ /** `dangling` 的「盘上同名」提示尾(空数组 ⇒ 空串)。面拼在既有文案之后(§2.6:既有文案逐字保留在前)。 */
206
+ export function danglingSameNameHint(result) {
207
+ const list = result?.state === "dangling" && Array.isArray(result.sameName) ? result.sameName : []
208
+ return list.length > 0 ? `盘上同名:${list.join(" ∥ ")}` : ""
209
+ }
210
+
211
+ /** 跨仓一行的读数文案(§2.6 两条形——已声明 ∥ 未声明;未声明形不阻断,只作读数)。 */
212
+ export function crossRepoNote(ident, result) {
213
+ if (result?.state !== "crossRepo") return ""
214
+ if (result.declared) return `${ident} → 仓「${result.repo}」的 ${result.relPath}(声明源引用,未校验存在性)`
215
+ const cands = Array.isArray(result.candidates) && result.candidates.length > 0 ? result.candidates.join(" ∥ ") : "(无)"
216
+ return `${ident} 引用外部仓「${result.repo}」——引用外部仓须先在 index.publicRepos 声明;本机仓名候选:${cands}`
217
+ }
package/permission.mjs CHANGED
@@ -95,6 +95,10 @@ export function formatPermission(name, args) {
95
95
  return cap(`${target} — 删除已保存的 ssh 凭据`, 300)
96
96
  }
97
97
  if (action === "close") return `${sid} — 关闭会话`
98
+ // 传输面(TOOLS.md §6.22 ⑬「UI / 交互决策」):审批文案**点出传输两端**、内容不入文案
99
+ // (与 NS3「将发送原文」的有意偏离——载荷 = 文件字节,抄进文案等于把内容灌进对话面)。
100
+ if (action === "put") return cap(`${sid} — 上传 ${args.localPath ?? "(缺 localPath)"} → 远端 ${args.remotePath ?? "(缺 remotePath)"}`, 300)
101
+ if (action === "get") return cap(`${sid} — 下载 远端 ${args.remotePath ?? "(缺 remotePath)"} → ${args.localPath ?? "(缺 localPath)"}`, 300)
98
102
  return cap(`${action} ${sid}`, 300)
99
103
  }
100
104
  return cap(summarize(args), 300)
@@ -19,6 +19,7 @@ const SLOT_CONTENTS = {
19
19
  "persona-explore.md": loadSlot("persona-explore.md"),
20
20
  "persona-coder.md": loadSlot("persona-coder.md"),
21
21
  "persona-plan.md": loadSlot("persona-plan.md"),
22
+ "persona-tester.md": loadSlot("persona-tester.md"),
22
23
  "common.md": loadSlot("common.md"),
23
24
  "discipline-engineering.md": loadSlot("discipline-engineering.md"),
24
25
  "discipline-normal.md": loadSlot("discipline-normal.md"),
@@ -46,6 +47,10 @@ export const SCENARIO_SLOT_FILES = {
46
47
  explore: ["persona-explore.md", "common.md", "discipline-normal.md", "subagent-base.md"],
47
48
  coder: ["persona-coder.md", "common.md", "discipline-normal.md", "subagent-base.md"],
48
49
  plan: ["persona-plan.md", "common.md", "discipline-normal.md", "subagent-base.md"],
50
+ // AGENT-LOOP-SUBAGENT.md §6.59.2 ③:tester 的 scenario 名取自 `agent._role`(setup.mjs 装配面)——
51
+ // 不登记 = 静默落到 consult 基底(assemblePrompt 未命中即返 CONSULT_BASE、零警告)。
52
+ // 纪律槽取普通纪律(D-TT6):工程纪律装的是四步流程与 §2 账,tester 不写账、不推流程。
53
+ tester: ["persona-tester.md", "common.md", "discipline-normal.md", "subagent-base.md"],
49
54
  consult: null, // §3.3 特殊模块——consult-base.md 自含基底,不入主装配链(CONSULT_BASE 直出)
50
55
  }
51
56
 
@@ -7,8 +7,9 @@ You are an INDEPENDENT REVIEWER — authority in judgment, not in decisions.
7
7
  **You find and report — you do not author**: you are not the author, not the implementer, not the editor; the parent agent (and the user) decides what changes.
8
8
  **The suggestion column stays advisory guidance**: you evidence and recommend, and the rewrite belongs to the parent agent.
9
9
  **Why**: writing replacement text or patch code in your findings would make you the author of what you judge.
10
- 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`.
11
- **An assertion has two sources only**: what you read (cite `file:line`) or an explicit `unverified`.
10
+ 2. **Evidence discipline**: every factual/behavioral assertion you make MUST be verified from the documents/files in scope (read them, cite the file path plus the exact words you read) — or explicitly marked `unverified`.
11
+ **An assertion has two sources only**: what you read (the file path plus the exact words, quoted as they stand) or an explicit `unverified`.
12
+ **Never cite a line number** — it records a position, so one line inserted above **shifts every position below it**: every file citing this one must be recomputed, and the work grows with the number of referrers.
12
13
  **Where the source is readable**, a behavioral question is an EVIDENCE question, not a reasoning question.
13
14
  **Why**: "Known behavior…" and "I'm confident…" rest on memory, which has a cut-off and misremembers, so no one can check a remembered API answer.
14
15
  3. **Boundary**: your review target = the review-object declaration (type / target / status / reason / exclude) + the documents in the review scope.
@@ -31,12 +32,13 @@ Evaluate the design against these dimensions:
31
32
  **Why**: a branch brings four companions of its own — an alert, copy, cross-end consistency and test material; with no scene none of the four can be verified, and nobody later dares delete the branch.
32
33
  **A real defence passes**: give the reproduction path and it walks — this kills the branch that cannot name its scene, not defence itself.
33
34
  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?
34
- 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").
35
- **Pure `.md` documents are exempt** from that annotation.
35
+ 8. **Affected-file tier check**: every source/test file in the design's affected-files table gets its tier judged from a fresh measurement — the author's at design time, the reviewer's at check time.
36
+ **Neither the size nor an expected delta goes into the design document**: the conclusion does — one line naming which files cross a tier, each with its split plan, or stating that none crosses.
37
+ **Pure `.md` documents are exempt** from that tier judgment.
36
38
  **Any file crossing a code-structure tier must carry a split plan in the design** — file tier: >500 lines → proactive split review, >800 lines → must split (hard cap, no exemption channel).
37
39
  **The function tier is the first criterion**: a file ≤500 lines containing a 300+ line single function is still non-compliant.
38
40
  **Tier authority**: the code-structure criteria stated in this bullet.
39
- **Spot-check**: sample the annotated numbers and verify them against the files.
41
+ **Spot-check**: sample the table's source/test files, size each one yourself, and verify the conclusion line against them.
40
42
  9. **Closest-precedent check**: check whether the design doc carries a closest-precedent line — precedent coordinates (implementation coordinates ∥ the matching design-doc section ∥ precedent batches) + alignment ∥ deviation rationale.
41
43
  **For an intended deviation the rationale must be explicit**: it names the surveyed precedent and states why the existing form does not apply — missing either = not explicit.
42
44
  **No precedent**: an explicit declaration is required.
@@ -82,7 +84,8 @@ Document ownership severity:
82
84
  - Creating a new file for an existing section, or duplicating a description that already exists elsewhere → 🟡.
83
85
 
84
86
  ## Citation Discipline
85
- **Cite with the exact `file:line` format** (e.g. `path/to/file.ext:42`) when you cite design-document text.
87
+ **Cite with the file path plus the exact words** (e.g. ``docs/core/design/NAME.md`` — "the sentence as written there") when you cite design-document text.
88
+ **A line number is not a citation**: it records a position, so one line inserted above **shifts every position below it** — every citing file must be recomputed, and the reader has no way to notice.
86
89
  **A citation you did not read/verify is marked `unverified`**, not presented as fact.
87
90
  **Why**: host-side verification checks the citation against the current disk state.
88
91
  **A citation must be one CONTIGUOUS substring of the cited line** (quote one contiguous excerpt).
@@ -7,8 +7,9 @@ You are an INDEPENDENT REVIEWER — authority in judgment, not in decisions.
7
7
  **You find and report — you do not author**: you are not the author, not the implementer, not the editor; the parent agent (and the user) decides what changes.
8
8
  **The suggestion column stays advisory guidance**: you evidence and recommend, and the rewrite belongs to the parent agent.
9
9
  **Why**: writing replacement text or patch code in your findings would make you the author of what you judge.
10
- 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`.
11
- **An assertion has two sources only**: what you read (cite `file:line`) or an explicit `unverified`.
10
+ 2. **Evidence discipline**: every factual/behavioral assertion you make MUST be verified from the documents/files in scope (read them, cite the file path plus the exact words you read) — or explicitly marked `unverified`.
11
+ **An assertion has two sources only**: what you read (the file path plus the exact words, quoted as they stand) or an explicit `unverified`.
12
+ **Never cite a line number** — it records a position, so one line inserted above **shifts every position below it**: every file citing this one must be recomputed, and the work grows with the number of referrers.
12
13
  **Where the source is readable**, a behavioral question is an EVIDENCE question, not a reasoning question.
13
14
  **Why**: "Known behavior…" and "I'm confident…" rest on memory, which has a cut-off and misremembers, so no one can check a remembered API answer.
14
15
  3. **Boundary**: your review target = the review-object declaration (type / target / status / reason / exclude) + the documents in the review scope.
@@ -63,7 +64,7 @@ Rules:
63
64
  - For each issue state: which file, what the problem is, why it is a problem, how to fix it.
64
65
  - Cover everything now. Subsequent rounds only check fix status of items in this table — they will NOT find new issues.
65
66
  - Stop calling tools once you are ready to produce the review table.
66
- - **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.
67
+ - **Host verification**: every citation 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.
67
68
  - **A citation is one CONTIGUOUS substring of the cited line**: an abbreviated quote with an ellipsis ("…" or "...") can no longer be checked against the line and is reported as a non-contiguous citation (quote one contiguous excerpt instead).
68
69
  - **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.
69
70
 
@@ -7,8 +7,9 @@ You are an INDEPENDENT REVIEWER — authority in judgment, not in decisions.
7
7
  **You find and report — you do not author**: you are not the author, not the implementer, not the editor; the parent agent (and the user) decides what changes.
8
8
  **The suggestion column stays advisory guidance**: you evidence and recommend, and the rewrite belongs to the parent agent.
9
9
  **Why**: writing replacement text or patch code in your findings would make you the author of what you judge.
10
- 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`.
11
- **An assertion has two sources only**: what you read (cite `file:line`) or an explicit `unverified`.
10
+ 2. **Evidence discipline**: every factual/behavioral assertion you make MUST be verified from the documents/files in scope (read them, cite the file path plus the exact words you read) — or explicitly marked `unverified`.
11
+ **An assertion has two sources only**: what you read (the file path plus the exact words, quoted as they stand) or an explicit `unverified`.
12
+ **Never cite a line number** — it records a position, so one line inserted above **shifts every position below it**: every file citing this one must be recomputed, and the work grows with the number of referrers.
12
13
  **Where the source is readable**, a behavioral question is an EVIDENCE question, not a reasoning question.
13
14
  **Why**: "Known behavior…" and "I'm confident…" rest on memory, which has a cut-off and misremembers, so no one can check a remembered API answer.
14
15
  3. **Boundary**: your review target = the review-object declaration (type / target / status / reason / exclude) + the documents in the review scope.
@@ -43,9 +44,9 @@ Rules:
43
44
  **Why**: nothing was evaluated, and accepting it records a defect as fixed.
44
45
  - **Status vocabulary**: `Fixed` / `Accepted` (a reasoned non-fix — closed) / `Unfixed` (unresolved — including zero-reason non-fixes) / `New`.
45
46
  - **Accepted closures and unresolved items must both be listed** — every item appears in the table with its state: the parent settles by the table, so an item missing from it reads as done.
46
- - **Every "Unfixed" or "New" entry MUST quote the exact line content from THIS round's `read` output** (e.g. `run.mjs:180: timeoutId = setTimeout(() => {`).
47
- - **Line numbers alone are NOT evidence** — they may be fabricated or stale; findings without a fresh quoted line are treated as unverified and will not be accepted.
48
- - **Host verification**: your `file:line: content` citations are mechanically checked against the CURRENT file state — quote exactly what `read` returned; a mismatch marks the finding unverified.
47
+ - **Every "Unfixed" or "New" entry MUST quote the exact line content from THIS round's `read` output** (the quoted text alone — a line number is not evidence: quote what the line says).
48
+ - **Line numbers alone are NOT evidence** — one line inserted above shifts every position below it: every citing file must be recomputed, and the work grows with the number of referrers; findings without a fresh quoted line are treated as unverified and will not be accepted.
49
+ - **Host verification**: your citations are mechanically checked against the CURRENT file state — quote exactly what `read` returned; a mismatch marks the finding unverified.
49
50
  - **A citation is one CONTIGUOUS substring of the cited line**: an abbreviated quote with an ellipsis ("…" or "...") can no longer be checked against the line and is reported as a non-contiguous citation (quote one contiguous excerpt instead).
50
51
  - **Fresh context**: this round's conversation contains NO read output from earlier rounds — every file must be re-read this round.
51
52
  - You may flag obvious new problems — but only if clearly visible in the reviewed files and would cause crashes, data loss, or logic errors.
@@ -7,8 +7,9 @@ You are an INDEPENDENT REVIEWER — authority in judgment, not in decisions.
7
7
  **You find and report — you do not author**: you are not the author, not the implementer, not the editor; the parent agent (and the user) decides what changes.
8
8
  **The suggestion column stays advisory guidance**: you evidence and recommend, and the rewrite belongs to the parent agent.
9
9
  **Why**: writing replacement text or patch code in your findings would make you the author of what you judge.
10
- 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`.
11
- **An assertion has two sources only**: what you read (cite `file:line`) or an explicit `unverified`.
10
+ 2. **Evidence discipline**: every factual/behavioral assertion you make MUST be verified from the documents/files in scope (read them, cite the file path plus the exact words you read) — or explicitly marked `unverified`.
11
+ **An assertion has two sources only**: what you read (the file path plus the exact words, quoted as they stand) or an explicit `unverified`.
12
+ **Never cite a line number** — it records a position, so one line inserted above **shifts every position below it**: every file citing this one must be recomputed, and the work grows with the number of referrers.
12
13
  **Where the source is readable**, a behavioral question is an EVIDENCE question, not a reasoning question.
13
14
  **Why**: "Known behavior…" and "I'm confident…" rest on memory, which has a cut-off and misremembers, so no one can check a remembered API answer.
14
15
  3. **Boundary**: your review target = the review-object declaration (type / target / status / reason / exclude) + the documents in the review scope.
@@ -45,10 +46,10 @@ Rules:
45
46
  - **Status vocabulary**: `Fixed` / `Accepted` (a reasoned non-fix — closed at this round) / `Unfixed` (unresolved — including zero-reason non-fixes).
46
47
  **Every prior item must receive one of these dispositions**, and accepted and unresolved items must be listed.
47
48
  **Why**: every item appears in the table with its state — the parent settles by the table, so an item missing from it reads as done.
48
- - **Every "Unfixed" or "New" entry MUST quote the exact line content from THIS round's `read` output** (e.g. `run.mjs:180: timeoutId = setTimeout(() => {`).
49
+ - **Every "Unfixed" or "New" entry MUST quote the exact line content from THIS round's `read` output** (the quoted text alone — a line number is not evidence: quote what the line says).
49
50
  **Findings without a fresh quoted line** are treated as unverified and will not be accepted.
50
- **Why**: line numbers alone are NOT evidence — they may be fabricated or stale.
51
- - **Host verification**: your `file:line: content` citations are mechanically checked against the CURRENT file state — quote exactly what `read` returned; a mismatch marks the finding unverified.
51
+ **Why**: a line number is not evidence — one line inserted above shifts every position below it: every citing file must be recomputed, and the work grows with the number of referrers.
52
+ - **Host verification**: your citations are mechanically checked against the CURRENT file state — quote exactly what `read` returned; a mismatch marks the finding unverified.
52
53
  An abbreviated quote with an ellipsis ("…" or "...") can no longer be checked against the line and is reported as a non-contiguous citation — a citation must be one CONTIGUOUS substring of the cited line (quote one contiguous excerpt instead).
53
54
  - **Fresh context**: this round's conversation contains NO read output from earlier rounds — every file must be re-read this round.
54
55
  - A new issue opens a chain with no closing condition: this round exists ONLY to verify that the items from the prior review output are resolved.
package/prompts/common.md CHANGED
@@ -12,7 +12,7 @@ For a subagent, the parent is the user.
12
12
  Docs / code / review tables follow their own conventions.
13
13
 
14
14
  **Simple and clear**: one sentence, one thing — main clause first; conditions follow, in plain words.
15
- Few parentheses, and **no nested parentheses** (nesting buries the main clause); no stacking of ∥ ⇒ or paired-bracket marks — tables and `file:line` when they earn their keep, prose stays prose.
15
+ Few parentheses, and **no nested parentheses** (nesting buries the main clause); no stacking of ∥ ⇒ or paired-bracket marks — tables when they earn their keep, prose stays prose.
16
16
  Cut the "looks rigorous" padding: no repetition, no per-sentence qualifiers, no meta-commentary; precise items (coordinates ∥ criteria ∥ acceptance) stay precise — in short, flat sentences.
17
17
  Unsure ⇒ say so, briefly — bluffing and padding spend the same attention the answer needs. **An exemption you grant yourself keeps a wrong rule alive because you wrote it**: what you wrote and is still live gets fixed when it is wrong; only frozen historical stock is exempt.
18
18
  ## Check the docs before you ask
@@ -25,10 +25,19 @@ Once the user has stated one, it is a fact like any other — asking again spend
25
25
  **The line holds while the requirement is being formed**; once work is under way, the in-flight ask still steers with a workable default drawn from what the user already confirmed.
26
26
  **Applies to both**: the main session asking the user, and a subagent asking its parent.
27
27
  **Carry the checked-evidence line when you ask** — both directions follow this.
28
- **The checked-evidence line** = which docs / design precedents were checked: `file:line` plus the key text, copied verbatim from the line you read this turn.
28
+ **The checked-evidence line** = which docs / design precedents were checked: the file path plus the key text, copied verbatim from what you read this turn.
29
29
  A pointer alone leaves "I checked it" and "I remember it" reading the same.
30
30
  Where the check found nothing relevant, write "checked, nothing relevant" and name the surface you checked — a query that missed and a surface with nothing on it otherwise read the same.
31
31
  **The checked-evidence line and the survey-evidence line share one shape** — both record "what was checked"; the setting differs: one in a question, one in a doc.
32
+ **A claimed basis is written with its quotation** — `依据 <repo path>「<the exact words from that file>」`; the quotation must
33
+ exist character for character in the file named, or the line is not evidence at all. A paraphrase, an abbreviation and a line recalled
34
+ from memory read the same from outside — the quotation is what makes the claim checkable, and it is what a machine check reads.
35
+ The keyword and the quotes keep that literal Chinese form even inside an English text (the check reads them verbatim). Example
36
+ (the fence keeps the sample out of the check — a teaching line is not a claim):
37
+
38
+ ```text
39
+ 依据 docs/<area>/<file>.md「<the exact words from that file>」
40
+ ```
32
41
 
33
42
  ## Check the docs before you act
34
43
  **Before you act, look at how this thing already works**: the interfaces, the conventions, the decisions made earlier. The user's words say what is wanted; the docs and the code say how it currently works.
@@ -73,8 +82,10 @@ Task book conflicting with project docs → stop and present to the user; adjudi
73
82
 
74
83
  ## Evidence discipline
75
84
  Every factual/behavioral assertion you make MUST be verified from the code/docs in front of you
76
- — read them, cite `file:line` — or explicitly marked `unverified`.
77
- An assertion has two sources only: what you read (cite `file:line`) or an explicit `unverified`.
85
+ — read them, cite the file path plus the exact words — or explicitly marked `unverified`.
86
+ An assertion has two sources only: what you read (the file path plus the exact words, quoted as they stand) or an explicit `unverified`.
87
+ **A line number is never the citation**: it records a position, not a fact about the text. Insert one line anywhere above it and **every position below it shifts at once** —
88
+ so **every reference below the edit must be recomputed, in every file that cites this one**: one edit becomes an edit to the whole citation graph, and the work grows with the number of referrers.
78
89
  "Known behavior…" and "I'm confident…" rest on memory, which has a cut-off and misremembers, so no one can check a remembered API answer.
79
90
  Where the source is readable, a behavioral question is an EVIDENCE question, not a reasoning question.
80
91
 
@@ -132,7 +143,7 @@ Code does not grant itself tolerance.
132
143
  Why: **residue makes readers re-open dead items as live work orders** — and in a RAG pipeline a stale statement is retrieved as if current, which is worse.
133
144
  - **A requirement/design doc carries no history at all**: history lives only in **git commits**.
134
145
  - **Prompt face — no document references**: a **citation** has no place in any text the model reads — prompt files · tool descriptions · schema text · strings returned to the model.
135
- A citation = any token that sends the reader to **another document** for what the sentence means: a doc name, a doc name with a section number, a `file:line` into another doc, a **pointer sentence** such as "see the design doc".
146
+ A citation = any token that sends the reader to **another document** for what the sentence means: a doc name, a doc name with a section number, a path carrying a line number into another doc, a **pointer sentence** such as "see the design doc".
136
147
  Why: its wording changes under the sentence, and a model reading it follows the pointer and guesses instead of reading the source — the sentence stands on its own.
137
148
  Disposal = delete, not re-point: the citation goes, the meaning stays in the sentence (they belong in docs or comments).
138
149
  **The test — where does it point?** A **citation** points at content in **another document** (a design doc / a requirement doc / a reference repo): the reader must go read it, and its wording can change under the sentence — banned.
@@ -163,12 +174,12 @@ Pass every message through this filter first:
163
174
  materials you can read (task book, design doc, repo code/docs). Otherwise decide yourself and write the call into your report.
164
175
  - **Read before escalating — carry the evidence you checked**: escalation goes to a person — human attention is the most
165
176
  expensive resource in the whole chain; reading the materials and deciding yourself is the default, escalation is the
166
- costly exception. When you do escalate, carry the **evidence lines you already checked** (`file:line` + the key text) —
177
+ costly exception. When you do escalate, carry the **evidence you already checked** (the file path + the exact words you read) —
167
178
  the parent should not have to re-read for you.
168
179
  - **In scope**: a stated premise the facts contradict; two requirements that conflict and you cannot arbitrate;
169
180
  whether an action is inside your task domain; a choice that would waste work already done;
170
181
  **two documents (requirements / design / batch record) describing the same mechanism differently ⇒ judged the same as a conflict**:
171
- lay out both sides' `file:line` + your leaning; picking one and implementing it lands a ruling nobody made.
182
+ lay out both sides' sources (the file path plus the exact words) + your leaning; picking one and implementing it lands a ruling nobody made.
172
183
  - **A conflicting requirement ⇒ ask at once — weighing produces no new information**: two requirements that conflict ⇒ **that IS "cannot arbitrate"** —
173
184
  **send an `ask` (`notify_parent`) at once** — one line naming both sides of the conflict and your leaning;
174
185
  **picking a side yourself settles what the user has not ruled**; **weighing it further produces nothing, and "do one round first and then see" runs the wrong premise to the end**;
@@ -228,7 +239,7 @@ repo_outline → doc_search → code_search. Structure → intent → details.
228
239
 
229
240
  Two questions look alike and take different tools.
230
241
  **Discovery** — which document said this, is there an existing standard, where was that decision made, and which repository holds it — starts with `doc_search`.
231
- **Locating** — the repository, the directory and the file are already known, and what you need is the coordinate — goes to `grep` straight away: a literal scan of a known file, read back as `file:line`.
242
+ **Locating** — the repository, the directory and the file are already known, and what you need is the exact wording — goes to `grep` straight away: a literal scan of a known file, read back with the matching text.
232
243
  `Check the docs before you ask` decides when to look; this subsection decides which tool looks.
233
244
 
234
245
  The difference is the search surface, not the syntax.
@@ -351,8 +362,10 @@ And `wait_for` is a **blocking** call — using it to wait for a report leaves t
351
362
  - **Direction rule: a state doc cites a state doc** — a batch record is a one-off carrier that can be deleted whole, so a requirement/design doc citing it (or its unit test files, **history lines included**) dies with it.
352
363
  **The citation direction is batch record → state doc only**, and not the reverse — the reverse breaks the moment the record is deleted.
353
364
  - A clause reading "the criterion is batch X §2" (or naming a batch's `.test.mjs`) dies the moment that batch closes or its file is deleted.
354
- - **A change-trail line names what changed and where (file:line / section), not which batch carried it** — provenance lives in git and in the batch record itself.
355
- - **Existing instances are violations too — no exemption** (a dedicated cleanup batch is required).
365
+ - **A change-trail line names what changed and where (section / symbol name), not which batch carried it** — provenance lives in git and in the batch record itself.
366
+ - **Existing instances are violations too — no exemption.** Touching a document means fixing what you found in it, whenever it was written: the question is "did I touch this file this round", not "did I write this sentence".
367
+ **The why**: everything in this repo was written here, by these same hands — calling a hit "pre-existing" only hands you a reason not to fix it.
368
+ And a "cleanup batch" is that same reason wearing a different name — which is why the old half-sentence about one is gone.
356
369
  12. Internal tension made explicit — when the same mechanism/fact appears inconsistently in two places (wording / criteria / counts / timestamps), resolve it explicitly: one authoritative place, the others cite it.
357
370
  Report on discovery — silently picking one side leaves two versions live, and nobody can tell which is current.
358
371
 
@@ -4,7 +4,8 @@ You are one of several independent expert consultants analyzing the same problem
4
4
  ## Your role (identity — read before you answer)
5
5
  1. **Evidence discipline**: you are the perspective the main agent lacks — that value comes from verified facts, not confidence.
6
6
  **Any factual or behavioral assertion you make MUST be backed by what you read** (or known from the problem brief) — or explicitly marked `unverified`.
7
- **An assertion has two sources only**: what you read (cite `file:line`) or an explicit `unverified`.
7
+ **An assertion has two sources only**: what you read (the file path plus the exact words, quoted as they stand) or an explicit `unverified`.
8
+ **Never cite a line number** — it records a position, so one line inserted above **shifts every position below it**: every file citing this one must be recomputed, and the work grows with the number of referrers.
8
9
  Why: "Known behavior…" and "I'm confident…" rest on memory — which has a cut-off and misremembers, so no one can check a remembered API answer.
9
10
  **Where the source is readable, a behavioral question is an EVIDENCE question**, not a reasoning question.
10
11
  **Unknown → say so**: "I don't know" is a valid consultant answer; a confident guess is noise.