auditor-lambda 0.1.0 → 0.2.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 (58) hide show
  1. package/README.md +2 -1
  2. package/audit-code-wrapper-lib.mjs +458 -380
  3. package/dist/cli.js +258 -11
  4. package/dist/coverage.d.ts +0 -1
  5. package/dist/coverage.js +3 -34
  6. package/dist/extractors/fileInventory.js +2 -0
  7. package/dist/io/artifacts.js +2 -1
  8. package/dist/orchestrator/advance.js +70 -52
  9. package/dist/orchestrator/flowCoverage.js +2 -1
  10. package/dist/orchestrator/flowPlanning.d.ts +1 -1
  11. package/dist/orchestrator/flowPlanning.js +21 -28
  12. package/dist/orchestrator/internalExecutors.js +0 -1
  13. package/dist/orchestrator/taskBuilder.d.ts +7 -2
  14. package/dist/orchestrator/taskBuilder.js +55 -47
  15. package/dist/prompts/renderWorkerPrompt.js +32 -0
  16. package/dist/providers/claudeCodeProvider.js +6 -0
  17. package/dist/providers/index.js +5 -2
  18. package/dist/providers/opencodeProvider.js +6 -1
  19. package/dist/providers/types.d.ts +1 -0
  20. package/dist/reporting/mergeFindings.js +0 -7
  21. package/dist/reporting/rootCause.d.ts +0 -1
  22. package/dist/reporting/rootCause.js +0 -6
  23. package/dist/reporting/synthesis.js +18 -0
  24. package/dist/supervisor/runLedger.js +6 -2
  25. package/dist/types/sessionConfig.d.ts +8 -0
  26. package/dist/types/workerSession.d.ts +2 -0
  27. package/dist/types.d.ts +1 -2
  28. package/dist/validation/auditResults.d.ts +11 -0
  29. package/dist/validation/auditResults.js +118 -0
  30. package/dist/validation/sessionConfig.js +15 -1
  31. package/docs/agent-integrations.md +61 -56
  32. package/docs/agent-roles.md +69 -69
  33. package/docs/architecture.md +90 -90
  34. package/docs/artifacts.md +69 -69
  35. package/docs/bootstrap-install.md +1 -1
  36. package/docs/model-selection.md +86 -86
  37. package/docs/next-steps.md +11 -9
  38. package/docs/packaging.md +3 -3
  39. package/docs/pipeline.md +152 -152
  40. package/docs/production-readiness.md +6 -5
  41. package/docs/repo-layout.md +18 -18
  42. package/docs/run-flow.md +5 -5
  43. package/docs/session-config.md +216 -210
  44. package/docs/supervisor.md +70 -70
  45. package/docs/windows-setup.md +139 -139
  46. package/package.json +56 -56
  47. package/schemas/audit-code-v1alpha1.schema.json +76 -76
  48. package/schemas/audit_result.schema.json +48 -48
  49. package/schemas/audit_task.schema.json +49 -49
  50. package/schemas/coverage_matrix.schema.json +0 -15
  51. package/schemas/file_disposition.schema.json +33 -33
  52. package/schemas/finding.schema.json +58 -62
  53. package/schemas/flow_coverage.schema.json +44 -44
  54. package/schemas/root_cause_clusters.schema.json +0 -4
  55. package/schemas/runtime_validation_report.schema.json +34 -34
  56. package/schemas/synthesis_report.schema.json +61 -61
  57. package/skills/audit-code/SKILL.md +37 -37
  58. package/skills/audit-code/audit-code.prompt.md +56 -54
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { mkdir } from "node:fs/promises";
2
2
  import { createReadStream } from "node:fs";
3
- import { resolve } from "node:path";
3
+ import { join, resolve } from "node:path";
4
4
  import { buildRepoManifest } from "./extractors/fileInventory.js";
5
5
  import { buildFileDisposition } from "./extractors/disposition.js";
6
6
  import { buildCriticalFlowManifest } from "./extractors/flows.js";
@@ -12,6 +12,7 @@ import { initializeCoverageFromPlan } from "./orchestrator/planning.js";
12
12
  import { loadArtifactBundle, writeCoreArtifacts, } from "./io/artifacts.js";
13
13
  import { readJsonFile, writeJsonFile } from "./io/json.js";
