thincoder 0.12.9 → 0.12.10

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.
package/README.md CHANGED
@@ -209,6 +209,12 @@ Code conventions: pure `.mjs`, no semicolons, no npm dependencies allowed (inclu
209
209
 
210
210
  ## Changelog
211
211
 
212
+ ### 0.12.10 (2026-08)
213
+ - **Code-quality pass (advisor subsystem):**
214
+ - **Drop 11 unused exports** — internal-use symbols no longer leak through the module API (advisor table headers/constants, plan reminders, token-UUID helper, shrinkOversized).
215
+ - **Advisor tool-timeout timer is now cleared** when the tool wins the race (no dangling timers); static import replaces a dynamic import on the hot path.
216
+ - **Advisor re-review no longer trusts git output** — the follow-up path previously injected a `git diff HEAD` snapshot: once fixes were committed the diff was empty and the model read "no changes" as "no fixes", misreporting fixed items as unfixed. The follow-up now injects **no git information at all** — verification is `read`-only, evidence must quote this round's read output (line numbers from the stale prior table are not evidence), and dead snapshot-dedup fields were removed.
217
+
212
218
  ### 0.12.9 (2026-08)
213
219
  - **Prompt-system quality pass (both CLI and VS Code extension, byte-identical sync):**
214
220
  - **Advisor-after-code rule moved from system.md to discipline.md** — engineering mode no longer receives the conflicting "call advisor after changing code" instruction (its review-timing rules say do not call unprompted). Standard mode behavior unchanged.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thincoder",
3
- "version": "0.12.9",
3
+ "version": "0.12.10",
4
4
  "description": "Thin coding agent - zero dependencies, no build step, Node.js native. Sharp code, zero bloat.",
5
5
  "keywords": [
6
6
  "ai",
@@ -6,8 +6,8 @@ import { join } from "node:path"
6
6
 
7
7
  export const ADVISOR_MD_PATH = ".thincoder/advisor.md"
8
8
  export const ADVISOR_TABLE_HEADER = "| # | File | Severity | Issue | Suggestion |"
9
- export const CONVERGENCE_TABLE_HEADER = "| # | Orig# | File | Severity | Status | Notes |"
10
- export const AGENT_RESPONSE_HEADER = "| # | Action | Detail |"
9
+ const CONVERGENCE_TABLE_HEADER = "| # | Orig# | File | Severity | Status | Notes |"
10
+ const AGENT_RESPONSE_HEADER = "| # | Action | Detail |"
11
11
  export const LEGACY_ADVISOR_HEADER = "| # | 文件 | 严重程度 | 问题描述 | 建议修复 |"
12
12
 
13
13
  const DEFAULT_CRITERIA = `Review the code changes, focusing on:
@@ -57,7 +57,6 @@ export function buildAdvisorUserMessage(agent, _prior, reviewType, designToken =
57
57
  }
58
58
 
59
59
  // Pre-collected changes — the design doc diff.
60
- // _advisorLastSnapshot is only consumed by code-review convergence — skip the write here.
61
60
  const snapshots = collectRepoSnapshots(repos, agent.cwd)
62
61
  if (snapshots.length > 0) {
63
62
  parts.push("## Design Document (git diff)")
@@ -96,7 +95,7 @@ export function buildAdvisorUserMessage(agent, _prior, reviewType, designToken =
96
95
  return parts.join("\n")
97
96
  }
98
97
 
99
- // Convergence data (round 2+)
98
+ // Convergence data (round 2+). Design reviews returned above — only code reviews reach here.
100
99
  if (prior && (agent._advisorRound || 0) > 0) {
101
100
  const response = extractAgentResponseTable(agent.history, prior.sinceIdx)
102
101
  || "(Agent did not provide a response table — re-evaluate each issue)"
@@ -164,10 +163,11 @@ export function buildAdvisorUserMessage(agent, _prior, reviewType, designToken =
164
163
  parts.push("## Instructions")
165
164
  parts.push("1. IMPORTANT: in the diff, `-` lines are REMOVED content (no longer in the file), `+` lines are ADDED. The prior issue table (if any) is HISTORY — always verify current file state with `read` before judging an item.")
166
165
  if (isReReview) {
167
- parts.push("2. STALE-CONTEXT WARNING: any diff embedded in earlier messages is a historical snapshot — treat it as expired. Only the \"Current Changes\" section above and fresh `read` results describe the current state. Never quote a `-` line from any diff as if it were live code.")
168
- parts.push("3. Do NOT re-read AGENTS.md / design docsconventions were established in round 1. Focus on verifying the prior issue table against the current diff.")
169
- parts.push("4. `read` only the files touched by the fixes. Batch independent reads/greps in a single reply.")
170
- parts.push("5. Produce your verification table. Do not re-read content you already have.")
166
+ parts.push("2. STALE-CONTEXT WARNING: any diff or file content embedded in earlier messages is a historical snapshot — treat it as expired. Only fresh `read` results describe the current state. Never quote a `-` line from an earlier diff as if it were live code.")
167
+ parts.push("3. Verify the prior issue table against the CURRENT FILE STATE use `read`, never `git diff` alone. Fixes may already be committed: an empty `git diff` does NOT mean nothing changed. `git log -3` shows recent commits.")
168
+ parts.push("4. `read` the files in the Review Scope in full — ALWAYS, regardless of what `git diff` shows. Batch reads/greps in a single reply.")
169
+ parts.push("5. Evidence rule: every 'Unfixed'/'New' finding MUST quote the exact line content from THIS round's `read` output (e.g. `run.mjs:180: timeoutId = setTimeout(...)`). Line numbers alone are NOT evidence — they may come from the stale prior table. Findings without a fresh quoted line are treated as unverified and will not be accepted.")
170
+ parts.push("6. Produce your verification table. Do not re-read content you already have.")
171
171
  } else {
172
172
  parts.push("2. Read `AGENTS.md` / design docs only if they exist (check once; do not re-probe with multiple patterns).")
173
173
  parts.push("3. `read` changed files for full context beyond the diff. Batch independent reads/greps in a single reply instead of one call per round-trip.")
@@ -5,8 +5,8 @@
5
5
  import { execFileSync } from "node:child_process"
6
6
  import { dirname, basename, resolve } from "node:path"
7
7
 
8
- export const GIT_TIMEOUT = 5_000
9
- export const MAX_EMBEDDED_DIFF = 50_000
8
+ const GIT_TIMEOUT = 5_000
9
+ const MAX_EMBEDDED_DIFF = 50_000
10
10
 
11
11
  /**
12
12
  * Find the git repository roots that contain the agent's touched files.
@@ -8,7 +8,7 @@ import { toOpenAISchema } from "../tools/index.mjs"
8
8
  import { prepareAdvisorMessages } from "../advisor.mjs"
9
9
  import { extractPriorIssueTable } from "../advisor/history.mjs"
10
10
 
11
- export const MAX_ADVISOR_TURNS = 100
11
+ const MAX_ADVISOR_TURNS = 100
12
12
  // Mechanical convergence cap: the protocol assumes up to 5 rounds suffice
13
13
  // (full review, verify+fix cycles, strict verification). A 6th call means the
14
14
  // model is looping — refuse it instead of burning tokens on a review that cannot
@@ -172,18 +172,21 @@ async function runAdvisorToolLoop(provider, messages, onOutput, signal, agent, c
172
172
  if (!tool) {
173
173
  result = `Error: unknown tool "${tc.name}". Available: ${[...ADVISOR_TOOL_BY_NAME.keys()].join(", ")}`
174
174
  } else {
175
- // Execute with timeout
175
+ // Execute with timeout (clear the timer when the tool wins the race —
176
+ // otherwise up to MAX_ADVISOR_TURNS dangling timers accumulate)
176
177
  try {
177
- const toolPromise = tool.execute(args, {
178
- cwd,
179
- agent,
180
- onOutput,
181
- signal,
178
+ let timeoutId
179
+ const timeoutPromise = new Promise((_, reject) => {
180
+ timeoutId = setTimeout(() => reject(new Error(`tool timeout after ${TOOL_TIMEOUT_MS}ms`)), TOOL_TIMEOUT_MS)
182
181
  })
183
- const timeoutPromise = new Promise((_, reject) =>
184
- setTimeout(() => reject(new Error(`tool timeout after ${TOOL_TIMEOUT_MS}ms`)), TOOL_TIMEOUT_MS)
185
- )
186
- result = await Promise.race([toolPromise, timeoutPromise])
182
+ try {
183
+ result = await Promise.race([
184
+ tool.execute(args, { cwd, agent, onOutput, signal }),
185
+ timeoutPromise,
186
+ ])
187
+ } finally {
188
+ clearTimeout(timeoutId)
189
+ }
187
190
  } catch (e) {
188
191
  const errorType = e.message.includes("timeout") ? "timeout"
189
192
  : e.message.includes("ENOENT") ? "file_not_found"
@@ -224,7 +227,7 @@ async function runAdvisorToolLoop(provider, messages, onOutput, signal, agent, c
224
227
  }
225
228
 
226
229
  /** Resolve the advisor's provider: cfg.provider/model when set, otherwise the main agent's provider */
227
- export function resolveAdvisorProvider(agent) {
230
+ function resolveAdvisorProvider(agent) {
228
231
  const cfg = agent.config?.advisor
229
232
  if (cfg?.provider) {
230
233
  try {
package/src/advisor.mjs CHANGED
@@ -37,8 +37,7 @@
37
37
  import { readFileSync } from "node:fs"
38
38
  import { join, dirname } from "node:path"
39
39
  import { fileURLToPath } from "node:url"
40
- import { createHash } from "node:crypto"
41
- import { findReviewRepos, collectRepoSnapshots } from "./advisor/repos.mjs"
40
+ import { findReviewRepos } from "./advisor/repos.mjs"
42
41
  import { extractPriorIssueTable, extractAgentResponseTable } from "./advisor/history.mjs"
43
42
  import { buildAdvisorUserMessage } from "./advisor/messages.mjs"
44
43
  // Re-export for run.mjs and tests (keeps their imports from "../advisor.mjs" stable)
@@ -111,31 +110,18 @@ export function buildAdvisorFollowUp(agent, _prior) {
111
110
  "## Instructions",
112
111
  round === 2
113
112
  ? "Verify each item in the prior table. Flag any obvious NEW issues introduced by the fixes (crashes, data loss, logic errors — not style). Produce a verification table."
114
- : "Strictly verify ONLY the items in the prior table against the current diff. Do NOT look for new issues.",
113
+ : "Strictly verify ONLY the items in the prior table against the CURRENT FILE STATE (use `read` — an empty diff does not mean the fixes are absent). Do NOT look for new issues.",
115
114
  "",
116
- "IMPORTANT: in any embedded diff, `-` lines are REMOVED content (no longer in the file), `+` lines are ADDED. The prior issue table is HISTORY — always verify current file state with `read` before judging an item as fixed or unfixed.",
115
+ "IMPORTANT: the prior issue table is HISTORY — always verify current file state with `read` before judging an item as fixed or unfixed.",
117
116
  // Round-aware evidence rule: "New" entries only exist in round 2 (round 3+ forbids them).
118
- `STALE-CONTEXT WARNING: all diffs in earlier messages (including round 1) are historical snapshots files have changed since. Only THIS message's "Current Changes" section and fresh \`read\` results are authoritative. Any "Unfixed" entry${round === 2 ? ' (and any "New" entry)' : ""} MUST cite read-verified evidence (file:line from a \`read\` of the current file); uncited findings are unverified and will be ignored.`,
117
+ `STALE-CONTEXT WARNING: only fresh \`read\` results describe the current state never judge from earlier snapshots or from \`git diff\` (committed fixes never show in \`git diff HEAD\`). Read the files to verify. Any "Unfixed" entry${round === 2 ? ' (and any "New" entry)' : ""} MUST quote the exact line content from THIS round's \`read\` output (e.g. \`run.mjs:180: timeoutId = setTimeout(...)\`); line numbers alone are NOT evidence (they may come from the stale prior table). Uncited findings are unverified and will be ignored.`,
119
118
  "",
120
- "Do NOT re-read AGENTS.md / design docs or re-run git status/diff (current changes are below) you already have full context from previous rounds.",
119
+ "Do NOT re-read AGENTS.md / design docs. Verify fix status with \`read\` only — do not rely on git output: a clean working tree does not mean fixes are absent (they may be committed).",
121
120
  "",
122
121
  ]
123
- const snapshots = collectRepoSnapshots(findReviewRepos(agent), agent.cwd)
124
- const snapshotText = snapshots.join("\n")
125
- const snapshotHash = snapshotText ? createHash("sha1").update(snapshotText).digest("hex") : null
126
- // Skip re-pushing an identical diff (e.g. advisor re-run without any file changes) —
127
- // the previous snapshot is already in the conversation, duplicating it wastes tokens.
128
- if (snapshots.length === 0) {
129
- parts.push("## Current Changes", "(No git repository or no changes detected.)")
130
- } else if (snapshotHash && snapshotHash === agent._advisorLastSnapshotHash) {
131
- // Hash match → the snapshot from the previous round is still current.
132
- // Using hash instead of full-text comparison avoids keeping the entire diff
133
- // string in memory and handles edge cases (e.g. file changed then reverted).
134
- parts.push("## Current Changes", "(No changes since your previous review — the diff snapshot is identical, so the one from your last round remains valid for this round.)")
135
- } else {
136
- parts.push("## Current Changes (git status + git diff HEAD, refreshed)", ...snapshots)
137
- }
138
- agent._advisorLastSnapshotHash = snapshotHash
122
+ // Deliberately NO git information injected here (no diff snapshot, no git context):
123
+ // git output misled re-reviews — committed fixes never show in `git diff HEAD`, so
124
+ // the model read "no changes" as "no fixes". Verification is `read`-only by design.
139
125
  return parts.join("\n")
140
126
  }
