auditor-lambda 0.10.3 → 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 (183) 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/nextStepCommand.d.ts +139 -0
  35. package/dist/cli/nextStepCommand.js +281 -232
  36. package/dist/cli/planCommand.d.ts +1 -0
  37. package/dist/cli/planCommand.js +16 -0
  38. package/dist/cli/prepareDispatchCommand.d.ts +1 -0
  39. package/dist/cli/prepareDispatchCommand.js +25 -0
  40. package/dist/cli/quotaCommand.d.ts +1 -0
  41. package/dist/cli/quotaCommand.js +56 -0
  42. package/dist/cli/requeueCommand.d.ts +1 -0
  43. package/dist/cli/requeueCommand.js +10 -0
  44. package/dist/cli/runToCompletion.js +451 -412
  45. package/dist/cli/sampleRunCommand.d.ts +1 -0
  46. package/dist/cli/sampleRunCommand.js +93 -0
  47. package/dist/cli/statusCommand.js +1 -1
  48. package/dist/cli/steps.js +4 -1
  49. package/dist/cli/submitPacketCommand.js +16 -15
  50. package/dist/cli/synthesizeCommand.d.ts +1 -0
  51. package/dist/cli/synthesizeCommand.js +15 -0
  52. package/dist/cli/updateRuntimeValidationCommand.d.ts +1 -0
  53. package/dist/cli/updateRuntimeValidationCommand.js +16 -0
  54. package/dist/cli/validateCommand.d.ts +1 -0
  55. package/dist/cli/validateCommand.js +41 -0
  56. package/dist/cli/validateResultCommand.d.ts +1 -0
  57. package/dist/cli/validateResultCommand.js +63 -0
  58. package/dist/cli/validateResultsCommand.d.ts +1 -0
  59. package/dist/cli/validateResultsCommand.js +31 -0
  60. package/dist/cli/workerRunCommand.d.ts +15 -1
  61. package/dist/cli/workerRunCommand.js +40 -4
  62. package/dist/cli.d.ts +3 -2
  63. package/dist/cli.js +21 -628
  64. package/dist/coverage.js +7 -3
  65. package/dist/extractors/analyzers/css.js +2 -2
  66. package/dist/extractors/analyzers/html.js +2 -2
  67. package/dist/extractors/analyzers/python.js +2 -2
  68. package/dist/extractors/analyzers/registry.js +17 -36
  69. package/dist/extractors/analyzers/treeSitter.d.ts +10 -1
  70. package/dist/extractors/analyzers/treeSitter.js +28 -6
  71. package/dist/extractors/analyzers/typescript.js +104 -85
  72. package/dist/extractors/browserExtension.js +4 -1
  73. package/dist/extractors/designAssessment.js +21 -21
  74. package/dist/extractors/fsIntake.js +34 -10
  75. package/dist/extractors/graph.js +17 -7
  76. package/dist/extractors/graphManifestEdges/cargo.d.ts +4 -0
  77. package/dist/extractors/graphManifestEdges/cargo.js +107 -0
  78. package/dist/extractors/graphManifestEdges/go.d.ts +5 -0
  79. package/dist/extractors/graphManifestEdges/go.js +151 -0
  80. package/dist/extractors/graphManifestEdges/index.d.ts +8 -0
  81. package/dist/extractors/graphManifestEdges/index.js +11 -0
  82. package/dist/extractors/graphManifestEdges/jsonc.d.ts +3 -0
  83. package/dist/extractors/graphManifestEdges/jsonc.js +97 -0
  84. package/dist/extractors/graphManifestEdges/maven.d.ts +3 -0
  85. package/dist/extractors/graphManifestEdges/maven.js +73 -0
  86. package/dist/extractors/graphManifestEdges/packageJson.d.ts +19 -0
  87. package/dist/extractors/graphManifestEdges/packageJson.js +204 -0
  88. package/dist/extractors/graphManifestEdges/pnpm.d.ts +2 -0
  89. package/dist/extractors/graphManifestEdges/pnpm.js +42 -0
  90. package/dist/extractors/graphManifestEdges/pyproject.d.ts +3 -0
  91. package/dist/extractors/graphManifestEdges/pyproject.js +83 -0
  92. package/dist/extractors/graphManifestEdges/toml.d.ts +4 -0
  93. package/dist/extractors/graphManifestEdges/toml.js +68 -0
  94. package/dist/extractors/graphManifestEdges/typescript.d.ts +3 -0
  95. package/dist/extractors/graphManifestEdges/typescript.js +56 -0
  96. package/dist/extractors/graphManifestEdges/workspace.d.ts +10 -0
  97. package/dist/extractors/graphManifestEdges/workspace.js +72 -0
  98. package/dist/extractors/graphManifestEdges/yaml.d.ts +3 -0
  99. package/dist/extractors/graphManifestEdges/yaml.js +59 -0
  100. package/dist/extractors/graphManifestEdges/yamlPaths.d.ts +4 -0
  101. package/dist/extractors/graphManifestEdges/yamlPaths.js +89 -0
  102. package/dist/extractors/graphPythonImports.js +4 -20
  103. package/dist/extractors/pathPatterns.js +3 -13
  104. package/dist/io/artifacts.d.ts +1 -1
  105. package/dist/io/artifacts.js +4 -1
  106. package/dist/io/runArtifacts.d.ts +8 -2
  107. package/dist/io/runArtifacts.js +103 -69
  108. package/dist/io/toolingManifest.js +2 -1
  109. package/dist/orchestrator/advance.js +36 -0
  110. package/dist/orchestrator/artifactFreshness.d.ts +1 -1
  111. package/dist/orchestrator/artifactFreshness.js +1 -1
  112. package/dist/orchestrator/artifactMetadata.js +5 -5
  113. package/dist/orchestrator/auditTaskUtils.d.ts +4 -0
  114. package/dist/orchestrator/auditTaskUtils.js +8 -12
  115. package/dist/orchestrator/autoFixExecutor.js +40 -26
  116. package/dist/orchestrator/dependencyMap.js +1 -1
  117. package/dist/orchestrator/executorResult.d.ts +33 -0
  118. package/dist/orchestrator/executors.d.ts +7 -0
  119. package/dist/orchestrator/executors.js +24 -0
  120. package/dist/orchestrator/fileAnchors.js +42 -29
  121. package/dist/orchestrator/fileIntegrity.js +6 -1
  122. package/dist/orchestrator/flowCoverage.js +1 -2
  123. package/dist/orchestrator/flowPlanning.js +8 -4
  124. package/dist/orchestrator/graphEnrichmentExecutor.js +67 -45
  125. package/dist/orchestrator/ingestionExecutors.js +9 -1
  126. package/dist/orchestrator/intakeExecutors.d.ts +0 -4
  127. package/dist/orchestrator/intakeExecutors.js +24 -14
  128. package/dist/orchestrator/localCommands.d.ts +1 -0
  129. package/dist/orchestrator/localCommands.js +10 -17
  130. package/dist/orchestrator/nextStep.js +3 -1
  131. package/dist/orchestrator/requeueCommand.js +4 -0
  132. package/dist/orchestrator/reviewPacketGraph.js +50 -18
  133. package/dist/orchestrator/reviewPackets.js +10 -8
  134. package/dist/orchestrator/runtimeCommand.js +35 -7
  135. package/dist/orchestrator/runtimeValidationUpdate.js +6 -0
  136. package/dist/orchestrator/selectiveDeepening/highRiskClean.js +3 -2
  137. package/dist/orchestrator/selectiveDeepening/lensVerification.js +44 -18
  138. package/dist/orchestrator/staleness.js +3 -3
  139. package/dist/orchestrator/state.js +1 -1
  140. package/dist/orchestrator/syntaxResolutionExecutor.js +17 -24
  141. package/dist/orchestrator/synthesisExecutors.js +1 -0
  142. package/dist/orchestrator/taskBuilder.js +5 -4
  143. package/dist/providers/claudeCodeProvider.js +4 -1
  144. package/dist/providers/opencodeProvider.js +4 -1
  145. package/dist/quota/discoveredLimits.js +3 -3
  146. package/dist/quota/headerExtraction.js +5 -2
  147. package/dist/quota/headerExtractors/claudeCodeHeaderExtractor.js +3 -0
  148. package/dist/quota/headerExtractors/index.js +3 -3
  149. package/dist/quota/index.d.ts +3 -1
  150. package/dist/quota/index.js +3 -0
  151. package/dist/reporting/findingRanks.d.ts +3 -0
  152. package/dist/reporting/findingRanks.js +24 -0
  153. package/dist/reporting/mergeFindings.js +1 -24
  154. package/dist/reporting/synthesis.d.ts +3 -1
  155. package/dist/reporting/synthesis.js +30 -6
  156. package/dist/reporting/synthesisNarrativePrompt.js +3 -0
  157. package/dist/reporting/workBlocks.js +1 -14
  158. package/dist/supervisor/operatorHandoff.js +2 -6
  159. package/dist/supervisor/runLedger.js +30 -41
  160. package/dist/types/activeDispatch.d.ts +31 -0
  161. package/dist/types/activeDispatch.js +2 -0
  162. package/dist/types.d.ts +21 -4
  163. package/dist/types.js +24 -16
  164. package/dist/validation/artifacts.js +3 -0
  165. package/dist/validation/auditResults.js +8 -2
  166. package/package.json +2 -2
  167. package/schemas/audit_findings.schema.json +5 -1
  168. package/schemas/audit_plan_metrics.schema.json +1 -1
  169. package/schemas/audit_result.schema.json +5 -6
  170. package/schemas/audit_task.schema.json +1 -4
  171. package/schemas/blind_spot_register.schema.json +1 -1
  172. package/schemas/coverage_matrix.schema.json +2 -8
  173. package/schemas/finding.schema.json +1 -16
  174. package/schemas/flow_coverage.schema.json +2 -8
  175. package/schemas/graph_bundle.schema.json +31 -0
  176. package/schemas/lens.schema.json +7 -0
  177. package/schemas/review_packets.schema.json +6 -17
  178. package/schemas/step_contract.schema.json +8 -2
  179. package/schemas/unit_manifest.schema.json +1 -4
  180. package/scripts/postinstall.mjs +3 -1
  181. package/skills/audit-code/audit-code.prompt.md +2 -3
  182. package/dist/extractors/graphManifestEdges.d.ts +0 -12
  183. package/dist/extractors/graphManifestEdges.js +0 -1135