14
14
  import { validateArtifactBundle } from "./validation/artifacts.js";
15
+ import { validateAuditResults, formatAuditResultIssues, } from "./validation/auditResults.js";
15
16
  import { validateConfiguredProviderEnvironment, validateSessionConfig, } from "./validation/sessionConfig.js";
16
17
  import { buildSynthesisReport } from "./reporting/synthesis.js";
17
18
  import { deriveAuditState } from "./orchestrator/state.js";
@@ -47,8 +48,41 @@ function getRootDir(argv) {
47
48
  return resolve(getFlag(argv, "--root", "."));
48
49
  }
49
50
  function getMaxRuns(argv) {
50
- const raw = Number(getFlag(argv, "--max-runs", "25"));
51
- return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 25;
51
+ const raw = Number(getFlag(argv, "--max-runs", "1000"));
52
+ return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 1000;
53
+ }
54
+ function getAgentBatchSize(argv, sessionConfig) {
55
+ const fromArg = getFlag(argv, "--agent-batch-size");
56
+ if (fromArg !== undefined) {
57
+ const parsed = Number(fromArg);
58
+ if (Number.isFinite(parsed) && parsed > 0)
59
+ return Math.floor(parsed);
60
+ }
61
+ if (typeof sessionConfig.agent_task_batch_size === "number" &&
62
+ sessionConfig.agent_task_batch_size > 0) {
63
+ return Math.floor(sessionConfig.agent_task_batch_size);
64
+ }
65
+ return 1;
66
+ }
67
+ function getParallelWorkers(argv, sessionConfig) {
68
+ const fromArg = getFlag(argv, "--parallel");
69
+ if (fromArg !== undefined) {
70
+ const parsed = Number(fromArg);
71
+ if (Number.isFinite(parsed) && parsed > 0)
72
+ return Math.floor(parsed);
73
+ }
74
+ if (typeof sessionConfig.parallel_workers === "number" &&
75
+ sessionConfig.parallel_workers > 0) {
76
+ return Math.floor(sessionConfig.parallel_workers);
77
+ }
78
+ return 1;
79
+ }
80
+ function chunkArray(arr, size) {
81
+ const chunks = [];
82
+ for (let i = 0; i < arr.length; i += size) {
83
+ chunks.push(arr.slice(i, i + size));
84
+ }
85
+ return chunks;
52
86
  }
53
87
  function getUiMode(argv, fallback = "headless") {
54
88
  const raw = getFlag(argv, "--ui");
@@ -160,6 +194,25 @@ async function buildLineIndex(root, repoManifest) {
160
194
  }
161
195
  return Object.fromEntries(entries);
162
196
  }
