iterate-plugin 2.7.3 → 2.8.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.
@@ -1,9 +1,22 @@
1
1
  import { defineTool } from '@deepseek-ai/dsh-tools'
2
2
  import type { JsonValue } from '@deepseek-ai/dsh-session'
3
3
  import { loadEffectiveConfig, resolveProjectRoot } from '../config-loader.ts'
4
- import { buildReviewPlan, buildReviewReport } from '../review.ts'
4
+ import {
5
+ buildReviewPlan,
6
+ buildReviewReport,
7
+ sanitizeRounds,
8
+ validateRoundsSchema,
9
+ } from '../review.ts'
5
10
  import { buildFinalReviewReport, metaReviewReport } from '../meta-review.ts'
11
+ import { evidenceToPlain, verifyFindings } from '../evidence.ts'
12
+ import {
13
+ collectScopeFiles,
14
+ computeCoverage,
15
+ coverageToDict,
16
+ } from '../review-scope.ts'
17
+ import { resolveChangedFiles } from '../git-scope.ts'
6
18
  import type { KnownIntentional, ReviewFinding, ReviewReport, ReviewRound } from '../types.ts'
19
+ import type { CoverageResult } from '../review-scope.ts'
7
20
 
8
21
  /** Default round cap when neither the arg nor config provides one. */
9
22
  const DEFAULT_MAX_REVIEW_ROUNDS = 3
@@ -94,6 +107,21 @@ export function registerReviewTool(ctx: { tools: { register: (def: ReturnType<ty
94
107
  found: { type: 'boolean' },
95
108
  plan: { type: 'json' },
96
109
  report: { type: 'json' },
110
+ schemaValidation: {
111
+ type: 'json',
112
+ description:
113
+ 'For `aggregate`: per-round schema validation results (round, valid, issues). ' +
114
+ 'Present only when reviewer.output_schema_validation is enabled; the workflow ' +
115
+ 'retries rounds with valid=false (≤2 times) before forwarding findings.',
116
+ },
117
+ evidence: { type: 'json' },
118
+ coverage: {
119
+ type: 'json',
120
+ description:
121
+ 'For `meta-review`: prompt-informative scope coverage result ' +
122
+ '(assigned vs self-reported reads). Present only when ' +
123
+ 'reviewer.coverage_validation is enabled and readFiles were supplied.',
124
+ },
97
125
  finalReport: { type: 'json' },
98
126
  error: { type: 'string' },
99
127
  },
@@ -118,7 +146,23 @@ export function registerReviewTool(ctx: { tools: { register: (def: ReturnType<ty
118
146
  const maxReviewRounds = args.maxReviewRounds ?? config.max_rounds ?? DEFAULT_MAX_REVIEW_ROUNDS
119
147
  const knownIntentional = (config.personalization as { known_intentional?: KnownIntentional[] } | undefined)
120
148
  ?.known_intentional
121
- const plan = buildReviewPlan({ config, mode, maxReviewRounds, knownIntentional })
149
+ // changed-only scope: resolve the changed-file set against
150
+ // git.target_branch before building the plan so reviewers get the
151
+ // concrete file list (and the plan auto-falls back to full when there
152
+ // are no changes). git failures degrade to a full-scope plan.
153
+ let changedFiles: string[] | undefined
154
+ if (config.review?.scope === 'changed-only') {
155
+ const gitScope = await resolveChangedFiles(projectRoot, config.git?.target_branch ?? 'main')
156
+ changedFiles = gitScope.changedFiles
157
+ }
158
+ // Full-codebase review: pre-collect the source inventory so
159
+ // buildReviewPlan can batch it into per-chunk reviewer tasks
160
+ // (coverage enforcement).
161
+ let scopeFiles: string[] | undefined
162
+ if (config.review?.scope === 'full') {
163
+ scopeFiles = collectScopeFiles(projectRoot, { scope: 'full' })
164
+ }
165
+ const plan = buildReviewPlan({ config, mode, maxReviewRounds, knownIntentional, changedFiles, scopeFiles })
122
166
  return { operation: 'plan', mode, found: true, plan: plan as unknown as JsonValue }
123
167
  }
124
168
 
@@ -143,16 +187,34 @@ export function registerReviewTool(ctx: { tools: { register: (def: ReturnType<ty
143
187
  const maxReviewRounds = args.maxReviewRounds ?? config.max_rounds ?? DEFAULT_MAX_REVIEW_ROUNDS
144
188
  const goal = args.goal ?? config.goal ?? ''
145
189
  const dimensions = config.dimensions ?? []
190
+
191
+ // Output schema validation gate (reviewer.output_schema_validation,
192
+ // default true): validate every round's findings against the findings
193
+ // schema, then drop schema-invalid entries before the deterministic
194
+ // core so malformed reviewer output can never crash dedupe/sort or
195
+ // leak into fixes. The `schemaValidation` array is surfaced so the
196
+ // workflow can retry failing rounds (≤2 times) with a strict-JSON
197
+ // nudge. When disabled, non-object entries are still dropped for
198
+ // crash-safety.
199
+ const schemaEnabled = config.reviewer?.output_schema_validation !== false
200
+ const schemaValidation = schemaEnabled ? validateRoundsSchema(rounds) : null
201
+ const cleanRounds = sanitizeRounds(rounds, schemaValidation)
202
+
146
203
  const report = buildReviewReport({
147
204
  mode,
148
205
  goal,
149
206
  dimensions,
150
207
  maxReviewRounds,
151
- rounds,
208
+ rounds: cleanRounds,
152
209
  knownIntentional: args.knownIntentional as KnownIntentional[] | undefined,
153
210
  fixedCount: typeof args.fixedCount === 'number' ? args.fixedCount : undefined,
154
211
  })
155
- return { operation: 'aggregate', mode, report: report as unknown as JsonValue }
212
+ return {
213
+ operation: 'aggregate',
214
+ mode,
215
+ report: report as unknown as JsonValue,
216
+ schemaValidation: schemaValidation as unknown as JsonValue | undefined,
217
+ }
156
218
  }
157
219
 
158
220
  if (args.operation === 'meta-review') {
@@ -165,12 +227,36 @@ export function registerReviewTool(ctx: { tools: { register: (def: ReturnType<ty
165
227
  }
166
228
  }
167
229
  const audit = metaReviewReport(source)
168
- const finalReport = buildFinalReviewReport(source)
230
+ // Hard code-evidence gate (default on): every finding's file/line is
231
+ // validated against real files on disk before folding into the final
232
+ // verdict. Disable via config `reviewer.evidence_validation: false`.
233
+ const evidenceEnabled = config.reviewer?.evidence_validation !== false
234
+ const findings: ReviewFinding[] = Array.isArray(source.findings) ? source.findings : []
235
+ const evidence = evidenceEnabled ? verifyFindings(projectRoot, findings) : null
236
+ // Prompt-informative coverage: compare the reviewer's self-reported
237
+ // reads against the assigned scope inventory (never flips the
238
+ // verdict). Disable via config `reviewer.coverage_validation: false`.
239
+ const coverageEnabled = config.reviewer?.coverage_validation !== false
240
+ let coverage: CoverageResult | null = null
241
+ if (coverageEnabled) {
242
+ const assigned = collectScopeFiles(projectRoot, {
243
+ scope: config.review?.scope === 'changed-only' ? 'changed-only' : 'full',
244
+ })
245
+ const readFiles = Array.isArray((source as unknown as { readFiles?: unknown }).readFiles)
246
+ ? ((source as unknown as { readFiles?: unknown }).readFiles as string[])
247
+ : null
248
+ if (readFiles && readFiles.length > 0) {
249
+ coverage = computeCoverage(assigned, readFiles)
250
+ }
251
+ }
252
+ const finalReport = buildFinalReviewReport(source, { evidence, coverage })
169
253
  return {
170
254
  operation: 'meta-review',
171
255
  mode,
172
256
  found: true,
173
257
  report: audit as unknown as JsonValue,
258
+ evidence: evidence ? (evidenceToPlain(evidence) as unknown as JsonValue) : null,
259
+ coverage: coverage ? (coverageToDict(coverage) as unknown as JsonValue) : null,
174
260
  finalReport: finalReport as unknown as JsonValue,
175
261
  }
176
262
  }
package/src/types.ts CHANGED
@@ -16,7 +16,12 @@ export interface IterateConfig {
16
16
  command_whitelist: string[]
17
17
  commands: Record<string, string[]>
18
18
  }
19
- reviewer: { output_schema_validation: boolean }
19
+ reviewer: {
20
+ output_schema_validation: boolean
21
+ evidence_validation: boolean
22
+ coverage_validation: boolean
23
+ scope_chunk_size: number
24
+ }
20
25
  onboarding?: Record<string, unknown>
21
26
  personalization?: Record<string, unknown>
22
27
  }