auditor-lambda 0.3.40 → 0.5.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 (193) hide show
  1. package/audit-code-wrapper-lib.mjs +20 -2
  2. package/dist/cli/args.d.ts +59 -0
  3. package/dist/cli/args.js +244 -0
  4. package/dist/cli/dispatch.d.ts +80 -0
  5. package/dist/cli/dispatch.js +532 -0
  6. package/dist/cli/prompts.d.ts +37 -0
  7. package/dist/cli/prompts.js +225 -0
  8. package/dist/cli/steps.d.ts +29 -0
  9. package/dist/cli/steps.js +30 -0
  10. package/dist/cli/waveManifest.d.ts +40 -0
  11. package/dist/cli/waveManifest.js +41 -0
  12. package/dist/cli/workerResult.d.ts +18 -0
  13. package/dist/cli/workerResult.js +42 -0
  14. package/dist/cli.d.ts +2 -22
  15. package/dist/cli.js +442 -975
  16. package/dist/extractors/analyzers/css.d.ts +2 -0
  17. package/dist/extractors/analyzers/css.js +101 -0
  18. package/dist/extractors/analyzers/html.d.ts +2 -0
  19. package/dist/extractors/analyzers/html.js +92 -0
  20. package/dist/extractors/analyzers/merge.d.ts +14 -0
  21. package/dist/extractors/analyzers/merge.js +85 -0
  22. package/dist/extractors/analyzers/python.d.ts +2 -0
  23. package/dist/extractors/analyzers/python.js +104 -0
  24. package/dist/extractors/analyzers/registry.d.ts +33 -0
  25. package/dist/extractors/analyzers/registry.js +100 -0
  26. package/dist/extractors/analyzers/resourceUrl.d.ts +7 -0
  27. package/dist/extractors/analyzers/resourceUrl.js +25 -0
  28. package/dist/extractors/analyzers/sql.d.ts +2 -0
  29. package/dist/extractors/analyzers/sql.js +19 -0
  30. package/dist/extractors/analyzers/treeSitter.d.ts +34 -0
  31. package/dist/extractors/analyzers/treeSitter.js +111 -0
  32. package/dist/extractors/analyzers/types.d.ts +53 -0
  33. package/dist/extractors/analyzers/typescript.d.ts +2 -0
  34. package/dist/extractors/analyzers/typescript.js +257 -0
  35. package/dist/extractors/browserExtension.d.ts +1 -3
  36. package/dist/extractors/browserExtension.js +2 -2
  37. package/dist/extractors/designAssessment.d.ts +1 -3
  38. package/dist/extractors/disposition.d.ts +2 -1
  39. package/dist/extractors/disposition.js +11 -1
  40. package/dist/extractors/flows.d.ts +1 -3
  41. package/dist/extractors/flows.js +2 -2
  42. package/dist/extractors/graph.d.ts +2 -2
  43. package/dist/extractors/graph.js +171 -327
  44. package/dist/extractors/graphManifestEdges.d.ts +1 -1
  45. package/dist/extractors/graphPathUtils.d.ts +1 -1
  46. package/dist/extractors/graphPythonImports.d.ts +18 -0
  47. package/dist/extractors/graphPythonImports.js +362 -0
  48. package/dist/extractors/pathPatterns.d.ts +6 -0
  49. package/dist/extractors/pathPatterns.js +8 -0
  50. package/dist/extractors/risk.d.ts +1 -2
  51. package/dist/extractors/surfaces.d.ts +1 -3
  52. package/dist/extractors/surfaces.js +2 -2
  53. package/dist/io/artifacts.d.ts +12 -5
  54. package/dist/io/artifacts.js +13 -1
  55. package/dist/io/runArtifacts.js +1 -1
  56. package/dist/mcp/server.js +1 -1
  57. package/dist/orchestrator/advance.d.ts +21 -0
  58. package/dist/orchestrator/advance.js +69 -7
  59. package/dist/orchestrator/auditTaskUtils.d.ts +4 -0
  60. package/dist/orchestrator/auditTaskUtils.js +27 -0
  61. package/dist/orchestrator/dependencyMap.js +27 -0
  62. package/dist/orchestrator/edgeReasoning.d.ts +39 -0
  63. package/dist/orchestrator/edgeReasoning.js +125 -0
  64. package/dist/orchestrator/executors.js +11 -1
  65. package/dist/orchestrator/fileAnchors.d.ts +1 -1
  66. package/dist/orchestrator/fileIntegrity.d.ts +7 -0
  67. package/dist/orchestrator/fileIntegrity.js +41 -0
  68. package/dist/orchestrator/flowCoverage.d.ts +1 -1
  69. package/dist/orchestrator/flowPlanning.d.ts +1 -1
  70. package/dist/orchestrator/flowRequeue.d.ts +1 -1
  71. package/dist/orchestrator/graphEnrichmentExecutor.d.ts +29 -0
  72. package/dist/orchestrator/graphEnrichmentExecutor.js +196 -0
  73. package/dist/orchestrator/internalExecutors.d.ts +13 -2
  74. package/dist/orchestrator/internalExecutors.js +112 -16
  75. package/dist/orchestrator/localCommands.js +6 -25
  76. package/dist/orchestrator/nextStep.d.ts +2 -1
  77. package/dist/orchestrator/nextStep.js +3 -1
  78. package/dist/orchestrator/planning.d.ts +1 -1
  79. package/dist/orchestrator/requeueCommand.d.ts +1 -1
  80. package/dist/orchestrator/reviewPackets.d.ts +37 -4
  81. package/dist/orchestrator/reviewPackets.js +113 -158
  82. package/dist/orchestrator/runtimeValidation.d.ts +1 -1
  83. package/dist/orchestrator/runtimeValidation.js +4 -31
  84. package/dist/orchestrator/scope.d.ts +62 -0
  85. package/dist/orchestrator/scope.js +227 -0
  86. package/dist/orchestrator/state.js +2 -0
  87. package/dist/orchestrator/taskBuilder.d.ts +1 -1
  88. package/dist/orchestrator/taskBuilder.js +1 -12
  89. package/dist/orchestrator/unionFind.d.ts +7 -0
  90. package/dist/orchestrator/unionFind.js +32 -0
  91. package/dist/orchestrator/unitBuilder.d.ts +2 -2
  92. package/dist/orchestrator/unitBuilder.js +4 -18
  93. package/dist/prompts/renderWorkerPrompt.js +18 -1
  94. package/dist/providers/claudeCodeProvider.d.ts +4 -4
  95. package/dist/providers/claudeCodeProvider.js +9 -3
  96. package/dist/providers/constants.d.ts +1 -1
  97. package/dist/providers/constants.js +1 -1
  98. package/dist/providers/index.d.ts +1 -2
  99. package/dist/providers/index.js +5 -4
  100. package/dist/providers/localSubprocessProvider.d.ts +2 -2
  101. package/dist/providers/localSubprocessProvider.js +1 -1
  102. package/dist/providers/opencodeProvider.d.ts +4 -4
  103. package/dist/providers/opencodeProvider.js +7 -2
  104. package/dist/providers/spawnLoggedCommand.d.ts +3 -1
  105. package/dist/providers/spawnLoggedCommand.js +21 -0
  106. package/dist/providers/subprocessTemplateProvider.d.ts +4 -4
  107. package/dist/providers/subprocessTemplateProvider.js +8 -3
  108. package/dist/providers/vscodeTaskProvider.d.ts +3 -4
  109. package/dist/providers/vscodeTaskProvider.js +2 -2
  110. package/dist/quota/discoveredLimits.js +1 -1
  111. package/dist/quota/hostLimits.d.ts +1 -2
  112. package/dist/quota/hostLimits.js +4 -46
  113. package/dist/quota/index.d.ts +18 -15
  114. package/dist/quota/index.js +4 -9
  115. package/dist/quota/scheduler.d.ts +1 -3
  116. package/dist/quota/scheduler.js +1 -2
  117. package/dist/reporting/synthesis.d.ts +37 -3
  118. package/dist/reporting/synthesis.js +97 -16
  119. package/dist/reporting/synthesisNarrativePrompt.d.ts +7 -0
  120. package/dist/reporting/synthesisNarrativePrompt.js +60 -0
  121. package/dist/reporting/workBlocks.d.ts +2 -11
  122. package/dist/supervisor/operatorHandoff.js +1 -1
  123. package/dist/supervisor/runLedger.d.ts +1 -1
  124. package/dist/supervisor/runLedger.js +2 -2
  125. package/dist/supervisor/sessionConfig.d.ts +8 -1
  126. package/dist/supervisor/sessionConfig.js +22 -3
  127. package/dist/types/analyzerCapability.d.ts +16 -0
  128. package/dist/types/auditScope.d.ts +43 -0
  129. package/dist/types/auditScope.js +14 -0
  130. package/dist/types/reviewPlanning.d.ts +1 -1
  131. package/dist/types/synthesisNarrative.d.ts +7 -0
  132. package/dist/types/synthesisNarrative.js +5 -0
  133. package/dist/types/workerSession.d.ts +6 -0
  134. package/dist/types.d.ts +2 -19
  135. package/dist/validation/artifacts.d.ts +1 -1
  136. package/dist/validation/artifacts.js +10 -1
  137. package/dist/validation/auditResults.d.ts +1 -1
  138. package/dist/validation/auditResults.js +1 -1
  139. package/dist/validation/sessionConfig.d.ts +2 -3
  140. package/dist/validation/sessionConfig.js +25 -3
  141. package/package.json +7 -3
  142. package/schemas/analyzer_capability.schema.json +47 -0
  143. package/schemas/audit_findings.schema.json +141 -0
  144. package/schemas/finding.schema.json +2 -1
  145. package/schemas/graph_bundle.schema.json +5 -0
  146. package/schemas/scope.schema.json +46 -0
  147. package/scripts/postinstall.mjs +0 -1
  148. package/dist/io/json.d.ts +0 -10
  149. package/dist/io/json.js +0 -142
  150. package/dist/providers/types.d.ts +0 -33
  151. package/dist/quota/compositeQuotaSource.d.ts +0 -7
  152. package/dist/quota/compositeQuotaSource.js +0 -20
  153. package/dist/quota/errorParsers/claudeCodeErrorParser.d.ts +0 -6
  154. package/dist/quota/errorParsers/claudeCodeErrorParser.js +0 -39
  155. package/dist/quota/errorParsers/genericErrorParser.d.ts +0 -9
  156. package/dist/quota/errorParsers/genericErrorParser.js +0 -7
  157. package/dist/quota/errorParsers/index.d.ts +0 -5
  158. package/dist/quota/errorParsers/index.js +0 -12
  159. package/dist/quota/errorParsing.d.ts +0 -7
  160. package/dist/quota/errorParsing.js +0 -69
  161. package/dist/quota/fileLock.d.ts +0 -6
  162. package/dist/quota/fileLock.js +0 -64
  163. package/dist/quota/learnedQuotaSource.d.ts +0 -7
  164. package/dist/quota/learnedQuotaSource.js +0 -25
  165. package/dist/quota/limits.d.ts +0 -16
  166. package/dist/quota/limits.js +0 -77
  167. package/dist/quota/quotaSource.d.ts +0 -12
  168. package/dist/quota/slidingWindow.d.ts +0 -4
  169. package/dist/quota/slidingWindow.js +0 -28
  170. package/dist/quota/state.d.ts +0 -15
  171. package/dist/quota/state.js +0 -148
  172. package/dist/quota/types.d.ts +0 -67
  173. package/dist/quota/types.js +0 -1
  174. package/dist/reporting/rootCause.d.ts +0 -10
  175. package/dist/reporting/rootCause.js +0 -146
  176. package/dist/types/disposition.d.ts +0 -9
  177. package/dist/types/disposition.js +0 -1
  178. package/dist/types/flows.d.ts +0 -17
  179. package/dist/types/flows.js +0 -1
  180. package/dist/types/graph.d.ts +0 -22
  181. package/dist/types/graph.js +0 -1
  182. package/dist/types/risk.d.ts +0 -9
  183. package/dist/types/risk.js +0 -1
  184. package/dist/types/runLedger.d.ts +0 -17
  185. package/dist/types/runLedger.js +0 -6
  186. package/dist/types/sessionConfig.d.ts +0 -79
  187. package/dist/types/sessionConfig.js +0 -15
  188. package/dist/types/surfaces.d.ts +0 -15
  189. package/dist/types/surfaces.js +0 -1
  190. package/dist/validation/basic.d.ts +0 -13
  191. package/dist/validation/basic.js +0 -46
  192. /package/dist/{providers → extractors/analyzers}/types.js +0 -0
  193. /package/dist/{quota/quotaSource.js → types/analyzerCapability.js} +0 -0
@@ -1,9 +1,12 @@
1
- import { decideNextStep } from "./nextStep.js";
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, runDesignAssessmentExecutor, runDesignReviewAutoComplete, runExternalAnalyzerImportExecutor, } from "./internalExecutors.js";
4
+ import { runIntakeExecutor, runStructureExecutor, runPlanningExecutor, runResultIngestionExecutor, runRuntimeValidationExecutor, runRuntimeValidationUpdateExecutor, runSynthesisExecutor, runSynthesisNarrativeExecutor, runDesignAssessmentExecutor, runDesignReviewAutoComplete, runExternalAnalyzerImportExecutor, } from "./internalExecutors.js";
5
5
  import { runAutoFixExecutor } from "./autoFixExecutor.js";
6
6
  import { runSyntaxResolutionExecutor } from "./syntaxResolutionExecutor.js";
7
+ import { runGraphEnrichmentExecutor } from "./graphEnrichmentExecutor.js";
8
+ import { resolveAuditScope } from "./scope.js";
9
+ import { RunLogger } from "@audit-tools/shared";
7
10
  function cloneState(state) {
8
11
  return {
9
12
  ...state,
@@ -18,12 +21,19 @@ function formatExecutorFailure(selectedExecutor, selectedObligation, error) {
18
21
  });
19
22
  }
20
23
  export async function advanceAudit(bundle, options = {}) {
24
+ const log = options.runLogger ?? RunLogger.disabled();
21
25
  const decision = decideNextStep(bundle);
22
26
  const forcedExecutor = options.preferredExecutor ?? null;
23
27
  const selectedExecutor = forcedExecutor ?? decision.selected_executor;
24
28
  const selectedObligation = forcedExecutor
25
29
  ? `forced:${forcedExecutor}`
26
30
  : decision.selected_obligation;
31
+ log.event({
32
+ phase: "advance",
33
+ kind: "obligation",
34
+ obligation: selectedObligation ?? undefined,
35
+ note: decision.reason,
36
+ });
27
37
  if (!selectedExecutor) {
28
38
  const state = cloneState(decision.state);
29
39
  state.last_executor = bundle.audit_state?.last_executor ?? state.last_executor;
@@ -43,6 +53,14 @@ export async function advanceAudit(bundle, options = {}) {
43
53
  };
44
54
  }
45
55
  let run;
56
+ let plannedScope;
57
+ const executorStartedAt = Date.now();
58
+ log.event({
59
+ phase: "advance",
60
+ kind: "executor_start",
61
+ obligation: selectedObligation ?? undefined,
62
+ note: selectedExecutor,
63
+ });
46
64
  try {
47
65
  switch (selectedExecutor) {
48
66
  case "intake_executor":
@@ -53,6 +71,14 @@ export async function advanceAudit(bundle, options = {}) {
53
71
  case "structure_executor":
54
72
  run = await runStructureExecutor(bundle, options.root);
55
73
  break;
74
+ case "graph_enrichment_executor":
75
+ run = await runGraphEnrichmentExecutor(bundle, {
76
+ root: options.root,
77
+ analyzers: options.analyzers,
78
+ llmEdgeReasoning: options.graphLlmEdgeReasoning,
79
+ edgeReasoning: options.edgeReasoningResults,
80
+ });
81
+ break;
56
82
  case "design_assessment_executor":
57
83
  run = runDesignAssessmentExecutor(bundle);
58
84
  break;
@@ -62,7 +88,12 @@ export async function advanceAudit(bundle, options = {}) {
62
88
  case "planning_executor":
63
89
  if (!options.root)
64
90
  throw new Error("advanceAudit planning_executor requires root");
65
- run = await runPlanningExecutor(bundle, options.root, options.lineIndex ?? {});
91
+ plannedScope = resolveAuditScope({
92
+ root: options.root,
93
+ since: options.since,
94
+ bundle,
95
+ });
96
+ run = await runPlanningExecutor(bundle, options.root, options.lineIndex ?? {}, options.sizeIndex, plannedScope);
66
97
  break;
67
98
  case "result_ingestion_executor":
68
99
  run = runResultIngestionExecutor(bundle, options.auditResults ?? bundle.audit_results ?? []);
@@ -70,11 +101,16 @@ export async function advanceAudit(bundle, options = {}) {
70
101
  case "runtime_validation_executor":
71
102
  if (!options.root)
72
103
  throw new Error("advanceAudit runtime_validation_executor requires root");
73
- run = await runRuntimeValidationExecutor(bundle, options.root);
104
+ run = await runRuntimeValidationExecutor(bundle, options.root, {
105
+ opentoken: options.opentoken,
106
+ });
74
107
  break;
75
108
  case "synthesis_executor":
76
109
  run = runSynthesisExecutor(bundle, options.auditResults);
77
110
  break;
111
+ case "synthesis_narrative_executor":
112
+ run = runSynthesisNarrativeExecutor(bundle, options.narrativeResults);
113
+ break;
78
114
  case "runtime_validation_update_executor":
79
115
  if (!options.runtimeValidationUpdates)
80
116
  throw new Error("advanceAudit runtime_validation_update_executor requires runtimeValidationUpdates");
@@ -95,7 +131,7 @@ export async function advanceAudit(bundle, options = {}) {
95
131
  throw new Error("advanceAudit syntax_resolution_executor requires root");
96
132
  run = runSyntaxResolutionExecutor(bundle, options.root);
97
133
  break;
98
- default:
134
+ default: {
99
135
  const state = deriveAuditState(bundle);
100
136
  state.last_executor = selectedExecutor;
101
137
  state.last_obligation = selectedObligation ?? undefined;
@@ -109,11 +145,37 @@ export async function advanceAudit(bundle, options = {}) {
109
145
  next_likely_step: selectedObligation,
110
146
  updated_bundle: { ...bundle, audit_state: state },
111
147
  };
148
+ }
112
149
  }
113
150
  }
114
151
  catch (error) {
115
152
  throw formatExecutorFailure(selectedExecutor, selectedObligation, error);
116
153
  }
154
+ log.event({
155
+ phase: "advance",
156
+ kind: "executor_end",
157
+ obligation: selectedObligation ?? undefined,
158
+ note: selectedExecutor,
159
+ duration_ms: Date.now() - executorStartedAt,
160
+ });
161
+ if (plannedScope) {
162
+ log.event({
163
+ phase: "advance",
164
+ kind: "scope",
165
+ obligation: selectedObligation ?? undefined,
166
+ note: plannedScope.mode === "delta"
167
+ ? `delta since ${plannedScope.since}: ${plannedScope.seed_files.length} changed + ${plannedScope.expanded_files.length} neighbors; full audit advised before release`
168
+ : "full audit scope",
169
+ });
170
+ }
171
+ for (const artifact of run.artifacts_written) {
172
+ log.event({
173
+ phase: "advance",
174
+ kind: "artifact_write",
175
+ obligation: selectedObligation ?? undefined,
176
+ artifact,
177
+ });
178
+ }
117
179
  const metadata = computeArtifactMetadata(run.updated, bundle.artifact_metadata, [...run.artifacts_written, "tooling_manifest.json"]);
118
180
  const metadataBundle = {
119
181
  ...run.updated,
@@ -124,7 +186,7 @@ export async function advanceAudit(bundle, options = {}) {
124
186
  updatedState.last_executor = selectedExecutor;
125
187
  updatedState.last_obligation = selectedObligation ?? undefined;
126
188
  const finalizedBundle = { ...metadataBundle, audit_state: updatedState };
127
- const followupDecision = decideNextStep(finalizedBundle);
189
+ const nextObligation = findObligation(updatedState.obligations);
128
190
  return {
129
191
  audit_state: updatedState,
130
192
  selected_obligation: selectedObligation,
@@ -136,7 +198,7 @@ export async function advanceAudit(bundle, options = {}) {
136
198
  "audit_state.json",
137
199
  ],
138
200
  progress_summary: run.progress_summary,
139
- next_likely_step: followupDecision.selected_obligation,
201
+ next_likely_step: nextObligation?.id ?? null,
140
202
  updated_bundle: finalizedBundle,
141
203
  };
142
204
  }
@@ -0,0 +1,4 @@
1
+ import type { AuditTask, Lens } from "../types.js";
2
+ export declare const LENS_ORDER: Lens[];
3
+ export declare function priorityRank(priority: AuditTask["priority"]): number;
4
+ export declare function sortLenses(lenses: Iterable<Lens>): Lens[];
@@ -0,0 +1,27 @@
1
+ export const LENS_ORDER = [
2
+ "security",
3
+ "correctness",
4
+ "reliability",
5
+ "data_integrity",
6
+ "performance",
7
+ "operability",
8
+ "config_deployment",
9
+ "observability",
10
+ "maintainability",
11
+ "tests",
12
+ ];
13
+ export function priorityRank(priority) {
14
+ switch (priority) {
15
+ case "high":
16
+ return 3;
17
+ case "medium":
18
+ return 2;
19
+ case "low":
20
+ default:
21
+ return 1;
22
+ }
23
+ }
24
+ export function sortLenses(lenses) {
25
+ const set = new Set(lenses);
26
+ return LENS_ORDER.filter((lens) => set.has(lens));
27
+ }
@@ -19,6 +19,15 @@ export const ARTIFACT_DEPENDENCY_MAP = {
19
19
  "runtime_validation_report.json",
20
20
  "audit-report.md",
21
21
  ],
22
+ // The optional graph-enrichment pass layers analyzer edges onto graph_bundle
23
+ // and records provenance in analyzer_capability.json. A re-built (structure)
24
+ // graph re-stales the marker so enrichment re-runs. No cycle: the enrichment
25
+ // executor writes graph_bundle AND the marker in one advanceAudit call, and
26
+ // computeArtifactMetadata is dependency-first, so the marker records the
27
+ // post-enrichment graph_bundle revision (mirrors audit-findings → narrative).
28
+ "graph_bundle.json": [
29
+ "analyzer_capability.json",
30
+ ],
22
31
  "file_disposition.json": [
23
32
  "unit_manifest.json",
24
33
  "surface_manifest.json",
@@ -91,6 +100,17 @@ export const ARTIFACT_DEPENDENCY_MAP = {
91
100
  "runtime_validation_report.json",
92
101
  "audit-report.md",
93
102
  ],
103
+ // Phase 3 delta scope. scope.json is produced by the planning executor (full
104
+ // or delta) and gates coverage: in delta mode it decides which coverage
105
+ // entries are (re)queued vs. inherited-complete/excluded. A changed scope
106
+ // (different `--since`/seed set → new content hash) re-stales coverage so the
107
+ // plan rebuilds. No cycle: planning writes scope.json AND coverage_matrix.json
108
+ // in one advanceAudit call, and computeArtifactMetadata is dependency-first,
109
+ // so coverage records the post-write scope revision (mirrors graph_bundle →
110
+ // analyzer_capability and audit-findings → narrative).
111
+ "scope.json": [
112
+ "coverage_matrix.json",
113
+ ],
94
114
  "coverage_matrix.json": [
95
115
  "flow_coverage.json",
96
116
  "audit_plan_metrics.json",
@@ -115,4 +135,11 @@ export const ARTIFACT_DEPENDENCY_MAP = {
115
135
  "runtime_validation_report.json": [
116
136
  "audit-report.md",
117
137
  ],
138
+ // The canonical machine contract is co-produced with audit-report.md by the
139
+ // synthesis executor. The optional narrative pass tracks its revision: a fresh
140
+ // (re-synthesized) audit-findings.json re-stales the narrative marker so the
141
+ // themes/executive-summary/top-risks regenerate. See spec/dependency-map.md.
142
+ "audit-findings.json": [
143
+ "synthesis-narrative.json",
144
+ ],
118
145
  };
@@ -0,0 +1,39 @@
1
+ import type { GraphBundle, GraphEdge } from "@audit-tools/shared";
2
+ export declare const DEFAULT_EDGE_CONFIDENCE_FLOOR = 0.65;
3
+ /** Bound the candidate set so one pathological repo cannot balloon the call. */
4
+ export declare const MAX_REASONED_EDGES = 200;
5
+ /** One host-supplied reason rewrite, matched to an edge by (from, to, kind). */
6
+ export interface EdgeReasonRewrite {
7
+ from: string;
8
+ to: string;
9
+ /** Optional; when omitted the rewrite matches any candidate with from+to. */
10
+ kind?: string;
11
+ reason: string;
12
+ }
13
+ export interface EdgeReasoningResults {
14
+ rewrites: EdgeReasonRewrite[];
15
+ }
16
+ export interface EdgeReasoningOptions {
17
+ /** Edges strictly below this confidence are candidates (default 0.65). */
18
+ confidenceFloor?: number;
19
+ }
20
+ export interface EdgeReasoningSummary {
21
+ rewritten: number;
22
+ candidates: number;
23
+ }
24
+ /**
25
+ * Collect the low-confidence edges (the actual edge objects, so the caller can
26
+ * mutate `reason` in place) in a deterministic order. Routes are excluded — they
27
+ * carry no `reason`/`confidence`.
28
+ */
29
+ export declare function collectLowConfidenceEdges(bundle: GraphBundle, floor?: number): GraphEdge[];
30
+ /** Stable content hash of the candidate edge set, for host-side call caching. */
31
+ export declare function edgeReasoningContentHash(candidates: GraphEdge[]): string;
32
+ /** The single bounded prompt a host runs to produce {@link EdgeReasoningResults}. */
33
+ export declare function buildEdgeReasoningPrompt(candidates: GraphEdge[]): string;
34
+ /**
35
+ * Apply host-supplied reason rewrites to `bundle` (mutated in place). Only edges
36
+ * below the confidence floor are eligible; a rewrite that matches no eligible
37
+ * edge is ignored. Returns a summary; the edge set itself is invariant.
38
+ */
39
+ export declare function applyEdgeReasoning(bundle: GraphBundle, results: EdgeReasoningResults | undefined, options?: EdgeReasoningOptions): EdgeReasoningSummary;
@@ -0,0 +1,125 @@
1
+ import { createHash } from "node:crypto";
2
+ /**
3
+ * Phase 4B — optional, bounded edge-reasoning pass.
4
+ *
5
+ * A deterministic transform that may only rewrite the human-readable `reason`
6
+ * of existing low-confidence graph edges. It never adds, removes, re-targets, or
7
+ * re-weights an edge: the `(from, to, kind, confidence, direction)` identity of
8
+ * every edge is preserved exactly. Rewrites are host/provider-supplied (the same
9
+ * conversation-first pattern as the Phase 6 synthesis narrative) — no in-process
10
+ * LLM call. No rewrites (or the config flag off) → no-op, leaving the
11
+ * deterministic graph byte-identical. This is part of the
12
+ * `graph_enrichment_current` obligation.
13
+ *
14
+ * The pass is bounded to edges below a confidence floor (default 0.65) because
15
+ * those are exactly the heuristic edges whose terse machine reason benefits from
16
+ * a clearer explanation; high-confidence compiler/import edges are left alone.
17
+ * {@link buildEdgeReasoningPrompt} and {@link edgeReasoningContentHash} let a
18
+ * host produce and cache that single rewriting call by edge-set content hash.
19
+ */
20
+ const EDGE_REASONING_VERSION = 1;
21
+ export const DEFAULT_EDGE_CONFIDENCE_FLOOR = 0.65;
22
+ /** Bound the candidate set so one pathological repo cannot balloon the call. */
23
+ export const MAX_REASONED_EDGES = 200;
24
+ function confidenceOf(edge) {
25
+ return typeof edge.confidence === "number" && Number.isFinite(edge.confidence)
26
+ ? edge.confidence
27
+ : 0;
28
+ }
29
+ function edgeSignature(edge) {
30
+ return `${edge.from}\0${edge.to}\0${edge.kind ?? ""}`;
31
+ }
32
+ /**
33
+ * Collect the low-confidence edges (the actual edge objects, so the caller can
34
+ * mutate `reason` in place) in a deterministic order. Routes are excluded — they
35
+ * carry no `reason`/`confidence`.
36
+ */
37
+ export function collectLowConfidenceEdges(bundle, floor = DEFAULT_EDGE_CONFIDENCE_FLOOR) {
38
+ const candidates = [];
39
+ for (const bucket of [
40
+ bundle.graphs.imports,
41
+ bundle.graphs.calls,
42
+ bundle.graphs.references,
43
+ ]) {
44
+ for (const edge of bucket ?? []) {
45
+ if (confidenceOf(edge) < floor) {
46
+ candidates.push(edge);
47
+ }
48
+ }
49
+ }
50
+ return candidates
51
+ .sort((a, b) => edgeSignature(a).localeCompare(edgeSignature(b)))
52
+ .slice(0, MAX_REASONED_EDGES);
53
+ }
54
+ /** Stable content hash of the candidate edge set, for host-side call caching. */
55
+ export function edgeReasoningContentHash(candidates) {
56
+ const basis = JSON.stringify({
57
+ version: EDGE_REASONING_VERSION,
58
+ edges: candidates.map((edge) => ({
59
+ from: edge.from,
60
+ to: edge.to,
61
+ kind: edge.kind ?? "",
62
+ confidence: confidenceOf(edge),
63
+ reason: edge.reason ?? "",
64
+ })),
65
+ });
66
+ return createHash("sha256").update(basis).digest("hex");
67
+ }
68
+ /** The single bounded prompt a host runs to produce {@link EdgeReasoningResults}. */
69
+ export function buildEdgeReasoningPrompt(candidates) {
70
+ const lines = candidates.map((edge) => `- from: ${edge.from} | to: ${edge.to} | kind: ${edge.kind ?? "?"} | confidence: ${confidenceOf(edge).toFixed(2)} | current: ${edge.reason ?? "(none)"}`);
71
+ return [
72
+ "You are improving the human-readable 'reason' for low-confidence edges in a code dependency graph.",
73
+ "Each edge links a source file to a target file by a relationship 'kind'.",
74
+ "For each edge you can improve, write one clear, specific sentence explaining why that relationship plausibly holds.",
75
+ "Do NOT invent new edges, drop edges, or change which files are linked — only rewrite the reason text.",
76
+ "Omit any edge whose reason you cannot improve.",
77
+ "",
78
+ "Edges:",
79
+ ...lines,
80
+ "",
81
+ 'Respond with JSON only: {"rewrites":[{"from":"...","to":"...","kind":"...","reason":"..."}]}',
82
+ ].join("\n");
83
+ }
84
+ /**
85
+ * Apply host-supplied reason rewrites to `bundle` (mutated in place). Only edges
86
+ * below the confidence floor are eligible; a rewrite that matches no eligible
87
+ * edge is ignored. Returns a summary; the edge set itself is invariant.
88
+ */
89
+ export function applyEdgeReasoning(bundle, results, options = {}) {
90
+ const floor = options.confidenceFloor ?? DEFAULT_EDGE_CONFIDENCE_FLOOR;
91
+ const candidates = collectLowConfidenceEdges(bundle, floor);
92
+ if (!results || !Array.isArray(results.rewrites) || candidates.length === 0) {
93
+ return { rewritten: 0, candidates: candidates.length };
94
+ }
95
+ const bySignature = new Map();
96
+ const byEndpoints = new Map();
97
+ for (const edge of candidates) {
98
+ bySignature.set(edgeSignature(edge), edge);
99
+ const endpoints = `${edge.from}\0${edge.to}`;
100
+ if (!byEndpoints.has(endpoints)) {
101
+ byEndpoints.set(endpoints, edge);
102
+ }
103
+ }
104
+ let rewritten = 0;
105
+ const seen = new Set();
106
+ for (const rewrite of results.rewrites) {
107
+ if (!rewrite ||
108
+ typeof rewrite.from !== "string" ||
109
+ typeof rewrite.to !== "string" ||
110
+ typeof rewrite.reason !== "string" ||
111
+ rewrite.reason.trim().length === 0) {
112
+ continue;
113
+ }
114
+ const edge = rewrite.kind !== undefined
115
+ ? bySignature.get(`${rewrite.from}\0${rewrite.to}\0${rewrite.kind}`)
116
+ : byEndpoints.get(`${rewrite.from}\0${rewrite.to}`);
117
+ if (!edge || seen.has(edge)) {
118
+ continue;
119
+ }
120
+ edge.reason = rewrite.reason.trim();
121
+ seen.add(edge);
122
+ rewritten += 1;
123
+ }
124
+ return { rewritten, candidates: candidates.length };
125
+ }
@@ -9,6 +9,11 @@ export const EXECUTOR_REGISTRY = [
9
9
  obligation_ids: ["structure_artifacts"],
10
10
  description: "Build structure artifacts such as units, surfaces, graphs, flows, and risk.",
11
11
  },
12
+ {
13
+ id: "graph_enrichment_executor",
14
+ obligation_ids: ["graph_enrichment_current"],
15
+ description: "Layer optional language-analyzer edges onto the deterministic graph (regex floor preserved); record analyzer provenance.",
16
+ },
12
17
  {
13
18
  id: "design_assessment_executor",
14
19
  obligation_ids: ["design_assessment_current"],
@@ -42,7 +47,12 @@ export const EXECUTOR_REGISTRY = [
42
47
  {
43
48
  id: "synthesis_executor",
44
49
  obligation_ids: ["synthesis_current"],
45
- description: "Render the final deterministic Markdown audit report.",
50
+ description: "Emit the canonical audit-findings.json and render the deterministic Markdown audit report.",
51
+ },
52
+ {
53
+ id: "synthesis_narrative_executor",
54
+ obligation_ids: ["synthesis_narrative_current"],
55
+ description: "Resolve the optional synthesis narrative (themes, executive summary, top risks); omit deterministically without a provider.",
46
56
  },
47
57
  {
48
58
  id: "external_analyzer_import_executor",
@@ -1,5 +1,5 @@
1
1
  import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
2
- import type { GraphBundle } from "../types/graph.js";
2
+ import type { GraphBundle } from "@audit-tools/shared";
3
3
  export type FileAnchorKind = "boundary" | "import" | "export" | "symbol" | "route" | "keyword" | "graph" | "analyzer_signal";
4
4
  export interface FileAnchor {
5
5
  kind: FileAnchorKind;
@@ -0,0 +1,7 @@
1
+ import type { RepoManifest } from "../types.js";
2
+ export interface FileIntegrityResult {
3
+ changed_files: string[];
4
+ missing_files: string[];
5
+ is_clean: boolean;
6
+ }
7
+ export declare function checkFileIntegrity(root: string, manifest: RepoManifest, scope?: string[]): Promise<FileIntegrityResult>;
@@ -0,0 +1,41 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+ import { join, isAbsolute } from "node:path";
4
+ import { existsSync } from "node:fs";
5
+ async function hashFile(absolutePath) {
6
+ const content = await readFile(absolutePath);
7
+ return createHash("sha256").update(content).digest("hex");
8
+ }
9
+ export async function checkFileIntegrity(root, manifest, scope) {
10
+ const changed = [];
11
+ const missing = [];
12
+ const scopeSet = scope ? new Set(scope) : null;
13
+ const files = scopeSet
14
+ ? manifest.files.filter((f) => scopeSet.has(f.path))
15
+ : manifest.files;
16
+ for (const record of files) {
17
+ if (!record.hash)
18
+ continue;
19
+ const absolute = isAbsolute(record.path)
20
+ ? record.path
21
+ : join(root, record.path);
22
+ if (!existsSync(absolute)) {
23
+ missing.push(record.path);
24
+ continue;
25
+ }
26
+ try {
27
+ const currentHash = await hashFile(absolute);
28
+ if (currentHash !== record.hash) {
29
+ changed.push(record.path);
30
+ }
31
+ }
32
+ catch {
33
+ missing.push(record.path);
34
+ }
35
+ }
36
+ return {
37
+ changed_files: changed,
38
+ missing_files: missing,
39
+ is_clean: changed.length === 0 && missing.length === 0,
40
+ };
41
+ }
@@ -1,4 +1,4 @@
1
1
  import type { CoverageMatrix } from "../types.js";
2
2
  import type { FlowCoverageManifest } from "../types/flowCoverage.js";
3
- import type { CriticalFlowManifest } from "../types/flows.js";
3
+ import type { CriticalFlowManifest } from "@audit-tools/shared";
4
4
  export declare function buildFlowCoverage(criticalFlows: CriticalFlowManifest, coverageMatrix: CoverageMatrix): FlowCoverageManifest;
@@ -1,5 +1,5 @@
1
1
  import type { Lens } from "../types.js";
2
- import type { CriticalFlowManifest } from "../types/flows.js";
2
+ import type { CriticalFlowManifest } from "@audit-tools/shared";
3
3
  export interface FlowReviewBlock {
4
4
  flow_id: string;
5
5
  lens: Lens;
@@ -1,5 +1,5 @@
1
1
  import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
2
2
  import type { AuditTask, CoverageMatrix } from "../types.js";
3
3
  import type { FlowCoverageManifest } from "../types/flowCoverage.js";
4
- import type { CriticalFlowManifest } from "../types/flows.js";
4
+ import type { CriticalFlowManifest } from "@audit-tools/shared";
5
5
  export declare function buildFlowRequeueTasks(criticalFlows: CriticalFlowManifest, flowCoverage: FlowCoverageManifest, coverageMatrix: CoverageMatrix, externalAnalyzerResults?: ExternalAnalyzerResults): AuditTask[];
@@ -0,0 +1,29 @@
1
+ import type { ArtifactBundle } from "../io/artifacts.js";
2
+ import type { ExecutorRunResult } from "./internalExecutors.js";
3
+ import type { AnalyzerSetting } from "@audit-tools/shared";
4
+ import type { LanguageAnalyzer } from "../extractors/analyzers/types.js";
5
+ import { type EdgeReasoningResults } from "./edgeReasoning.js";
6
+ export interface GraphEnrichmentOptions {
7
+ root?: string;
8
+ analyzers?: Record<string, AnalyzerSetting>;
9
+ /** Injectable for tests; defaults to the global registry. */
10
+ registry?: LanguageAnalyzer[];
11
+ /** Injectable analyzer-cache root; defaults to ~/.audit-tools/analyzer-cache. */
12
+ cacheRoot?: string;
13
+ /**
14
+ * Phase 4B: gate for the optional edge-reasoning pass (mirrors
15
+ * session-config `graph.llm_edge_reasoning`; default off).
16
+ */
17
+ llmEdgeReasoning?: boolean;
18
+ /** Phase 4B: host-supplied reason rewrites for low-confidence edges. */
19
+ edgeReasoning?: EdgeReasoningResults;
20
+ }
21
+ /**
22
+ * Resolve the optional graph-enrichment obligation. Layers language-analyzer
23
+ * edges onto the deterministic regex floor in `graph_bundle.json`
24
+ * (higher-confidence-kind-wins) and records provenance in
25
+ * `analyzer_capability.json`. With no root, or when every analyzer skips / is
26
+ * absent / not-applicable, the graph bundle is left byte-identical to the floor
27
+ * and only the marker is written.
28
+ */
29
+ export declare function runGraphEnrichmentExecutor(bundle: ArtifactBundle, options?: GraphEnrichmentOptions): Promise<ExecutorRunResult>;