197
+ function resolveModelForTasks(tasks, sessionConfig) {
198
+ if (!sessionConfig.model_tiers)
199
+ return undefined;
200
+ const tiers = [
201
+ "capable",
202
+ "balanced",
203
+ "fast",
204
+ ];
205
+ for (const tier of tiers) {
206
+ if (tasks.some((t) => t.model_hint === tier)) {
207
+ return sessionConfig.model_tiers[tier];
208
+ }
209
+ }
210
+ return undefined;
211
+ }
212
+ function buildPendingAuditTasks(bundle) {
213
+ const completedTaskIds = new Set((bundle.audit_results ?? []).map((result) => result.task_id));
214
+ return (bundle.audit_tasks ?? []).filter((task) => !completedTaskIds.has(task.task_id));
215
+ }
163
216
  async function runAuditStep(options) {
164
217
  const bundle = await loadArtifactBundle(options.artifactsDir);
165
218
  const auditResults = options.auditResultsPath
@@ -282,6 +335,8 @@ async function cmdRunToCompletion(argv) {
282
335
  const provider = createFreshSessionProvider(getFlag(argv, "--provider"), sessionConfig);
283
336
  const uiMode = getUiMode(argv, sessionConfig.ui_mode ?? "headless");
284
337
  const maxRuns = getMaxRuns(argv);
338
+ const agentBatchSize = getAgentBatchSize(argv, sessionConfig);
339
+ const parallelWorkers = getParallelWorkers(argv, sessionConfig);
285
340
  const timeoutMs = sessionConfig.timeout_ms ?? 30 * 60 * 1000;
286
341
  const selfCliPath = resolve(process.argv[1] ?? "");
287
342
  await mkdir(artifactsDir, { recursive: true });
@@ -328,6 +383,32 @@ async function cmdRunToCompletion(argv) {
328
383
  ...bundle,
329
384
  audit_state: blockedState,
330
385
  });
386
+ const blockRunId = buildRunId(obligationId, runCount + 1);
387
+ const blockPaths = getRunPaths(artifactsDir, blockRunId);
388
+ const blockPendingTasks = buildPendingAuditTasks(bundle).slice(0, agentBatchSize);
389
+ const blockPendingTasksPath = join(blockPaths.runDir, "pending-audit-tasks.json");
390
+ const blockAuditResultsPath = join(blockPaths.runDir, "audit-results.json");
391
+ const blockTask = {
392
+ contract_version: "audit-code-worker/v1alpha1",
393
+ run_id: blockRunId,
394
+ repo_root: root,
395
+ artifacts_dir: artifactsDir,
396
+ obligation_id: obligationId,
397
+ preferred_executor: preferredExecutor,
398
+ result_path: blockPaths.resultPath,
399
+ worker_command: [
400
+ process.execPath,
401
+ selfCliPath,
402
+ "worker-run",
403
+ "--task",
404
+ blockPaths.taskPath,
405
+ ],
406
+ audit_results_path: blockAuditResultsPath,
407
+ pending_audit_tasks_path: blockPendingTasksPath,
408
+ };
409
+ const blockPrompt = renderWorkerPrompt(blockTask);
410
+ await writeWorkerTaskFiles(blockTask, blockPrompt, blockPaths, artifactsDir);
411
+ await writeJsonFile(blockPendingTasksPath, blockPendingTasks);
331
412
  await emitEnvelope({
332
413
  root,
333
414
  artifactsDir,
@@ -369,9 +450,143 @@ async function cmdRunToCompletion(argv) {
369
450
  });
370
451
  return;
371
452
  }
453
+ if (preferredExecutor === "agent" && parallelWorkers > 1) {
454
+ const allPendingTasks = buildPendingAuditTasks(bundle);
455
+ const taskGroups = chunkArray(allPendingTasks.slice(0, parallelWorkers * agentBatchSize), agentBatchSize);
456
+ const workerSlots = [];
457
+ for (const group of taskGroups) {
458
+ runCount += 1;
459
+ const slotRunId = buildRunId(obligationId, runCount);
460
+ const slotPaths = getRunPaths(artifactsDir, slotRunId);
461
+ const slotAuditResultsPath = join(slotPaths.runDir, "audit-results.json");
462
+ const slotPendingTasksPath = join(slotPaths.runDir, "pending-audit-tasks.json");
463
+ const slotTask = {
464
+ contract_version: "audit-code-worker/v1alpha1",
465
+ run_id: slotRunId,
466
+ repo_root: root,
467
+ artifacts_dir: artifactsDir,
468
+ obligation_id: obligationId,
469
+ preferred_executor: "agent",
470
+ result_path: slotPaths.resultPath,
471
+ worker_command: [
472
+ process.execPath,
473
+ selfCliPath,
474
+ "worker-run",
475
+ "--task",
476
+ slotPaths.taskPath,
477
+ ],
478
+ audit_results_path: slotAuditResultsPath,
479
+ pending_audit_tasks_path: slotPendingTasksPath,
480
+ skip_worker_command: true,
481
+ };
482
+ const slotPrompt = renderWorkerPrompt(slotTask);
483
+ await writeWorkerTaskFiles(slotTask, slotPrompt, slotPaths, artifactsDir);
484
+ await writeJsonFile(slotPendingTasksPath, group);
485
+ workerSlots.push({
486
+ runId: slotRunId,
487
+ paths: slotPaths,
488
+ auditResultsPath: slotAuditResultsPath,
489
+ pendingTasksPath: slotPendingTasksPath,
490
+ group,
491
+ });
492
+ }
493
+ const parallelStartedAt = new Date().toISOString();
494
+ await Promise.allSettled(workerSlots.map((slot) => provider.launch({
495
+ repoRoot: root,
496
+ runId: slot.runId,
497
+ obligationId,
498
+ promptPath: slot.paths.promptPath,
499
+ taskPath: slot.paths.taskPath,
500
+ resultPath: slot.paths.resultPath,
501
+ stdoutPath: slot.paths.stdoutPath,
502
+ stderrPath: slot.paths.stderrPath,
503
+ uiMode,
504
+ timeoutMs,
505
+ model: resolveModelForTasks(slot.group, sessionConfig),
506
+ })));
507
+ let batchProgress = false;
508
+ for (const slot of workerSlots) {
509
+ const parallelEndedAt = new Date().toISOString();
510
+ let slotStatus = "no_progress";
511
+ try {
512
+ const auditResults = await readJsonFile(slot.auditResultsPath);
513
+ const pendingTaskIds = new Set(slot.group.map((t) => t.task_id));
514
+ const matchedCount = auditResults.filter((r) => pendingTaskIds.has(r.task_id)).length;
515
+ if (slot.group.length > 0 && matchedCount === 0) {
516
+ throw new Error("Worker did not emit any audit results for the assigned tasks.");
517
+ }
518
+ const issues = validateAuditResults(auditResults, slot.group);
519
+ const errors = issues.filter((issue) => issue.severity === "error");
520
+ const warnings = issues.filter((issue) => issue.severity === "warning");
521
+ if (warnings.length > 0) {
522
+ process.stderr.write(`audit-results validation: ${warnings.length} warning(s) for ${slot.runId}:\n` +
523
+ formatAuditResultIssues(warnings) +
524
+ "\n");
525
+ }
526
+ if (errors.length > 0) {
527
+ throw new Error(`audit-results validation failed with ${errors.length} error(s):\n` +
528
+ formatAuditResultIssues(errors));
529
+ }
530
+ const stepResult = await runAuditStep({
531
+ root,
532
+ artifactsDir,
533
+ preferredExecutor: "result_ingestion_executor",
534
+ auditResultsPath: slot.auditResultsPath,
535
+ });
536
+ slotStatus = stepResult.progress_made ? "completed" : "no_progress";
537
+ batchProgress ||= stepResult.progress_made;
538
+ if (stepResult.progress_made)
539
+ anyProgress = true;
540
+ for (const a of stepResult.artifacts_written)
541
+ artifactsWritten.add(a);
542
+ }
543
+ catch {
544
+ slotStatus = "failed";
545
+ }
546
+ await appendRunLedgerEntry(artifactsDir, {
547
+ run_id: slot.runId,
548
+ provider: provider.name,
549
+ obligation_id: obligationId,
550
+ selected_executor: "agent",
551
+ status: slotStatus,
552
+ started_at: parallelStartedAt,
553
+ ended_at: parallelEndedAt,
554
+ result_path: slot.paths.resultPath,
555
+ });
556
+ artifactsWritten.add("run-ledger.json");
557
+ }
558
+ if (!batchProgress) {
559
+ const bundleAfter = await loadArtifactBundle(artifactsDir);
560
+ const state = bundleAfter.audit_state ?? deriveAuditState(bundleAfter);
561
+ await emitEnvelope({
562
+ root,
563
+ artifactsDir,
564
+ bundle: bundleAfter,
565
+ audit_state: state,
566
+ selected_obligation: obligationId,
567
+ selected_executor: "agent",
568
+ progress_made: anyProgress,
569
+ artifacts_written: Array.from(artifactsWritten),
570
+ progress_summary: "Parallel worker batch made no progress.",
571
+ next_likely_step: obligationId,
572
+ providerName: provider.name,
573
+ });
574
+ return;
575
+ }
576
+ continue;
577
+ }
372
578
  runCount += 1;
373
579
  const runId = buildRunId(obligationId, runCount);
374
580
  const paths = getRunPaths(artifactsDir, runId);
581
+ const pendingAuditTasks = preferredExecutor === "agent"
582
+ ? buildPendingAuditTasks(bundle).slice(0, agentBatchSize)
583
+ : undefined;
584
+ const pendingAuditTasksPath = preferredExecutor === "agent"
585
+ ? join(paths.runDir, "pending-audit-tasks.json")
586
+ : undefined;
587
+ const providerAuditResultsPath = preferredExecutor === "agent"
588
+ ? join(paths.runDir, "audit-results.json")
589
+ : auditResultsPath;
375
590
  const task = {
376
591
  contract_version: "audit-code-worker/v1alpha1",
377
592
  run_id: runId,
@@ -387,12 +602,16 @@ async function cmdRunToCompletion(argv) {
387
602
  "--task",
388
603
  paths.taskPath,
389
604
  ],
390
- audit_results_path: auditResultsPath,
605
+ audit_results_path: providerAuditResultsPath,
606
+ pending_audit_tasks_path: pendingAuditTasksPath,
391
607
  runtime_updates_path: runtimeUpdatesPath,
392
608
  external_analyzer_results_path: externalAnalyzerPath,
393
609
  };
394
610
  const prompt = renderWorkerPrompt(task);
395
611
  await writeWorkerTaskFiles(task, prompt, paths, artifactsDir);
612
+ if (pendingAuditTasksPath && pendingAuditTasks) {
613
+ await writeJsonFile(pendingAuditTasksPath, pendingAuditTasks);
614
+ }
396
615
  const startedAt = new Date().toISOString();
397
616
  let workerResult;
398
617
  try {
@@ -407,6 +626,9 @@ async function cmdRunToCompletion(argv) {
407
626
  stderrPath: paths.stderrPath,
408
627
  uiMode,
409
628
  timeoutMs,
629
+ model: pendingAuditTasks
630
+ ? resolveModelForTasks(pendingAuditTasks, sessionConfig)
631
+ : undefined,
410
632
  });
411
633
  const candidate = await readJsonFile(paths.resultPath);
412
634
  workerResult = isWorkerResult(candidate)
@@ -459,7 +681,7 @@ async function cmdRunToCompletion(argv) {
459
681
  artifactsWritten.add("run-ledger.json");
460
682
  if (externalAnalyzerPath)
461
683
  pendingExternalAnalyzerPath = undefined;
462
- if (auditResultsPath)
684
+ if (providerAuditResultsPath)
463
685
  pendingAuditResultsPath = undefined;
464
686
  if (runtimeUpdatesPath)
465
687
  pendingRuntimeUpdatesPath = undefined;
@@ -509,10 +731,39 @@ async function cmdWorkerRun(argv) {
509
731
  const task = await readJsonFile(taskPath);
510
732
  let workerResult;
511
733
  try {
734
+ if (task.preferred_executor === "agent" && !task.audit_results_path) {
735
+ throw new Error("agent worker-run requires audit_results_path so provider-assisted review can be ingested.");
736
+ }
737
+ if (task.preferred_executor === "agent" && task.audit_results_path) {
738
+ const pendingTasks = task.pending_audit_tasks_path
739
+ ? await readJsonFile(task.pending_audit_tasks_path)
740
+ : [];
741
+ const auditResults = await readJsonFile(task.audit_results_path);
742
+ const pendingTaskIds = new Set(pendingTasks.map((item) => item.task_id));
743
+ const matchedResultCount = auditResults.filter((result) => pendingTaskIds.has(result.task_id)).length;
744
+ if (pendingTasks.length > 0 && matchedResultCount === 0) {
745
+ throw new Error("Provider-assisted review did not emit any audit results for the pending audit tasks.");
746
+ }
747
+ const issues = validateAuditResults(auditResults, pendingTasks);
748
+ const errors = issues.filter((issue) => issue.severity === "error");
749
+ const warnings = issues.filter((issue) => issue.severity === "warning");
750
+ if (warnings.length > 0) {
751
+ process.stderr.write(`audit-results validation: ${warnings.length} warning(s):\n` +
752
+ formatAuditResultIssues(warnings) +
753
+ "\n");
754
+ }
755
+ if (errors.length > 0) {
756
+ throw new Error(`audit-results validation failed with ${errors.length} error(s):\n` +
757
+ formatAuditResultIssues(errors));
758
+ }
759
+ }
760
+ const preferredExecutor = task.preferred_executor === "agent"
761
+ ? "result_ingestion_executor"
762
+ : task.preferred_executor;
512
763
  const result = await runAuditStep({
513
764
  root: task.repo_root,
514
765
  artifactsDir: task.artifacts_dir,
515
- preferredExecutor: task.preferred_executor,
766
+ preferredExecutor,
516
767
  auditResultsPath: task.audit_results_path,
517
768
  runtimeUpdatesPath: task.runtime_updates_path,
518
769
  externalAnalyzerPath: task.external_analyzer_results_path,
@@ -630,11 +881,7 @@ async function cmdValidate(argv) {
630
881
  const providerIssues = rawSessionConfig === undefined || sessionConfigIssues.length > 0
631
882
  ? []
632
883
  : prefixValidationIssues("session_config", validateConfiguredProviderEnvironment(rawSessionConfig));
633
- const issues = [
634
- ...artifactIssues,
635
- ...sessionConfigIssues,
636
- ...providerIssues,
637
- ];
884
+ const issues = [...artifactIssues, ...sessionConfigIssues, ...providerIssues];
638
885
  const resolvedProvider = rawSessionConfig === undefined
639
886
  ? "local-subprocess"
640
887
  : sessionConfigIssues.length > 0
@@ -1,5 +1,4 @@
1
1
  import type { CoverageFileRecord, CoverageMatrix, Lens, ReviewedLineRange } from "./types.js";
2
- export declare function mergeRanges(ranges: ReviewedLineRange[]): ReviewedLineRange[];
3
2
  export declare function createCoverageMatrix(paths: string[]): CoverageMatrix;
4
3
  export declare function markExcludedPath(matrix: CoverageMatrix, path: string, classificationStatus: string): void;
5
4
  export declare function applyUnitCoverage(matrix: CoverageMatrix, path: string, unitId: string, requiredLenses: Lens[]): void;
package/dist/coverage.js CHANGED
@@ -1,29 +1,3 @@
1
- function sortRanges(ranges) {
2
- return [...ranges].sort((a, b) => {
3
- if (a.path !== b.path)
4
- return a.path.localeCompare(b.path);
5
- if (a.start !== b.start)
6
- return a.start - b.start;
7
- return a.end - b.end;
8
- });
9
- }
10
- export function mergeRanges(ranges) {
11
- const sorted = sortRanges(ranges);
12
- const merged = [];
13
- for (const range of sorted) {
14
- const last = merged[merged.length - 1];
15
- if (!last || last.path !== range.path || range.start > last.end + 1) {
16
- merged.push({ ...range });
17
- continue;
18
- }
19
- last.end = Math.max(last.end, range.end);
20
- last.pass_id = `${last.pass_id},${range.pass_id}`;
21
- last.agent_role = [last.agent_role, range.agent_role]
22
- .filter(Boolean)
23
- .join(",");
24
- }
25
- return merged;
26
- }
27
1
  export function createCoverageMatrix(paths) {
28
2
  return {
29
3
  files: paths.map((path) => ({
@@ -33,7 +7,6 @@ export function createCoverageMatrix(paths) {
33
7
  audit_status: "pending",
34
8
  required_lenses: [],
35
9
  completed_lenses: [],
36
- reviewed_line_ranges: [],
37
10
  })),
38
11
  };
39
12
  }
@@ -45,7 +18,6 @@ export function markExcludedPath(matrix, path, classificationStatus) {
45
18
  record.audit_status = "excluded";
46
19
  record.required_lenses = [];
47
20
  record.completed_lenses = [];
48
- record.reviewed_line_ranges = [];
49
21
  record.unit_ids = [];
50
22
  }
51
23
  export function applyUnitCoverage(matrix, path, unitId, requiredLenses) {
@@ -65,21 +37,18 @@ export function applyReviewedRanges(matrix, reviewedRanges) {
65
37
  const record = matrix.files.find((file) => file.path === range.path);
66
38
  if (!record || record.audit_status === "excluded")
67
39
  continue;
68
- record.reviewed_line_ranges.push(range);
69
- record.reviewed_line_ranges = mergeRanges(record.reviewed_line_ranges);
70
40
  if (range.lens && !record.completed_lenses.includes(range.lens)) {
71
41
  record.completed_lenses.push(range.lens);
72
42
  }
73
43
  }
74
44
  for (const file of matrix.files) {
75
- const hasAllRequired = file.required_lenses.every((lens) => file.completed_lenses.includes(lens));
76
- if (file.audit_status === "excluded") {
45
+ if (file.audit_status === "excluded")
77
46
  continue;
78
- }
47
+ const hasAllRequired = file.required_lenses.every((lens) => file.completed_lenses.includes(lens));
79
48
  if (hasAllRequired && file.required_lenses.length > 0) {
80
49
  file.audit_status = "complete";
81
50
  }
82
- else if (file.reviewed_line_ranges.length > 0) {
51
+ else if (file.completed_lenses.length > 0) {
83
52
  file.audit_status = "partial";
84
53
  }
85
54
  }
@@ -6,6 +6,8 @@ function inferLanguage(path) {
6
6
  return "typescript";
7
7
  case "js":
8
8
  case "jsx":
9
+ case "mjs":
10
+ case "cjs":
9
11
  return "javascript";
10
12
  case "py":
11
13
  return "python";
@@ -51,7 +51,8 @@ export async function loadArtifactBundle(root) {
51
51
  bundle.root_cause_clusters = await readOptionalJsonFile(`${root}/root_cause_clusters.json`);
52
52
  bundle.synthesis_report = await readOptionalJsonFile(`${root}/synthesis_report.json`);
53
53
  bundle.audit_state = await readOptionalJsonFile(`${root}/audit_state.json`);
54
- bundle.artifact_metadata = await readOptionalJsonFile(`${root}/artifact_metadata.json`);
54
+ bundle.artifact_metadata =
55
+ await readOptionalJsonFile(`${root}/artifact_metadata.json`);
55
56
  return bundle;
56
57
  }
57
58
  export async function writeCoreArtifacts(root, bundle) {
@@ -27,58 +27,76 @@ export async function advanceAudit(bundle, options = {}) {
27
27
  };
28
28
  }
29
29
  let run;
30
- switch (selectedExecutor) {
31
- case "intake_executor":
32
- if (!options.root)
33
- throw new Error("advanceAudit intake_executor requires root");
34
- run = await runIntakeExecutor(bundle, options.root);
35
- break;
36
- case "structure_executor":
37
- run = runStructureExecutor(bundle);
38
- break;
39
- case "planning_executor":
40
- run = runPlanningExecutor(bundle, options.lineIndex ?? {});
41
- break;
42
- case "result_ingestion_executor":
43
- run = runResultIngestionExecutor(bundle, options.auditResults ?? bundle.audit_results ?? []);
44
- break;
45
- case "synthesis_executor":
46
- run = runSynthesisExecutor(bundle, options.auditResults);
47
- break;
48
- case "runtime_validation_update_executor":
49
- if (!options.runtimeValidationUpdates)
50
- throw new Error("advanceAudit runtime_validation_update_executor requires runtimeValidationUpdates");
51
- run = runRuntimeValidationUpdateExecutor(bundle, options.runtimeValidationUpdates);
52
- break;
53
- case "external_analyzer_import_executor":
54
- if (!options.externalAnalyzerResults)
55
- throw new Error("advanceAudit external_analyzer_import_executor requires externalAnalyzerResults");
56
- run = runExternalAnalyzerImportExecutor(bundle, options.externalAnalyzerResults);
57
- break;
58
- case "auto_fix_executor":
59
- if (!options.root)
60
- throw new Error("advanceAudit auto_fix_executor requires root");
61
- run = runAutoFixExecutor(bundle, options.root);
62
- break;
63
- case "syntax_resolution_executor":
64
- if (!options.root)
65
- throw new Error("advanceAudit syntax_resolution_executor requires root");
66
- run = runSyntaxResolutionExecutor(bundle, options.root);
67
- break;
68
- default:
69
- const state = deriveAuditState(bundle);
70
- state.last_executor = selectedExecutor;
71
- state.last_obligation = selectedObligation ?? undefined;
72
- return {
73
- audit_state: state,
74
- selected_obligation: selectedObligation,
75
- selected_executor: selectedExecutor,
76
- progress_made: false,
77
- artifacts_written: ["audit_state.json"],
78
- progress_summary: `Executor ${selectedExecutor} is selected but not yet dispatched through advance-audit.`,
79
- next_likely_step: selectedObligation,
80
- updated_bundle: { ...bundle, audit_state: state },
81
- };
30
+ try {
31
+ switch (selectedExecutor) {
32
+ case "intake_executor":
33
+ if (!options.root)
34
+ throw new Error("advanceAudit intake_executor requires root");
35
+ run = await runIntakeExecutor(bundle, options.root);
36
+ break;
37
+ case "structure_executor":
38
+ run = runStructureExecutor(bundle);
39
+ break;
40
+ case "planning_executor":
41
+ run = runPlanningExecutor(bundle, options.lineIndex ?? {});
42
+ break;
43
+ case "result_ingestion_executor":
44
+ run = runResultIngestionExecutor(bundle, options.auditResults ?? bundle.audit_results ?? []);
45
+ break;
46
+ case "synthesis_executor":
47
+ run = runSynthesisExecutor(bundle, options.auditResults);
48
+ break;
49
+ case "runtime_validation_update_executor":
50
+ if (!options.runtimeValidationUpdates)
51
+ throw new Error("advanceAudit runtime_validation_update_executor requires runtimeValidationUpdates");
52
+ run = runRuntimeValidationUpdateExecutor(bundle, options.runtimeValidationUpdates);
53
+ break;
54
+ case "external_analyzer_import_executor":
55
+ if (!options.externalAnalyzerResults)
56
+ throw new Error("advanceAudit external_analyzer_import_executor requires externalAnalyzerResults");
57
+ run = runExternalAnalyzerImportExecutor(bundle, options.externalAnalyzerResults);
58
+ break;
59
+ case "auto_fix_executor":
60
+ if (!options.root)
61
+ throw new Error("advanceAudit auto_fix_executor requires root");
62
+ run = runAutoFixExecutor(bundle, options.root);
63
+ break;
64
+ case "syntax_resolution_executor":
65
+ if (!options.root)
66
+ throw new Error("advanceAudit syntax_resolution_executor requires root");
67
+ run = runSyntaxResolutionExecutor(bundle, options.root);
68
+ break;
69
+ default: {
70
+ const state = deriveAuditState(bundle);
71
+ state.last_executor = selectedExecutor;
72
+ state.last_obligation = selectedObligation ?? undefined;
73
+ return {
74
+ audit_state: state,
75
+ selected_obligation: selectedObligation,
76
+ selected_executor: selectedExecutor,
77
+ progress_made: false,
78
+ artifacts_written: ["audit_state.json"],
79
+ progress_summary: `Executor ${selectedExecutor} is selected but not yet dispatched through advance-audit.`,
80
+ next_likely_step: selectedObligation,
81
+ updated_bundle: { ...bundle, audit_state: state },
82
+ };
83
+ }
84
+ }
85
+ }
86
+ catch (err) {
87
+ const state = deriveAuditState(bundle);
88
+ state.last_executor = selectedExecutor;
89
+ state.last_obligation = selectedObligation ?? undefined;
90
+ return {
91
+ audit_state: state,
92
+ selected_obligation: selectedObligation,
93
+ selected_executor: selectedExecutor,
94
+ progress_made: false,
95
+ artifacts_written: [],
96
+ progress_summary: `Executor ${selectedExecutor} failed: ${err instanceof Error ? err.message : String(err)}`,
97
+ next_likely_step: selectedObligation,
98
+ updated_bundle: { ...bundle, audit_state: state },
99
+ };
82
100
  }
83
101
  const metadata = computeArtifactMetadata(run.updated, bundle.artifact_metadata);
84
102
  const metadataBundle = { ...run.updated, artifact_metadata: metadata };
@@ -10,11 +10,12 @@ function lensSetForFlow(concerns) {
10
10
  return concerns.filter((concern) => allowed.includes(concern));
11
11
  }
12
12
  export function buildFlowCoverage(criticalFlows, coverageMatrix) {
13
+ const fileIndex = new Map(coverageMatrix.files.map((file) => [file.path, file]));
13
14
  const flows = criticalFlows.flows.map((flow) => {
14
15
  const required = lensSetForFlow(flow.concerns);
15
16
  const completed = new Set();
16
17
  for (const path of flow.paths) {
17
- const record = coverageMatrix.files.find((file) => file.path === path);
18
+ const record = fileIndex.get(path);
18
19
  if (!record || record.audit_status === "excluded") {
19
20
  continue;
20
21
  }
@@ -1,3 +1,3 @@
1
1
  import type { AuditTask } from "../types.js";
2
2
  import type { CriticalFlowManifest } from "../types/flows.js";
3
- export declare function buildFlowAwareTaskAugmentations(existingTasks: AuditTask[], criticalFlows: CriticalFlowManifest, lineIndex: Record<string, number>): AuditTask[];
3
+ export declare function buildFlowAwareTaskAugmentations(existingTasks: AuditTask[], criticalFlows: CriticalFlowManifest, _lineIndex: Record<string, number>): AuditTask[];