auditor-lambda 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/audit-code-wrapper-lib.mjs +149 -129
  2. package/dist/adapters/normalizeExternal.js +6 -3
  3. package/dist/cli/args.d.ts +0 -1
  4. package/dist/cli/args.js +0 -6
  5. package/dist/cli/dispatch.js +3 -2
  6. package/dist/cli/lineIndex.js +4 -1
  7. package/dist/cli/mergeAndIngestCommand.d.ts +1 -0
  8. package/dist/cli/mergeAndIngestCommand.js +219 -0
  9. package/dist/cli/nextStepCommand.js +5 -1
  10. package/dist/cli/runToCompletion.d.ts +9 -0
  11. package/dist/cli/runToCompletion.js +655 -480
  12. package/dist/cli/statusCommand.d.ts +1 -0
  13. package/dist/cli/statusCommand.js +113 -0
  14. package/dist/cli/submitPacketCommand.d.ts +1 -0
  15. package/dist/cli/submitPacketCommand.js +155 -0
  16. package/dist/cli/workerResult.d.ts +1 -1
  17. package/dist/cli/workerRunCommand.d.ts +1 -0
  18. package/dist/cli/workerRunCommand.js +88 -0
  19. package/dist/cli.js +14 -563
  20. package/dist/extractors/analyzers/sql.js +4 -1
  21. package/dist/extractors/analyzers/treeSitter.js +29 -15
  22. package/dist/extractors/analyzers/typescript.js +10 -8
  23. package/dist/extractors/designAssessment.js +43 -24
  24. package/dist/extractors/graph.js +139 -73
  25. package/dist/extractors/pathPatterns.js +17 -5
  26. package/dist/io/runArtifactTypes.d.ts +18 -0
  27. package/dist/io/runArtifactTypes.js +1 -0
  28. package/dist/io/runArtifacts.d.ts +2 -18
  29. package/dist/io/runArtifacts.js +14 -3
  30. package/dist/mcp/server.js +9 -0
  31. package/dist/orchestrator/advance.js +37 -22
  32. package/dist/orchestrator/artifactFreshness.js +2 -2
  33. package/dist/orchestrator/autoFixExecutor.d.ts +1 -1
  34. package/dist/orchestrator/autoFixExecutor.js +16 -8
  35. package/dist/orchestrator/dependencyMap.d.ts +1 -1
  36. package/dist/orchestrator/dependencyMap.js +7 -1
  37. package/dist/orchestrator/fileAnchors.js +14 -3
  38. package/dist/orchestrator/flowCoverage.js +1 -0
  39. package/dist/orchestrator/flowRequeue.js +4 -1
  40. package/dist/orchestrator/{internalExecutors.d.ts → ingestionExecutors.d.ts} +0 -6
  41. package/dist/orchestrator/ingestionExecutors.js +237 -0
  42. package/dist/orchestrator/intakeExecutors.d.ts +3 -0
  43. package/dist/orchestrator/intakeExecutors.js +25 -0
  44. package/dist/orchestrator/planningExecutors.d.ts +4 -0
  45. package/dist/orchestrator/planningExecutors.js +95 -0
  46. package/dist/orchestrator/runtimeCommand.js +7 -15
  47. package/dist/orchestrator/selectiveDeepening/conflict.d.ts +8 -0
  48. package/dist/orchestrator/selectiveDeepening/conflict.js +71 -0
  49. package/dist/orchestrator/selectiveDeepening/findingFollowup.d.ts +10 -0
  50. package/dist/orchestrator/selectiveDeepening/findingFollowup.js +52 -0
  51. package/dist/orchestrator/selectiveDeepening/highRiskClean.d.ts +7 -0
  52. package/dist/orchestrator/selectiveDeepening/highRiskClean.js +44 -0
  53. package/dist/orchestrator/selectiveDeepening/index.d.ts +18 -0
  54. package/dist/orchestrator/selectiveDeepening/index.js +128 -0
  55. package/dist/orchestrator/selectiveDeepening/lensVerification.d.ts +12 -0
  56. package/dist/orchestrator/selectiveDeepening/lensVerification.js +242 -0
  57. package/dist/orchestrator/selectiveDeepening/runtimeValidation.d.ts +13 -0
  58. package/dist/orchestrator/selectiveDeepening/runtimeValidation.js +57 -0
  59. package/dist/orchestrator/selectiveDeepening/shared.d.ts +45 -0
  60. package/dist/orchestrator/selectiveDeepening/shared.js +128 -0
  61. package/dist/orchestrator/selectiveDeepening/stewardFollowup.d.ts +6 -0
  62. package/dist/orchestrator/selectiveDeepening/stewardFollowup.js +72 -0
  63. package/dist/orchestrator/selectiveDeepening.d.ts +2 -20
  64. package/dist/orchestrator/selectiveDeepening.js +6 -760
  65. package/dist/orchestrator/staleness.js +3 -3
  66. package/dist/orchestrator/structureExecutors.d.ts +5 -0
  67. package/dist/orchestrator/structureExecutors.js +94 -0
  68. package/dist/orchestrator/taskBuilder.d.ts +2 -2
  69. package/dist/orchestrator/taskBuilder.js +101 -82
  70. package/dist/providers/index.d.ts +7 -0
  71. package/dist/providers/index.js +14 -95
  72. package/dist/quota/discoveredLimits.d.ts +1 -0
  73. package/dist/quota/discoveredLimits.js +7 -1
  74. package/dist/quota/index.d.ts +0 -2
  75. package/dist/quota/index.js +1 -2
  76. package/dist/reporting/workBlocks.js +7 -4
  77. package/dist/types/reviewPlanning.d.ts +23 -16
  78. package/dist/validation/auditResults.js +97 -95
  79. package/dist/validation/sessionConfig.d.ts +2 -2
  80. package/dist/validation/sessionConfig.js +14 -7
  81. package/package.json +3 -2
  82. package/schemas/audit_findings.schema.json +3 -3
  83. package/schemas/critical_flows.schema.json +3 -2
  84. package/schemas/dispatch_quota.schema.json +1 -1
  85. package/schemas/graph_bundle.schema.json +1 -1
  86. package/schemas/review_packets.schema.json +1 -1
  87. package/schemas/step_contract.schema.json +80 -0
  88. package/scripts/postinstall.mjs +19 -2
  89. package/skills/audit-code/opencode-command-template.txt +3 -3
  90. package/dist/orchestrator/internalExecutors.js +0 -424
  91. package/dist/providers/localSubprocessProvider.d.ts +0 -9
  92. package/dist/providers/localSubprocessProvider.js +0 -18
  93. package/dist/providers/subprocessTemplateProvider.d.ts +0 -8
  94. package/dist/providers/subprocessTemplateProvider.js +0 -59
  95. package/dist/providers/vscodeTaskProvider.d.ts +0 -7
  96. package/dist/providers/vscodeTaskProvider.js +0 -14
  97. package/dist/quota/probe.d.ts +0 -10
  98. package/dist/quota/probe.js +0 -18
@@ -61,6 +61,10 @@ export async function ensureSupervisorDirs(artifactsDir) {
61
61
  }
62
62
  async function writeDispatchSchemaFiles(artifactsDir) {
63
63
  const dispatchDir = join(artifactsDir, "dispatch");
64
+ // Ensure the dispatch dir exists: this is now written before the pointer
65
+ // files (which formerly created it), and parallel-slot dispatch may reach
66
+ // here before the canonical dispatch has run.
67
+ await mkdir(dispatchDir, { recursive: true });
64
68
  await writeFile(join(dispatchDir, CURRENT_SCHEMA_FILENAME), await readFile(auditResultSchemaPath, "utf8"), "utf8");
65
69
  await writeFile(join(dispatchDir, CURRENT_RESULTS_SCHEMA_FILENAME), await readFile(auditResultsSchemaPath, "utf8"), "utf8");
66
70
  await writeFile(join(dispatchDir, CURRENT_FINDING_SCHEMA_FILENAME), await readFile(findingSchemaPath, "utf8"), "utf8");
@@ -116,15 +120,22 @@ export async function writeWorkerTaskFiles(task, prompt, paths, artifactsDir, cu
116
120
  run_id: task.run_id,
117
121
  status: "dispatched",
118
122
  });
119
- if (options.updateDispatch === false) {
120
- await writeDispatchSchemaFiles(artifactsDir);
123
+ // The result schema files are always required by the worker, regardless of
124
+ // whether this run owns the shared "current dispatch" pointer files.
125
+ await writeDispatchSchemaFiles(artifactsDir);
126
+ // Parallel-slot dispatch passes updateDispatch:false so each slot does NOT
127
+ // clobber the shared current-task / current-prompt / current-tasks pointers
128
+ // (only the single canonical dispatch should own them). The default path
129
+ // (updateDispatch unset/true) refreshes those pointers and the single-task
130
+ // fallback.
131
+ const updateDispatch = options.updateDispatch !== false;
132
+ if (!updateDispatch) {
121
133
  return;
122
134
  }
123
135
  await writeJsonFile(join(artifactsDir, "dispatch", CURRENT_TASK_FILENAME), task);
124
136
  await writeFile(join(artifactsDir, "dispatch", CURRENT_PROMPT_FILENAME), prompt, "utf8");
125
137
  await writeJsonFile(join(artifactsDir, "dispatch", CURRENT_TASKS_FILENAME), currentTasks ?? []);
126
138
  await writeSingleTaskFallbackFiles(artifactsDir, task, currentTasks);
127
- await writeDispatchSchemaFiles(artifactsDir);
128
139
  }
129
140
  export async function writeDispatchBatchFiles(artifactsDir, runs, currentTasks) {
130
141
  const summary = {
@@ -125,6 +125,7 @@ async function runWrapperCommand(args, options) {
125
125
  });
126
126
  });
127
127
  }
128
+ const SUBPROCESS_STDERR_TAIL_CHARS = 2000;
128
129
  async function parseCliJson(args, options, allowNonZero = false) {
129
130
  const result = await runWrapperCommand(args, options);
130
131
  const combined = result.stdout.trim() || result.stderr.trim();
@@ -134,6 +135,14 @@ async function parseCliJson(args, options, allowNonZero = false) {
134
135
  if (combined.length === 0) {
135
136
  throw new Error("Command completed without JSON output.");
136
137
  }
138
+ // On a successful (or tolerated-nonzero) call we parse stdout for the JSON
139
+ // payload and otherwise discard stderr. Surface any captured stderr as a
140
+ // tail so subprocess diagnostics (warnings, structured stderr lines) are not
141
+ // lost when the command still succeeded.
142
+ const stderrTail = result.stderr.trim();
143
+ if (stderrTail.length > 0) {
144
+ process.stderr.write(`[audit-code] mcp: subprocess stderr: ${stderrTail.slice(-SUBPROCESS_STDERR_TAIL_CHARS)}\n`);
145
+ }
137
146
  try {
138
147
  return JSON.parse(result.stdout);
139
148
  }
@@ -1,13 +1,28 @@
1
1
  import { decideNextStep, findObligation } from "./nextStep.js";
2
2
  import { deriveAuditState } from "./state.js";
3
3
  import { computeArtifactMetadata } from "./artifactMetadata.js";
4
- import { runIntakeExecutor, runStructureExecutor, runPlanningExecutor, runResultIngestionExecutor, runRuntimeValidationExecutor, runRuntimeValidationUpdateExecutor, runDesignAssessmentExecutor, runDesignReviewAutoComplete, runExternalAnalyzerImportExecutor, } from "./internalExecutors.js";
4
+ import { runIntakeExecutor } from "./intakeExecutors.js";
5
+ import { runStructureExecutor, runDesignAssessmentExecutor, runDesignReviewAutoComplete, } from "./structureExecutors.js";
6
+ import { runPlanningExecutor } from "./planningExecutors.js";
7
+ import { runResultIngestionExecutor, runRuntimeValidationExecutor, runRuntimeValidationUpdateExecutor, runExternalAnalyzerImportExecutor, } from "./ingestionExecutors.js";
5
8
  import { runSynthesisExecutor, runSynthesisNarrativeExecutor, } from "./synthesisExecutors.js";
6
9
  import { runAutoFixExecutor } from "./autoFixExecutor.js";
7
10
  import { runSyntaxResolutionExecutor } from "./syntaxResolutionExecutor.js";
8
11
  import { runGraphEnrichmentExecutor } from "./graphEnrichmentExecutor.js";
9
12
  import { resolveAuditScope } from "./scope.js";
10
13
  import { RunLogger } from "@audit-tools/shared";
14
+ /**
15
+ * Narrow an optional root to a definite string for an executor that requires
16
+ * it, throwing the canonical "advanceAudit <executor> requires root" error
17
+ * otherwise. Replaces the guard previously copy-pasted across every
18
+ * root-dependent executor branch below.
19
+ */
20
+ function requireRoot(root, executorName) {
21
+ if (!root) {
22
+ throw new Error(`advanceAudit ${executorName} requires root`);
23
+ }
24
+ return root;
25
+ }
11
26
  function cloneState(state) {
12
27
  return {
13
28
  ...state,
@@ -64,11 +79,11 @@ export async function advanceAudit(bundle, options = {}) {
64
79
  });
65
80
  try {
66
81
  switch (selectedExecutor) {
67
- case "intake_executor":
68
- if (!options.root)
69
- throw new Error("advanceAudit intake_executor requires root");
70
- run = await runIntakeExecutor(bundle, options.root);
82
+ case "intake_executor": {
83
+ const root = requireRoot(options.root, "intake_executor");
84
+ run = await runIntakeExecutor(bundle, root);
71
85
  break;
86
+ }
72
87
  case "structure_executor":
73
88
  run = await runStructureExecutor(bundle, options.root);
74
89
  break;
@@ -86,26 +101,26 @@ export async function advanceAudit(bundle, options = {}) {
86
101
  case "design_review":
87
102
  run = runDesignReviewAutoComplete(bundle);
88
103
  break;
89
- case "planning_executor":
90
- if (!options.root)
91
- throw new Error("advanceAudit planning_executor requires root");
104
+ case "planning_executor": {
105
+ const root = requireRoot(options.root, "planning_executor");
92
106
  plannedScope = resolveAuditScope({
93
- root: options.root,
107
+ root,
94
108
  since: options.since,
95
109
  bundle,
96
110
  });
97
- run = await runPlanningExecutor(bundle, options.root, options.lineIndex ?? {}, options.sizeIndex, plannedScope);
111
+ run = await runPlanningExecutor(bundle, root, options.lineIndex ?? {}, options.sizeIndex, plannedScope);
98
112
  break;
113
+ }
99
114
  case "result_ingestion_executor":
100
115
  run = runResultIngestionExecutor(bundle, options.auditResults ?? bundle.audit_results ?? []);
101
116
  break;
102
- case "runtime_validation_executor":
103
- if (!options.root)
104
- throw new Error("advanceAudit runtime_validation_executor requires root");
105
- run = await runRuntimeValidationExecutor(bundle, options.root, {
117
+ case "runtime_validation_executor": {
118
+ const root = requireRoot(options.root, "runtime_validation_executor");
119
+ run = await runRuntimeValidationExecutor(bundle, root, {
106
120
  opentoken: options.opentoken,
107
121
  });
108
122
  break;
123
+ }
109
124
  case "synthesis_executor":
110
125
  run = runSynthesisExecutor(bundle, options.auditResults);
111
126
  break;
@@ -122,16 +137,16 @@ export async function advanceAudit(bundle, options = {}) {
122
137
  throw new Error("advanceAudit external_analyzer_import_executor requires externalAnalyzerResults");
123
138
  run = runExternalAnalyzerImportExecutor(bundle, options.externalAnalyzerResults);
124
139
  break;
125
- case "auto_fix_executor":
126
- if (!options.root)
127
- throw new Error("advanceAudit auto_fix_executor requires root");
128
- run = runAutoFixExecutor(bundle, options.root);
140
+ case "auto_fix_executor": {
141
+ const root = requireRoot(options.root, "auto_fix_executor");
142
+ run = await runAutoFixExecutor(bundle, root);
129
143
  break;
130
- case "syntax_resolution_executor":
131
- if (!options.root)
132
- throw new Error("advanceAudit syntax_resolution_executor requires root");
133
- run = runSyntaxResolutionExecutor(bundle, options.root);
144
+ }
145
+ case "syntax_resolution_executor": {
146
+ const root = requireRoot(options.root, "syntax_resolution_executor");
147
+ run = runSyntaxResolutionExecutor(bundle, root);
134
148
  break;
149
+ }
135
150
  default: {
136
151
  const state = deriveAuditState(bundle);
137
152
  state.last_executor = selectedExecutor;
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- import { ARTIFACT_DEPENDENCY_MAP } from "./dependencyMap.js";
2
+ import { ARTIFACT_DEPENDENTS_MAP } from "./dependencyMap.js";
3
3
  export function stableStringify(value) {
4
4
  if (value === undefined) {
5
5
  return "null";
@@ -44,7 +44,7 @@ export function hashArtifactValue(artifactName, value) {
44
44
  }
45
45
  export function buildReverseDependencyMap() {
46
46
  const reverse = {};
47
- for (const [upstream, downstreamList] of Object.entries(ARTIFACT_DEPENDENCY_MAP)) {
47
+ for (const [upstream, downstreamList] of Object.entries(ARTIFACT_DEPENDENTS_MAP)) {
48
48
  reverse[upstream] ??= [];
49
49
  for (const downstream of downstreamList) {
50
50
  reverse[downstream] ??= [];
@@ -1,3 +1,3 @@
1
1
  import type { ArtifactBundle } from "../io/artifacts.js";
2
2
  import type { ExecutorRunResult } from "./executorResult.js";
3
- export declare function runAutoFixExecutor(bundle: ArtifactBundle, root: string): ExecutorRunResult;
3
+ export declare function runAutoFixExecutor(bundle: ArtifactBundle, root: string): Promise<ExecutorRunResult>;
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync } from "node:fs";
1
+ import { access, readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { isAuditExcludedStatus } from "../extractors/disposition.js";
4
4
  import { resolveNodeTool, runFirstAvailableCommand, } from "./localCommands.js";
@@ -19,23 +19,31 @@ const PRETTIER_CONFIG_FILES = [
19
19
  "prettier.config.cjs",
20
20
  "prettier.config.mjs",
21
21
  ];
22
- function hasPrettierConfig(root) {
23
- if (PRETTIER_CONFIG_FILES.some((file) => existsSync(join(root, file)))) {
22
+ async function pathExists(target) {
23
+ try {
24
+ await access(target);
24
25
  return true;
25
26
  }
26
- const packageJsonPath = join(root, "package.json");
27
- if (!existsSync(packageJsonPath)) {
27
+ catch {
28
28
  return false;
29
29
  }
30
+ }
31
+ async function hasPrettierConfig(root) {
32
+ const configChecks = await Promise.all(PRETTIER_CONFIG_FILES.map((file) => pathExists(join(root, file))));
33
+ if (configChecks.some(Boolean)) {
34
+ return true;
35
+ }
36
+ const packageJsonPath = join(root, "package.json");
30
37
  try {
31
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
38
+ const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
32
39
  return packageJson.prettier !== undefined;
33
40
  }
34
41
  catch {
42
+ // Missing package.json or unparseable JSON => no prettier config.
35
43
  return false;
36
44
  }
37
45
  }
38
- export function runAutoFixExecutor(bundle, root) {
46
+ export async function runAutoFixExecutor(bundle, root) {
39
47
  if (!bundle.file_disposition) {
40
48
  throw new Error("Cannot run auto fix executor without file_disposition");
41
49
  }
@@ -51,7 +59,7 @@ export function runAutoFixExecutor(bundle, root) {
51
59
  const executedTools = [];
52
60
  const toolTimings = [];
53
61
  // JS, TS, HTML, CSS, JSON, YAML, MD
54
- if (hasPrettierConfig(root) &&
62
+ if ((await hasPrettierConfig(root)) &&
55
63
  (extensions.has("ts") ||
56
64
  extensions.has("js") ||
57
65
  extensions.has("tsx") ||
@@ -1 +1 @@
1
- export declare const ARTIFACT_DEPENDENCY_MAP: Record<string, string[]>;
1
+ export declare const ARTIFACT_DEPENDENTS_MAP: Record<string, string[]>;
@@ -1,4 +1,10 @@
1
- export const ARTIFACT_DEPENDENCY_MAP = {
1
+ // Invalidation map keyed by UPSTREAM artifact → the list of DOWNSTREAM
2
+ // artifacts that depend on it (and so become stale when it changes). The name
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
5
+ // computeArtifactMetadata. (Renamed from the misleading ARTIFACT_DEPENDENCY_MAP,
6
+ // which read as "X's dependencies" — the opposite of what it stores.)
7
+ export const ARTIFACT_DEPENDENTS_MAP = {
2
8
  "tooling_manifest.json": [
3
9
  "repo_manifest.json",
4
10
  ],
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Graph buckets in `graph_bundle.json` that carry file-to-file edges relevant to
3
+ * large-file anchoring. Named here (rather than inlined as bare strings in the
4
+ * collection loop) so the set of scanned buckets is a single typed source of
5
+ * truth and each bucket key doubles as a typed fallback edge `kind`. `as const`
6
+ * narrows the element type from `string` to the literal union.
7
+ */
8
+ const GRAPH_EDGE_BUCKETS = ["imports", "calls", "references"];
1
9
  const MAX_ANCHORS = 160;
2
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;
3
11
  const SYMBOL_PATTERNS = [
@@ -85,8 +93,11 @@ function collectGraphEdges(graphBundle, path) {
85
93
  }
86
94
  const normalizedPath = normalizePath(path).toLowerCase();
87
95
  const edges = [];
88
- for (const key of ["imports", "calls", "references"]) {
89
- const raw = graphBundle.graphs[key];
96
+ // Typed as `string` (not the literal union) so the lookup resolves through the
97
+ // `[key: string]: unknown` index signature on `graphs` — the loop body then
98
+ // re-validates each entry's shape, matching the original deterministic parse.
99
+ for (const bucket of GRAPH_EDGE_BUCKETS) {
100
+ const raw = graphBundle.graphs[bucket];
90
101
  if (!Array.isArray(raw)) {
91
102
  continue;
92
103
  }
@@ -103,7 +114,7 @@ function collectGraphEdges(graphBundle, path) {
103
114
  edges.push({
104
115
  from: record.from,
105
116
  to: record.to,
106
- kind: typeof record.kind === "string" ? record.kind : key,
117
+ kind: typeof record.kind === "string" ? record.kind : bucket,
107
118
  });
108
119
  }
109
120
  }
@@ -6,6 +6,7 @@ function lensSetForFlow(concerns) {
6
6
  "data_integrity",
7
7
  "operability",
8
8
  "performance",
9
+ "observability",
9
10
  ];
10
11
  return concerns.filter((concern) => allowed.includes(concern));
11
12
  }
@@ -45,8 +45,11 @@ export function buildFlowRequeueTasks(criticalFlows, flowCoverage, coverageMatri
45
45
  ? flow.paths.filter((path) => typeof path === "string")
46
46
  : [];
47
47
  for (const lensName of missingLenses) {
48
+ // Skip (rather than throw on) an unsupported lens value, consistent with
49
+ // the filter-based lens guards elsewhere in the orchestrator: a stray
50
+ // non-canonical lens in flow coverage should not abort the whole requeue.
48
51
  if (!isLens(lensName)) {
49
- throw new Error(`buildFlowRequeueTasks encountered unsupported lens "${String(lensName)}" for flow ${record.flow_id}.`);
52
+ continue;
50
53
  }
51
54
  for (const path of flowPaths) {
52
55
  if (!fileStillNeedsLens(coverageByPath, path, lensName)) {
@@ -2,13 +2,7 @@ import type { ArtifactBundle } from "../io/artifacts.js";
2
2
  import type { AuditResult } from "../types.js";
3
3
  import type { RuntimeValidationReport } from "../types/runtimeValidation.js";
4
4
  import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
5
- import type { AuditScopeManifest } from "../types/auditScope.js";
6
5
  import type { ExecutorRunResult } from "./executorResult.js";
7
- export declare function runIntakeExecutor(bundle: ArtifactBundle, root: string): Promise<ExecutorRunResult>;
8
- export declare function runStructureExecutor(bundle: ArtifactBundle, root?: string): Promise<ExecutorRunResult>;
9
- export declare function runDesignAssessmentExecutor(bundle: ArtifactBundle): ExecutorRunResult;
10
- export declare function runDesignReviewAutoComplete(bundle: ArtifactBundle): ExecutorRunResult;
11
- export declare function runPlanningExecutor(bundle: ArtifactBundle, root: string, lineIndex?: Record<string, number>, sizeIndex?: Record<string, number>, scope?: AuditScopeManifest): Promise<ExecutorRunResult>;
12
6
  export declare function runResultIngestionExecutor(bundle: ArtifactBundle, results: AuditResult[]): ExecutorRunResult;
13
7
  export declare function runRuntimeValidationExecutor(bundle: ArtifactBundle, root: string, options?: {
14
8
  opentoken?: boolean;
@@ -0,0 +1,237 @@
1
+ import { runCommand } from "./runtimeCommand.js";
2
+ import { buildFlowCoverage } from "./flowCoverage.js";
3
+ import { buildRequeuePayload } from "./requeueCommand.js";
4
+ import { buildRuntimeValidationTasks, mergeRuntimeValidationReport, } from "./runtimeValidation.js";
5
+ import { ingestAuditResults, updateAuditTaskStatuses, } from "./resultIngestion.js";
6
+ import { buildAuditPlanMetrics, buildReviewPackets, sizeIndexFromManifest, } from "./reviewPackets.js";
7
+ import { updateRuntimeValidationReport } from "./runtimeValidationUpdate.js";
8
+ import { buildSelectiveDeepeningTasks } from "./selectiveDeepening.js";
9
+ function lineIndexFromTasks(tasks) {
10
+ return Object.fromEntries((tasks ?? []).flatMap((task) => Object.entries(task.file_line_counts ?? {})));
11
+ }
12
+ function appendSelectiveDeepeningTasks(params) {
13
+ if (!params.bundle.audit_tasks) {
14
+ return { bundle: params.bundle, taskCount: 0, artifacts: [] };
15
+ }
16
+ const lineIndex = lineIndexFromTasks(params.bundle.audit_tasks);
17
+ const sizeIndex = sizeIndexFromManifest(params.bundle.repo_manifest);
18
+ const selectiveDeepeningTasks = buildSelectiveDeepeningTasks({
19
+ existingTasks: params.bundle.audit_tasks,
20
+ results: params.results,
21
+ lineIndex,
22
+ runtimeValidationTasks: params.bundle.runtime_validation_tasks,
23
+ runtimeValidationReport: params.runtimeValidationReport ?? params.bundle.runtime_validation_report,
24
+ externalAnalyzerResults: params.bundle.external_analyzer_results,
25
+ });
26
+ if (selectiveDeepeningTasks.length === 0) {
27
+ return { bundle: params.bundle, taskCount: 0, artifacts: [] };
28
+ }
29
+ const auditTasks = [...params.bundle.audit_tasks, ...selectiveDeepeningTasks];
30
+ return {
31
+ bundle: {
32
+ ...params.bundle,
33
+ audit_tasks: auditTasks,
34
+ audit_plan_metrics: buildAuditPlanMetrics(auditTasks, {
35
+ graphBundle: params.bundle.graph_bundle,
36
+ lineIndex,
37
+ sizeIndex,
38
+ }),
39
+ review_packets: buildReviewPackets(auditTasks, {
40
+ graphBundle: params.bundle.graph_bundle,
41
+ lineIndex,
42
+ sizeIndex,
43
+ }),
44
+ },
45
+ taskCount: selectiveDeepeningTasks.length,
46
+ artifacts: ["audit_tasks.json", "audit_plan_metrics.json", "review_packets.json"],
47
+ };
48
+ }
49
+ /**
50
+ * Apply selective deepening to an already-prepared base bundle and return the
51
+ * pieces every executor needs to assemble its `ExecutorRunResult`: the updated
52
+ * bundle, the deepening artifacts, and the progress-summary suffix.
53
+ *
54
+ * Centralizing this keeps the three executors that run deepening consistent —
55
+ * previously each site invoked `appendSelectiveDeepeningTasks` and then
56
+ * re-derived the same `Added N selective deepening task(s)` suffix by hand.
57
+ * `excludeArtifacts` lets a caller that already lists an artifact explicitly
58
+ * (the result-ingestion executor lists `audit_tasks.json`) drop it from the
59
+ * spread so it is never double-counted.
60
+ */
61
+ function applySelectiveDeepening(params) {
62
+ const selectiveDeepening = appendSelectiveDeepeningTasks({
63
+ bundle: params.baseBundle,
64
+ results: params.results,
65
+ runtimeValidationReport: params.runtimeValidationReport,
66
+ });
67
+ const exclude = new Set(params.excludeArtifacts ?? []);
68
+ return {
69
+ bundle: selectiveDeepening.bundle,
70
+ artifacts: selectiveDeepening.artifacts.filter((artifact) => !exclude.has(artifact)),
71
+ summarySuffix: selectiveDeepening.taskCount > 0
72
+ ? ` Added ${selectiveDeepening.taskCount} selective deepening task(s).`
73
+ : "",
74
+ };
75
+ }
76
+ export function runResultIngestionExecutor(bundle, results) {
77
+ if (!bundle.coverage_matrix) {
78
+ throw new Error("Cannot ingest results without coverage_matrix");
79
+ }
80
+ const updatedCoverageMatrix = ingestAuditResults(bundle.coverage_matrix, results);
81
+ const flowCoverage = bundle.critical_flows
82
+ ? buildFlowCoverage(bundle.critical_flows, updatedCoverageMatrix)
83
+ : bundle.flow_coverage;
84
+ const runtimeCommand = bundle.runtime_validation_tasks?.tasks.find((task) => task.command && task.command.length > 0)?.command;
85
+ const runtimeValidationTasks = bundle.unit_manifest && flowCoverage
86
+ ? buildRuntimeValidationTasks({
87
+ unitManifest: bundle.unit_manifest,
88
+ criticalFlows: bundle.critical_flows,
89
+ flowCoverage,
90
+ command: runtimeCommand,
91
+ })
92
+ : bundle.runtime_validation_tasks;
93
+ const runtimeValidationReport = runtimeValidationTasks
94
+ ? mergeRuntimeValidationReport(runtimeValidationTasks, bundle.runtime_validation_report)
95
+ : bundle.runtime_validation_report;
96
+ const mergedResults = [...(bundle.audit_results ?? []), ...results];
97
+ const completedAuditTasks = updateAuditTaskStatuses(bundle.audit_tasks, mergedResults);
98
+ const baseUpdatedBundle = {
99
+ ...bundle,
100
+ coverage_matrix: updatedCoverageMatrix,
101
+ flow_coverage: flowCoverage,
102
+ runtime_validation_tasks: runtimeValidationTasks,
103
+ runtime_validation_report: runtimeValidationReport,
104
+ audit_results: mergedResults,
105
+ audit_tasks: completedAuditTasks,
106
+ audit_report: undefined,
107
+ };
108
+ const selectiveDeepening = applySelectiveDeepening({
109
+ baseBundle: baseUpdatedBundle,
110
+ results: mergedResults,
111
+ runtimeValidationReport,
112
+ excludeArtifacts: ["audit_tasks.json"],
113
+ });
114
+ const requeuePayload = buildRequeuePayload(updatedCoverageMatrix, selectiveDeepening.bundle.critical_flows, selectiveDeepening.bundle.flow_coverage, selectiveDeepening.bundle.external_analyzer_results);
115
+ const finalBundle = {
116
+ ...selectiveDeepening.bundle,
117
+ requeue_tasks: requeuePayload.tasks,
118
+ };
119
+ return {
120
+ updated: finalBundle,
121
+ artifacts_written: [
122
+ "coverage_matrix.json",
123
+ "flow_coverage.json",
124
+ ...(runtimeValidationTasks ? ["runtime_validation_tasks.json"] : []),
125
+ ...(runtimeValidationReport ? ["runtime_validation_report.json"] : []),
126
+ "audit_results.jsonl",
127
+ "audit_tasks.json",
128
+ ...selectiveDeepening.artifacts,
129
+ "requeue_tasks.json",
130
+ ],
131
+ progress_summary: `Ingested ${results.length} audit result entries and refreshed dependent artifacts.` +
132
+ selectiveDeepening.summarySuffix,
133
+ };
134
+ }
135
+ export async function runRuntimeValidationExecutor(bundle, root, options = {}) {
136
+ if (!bundle.runtime_validation_tasks) {
137
+ throw new Error("Cannot execute runtime validation without runtime_validation_tasks");
138
+ }
139
+ const existing = bundle.runtime_validation_report ?? { results: [] };
140
+ const byTaskId = new Map(existing.results.map((result) => [result.task_id, result]));
141
+ const byCommand = new Map();
142
+ for (const task of bundle.runtime_validation_tasks.tasks) {
143
+ const prior = byTaskId.get(task.id);
144
+ if (prior &&
145
+ ["confirmed", "not_confirmed", "inconclusive", "not_required"].includes(prior.status)) {
146
+ continue;
147
+ }
148
+ if (!task.command || task.command.length === 0) {
149
+ byTaskId.set(task.id, {
150
+ task_id: task.id,
151
+ status: "not_required",
152
+ summary: `No deterministic runtime command was available for ${task.id}.`,
153
+ evidence: [],
154
+ notes: ["Runtime validation was not planned for this task."],
155
+ });
156
+ continue;
157
+ }
158
+ const signature = task.command.join("\0");
159
+ const outcome = byCommand.get(signature) ?? (await runCommand(task.command, root, { opentoken: options.opentoken }));
160
+ byCommand.set(signature, outcome);
161
+ byTaskId.set(task.id, {
162
+ task_id: task.id,
163
+ status: outcome.status,
164
+ summary: outcome.summary,
165
+ evidence: outcome.evidence,
166
+ notes: [`Target paths: ${task.target_paths.join(", ")}`],
167
+ });
168
+ }
169
+ const runtimeValidationReport = {
170
+ results: [...byTaskId.values()].sort((a, b) => a.task_id.localeCompare(b.task_id)),
171
+ };
172
+ const baseUpdatedBundle = {
173
+ ...bundle,
174
+ runtime_validation_report: runtimeValidationReport,
175
+ audit_report: undefined,
176
+ };
177
+ const selectiveDeepening = applySelectiveDeepening({
178
+ baseBundle: baseUpdatedBundle,
179
+ results: bundle.audit_results ?? [],
180
+ runtimeValidationReport,
181
+ });
182
+ return {
183
+ updated: selectiveDeepening.bundle,
184
+ artifacts_written: [
185
+ "runtime_validation_report.json",
186
+ ...selectiveDeepening.artifacts,
187
+ ],
188
+ progress_summary: `Executed deterministic runtime validation for ${bundle.runtime_validation_tasks.tasks.length} task(s).` +
189
+ selectiveDeepening.summarySuffix,
190
+ };
191
+ }
192
+ export function runRuntimeValidationUpdateExecutor(bundle, updates) {
193
+ if (!bundle.runtime_validation_tasks) {
194
+ throw new Error("Cannot update runtime validation without runtime_validation_tasks");
195
+ }
196
+ const existingReport = bundle.runtime_validation_report ?? { results: [] };
197
+ const mergedReport = updateRuntimeValidationReport(bundle.runtime_validation_tasks, existingReport, updates);
198
+ const baseUpdatedBundle = {
199
+ ...bundle,
200
+ runtime_validation_report: mergedReport,
201
+ audit_report: undefined,
202
+ };
203
+ const selectiveDeepening = applySelectiveDeepening({
204
+ baseBundle: baseUpdatedBundle,
205
+ results: bundle.audit_results ?? [],
206
+ runtimeValidationReport: mergedReport,
207
+ });
208
+ return {
209
+ updated: selectiveDeepening.bundle,
210
+ artifacts_written: [
211
+ "runtime_validation_report.json",
212
+ ...selectiveDeepening.artifacts,
213
+ ],
214
+ progress_summary: `Merged ${updates.results.length} runtime validation updates.` +
215
+ selectiveDeepening.summarySuffix,
216
+ };
217
+ }
218
+ export function runExternalAnalyzerImportExecutor(bundle, externalResults) {
219
+ const summary = `Imported ${externalResults.results.length} normalized findings from ${externalResults.tool}.`;
220
+ return {
221
+ updated: {
222
+ ...bundle,
223
+ external_analyzer_results: externalResults,
224
+ coverage_matrix: undefined,
225
+ flow_coverage: undefined,
226
+ runtime_validation_tasks: undefined,
227
+ runtime_validation_report: undefined,
228
+ audit_tasks: undefined,
229
+ audit_plan_metrics: undefined,
230
+ review_packets: undefined,
231
+ requeue_tasks: undefined,
232
+ audit_report: undefined,
233
+ },
234
+ artifacts_written: ["external_analyzer_results.json"],
235
+ progress_summary: summary,
236
+ };
237
+ }
@@ -0,0 +1,3 @@
1
+ import type { ArtifactBundle } from "../io/artifacts.js";
2
+ import type { ExecutorRunResult } from "./executorResult.js";
3
+ export declare function runIntakeExecutor(bundle: ArtifactBundle, root: string): Promise<ExecutorRunResult>;
@@ -0,0 +1,25 @@
1
+ import { buildFileDisposition, isAuditExcludedStatus, } from "../extractors/disposition.js";
2
+ import { buildRepoManifestFromFs } from "../extractors/fsIntake.js";
3
+ import { loadIgnoreFile } from "../extractors/ignore.js";
4
+ export async function runIntakeExecutor(bundle, root) {
5
+ const ignore = await loadIgnoreFile(root);
6
+ const repoManifest = await buildRepoManifestFromFs({
7
+ root,
8
+ ignore,
9
+ hash_files: true,
10
+ });
11
+ const disposition = buildFileDisposition(repoManifest);
12
+ const auditableCount = disposition.files.filter((file) => !isAuditExcludedStatus(file.status)).length;
13
+ if (auditableCount === 0) {
14
+ throw new Error(`No auditable files found in ${root}. The repository may be empty, generated-only, documentation-only, or filtered by .auditorignore.`);
15
+ }
16
+ return {
17
+ updated: {
18
+ ...bundle,
19
+ repo_manifest: repoManifest,
20
+ file_disposition: disposition,
21
+ },
22
+ artifacts_written: ["repo_manifest.json", "file_disposition.json"],
23
+ progress_summary: `Created intake artifacts for ${repoManifest.files.length} files.`,
24
+ };
25
+ }
@@ -0,0 +1,4 @@
1
+ import type { ArtifactBundle } from "../io/artifacts.js";
2
+ import type { AuditScopeManifest } from "../types/auditScope.js";
3
+ import type { ExecutorRunResult } from "./executorResult.js";
4
+ export declare function runPlanningExecutor(bundle: ArtifactBundle, root: string, lineIndex?: Record<string, number>, sizeIndex?: Record<string, number>, scope?: AuditScopeManifest): Promise<ExecutorRunResult>;