141
127
 
@@ -165,7 +151,6 @@ export function prepareAdvisorMessages(agent, reviewType, designToken = null, do
165
151
  // a follow-up "Verify Prior Table" would be meaningless; start a fresh full review
166
152
  if (!prior) {
167
153
  agent._advisorSession = null
168
- agent._advisorLastSnapshotHash = null
169
154
  session = null
170
155
  // Only reset the round counter on a truly fresh start (no prior reviews at all).
171
156
  // If _advisorRound > 0, there WAS a prior review — it just passed (all-clear).
@@ -188,7 +173,6 @@ export function prepareAdvisorMessages(agent, reviewType, designToken = null, do
188
173
  // Fresh session. Only reset round if this is truly the first review.
189
174
  // If _advisorRound > 0, there was a prior review that passed (all-clear).
190
175
  if (!agent._advisorRound) agent._advisorRound = 0
191
- agent._advisorLastSnapshot = null
192
176
  if (!prior) {
193
177
  // Tell the advisor why no prior issue table is present
194
178
  session[1] = {
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { randomUUID, createHmac } from "node:crypto"
7
7
  import { runAdvisorReview } from "../advisor/run.mjs"
8
+ import { isDocFile } from "../advisor/repos.mjs"
8
9
 
9
10
  const TOKEN_EXPIRY_MS = 3600000 // 1 hour
10
11
  const TOKEN_SECRET = process.env.THINCODER_TOKEN_SECRET || "thincoder-default-secret"
@@ -50,7 +51,7 @@ export function validateDesignToken(token) {
50
51
  }
51
52
 
52
53
  /** Extract UUID from signed token for regex matching */
53
- export function extractTokenUUID(token) {
54
+ function extractTokenUUID(token) {
54
55
  const parts = token.split(":")
55
56
  return parts.length >= 1 ? parts[0] : token
56
57
  }
@@ -118,7 +119,6 @@ export const advisorTool = {
118
119
 
119
120
  // Design review: validate that documents are in docs/ or are recognized doc files
120
121
  if (reviewType === "design" && documents) {
121
- const { isDocFile } = await import("../advisor/repos.mjs")
122
122
  const invalidDocs = documents.filter((doc) => {
123
123
  // Allow docs/ directory and recognized doc files (METHODOLOGY.md, README.md, etc.)
124
124
  if (doc.startsWith("docs/") || doc.startsWith("docs\\")) return false
@@ -133,7 +133,6 @@ export const advisorTool = {
133
133
  if (reviewType === "design") {
134
134
  agent._advisorRound = 0
135
135
  agent._advisorSession = null
136
- agent._advisorLastSnapshotHash = null // stale diff dedup baseline must not leak into the next code review
137
136
  }
138
137
 
139
138
  // Generate the design token BEFORE the review and inject it into the advisor's prompt.
@@ -8,17 +8,17 @@
8
8
  * user sends a new message — so the constraint never fades from context.
9
9
  */
10
10
 
11
- export const PLAN_FULL_REMINDER =
11
+ const PLAN_FULL_REMINDER =
12
12
  "[System reminder: plan mode is ON. Workflow: (1) explore/read codebase with read-only tools, " +
13
13
  "(2) design a solution considering trade-offs, (3) present your plan by calling plan with action='exit' " +
14
14
  "so the user can approve it. Only read-only tools are allowed — do not write, edit, or run mutation commands. " +
15
15
  "Your turn must end with either a clarifying question to the user or a call to plan with action='exit'.]"
16
16
 
17
- export const PLAN_SPARSE_REMINDER =
17
+ const PLAN_SPARSE_REMINDER =
18
18
  "[System reminder: plan mode still active — read-only tools only (the current plan file exempt). " +
19
19
  "Design the solution, then call plan with action='exit' for user approval.]"
20
20
 
21
- export const PLAN_EXIT_REMINDER =
21
+ const PLAN_EXIT_REMINDER =
22
22
  "[System reminder: plan mode is now OFF. Start implementing your plan — edit files, run commands. " +
23
23
  "No need for a task list (plan already covered that) or further confirmation.]"
24
24
 
@@ -180,11 +180,10 @@ export function mergeChildMutations(parent, child) {
180
180
  parent._verifiedThisRun = false
181
181
  parent._verifyPassed = undefined
182
182
  }
183
- // Fresh code → fresh convergence budget + stale session/diff cleanup.
183
+ // Fresh code → fresh convergence budget + stale session cleanup.
184
184
  // _advisorRound reset ensures new code gets a full round-1 review;
185
- // _advisorSession + _advisorLastSnapshotHash prevent cross-contamination.
185
+ // _advisorSession prevents cross-contamination between reviews.
186
186
  parent._advisorRound = 0
187
187
  parent._advisorSession = null
188
- parent._advisorLastSnapshotHash = null
189
188
  return true
190
189
  }
package/src/agent.mjs CHANGED
@@ -100,7 +100,7 @@ export function createAgent({
100
100
  _mutatedThisRun: false, _verifiedThisRun: false, _verifyPassed: undefined, _calledAdvisorThisRun: false,
101
101
  _engDesignReviewed: false, // eng-coder: design review gate passed (hard gate in dispatch.mjs)
102
102
  _engDesignToken: null, // issued by advisor(type="design"); required to spawn eng-coder
103
- _touchedFiles: [], _verifyRetries: 0, _advisorRound: 0, _advisorSession: null, _advisorLastSnapshotHash: null,
103
+ _touchedFiles: [], _verifyRetries: 0, _advisorRound: 0, _advisorSession: null,
104
104
  _lastEngState: false,
105
105
  _pendingReminders: [],
106
106
  _pendingTimers: [],
@@ -131,7 +131,6 @@ export async function runAgent(agent, input, callbacks = {}, { depth = 0, signal
131
131
  agent._verifyRetries = 0
132
132
  agent._advisorRound = 0
133
133
  agent._advisorSession = null // advisor session is per-run: discard when the task ends, next task starts fresh
134
- agent._advisorLastSnapshotHash = null // dedup baseline is per-run too — stale snapshot could wrongly suppress a diff refresh
135
134
  agent._emptyRetries = 0 // empty-response retry budget is per-run: a fresh user turn restarts from zero
136
135
  agent._compressFailures = 0 // compaction summary-failure counter is per-run: a fresh user turn restarts from zero
137
136
  }
package/src/context.mjs CHANGED
@@ -237,7 +237,7 @@ const OVERSIZE_CONTENT_LIMIT = 8_000
237
237
  * does not touch reasoning_content (DeepSeek/Kimi echo protocol) or tool_calls pairing structure — no protocol 400 risk.
238
238
  * Only called after compressIfNeeded determines threshold is exceeded. Returns whether any message was truncated.
239
239
  */
240
- export function shrinkOversized(agent, limit = OVERSIZE_CONTENT_LIMIT) {
240
+ function shrinkOversized(agent, limit = OVERSIZE_CONTENT_LIMIT) {
241
241
  let shrunk = false
242
242
  for (const m of agent.history) {
243
243
  if ((m.role !== "user" && m.role !== "tool") || typeof m.content !== "string") continue
@@ -5,21 +5,21 @@ You have read-only tools to explore the codebase.
5
5
  You have a budget of 30 tool rounds (chat turns). Hard mechanical cap: 100 rounds.
6
6
 
7
7
  Review workflow:
8
- 1. The files to review are listed in the review scope — read them in full. The prior issue table is HISTORY from a previous review, not current state.
8
+ 1. The affected files are named in the prior issue table — read them in full. The prior issue table is HISTORY from a previous review, not current state.
9
9
  2. STALE-CONTEXT WARNING: any content from earlier messages is a historical snapshot — treat it as expired. Only fresh `read` results describe the current state.
10
10
  3. Project conventions were established in round 1 — do NOT re-read AGENTS.md / design docs unless a fix appears to contradict the task itself.
11
- 4. Read the specified files for full context. **Batch independent tool calls in one reply.** ALWAYS verify current file content with `read` before judging a prior-table item as fixed or unfixed — never decide based on the prior table alone.
11
+ 4. **ALWAYS verify current file content with `read` before judging a prior-table item as fixed or unfixed — never decide based on the prior table alone.** An empty `git diff` does NOT mean nothing changed: fixes may already be committed (`git log -3` shows recent commits) — `read` the files named in the prior table regardless of the diff. Batch independent tool calls in one reply.
12
12
  5. Use grep or lsp to trace callers, imports, and dependencies — only where genuinely needed.
13
13
  6. Produce your review table.
14
14
 
15
- Budget: read only the files affected by the prior-table items. If at 15 rounds you have not yet verified all items, wrap up.
15
+ Budget: read only the files named in the prior-table items. If at 15 rounds you have not yet verified all items, wrap up.
16
16
 
17
17
  Rules:
18
18
  - Respect the project's stated platform requirements — do not flag features as errors if they are valid under the project's target environment.
19
19
  - Primarily check fix status of items in the prior issue table.
20
20
  - For items marked "fixed": verify they were actually fixed.
21
21
  - For items marked "not an issue": evaluate whether the reasoning is sound.
22
- - Every "Unfixed" or "New" entry MUST cite read-verified evidence — file:line from a `read` of the CURRENT file (e.g. `src/x.mjs:42`). Findings without such evidence are treated as unverified and will not be accepted.
22
+ - Every "Unfixed" or "New" entry MUST quote the exact line content from THIS round's `read` output (e.g. `run.mjs:180: timeoutId = setTimeout(...)`). Line numbers alone are NOT evidence — they may come from the stale prior table. Findings without a fresh quoted line are treated as unverified and will not be accepted.
23
23
  - You may flag obvious new problems — but only if clearly visible in the reviewed files and would cause crashes, data loss, or logic errors.
24
24
  - Do NOT nitpick style or naming.
25
25
  - Output a Markdown table listing all remaining problems (old or new):
@@ -5,21 +5,21 @@ You have read-only tools to explore the codebase.
5
5
  You have a budget of 30 tool rounds (chat turns). Hard mechanical cap: 100 rounds.
6
6
 
7
7
  Review workflow:
8
- 1. The files to review are listed in the review scope — read them in full. The prior issue table is HISTORY from a previous review, not current state.
8
+ 1. The affected files are named in the prior issue table — read them in full. The prior issue table is HISTORY from a previous review, not current state.
9
9
  2. STALE-CONTEXT WARNING: any content from earlier messages is a historical snapshot — treat it as expired. Only fresh `read` results describe the current state.
10
10
  3. Project conventions were established in round 1 — do NOT re-read AGENTS.md / design docs.
11
- 4. Read the specified files for full context. **Batch independent tool calls in one reply.** ALWAYS verify current file content with `read` before judging a prior-table item as fixed or unfixed — never decide based on the prior table alone.
11
+ 4. **ALWAYS verify current file content with `read` before judging a prior-table item as fixed or unfixed — never decide based on the prior table alone.** An empty `git diff` does NOT mean nothing changed: fixes may already be committed (`git log -3` shows recent commits) — `read` the files named in the prior table regardless of the diff. Batch independent tool calls in one reply.
12
12
  5. Verify fix status of each item in the prior issue table.
13
13
  6. Produce your review table.
14
14
 
15
- Budget: read only the files affected by the prior-table items. If at 15 rounds you have not yet verified all items, wrap up.
15
+ Budget: read only the files named in the prior-table items. If at 15 rounds you have not yet verified all items, wrap up.
16
16
 
17
17
  Rules:
18
18
  - Respect the project's stated platform requirements — do not flag features as errors if they are valid under the project's target environment.
19
19
  - Only check fix status of items in the prior issue table.
20
20
  - For items marked "fixed": verify they were actually fixed.
21
21
  - For items marked "not an issue": evaluate whether the reasoning is sound.
22
- - Every "Unfixed" entry MUST cite read-verified evidence — file:line from a `read` of the CURRENT file (e.g. `src/x.mjs:42`). Findings without such evidence are treated as unverified and will not be accepted.
22
+ - Every "Unfixed" entry MUST quote the exact line content from THIS round's `read` output (e.g. `run.mjs:180: timeoutId = setTimeout(...)`). Line numbers alone are NOT evidence — they may come from the stale prior table. Findings without a fresh quoted line are treated as unverified and will not be accepted.
23
23
  - Output a Markdown table. Only list items that still have problems:
24
24
  | # | Orig# | File | Severity | Status | Notes |
25
25
  |---|-------|------|----------|--------|-------|