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
@@ -0,0 +1,196 @@
1
+ import { installToCache, resolveAnalyzerDep } from "@audit-tools/shared";
2
+ import { buildDispositionMap } from "../extractors/disposition.js";
3
+ import { buildPathLookup } from "../extractors/graph.js";
4
+ import { mergeAnalyzerEdges } from "../extractors/analyzers/merge.js";
5
+ import { ANALYZER_REGISTRY } from "../extractors/analyzers/registry.js";
6
+ import { applyEdgeReasoning, } from "./edgeReasoning.js";
7
+ const BUCKET_BY_KIND = {
8
+ "ts-import": "imports",
9
+ "ts-reexport": "imports",
10
+ "ts-call": "calls",
11
+ "ts-extends": "references",
12
+ "ts-implements": "references",
13
+ // Python (tree-sitter) imports merge into the imports bucket alongside the
14
+ // regex floor's python-* edges.
15
+ "py-import": "imports",
16
+ "py-from-import": "imports",
17
+ // HTML/CSS (tree-sitter) resource references live with the floor's
18
+ // html-resource-link / reference edges.
19
+ "html-resource": "references",
20
+ "css-import": "references",
21
+ "css-url": "references",
22
+ };
23
+ function bucketForKind(kind) {
24
+ const bucket = kind ? BUCKET_BY_KIND[kind] : undefined;
25
+ return bucket ?? "references";
26
+ }
27
+ function settingFor(analyzers, id) {
28
+ return analyzers?.[id] ?? "auto";
29
+ }
30
+ function routeSignature(route) {
31
+ return `${route.method ?? ""}\0${route.path}\0${route.handler}`;
32
+ }
33
+ function mergeRoutes(floor, analyzer) {
34
+ const deduped = new Map();
35
+ for (const route of [...floor, ...analyzer]) {
36
+ deduped.set(routeSignature(route), route);
37
+ }
38
+ return [...deduped.values()].sort((a, b) => a.path.localeCompare(b.path) ||
39
+ a.handler.localeCompare(b.handler) ||
40
+ (a.method ?? "").localeCompare(b.method ?? ""));
41
+ }
42
+ /**
43
+ * Resolve a dependency for actual execution (may install for ephemeral/permanent).
44
+ * `auto`/`repo` with an absent dependency falls back to the regex floor.
45
+ */
46
+ function resolveForRun(analyzer, root, setting, cacheRoot) {
47
+ if (!analyzer.dependency) {
48
+ return { resolution: "repo" };
49
+ }
50
+ const options = cacheRoot ? { cacheRoot } : {};
51
+ const resolved = resolveAnalyzerDep(analyzer.dependency, root, options);
52
+ if (resolved.via === "repo" || resolved.via === "cache") {
53
+ return { resolution: resolved.via, path: resolved.path };
54
+ }
55
+ if (setting === "ephemeral" || setting === "permanent") {
56
+ const install = installToCache(analyzer.dependency, options);
57
+ if (install.ok && install.path) {
58
+ return { resolution: "installed", path: install.path };
59
+ }
60
+ return {
61
+ resolution: "absent",
62
+ note: `Install of '${analyzer.dependency}' failed: ${install.error ?? "unknown error"}.`,
63
+ };
64
+ }
65
+ return {
66
+ resolution: "absent",
67
+ note: `Dependency '${analyzer.dependency}' not resolvable; kept regex floor.`,
68
+ };
69
+ }
70
+ /**
71
+ * Resolve the optional graph-enrichment obligation. Layers language-analyzer
72
+ * edges onto the deterministic regex floor in `graph_bundle.json`
73
+ * (higher-confidence-kind-wins) and records provenance in
74
+ * `analyzer_capability.json`. With no root, or when every analyzer skips / is
75
+ * absent / not-applicable, the graph bundle is left byte-identical to the floor
76
+ * and only the marker is written.
77
+ */
78
+ export async function runGraphEnrichmentExecutor(bundle, options = {}) {
79
+ const floor = bundle.graph_bundle;
80
+ if (!floor) {
81
+ throw new Error("Cannot run graph enrichment without graph_bundle");
82
+ }
83
+ const registry = options.registry ?? ANALYZER_REGISTRY;
84
+ const root = options.root;
85
+ const disposition = bundle.file_disposition;
86
+ const dispositionMap = buildDispositionMap(disposition);
87
+ const pathLookup = bundle.repo_manifest
88
+ ? buildPathLookup(bundle.repo_manifest, dispositionMap)
89
+ : new Map();
90
+ const includedFiles = [...new Set(pathLookup.values())].sort((a, b) => a.localeCompare(b));
91
+ const entries = [];
92
+ const bucketEdges = { imports: [], calls: [], references: [] };
93
+ const routeEdges = [];
94
+ const analyzersUsed = [];
95
+ for (const analyzer of registry) {
96
+ const setting = settingFor(options.analyzers, analyzer.id);
97
+ const supportedFiles = includedFiles.filter((file) => analyzer.supports(file));
98
+ if (supportedFiles.length === 0) {
99
+ entries.push({ id: analyzer.id, resolution: "not_applicable", setting, edges_added: 0, routes_added: 0 });
100
+ continue;
101
+ }
102
+ if (setting === "skip") {
103
+ entries.push({ id: analyzer.id, resolution: "skip", setting, edges_added: 0, routes_added: 0, note: "Analyzer disabled via session config." });
104
+ continue;
105
+ }
106
+ if (!root || !bundle.repo_manifest) {
107
+ entries.push({ id: analyzer.id, resolution: "absent", setting, edges_added: 0, routes_added: 0, note: "No repository root available for analysis." });
108
+ continue;
109
+ }
110
+ const run = resolveForRun(analyzer, root, setting, options.cacheRoot);
111
+ if (run.resolution === "absent") {
112
+ entries.push({ id: analyzer.id, resolution: "absent", setting, edges_added: 0, routes_added: 0, note: run.note });
113
+ continue;
114
+ }
115
+ let edges = [];
116
+ let routes = [];
117
+ try {
118
+ const output = await analyzer.analyze(supportedFiles, {
119
+ root,
120
+ repoManifest: bundle.repo_manifest,
121
+ disposition,
122
+ includedFiles,
123
+ pathLookup,
124
+ dependencyPath: run.path,
125
+ });
126
+ edges = output.edges ?? [];
127
+ routes = output.routes ?? [];
128
+ }
129
+ catch (error) {
130
+ entries.push({
131
+ id: analyzer.id,
132
+ resolution: run.resolution,
133
+ setting,
134
+ edges_added: 0,
135
+ routes_added: 0,
136
+ note: `Analyzer failed: ${error instanceof Error ? error.message : String(error)}.`,
137
+ });
138
+ continue;
139
+ }
140
+ for (const edge of edges) {
141
+ bucketEdges[bucketForKind(edge.kind)].push(edge);
142
+ }
143
+ routeEdges.push(...routes);
144
+ if (edges.length + routes.length > 0) {
145
+ analyzersUsed.push(analyzer.id);
146
+ }
147
+ entries.push({ id: analyzer.id, resolution: run.resolution, setting, edges_added: edges.length, routes_added: routes.length });
148
+ }
149
+ const applied = analyzersUsed.length > 0;
150
+ const record = {
151
+ status: applied ? "applied" : "omitted",
152
+ analyzers: entries,
153
+ };
154
+ // The graph this obligation produces: the enriched bundle when analyzers
155
+ // contributed, otherwise the regex floor. Phase 4B may then rewrite the
156
+ // reasons of low-confidence edges on whichever graph stands — the floor's
157
+ // heuristic edges exist regardless of analyzers.
158
+ const graphBundle = applied
159
+ ? {
160
+ ...floor,
161
+ graphs: {
162
+ ...floor.graphs,
163
+ imports: mergeAnalyzerEdges(floor.graphs.imports ?? [], bucketEdges.imports),
164
+ calls: mergeAnalyzerEdges(floor.graphs.calls ?? [], bucketEdges.calls),
165
+ references: mergeAnalyzerEdges(floor.graphs.references ?? [], bucketEdges.references),
166
+ ...(routeEdges.length > 0
167
+ ? { routes: mergeRoutes(floor.graphs.routes ?? [], routeEdges) }
168
+ : {}),
169
+ },
170
+ analyzers_used: [...new Set(analyzersUsed)].sort(),
171
+ }
172
+ : floor;
173
+ let reasoned = { rewritten: 0, candidates: 0 };
174
+ if (options.llmEdgeReasoning === true && options.edgeReasoning) {
175
+ reasoned = applyEdgeReasoning(graphBundle, options.edgeReasoning);
176
+ }
177
+ const graphChanged = applied || reasoned.rewritten > 0;
178
+ const reasonSuffix = reasoned.rewritten > 0
179
+ ? ` Edge reasoning rewrote ${reasoned.rewritten} reason(s).`
180
+ : "";
181
+ if (!graphChanged) {
182
+ return {
183
+ updated: { ...bundle, analyzer_capability: record },
184
+ artifacts_written: ["analyzer_capability.json"],
185
+ progress_summary: "Graph enrichment omitted; deterministic regex graph retained.",
186
+ };
187
+ }
188
+ const totalEdges = entries.reduce((sum, entry) => sum + entry.edges_added, 0);
189
+ return {
190
+ updated: { ...bundle, graph_bundle: graphBundle, analyzer_capability: record },
191
+ artifacts_written: ["graph_bundle.json", "analyzer_capability.json"],
192
+ progress_summary: applied
193
+ ? `Graph enrichment applied ${totalEdges} analyzer edge(s) from ${analyzersUsed.join(", ")}.${reasonSuffix}`
194
+ : `Graph enrichment omitted analyzers; edge reasoning rewrote ${reasoned.rewritten} reason(s).`,
195
+ };
196
+ }
@@ -2,6 +2,8 @@ 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
+ import type { SynthesisNarrative } from "@audit-tools/shared";
5
7
  export interface ExecutorRunResult {
6
8
  updated: ArtifactBundle;
7
9
  artifacts_written: string[];
@@ -15,9 +17,18 @@ export declare function runIntakeExecutor(bundle: ArtifactBundle, root: string):
15
17
  export declare function runStructureExecutor(bundle: ArtifactBundle, root?: string): Promise<ExecutorRunResult>;
16
18
  export declare function runDesignAssessmentExecutor(bundle: ArtifactBundle): ExecutorRunResult;
17
19
  export declare function runDesignReviewAutoComplete(bundle: ArtifactBundle): ExecutorRunResult;
18
- export declare function runPlanningExecutor(bundle: ArtifactBundle, root: string, lineIndex?: Record<string, number>): Promise<ExecutorRunResult>;
20
+ export declare function runPlanningExecutor(bundle: ArtifactBundle, root: string, lineIndex?: Record<string, number>, sizeIndex?: Record<string, number>, scope?: AuditScopeManifest): Promise<ExecutorRunResult>;
19
21
  export declare function runResultIngestionExecutor(bundle: ArtifactBundle, results: AuditResult[]): ExecutorRunResult;
20
- export declare function runRuntimeValidationExecutor(bundle: ArtifactBundle, root: string): Promise<ExecutorRunResult>;
22
+ export declare function runRuntimeValidationExecutor(bundle: ArtifactBundle, root: string, options?: {
23
+ opentoken?: boolean;
24
+ }): Promise<ExecutorRunResult>;
21
25
  export declare function runRuntimeValidationUpdateExecutor(bundle: ArtifactBundle, updates: RuntimeValidationReport): ExecutorRunResult;
22
26
  export declare function runSynthesisExecutor(bundle: ArtifactBundle, results?: AuditResult[]): ExecutorRunResult;
27
+ /**
28
+ * Resolve the optional synthesis-narrative obligation. When a host/provider
29
+ * narrative is supplied it is merged into the canonical findings report and the
30
+ * human report is re-rendered with themes/executive-summary/top-risks; without
31
+ * one the narrative is recorded as omitted and the deterministic report stands.
32
+ */
33
+ export declare function runSynthesisNarrativeExecutor(bundle: ArtifactBundle, narrative?: SynthesisNarrative): ExecutorRunResult;
23
34
  export declare function runExternalAnalyzerImportExecutor(bundle: ArtifactBundle, externalResults: ExternalAnalyzerResults): ExecutorRunResult;
@@ -5,12 +5,13 @@ import { buildCriticalFlowManifest } from "../extractors/flows.js";
5
5
  import { buildRiskRegister } from "../extractors/risk.js";
6
6
  import { buildSurfaceManifest } from "../extractors/surfaces.js";
7
7
  import { initializeCoverageFromPlan } from "./planning.js";
8
+ import { applyScopeToCoverage, fullAuditScope } from "./scope.js";
8
9
  import { buildFlowCoverage } from "./flowCoverage.js";
9
10
  import { buildRequeuePayload } from "./requeueCommand.js";
10
11
  import { buildRuntimeValidationTasks, discoverRuntimeValidationCommand, mergeRuntimeValidationReport, } from "./runtimeValidation.js";
11
- import { buildAuditReportModel, renderAuditReportMarkdown, } from "../reporting/synthesis.js";
12
+ import { applyNarrative, buildAuditFindingsReport, buildAuditReportModel, renderAuditReportMarkdown, } from "../reporting/synthesis.js";
12
13
  import { buildChunkedAuditTasks, } from "./taskBuilder.js";
13
- import { buildAuditPlanMetrics, buildReviewPackets, } from "./reviewPackets.js";
14
+ import { buildAuditPlanMetrics, buildReviewPackets, sizeIndexFromManifest, } from "./reviewPackets.js";
14
15
  import { buildUnitManifest } from "./unitBuilder.js";
15
16
  import { buildRepoManifestFromFs } from "../extractors/fsIntake.js";
16
17
  import { loadIgnoreFile } from "../extractors/ignore.js";
@@ -27,6 +28,7 @@ function appendSelectiveDeepeningTasks(params) {
27
28
  return { bundle: params.bundle, taskCount: 0, artifacts: [] };
28
29
  }
29
30
  const lineIndex = lineIndexFromTasks(params.bundle.audit_tasks);
31
+ const sizeIndex = sizeIndexFromManifest(params.bundle.repo_manifest);
30
32
  const selectiveDeepeningTasks = buildSelectiveDeepeningTasks({
31
33
  existingTasks: params.bundle.audit_tasks,
32
34
  results: params.results,
@@ -46,18 +48,33 @@ function appendSelectiveDeepeningTasks(params) {
46
48
  audit_plan_metrics: buildAuditPlanMetrics(auditTasks, {
47
49
  graphBundle: params.bundle.graph_bundle,
48
50
  lineIndex,
51
+ sizeIndex,
49
52
  }),
50
53
  review_packets: buildReviewPackets(auditTasks, {
51
54
  graphBundle: params.bundle.graph_bundle,
52
55
  lineIndex,
56
+ sizeIndex,
53
57
  }),
54
58
  },
55
59
  taskCount: selectiveDeepeningTasks.length,
56
60
  artifacts: ["audit_tasks.json", "audit_plan_metrics.json", "review_packets.json"],
57
61
  };
58
62
  }
59
- async function runCommand(command, cwd) {
60
- const spawnCommand = resolveRuntimeValidationSpawnCommand(command);
63
+ function resolveOpentokenWrap(resolved, platform = process.platform) {
64
+ if (platform === "win32") {
65
+ const shell = process.env.ComSpec ?? "cmd.exe";
66
+ const inner = [resolved.command, ...resolved.args]
67
+ .map((v) => (/^[A-Za-z0-9_./:=@+-]+$/.test(v) ? v : `"${v.replace(/(["^&|<>%])/g, "^$1")}"`))
68
+ .join(" ");
69
+ return { command: shell, args: ["/d", "/s", "/c", `opentoken wrap ${inner}`] };
70
+ }
71
+ return { command: "opentoken", args: ["wrap", resolved.command, ...resolved.args] };
72
+ }
73
+ async function runCommand(command, cwd, options = {}) {
74
+ let spawnCommand = resolveRuntimeValidationSpawnCommand(command);
75
+ if (options.opentoken) {
76
+ spawnCommand = resolveOpentokenWrap(spawnCommand);
77
+ }
61
78
  const displayCommand = command.join(" ");
62
79
  return await new Promise((resolve) => {
63
80
  const child = spawn(spawnCommand.command, spawnCommand.args, {
@@ -121,7 +138,7 @@ export async function runIntakeExecutor(bundle, root) {
121
138
  const repoManifest = await buildRepoManifestFromFs({
122
139
  root,
123
140
  ignore,
124
- hash_files: false,
141
+ hash_files: true,
125
142
  });
126
143
  const disposition = buildFileDisposition(repoManifest);
127
144
  const auditableCount = disposition.files.filter((file) => !isAuditExcludedStatus(file.status)).length;
@@ -192,6 +209,11 @@ export function runDesignAssessmentExecutor(bundle) {
192
209
  criticalFlows: bundle.critical_flows,
193
210
  riskRegister: bundle.risk_register,
194
211
  });
212
+ const previous = bundle.design_assessment;
213
+ if (previous?.reviewed) {
214
+ designAssessment.reviewed = true;
215
+ designAssessment.review_findings = previous.review_findings ?? [];
216
+ }
195
217
  return {
196
218
  updated: {
197
219
  ...bundle,
@@ -220,10 +242,11 @@ export function runDesignReviewAutoComplete(bundle) {
220
242
  progress_summary: "Design review auto-completed (host-agent review available via next-step).",
221
243
  };
222
244
  }
223
- export async function runPlanningExecutor(bundle, root, lineIndex = {}) {
245
+ export async function runPlanningExecutor(bundle, root, lineIndex = {}, sizeIndex, scope) {
224
246
  if (!bundle.repo_manifest) {
225
247
  throw new Error("Cannot run planning executor without repo_manifest");
226
248
  }
249
+ const resolvedSizeIndex = sizeIndex ?? sizeIndexFromManifest(bundle.repo_manifest);
227
250
  if (!bundle.file_disposition ||
228
251
  !bundle.unit_manifest ||
229
252
  !bundle.surface_manifest ||
@@ -231,9 +254,13 @@ export async function runPlanningExecutor(bundle, root, lineIndex = {}) {
231
254
  !bundle.risk_register) {
232
255
  throw new Error("Cannot run planning executor without current structure artifacts");
233
256
  }
257
+ const resolvedScope = scope ?? fullAuditScope();
234
258
  const externalAnalyzerResults = bundle.external_analyzer_results;
235
259
  const coverage = initializeCoverageFromPlan(bundle.repo_manifest, bundle.unit_manifest, bundle.file_disposition, externalAnalyzerResults);
236
260
  const skippedTrivialPaths = autoCompleteTrivialCoverage(coverage, lineIndex, externalAnalyzerResults);
261
+ // Delta scope: only seed + expanded files stay pending; the rest inherit prior
262
+ // completion or are excluded from this run. Full scope is a no-op.
263
+ applyScopeToCoverage(coverage, resolvedScope, bundle.coverage_matrix);
237
264
  const flowCoverage = buildFlowCoverage(bundle.critical_flows, coverage);
238
265
  const runtimeCommand = await discoverRuntimeValidationCommand(root);
239
266
  const runtimeValidationTasks = buildRuntimeValidationTasks({
@@ -256,15 +283,21 @@ export async function runPlanningExecutor(bundle, root, lineIndex = {}) {
256
283
  const reviewPackets = buildReviewPackets(taggedAuditTasks, {
257
284
  graphBundle: bundle.graph_bundle,
258
285
  lineIndex,
286
+ sizeIndex: resolvedSizeIndex,
259
287
  });
260
288
  const auditPlanMetrics = buildAuditPlanMetrics(taggedAuditTasks, {
261
289
  graphBundle: bundle.graph_bundle,
262
290
  lineIndex,
291
+ sizeIndex: resolvedSizeIndex,
263
292
  });
264
293
  const requeuePayload = buildRequeuePayload(coverage, bundle.critical_flows, flowCoverage, externalAnalyzerResults);
294
+ const scopeSummary = resolvedScope.mode === "delta"
295
+ ? ` Delta scope since ${resolvedScope.since}: ${resolvedScope.seed_files.length} changed file(s) + ${resolvedScope.expanded_files.length} graph neighbour(s) queued; a full audit is advised before release.`
296
+ : "";
265
297
  return {
266
298
  updated: {
267
299
  ...bundle,
300
+ scope: resolvedScope,
268
301
  coverage_matrix: coverage,
269
302
  flow_coverage: flowCoverage,
270
303
  runtime_validation_tasks: runtimeValidationTasks,
@@ -276,6 +309,7 @@ export async function runPlanningExecutor(bundle, root, lineIndex = {}) {
276
309
  audit_report: undefined,
277
310
  },
278
311
  artifacts_written: [
312
+ "scope.json",
279
313
  "coverage_matrix.json",
280
314
  "flow_coverage.json",
281
315
  "runtime_validation_tasks.json",
@@ -286,6 +320,7 @@ export async function runPlanningExecutor(bundle, root, lineIndex = {}) {
286
320
  "requeue_tasks.json",
287
321
  ],
288
322
  progress_summary: `Built planning artifacts; generated ${taggedAuditTasks.length} review tasks in ${reviewPackets.length} packet(s) and ${requeuePayload.task_count} requeue tasks.` +
323
+ scopeSummary +
289
324
  (skippedTrivialPaths.length > 0
290
325
  ? ` Skipped ${skippedTrivialPaths.length} trivial path${skippedTrivialPaths.length === 1 ? "" : "s"} from semantic review.`
291
326
  : "") +
@@ -354,7 +389,7 @@ export function runResultIngestionExecutor(bundle, results) {
354
389
  : ""),
355
390
  };
356
391
  }
357
- export async function runRuntimeValidationExecutor(bundle, root) {
392
+ export async function runRuntimeValidationExecutor(bundle, root, options = {}) {
358
393
  if (!bundle.runtime_validation_tasks) {
359
394
  throw new Error("Cannot execute runtime validation without runtime_validation_tasks");
360
395
  }
@@ -378,7 +413,7 @@ export async function runRuntimeValidationExecutor(bundle, root) {
378
413
  continue;
379
414
  }
380
415
  const signature = task.command.join("\0");
381
- const outcome = byCommand.get(signature) ?? (await runCommand(task.command, root));
416
+ const outcome = byCommand.get(signature) ?? (await runCommand(task.command, root, { opentoken: options.opentoken }));
382
417
  byCommand.set(signature, outcome);
383
418
  byTaskId.set(task.id, {
384
419
  task_id: task.id,
@@ -441,10 +476,9 @@ export function runRuntimeValidationUpdateExecutor(bundle, updates) {
441
476
  : ""),
442
477
  };
443
478
  }
444
- export function runSynthesisExecutor(bundle, results) {
445
- const finalResults = results ?? bundle.audit_results ?? [];
446
- const model = buildAuditReportModel({
447
- results: finalResults,
479
+ function buildBaseFindingsReport(bundle, results) {
480
+ return buildAuditFindingsReport(buildAuditReportModel({
481
+ results,
448
482
  unitManifest: bundle.unit_manifest,
449
483
  graphBundle: bundle.graph_bundle,
450
484
  criticalFlows: bundle.critical_flows,
@@ -452,15 +486,77 @@ export function runSynthesisExecutor(bundle, results) {
452
486
  runtimeValidationReport: bundle.runtime_validation_report,
453
487
  externalAnalyzerResults: bundle.external_analyzer_results,
454
488
  designAssessment: bundle.design_assessment,
455
- });
489
+ }));
490
+ }
491
+ export function runSynthesisExecutor(bundle, results) {
492
+ const finalResults = results ?? bundle.audit_results ?? [];
493
+ // Emit the canonical machine contract and render the human report from it.
494
+ // No narrative yet — that is layered by the synthesis-narrative obligation.
495
+ const findings = buildBaseFindingsReport(bundle, finalResults);
456
496
  return {
457
497
  updated: {
458
498
  ...bundle,
459
499
  audit_results: finalResults,
460
- audit_report: renderAuditReportMarkdown(model),
500
+ audit_findings: findings,
501
+ audit_report: renderAuditReportMarkdown(findings, { scope: bundle.scope }),
502
+ },
503
+ artifacts_written: ["audit-findings.json", "audit-report.md"],
504
+ progress_summary: `Rendered deterministic audit report and canonical findings for ${finalResults.length} audit result entries.`,
505
+ };
506
+ }
507
+ /**
508
+ * Resolve the optional synthesis-narrative obligation. When a host/provider
509
+ * narrative is supplied it is merged into the canonical findings report and the
510
+ * human report is re-rendered with themes/executive-summary/top-risks; without
511
+ * one the narrative is recorded as omitted and the deterministic report stands.
512
+ */
513
+ export function runSynthesisNarrativeExecutor(bundle, narrative) {
514
+ const baseReport = bundle.audit_findings ??
515
+ buildBaseFindingsReport(bundle, bundle.audit_results ?? []);
516
+ const needsBaseWrite = !bundle.audit_findings;
517
+ const hasNarrative = Boolean(narrative &&
518
+ ((narrative.themes?.length ?? 0) > 0 ||
519
+ (narrative.executive_summary?.trim().length ?? 0) > 0 ||
520
+ (narrative.top_risks?.length ?? 0) > 0));
521
+ if (!hasNarrative) {
522
+ const record = {
523
+ status: "omitted",
524
+ theme_count: 0,
525
+ executive_summary_present: false,
526
+ top_risk_count: 0,
527
+ };
528
+ return {
529
+ updated: {
530
+ ...bundle,
531
+ audit_findings: baseReport,
532
+ synthesis_narrative: record,
533
+ },
534
+ artifacts_written: needsBaseWrite
535
+ ? ["audit-findings.json", "synthesis-narrative.json"]
536
+ : ["synthesis-narrative.json"],
537
+ progress_summary: "Synthesis narrative omitted; deterministic findings report retained.",
538
+ };
539
+ }
540
+ const enriched = applyNarrative(baseReport, narrative);
541
+ const record = {
542
+ status: "applied",
543
+ theme_count: enriched.themes?.length ?? 0,
544
+ executive_summary_present: (enriched.executive_summary?.trim().length ?? 0) > 0,
545
+ top_risk_count: enriched.top_risks?.length ?? 0,
546
+ };
547
+ return {
548
+ updated: {
549
+ ...bundle,
550
+ audit_findings: enriched,
551
+ audit_report: renderAuditReportMarkdown(enriched, { scope: bundle.scope }),
552
+ synthesis_narrative: record,
461
553
  },
462
- artifacts_written: ["audit-report.md"],
463
- progress_summary: `Rendered deterministic audit report for ${finalResults.length} audit result entries.`,
554
+ artifacts_written: [
555
+ "audit-findings.json",
556
+ "audit-report.md",
557
+ "synthesis-narrative.json",
558
+ ],
559
+ progress_summary: `Synthesis narrative applied: ${record.theme_count} theme(s), ${record.top_risk_count} top risk(s).`,
464
560
  };
465
561
  }
466
562
  export function runExternalAnalyzerImportExecutor(bundle, externalResults) {
@@ -1,32 +1,13 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { spawnSync } from "node:child_process";
3
3
  import { delimiter, extname, isAbsolute, join } from "node:path";
4
- function isWindowsBatchCommand(path) {
5
- return process.platform === "win32" && /\.(cmd|bat)$/i.test(path);
6
- }
7
- function quoteForCmd(arg) {
8
- if (arg.length === 0)
9
- return '""';
10
- if (!/[\s"]/u.test(arg))
11
- return arg;
12
- return `"${arg.replace(/"/g, '""')}"`;
13
- }
4
+ import { resolveExecArgv } from "@audit-tools/shared";
14
5
  function toSpawnTuple(candidate) {
15
- if (!isWindowsBatchCommand(candidate.command)) {
16
- return {
17
- command: candidate.command,
18
- args: candidate.args,
19
- };
20
- }
21
- return {
22
- command: process.env.ComSpec ?? "cmd.exe",
23
- args: [
24
- "/d",
25
- "/s",
26
- "/c",
27
- [candidate.command, ...candidate.args].map(quoteForCmd).join(" "),
28
- ],
29
- };
6
+ // Shared resolver applies the single Windows `.cmd`/`.bat` wrapping impl.
7
+ // The candidate command is already PATH-resolved (absolute path or
8
+ // process.execPath), so package-manager shim mapping is a no-op here.
9
+ const resolved = resolveExecArgv([candidate.command, ...candidate.args]);
10
+ return { command: resolved[0], args: resolved.slice(1) };
30
11
  }
31
12
  function resolveFromPath(command) {
32
13
  if (command.trim().length === 0) {
@@ -1,9 +1,10 @@
1
1
  import type { ArtifactBundle } from "../io/artifacts.js";
2
- import type { AuditState } from "../types/auditState.js";
2
+ import type { AuditObligation, AuditState } from "../types/auditState.js";
3
3
  export interface NextStepDecision {
4
4
  state: AuditState;
5
5
  selected_obligation: string | null;
6
6
  selected_executor: string | null;
7
7
  reason: string;
8
8
  }
9
+ export declare function findObligation(obligations: AuditObligation[]): AuditObligation | undefined;
9
10
  export declare function decideNextStep(bundle: ArtifactBundle): NextStepDecision;
@@ -6,6 +6,7 @@ const PRIORITY = [
6
6
  "auto_fixes_applied",
7
7
  "syntax_resolved",
8
8
  "structure_artifacts",
9
+ "graph_enrichment_current",
9
10
  "design_assessment_current",
10
11
  "design_review_completed",
11
12
  "planning_artifacts",
@@ -13,8 +14,9 @@ const PRIORITY = [
13
14
  "audit_results_ingested",
14
15
  "runtime_validation_current",
15
16
  "synthesis_current",
17
+ "synthesis_narrative_current",
16
18
  ];
17
- function findObligation(obligations) {
19
+ export function findObligation(obligations) {
18
20
  for (const id of PRIORITY) {
19
21
  const item = obligations.find((o) => o.id === id);
20
22
  if (item && (item.state === "missing" || item.state === "stale")) {
@@ -1,4 +1,4 @@
1
1
  import type { CoverageMatrix, RepoManifest, UnitManifest } from "../types.js";
2
- import type { FileDisposition } from "../types/disposition.js";
2
+ import type { FileDisposition } from "@audit-tools/shared";
3
3
  import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
4
4
  export declare function initializeCoverageFromPlan(repoManifest: RepoManifest, unitManifest: UnitManifest, disposition: FileDisposition, externalAnalyzerResults?: ExternalAnalyzerResults): CoverageMatrix;
@@ -1,7 +1,7 @@
1
1
  import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
2
2
  import type { 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 buildRequeuePayload(matrix: CoverageMatrix, criticalFlows?: CriticalFlowManifest, flowCoverage?: FlowCoverageManifest, externalAnalyzerResults?: ExternalAnalyzerResults): {
6
6
  task_count: number;
7
7
  file_task_count: number;
@@ -1,20 +1,53 @@
1
1
  import type { AuditTask } from "../types.js";
2
2
  import type { AuditPlanMetrics, ReviewPacket } from "../types/reviewPlanning.js";
3
- import type { GraphBundle } from "../types/graph.js";
3
+ import type { GraphBundle, GraphEdge } from "@audit-tools/shared";
4
4
  export declare const ESTIMATED_TOKENS_PER_LINE = 4;
5
5
  export declare const ESTIMATED_PACKET_PROMPT_TOKENS = 900;
6
- export declare function estimateTaskGroupTokens(tasks: AuditTask[]): number;
6
+ /**
7
+ * Build a path → size_bytes index from a repo manifest. Byte counts are
8
+ * recorded during intake, so this never reads files. Review packet token
9
+ * estimates are derived from these bytes (Phase 2) instead of counted lines.
10
+ */
11
+ export declare function sizeIndexFromManifest(repoManifest?: {
12
+ files: ReadonlyArray<{
13
+ path: string;
14
+ size_bytes: number;
15
+ }>;
16
+ }): Record<string, number>;
17
+ export declare function estimateTaskGroupTokens(tasks: AuditTask[], sizeIndex?: Record<string, number>, lineIndex?: Record<string, number>): number;
18
+ /**
19
+ * Fan-in / fan-out degree above which a node is treated as a hub. Exported so
20
+ * the Phase 3 delta-scope expansion skips the same hubs that packet planning
21
+ * skips, preventing scope blow-up through highly-connected modules.
22
+ */
23
+ export declare const HIGH_FAN_DEGREE_THRESHOLD = 12;
7
24
  export interface BuildReviewPacketOptions {
8
25
  graphBundle?: GraphBundle;
9
26
  lineIndex?: Record<string, number>;
27
+ /** Path → size_bytes (from the repo manifest); drives byte-based token sizing. */
28
+ sizeIndex?: Record<string, number>;
10
29
  maxTasksPerPacket?: number;
11
- targetPacketLines?: number;
30
+ /**
31
+ * Soft per-packet content-token budget. Defaults to
32
+ * DEFAULT_TARGET_PACKET_TOKENS. A packet is split when its estimated content
33
+ * tokens would exceed this budget.
34
+ */
35
+ targetPacketTokens?: number;
12
36
  /**
13
37
  * Available context budget in tokens (context_tokens − reserved_output_tokens).
14
- * When provided, targetPacketLines is capped to fit within this budget.
38
+ * When provided, targetPacketTokens is capped to fit within this budget so a
39
+ * packet's estimated tokens never exceed it.
15
40
  */
16
41
  maxContextTokens?: number;
17
42
  }
43
+ export declare function normalizeGraphPath(path: string): string;
44
+ export declare function collectGraphEdges(graphBundle?: GraphBundle): GraphEdge[];
45
+ export declare function graphEdgeConfidence(edge: GraphEdge): number;
46
+ export interface GraphDegreeIndex {
47
+ fanIn: Map<string, number>;
48
+ fanOut: Map<string, number>;
49
+ }
50
+ export declare function buildGraphDegreeIndex(edges: GraphEdge[]): GraphDegreeIndex;
18
51
  export declare function buildReviewPackets(tasks: AuditTask[], options?: BuildReviewPacketOptions): ReviewPacket[];
19
52
  export declare function orderTasksForPacketReview(tasks: AuditTask[], options?: BuildReviewPacketOptions): AuditTask[];
20
53
  export declare function buildAuditPlanMetrics(tasks: AuditTask[], options?: BuildReviewPacketOptions & {