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
@@ -4,7 +4,11 @@ import { isAuditExcludedStatus } from "../extractors/disposition.js";
4
4
  import { resolveNodeTool, runFirstAvailableCommand, } from "./localCommands.js";
5
5
  function tryRunConfiguredFormatter(root, candidates) {
6
6
  const result = runFirstAvailableCommand(root, candidates);
7
- return result !== null && !result.error && result.exitCode === 0;
7
+ if (result === null)
8
+ return "not_found";
9
+ if (!result.error && result.exitCode === 0)
10
+ return "success";
11
+ return "failed";
8
12
  }
9
13
  const PRETTIER_CONFIG_FILES = [
10
14
  ".prettierrc",
@@ -43,6 +47,17 @@ async function hasPrettierConfig(root) {
43
47
  return false;
44
48
  }
45
49
  }
50
+ function runFormatter(root, toolName, candidates, executedTools, failedTools, toolTimings) {
51
+ const start = Date.now();
52
+ const outcome = tryRunConfiguredFormatter(root, candidates);
53
+ if (outcome === "success") {
54
+ executedTools.push(toolName);
55
+ toolTimings.push({ tool: toolName, duration_ms: Date.now() - start });
56
+ }
57
+ else if (outcome === "failed") {
58
+ failedTools.push(toolName);
59
+ }
60
+ }
46
61
  export async function runAutoFixExecutor(bundle, root) {
47
62
  if (!bundle.file_disposition) {
48
63
  throw new Error("Cannot run auto fix executor without file_disposition");
@@ -57,6 +72,7 @@ export async function runAutoFixExecutor(bundle, root) {
57
72
  }
58
73
  }
59
74
  const executedTools = [];
75
+ const failedTools = [];
60
76
  const toolTimings = [];
61
77
  // JS, TS, HTML, CSS, JSON, YAML, MD
62
78
  if ((await hasPrettierConfig(root)) &&
@@ -70,62 +86,60 @@ export async function runAutoFixExecutor(bundle, root) {
70
86
  extensions.has("yml") ||
71
87
  extensions.has("yaml") ||
72
88
  extensions.has("md"))) {
73
- const prettierStart = Date.now();
74
- if (tryRunConfiguredFormatter(root, [
89
+ runFormatter(root, "prettier", [
75
90
  ...resolveNodeTool(root, join("node_modules", "prettier", "bin", "prettier.cjs"), ["--write", "."], "prettier --write ."),
76
91
  { command: "prettier", args: ["--write", "."], display: "prettier --write ." },
77
92
  { command: "npx", args: ["--yes", "prettier", "--write", "."], display: "npx --yes prettier --write ." },
78
- ])) {
79
- executedTools.push("prettier");
80
- toolTimings.push({ tool: "prettier", duration_ms: Date.now() - prettierStart });
81
- }
93
+ ], executedTools, failedTools, toolTimings);
82
94
  }
83
95
  // Python
84
96
  if (extensions.has("py")) {
85
- const blackStart = Date.now();
86
- if (tryRunConfiguredFormatter(root, [
97
+ runFormatter(root, "black", [
87
98
  { command: "black", args: ["."], display: "black ." },
88
99
  { command: "python", args: ["-m", "black", "."], display: "python -m black ." },
89
100
  { command: "uvx", args: ["black", "."], display: "uvx black ." },
90
101
  { command: "pipx", args: ["run", "black", "."], display: "pipx run black ." },
91
- ])) {
92
- executedTools.push("black");
93
- toolTimings.push({ tool: "black", duration_ms: Date.now() - blackStart });
94
- }
102
+ ], executedTools, failedTools, toolTimings);
95
103
  }
96
104
  // SQL
97
105
  if (extensions.has("sql")) {
98
- const sqlfluffStart = Date.now();
99
- if (tryRunConfiguredFormatter(root, [
106
+ runFormatter(root, "sqlfluff", [
100
107
  { command: "sqlfluff", args: ["fix", "--force", "."], display: "sqlfluff fix --force ." },
101
108
  { command: "uvx", args: ["sqlfluff", "fix", "--force", "."], display: "uvx sqlfluff fix --force ." },
102
109
  { command: "pipx", args: ["run", "sqlfluff", "fix", "--force", "."], display: "pipx run sqlfluff fix --force ." },
103
- ])) {
104
- executedTools.push("sqlfluff");
105
- toolTimings.push({ tool: "sqlfluff", duration_ms: Date.now() - sqlfluffStart });
106
- }
110
+ ], executedTools, failedTools, toolTimings);
107
111
  }
108
112
  // Go
109
113
  if (extensions.has("go")) {
110
- const gofmtStart = Date.now();
111
- if (tryRunConfiguredFormatter(root, [
114
+ runFormatter(root, "gofmt", [
112
115
  { command: "gofmt", args: ["-w", "."], display: "gofmt -w ." },
113
- ])) {
114
- executedTools.push("gofmt");
115
- toolTimings.push({ tool: "gofmt", duration_ms: Date.now() - gofmtStart });
116
- }
116
+ ], executedTools, failedTools, toolTimings);
117
117
  }
118
118
  const resultsArtifact = {
119
119
  executed_tools: executedTools,
120
+ failed_tools: failedTools,
120
121
  tool_timings: toolTimings,
121
122
  timestamp: new Date().toISOString(),
122
123
  };
124
+ let progressDetail;
125
+ if (executedTools.length === 0 && failedTools.length === 0) {
126
+ progressDetail = "Formatters executed: None.";
127
+ }
128
+ else if (failedTools.length === 0) {
129
+ progressDetail = `Formatters executed: ${executedTools.join(", ")}.`;
130
+ }
131
+ else if (executedTools.length === 0) {
132
+ progressDetail = `Formatters executed: None. Formatters failed: ${failedTools.join(", ")}.`;
133
+ }
134
+ else {
135
+ progressDetail = `Formatters executed: ${executedTools.join(", ")}. Formatters failed: ${failedTools.join(", ")}.`;
136
+ }
123
137
  return {
124
138
  updated: {
125
139
  ...bundle,
126
140
  auto_fixes_applied: resultsArtifact,
127
141
  },
128
142
  artifacts_written: ["auto_fixes_applied.json"],
129
- progress_summary: `Phase 1 Deterministic Auto-Fix complete. Formatters executed: ${executedTools.length > 0 ? executedTools.join(", ") : "None"}.`,
143
+ progress_summary: `Phase 1 Deterministic Auto-Fix complete. ${progressDetail}`,
130
144
  };
131
145
  }
