auditor-lambda 0.7.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 (125) hide show
  1. package/README.md +0 -21
  2. package/audit-code-wrapper-lib.mjs +149 -129
  3. package/dist/adapters/normalizeExternal.js +6 -3
  4. package/dist/cli/args.d.ts +0 -1
  5. package/dist/cli/args.js +0 -6
  6. package/dist/cli/auditStep.js +7 -1
  7. package/dist/cli/dispatch.js +3 -2
  8. package/dist/cli/lineIndex.js +4 -1
  9. package/dist/cli/mergeAndIngestCommand.d.ts +1 -0
  10. package/dist/cli/mergeAndIngestCommand.js +219 -0
  11. package/dist/cli/nextStepCommand.js +5 -1
  12. package/dist/cli/runToCompletion.d.ts +9 -0
  13. package/dist/cli/runToCompletion.js +655 -480
  14. package/dist/cli/statusCommand.d.ts +1 -0
  15. package/dist/cli/statusCommand.js +113 -0
  16. package/dist/cli/submitPacketCommand.d.ts +1 -0
  17. package/dist/cli/submitPacketCommand.js +155 -0
  18. package/dist/cli/workerResult.d.ts +1 -1
  19. package/dist/cli/workerRunCommand.d.ts +1 -0
  20. package/dist/cli/workerRunCommand.js +88 -0
  21. package/dist/cli.d.ts +0 -1
  22. package/dist/cli.js +14 -565
  23. package/dist/extractors/analyzers/sql.js +4 -1
  24. package/dist/extractors/analyzers/treeSitter.js +29 -15
  25. package/dist/extractors/analyzers/typescript.js +10 -8
  26. package/dist/extractors/designAssessment.js +43 -24
  27. package/dist/extractors/graph.js +151 -75
  28. package/dist/extractors/pathPatterns.js +17 -5
  29. package/dist/io/artifacts.d.ts +3 -1
  30. package/dist/io/artifacts.js +18 -2
  31. package/dist/io/runArtifactTypes.d.ts +18 -0
  32. package/dist/io/runArtifactTypes.js +1 -0
  33. package/dist/io/runArtifacts.d.ts +2 -18
  34. package/dist/io/runArtifacts.js +14 -3
  35. package/dist/mcp/server.js +9 -0
  36. package/dist/orchestrator/advance.js +38 -22
  37. package/dist/orchestrator/artifactFreshness.js +14 -4
  38. package/dist/orchestrator/autoFixExecutor.d.ts +2 -2
  39. package/dist/orchestrator/autoFixExecutor.js +26 -8
  40. package/dist/orchestrator/dependencyMap.d.ts +1 -1
  41. package/dist/orchestrator/dependencyMap.js +7 -1
  42. package/dist/orchestrator/executorResult.d.ts +12 -0
  43. package/dist/orchestrator/executorResult.js +1 -0
  44. package/dist/orchestrator/fileAnchors.js +14 -3
  45. package/dist/orchestrator/fileIntegrity.d.ts +1 -0
  46. package/dist/orchestrator/fileIntegrity.js +12 -3
  47. package/dist/orchestrator/flowCoverage.js +1 -0
  48. package/dist/orchestrator/flowRequeue.js +4 -1
  49. package/dist/orchestrator/graphEnrichmentExecutor.d.ts +1 -1
  50. package/dist/orchestrator/graphEnrichmentExecutor.js +3 -1
  51. package/dist/orchestrator/ingestionExecutors.d.ts +11 -0
  52. package/dist/orchestrator/ingestionExecutors.js +237 -0
  53. package/dist/orchestrator/intakeExecutors.d.ts +3 -0
  54. package/dist/orchestrator/intakeExecutors.js +25 -0
  55. package/dist/orchestrator/planningExecutors.d.ts +4 -0
  56. package/dist/orchestrator/planningExecutors.js +95 -0
  57. package/dist/orchestrator/reviewPacketGraph.d.ts +31 -0
  58. package/dist/orchestrator/reviewPacketGraph.js +691 -0
  59. package/dist/orchestrator/reviewPackets.d.ts +2 -15
  60. package/dist/orchestrator/reviewPackets.js +3 -685
  61. package/dist/orchestrator/runtimeCommand.d.ts +11 -0
  62. package/dist/orchestrator/runtimeCommand.js +71 -0
  63. package/dist/orchestrator/scope.js +1 -1
  64. package/dist/orchestrator/selectiveDeepening/conflict.d.ts +8 -0
  65. package/dist/orchestrator/selectiveDeepening/conflict.js +71 -0
  66. package/dist/orchestrator/selectiveDeepening/findingFollowup.d.ts +10 -0
  67. package/dist/orchestrator/selectiveDeepening/findingFollowup.js +52 -0
  68. package/dist/orchestrator/selectiveDeepening/highRiskClean.d.ts +7 -0
  69. package/dist/orchestrator/selectiveDeepening/highRiskClean.js +44 -0
  70. package/dist/orchestrator/selectiveDeepening/index.d.ts +18 -0
  71. package/dist/orchestrator/selectiveDeepening/index.js +128 -0
  72. package/dist/orchestrator/selectiveDeepening/lensVerification.d.ts +12 -0
  73. package/dist/orchestrator/selectiveDeepening/lensVerification.js +242 -0
  74. package/dist/orchestrator/selectiveDeepening/runtimeValidation.d.ts +13 -0
  75. package/dist/orchestrator/selectiveDeepening/runtimeValidation.js +57 -0
  76. package/dist/orchestrator/selectiveDeepening/shared.d.ts +45 -0
  77. package/dist/orchestrator/selectiveDeepening/shared.js +128 -0
  78. package/dist/orchestrator/selectiveDeepening/stewardFollowup.d.ts +6 -0
  79. package/dist/orchestrator/selectiveDeepening/stewardFollowup.js +72 -0
  80. package/dist/orchestrator/selectiveDeepening.d.ts +2 -20
  81. package/dist/orchestrator/selectiveDeepening.js +6 -760
  82. package/dist/orchestrator/staleness.js +3 -3
  83. package/dist/orchestrator/structureExecutors.d.ts +5 -0
  84. package/dist/orchestrator/structureExecutors.js +94 -0
  85. package/dist/orchestrator/syntaxResolutionExecutor.d.ts +1 -1
  86. package/dist/orchestrator/synthesisExecutors.d.ts +12 -0
  87. package/dist/orchestrator/synthesisExecutors.js +90 -0
  88. package/dist/orchestrator/taskBuilder.d.ts +2 -2
  89. package/dist/orchestrator/taskBuilder.js +101 -82
  90. package/dist/providers/index.d.ts +7 -0
  91. package/dist/providers/index.js +14 -95
  92. package/dist/quota/discoveredLimits.d.ts +1 -0
  93. package/dist/quota/discoveredLimits.js +7 -1
  94. package/dist/quota/index.d.ts +0 -2
  95. package/dist/quota/index.js +1 -2
  96. package/dist/reporting/workBlocks.js +7 -4
  97. package/dist/types/reviewPlanning.d.ts +23 -16
  98. package/dist/validation/auditResults.js +97 -95
  99. package/dist/validation/sessionConfig.d.ts +2 -2
  100. package/dist/validation/sessionConfig.js +14 -7
  101. package/docs/development.md +35 -139
  102. package/docs/history.md +26 -0
  103. package/docs/product.md +41 -108
  104. package/package.json +3 -2
  105. package/schemas/audit_findings.schema.json +6 -5
  106. package/schemas/critical_flows.schema.json +3 -2
  107. package/schemas/dispatch_quota.schema.json +3 -1
  108. package/schemas/external_analyzer_results.schema.json +2 -2
  109. package/schemas/graph_bundle.schema.json +1 -1
  110. package/schemas/repo_manifest.schema.json +1 -1
  111. package/schemas/review_packets.schema.json +1 -1
  112. package/schemas/step_contract.schema.json +80 -0
  113. package/scripts/postinstall.mjs +19 -2
  114. package/skills/audit-code/opencode-command-template.txt +3 -3
  115. package/dist/orchestrator/internalExecutors.d.ts +0 -34
  116. package/dist/orchestrator/internalExecutors.js +0 -581
  117. package/dist/providers/localSubprocessProvider.d.ts +0 -9
  118. package/dist/providers/localSubprocessProvider.js +0 -18
  119. package/dist/providers/subprocessTemplateProvider.d.ts +0 -8
  120. package/dist/providers/subprocessTemplateProvider.js +0 -59
  121. package/dist/providers/vscodeTaskProvider.d.ts +0 -7
  122. package/dist/providers/vscodeTaskProvider.js +0 -14
  123. package/dist/quota/probe.d.ts +0 -10
  124. package/dist/quota/probe.js +0 -18
  125. package/docs/handoff.md +0 -204
@@ -79,13 +79,29 @@ export async function loadArtifactBundle(root) {
79
79
  bundle.tooling_manifest = await buildToolingManifest();
80
80
  return bundle;
81
81
  }
82
- export async function writeCoreArtifacts(root, bundle) {
82
+ export async function writeCoreArtifacts(root, bundle, options = {}) {
83
83
  const bundleRecord = bundle;
84
84
  for (const entry of ARTIFACT_ENTRIES) {
85
85
  const [key, definition] = entry;
86
86
  const value = bundleRecord[key];
87
+ const path = join(root, definition.fileName);
87
88
  if (value !== undefined) {
88
- await definition.write(join(root, definition.fileName), value);
89
+ await definition.write(path, value);
90
+ }
91
+ else if (options.prune) {
92
+ // The bundle is authoritative. An executor that clears an artifact to
93
+ // `undefined` (to force a downstream rebuild — e.g. planning/ingestion
94
+ // reset audit_report) intends the file gone; if it lingers it reloads as a
95
+ // stale "present" artifact with no metadata entry, which deriveAuditState
96
+ // reads as satisfied — masking the invalidation and stranding a stale
97
+ // report. Only callers passing the full accumulated bundle may prune.
98
+ try {
99
+ await unlink(path);
100
+ }
101
+ catch (error) {
102
+ if (!isFileMissingError(error))
103
+ throw error;
104
+ }
89
105
  }
90
106
  }
91
107
  }
@@ -0,0 +1,18 @@
1
+ export interface RunPaths {
2
+ runDir: string;
3
+ taskPath: string;
4
+ promptPath: string;
5
+ resultPath: string;
6
+ stdoutPath: string;
7
+ stderrPath: string;
8
+ statusPath: string;
9
+ }
10
+ export interface DispatchBatchRun {
11
+ run_id: string;
12
+ task_path: string;
13
+ prompt_path: string;
14
+ result_path: string;
15
+ status_path: string;
16
+ audit_results_path?: string;
17
+ pending_audit_tasks_path?: string;
18
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,23 +1,7 @@
1
1
  import type { AuditTask } from "../types.js";
2
2
  import type { WorkerTask } from "../types/workerSession.js";
3
- export interface RunPaths {
4
- runDir: string;
5
- taskPath: string;
6
- promptPath: string;
7
- resultPath: string;
8
- stdoutPath: string;
9
- stderrPath: string;
10
- statusPath: string;
11
- }
12
- export interface DispatchBatchRun {
13
- run_id: string;
14
- task_path: string;
15
- prompt_path: string;
16
- result_path: string;
17
- status_path: string;
18
- audit_results_path?: string;
19
- pending_audit_tasks_path?: string;
20
- }
3
+ import type { RunPaths, DispatchBatchRun } from "./runArtifactTypes.js";
4
+ export type { RunPaths, DispatchBatchRun } from "./runArtifactTypes.js";
21
5
  export declare function buildRunId(obligationId: string | null, index: number, now?: Date): string;
22
6
  export declare function getRunPaths(artifactsDir: string, runId: string): RunPaths;
23
7
  export declare function ensureSupervisorDirs(artifactsDir: string): Promise<void>;
@@ -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,12 +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, runSynthesisExecutor, runSynthesisNarrativeExecutor, 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";
8
+ import { runSynthesisExecutor, runSynthesisNarrativeExecutor, } from "./synthesisExecutors.js";
5
9
  import { runAutoFixExecutor } from "./autoFixExecutor.js";
6
10
  import { runSyntaxResolutionExecutor } from "./syntaxResolutionExecutor.js";
7
11
  import { runGraphEnrichmentExecutor } from "./graphEnrichmentExecutor.js";
8
12
  import { resolveAuditScope } from "./scope.js";
9
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
+ }
10
26
  function cloneState(state) {
11
27
  return {
12
28
  ...state,
@@ -63,11 +79,11 @@ export async function advanceAudit(bundle, options = {}) {
63
79
  });
64
80
  try {
65
81
  switch (selectedExecutor) {
66
- case "intake_executor":
67
- if (!options.root)
68
- throw new Error("advanceAudit intake_executor requires root");
69
- run = await runIntakeExecutor(bundle, options.root);
82
+ case "intake_executor": {
83
+ const root = requireRoot(options.root, "intake_executor");
84
+ run = await runIntakeExecutor(bundle, root);
70
85
  break;
86
+ }
71
87
  case "structure_executor":
72
88
  run = await runStructureExecutor(bundle, options.root);
73
89
  break;
@@ -85,26 +101,26 @@ export async function advanceAudit(bundle, options = {}) {
85
101
  case "design_review":
86
102
  run = runDesignReviewAutoComplete(bundle);
87
103
  break;
88
- case "planning_executor":
89
- if (!options.root)
90
- throw new Error("advanceAudit planning_executor requires root");
104
+ case "planning_executor": {
105
+ const root = requireRoot(options.root, "planning_executor");
91
106
  plannedScope = resolveAuditScope({
92
- root: options.root,
107
+ root,
93
108
  since: options.since,
94
109
  bundle,
95
110
  });
96
- run = await runPlanningExecutor(bundle, options.root, options.lineIndex ?? {}, options.sizeIndex, plannedScope);
111
+ run = await runPlanningExecutor(bundle, root, options.lineIndex ?? {}, options.sizeIndex, plannedScope);
97
112
  break;
113
+ }
98
114
  case "result_ingestion_executor":
99
115
  run = runResultIngestionExecutor(bundle, options.auditResults ?? bundle.audit_results ?? []);
100
116
  break;
101
- case "runtime_validation_executor":
102
- if (!options.root)
103
- throw new Error("advanceAudit runtime_validation_executor requires root");
104
- 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, {
105
120
  opentoken: options.opentoken,
106
121
  });
107
122
  break;
123
+ }
108
124
  case "synthesis_executor":
109
125
  run = runSynthesisExecutor(bundle, options.auditResults);
110
126
  break;
@@ -121,16 +137,16 @@ export async function advanceAudit(bundle, options = {}) {
121
137
  throw new Error("advanceAudit external_analyzer_import_executor requires externalAnalyzerResults");
122
138
  run = runExternalAnalyzerImportExecutor(bundle, options.externalAnalyzerResults);
123
139
  break;
124
- case "auto_fix_executor":
125
- if (!options.root)
126
- throw new Error("advanceAudit auto_fix_executor requires root");
127
- 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);
128
143
  break;
129
- case "syntax_resolution_executor":
130
- if (!options.root)
131
- throw new Error("advanceAudit syntax_resolution_executor requires root");
132
- 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);
133
148
  break;
149
+ }
134
150
  default: {
135
151
  const state = deriveAuditState(bundle);
136
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";
@@ -15,9 +15,19 @@ export function stableStringify(value) {
15
15
  .sort(([a], [b]) => a.localeCompare(b));
16
16
  return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(",")}}`;
17
17
  }
18
+ // Artifacts that stamp a wall-clock `generated_at` on every (re)build. The
19
+ // timestamp is provenance, not content: two rebuilds with identical data but
20
+ // different timestamps must hash equal, or the artifact's revision churns every
21
+ // rebuild and perpetually re-stales its downstreams (e.g. audit-report.md
22
+ // depends on design_assessment) — a finalization-oscillation hazard.
23
+ const GENERATED_AT_STRIPPED_ARTIFACTS = new Set([
24
+ "repo_manifest.json",
25
+ "tooling_manifest.json",
26
+ "audit_plan_metrics.json",
27
+ "design_assessment.json",
28
+ ]);
18
29
  export function normalizeForMetadataHash(artifactName, value) {
19
- if ((artifactName === "repo_manifest.json" ||
20
- artifactName === "tooling_manifest.json") &&
30
+ if (GENERATED_AT_STRIPPED_ARTIFACTS.has(artifactName) &&
21
31
  value &&
22
32
  typeof value === "object" &&
23
33
  !Array.isArray(value)) {
@@ -34,7 +44,7 @@ export function hashArtifactValue(artifactName, value) {
34
44
  }
35
45
  export function buildReverseDependencyMap() {
36
46
  const reverse = {};
37
- for (const [upstream, downstreamList] of Object.entries(ARTIFACT_DEPENDENCY_MAP)) {
47
+ for (const [upstream, downstreamList] of Object.entries(ARTIFACT_DEPENDENTS_MAP)) {
38
48
  reverse[upstream] ??= [];
39
49
  for (const downstream of downstreamList) {
40
50
  reverse[downstream] ??= [];
@@ -1,3 +1,3 @@
1
1
  import type { ArtifactBundle } from "../io/artifacts.js";
2
- import type { ExecutorRunResult } from "./internalExecutors.js";
3
- export declare function runAutoFixExecutor(bundle: ArtifactBundle, root: string): ExecutorRunResult;
2
+ import type { ExecutorRunResult } from "./executorResult.js";
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
  }
@@ -49,8 +57,9 @@ export function runAutoFixExecutor(bundle, root) {
49
57
  }
50
58
  }
51
59
  const executedTools = [];
60
+ const toolTimings = [];
52
61
  // JS, TS, HTML, CSS, JSON, YAML, MD
53
- if (hasPrettierConfig(root) &&
62
+ if ((await hasPrettierConfig(root)) &&
54
63
  (extensions.has("ts") ||
55
64
  extensions.has("js") ||
56
65
  extensions.has("tsx") ||
@@ -61,16 +70,19 @@ export function runAutoFixExecutor(bundle, root) {
61
70
  extensions.has("yml") ||
62
71
  extensions.has("yaml") ||
63
72
  extensions.has("md"))) {
73
+ const prettierStart = Date.now();
64
74
  if (tryRunConfiguredFormatter(root, [
65
75
  ...resolveNodeTool(root, join("node_modules", "prettier", "bin", "prettier.cjs"), ["--write", "."], "prettier --write ."),
66
76
  { command: "prettier", args: ["--write", "."], display: "prettier --write ." },
67
77
  { command: "npx", args: ["--yes", "prettier", "--write", "."], display: "npx --yes prettier --write ." },
68
78
  ])) {
69
79
  executedTools.push("prettier");
80
+ toolTimings.push({ tool: "prettier", duration_ms: Date.now() - prettierStart });
70
81
  }
71
82
  }
72
83
  // Python
73
84
  if (extensions.has("py")) {
85
+ const blackStart = Date.now();
74
86
  if (tryRunConfiguredFormatter(root, [
75
87
  { command: "black", args: ["."], display: "black ." },
76
88
  { command: "python", args: ["-m", "black", "."], display: "python -m black ." },
@@ -78,28 +90,34 @@ export function runAutoFixExecutor(bundle, root) {
78
90
  { command: "pipx", args: ["run", "black", "."], display: "pipx run black ." },
79
91
  ])) {
80
92
  executedTools.push("black");
93
+ toolTimings.push({ tool: "black", duration_ms: Date.now() - blackStart });
81
94
  }
82
95
  }
83
96
  // SQL
84
97
  if (extensions.has("sql")) {
98
+ const sqlfluffStart = Date.now();
85
99
  if (tryRunConfiguredFormatter(root, [
86
100
  { command: "sqlfluff", args: ["fix", "--force", "."], display: "sqlfluff fix --force ." },
87
101
  { command: "uvx", args: ["sqlfluff", "fix", "--force", "."], display: "uvx sqlfluff fix --force ." },
88
102
  { command: "pipx", args: ["run", "sqlfluff", "fix", "--force", "."], display: "pipx run sqlfluff fix --force ." },
89
103
  ])) {
90
104
  executedTools.push("sqlfluff");
105
+ toolTimings.push({ tool: "sqlfluff", duration_ms: Date.now() - sqlfluffStart });
91
106
  }
92
107
  }
93
108
  // Go
94
109
  if (extensions.has("go")) {
110
+ const gofmtStart = Date.now();
95
111
  if (tryRunConfiguredFormatter(root, [
96
112
  { command: "gofmt", args: ["-w", "."], display: "gofmt -w ." },
97
113
  ])) {
98
114
  executedTools.push("gofmt");
115
+ toolTimings.push({ tool: "gofmt", duration_ms: Date.now() - gofmtStart });
99
116
  }
100
117
  }
101
118
  const resultsArtifact = {
102
119
  executed_tools: executedTools,
120
+ tool_timings: toolTimings,
103
121
  timestamp: new Date().toISOString(),
104
122
  };
105
123
  return {
@@ -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
  ],
@@ -0,0 +1,12 @@
1
+ import type { ArtifactBundle } from "../io/artifacts.js";
2
+ /**
3
+ * Uniform result of running one audit executor: the updated artifact bundle, the
4
+ * artifact filenames it wrote (which drive metadata/staleness bookkeeping in
5
+ * advanceAudit), and a one-line human progress summary. Shared by every executor
6
+ * module so they need not depend on the internalExecutors barrel.
7
+ */
8
+ export interface ExecutorRunResult {
9
+ updated: ArtifactBundle;
10
+ artifacts_written: string[];
11
+ progress_summary: string;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -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
  }
@@ -2,6 +2,7 @@ import type { RepoManifest } from "../types.js";
2
2
  export interface FileIntegrityResult {
3
3
  changed_files: string[];
4
4
  missing_files: string[];
5
+ io_errors: string[];
5
6
  is_clean: boolean;
6
7
  }
7
8
  export declare function checkFileIntegrity(root: string, manifest: RepoManifest, scope?: string[]): Promise<FileIntegrityResult>;
@@ -9,6 +9,7 @@ async function hashFile(absolutePath) {
9
9
  export async function checkFileIntegrity(root, manifest, scope) {
10
10
  const changed = [];
11
11
  const missing = [];
12
+ const ioErrors = [];
12
13
  const scopeSet = scope ? new Set(scope) : null;
13
14
  const files = scopeSet
14
15
  ? manifest.files.filter((f) => scopeSet.has(f.path))
@@ -29,13 +30,21 @@ export async function checkFileIntegrity(root, manifest, scope) {
29
30
  changed.push(record.path);
30
31
  }
31
32
  }
32
- catch {
33
- missing.push(record.path);
33
+ catch (err) {
34
+ const code = err.code;
35
+ if (code === "ENOENT") {
36
+ missing.push(record.path);
37
+ }
38
+ else {
39
+ console.warn(`fileIntegrity: I/O error on ${record.path}: ${code ?? String(err)}`);
40
+ ioErrors.push(record.path);
41
+ }
34
42
  }
35
43
  }
36
44
  return {
37
45
  changed_files: changed,
38
46
  missing_files: missing,
39
- is_clean: changed.length === 0 && missing.length === 0,
47
+ io_errors: ioErrors,
48
+ is_clean: changed.length === 0 && missing.length === 0 && ioErrors.length === 0,
40
49
  };
41
50
  }
@@ -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)) {
@@ -1,5 +1,5 @@
1
1
  import type { ArtifactBundle } from "../io/artifacts.js";
2
- import type { ExecutorRunResult } from "./internalExecutors.js";
2
+ import type { ExecutorRunResult } from "./executorResult.js";
3
3
  import type { AnalyzerSetting } from "@audit-tools/shared";
4
4
  import type { LanguageAnalyzer } from "../extractors/analyzers/types.js";
5
5
  import { type EdgeReasoningResults } from "./edgeReasoning.js";
@@ -133,7 +133,9 @@ export async function runGraphEnrichmentExecutor(bundle, options = {}) {
133
133
  setting,
134
134
  edges_added: 0,
135
135
  routes_added: 0,
136
- note: `Analyzer failed: ${error instanceof Error ? error.message : String(error)}.`,
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)}.`,
137
139
  });
138
140
  continue;
139
141
  }
@@ -0,0 +1,11 @@
1
+ import type { ArtifactBundle } from "../io/artifacts.js";
2
+ import type { AuditResult } from "../types.js";
3
+ import type { RuntimeValidationReport } from "../types/runtimeValidation.js";
4
+ import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
5
+ import type { ExecutorRunResult } from "./executorResult.js";
6
+ export declare function runResultIngestionExecutor(bundle: ArtifactBundle, results: AuditResult[]): ExecutorRunResult;
7
+ export declare function runRuntimeValidationExecutor(bundle: ArtifactBundle, root: string, options?: {
8
+ opentoken?: boolean;
9
+ }): Promise<ExecutorRunResult>;
10
+ export declare function runRuntimeValidationUpdateExecutor(bundle: ArtifactBundle, updates: RuntimeValidationReport): ExecutorRunResult;
11
+ export declare function runExternalAnalyzerImportExecutor(bundle: ArtifactBundle, externalResults: ExternalAnalyzerResults): ExecutorRunResult;