auditor-lambda 0.10.2 → 0.10.7

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 (186) hide show
  1. package/audit-code-wrapper-build.mjs +198 -0
  2. package/audit-code-wrapper-install-hosts.mjs +1140 -0
  3. package/audit-code-wrapper-io.mjs +155 -0
  4. package/audit-code-wrapper-legacy.mjs +125 -0
  5. package/audit-code-wrapper-lib.mjs +17 -1801
  6. package/audit-code-wrapper-opencode.mjs +256 -0
  7. package/dispatch/merge-results.mjs +5 -3
  8. package/dispatch/validate-result.mjs +2 -2
  9. package/dist/adapters/coverageSummary.js +6 -2
  10. package/dist/adapters/normalizeExternal.js +16 -1
  11. package/dist/adapters/npmAudit.js +20 -9
  12. package/dist/adapters/semgrep.js +26 -1
  13. package/dist/cli/advanceAuditCommand.d.ts +1 -0
  14. package/dist/cli/advanceAuditCommand.js +95 -0
  15. package/dist/cli/args.js +1 -2
  16. package/dist/cli/auditStep.js +2 -2
  17. package/dist/cli/cleanup.d.ts +11 -1
  18. package/dist/cli/cleanup.js +25 -5
  19. package/dist/cli/cleanupCommand.d.ts +1 -0
  20. package/dist/cli/cleanupCommand.js +24 -0
  21. package/dist/cli/dispatch.d.ts +55 -31
  22. package/dist/cli/dispatch.js +298 -241
  23. package/dist/cli/dispatchStatusCommand.d.ts +1 -0
  24. package/dist/cli/dispatchStatusCommand.js +68 -0
  25. package/dist/cli/explainTaskCommand.d.ts +1 -0
  26. package/dist/cli/explainTaskCommand.js +33 -0
  27. package/dist/cli/importExternalAnalyzerCommand.d.ts +1 -0
  28. package/dist/cli/importExternalAnalyzerCommand.js +20 -0
  29. package/dist/cli/ingestResultsCommand.d.ts +1 -0
  30. package/dist/cli/ingestResultsCommand.js +34 -0
  31. package/dist/cli/intakeCommand.d.ts +1 -0
  32. package/dist/cli/intakeCommand.js +17 -0
  33. package/dist/cli/lineIndex.js +19 -12
  34. package/dist/cli/mergeAndIngestCommand.js +68 -26
  35. package/dist/cli/nextStepCommand.d.ts +139 -0
  36. package/dist/cli/nextStepCommand.js +281 -232
  37. package/dist/cli/planCommand.d.ts +1 -0
  38. package/dist/cli/planCommand.js +16 -0
  39. package/dist/cli/prepareDispatchCommand.d.ts +1 -0
  40. package/dist/cli/prepareDispatchCommand.js +25 -0
  41. package/dist/cli/quotaCommand.d.ts +1 -0
  42. package/dist/cli/quotaCommand.js +56 -0
  43. package/dist/cli/requeueCommand.d.ts +1 -0
  44. package/dist/cli/requeueCommand.js +10 -0
  45. package/dist/cli/runToCompletion.js +451 -412
  46. package/dist/cli/sampleRunCommand.d.ts +1 -0
  47. package/dist/cli/sampleRunCommand.js +93 -0
  48. package/dist/cli/statusCommand.js +1 -1
  49. package/dist/cli/steps.js +4 -1
  50. package/dist/cli/submitPacketCommand.js +16 -15
  51. package/dist/cli/synthesizeCommand.d.ts +1 -0
  52. package/dist/cli/synthesizeCommand.js +15 -0
  53. package/dist/cli/updateRuntimeValidationCommand.d.ts +1 -0
  54. package/dist/cli/updateRuntimeValidationCommand.js +16 -0
  55. package/dist/cli/validateCommand.d.ts +1 -0
  56. package/dist/cli/validateCommand.js +41 -0
  57. package/dist/cli/validateResultCommand.d.ts +1 -0
  58. package/dist/cli/validateResultCommand.js +63 -0
  59. package/dist/cli/validateResultsCommand.d.ts +1 -0
  60. package/dist/cli/validateResultsCommand.js +31 -0
  61. package/dist/cli/workerRunCommand.d.ts +15 -1
  62. package/dist/cli/workerRunCommand.js +40 -4
  63. package/dist/cli.d.ts +3 -2
  64. package/dist/cli.js +21 -628
  65. package/dist/coverage.js +7 -3
  66. package/dist/extractors/analyzers/css.js +2 -2
  67. package/dist/extractors/analyzers/html.js +2 -2
  68. package/dist/extractors/analyzers/python.js +2 -2
  69. package/dist/extractors/analyzers/registry.js +17 -36
  70. package/dist/extractors/analyzers/treeSitter.d.ts +10 -1
  71. package/dist/extractors/analyzers/treeSitter.js +28 -6
  72. package/dist/extractors/analyzers/typescript.js +104 -85
  73. package/dist/extractors/browserExtension.js +4 -1
  74. package/dist/extractors/designAssessment.js +21 -21
  75. package/dist/extractors/fsIntake.js +34 -10
  76. package/dist/extractors/graph.js +17 -7
  77. package/dist/extractors/graphManifestEdges/cargo.d.ts +4 -0
  78. package/dist/extractors/graphManifestEdges/cargo.js +107 -0
  79. package/dist/extractors/graphManifestEdges/go.d.ts +5 -0
  80. package/dist/extractors/graphManifestEdges/go.js +151 -0
  81. package/dist/extractors/graphManifestEdges/index.d.ts +8 -0
  82. package/dist/extractors/graphManifestEdges/index.js +11 -0
  83. package/dist/extractors/graphManifestEdges/jsonc.d.ts +3 -0
  84. package/dist/extractors/graphManifestEdges/jsonc.js +97 -0
  85. package/dist/extractors/graphManifestEdges/maven.d.ts +3 -0
  86. package/dist/extractors/graphManifestEdges/maven.js +73 -0
  87. package/dist/extractors/graphManifestEdges/packageJson.d.ts +19 -0
  88. package/dist/extractors/graphManifestEdges/packageJson.js +204 -0
  89. package/dist/extractors/graphManifestEdges/pnpm.d.ts +2 -0
  90. package/dist/extractors/graphManifestEdges/pnpm.js +42 -0
  91. package/dist/extractors/graphManifestEdges/pyproject.d.ts +3 -0
  92. package/dist/extractors/graphManifestEdges/pyproject.js +83 -0
  93. package/dist/extractors/graphManifestEdges/toml.d.ts +4 -0
  94. package/dist/extractors/graphManifestEdges/toml.js +68 -0
  95. package/dist/extractors/graphManifestEdges/typescript.d.ts +3 -0
  96. package/dist/extractors/graphManifestEdges/typescript.js +56 -0
  97. package/dist/extractors/graphManifestEdges/workspace.d.ts +10 -0
  98. package/dist/extractors/graphManifestEdges/workspace.js +72 -0
  99. package/dist/extractors/graphManifestEdges/yaml.d.ts +3 -0
  100. package/dist/extractors/graphManifestEdges/yaml.js +59 -0
  101. package/dist/extractors/graphManifestEdges/yamlPaths.d.ts +4 -0
  102. package/dist/extractors/graphManifestEdges/yamlPaths.js +89 -0
  103. package/dist/extractors/graphPythonImports.js +4 -20
  104. package/dist/extractors/pathPatterns.js +3 -13
  105. package/dist/io/artifacts.d.ts +1 -1
  106. package/dist/io/artifacts.js +4 -1
  107. package/dist/io/runArtifacts.d.ts +8 -2
  108. package/dist/io/runArtifacts.js +103 -69
  109. package/dist/io/toolingManifest.js +2 -1
  110. package/dist/orchestrator/advance.js +36 -0
  111. package/dist/orchestrator/artifactFreshness.d.ts +1 -1
  112. package/dist/orchestrator/artifactFreshness.js +1 -1
  113. package/dist/orchestrator/artifactMetadata.js +5 -5
  114. package/dist/orchestrator/auditTaskUtils.d.ts +4 -0
  115. package/dist/orchestrator/auditTaskUtils.js +8 -12
  116. package/dist/orchestrator/autoFixExecutor.js +40 -26
  117. package/dist/orchestrator/dependencyMap.js +1 -1
  118. package/dist/orchestrator/executorResult.d.ts +33 -0
  119. package/dist/orchestrator/executors.d.ts +7 -0
  120. package/dist/orchestrator/executors.js +24 -0
  121. package/dist/orchestrator/fileAnchors.js +42 -29
  122. package/dist/orchestrator/fileIntegrity.js +6 -1
  123. package/dist/orchestrator/flowCoverage.js +1 -2
  124. package/dist/orchestrator/flowPlanning.js +8 -4
  125. package/dist/orchestrator/graphEnrichmentExecutor.js +67 -45
  126. package/dist/orchestrator/ingestionExecutors.js +9 -1
  127. package/dist/orchestrator/intakeExecutors.d.ts +0 -4
  128. package/dist/orchestrator/intakeExecutors.js +24 -14
  129. package/dist/orchestrator/localCommands.d.ts +1 -0
  130. package/dist/orchestrator/localCommands.js +10 -17
  131. package/dist/orchestrator/nextStep.js +3 -1
  132. package/dist/orchestrator/requeueCommand.js +4 -0
  133. package/dist/orchestrator/reviewPacketGraph.js +50 -18
  134. package/dist/orchestrator/reviewPackets.js +10 -8
  135. package/dist/orchestrator/runtimeCommand.js +35 -7
  136. package/dist/orchestrator/runtimeValidationUpdate.js +6 -0
  137. package/dist/orchestrator/selectiveDeepening/highRiskClean.js +3 -2
  138. package/dist/orchestrator/selectiveDeepening/lensVerification.js +44 -18
  139. package/dist/orchestrator/staleness.js +3 -3
  140. package/dist/orchestrator/state.js +1 -1
  141. package/dist/orchestrator/syntaxResolutionExecutor.js +17 -24
  142. package/dist/orchestrator/synthesisExecutors.js +1 -0
  143. package/dist/orchestrator/taskBuilder.js +5 -4
  144. package/dist/providers/claudeCodeProvider.js +4 -1
  145. package/dist/providers/opencodeProvider.js +4 -1
  146. package/dist/quota/discoveredLimits.js +3 -3
  147. package/dist/quota/headerExtraction.js +5 -2
  148. package/dist/quota/headerExtractors/claudeCodeHeaderExtractor.js +3 -0
  149. package/dist/quota/headerExtractors/index.js +3 -3
  150. package/dist/quota/index.d.ts +3 -1
  151. package/dist/quota/index.js +3 -0
  152. package/dist/reporting/findingIdentity.d.ts +21 -0
  153. package/dist/reporting/findingIdentity.js +72 -0
  154. package/dist/reporting/findingRanks.d.ts +3 -0
  155. package/dist/reporting/findingRanks.js +24 -0
  156. package/dist/reporting/mergeFindings.js +1 -24
  157. package/dist/reporting/synthesis.d.ts +3 -1
  158. package/dist/reporting/synthesis.js +36 -7
  159. package/dist/reporting/synthesisNarrativePrompt.js +3 -0
  160. package/dist/reporting/workBlocks.js +1 -14
  161. package/dist/supervisor/operatorHandoff.js +2 -6
  162. package/dist/supervisor/runLedger.js +30 -41
  163. package/dist/types/activeDispatch.d.ts +31 -0
  164. package/dist/types/activeDispatch.js +2 -0
  165. package/dist/types.d.ts +21 -4
  166. package/dist/types.js +24 -16
  167. package/dist/validation/artifacts.js +3 -0
  168. package/dist/validation/auditResults.js +8 -2
  169. package/package.json +2 -2
  170. package/schemas/audit_findings.schema.json +5 -1
  171. package/schemas/audit_plan_metrics.schema.json +1 -1
  172. package/schemas/audit_result.schema.json +5 -6
  173. package/schemas/audit_task.schema.json +1 -4
  174. package/schemas/blind_spot_register.schema.json +1 -1
  175. package/schemas/coverage_matrix.schema.json +2 -8
  176. package/schemas/finding.schema.json +3 -17
  177. package/schemas/flow_coverage.schema.json +2 -8
  178. package/schemas/graph_bundle.schema.json +31 -0
  179. package/schemas/lens.schema.json +7 -0
  180. package/schemas/review_packets.schema.json +6 -17
  181. package/schemas/step_contract.schema.json +8 -2
  182. package/schemas/unit_manifest.schema.json +1 -4
  183. package/scripts/postinstall.mjs +3 -1
  184. package/skills/audit-code/audit-code.prompt.md +2 -3
  185. package/dist/extractors/graphManifestEdges.d.ts +0 -12
  186. package/dist/extractors/graphManifestEdges.js +0 -1135