@@ -0,0 +1,89 @@
1
+ import { posix } from "node:path";
2
+ import { graphEdge, normalizeGraphPath, resolveCandidate } from "../graphPathUtils.js";
3
+ import { stripYamlComment, unquoteYamlScalar } from "./yaml.js";
4
+ export const YAML_PATH_REFERENCE_LINK_CONFIDENCE = 0.8;
5
+ export const YAML_CONFIG_EXTENSIONS = [".yaml", ".yml", ".json", ".toml"];
6
+ function isYamlSourcePath(path) {
7
+ const lower = normalizeGraphPath(path).toLowerCase();
8
+ return lower.endsWith(".yaml") || lower.endsWith(".yml");
9
+ }
10
+ function looksLikeConfigFilePath(value) {
11
+ if (!value.includes("/"))
12
+ return false;
13
+ if (/^[a-z][a-z0-9+.-]*:/i.test(value))
14
+ return false;
15
+ if (value.startsWith("/"))
16
+ return false;
17
+ const lower = value.toLowerCase();
18
+ return YAML_CONFIG_EXTENSIONS.some((ext) => lower.endsWith(ext));
19
+ }
20
+ function extractYamlScalarValues(content) {
21
+ const values = [];
22
+ for (const rawLine of content.split(/\r?\n/)) {
23
+ const withoutComment = stripYamlComment(rawLine);
24
+ const trimmed = withoutComment.trim();
25
+ if (trimmed.length === 0)
26
+ continue;
27
+ let rawValue;
28
+ // key: value
29
+ const keyValueMatch = /^[^:[\]{}]+:\s+(.+)$/.exec(trimmed);
30
+ if (keyValueMatch?.[1]) {
31
+ rawValue = keyValueMatch[1].trim();
32
+ }
33
+ else {
34
+ // - value (list item)
35
+ const listItemMatch = /^-\s+(.+)$/.exec(trimmed);
36
+ if (listItemMatch?.[1]) {
37
+ rawValue = listItemMatch[1].trim();
38
+ }
39
+ }
40
+ if (!rawValue)
41
+ continue;
42
+ const value = unquoteYamlScalar(rawValue);
43
+ if (looksLikeConfigFilePath(value)) {
44
+ values.push(value);
45
+ }
46
+ }
47
+ return values;
48
+ }
49
+ function resolveYamlPathReference(fromPath, specifier, pathLookup) {
50
+ const normalized = normalizeGraphPath(specifier.replace(/^\.\//, ""));
51
+ if (normalized.length === 0)
52
+ return undefined;
53
+ // Try as repo-root-relative first (many YAML configs use repo-root paths)
54
+ const repoRootTarget = resolveCandidate(normalized, pathLookup);
55
+ if (repoRootTarget)
56
+ return repoRootTarget;
57
+ // Fallback: relative to the YAML file's directory
58
+ const fromDir = posix.dirname(normalizeGraphPath(fromPath));
59
+ if (fromDir !== ".") {
60
+ const dirRelative = posix.join(fromDir, normalized);
61
+ const dirTarget = resolveCandidate(dirRelative, pathLookup);
62
+ if (dirTarget)
63
+ return dirTarget;
64
+ }
65
+ return undefined;
66
+ }
67
+ export function extractYamlPathReferenceEdges(fromPath, content, pathLookup) {
68
+ if (!isYamlSourcePath(fromPath))
69
+ return [];
70
+ const values = extractYamlScalarValues(content);
71
+ if (values.length === 0)
72
+ return [];
73
+ const edges = [];
74
+ const seen = new Set();
75
+ for (const value of values) {
76
+ const target = resolveYamlPathReference(fromPath, value, pathLookup);
77
+ if (!target || target === fromPath || seen.has(target))
78
+ continue;
79
+ seen.add(target);
80
+ edges.push(graphEdge({
81
+ from: fromPath,
82
+ to: target,
83
+ kind: "yaml-path-reference-link",
84
+ confidence: YAML_PATH_REFERENCE_LINK_CONFIDENCE,
85
+ reason: `YAML file references path '${value}'.`,
86
+ }));
87
+ }
88
+ return edges;
89
+ }
@@ -83,7 +83,7 @@ function pythonLogicalLines(content) {
83
83
  const continued = stripped.endsWith("\\");
84
84
  const line = continued ? stripped.slice(0, -1).trimEnd() : stripped;
85
85
  pending = pending.length > 0 ? `${pending} ${line}` : line;
86
- parenDepth += pythonParenDelta(line);
86
+ parenDepth = Math.max(0, parenDepth + pythonParenDelta(line));
87
87
  if (!continued && parenDepth <= 0) {
88
88
  logicalLines.push(pending.replace(/\s+/g, " ").trim());
89
89
  pending = "";
@@ -337,26 +337,10 @@ export function extractPythonImportEdges(fromPath, content, pathLookup) {
337
337
  continue;
338
338
  }
339
339
  const moduleSpecifier = fromImportMatch[1] ?? "";
340
- if (!isPythonModuleSpecifier(moduleSpecifier)) {
341
- continue;
342
- }
343
- const importedNames = splitPythonImportList(fromImportMatch[2] ?? "")
344
- .map(stripPythonAlias)
345
- .filter((name) => name !== "*" && isPythonIdentifier(name));
346
- const submoduleTargets = importedNames
347
- .map((name) => appendPythonImportedSpecifier(moduleSpecifier, name))
348
- .map((specifier) => ({
349
- specifier,
350
- target: resolvePythonModuleSpecifier(fromPath, specifier, pathLookup),
351
- }))
352
- .filter((item) => item.target);
353
- if (submoduleTargets.length > 0) {
354
- for (const { specifier, target } of submoduleTargets) {
355
- addPythonImportEdge(edges, fromPath, target, "python-from-import", specifier);
356
- }
357
- continue;
340
+ const rawNames = splitPythonImportList(fromImportMatch[2] ?? "").map(stripPythonAlias);
341
+ for (const { specifier, target } of resolvePythonFromImportTargets(fromPath, moduleSpecifier, rawNames, pathLookup)) {
342
+ addPythonImportEdge(edges, fromPath, target, "python-from-import", specifier);
358
343
  }
359
- addPythonImportEdge(edges, fromPath, resolvePythonModuleSpecifier(fromPath, moduleSpecifier, pathLookup), "python-from-import", moduleSpecifier);
360
344
  }
361
345
  return edges;
362
346
  }
@@ -204,7 +204,7 @@ export function isGeneratedTestArtifactPath(normalized) {
204
204
  return splitSegments(normalized).some((segment) => segment.startsWith(".test-") && segment.endsWith("-artifacts"));
205
205
  }
206
206
  export function isAuditArtifactPath(normalized) {
207
- return splitSegments(normalized).some((segment) => segment.startsWith(".audit-artifacts"));
207
+ return hasSegment(normalized, ".audit-artifacts");
208
208
  }
209
209
  export function isTestPath(normalized) {
210
210
  const segments = splitSegments(normalized);
@@ -259,20 +259,10 @@ export function isSurfacePath(normalized) {
259
259
  hasToken(normalized, ["cli"]));
260
260
  }
261
261
  export function isBackgroundSurfacePath(normalized) {
262
- return hasToken(normalized, ["worker", "workers", "job", "jobs"]);
262
+ return hasToken(normalized, CONCURRENCY_KEYWORDS);
263
263
  }
264
264
  export function isNetworkSurfacePath(normalized) {
265
- return (hasSegment(normalized, "api") ||
266
- hasToken(normalized, [
267
- "route",
268
- "routes",
269
- "controller",
270
- "controllers",
271
- "handler",
272
- "handlers",
273
- "endpoint",
274
- "endpoints",
275
- ]));
265
+ return hasSegment(normalized, "api") || hasToken(normalized, INTERFACE_KEYWORDS);
276
266
  }
277
267
  export function isBillingPath(normalized) {
278
268
  return hasToken(normalized, BILLING_KEYWORDS);
@@ -12,7 +12,7 @@ import type { DesignAssessment } from "../types/designAssessment.js";
12
12
  import type { AnalyzerCapabilityRecord } from "../types/analyzerCapability.js";
13
13
  import type { AuditScopeManifest } from "../types/auditScope.js";
14
14
  import type { ToolingManifest } from "../types/toolingManifest.js";
15
- import type { ActiveDispatchState } from "../cli/dispatch.js";
15
+ import type { ActiveDispatchState } from "../types/activeDispatch.js";
16
16
  type ArtifactPayloadMap = {
17
17
  repo_manifest: RepoManifest;
18
18
  file_disposition: FileDisposition;
@@ -149,8 +149,11 @@ export async function promoteFinalAuditReport(params, options = {}) {
149
149
  try {
150
150
  await copy(join(params.artifactsDir, "audit-findings.json"), join(params.repoRoot, "audit-findings.json"), { force: true });
151
151
  }
152
- catch {
152
+ catch (error) {
153
153
  // audit-findings.json is optional output; absence must not fail promotion.
154
+ // Log so operators can distinguish a partial promotion from a clean one.
155
+ warn(`audit-code: could not promote audit-findings.json to ${join(params.repoRoot, "audit-findings.json")}: ` +
156
+ (error instanceof Error ? error.message : String(error)));
154
157
  }
155
158
  try {
156
159
  await remove(params.artifactsDir, { recursive: true, force: true });
@@ -21,6 +21,12 @@ export declare function getRunPaths(artifactsDir: string, runId: string): RunPat
21
21
  export declare function ensureSupervisorDirs(artifactsDir: string): Promise<void>;
22
22
  export declare function writeWorkerTaskFiles(task: WorkerTask, prompt: string, paths: RunPaths, artifactsDir: string, currentTasks?: AuditTask[], options?: {
23
23
  updateDispatch?: boolean;
24
+ }, log?: {
25
+ event: (name: string, data: Record<string, unknown>) => void;
26
+ }): Promise<void>;
27
+ export declare function writeDispatchBatchFiles(artifactsDir: string, runs: DispatchBatchRun[], currentTasks: AuditTask[], log?: {
28
+ event: (name: string, data: Record<string, unknown>) => void;
29
+ }): Promise<void>;
30
+ export declare function clearDispatchFiles(artifactsDir: string, log?: {
31
+ event: (name: string, data: Record<string, unknown>) => void;
24
32
  }): Promise<void>;
25
- export declare function writeDispatchBatchFiles(artifactsDir: string, runs: DispatchBatchRun[], currentTasks: AuditTask[]): Promise<void>;
26
- export declare function clearDispatchFiles(artifactsDir: string): Promise<void>;
@@ -19,16 +19,19 @@ export const PACKET_SCHEMA_FILENAMES = [
19
19
  "finding.schema.json",
20
20
  "audit_task.schema.json",
21
21
  ];
22
+ async function copySchemaFiles(targetDir, entries) {
23
+ await mkdir(targetDir, { recursive: true });
24
+ for (const entry of entries) {
25
+ await writeFile(join(targetDir, entry.name), await readFile(entry.srcPath, "utf8"), "utf8");
26
+ }
27
+ }
22
28
  /**
23
29
  * Copy {@link PACKET_SCHEMA_FILENAMES} into `targetDir` under their canonical
24
30
  * filenames, making the AuditResult schema reachable from a dispatch run's
25
31
  * `task-results/` directory.
26
32
  */
27
33
  export async function writePacketSchemaFiles(targetDir, pkgRoot) {
28
- await mkdir(targetDir, { recursive: true });
29
- for (const name of PACKET_SCHEMA_FILENAMES) {
30
- await writeFile(join(targetDir, name), await readFile(join(pkgRoot, "schemas", name), "utf8"), "utf8");
31
- }
34
+ await copySchemaFiles(targetDir, PACKET_SCHEMA_FILENAMES.map(name => ({ srcPath: join(pkgRoot, "schemas", name), name })));
32
35
  }
33
36
  const CURRENT_TASK_FILENAME = "current-task.json";
34
37
  const CURRENT_PROMPT_FILENAME = "current-prompt.md";
@@ -87,10 +90,11 @@ async function writeDispatchSchemaFiles(artifactsDir) {
87
90
  // Ensure the dispatch dir exists: this is now written before the pointer
88
91
  // files (which formerly created it), and parallel-slot dispatch may reach
89
92
  // here before the canonical dispatch has run.
90
- await mkdir(dispatchDir, { recursive: true });
91
- await writeFile(join(dispatchDir, CURRENT_SCHEMA_FILENAME), await readFile(auditResultSchemaPath, "utf8"), "utf8");
92
- await writeFile(join(dispatchDir, CURRENT_RESULTS_SCHEMA_FILENAME), await readFile(auditResultsSchemaPath, "utf8"), "utf8");
93
- await writeFile(join(dispatchDir, CURRENT_FINDING_SCHEMA_FILENAME), await readFile(findingSchemaPath, "utf8"), "utf8");
93
+ await copySchemaFiles(dispatchDir, [
94
+ { srcPath: auditResultSchemaPath, name: CURRENT_SCHEMA_FILENAME },
95
+ { srcPath: auditResultsSchemaPath, name: CURRENT_RESULTS_SCHEMA_FILENAME },
96
+ { srcPath: findingSchemaPath, name: CURRENT_FINDING_SCHEMA_FILENAME },
97
+ ]);
94
98
  }
95
99
  function renderSingleTaskFallbackPrompt(task, auditTask) {
96
100
  const commandArgv = JSON.stringify(task.worker_command);
@@ -135,68 +139,88 @@ async function writeSingleTaskFallbackFiles(artifactsDir, task, currentTasks) {
135
139
  await writeJsonFile(join(artifactsDir, "dispatch", CURRENT_SINGLE_TASK_FILENAME), firstTask);
136
140
  await writeFile(join(artifactsDir, "dispatch", CURRENT_SINGLE_TASK_PROMPT_FILENAME), renderSingleTaskFallbackPrompt(task, firstTask), "utf8");
137
141
  }
138
- export async function writeWorkerTaskFiles(task, prompt, paths, artifactsDir, currentTasks, options = {}) {
139
- await mkdir(paths.runDir, { recursive: true });
140
- await writeJsonFile(paths.taskPath, task);
141
- await writeFile(paths.promptPath, prompt, "utf8");
142
- await writeJsonFile(paths.statusPath, {
143
- run_id: task.run_id,
144
- status: "dispatched",
145
- });
146
- // The result schema files are always required by the worker, regardless of
147
- // whether this run owns the shared "current dispatch" pointer files.
148
- await writeDispatchSchemaFiles(artifactsDir);
149
- // Parallel-slot dispatch passes updateDispatch:false so each slot does NOT
150
- // clobber the shared current-task / current-prompt / current-tasks pointers
151
- // (only the single canonical dispatch should own them). The default path
152
- // (updateDispatch unset/true) refreshes those pointers and the single-task
153
- // fallback.
154
- const updateDispatch = options.updateDispatch !== false;
155
- if (!updateDispatch) {
156
- return;
142
+ export async function writeWorkerTaskFiles(task, prompt, paths, artifactsDir, currentTasks, options = {}, log) {
143
+ try {
144
+ await mkdir(paths.runDir, { recursive: true });
145
+ await writeJsonFile(paths.taskPath, task);
146
+ await writeFile(paths.promptPath, prompt, "utf8");
147
+ await writeJsonFile(paths.statusPath, {
148
+ run_id: task.run_id,
149
+ status: "dispatched",
150
+ });
151
+ // The result schema files are always required by the worker, regardless of
152
+ // whether this run owns the shared "current dispatch" pointer files.
153
+ await writeDispatchSchemaFiles(artifactsDir);
154
+ // Parallel-slot dispatch passes updateDispatch:false so each slot does NOT
155
+ // clobber the shared current-task / current-prompt / current-tasks pointers
156
+ // (only the single canonical dispatch should own them). The default path
157
+ // (updateDispatch unset/true) refreshes those pointers and the single-task
158
+ // fallback.
159
+ const updateDispatch = options.updateDispatch !== false;
160
+ if (!updateDispatch) {
161
+ return;
162
+ }
163
+ await writeJsonFile(join(artifactsDir, "dispatch", CURRENT_TASK_FILENAME), task);
164
+ await writeFile(join(artifactsDir, "dispatch", CURRENT_PROMPT_FILENAME), prompt, "utf8");
165
+ await writeJsonFile(join(artifactsDir, "dispatch", CURRENT_TASKS_FILENAME), currentTasks ?? []);
166
+ await writeSingleTaskFallbackFiles(artifactsDir, task, currentTasks);
167
+ }
168
+ catch (err) {
169
+ log?.event("dispatch_io_error", {
170
+ run_id: task.run_id ?? null,
171
+ function: "writeWorkerTaskFiles",
172
+ error: String(err),
173
+ });
174
+ throw err;
157
175
  }
158
- await writeJsonFile(join(artifactsDir, "dispatch", CURRENT_TASK_FILENAME), task);
159
- await writeFile(join(artifactsDir, "dispatch", CURRENT_PROMPT_FILENAME), prompt, "utf8");
160
- await writeJsonFile(join(artifactsDir, "dispatch", CURRENT_TASKS_FILENAME), currentTasks ?? []);
161
- await writeSingleTaskFallbackFiles(artifactsDir, task, currentTasks);
162
176
  }
163
- export async function writeDispatchBatchFiles(artifactsDir, runs, currentTasks) {
164
- const summary = {
165
- contract_version: "audit-code-dispatch/v1alpha1",
166
- mode: "parallel-batch",
167
- run_count: runs.length,
168
- current_tasks_path: join(artifactsDir, "dispatch", CURRENT_TASKS_FILENAME),
169
- runs,
170
- };
171
- const promptLines = [
172
- "# audit-code parallel dispatch",
173
- "",
174
- `This batch launched ${runs.length} deferred review run(s).`,
175
- "Each run keeps its own task.json, prompt.md, result.json, and status.json under .audit-artifacts/runs/<run_id>/.",
176
- "Use current-tasks.json for the combined task list. The per-run files below are operational references for launched workers; do not read per-run prompt or schema files unless debugging a failed dispatch.",
177
- "",
178
- "Runs:",
179
- ...runs.flatMap((run) => [
180
- `- ${run.run_id}`,
181
- ` task: ${run.task_path}`,
182
- ` prompt (worker-owned; do not read during normal orchestration): ${run.prompt_path}`,
183
- ` result: ${run.result_path}`,
184
- ` status: ${run.status_path}`,
185
- ...(run.audit_results_path
186
- ? [` audit results: ${run.audit_results_path}`]
187
- : []),
188
- ...(run.pending_audit_tasks_path
189
- ? [` pending tasks: ${run.pending_audit_tasks_path}`]
190
- : []),
191
- ]),
192
- "",
193
- ];
194
- await writeJsonFile(join(artifactsDir, "dispatch", CURRENT_TASK_FILENAME), summary);
195
- await writeFile(join(artifactsDir, "dispatch", CURRENT_PROMPT_FILENAME), promptLines.join("\n"), "utf8");
196
- await writeJsonFile(join(artifactsDir, "dispatch", CURRENT_TASKS_FILENAME), currentTasks);
197
- await writeDispatchSchemaFiles(artifactsDir);
177
+ export async function writeDispatchBatchFiles(artifactsDir, runs, currentTasks, log) {
178
+ try {
179
+ const summary = {
180
+ contract_version: "audit-code-dispatch/v1alpha1",
181
+ mode: "parallel-batch",
182
+ run_count: runs.length,
183
+ current_tasks_path: join(artifactsDir, "dispatch", CURRENT_TASKS_FILENAME),
184
+ runs,
185
+ };
186
+ const promptLines = [
187
+ "# audit-code parallel dispatch",
188
+ "",
189
+ `This batch launched ${runs.length} deferred review run(s).`,
190
+ "Each run keeps its own task.json, prompt.md, result.json, and status.json under .audit-artifacts/runs/<run_id>/.",
191
+ "Use current-tasks.json for the combined task list. The per-run files below are operational references for launched workers; do not read per-run prompt or schema files unless debugging a failed dispatch.",
192
+ "",
193
+ "Runs:",
194
+ ...runs.flatMap((run) => [
195
+ `- ${run.run_id}`,
196
+ ` task: ${run.task_path}`,
197
+ ` prompt (worker-owned; do not read during normal orchestration): ${run.prompt_path}`,
198
+ ` result: ${run.result_path}`,
199
+ ` status: ${run.status_path}`,
200
+ ...(run.audit_results_path
201
+ ? [` audit results: ${run.audit_results_path}`]
202
+ : []),
203
+ ...(run.pending_audit_tasks_path
204
+ ? [` pending tasks: ${run.pending_audit_tasks_path}`]
205
+ : []),
206
+ ]),
207
+ "",
208
+ ];
209
+ await writeJsonFile(join(artifactsDir, "dispatch", CURRENT_TASK_FILENAME), summary);
210
+ await writeFile(join(artifactsDir, "dispatch", CURRENT_PROMPT_FILENAME), promptLines.join("\n"), "utf8");
211
+ await writeJsonFile(join(artifactsDir, "dispatch", CURRENT_TASKS_FILENAME), currentTasks);
212
+ await writeDispatchSchemaFiles(artifactsDir);
213
+ }
214
+ catch (err) {
215
+ log?.event("dispatch_io_error", {
216
+ run_id: "batch",
217
+ function: "writeDispatchBatchFiles",
218
+ error: String(err),
219
+ });
220
+ throw err;
221
+ }
198
222
  }
199
- export async function clearDispatchFiles(artifactsDir) {
223
+ export async function clearDispatchFiles(artifactsDir, log) {
200
224
  const targets = [
201
225
  CURRENT_TASK_FILENAME,
202
226
  CURRENT_PROMPT_FILENAME,
@@ -207,7 +231,17 @@ export async function clearDispatchFiles(artifactsDir) {
207
231
  CURRENT_RESULTS_SCHEMA_FILENAME,
208
232
  CURRENT_FINDING_SCHEMA_FILENAME,
209
233
  ];
210
- for (const name of targets) {
211
- await rm(join(artifactsDir, "dispatch", name), { force: true });
234
+ try {
235
+ for (const name of targets) {
236
+ await rm(join(artifactsDir, "dispatch", name), { force: true });
237
+ }
238
+ }
239
+ catch (err) {
240
+ log?.event("dispatch_io_error", {
241
+ run_id: "clear",
242
+ function: "clearDispatchFiles",
243
+ error: String(err),
244
+ });
245
+ throw err;
212
246
  }
213
247
  }
@@ -44,7 +44,8 @@ async function readPackageVersion() {
44
44
  const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
45
45
  return typeof packageJson.version === "string" ? packageJson.version : null;
46
46
  }
47
- catch {
47
+ catch (error) {
48
+ process.stderr.write(`[audit-code] readPackageVersion: failed to read/parse ${packageJsonPath}: ${error instanceof Error ? error.message : String(error)}\n`);
48
49
  return null;
49
50
  }
50
51
  }
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  import { decideNextStep, findObligation } from "./nextStep.js";
2
3
  import { deriveAuditState } from "./state.js";
3
4
  import { computeArtifactMetadata } from "./artifactMetadata.js";
@@ -36,8 +37,12 @@ function formatExecutorFailure(selectedExecutor, selectedObligation, error) {
36
37
  cause: error instanceof Error ? error : undefined,
37
38
  });
38
39
  }
40
+ function createCorrelationId() {
41
+ return randomUUID().replace(/-/g, "").slice(0, 8);
42
+ }
39
43
  export async function advanceAudit(bundle, options = {}) {
40
44
  const log = options.runLogger ?? RunLogger.disabled();
45
+ const correlationId = createCorrelationId();
41
46
  const decision = decideNextStep(bundle);
42
47
  const forcedExecutor = options.preferredExecutor ?? null;
43
48
  const selectedExecutor = forcedExecutor ?? decision.selected_executor;
@@ -47,6 +52,7 @@ export async function advanceAudit(bundle, options = {}) {
47
52
  log.event({
48
53
  phase: "advance",
49
54
  kind: "obligation",
55
+ correlationId,
50
56
  obligation: selectedObligation ?? undefined,
51
57
  note: decision.reason,
52
58
  });
@@ -74,6 +80,7 @@ export async function advanceAudit(bundle, options = {}) {
74
80
  log.event({
75
81
  phase: "advance",
76
82
  kind: "executor_start",
83
+ correlationId,
77
84
  obligation: selectedObligation ?? undefined,
78
85
  note: selectedExecutor,
79
86
  });
@@ -85,6 +92,7 @@ export async function advanceAudit(bundle, options = {}) {
85
92
  break;
86
93
  }
87
94
  case "structure_executor":
95
+ // root is intentionally optional: present → buildGraphBundleFromFs, absent → manifest-only buildGraphBundle
88
96
  run = await runStructureExecutor(bundle, options.root);
89
97
  break;
90
98
  case "graph_enrichment_executor":
@@ -147,7 +155,32 @@ export async function advanceAudit(bundle, options = {}) {
147
155
  run = runSyntaxResolutionExecutor(bundle, root);
148
156
  break;
149
157
  }
158
+ // `agent` is a host-delegation executor: its review tasks are dispatched
159
+ // to the active LLM agent (or a worker) and ingested via
160
+ // result_ingestion_executor — advanceAudit cannot complete them
161
+ // deterministically. Callers (next-step / run-to-completion) route it
162
+ // through host delegation before reaching here; if it is dispatched into
163
+ // advanceAudit directly it falls through to the default branch, which
164
+ // returns a no-progress "selected but not yet dispatched" handoff rather
165
+ // than throwing. An explicit case keeps the registry⇄switch invariant
166
+ // (executor-registry-sync) honest about agent being handled here.
167
+ case "agent":
150
168
  default: {
169
+ log.event({
170
+ phase: "advance",
171
+ kind: "error",
172
+ correlationId,
173
+ obligation: selectedObligation ?? undefined,
174
+ note: `Unrecognized executor: ${selectedExecutor}`,
175
+ });
176
+ log.event({
177
+ phase: "advance",
178
+ kind: "executor_end",
179
+ correlationId,
180
+ obligation: selectedObligation ?? undefined,
181
+ note: selectedExecutor,
182
+ duration_ms: Date.now() - executorStartedAt,
183
+ });
151
184
  const state = deriveAuditState(bundle);
152
185
  state.last_executor = selectedExecutor;
153
186
  state.last_obligation = selectedObligation ?? undefined;
@@ -170,6 +203,7 @@ export async function advanceAudit(bundle, options = {}) {
170
203
  log.event({
171
204
  phase: "advance",
172
205
  kind: "executor_end",
206
+ correlationId,
173
207
  obligation: selectedObligation ?? undefined,
174
208
  note: selectedExecutor,
175
209
  duration_ms: Date.now() - executorStartedAt,
@@ -178,6 +212,7 @@ export async function advanceAudit(bundle, options = {}) {
178
212
  log.event({
179
213
  phase: "advance",
180
214
  kind: "scope",
215
+ correlationId,
181
216
  obligation: selectedObligation ?? undefined,
182
217
  note: plannedScope.mode === "delta"
183
218
  ? `delta since ${plannedScope.since}: ${plannedScope.seed_files.length} changed + ${plannedScope.expanded_files.length} neighbors; full audit advised before release`
@@ -188,6 +223,7 @@ export async function advanceAudit(bundle, options = {}) {
188
223
  log.event({
189
224
  phase: "advance",
190
225
  kind: "artifact_write",
226
+ correlationId,
191
227
  obligation: selectedObligation ?? undefined,
192
228
  artifact,
193
229
  });
@@ -1,4 +1,4 @@
1
1
  export declare function stableStringify(value: unknown): string;
2
2
  export declare function normalizeForMetadataHash(artifactName: string, value: unknown): unknown;
3
3
  export declare function hashArtifactValue(artifactName: string, value: unknown): string;
4
- export declare function buildReverseDependencyMap(): Record<string, string[]>;
4
+ export declare function buildArtifactDependenciesMap(): Record<string, string[]>;
@@ -42,7 +42,7 @@ export function hashArtifactValue(artifactName, value) {
42
42
  .update(stableStringify(normalizeForMetadataHash(artifactName, value)))
43
43
  .digest("hex");
44
44
  }
45
- export function buildReverseDependencyMap() {
45
+ export function buildArtifactDependenciesMap() {
46
46
  const reverse = {};
47
47
  for (const [upstream, downstreamList] of Object.entries(ARTIFACT_DEPENDENTS_MAP)) {
48
48
  reverse[upstream] ??= [];
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { getArtifactValue } from "../io/artifacts.js";
3
- import { buildReverseDependencyMap, hashArtifactValue, stableStringify, } from "./artifactFreshness.js";
4
- const REVERSE_DEPENDENCY_MAP = buildReverseDependencyMap();
3
+ import { buildArtifactDependenciesMap, hashArtifactValue, stableStringify, } from "./artifactFreshness.js";
4
+ const ARTIFACT_DEPENDENCIES_MAP = buildArtifactDependenciesMap();
5
5
  function computeDependencyFirstOrder(artifactNames) {
6
6
  const target = new Set(artifactNames);
7
7
  const ordered = [];
@@ -13,7 +13,7 @@ function computeDependencyFirstOrder(artifactNames) {
13
13
  if (temporary.has(artifactName))
14
14
  return;
15
15
  temporary.add(artifactName);
16
- const dependencies = (REVERSE_DEPENDENCY_MAP[artifactName] ?? [])
16
+ const dependencies = (ARTIFACT_DEPENDENCIES_MAP[artifactName] ?? [])
17
17
  .filter((dependencyName) => target.has(dependencyName))
18
18
  .sort();
19
19
  for (const dependencyName of dependencies) {
@@ -49,7 +49,7 @@ export function computeArtifactStateSignature(bundle) {
49
49
  export function computeArtifactMetadata(bundle, previous, updatedArtifacts = []) {
50
50
  const artifacts = {};
51
51
  const updated = new Set(updatedArtifacts);
52
- const presentArtifacts = Object.keys(REVERSE_DEPENDENCY_MAP).filter((artifactName) => artifactName !== "artifact_metadata.json" &&
52
+ const presentArtifacts = Object.keys(ARTIFACT_DEPENDENCIES_MAP).filter((artifactName) => artifactName !== "artifact_metadata.json" &&
53
53
  present(bundle, artifactName));
54
54
  const orderedArtifacts = computeDependencyFirstOrder(presentArtifacts);
55
55
  for (const artifactName of orderedArtifacts) {
@@ -64,7 +64,7 @@ export function computeArtifactMetadata(bundle, previous, updatedArtifacts = [])
64
64
  continue;
65
65
  }
66
66
  const contentHash = hashArtifactValue(artifactName, value);
67
- const dependencyRevisions = Object.fromEntries((REVERSE_DEPENDENCY_MAP[artifactName] ?? [])
67
+ const dependencyRevisions = Object.fromEntries((ARTIFACT_DEPENDENCIES_MAP[artifactName] ?? [])
68
68
  .filter((dependencyName) => dependencyName !== "artifact_metadata.json")
69
69
  .sort()
70
70
  .map((dependencyName) => [
@@ -1,4 +1,8 @@
1
1
  import type { AuditTask, Lens } from "../types.js";
2
+ /** Lens ordering for task prioritization, derived from {@link LENS_REGISTRY}
3
+ * (sorted ascending by `order_weight`). Deriving this from the registry ensures
4
+ * every lens — including `architecture`, which was previously absent from the
5
+ * hardcoded array — is automatically included when added to the registry. */
2
6
  export declare const LENS_ORDER: Lens[];
3
7
  export declare function priorityRank(priority: AuditTask["priority"]): number;
4
8
  export declare function sortLenses(lenses: Iterable<Lens>): Lens[];
@@ -1,15 +1,11 @@
1
- export const LENS_ORDER = [
2
- "security",
3
- "correctness",
4
- "reliability",
5
- "data_integrity",
6
- "performance",
7
- "operability",
8
- "config_deployment",
9
- "observability",
10
- "maintainability",
11
- "tests",
12
- ];
1
+ import { LENS_REGISTRY } from "../types.js";
2
+ /** Lens ordering for task prioritization, derived from {@link LENS_REGISTRY}
3
+ * (sorted ascending by `order_weight`). Deriving this from the registry ensures
4
+ * every lens — including `architecture`, which was previously absent from the
5
+ * hardcoded array — is automatically included when added to the registry. */
6
+ export const LENS_ORDER = [...LENS_REGISTRY]
7
+ .sort((a, b) => a.order_weight - b.order_weight)
8
+ .map((d) => d.id);
13
9
  export function priorityRank(priority) {
14
10
  switch (priority) {
15
11
  case "high":