auditor-lambda 0.8.0 → 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 (98) hide show
  1. package/audit-code-wrapper-lib.mjs +149 -129
  2. package/dist/adapters/normalizeExternal.js +6 -3
  3. package/dist/cli/args.d.ts +0 -1
  4. package/dist/cli/args.js +0 -6
  5. package/dist/cli/dispatch.js +3 -2
  6. package/dist/cli/lineIndex.js +4 -1
  7. package/dist/cli/mergeAndIngestCommand.d.ts +1 -0
  8. package/dist/cli/mergeAndIngestCommand.js +219 -0
  9. package/dist/cli/nextStepCommand.js +5 -1
  10. package/dist/cli/runToCompletion.d.ts +9 -0
  11. package/dist/cli/runToCompletion.js +655 -480
  12. package/dist/cli/statusCommand.d.ts +1 -0
  13. package/dist/cli/statusCommand.js +113 -0
  14. package/dist/cli/submitPacketCommand.d.ts +1 -0
  15. package/dist/cli/submitPacketCommand.js +155 -0
  16. package/dist/cli/workerResult.d.ts +1 -1
  17. package/dist/cli/workerRunCommand.d.ts +1 -0
  18. package/dist/cli/workerRunCommand.js +88 -0
  19. package/dist/cli.js +14 -563
  20. package/dist/extractors/analyzers/sql.js +4 -1
  21. package/dist/extractors/analyzers/treeSitter.js +29 -15
  22. package/dist/extractors/analyzers/typescript.js +10 -8
  23. package/dist/extractors/designAssessment.js +43 -24
  24. package/dist/extractors/graph.js +139 -73
  25. package/dist/extractors/pathPatterns.js +17 -5
  26. package/dist/io/runArtifactTypes.d.ts +18 -0
  27. package/dist/io/runArtifactTypes.js +1 -0
  28. package/dist/io/runArtifacts.d.ts +2 -18
  29. package/dist/io/runArtifacts.js +14 -3
  30. package/dist/mcp/server.js +9 -0
  31. package/dist/orchestrator/advance.js +37 -22
  32. package/dist/orchestrator/artifactFreshness.js +2 -2
  33. package/dist/orchestrator/autoFixExecutor.d.ts +1 -1
  34. package/dist/orchestrator/autoFixExecutor.js +16 -8
  35. package/dist/orchestrator/dependencyMap.d.ts +1 -1
  36. package/dist/orchestrator/dependencyMap.js +7 -1
  37. package/dist/orchestrator/fileAnchors.js +14 -3
  38. package/dist/orchestrator/flowCoverage.js +1 -0
  39. package/dist/orchestrator/flowRequeue.js +4 -1
  40. package/dist/orchestrator/{internalExecutors.d.ts → ingestionExecutors.d.ts} +0 -6
  41. package/dist/orchestrator/ingestionExecutors.js +237 -0
  42. package/dist/orchestrator/intakeExecutors.d.ts +3 -0
  43. package/dist/orchestrator/intakeExecutors.js +25 -0
  44. package/dist/orchestrator/planningExecutors.d.ts +4 -0
  45. package/dist/orchestrator/planningExecutors.js +95 -0
  46. package/dist/orchestrator/runtimeCommand.js +7 -15
  47. package/dist/orchestrator/selectiveDeepening/conflict.d.ts +8 -0
  48. package/dist/orchestrator/selectiveDeepening/conflict.js +71 -0
  49. package/dist/orchestrator/selectiveDeepening/findingFollowup.d.ts +10 -0
  50. package/dist/orchestrator/selectiveDeepening/findingFollowup.js +52 -0
  51. package/dist/orchestrator/selectiveDeepening/highRiskClean.d.ts +7 -0
  52. package/dist/orchestrator/selectiveDeepening/highRiskClean.js +44 -0
  53. package/dist/orchestrator/selectiveDeepening/index.d.ts +18 -0
  54. package/dist/orchestrator/selectiveDeepening/index.js +128 -0
  55. package/dist/orchestrator/selectiveDeepening/lensVerification.d.ts +12 -0
  56. package/dist/orchestrator/selectiveDeepening/lensVerification.js +242 -0
  57. package/dist/orchestrator/selectiveDeepening/runtimeValidation.d.ts +13 -0
  58. package/dist/orchestrator/selectiveDeepening/runtimeValidation.js +57 -0
  59. package/dist/orchestrator/selectiveDeepening/shared.d.ts +45 -0
  60. package/dist/orchestrator/selectiveDeepening/shared.js +128 -0
  61. package/dist/orchestrator/selectiveDeepening/stewardFollowup.d.ts +6 -0
  62. package/dist/orchestrator/selectiveDeepening/stewardFollowup.js +72 -0
  63. package/dist/orchestrator/selectiveDeepening.d.ts +2 -20
  64. package/dist/orchestrator/selectiveDeepening.js +6 -760
  65. package/dist/orchestrator/staleness.js +3 -3
  66. package/dist/orchestrator/structureExecutors.d.ts +5 -0
  67. package/dist/orchestrator/structureExecutors.js +94 -0
  68. package/dist/orchestrator/taskBuilder.d.ts +2 -2
  69. package/dist/orchestrator/taskBuilder.js +101 -82
  70. package/dist/providers/index.d.ts +7 -0
  71. package/dist/providers/index.js +14 -95
  72. package/dist/quota/discoveredLimits.d.ts +1 -0
  73. package/dist/quota/discoveredLimits.js +7 -1
  74. package/dist/quota/index.d.ts +0 -2
  75. package/dist/quota/index.js +1 -2
  76. package/dist/reporting/workBlocks.js +7 -4
  77. package/dist/types/reviewPlanning.d.ts +23 -16
  78. package/dist/validation/auditResults.js +97 -95
  79. package/dist/validation/sessionConfig.d.ts +2 -2
  80. package/dist/validation/sessionConfig.js +14 -7
  81. package/package.json +4 -3
  82. package/schemas/audit_findings.schema.json +3 -3
  83. package/schemas/critical_flows.schema.json +3 -2
  84. package/schemas/dispatch_quota.schema.json +1 -1
  85. package/schemas/graph_bundle.schema.json +1 -1
  86. package/schemas/review_packets.schema.json +1 -1
  87. package/schemas/step_contract.schema.json +80 -0
  88. package/scripts/postinstall.mjs +19 -2
  89. package/skills/audit-code/opencode-command-template.txt +3 -3
  90. package/dist/orchestrator/internalExecutors.js +0 -424
  91. package/dist/providers/localSubprocessProvider.d.ts +0 -9
  92. package/dist/providers/localSubprocessProvider.js +0 -18
  93. package/dist/providers/subprocessTemplateProvider.d.ts +0 -8
  94. package/dist/providers/subprocessTemplateProvider.js +0 -59
  95. package/dist/providers/vscodeTaskProvider.d.ts +0 -7
  96. package/dist/providers/vscodeTaskProvider.js +0 -14
  97. package/dist/quota/probe.d.ts +0 -10
  98. package/dist/quota/probe.js +0 -18
@@ -1,760 +1,6 @@
1
- import { createHash } from "node:crypto";
2
- const DEFAULT_MAX_DEEPENING_TASKS = 6;
3
- const DEFAULT_MAX_TOTAL_DEEPENING_TASKS = 24;
4
- const DEEPENING_TAG = "selective_deepening";
5
- const LENS_VERIFICATION_TAG = "lens_verification";
6
- const LENS_VERIFICATION_FOLLOWUP_TAG = "lens_verification_followup";
7
- const MAX_LENS_VERIFICATION_FILES = 12;
8
- const MAX_LENS_VERIFICATION_RESULT_SUMMARIES = 12;
9
- const MAX_VERIFICATION_FOLLOWUP_TASKS_PER_RESULT = 4;
10
- const IMPORTANT_LENS_VERIFICATION_LENSES = new Set([
11
- "security",
12
- "data_integrity",
13
- "reliability",
14
- ]);
15
- const SEVERITY_RANK = {
16
- critical: 5,
17
- high: 4,
18
- medium: 3,
19
- low: 2,
20
- info: 1,
21
- };
22
- const CONFIDENCE_RANK = {
23
- high: 3,
24
- medium: 2,
25
- low: 1,
26
- };
27
- function priorityRank(priority) {
28
- switch (priority) {
29
- case "high":
30
- return 3;
31
- case "medium":
32
- return 2;
33
- case "low":
34
- default:
35
- return 1;
36
- }
37
- }
38
- function isDeepeningTask(task) {
39
- return task?.tags?.includes(DEEPENING_TAG) ?? false;
40
- }
41
- function isLensVerificationTask(task) {
42
- return task?.tags?.includes(LENS_VERIFICATION_TAG) ?? false;
43
- }
44
- function sanitizeSegment(value) {
45
- const sanitized = value
46
- .replace(/[^a-zA-Z0-9_-]+/g, "-")
47
- .replace(/^-+|-+$/g, "");
48
- return sanitized.length > 0 ? sanitized : "followup";
49
- }
50
- function shortHash(value) {
51
- return createHash("sha1").update(value).digest("hex").slice(0, 10);
52
- }
53
- function resultLineIndex(result) {
54
- return Object.fromEntries(result.file_coverage.map((coverage) => [
55
- coverage.path,
56
- coverage.total_lines,
57
- ]));
58
- }
59
- function lineCountForPath(path, task, result, lineIndex) {
60
- return (task?.file_line_counts?.[path] ??
61
- resultLineIndex(result)[path] ??
62
- lineIndex?.[path] ??
63
- 0);
64
- }
65
- function uniqueSorted(values) {
66
- return [...new Set(values)].sort((a, b) => a.localeCompare(b));
67
- }
68
- function intersects(left, right) {
69
- const rightSet = new Set(right);
70
- return left.some((value) => rightSet.has(value));
71
- }
72
- function pathsForFinding(finding, result, task) {
73
- const assignedPaths = new Set([
74
- ...(task?.file_paths ?? []),
75
- ...result.file_coverage.map((coverage) => coverage.path),
76
- ]);
77
- const affected = finding.affected_files
78
- .map((file) => file.path)
79
- .filter((path) => assignedPaths.size === 0 || assignedPaths.has(path));
80
- return uniqueSorted(affected.length > 0
81
- ? affected
82
- : result.file_coverage.map((coverage) => coverage.path));
83
- }
84
- function taskIdFor(prefix, values) {
85
- return `deepening:${prefix}:${shortHash(values.join("\0"))}`;
86
- }
87
- function lineCountFromSources(path, tasks, results, lineIndex) {
88
- for (const task of tasks) {
89
- const count = task.file_line_counts?.[path];
90
- if (count !== undefined) {
91
- return count;
92
- }
93
- }
94
- for (const result of results) {
95
- const coverage = result.file_coverage.find((item) => item.path === path);
96
- if (coverage) {
97
- return coverage.total_lines;
98
- }
99
- }
100
- return lineIndex?.[path] ?? 0;
101
- }
102
- function formatList(values, maxItems) {
103
- const visible = values.slice(0, maxItems);
104
- const suffix = values.length > maxItems ? `, ... (+${values.length - maxItems} more)` : "";
105
- return `${visible.join(", ")}${suffix}`;
106
- }
107
- function priorityLabel(priority) {
108
- return priority ?? "low";
109
- }
110
- function getExternalAnalyzerPaths(externalAnalyzerResults) {
111
- return new Set((externalAnalyzerResults?.results ?? [])
112
- .map((result) => result && typeof result.path === "string" && result.path.length > 0
113
- ? result.path
114
- : null)
115
- .filter((path) => path !== null));
116
- }
117
- function isRecord(value) {
118
- return value !== null && typeof value === "object" && !Array.isArray(value);
119
- }
120
- function normalizedSuggestedPriority(value, fallback = "medium") {
121
- return value === "high" || value === "medium" || value === "low"
122
- ? value
123
- : fallback;
124
- }
125
- function buildFindingFollowupTask(params) {
126
- const paths = pathsForFinding(params.finding, params.result, params.task);
127
- const triggerLabel = params.triggers.join("+");
128
- const taskId = taskIdFor("finding", [
129
- params.result.task_id,
130
- params.finding.id,
131
- triggerLabel,
132
- ]);
133
- const priority = SEVERITY_RANK[params.finding.severity] >= SEVERITY_RANK.high
134
- ? "high"
135
- : "medium";
136
- return {
137
- task_id: taskId,
138
- unit_id: params.result.unit_id,
139
- pass_id: `deepening:${params.result.pass_id}`,
140
- lens: params.result.lens,
141
- file_paths: paths,
142
- file_line_counts: Object.fromEntries(paths.map((path) => [
143
- path,
144
- lineCountForPath(path, params.task, params.result, params.lineIndex),
145
- ])),
146
- rationale: `Follow up on ${params.finding.id} (${params.finding.severity}/${params.finding.confidence}) from ${params.result.task_id}. ` +
147
- "Verify impact, evidence quality, affected scope, and whether the finding should stand, narrow, or be downgraded.",
148
- priority,
149
- tags: [
150
- DEEPENING_TAG,
151
- ...params.triggers.map((trigger) => `trigger:${trigger}`),
152
- `source_task:${sanitizeSegment(params.result.task_id)}`,
153
- `finding:${sanitizeSegment(params.finding.id)}`,
154
- ],
155
- status: "pending",
156
- };
157
- }
158
- function buildConflictFollowupTask(params) {
159
- const [first] = params.contexts;
160
- const paths = uniqueSorted(params.contexts.flatMap((context) => context.paths));
161
- const maxSeverity = Math.max(...params.contexts.map((context) => SEVERITY_RANK[context.finding.severity]));
162
- const lineSources = new Map();
163
- for (const context of params.contexts) {
164
- for (const path of context.paths) {
165
- if (!lineSources.has(path)) {
166
- lineSources.set(path, { task: context.task, result: context.result });
167
- }
168
- }
169
- }
170
- const sourceTaskIds = uniqueSorted(params.contexts.map((context) => context.result.task_id));
171
- const findingIds = uniqueSorted(params.contexts.map((context) => context.finding.id));
172
- return {
173
- task_id: taskIdFor("conflict", [
174
- params.conflictKey,
175
- ...sourceTaskIds,
176
- ...findingIds,
177
- ]),
178
- unit_id: first?.result.unit_id ?? "selective-deepening",
179
- pass_id: `deepening:${first?.result.pass_id ?? "conflict"}`,
180
- lens: (first?.result.lens ?? "correctness"),
181
- file_paths: paths,
182
- file_line_counts: Object.fromEntries(paths.map((path) => {
183
- const source = lineSources.get(path);
184
- return [
185
- path,
186
- source
187
- ? lineCountForPath(path, source.task, source.result, params.lineIndex)
188
- : (params.lineIndex?.[path] ?? 0),
189
- ];
190
- })),
191
- rationale: `Reconcile conflicting audit output for ${params.conflictKey}. ` +
192
- `Compare source tasks ${sourceTaskIds.join(", ")} and decide the correct severity, confidence, and evidence-backed conclusion.`,
193
- priority: maxSeverity >= SEVERITY_RANK.high ? "high" : "medium",
194
- tags: [
195
- DEEPENING_TAG,
196
- "trigger:conflicting_output",
197
- ...sourceTaskIds.slice(0, 3).map((id) => `source_task:${sanitizeSegment(id)}`),
198
- ],
199
- status: "pending",
200
- };
201
- }
202
- function isHighRiskCleanResult(result, task) {
203
- if (result.findings.length > 0 ||
204
- result.requires_followup === false ||
205
- isDeepeningTask(task)) {
206
- return false;
207
- }
208
- if (!task) {
209
- return (result.requires_followup === true &&
210
- (result.lens === "security" || result.lens === "data_integrity"));
211
- }
212
- if (task.priority === "high") {
213
- return true;
214
- }
215
- if (task.tags?.some((tag) => ["critical_flow", "external_analyzer_signal"].includes(tag))) {
216
- return true;
217
- }
218
- return result.requires_followup === true && task.priority === "medium";
219
- }
220
- function buildHighRiskCleanFollowupTask(params) {
221
- const paths = uniqueSorted((params.task?.file_paths.length ?? 0) > 0
222
- ? (params.task?.file_paths ?? [])
223
- : params.result.file_coverage.map((coverage) => coverage.path));
224
- return {
225
- task_id: taskIdFor("clean", [params.result.task_id, params.result.lens]),
226
- unit_id: params.result.unit_id,
227
- pass_id: `deepening:${params.result.pass_id}`,
228
- lens: params.result.lens,
229
- file_paths: paths,
230
- file_line_counts: Object.fromEntries(paths.map((path) => [
231
- path,
232
- lineCountForPath(path, params.task, params.result, params.lineIndex),
233
- ])),
234
- rationale: `Sample high-risk no-finding result from ${params.result.task_id}. ` +
235
- "Re-review the assigned files for missed edge cases, hidden runtime failures, and whether the clean conclusion should stand.",
236
- priority: params.task?.priority === "high" ? "high" : "medium",
237
- tags: [
238
- DEEPENING_TAG,
239
- "trigger:high_risk_no_finding",
240
- `source_task:${sanitizeSegment(params.result.task_id)}`,
241
- ],
242
- status: "pending",
243
- };
244
- }
245
- function runtimeResultNeedsFollowup(status) {
246
- return status === "not_confirmed" || status === "inconclusive";
247
- }
248
- function pickRuntimeFollowupLens(relatedTasks) {
249
- const preference = [
250
- "security",
251
- "data_integrity",
252
- "reliability",
253
- "correctness",
254
- "tests",
255
- "operability",
256
- "config_deployment",
257
- "performance",
258
- "architecture",
259
- "maintainability",
260
- ];
261
- for (const lens of preference) {
262
- if (relatedTasks.some((task) => task.lens === lens)) {
263
- return lens;
264
- }
265
- }
266
- return "correctness";
267
- }
268
- function runtimeValidationHasStrongStaticFinding(runtimeTask, contexts) {
269
- return contexts.some((context) => intersects(context.paths, runtimeTask.target_paths) &&
270
- SEVERITY_RANK[context.finding.severity] >= SEVERITY_RANK.high);
271
- }
272
- function buildRuntimeValidationFollowupTask(params) {
273
- const paths = uniqueSorted(params.runtimeTask.target_paths);
274
- const lens = pickRuntimeFollowupLens(params.relatedTasks);
275
- const firstRelated = params.relatedTasks[0];
276
- return {
277
- task_id: taskIdFor("runtime", [params.runtimeTask.id]),
278
- unit_id: firstRelated?.unit_id ?? `runtime:${sanitizeSegment(params.runtimeTask.id)}`,
279
- pass_id: `deepening:runtime:${sanitizeSegment(params.runtimeTask.id)}`,
280
- lens,
281
- file_paths: paths,
282
- file_line_counts: Object.fromEntries(paths.map((path) => [
283
- path,
284
- lineCountFromSources(path, params.relatedTasks, params.results, params.lineIndex),
285
- ])),
286
- rationale: `Reconcile runtime validation ${params.runtimeTask.id} (${params.runtimeResultStatus}) with semantic audit output. ` +
287
- "Verify the failing or inconclusive runtime evidence, map it to source behavior, and decide whether a finding should be added or escalated.",
288
- priority: params.runtimeTask.priority === "high" ||
289
- params.runtimeResultStatus === "not_confirmed"
290
- ? "high"
291
- : "medium",
292
- tags: [
293
- DEEPENING_TAG,
294
- "trigger:runtime_validation_disagreement",
295
- `runtime_task:${sanitizeSegment(params.runtimeTask.id)}`,
296
- `runtime_status:${params.runtimeResultStatus}`,
297
- ],
298
- status: "pending",
299
- };
300
- }
301
- function sourceTaskIds(sources) {
302
- return uniqueSorted(sources.map((source) => source.result.task_id));
303
- }
304
- function resultFiles(source) {
305
- return uniqueSorted(source.task?.file_paths && source.task.file_paths.length > 0
306
- ? source.task.file_paths
307
- : source.result.file_coverage.map((coverage) => coverage.path));
308
- }
309
- function lensVerificationTriggers(params) {
310
- const filePaths = uniqueSorted(params.sources.flatMap(resultFiles));
311
- const findingPaths = new Set(params.sources.flatMap((source) => source.result.findings.flatMap((finding) => finding.affected_files.map((file) => file.path))));
312
- const externalPathsInScope = filePaths.filter((path) => params.externalAnalyzerPaths.has(path));
313
- const unresolvedExternalPaths = externalPathsInScope.filter((path) => !findingPaths.has(path));
314
- const cleanResults = params.sources.filter((source) => source.result.findings.length === 0 &&
315
- source.result.requires_followup !== false);
316
- const highRiskCleanResults = params.sources.filter((source) => isHighRiskCleanResult(source.result, source.task));
317
- const totalLines = filePaths.reduce((sum, path) => {
318
- const owner = params.sources.find((source) => resultFiles(source).includes(path));
319
- return (sum +
320
- (owner
321
- ? lineCountForPath(path, owner.task, owner.result)
322
- : 0));
323
- }, 0);
324
- const triggers = [];
325
- if (params.sources.some((source) => source.task?.priority === "high")) {
326
- triggers.push("high_priority_lens");
327
- }
328
- if (params.sources.some((source) => source.task?.tags?.some((tag) => tag === "critical_flow" || tag.startsWith("critical_flow:")))) {
329
- triggers.push("critical_flow");
330
- }
331
- if (params.sources.some((source) => source.task?.tags?.some((tag) => tag === "external_analyzer_signal" ||
332
- tag.startsWith("external_tool:"))) ||
333
- externalPathsInScope.length > 0) {
334
- triggers.push("external_analyzer_signal");
335
- }
336
- if (unresolvedExternalPaths.length > 0) {
337
- triggers.push("unresolved_external_signal");
338
- }
339
- if (params.sources.length >= 3 ||
340
- filePaths.length >= 4 ||
341
- totalLines >= 2000) {
342
- triggers.push("large_lens_surface");
343
- }
344
- if (cleanResults.length >= 2 && cleanResults.length >= params.sources.length / 2) {
345
- triggers.push("many_no_finding_results");
346
- }
347
- if (highRiskCleanResults.length > 0) {
348
- triggers.push("high_risk_clean_result");
349
- }
350
- if (params.sources.some((source) => source.task?.tags?.some((tag) => tag === "large_file"))) {
351
- triggers.push("large_file_reviewed");
352
- }
353
- return uniqueSorted(triggers);
354
- }
355
- function hasPendingBaseTaskForLens(lens, tasks, completedResultIds) {
356
- return tasks.some((task) => task.lens === lens &&
357
- !isDeepeningTask(task) &&
358
- !completedResultIds.has(task.task_id) &&
359
- task.status !== "complete");
360
- }
361
- function shouldBuildLensVerificationTask(params) {
362
- if (!IMPORTANT_LENS_VERIFICATION_LENSES.has(params.lens)) {
363
- return false;
364
- }
365
- if (params.sources.length === 0 || params.triggers.length === 0) {
366
- return false;
367
- }
368
- const explicitlyClosedCleanScope = params.sources.every((source) => source.result.findings.length === 0 &&
369
- source.result.requires_followup === false);
370
- if (explicitlyClosedCleanScope &&
371
- !params.triggers.some((trigger) => ["external_analyzer_signal", "unresolved_external_signal"].includes(trigger))) {
372
- return false;
373
- }
374
- if (hasPendingBaseTaskForLens(params.lens, params.existingTasks, params.completedResultIds)) {
375
- return false;
376
- }
377
- const enoughSurface = params.sources.length >= 2 ||
378
- params.triggers.some((trigger) => [
379
- "critical_flow",
380
- "external_analyzer_signal",
381
- "unresolved_external_signal",
382
- "large_lens_surface",
383
- ].includes(trigger));
384
- if (!enoughSurface) {
385
- return false;
386
- }
387
- const sourceSignature = sourceTaskIds(params.sources);
388
- const candidateId = taskIdFor("steward", [params.lens, ...sourceSignature]);
389
- return !params.existingTasks.some((task) => task.task_id === candidateId);
390
- }
391
- function selectLensVerificationFiles(sources, externalAnalyzerPaths) {
392
- const scores = new Map();
393
- function add(path, score, lines) {
394
- const current = scores.get(path) ?? { score: 0, lines };
395
- current.score += score;
396
- current.lines = Math.max(current.lines, lines);
397
- scores.set(path, current);
398
- }
399
- for (const source of sources) {
400
- const priorityScore = priorityRank(source.task?.priority);
401
- const highRiskClean = isHighRiskCleanResult(source.result, source.task);
402
- for (const path of resultFiles(source)) {
403
- add(path, priorityScore, lineCountForPath(path, source.task, source.result));
404
- if (source.task?.tags?.includes("critical_flow"))
405
- add(path, 6, 0);
406
- if (source.task?.tags?.includes("external_analyzer_signal"))
407
- add(path, 6, 0);
408
- if (source.task?.tags?.includes("large_file"))
409
- add(path, 4, 0);
410
- if (highRiskClean)
411
- add(path, 5, 0);
412
- }
413
- for (const finding of source.result.findings) {
414
- for (const file of finding.affected_files) {
415
- add(file.path, SEVERITY_RANK[finding.severity], 0);
416
- }
417
- }
418
- }
419
- for (const path of externalAnalyzerPaths) {
420
- if (scores.has(path)) {
421
- add(path, 8, 0);
422
- }
423
- }
424
- return [...scores.entries()]
425
- .sort((a, b) => {
426
- const scoreDelta = b[1].score - a[1].score;
427
- if (scoreDelta !== 0)
428
- return scoreDelta;
429
- const lineDelta = b[1].lines - a[1].lines;
430
- if (lineDelta !== 0)
431
- return lineDelta;
432
- return a[0].localeCompare(b[0]);
433
- })
434
- .slice(0, MAX_LENS_VERIFICATION_FILES)
435
- .map(([path]) => path);
436
- }
437
- function summarizeLensVerificationSource(source) {
438
- const findings = source.result.findings.length === 0
439
- ? "findings=none"
440
- : `findings=${source.result.findings
441
- .slice(0, 3)
442
- .map((finding) => `${finding.id} ${finding.severity}/${finding.confidence} ${finding.category}: ${finding.title}`)
443
- .join("; ")}${source.result.findings.length > 3 ? "; ..." : ""}`;
444
- const tags = source.task?.tags?.length
445
- ? ` tags=${source.task.tags.join(",")}`
446
- : "";
447
- return (`- ${source.result.task_id} priority=${priorityLabel(source.task?.priority)}` +
448
- `${tags} files=${formatList(resultFiles(source), 4)} ${findings}` +
449
- (source.result.requires_followup === true ? " requires_followup=true" : ""));
450
- }
451
- function buildLensVerificationTask(params) {
452
- const sourceIds = sourceTaskIds(params.sources);
453
- const selectedPaths = selectLensVerificationFiles(params.sources, params.externalAnalyzerPaths);
454
- const allPaths = uniqueSorted(params.sources.flatMap(resultFiles));
455
- const omittedPathCount = Math.max(0, allPaths.length - selectedPaths.length);
456
- const externalPathsInScope = allPaths.filter((path) => params.externalAnalyzerPaths.has(path));
457
- const summaries = params.sources
458
- .sort((a, b) => a.result.task_id.localeCompare(b.result.task_id))
459
- .slice(0, MAX_LENS_VERIFICATION_RESULT_SUMMARIES)
460
- .map(summarizeLensVerificationSource);
461
- return {
462
- task_id: taskIdFor("steward", [params.lens, ...sourceIds]),
463
- unit_id: `lens-steward:${params.lens}`,
464
- pass_id: `lens-steward:${params.lens}`,
465
- lens: params.lens,
466
- file_paths: selectedPaths,
467
- file_line_counts: Object.fromEntries(selectedPaths.map((path) => [
468
- path,
469
- lineCountFromSources(path, params.sources.map((source) => source.task).filter((task) => task !== undefined), params.sources.map((source) => source.result), params.lineIndex),
470
- ])),
471
- inputs: {
472
- source_task_ids: sourceIds.join(","),
473
- trigger_summary: params.triggers.join(","),
474
- },
475
- rationale: `Lens steward verification for ${params.lens} after ${params.sources.length} completed base result(s) across ${allPaths.length} file(s). ` +
476
- `Triggers: ${params.triggers.join(", ")}. ` +
477
- "Review whether high-risk packets are suspiciously clean, severity/confidence levels are consistent, external analyzer signals were resolved rather than hand-waved, cross-packet issues are visible, no-finding conclusions are believable, and related-file findings contradict each other. " +
478
- "Do not write direct findings from this verification task; return findings: [] plus verification metadata with bounded follow-up AuditTask suggestions when needed.\n" +
479
- `Selected verification files: ${formatList(selectedPaths, 8)}${omittedPathCount > 0 ? `; omitted ${omittedPathCount} lower-priority file(s) from direct source checks` : ""}.\n` +
480
- (externalPathsInScope.length > 0
481
- ? `External analyzer paths in scope: ${formatList(externalPathsInScope, 8)}.\n`
482
- : "") +
483
- "Source result summary:\n" +
484
- summaries.join("\n") +
485
- (params.sources.length > MAX_LENS_VERIFICATION_RESULT_SUMMARIES
486
- ? `\n- ... (+${params.sources.length - MAX_LENS_VERIFICATION_RESULT_SUMMARIES} more result summaries omitted)`
487
- : ""),
488
- priority: "high",
489
- tags: [
490
- DEEPENING_TAG,
491
- LENS_VERIFICATION_TAG,
492
- `lens:${params.lens}`,
493
- ...params.triggers.map((trigger) => `trigger:${trigger}`),
494
- ],
495
- status: "pending",
496
- };
497
- }
498
- function buildLensVerificationTasks(params) {
499
- const taskById = new Map(params.existingTasks.map((task) => [task.task_id, task]));
500
- const completedResultIds = new Set(params.results.map((result) => result.task_id));
501
- const externalAnalyzerPaths = getExternalAnalyzerPaths(params.externalAnalyzerResults);
502
- const tasks = [];
503
- for (const lens of [...IMPORTANT_LENS_VERIFICATION_LENSES].sort((a, b) => a.localeCompare(b))) {
504
- const sources = params.results
505
- .map((result) => ({ result, task: taskById.get(result.task_id) }))
506
- .filter((source) => source.result.lens === lens &&
507
- !isDeepeningTask(source.task) &&
508
- !isLensVerificationTask(source.task));
509
- const triggers = lensVerificationTriggers({
510
- lens,
511
- sources,
512
- externalAnalyzerPaths,
513
- });
514
- if (!shouldBuildLensVerificationTask({
515
- lens,
516
- sources,
517
- triggers,
518
- existingTasks: params.existingTasks,
519
- completedResultIds,
520
- })) {
521
- continue;
522
- }
523
- tasks.push(buildLensVerificationTask({
524
- lens,
525
- sources,
526
- triggers,
527
- externalAnalyzerPaths,
528
- lineIndex: params.lineIndex,
529
- }));
530
- }
531
- return tasks;
532
- }
533
- function buildVerificationFollowupTasks(params) {
534
- if (!params.result.verification?.needs_followup ||
535
- !Array.isArray(params.result.verification.followup_tasks) ||
536
- !isLensVerificationTask(params.task)) {
537
- return [];
538
- }
539
- const coverageByPath = new Map(params.result.file_coverage.map((coverage) => [
540
- coverage.path,
541
- coverage.total_lines,
542
- ]));
543
- const concerns = [
544
- ...(params.result.verification.concerns ?? []),
545
- ...(params.result.verification.coverage_concerns ?? []),
546
- ...(params.result.verification.confidence_concerns ?? []),
547
- ];
548
- const tasks = [];
549
- for (let index = 0; index < params.result.verification.followup_tasks.length; index++) {
550
- if (tasks.length >= MAX_VERIFICATION_FOLLOWUP_TASKS_PER_RESULT) {
551
- break;
552
- }
553
- const suggestion = params.result.verification.followup_tasks[index];
554
- if (!isRecord(suggestion)) {
555
- continue;
556
- }
557
- if (suggestion.lens !== params.result.lens) {
558
- continue;
559
- }
560
- const suggestedPaths = Array.isArray(suggestion.file_paths)
561
- ? suggestion.file_paths.filter((path) => typeof path === "string" && coverageByPath.has(path))
562
- : [];
563
- const paths = uniqueSorted(suggestedPaths);
564
- if (paths.length === 0) {
565
- continue;
566
- }
567
- const suggestedRationale = typeof suggestion.rationale === "string" && suggestion.rationale.trim().length > 0
568
- ? suggestion.rationale.trim()
569
- : concerns[0] ?? "Lens steward requested bounded follow-up.";
570
- const suggestedId = typeof suggestion.task_id === "string" && suggestion.task_id.trim().length > 0
571
- ? suggestion.task_id
572
- : `suggestion-${index + 1}`;
573
- tasks.push({
574
- task_id: taskIdFor("steward-followup", [
575
- params.result.task_id,
576
- String(index),
577
- suggestedId,
578
- ...paths,
579
- suggestedRationale,
580
- ]),
581
- unit_id: typeof suggestion.unit_id === "string" && suggestion.unit_id.trim().length > 0
582
- ? suggestion.unit_id
583
- : params.result.unit_id,
584
- pass_id: `deepening:${params.result.pass_id}`,
585
- lens: params.result.lens,
586
- file_paths: paths,
587
- file_line_counts: Object.fromEntries(paths.map((path) => [
588
- path,
589
- coverageByPath.get(path) ?? params.lineIndex?.[path] ?? 0,
590
- ])),
591
- rationale: `Lens steward follow-up from ${params.result.task_id}. ${suggestedRationale}`,
592
- priority: normalizedSuggestedPriority(suggestion.priority, "medium"),
593
- tags: [
594
- DEEPENING_TAG,
595
- LENS_VERIFICATION_FOLLOWUP_TAG,
596
- "trigger:lens_verification",
597
- `source_task:${sanitizeSegment(params.result.task_id)}`,
598
- ],
599
- status: "pending",
600
- });
601
- }
602
- return tasks;
603
- }
604
- function findingContexts(results, taskById) {
605
- const contexts = [];
606
- for (const result of results) {
607
- const task = taskById.get(result.task_id);
608
- if (isDeepeningTask(task)) {
609
- continue;
610
- }
611
- for (const finding of result.findings) {
612
- contexts.push({
613
- result,
614
- task,
615
- finding,
616
- paths: pathsForFinding(finding, result, task),
617
- });
618
- }
619
- }
620
- return contexts;
621
- }
622
- function conflictGroups(contexts) {
623
- const groups = new Map();
624
- for (const context of contexts) {
625
- for (const path of context.paths) {
626
- const key = [
627
- context.result.lens,
628
- context.finding.category,
629
- path.toLowerCase(),
630
- ].join(":");
631
- const group = groups.get(key) ?? [];
632
- group.push(context);
633
- groups.set(key, group);
634
- }
635
- }
636
- for (const [key, group] of groups) {
637
- const uniqueTasks = new Set(group.map((context) => context.result.task_id));
638
- const severities = group.map((context) => SEVERITY_RANK[context.finding.severity]);
639
- const confidences = group.map((context) => CONFIDENCE_RANK[context.finding.confidence]);
640
- const severitySpread = Math.max(...severities) - Math.min(...severities);
641
- const confidenceSpread = Math.max(...confidences) - Math.min(...confidences);
642
- if (uniqueTasks.size < 2 || (severitySpread < 2 && confidenceSpread < 2)) {
643
- groups.delete(key);
644
- }
645
- }
646
- return groups;
647
- }
648
- export function buildSelectiveDeepeningTasks(options) {
649
- const taskById = new Map((options.existingTasks ?? []).map((task) => [task.task_id, task]));
650
- const existingTasks = options.existingTasks ?? [];
651
- const existingIds = new Set(taskById.keys());
652
- const maxTasks = options.maxTasks ?? DEFAULT_MAX_DEEPENING_TASKS;
653
- const maxTotalDeepeningTasks = options.maxTotalDeepeningTasks ?? DEFAULT_MAX_TOTAL_DEEPENING_TASKS;
654
- const existingDeepeningCount = existingTasks.filter((task) => isDeepeningTask(task)).length;
655
- if (existingDeepeningCount >= maxTotalDeepeningTasks) {
656
- return [];
657
- }
658
- const remainingBudget = maxTotalDeepeningTasks - existingDeepeningCount;
659
- const effectiveMax = Math.min(maxTasks, remainingBudget);
660
- const created = [];
661
- function pushIfNew(task) {
662
- if (created.length >= effectiveMax || existingIds.has(task.task_id)) {
663
- return;
664
- }
665
- existingIds.add(task.task_id);
666
- created.push(task);
667
- }
668
- const contexts = findingContexts(options.results, taskById);
669
- for (const context of contexts) {
670
- const triggers = [];
671
- if (SEVERITY_RANK[context.finding.severity] >= SEVERITY_RANK.high) {
672
- triggers.push("high_severity");
673
- }
674
- if (context.finding.confidence === "low") {
675
- triggers.push("low_confidence");
676
- }
677
- if (triggers.length === 0) {
678
- continue;
679
- }
680
- pushIfNew(buildFindingFollowupTask({
681
- result: context.result,
682
- task: context.task,
683
- finding: context.finding,
684
- triggers,
685
- lineIndex: options.lineIndex,
686
- }));
687
- }
688
- for (const [key, group] of [...conflictGroups(contexts).entries()].sort(([a], [b]) => a.localeCompare(b))) {
689
- pushIfNew(buildConflictFollowupTask({
690
- contexts: group,
691
- conflictKey: key,
692
- lineIndex: options.lineIndex,
693
- }));
694
- }
695
- for (const result of options.results) {
696
- const task = taskById.get(result.task_id);
697
- for (const followupTask of buildVerificationFollowupTasks({
698
- result,
699
- task,
700
- lineIndex: options.lineIndex,
701
- })) {
702
- pushIfNew(followupTask);
703
- }
704
- }
705
- const runtimeTaskById = new Map((options.runtimeValidationTasks?.tasks ?? []).map((task) => [
706
- task.id,
707
- task,
708
- ]));
709
- for (const result of [...(options.runtimeValidationReport?.results ?? [])].sort((a, b) => a.task_id.localeCompare(b.task_id))) {
710
- if (!runtimeResultNeedsFollowup(result.status)) {
711
- continue;
712
- }
713
- const runtimeTask = runtimeTaskById.get(result.task_id);
714
- if (!runtimeTask || runtimeTask.target_paths.length === 0) {
715
- continue;
716
- }
717
- if (runtimeValidationHasStrongStaticFinding(runtimeTask, contexts)) {
718
- continue;
719
- }
720
- const relatedTasks = existingTasks.filter((task) => !isDeepeningTask(task) && intersects(task.file_paths, runtimeTask.target_paths));
721
- pushIfNew(buildRuntimeValidationFollowupTask({
722
- runtimeTask,
723
- runtimeResultStatus: result.status,
724
- relatedTasks,
725
- results: options.results,
726
- lineIndex: options.lineIndex,
727
- }));
728
- }
729
- for (const task of buildLensVerificationTasks({
730
- existingTasks,
731
- results: options.results,
732
- lineIndex: options.lineIndex,
733
- externalAnalyzerResults: options.externalAnalyzerResults,
734
- })) {
735
- pushIfNew(task);
736
- }
737
- const cleanResults = options.results
738
- .map((result) => ({ result, task: taskById.get(result.task_id) }))
739
- .filter(({ result, task }) => isHighRiskCleanResult(result, task))
740
- .sort((a, b) => {
741
- const priorityDelta = priorityRank(b.task?.priority) - priorityRank(a.task?.priority);
742
- if (priorityDelta !== 0)
743
- return priorityDelta;
744
- return a.result.task_id.localeCompare(b.result.task_id);
745
- });
746
- for (const { result, task } of cleanResults) {
747
- pushIfNew(buildHighRiskCleanFollowupTask({
748
- result,
749
- task,
750
- lineIndex: options.lineIndex,
751
- }));
752
- }
753
- return created;
754
- }
755
- export const selectiveDeepeningTestUtils = {
756
- DEEPENING_TAG,
757
- LENS_VERIFICATION_TAG,
758
- LENS_VERIFICATION_FOLLOWUP_TAG,
759
- DEFAULT_MAX_TOTAL_DEEPENING_TASKS,
760
- };
1
+ // Public entry for selective deepening. The implementation was split by
2
+ // task-building strategy into ./selectiveDeepening/ (finding-followup, conflict,
3
+ // high-risk-clean, runtime-validation, lens-verification, steward-followup) with
4
+ // the shared primitives in ./selectiveDeepening/shared.ts. This barrel preserves
5
+ // the original module path so importers and tests are unaffected.
6
+ export { buildSelectiveDeepeningTasks, selectiveDeepeningTestUtils, } from "./selectiveDeepening/index.js";