@@ -0,0 +1,68 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { readJsonFile, isFileMissingError } from "@audit-tools/shared";
4
+ import { ACTIVE_DISPATCH_FILENAME, loadDispatchResultMap } from "./dispatch.js";
5
+ import { getArtifactsDir } from "./args.js";
6
+ export async function cmdDispatchStatus(argv) {
7
+ const artifactsDir = getArtifactsDir(argv);
8
+ const activeDispatchPath = join(artifactsDir, ACTIVE_DISPATCH_FILENAME);
9
+ let activeDispatch = null;
10
+ try {
11
+ activeDispatch = await readJsonFile(activeDispatchPath);
12
+ }
13
+ catch (e) {
14
+ if (!isFileMissingError(e))
15
+ throw e;
16
+ }
17
+ if (!activeDispatch) {
18
+ console.log(JSON.stringify({ status: "no_active_dispatch" }, null, 2));
19
+ return;
20
+ }
21
+ const runDir = join(artifactsDir, "runs", activeDispatch.run_id);
22
+ const resultMap = await loadDispatchResultMap(runDir);
23
+ if (!resultMap) {
24
+ console.log(JSON.stringify({
25
+ status: "missing_result_map",
26
+ run_id: activeDispatch.run_id,
27
+ }, null, 2));
28
+ return;
29
+ }
30
+ const packetIds = [...new Set(resultMap.entries.map((e) => e.packet_id))];
31
+ const packetStatus = [];
32
+ for (const pid of packetIds) {
33
+ if (pid === "__prior_dispatch__")
34
+ continue;
35
+ const entries = resultMap.entries.filter((e) => e.packet_id === pid);
36
+ let completed = 0;
37
+ const missing = [];
38
+ for (const entry of entries) {
39
+ try {
40
+ await readFile(entry.result_path, "utf8");
41
+ completed++;
42
+ }
43
+ catch {
44
+ missing.push(entry.task_id);
45
+ }
46
+ }
47
+ packetStatus.push({
48
+ packet_id: pid,
49
+ task_count: entries.length,
50
+ completed_count: completed,
51
+ missing_task_ids: missing,
52
+ });
53
+ }
54
+ const totalTasks = packetStatus.reduce((s, p) => s + p.task_count, 0);
55
+ const completedTasks = packetStatus.reduce((s, p) => s + p.completed_count, 0);
56
+ const completedPackets = packetStatus.filter((p) => p.missing_task_ids.length === 0).length;
57
+ console.log(JSON.stringify({
58
+ run_id: activeDispatch.run_id,
59
+ dispatch_status: activeDispatch.status,
60
+ created_at: activeDispatch.created_at,
61
+ total_packets: packetStatus.length,
62
+ completed_packets: completedPackets,
63
+ total_tasks: totalTasks,
64
+ completed_tasks: completedTasks,
65
+ missing_tasks: totalTasks - completedTasks,
66
+ packets: packetStatus,
67
+ }, null, 2));
68
+ }
@@ -0,0 +1 @@
1
+ export declare function cmdExplainTask(argv: string[]): Promise<void>;
@@ -0,0 +1,33 @@
1
+ import { loadArtifactBundle } from "../io/artifacts.js";
2
+ import { getArtifactsDir, getFlag } from "./args.js";
3
+ export async function cmdExplainTask(argv) {
4
+ const artifactsDir = getArtifactsDir(argv);
5
+ const taskId = getFlag(argv, "--task-id") ?? argv[3];
6
+ if (!taskId) {
7
+ throw new Error("explain-task requires <task_id> or --task-id <task_id>");
8
+ }
9
+ const bundle = await loadArtifactBundle(artifactsDir);
10
+ const task = [...(bundle.audit_tasks ?? []), ...(bundle.requeue_tasks ?? [])].find((item) => item.task_id === taskId);
11
+ if (!task) {
12
+ throw new Error(`Unknown task_id '${taskId}'.`);
13
+ }
14
+ const coverageEntries = (bundle.coverage_matrix?.files ?? [])
15
+ .filter((file) => task.file_paths.includes(file.path))
16
+ .sort((a, b) => a.path.localeCompare(b.path));
17
+ const matchingResults = (bundle.audit_results ?? []).filter((result) => result.task_id === task.task_id);
18
+ console.log(JSON.stringify({
19
+ artifacts_dir: artifactsDir,
20
+ task_id: task.task_id,
21
+ task,
22
+ file_count: task.file_paths.length,
23
+ coverage_entries: coverageEntries,
24
+ pending_coverage: coverageEntries
25
+ .map((file) => ({
26
+ path: file.path,
27
+ missing_lenses: file.required_lenses.filter((lens) => !file.completed_lenses.includes(lens)),
28
+ }))
29
+ .filter((file) => file.missing_lenses.length > 0),
30
+ matching_result_count: matchingResults.length,
31
+ matching_finding_ids: matchingResults.flatMap((result) => result.findings.map((finding) => finding.id)),
32
+ }, null, 2));
33
+ }
@@ -0,0 +1 @@
1
+ export declare function cmdImportExternalAnalyzer(argv: string[]): Promise<void>;
@@ -0,0 +1,20 @@
1
+ import { readJsonFile } from "@audit-tools/shared";
2
+ import { runAuditStep } from "./auditStep.js";
3
+ import { getArtifactsDir, getFlag, getRootDir } from "./args.js";
4
+ export async function cmdImportExternalAnalyzer(argv) {
5
+ const artifactsDir = getArtifactsDir(argv);
6
+ const sourcePath = getFlag(argv, "--external-analyzer-results", `${artifactsDir}/external_analyzer_results.json`);
7
+ const externalAnalyzerResults = await readJsonFile(sourcePath);
8
+ const result = await runAuditStep({
9
+ root: getRootDir(argv),
10
+ artifactsDir,
11
+ preferredExecutor: "external_analyzer_import_executor",
12
+ externalAnalyzerPath: sourcePath,
13
+ });
14
+ console.log(JSON.stringify({
15
+ artifacts_dir: artifactsDir,
16
+ tool: externalAnalyzerResults.tool,
17
+ imported_count: externalAnalyzerResults.results.length,
18
+ selected_executor: result.selected_executor,
19
+ }, null, 2));
20
+ }
@@ -0,0 +1 @@
1
+ export declare function cmdIngestResults(argv: string[]): Promise<void>;
@@ -0,0 +1,34 @@
1
+ import { runAuditStep, ingestBatchAuditResults } from "./auditStep.js";
2
+ import { getArtifactsDir, getBatchResultsDir, getFlag, getRootDir } from "./args.js";
3
+ export async function cmdIngestResults(argv) {
4
+ const artifactsDir = getArtifactsDir(argv);
5
+ const batchResultsDir = getBatchResultsDir(argv);
6
+ if (batchResultsDir && getFlag(argv, "--results")) {
7
+ throw new Error("Use either --results <file> or --batch-results <dir>, not both.");
8
+ }
9
+ if (batchResultsDir) {
10
+ const result = await ingestBatchAuditResults({
11
+ root: getRootDir(argv),
12
+ artifactsDir,
13
+ batchDir: batchResultsDir,
14
+ });
15
+ console.log(JSON.stringify({
16
+ artifacts_dir: artifactsDir,
17
+ imported_files: result.batchFiles,
18
+ selected_executor: result.selected_executor,
19
+ progress_summary: result.progress_summary,
20
+ }, null, 2));
21
+ return;
22
+ }
23
+ const result = await runAuditStep({
24
+ root: getRootDir(argv),
25
+ artifactsDir,
26
+ preferredExecutor: "result_ingestion_executor",
27
+ auditResultsPath: getFlag(argv, "--results"),
28
+ });
29
+ console.log(JSON.stringify({
30
+ artifacts_dir: artifactsDir,
31
+ selected_executor: result.selected_executor,
32
+ progress_summary: result.progress_summary,
33
+ }, null, 2));
34
+ }
@@ -0,0 +1 @@
1
+ export declare function cmdIntake(argv: string[]): Promise<void>;
@@ -0,0 +1,17 @@
1
+ import { runAuditStep } from "./auditStep.js";
2
+ import { getArtifactsDir, getRootDir, warnIfNotGitRepo } from "./args.js";
3
+ export async function cmdIntake(argv) {
4
+ const root = getRootDir(argv);
5
+ warnIfNotGitRepo(root);
6
+ const artifactsDir = getArtifactsDir(argv);
7
+ const result = await runAuditStep({
8
+ root,
9
+ artifactsDir,
10
+ preferredExecutor: "intake_executor",
11
+ });
12
+ console.log(JSON.stringify({
13
+ artifacts_dir: artifactsDir,
14
+ selected_executor: result.selected_executor,
15
+ progress_summary: result.progress_summary,
16
+ }, null, 2));
17
+ }
@@ -8,9 +8,8 @@ import { countLines } from "./args.js";
8
8
  const LINE_COUNT_BATCH_SIZE = 25;
9
9
  export async function buildLineIndex(root, repoManifest) {
10
10
  const entries = [];
11
- const batchSize = LINE_COUNT_BATCH_SIZE;
12
- for (let i = 0; i < repoManifest.files.length; i += batchSize) {
13
- const batch = repoManifest.files.slice(i, i + batchSize);
11
+ for (let i = 0; i < repoManifest.files.length; i += LINE_COUNT_BATCH_SIZE) {
12
+ const batch = repoManifest.files.slice(i, i + LINE_COUNT_BATCH_SIZE);
14
13
  const results = await Promise.all(batch.map(async (file) => {
15
14
  try {
16
15
  return [
@@ -18,7 +17,8 @@ export async function buildLineIndex(root, repoManifest) {
18
17
  await countLines(resolve(root, file.path)),
19
18
  ];
20
19
  }
21
- catch {
20
+ catch (err) {
21
+ console.warn(`[lineIndex] Failed to count lines for '${file.path}': ${err instanceof Error ? err.message : String(err)}`);
22
22
  return [file.path, 0];
23
23
  }
24
24
  }));
@@ -28,14 +28,21 @@ export async function buildLineIndex(root, repoManifest) {
28
28
  }
29
29
  export async function buildLineIndexForPaths(root, paths) {
30
30
  const uniquePaths = [...new Set(paths)].sort();
31
- const entries = await Promise.all(uniquePaths.map(async (path) => {
32
- try {
33
- return [path, await countLines(resolve(root, path))];
34
- }
35
- catch {
36
- return [path, 0];
37
- }
38
- }));
31
+ const entries = [];
32
+ const batchSize = LINE_COUNT_BATCH_SIZE;
33
+ for (let i = 0; i < uniquePaths.length; i += batchSize) {
34
+ const batch = uniquePaths.slice(i, i + batchSize);
35
+ const results = await Promise.all(batch.map(async (path) => {
36
+ try {
37
+ return [path, await countLines(resolve(root, path))];
38
+ }
39
+ catch (err) {
40
+ console.warn(`[lineIndex] Failed to count lines for '${path}': ${err instanceof Error ? err.message : String(err)}`);
41
+ return [path, 0];
42
+ }
43
+ }));
44
+ entries.push(...results);
45
+ }
39
46
  return Object.fromEntries(entries);
40
47
  }
41
48
  export async function addFileLineCountHints(root, tasks) {
@@ -1,11 +1,12 @@
1
- import { readFile, readdir } from "node:fs/promises";
1
+ import { readFile, readdir, rm } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
2
3
  import { join, resolve } from "node:path";
3
4
  import { isFileMissingError, readJsonFile, writeJsonFile } from "@audit-tools/shared";
4
5
  import { validateAuditResults } from "../validation/auditResults.js";
5
6
  import { runAuditStep } from "./auditStep.js";
6
7
  import { DISPATCH_RESULT_MAP_FILENAME, ACTIVE_DISPATCH_FILENAME, loadDispatchResultMap, entriesByTaskId, buildPendingAuditTasks, } from "./dispatch.js";
7
8
  import { addFileLineCountHints } from "./lineIndex.js";
8
- import { isCanonicalResultFilename, getArtifactsDir, getFlag } from "./args.js";
9
+ import { isCanonicalResultFilename, taskResultPath, getArtifactsDir, getFlag } from "./args.js";
9
10
  import { buildWorkerResult } from "./workerResult.js";
10
11
  import { PACKET_SCHEMA_FILENAMES } from "../io/runArtifacts.js";
11
12
  // Schema pointer files prepare-dispatch copies into task-results/ for optional
@@ -38,8 +39,31 @@ export async function cmdMergeAndIngest(argv) {
38
39
  throw e;
39
40
  }
40
41
  if (priorSummary) {
41
- console.log(JSON.stringify({ ...priorSummary, idempotent_replay: true }, null, 2));
42
- return;
42
+ // A completion marker can go stale. Selective deepening appends new pending
43
+ // tasks to the SAME run-id, and — in the no-progress-loop bug — their answers
44
+ // already sit on disk under canonical per-task names while the marker says the
45
+ // run is done. If any pending task has a recoverable on-disk result, the marker
46
+ // no longer reflects reality: discard it and re-process so those answers ingest
47
+ // instead of replaying a no-op forever. A genuinely terminal run (no pending
48
+ // tasks, or pending tasks not yet answered — e.g. a new round handled under a
49
+ // different run-id) still replays cleanly.
50
+ let pendingWithResults = 0;
51
+ try {
52
+ const pending = await readJsonFile(tasksPath);
53
+ for (const task of pending) {
54
+ if (existsSync(taskResultPath(taskResultsDir, task.task_id))) {
55
+ pendingWithResults++;
56
+ }
57
+ }
58
+ }
59
+ catch { /* no pending-tasks file — treat as terminal and replay */ }
60
+ if (pendingWithResults === 0) {
61
+ console.log(JSON.stringify({ ...priorSummary, idempotent_replay: true }, null, 2));
62
+ return;
63
+ }
64
+ process.stderr.write(`[merge-and-ingest] completion marker for ${runId} is stale: ` +
65
+ `${pendingWithResults} pending task(s) have un-ingested on-disk results; re-processing.\n`);
66
+ await rm(mergeCompletePath, { force: true });
43
67
  }
44
68
  const workerTask = await readJsonFile(taskPath);
45
69
  const resultMap = await loadDispatchResultMap(runDir);
@@ -116,36 +140,48 @@ export async function cmdMergeAndIngest(argv) {
116
140
  }
117
141
  for (const task of allTasks) {
118
142
  const entry = entryByTaskId.get(task.task_id);
119
- if (!entry) {
120
- // No result-map entry => this pending task was not dispatched this round.
121
- // Leave it pending for the next dispatch; it is not a failure.
122
- notDispatched.push(task.task_id);
123
- continue;
124
- }
125
- const filePath = entry.result_path;
126
143
  let obj;
127
- try {
128
- obj = JSON.parse(await readFile(filePath, "utf8"));
129
- }
130
- catch (e) {
131
- if (isFileMissingError(e)) {
132
- const fallback = fallbackByTaskId.get(task.task_id);
133
- if (fallback) {
134
- process.stderr.write(`[merge-and-ingest] Recovered result for '${task.task_id}' from unexpected file (matched by task_id)\n`);
135
- obj = fallback;
144
+ if (entry) {
145
+ const filePath = entry.result_path;
146
+ try {
147
+ obj = JSON.parse(await readFile(filePath, "utf8"));
148
+ }
149
+ catch (e) {
150
+ if (isFileMissingError(e)) {
151
+ const fallback = fallbackByTaskId.get(task.task_id);
152
+ if (fallback) {
153
+ process.stderr.write(`[merge-and-ingest] Recovered result for '${task.task_id}' from unexpected file (matched by task_id)\n`);
154
+ obj = fallback;
155
+ }
156
+ else {
157
+ failing.push({
158
+ task_id: task.task_id,
159
+ errors: ["Missing audit result for assigned task."],
160
+ });
161
+ continue;
162
+ }
136
163
  }
137
164
  else {
138
- failing.push({
139
- task_id: task.task_id,
140
- errors: ["Missing audit result for assigned task."],
141
- });
165
+ failing.push({ task_id: task.task_id, errors: [`Invalid JSON: ${e.message}`] });
142
166
  continue;
143
167
  }
144
168
  }
145
- else {
146
- failing.push({ task_id: task.task_id, errors: [`Invalid JSON: ${e.message}`] });
169
+ }
170
+ else {
171
+ // No result-map entry => this pending task was not dispatched this round.
172
+ // But its answer may already exist on disk under a canonical per-task name
173
+ // (e.g. a selective-deepening task answered in a prior round whose dispatch
174
+ // manifest was later regenerated empty — the no-progress loop this guards
175
+ // against). Recover it by task_id so it ingests instead of looping forever
176
+ // as "pending"; only when no such file exists is the task genuinely held
177
+ // back for the next dispatch (not a failure).
178
+ const fallback = fallbackByTaskId.get(task.task_id);
179
+ if (!fallback) {
180
+ notDispatched.push(task.task_id);
147
181
  continue;
148
182
  }
183
+ process.stderr.write(`[merge-and-ingest] Recovered un-dispatched task '${task.task_id}' from on-disk result file (matched by task_id)\n`);
184
+ obj = fallback;
149
185
  }
150
186
  const record = obj && typeof obj === "object" && !Array.isArray(obj)
151
187
  ? obj
@@ -278,6 +314,12 @@ export async function cmdMergeAndIngest(argv) {
278
314
  // failures stay replayable for retry, and a canary (notDispatched > 0) must NOT
279
315
  // be marked complete or the fan-out merge on the same run-id would short-circuit
280
316
  // to an idempotent replay and silently drop the fan-out results.
317
+ //
318
+ // Selective deepening appends new pending tasks to the SAME run-id; this marker
319
+ // can therefore go stale once those tasks are later dispatched and answered. The
320
+ // replay guard at the top detects that (a pending task with an on-disk result)
321
+ // and re-processes, so a premature marker self-heals instead of stranding the
322
+ // deepening answers behind an idempotent replay (the no-progress loop).
281
323
  if (failing.length === 0 && notDispatched.length === 0) {
282
324
  await writeJsonFile(mergeCompletePath, summaryPayload);
283
325
  }
@@ -1 +1,140 @@
1
+ import type { AnalyzerSetting, GraphEdge } from "@audit-tools/shared";
2
+ import { type ArtifactBundle } from "../io/artifacts.js";
3
+ import type { AuditState } from "../types/auditState.js";
4
+ import { type AdvanceAuditResult } from "../orchestrator/advance.js";
5
+ import { decideNextStep } from "../orchestrator/nextStep.js";
6
+ import type { AnalyzerPlanEntry } from "../extractors/analyzers/types.js";
7
+ /**
8
+ * Read a JSON file from the `incoming/` subdirectory of `artifactsDir`.
9
+ * Returns `{ value, path }` when the file exists and parses successfully.
10
+ * Returns `undefined` when the file is absent (ENOENT-family errors).
11
+ * Re-throws all other IO errors unchanged.
12
+ */
13
+ export declare function tryConsumeIncoming<T>(artifactsDir: string, filename: string): Promise<{
14
+ value: T;
15
+ path: string;
16
+ } | undefined>;
17
+ type NextStepParams = {
18
+ root: string;
19
+ artifactsDir: string;
20
+ selfCliPath: string;
21
+ timeoutMs: number;
22
+ maxRuns: number;
23
+ opentoken?: boolean;
24
+ narrativeEnabled?: boolean;
25
+ analyzers?: Record<string, AnalyzerSetting>;
26
+ graphLlmEdgeReasoning?: boolean;
27
+ since?: string;
28
+ };
29
+ type TerminalStepResult = {
30
+ kind: "complete";
31
+ state: AuditState;
32
+ bundle: ArtifactBundle;
33
+ finalReportPath: string;
34
+ } | {
35
+ kind: "blocked";
36
+ state: AuditState;
37
+ bundle: ArtifactBundle;
38
+ reason: string;
39
+ };
40
+ /**
41
+ * Build the terminal step for a deterministic loop that has stopped advancing
42
+ * (hit the run backstop or the finalization cycle guard). A rendered report is
43
+ * the deliverable: if synthesis already produced one — or the state is formally
44
+ * complete — present it instead of reporting the stopped loop as a bare
45
+ * "blocked" failure. A completed audit must never surface as blocked just
46
+ * because finalization kept churning (e.g. a runtime_validation <-> synthesis
47
+ * ping-pong, or revision churn from filesystem retries) after the report was
48
+ * written. With no report yet, the stop is a genuine block.
49
+ */
50
+ export declare function buildTerminalStep(params: Pick<NextStepParams, "root" | "artifactsDir">, bundle: ArtifactBundle, state: AuditState, blockedReason: string): Promise<TerminalStepResult>;
51
+ type GraphEnrichmentBranchResult = {
52
+ action: "continue";
53
+ } | {
54
+ action: "return";
55
+ result: {
56
+ kind: "analyzer_install";
57
+ state: AuditState;
58
+ bundle: ArtifactBundle;
59
+ unresolved: AnalyzerPlanEntry[];
60
+ };
61
+ } | {
62
+ action: "return";
63
+ result: {
64
+ kind: "edge_reasoning";
65
+ state: AuditState;
66
+ bundle: ArtifactBundle;
67
+ candidates: GraphEdge[];
68
+ };
69
+ } | {
70
+ action: "fallthrough";
71
+ };
72
+ /**
73
+ * Handle the `graph_enrichment_executor` incoming-artifact polling block.
74
+ * Checks for pending analyzer install decisions and edge-reasoning results.
75
+ * Returns an action object:
76
+ * - `continue` → caller should `continue` the for-loop (already consumed an artifact).
77
+ * - `return` → caller should return the embedded result to cmdNextStep.
78
+ * - `fallthrough` → no incoming artifacts; fall through to the deterministic executor.
79
+ */
80
+ export declare function handleGraphEnrichmentBranch(params: Pick<NextStepParams, "root" | "artifactsDir" | "graphLlmEdgeReasoning" | "since" | "opentoken">, bundle: ArtifactBundle, state: AuditState, analyzersRef: {
81
+ value: Record<string, AnalyzerSetting> | undefined;
82
+ }): Promise<GraphEnrichmentBranchResult>;
83
+ type BranchActionResult = {
84
+ action: "continue";
85
+ } | {
86
+ action: "return";
87
+ result: {
88
+ kind: "design_review";
89
+ state: AuditState;
90
+ bundle: ArtifactBundle;
91
+ };
92
+ };
93
+ /**
94
+ * Handle the `design_review` incoming-artifact polling block.
95
+ * Returns `continue` if an incoming findings file was consumed, or `return`
96
+ * with a design_review kind when the host turn is still needed.
97
+ */
98
+ export declare function handleDesignReviewBranch(params: Pick<NextStepParams, "artifactsDir">, bundle: ArtifactBundle, state: AuditState): Promise<BranchActionResult>;
99
+ type SynthesisNarrativeBranchResult = {
100
+ action: "continue";
101
+ } | {
102
+ action: "return";
103
+ result: {
104
+ kind: "synthesis_narrative";
105
+ state: AuditState;
106
+ bundle: ArtifactBundle;
107
+ };
108
+ };
109
+ /**
110
+ * Handle the `synthesis_narrative_executor` incoming-artifact polling block.
111
+ * Returns `continue` if an incoming narrative file was consumed, or `return`
112
+ * with a synthesis_narrative kind when the host turn is still needed (and
113
+ * narrative is enabled), or `continue` when narrative is disabled (so the
114
+ * deterministic omit runs below).
115
+ */
116
+ export declare function handleSynthesisNarrativeBranch(params: Pick<NextStepParams, "root" | "artifactsDir" | "narrativeEnabled" | "opentoken">, bundle: ArtifactBundle, state: AuditState): Promise<SynthesisNarrativeBranchResult>;
117
+ /**
118
+ * Execute one deterministic audit step and record its progress. Throws (with
119
+ * cause) if the executor fails, preserving the existing throw-with-cause pattern.
120
+ */
121
+ export declare function executeAndRecord(params: Pick<NextStepParams, "root" | "artifactsDir" | "graphLlmEdgeReasoning" | "since" | "opentoken" | "maxRuns">, analyzers: Record<string, AnalyzerSetting> | undefined, decision: ReturnType<typeof decideNextStep>, index: number, lastSummary: string): Promise<AdvanceAuditResult>;
122
+ /**
123
+ * Check for a finalization cycle: when iterations outrun distinct artifact
124
+ * states by FINALIZATION_CYCLE_TOLERANCE, the deterministic executors are
125
+ * revisiting states rather than progressing. Returns a terminal-step result
126
+ * when a cycle is detected, or undefined when the run is still progressing.
127
+ */
128
+ export declare function checkFinalizationCycle(ctx: {
129
+ index: number;
130
+ obligationTrail: string[];
131
+ seenStateSignatures: Set<string>;
132
+ tolerance: number;
133
+ params: Pick<NextStepParams, "artifactsDir" | "maxRuns" | "root">;
134
+ bundle: ArtifactBundle;
135
+ state: AuditState;
136
+ result: AdvanceAuditResult;
137
+ selectedObligation: string | null | undefined;
138
+ }): Promise<TerminalStepResult | undefined>;
1
139
  export declare function cmdNextStep(argv: string[]): Promise<void>;
140
+ export {};