@@ -1,7 +1,7 @@
1
1
  // Invalidation map keyed by UPSTREAM artifact → the list of DOWNSTREAM
2
2
  // artifacts that depend on it (and so become stale when it changes). The name
3
3
  // reflects the actual direction: each entry's value is that key's *dependents*.
4
- // `buildReverseDependencyMap` flips this to the "X depends on Y" view used by
4
+ // `buildArtifactDependenciesMap` flips this to the "X depends on Y" view used by
5
5
  // computeArtifactMetadata. (Renamed from the misleading ARTIFACT_DEPENDENCY_MAP,
6
6
  // which read as "X's dependencies" — the opposite of what it stores.)
7
7
  export const ARTIFACT_DEPENDENTS_MAP = {
@@ -1,4 +1,16 @@
1
1
  import type { ArtifactBundle } from "../io/artifacts.js";
2
+ /**
3
+ * Structured log event emitted during an executor step. Machine consumers should
4
+ * read `log_entries` and `degraded` on `ExecutorRunResult` rather than parsing
5
+ * `progress_summary`.
6
+ */
7
+ export interface LogEntry {
8
+ severity: "debug" | "info" | "warn" | "error";
9
+ message: string;
10
+ timestamp_ms: number;
11
+ /** Contextual key/value pairs such as task_id, run_id, analyzer name, file path. */
12
+ context?: Record<string, unknown>;
13
+ }
2
14
  /**
3
15
  * Resolved audit scope, emitted by the intake executor so the conversation-first
4
16
  * loader can echo what is about to be audited (and gate on confirmation when a
@@ -23,6 +35,9 @@ export interface ScopeSummary {
23
35
  * artifact filenames it wrote (which drive metadata/staleness bookkeeping in
24
36
  * advanceAudit), and a one-line human progress summary. Shared by every executor
25
37
  * module so they need not depend on the internalExecutors barrel.
38
+ *
39
+ * `progress_summary` is a human-readable one-liner for UI display only. Machine
40
+ * consumers should read `log_entries` and `degraded` instead of parsing it.
26
41
  */
27
42
  export interface ExecutorRunResult {
28
43
  updated: ArtifactBundle;
@@ -34,4 +49,22 @@ export interface ExecutorRunResult {
34
49
  * `mis_scope_smells` is non-empty.
35
50
  */
36
51
  scope_summary?: ScopeSummary;
52
+ /**
53
+ * Structured log events emitted during the step (errors, warnings, partial
54
+ * failures, analyzer skips). Machine consumers should read this instead of
55
+ * parsing `progress_summary`.
56
+ */
57
+ log_entries?: LogEntry[];
58
+ /**
59
+ * Wall-clock milliseconds from executor entry to return, enabling step-latency
60
+ * tracking without string-parsing.
61
+ */
62
+ step_duration_ms?: number;
63
+ /**
64
+ * Set to `true` when the step completed but encountered at least one non-fatal
65
+ * error (e.g. a language-analyzer failure, a line-count error, a missing task
66
+ * artifact). Lets callers detect partial failure without scanning
67
+ * `progress_summary`.
68
+ */
69
+ degraded?: boolean;
37
70
  }
@@ -1,6 +1,13 @@
1
1
  export interface ExecutorDefinition {
2
2
  id: string;
3
+ kind: "deterministic" | "host_delegation";
3
4
  obligation_ids: string[];
4
5
  description: string;
5
6
  }
7
+ /**
8
+ * Returns true when the executor identified by `id` is a host-delegation point
9
+ * (i.e. it pauses the deterministic pipeline and asks the active LLM agent to
10
+ * perform work) rather than a deterministic executor.
11
+ */
12
+ export declare function isHostDelegationExecutor(id: string): boolean;
6
13
  export declare const EXECUTOR_REGISTRY: ExecutorDefinition[];
@@ -1,76 +1,100 @@
1
+ /**
2
+ * Returns true when the executor identified by `id` is a host-delegation point
3
+ * (i.e. it pauses the deterministic pipeline and asks the active LLM agent to
4
+ * perform work) rather than a deterministic executor.
5
+ */
6
+ export function isHostDelegationExecutor(id) {
7
+ const entry = EXECUTOR_REGISTRY.find((e) => e.id === id);
8
+ return entry?.kind === "host_delegation";
9
+ }
1
10
  export const EXECUTOR_REGISTRY = [
2
11
  {
3
12
  id: "intake_executor",
13
+ kind: "deterministic",
4
14
  obligation_ids: ["repo_manifest", "file_disposition"],
5
15
  description: "Create intake artifacts for repository discovery and disposition.",
6
16
  },
7
17
  {
8
18
  id: "structure_executor",
19
+ kind: "deterministic",
9
20
  obligation_ids: ["structure_artifacts"],
10
21
  description: "Build structure artifacts such as units, surfaces, graphs, flows, and risk.",
11
22
  },
12
23
  {
13
24
  id: "graph_enrichment_executor",
25
+ kind: "deterministic",
14
26
  obligation_ids: ["graph_enrichment_current"],
15
27
  description: "Layer optional language-analyzer edges onto the deterministic graph (regex floor preserved); record analyzer provenance.",
16
28
  },
17
29
  {
18
30
  id: "design_assessment_executor",
31
+ kind: "deterministic",
19
32
  obligation_ids: ["design_assessment_current"],
20
33
  description: "Run deterministic structural analysis to assess overall project design.",
21
34
  },
22
35
  {
23
36
  id: "design_review",
37
+ kind: "host_delegation",
24
38
  obligation_ids: ["design_review_completed"],
25
39
  description: "Pause the pipeline and delegate a holistic project design review to the active LLM agent.",
26
40
  },
27
41
  {
28
42
  id: "planning_executor",
43
+ kind: "deterministic",
29
44
  obligation_ids: ["planning_artifacts"],
30
45
  description: "Build coverage, tasks, runtime validation planning artifacts, and related planning outputs.",
31
46
  },
32
47
  {
33
48
  id: "result_ingestion_executor",
49
+ kind: "deterministic",
34
50
  obligation_ids: ["audit_results_ingested"],
35
51
  description: "Ingest available audit result artifacts and refresh dependent coverage artifacts.",
36
52
  },
37
53
  {
38
54
  id: "runtime_validation_executor",
55
+ kind: "deterministic",
39
56
  obligation_ids: ["runtime_validation_current"],
40
57
  description: "Merge runtime validation evidence updates when provided.",
41
58
  },
42
59
  {
43
60
  id: "runtime_validation_update_executor",
61
+ kind: "deterministic",
44
62
  obligation_ids: [],
45
63
  description: "Merge imported runtime validation evidence updates.",
46
64
  },
47
65
  {
48
66
  id: "synthesis_executor",
67
+ kind: "deterministic",
49
68
  obligation_ids: ["synthesis_current"],
50
69
  description: "Emit the canonical audit-findings.json and render the deterministic Markdown audit report.",
51
70
  },
52
71
  {
53
72
  id: "synthesis_narrative_executor",
73
+ kind: "deterministic",
54
74
  obligation_ids: ["synthesis_narrative_current"],
55
75
  description: "Resolve the optional synthesis narrative (themes, executive summary, top risks); omit deterministically without a provider.",
56
76
  },
57
77
  {
58
78
  id: "external_analyzer_import_executor",
79
+ kind: "deterministic",
59
80
  obligation_ids: [],
60
81
  description: "Import normalized external analyzer results into the artifact set.",
61
82
  },
62
83
  {
63
84
  id: "auto_fix_executor",
85
+ kind: "deterministic",
64
86
  obligation_ids: ["auto_fixes_applied"],
65
87
  description: "Run configured deterministic code formatters to apply surface-level fixes automatically.",
66
88
  },
67
89
  {
68
90
  id: "syntax_resolution_executor",
91
+ kind: "deterministic",
69
92
  obligation_ids: ["syntax_resolved"],
70
93
  description: "Run deterministic static analysis/compilers and extract any remaining unfixable syntactical errors into external signals.",
71
94
  },
72
95
  {
73
96
  id: "agent",
97
+ kind: "host_delegation",
74
98
  obligation_ids: ["audit_tasks_completed"],
75
99
  description: "Pause the pipeline and delegate pending codebase review tasks or syntax resolutions to the active LLM agent.",
76
100
  },
@@ -7,7 +7,13 @@
7
7
  */
8
8
  const GRAPH_EDGE_BUCKETS = ["imports", "calls", "references"];
9
9
  const MAX_ANCHORS = 160;
10
- const KEYWORD_PATTERN = /\b(auth|token|password|secret|permission|role|sql|query|exec|spawn|eval|deserialize|encrypt|decrypt|cache|retry|timeout|transaction|lock|race|TODO|FIXME)\b/i;
10
+ // Keywords that signal elevated-risk or review-worthy lines, grouped by concern:
11
+ // auth/access: auth, password, permission, role, secret, token
12
+ // injection/execution: deserialize, eval, exec, query, spawn, sql
13
+ // crypto: decrypt, encrypt
14
+ // concurrency/reliability: cache, lock, race, retry, timeout, transaction
15
+ // debt markers: FIXME, TODO
16
+ const KEYWORD_PATTERN = /\b(auth|password|permission|role|secret|token|deserialize|eval|exec|query|spawn|sql|decrypt|encrypt|cache|lock|race|retry|timeout|transaction|FIXME|TODO)\b/i;
11
17
  const SYMBOL_PATTERNS = [
12
18
  {
13
19
  kind: "import",
@@ -124,6 +130,35 @@ function collectGraphEdges(graphBundle, path) {
124
130
  a.from.localeCompare(b.from) ||
125
131
  a.to.localeCompare(b.to));
126
132
  }
133
+ function scanSymbol(line, lineNumber, anchors, seen) {
134
+ for (const { kind, pattern, label } of SYMBOL_PATTERNS) {
135
+ const match = line.match(pattern);
136
+ if (!match) {
137
+ continue;
138
+ }
139
+ const name = match.slice(1).find((value) => value && value.trim().length > 0) ?? label;
140
+ addAnchor(anchors, seen, {
141
+ kind,
142
+ name: truncate(name, 80),
143
+ line: lineNumber,
144
+ detail: truncate(`${label}: ${line}`, 180),
145
+ });
146
+ return kind;
147
+ }
148
+ return null;
149
+ }
150
+ function scanKeyword(line, lineNumber, anchors, seen) {
151
+ if (!KEYWORD_PATTERN.test(line)) {
152
+ return false;
153
+ }
154
+ addAnchor(anchors, seen, {
155
+ kind: "keyword",
156
+ name: truncate(line.match(KEYWORD_PATTERN)?.[1] ?? "keyword", 80),
157
+ line: lineNumber,
158
+ detail: truncate(line, 180),
159
+ });
160
+ return true;
161
+ }
127
162
  export function buildFileAnchorSummary(params) {
128
163
  const anchors = [];
129
164
  const seen = new Set();
@@ -148,35 +183,13 @@ export function buildFileAnchorSummary(params) {
148
183
  }
149
184
  lines.forEach((line, index) => {
150
185
  const lineNumber = index + 1;
151
- for (const { kind, pattern, label } of SYMBOL_PATTERNS) {
152
- const match = line.match(pattern);
153
- if (!match) {
154
- continue;
155
- }
156
- const name = match.slice(1).find((value) => value && value.trim().length > 0) ?? label;
157
- if (kind === "route") {
158
- routeCount += 1;
159
- }
160
- else if (kind === "symbol") {
161
- symbolCount += 1;
162
- }
163
- addAnchor(anchors, seen, {
164
- kind,
165
- name: truncate(name, 80),
166
- line: lineNumber,
167
- detail: truncate(`${label}: ${line}`, 180),
168
- });
169
- break;
170
- }
171
- if (KEYWORD_PATTERN.test(line)) {
186
+ const symbolKind = scanSymbol(line, lineNumber, anchors, seen);
187
+ if (symbolKind === "route")
188
+ routeCount += 1;
189
+ else if (symbolKind === "symbol")
190
+ symbolCount += 1;
191
+ if (scanKeyword(line, lineNumber, anchors, seen))
172
192
  keywordCount += 1;
173
- addAnchor(anchors, seen, {
174
- kind: "keyword",
175
- name: truncate(line.match(KEYWORD_PATTERN)?.[1] ?? "keyword", 80),
176
- line: lineNumber,
177
- detail: truncate(line, 180),
178
- });
179
- }
180
193
  });
181
194
  const graphEdges = collectGraphEdges(params.graphBundle, path);
182
195
  for (const edge of graphEdges) {
@@ -36,7 +36,12 @@ export async function checkFileIntegrity(root, manifest, scope) {
36
36
  missing.push(record.path);
37
37
  }
38
38
  else {
39
- console.warn(`fileIntegrity: I/O error on ${record.path}: ${code ?? String(err)}`);
39
+ console.warn('fileIntegrity: I/O error', {
40
+ root,
41
+ scope_size: files.length,
42
+ file: record.path,
43
+ code: code ?? String(err),
44
+ });
40
45
  ioErrors.push(record.path);
41
46
  }
42
47
  }
@@ -32,8 +32,7 @@ export function buildFlowCoverage(criticalFlows, coverageMatrix) {
32
32
  }
33
33
  }
34
34
  const completed_lenses = [...completed];
35
- const status = required.length > 0 &&
36
- required.every((lens) => completed_lenses.includes(lens))
35
+ const status = required.every((lens) => completed_lenses.includes(lens))
37
36
  ? "complete"
38
37
  : completed_lenses.length > 0
39
38
  ? "partial"
@@ -1,21 +1,25 @@
1
- const DEFAULT_FLOW_LENS_PRIORITY = [
1
+ const FLOW_REVIEW_LENSES = [
2
2
  "security",
3
3
  "reliability",
4
4
  "correctness",
5
+ "data_integrity",
6
+ "operability",
7
+ "performance",
8
+ "observability",
5
9
  ];
6
10
  function lensPathKey(lens, path) {
7
11
  return `${lens}:${path}`;
8
12
  }
9
13
  function flowLensPriority(lens) {
10
- const index = DEFAULT_FLOW_LENS_PRIORITY.indexOf(lens);
11
- return index >= 0 ? index : DEFAULT_FLOW_LENS_PRIORITY.length;
14
+ const index = FLOW_REVIEW_LENSES.indexOf(lens);
15
+ return index >= 0 ? index : FLOW_REVIEW_LENSES.length;
12
16
  }
13
17
  export function claimFlowReviewBlocks(criticalFlows, pendingByLens, assigned) {
14
18
  const candidates = [];
15
19
  for (const flow of criticalFlows.flows) {
16
20
  const flowPaths = [...new Set(flow.paths)].sort((a, b) => a.localeCompare(b));
17
21
  const desiredLenses = flow.concerns
18
- .filter((concern) => DEFAULT_FLOW_LENS_PRIORITY.includes(concern))
22
+ .filter((concern) => FLOW_REVIEW_LENSES.includes(concern))
19
23
  .sort((a, b) => flowLensPriority(a) - flowLensPriority(b));
20
24
  for (const lens of desiredLenses) {
21
25
  const pendingPaths = pendingByLens.get(lens);
@@ -39,6 +39,58 @@ function mergeRoutes(floor, analyzer) {
39
39
  a.handler.localeCompare(b.handler) ||
40
40
  (a.method ?? "").localeCompare(b.method ?? ""));
41
41
  }
42
+ /**
43
+ * Run one analyzer: resolve its dependency, invoke analyze(), and return a
44
+ * discriminated result. Early-exit guards (not_applicable / skip / absent-root)
45
+ * are handled by the caller; this helper starts from a confirmed runnable state.
46
+ */
47
+ async function runSingleAnalyzer(analyzer, root, setting, bundle, pathLookup, includedFiles, disposition, cacheRoot) {
48
+ const run = resolveForRun(analyzer, root, setting, cacheRoot);
49
+ if (run.resolution === "absent") {
50
+ return { ok: false, note: run.note ?? "Dependency absent.", resolution: "absent" };
51
+ }
52
+ try {
53
+ const output = await analyzer.analyze(includedFiles.filter((f) => analyzer.supports(f)), {
54
+ root,
55
+ repoManifest: bundle.repo_manifest,
56
+ disposition,
57
+ includedFiles,
58
+ pathLookup,
59
+ dependencyPath: run.path,
60
+ });
61
+ return {
62
+ ok: true,
63
+ edges: output.edges ?? [],
64
+ routes: output.routes ?? [],
65
+ resolution: run.resolution,
66
+ };
67
+ }
68
+ catch (error) {
69
+ const note = error instanceof Error
70
+ ? `Analyzer failed [${error.name}]: ${error.message}${error.stack ? ` — stack: ${error.stack.split("\n").slice(0, 4).join(" | ")}` : ""}`
71
+ : `Analyzer failed: ${String(error)}.`;
72
+ return { ok: false, note, resolution: run.resolution };
73
+ }
74
+ }
75
+ /**
76
+ * Assemble the enriched GraphBundle from the regex floor plus per-bucket
77
+ * analyzer edges and merged route edges.
78
+ */
79
+ function buildEnrichedGraph(floor, bucketEdges, routeEdges, analyzersUsed) {
80
+ return {
81
+ ...floor,
82
+ graphs: {
83
+ ...floor.graphs,
84
+ imports: mergeAnalyzerEdges(floor.graphs.imports ?? [], bucketEdges.imports),
85
+ calls: mergeAnalyzerEdges(floor.graphs.calls ?? [], bucketEdges.calls),
86
+ references: mergeAnalyzerEdges(floor.graphs.references ?? [], bucketEdges.references),
87
+ ...(routeEdges.length > 0
88
+ ? { routes: mergeRoutes(floor.graphs.routes ?? [], routeEdges) }
89
+ : {}),
90
+ },
91
+ analyzers_used: [...new Set(analyzersUsed)].sort(),
92
+ };
93
+ }
42
94
  /**
43
95
  * Resolve a dependency for actual execution (may install for ephemeral/permanent).
44
96
  * `auto`/`repo` with an absent dependency falls back to the regex floor.
@@ -107,38 +159,16 @@ export async function runGraphEnrichmentExecutor(bundle, options = {}) {
107
159
  entries.push({ id: analyzer.id, resolution: "absent", setting, edges_added: 0, routes_added: 0, note: "No repository root available for analysis." });
108
160
  continue;
109
161
  }
110
- const run = resolveForRun(analyzer, root, setting, options.cacheRoot);
111
- if (run.resolution === "absent") {
112
- entries.push({ id: analyzer.id, resolution: "absent", setting, edges_added: 0, routes_added: 0, note: run.note });
113
- continue;
114
- }
115
- let edges = [];
116
- let routes = [];
117
- try {
118
- const output = await analyzer.analyze(supportedFiles, {
119
- root,
120
- repoManifest: bundle.repo_manifest,
121
- disposition,
122
- includedFiles,
123
- pathLookup,
124
- dependencyPath: run.path,
125
- });
126
- edges = output.edges ?? [];
127
- routes = output.routes ?? [];
128
- }
129
- catch (error) {
130
- entries.push({
131
- id: analyzer.id,
132
- resolution: run.resolution,
133
- setting,
134
- edges_added: 0,
135
- routes_added: 0,
136
- note: error instanceof Error
137
- ? `Analyzer failed [${error.name}]: ${error.message}${error.stack ? ` — stack: ${error.stack.split("\n").slice(0, 4).join(" | ")}` : ""}`
138
- : `Analyzer failed: ${String(error)}.`,
139
- });
162
+ const result = await runSingleAnalyzer(analyzer, root, setting, bundle, pathLookup, includedFiles, disposition, options.cacheRoot);
163
+ if (!result.ok) {
164
+ const entry = { id: analyzer.id, resolution: result.resolution, setting, edges_added: 0, routes_added: 0, note: result.note };
165
+ entries.push(entry);
166
+ if (entry.note?.startsWith("Analyzer failed")) {
167
+ console.warn(`[graph-enrichment] Analyzer '${analyzer.id}' failed: ${entry.note}`);
168
+ }
140
169
  continue;
141
170
  }
171
+ const { edges, routes, resolution } = result;
142
172
  for (const edge of edges) {
143
173
  bucketEdges[bucketForKind(edge.kind)].push(edge);
144
174
  }
@@ -146,7 +176,7 @@ export async function runGraphEnrichmentExecutor(bundle, options = {}) {
146
176
  if (edges.length + routes.length > 0) {
147
177
  analyzersUsed.push(analyzer.id);
148
178
  }
149
- entries.push({ id: analyzer.id, resolution: run.resolution, setting, edges_added: edges.length, routes_added: routes.length });
179
+ entries.push({ id: analyzer.id, resolution, setting, edges_added: edges.length, routes_added: routes.length });
150
180
  }
151
181
  const applied = analyzersUsed.length > 0;
152
182
  const record = {
@@ -158,19 +188,7 @@ export async function runGraphEnrichmentExecutor(bundle, options = {}) {
158
188
  // reasons of low-confidence edges on whichever graph stands — the floor's
159
189
  // heuristic edges exist regardless of analyzers.
160
190
  const graphBundle = applied
161
- ? {
162
- ...floor,
163
- graphs: {
164
- ...floor.graphs,
165
- imports: mergeAnalyzerEdges(floor.graphs.imports ?? [], bucketEdges.imports),
166
- calls: mergeAnalyzerEdges(floor.graphs.calls ?? [], bucketEdges.calls),
167
- references: mergeAnalyzerEdges(floor.graphs.references ?? [], bucketEdges.references),
168
- ...(routeEdges.length > 0
169
- ? { routes: mergeRoutes(floor.graphs.routes ?? [], routeEdges) }
170
- : {}),
171
- },
172
- analyzers_used: [...new Set(analyzersUsed)].sort(),
173
- }
191
+ ? buildEnrichedGraph(floor, bucketEdges, routeEdges, analyzersUsed)
174
192
  : floor;
175
193
  let reasoned = { rewritten: 0, candidates: 0 };
176
194
  if (options.llmEdgeReasoning === true && options.edgeReasoning) {
@@ -181,10 +199,14 @@ export async function runGraphEnrichmentExecutor(bundle, options = {}) {
181
199
  ? ` Edge reasoning rewrote ${reasoned.rewritten} reason(s).`
182
200
  : "";
183
201
  if (!graphChanged) {
202
+ const failedEntries = entries.filter((e) => e.note?.startsWith("Analyzer failed"));
203
+ const failureSuffix = failedEntries.length > 0
204
+ ? `; ${failedEntries.length} analyzer(s) failed: ${failedEntries.map((e) => e.id).join(", ")} (see analyzer_capability.json)`
205
+ : "";
184
206
  return {
185
207
  updated: { ...bundle, analyzer_capability: record },
186
208
  artifacts_written: ["analyzer_capability.json"],
187
- progress_summary: "Graph enrichment omitted; deterministic regex graph retained.",
209
+ progress_summary: `Graph enrichment omitted; deterministic regex graph retained.${failureSuffix}`,
188
210
  };
189
211
  }
190
212
  const totalEdges = entries.reduce((sum, entry) => sum + entry.edges_added, 0);
@@ -139,6 +139,8 @@ export async function runRuntimeValidationExecutor(bundle, root, options = {}) {
139
139
  const existing = bundle.runtime_validation_report ?? { results: [] };
140
140
  const byTaskId = new Map(existing.results.map((result) => [result.task_id, result]));
141
141
  const byCommand = new Map();
142
+ let uniqueCommandsRun = 0;
143
+ let deduplicatedHits = 0;
142
144
  for (const task of bundle.runtime_validation_tasks.tasks) {
143
145
  const prior = byTaskId.get(task.id);
144
146
  if (prior &&
@@ -156,6 +158,12 @@ export async function runRuntimeValidationExecutor(bundle, root, options = {}) {
156
158
  continue;
157
159
  }
158
160
  const signature = task.command.join("\0");
161
+ if (byCommand.has(signature)) {
162
+ deduplicatedHits++;
163
+ }
164
+ else {
165
+ uniqueCommandsRun++;
166
+ }
159
167
  const outcome = byCommand.get(signature) ?? (await runCommand(task.command, root, { opentoken: options.opentoken }));
160
168
  byCommand.set(signature, outcome);
161
169
  byTaskId.set(task.id, {
@@ -185,7 +193,7 @@ export async function runRuntimeValidationExecutor(bundle, root, options = {}) {
185
193
  "runtime_validation_report.json",
186
194
  ...selectiveDeepening.artifacts,
187
195
  ],
188
- progress_summary: `Executed deterministic runtime validation for ${bundle.runtime_validation_tasks.tasks.length} task(s).` +
196
+ progress_summary: `Executed deterministic runtime validation for ${bundle.runtime_validation_tasks.tasks.length} task(s) (${uniqueCommandsRun} unique command(s) run, ${deduplicatedHits} served from deduplication cache).` +
189
197
  selectiveDeepening.summarySuffix,
190
198
  };
191
199
  }
@@ -1,9 +1,5 @@
1
1
  import type { ArtifactBundle } from "../io/artifacts.js";
2
2
  import type { ExecutorRunResult } from "./executorResult.js";
3
- /** Prefix used to carry the scope summary inside `progress_summary` for hosts
4
- * that read the step's progress text rather than the `scope_summary.json`
5
- * artifact. The loader extracts everything after this marker as JSON. */
6
- export declare const SCOPE_SUMMARY_PREFIX = "SCOPE_SUMMARY:";
7
3
  /**
8
4
  * Detect signals that the resolved audit root may be the *wrong* directory.
9
5
  * Two heuristics, returned as zero or more human-readable warnings: