auditor-lambda 0.2.6 → 0.2.9
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.
- package/README.md +29 -7
- package/audit-code-wrapper-lib.mjs +1605 -330
- package/dist/adapters/eslint.js +9 -5
- package/dist/cli.d.ts +42 -1
- package/dist/cli.js +192 -80
- package/dist/coverage.d.ts +2 -2
- package/dist/coverage.js +5 -5
- package/dist/extractors/bucketing.d.ts +4 -0
- package/dist/extractors/bucketing.js +6 -2
- package/dist/extractors/disposition.d.ts +4 -0
- package/dist/extractors/disposition.js +15 -2
- package/dist/extractors/fileInventory.js +24 -28
- package/dist/extractors/flows.d.ts +5 -0
- package/dist/extractors/flows.js +25 -39
- package/dist/extractors/pathPatterns.d.ts +13 -3
- package/dist/extractors/pathPatterns.js +116 -53
- package/dist/extractors/risk.js +7 -1
- package/dist/extractors/surfaces.d.ts +4 -0
- package/dist/extractors/surfaces.js +11 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/io/artifacts.d.ts +59 -44
- package/dist/io/artifacts.js +80 -120
- package/dist/io/json.d.ts +2 -0
- package/dist/io/json.js +65 -19
- package/dist/io/runArtifacts.d.ts +2 -1
- package/dist/io/runArtifacts.js +44 -7
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +579 -0
- package/dist/orchestrator/advance.js +84 -56
- package/dist/orchestrator/dependencyMap.js +9 -13
- package/dist/orchestrator/executors.js +7 -2
- package/dist/orchestrator/flowCoverage.js +11 -5
- package/dist/orchestrator/flowPlanning.d.ts +7 -2
- package/dist/orchestrator/flowPlanning.js +46 -21
- package/dist/orchestrator/flowRequeue.js +29 -9
- package/dist/orchestrator/internalExecutors.d.ts +2 -1
- package/dist/orchestrator/internalExecutors.js +130 -69
- package/dist/orchestrator/planning.js +25 -3
- package/dist/orchestrator/requeue.js +20 -5
- package/dist/orchestrator/resultIngestion.js +5 -6
- package/dist/orchestrator/runtimeValidation.d.ts +7 -2
- package/dist/orchestrator/runtimeValidation.js +61 -49
- package/dist/orchestrator/runtimeValidationUpdate.js +2 -4
- package/dist/orchestrator/state.js +18 -13
- package/dist/orchestrator/taskBuilder.d.ts +4 -2
- package/dist/orchestrator/taskBuilder.js +153 -52
- package/dist/orchestrator/trivialAudit.js +8 -5
- package/dist/orchestrator/unitBuilder.d.ts +3 -1
- package/dist/orchestrator/unitBuilder.js +24 -16
- package/dist/prompts/renderWorkerPrompt.d.ts +1 -1
- package/dist/prompts/renderWorkerPrompt.js +19 -10
- package/dist/providers/claudeCodeProvider.d.ts +4 -1
- package/dist/providers/claudeCodeProvider.js +8 -5
- package/dist/providers/localSubprocessProvider.d.ts +4 -0
- package/dist/providers/localSubprocessProvider.js +7 -2
- package/dist/providers/spawnLoggedCommand.d.ts +9 -1
- package/dist/providers/spawnLoggedCommand.js +77 -29
- package/dist/reporting/mergeFindings.js +0 -11
- package/dist/reporting/synthesis.d.ts +26 -21
- package/dist/reporting/synthesis.js +97 -61
- package/dist/reporting/workBlocks.d.ts +12 -3
- package/dist/reporting/workBlocks.js +124 -70
- package/dist/supervisor/operatorHandoff.js +48 -18
- package/dist/supervisor/runLedger.d.ts +1 -1
- package/dist/supervisor/runLedger.js +112 -5
- package/dist/supervisor/sessionConfig.js +10 -10
- package/dist/types/externalAnalyzer.d.ts +3 -0
- package/dist/types/flowCoverage.d.ts +5 -1
- package/dist/types/flowCoverage.js +5 -1
- package/dist/types/flows.d.ts +6 -0
- package/dist/types/flows.js +1 -1
- package/dist/types/runLedger.d.ts +5 -1
- package/dist/types/runLedger.js +6 -1
- package/dist/types/runtimeValidation.d.ts +13 -3
- package/dist/types/runtimeValidation.js +16 -1
- package/dist/types/sessionConfig.d.ts +15 -2
- package/dist/types/sessionConfig.js +15 -1
- package/dist/types/surfaces.d.ts +4 -1
- package/dist/types/surfaces.js +1 -1
- package/dist/types/workerSession.d.ts +9 -0
- package/dist/types/workerSession.js +5 -1
- package/dist/types.d.ts +4 -7
- package/dist/validation/artifacts.d.ts +1 -1
- package/dist/validation/artifacts.js +33 -20
- package/dist/validation/auditResults.d.ts +2 -2
- package/dist/validation/auditResults.js +71 -114
- package/dist/validation/basic.d.ts +9 -1
- package/dist/validation/basic.js +40 -3
- package/dist/validation/sessionConfig.d.ts +4 -2
- package/dist/validation/sessionConfig.js +62 -15
- package/docs/agent-integrations.md +67 -38
- package/docs/artifacts.md +16 -56
- package/docs/bootstrap-install.md +60 -30
- package/docs/contract.md +22 -205
- package/docs/next-steps.md +76 -44
- package/docs/packaging.md +27 -3
- package/docs/product-direction.md +22 -0
- package/docs/production-launch-bar.md +4 -2
- package/docs/production-readiness.md +9 -5
- package/docs/releasing.md +98 -0
- package/docs/remediation-baseline.md +75 -0
- package/docs/run-flow.md +23 -11
- package/docs/session-config.md +50 -5
- package/docs/supervisor.md +7 -0
- package/docs/workflow-refactor-brief.md +177 -0
- package/package.json +4 -1
- package/schemas/audit_result.schema.json +8 -7
- package/schemas/audit_task.schema.json +3 -1
- package/schemas/coverage_matrix.schema.json +3 -3
- package/schemas/critical_flows.schema.json +6 -2
- package/schemas/file_disposition.schema.json +2 -2
- package/schemas/finding.schema.json +9 -4
- package/schemas/flow_coverage.schema.json +2 -2
- package/schemas/repo_manifest.schema.json +4 -4
- package/schemas/risk_register.schema.json +2 -2
- package/schemas/runtime_validation_report.schema.json +3 -3
- package/schemas/runtime_validation_tasks.schema.json +8 -2
- package/schemas/surface_manifest.schema.json +6 -3
- package/schemas/unit_manifest.schema.json +3 -2
- package/skills/audit-code/SKILL.md +16 -2
- package/skills/audit-code/audit-code.prompt.md +5 -8
- package/schemas/merged_findings.schema.json +0 -19
- package/schemas/root_cause_clusters.schema.json +0 -28
- package/schemas/synthesis_report.schema.json +0 -61
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
1
2
|
import { buildFileDisposition } from "../extractors/disposition.js";
|
|
2
3
|
import { buildGraphBundle } from "../extractors/graph.js";
|
|
3
4
|
import { buildCriticalFlowManifest } from "../extractors/flows.js";
|
|
@@ -5,28 +6,50 @@ import { buildRiskRegister } from "../extractors/risk.js";
|
|
|
5
6
|
import { buildSurfaceManifest } from "../extractors/surfaces.js";
|
|
6
7
|
import { initializeCoverageFromPlan } from "./planning.js";
|
|
7
8
|
import { buildFlowCoverage } from "./flowCoverage.js";
|
|
8
|
-
import { buildFlowAwareTaskAugmentations } from "./flowPlanning.js";
|
|
9
9
|
import { buildRequeuePayload } from "./requeueCommand.js";
|
|
10
|
-
import { buildRuntimeValidationTasks,
|
|
11
|
-
import {
|
|
12
|
-
import { buildChunkedAuditTasks,
|
|
10
|
+
import { buildRuntimeValidationTasks, discoverRuntimeValidationCommand, mergeRuntimeValidationReport, } from "./runtimeValidation.js";
|
|
11
|
+
import { buildAuditReportModel, renderAuditReportMarkdown, } from "../reporting/synthesis.js";
|
|
12
|
+
import { buildChunkedAuditTasks, } from "./taskBuilder.js";
|
|
13
13
|
import { buildUnitManifest } from "./unitBuilder.js";
|
|
14
14
|
import { buildRepoManifestFromFs } from "../extractors/fsIntake.js";
|
|
15
15
|
import { loadIgnoreFile } from "../extractors/ignore.js";
|
|
16
16
|
import { ingestAuditResults, updateAuditTaskStatuses, } from "./resultIngestion.js";
|
|
17
17
|
import { updateRuntimeValidationReport } from "./runtimeValidationUpdate.js";
|
|
18
18
|
import { autoCompleteTrivialCoverage } from "./trivialAudit.js";
|
|
19
|
-
function
|
|
20
|
-
return {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
async function runCommand(command, cwd) {
|
|
20
|
+
return await new Promise((resolve) => {
|
|
21
|
+
const child = spawn(command[0], command.slice(1), {
|
|
22
|
+
cwd,
|
|
23
|
+
env: process.env,
|
|
24
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
25
|
+
});
|
|
26
|
+
let stdout = "";
|
|
27
|
+
let stderr = "";
|
|
28
|
+
child.stdout.on("data", (chunk) => {
|
|
29
|
+
stdout += String(chunk);
|
|
30
|
+
});
|
|
31
|
+
child.stderr.on("data", (chunk) => {
|
|
32
|
+
stderr += String(chunk);
|
|
33
|
+
});
|
|
34
|
+
child.on("error", (error) => {
|
|
35
|
+
resolve({
|
|
36
|
+
status: "inconclusive",
|
|
37
|
+
summary: `Failed to execute ${command.join(" ")}: ${error.message}`,
|
|
38
|
+
evidence: [],
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
child.on("exit", (code) => {
|
|
42
|
+
const output = `${stdout}\n${stderr}`.trim();
|
|
43
|
+
const evidence = output.length > 0 ? output.split(/\r?\n/).slice(-10) : [];
|
|
44
|
+
resolve({
|
|
45
|
+
status: code === 0 ? "confirmed" : "not_confirmed",
|
|
46
|
+
summary: code === 0
|
|
47
|
+
? `Deterministic runtime command succeeded: ${command.join(" ")}`
|
|
48
|
+
: `Deterministic runtime command failed with exit code ${code}: ${command.join(" ")}`,
|
|
49
|
+
evidence,
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
30
53
|
}
|
|
31
54
|
export async function runIntakeExecutor(bundle, root) {
|
|
32
55
|
const ignore = await loadIgnoreFile(root);
|
|
@@ -54,8 +77,8 @@ export function runStructureExecutor(bundle) {
|
|
|
54
77
|
const disposition = bundle.file_disposition ?? buildFileDisposition(bundle.repo_manifest);
|
|
55
78
|
const unitManifest = buildUnitManifest(bundle.repo_manifest, disposition);
|
|
56
79
|
const surfaceManifest = buildSurfaceManifest(bundle.repo_manifest, disposition);
|
|
57
|
-
const criticalFlows = buildCriticalFlowManifest(bundle.repo_manifest, surfaceManifest, disposition);
|
|
58
80
|
const graphBundle = buildGraphBundle(bundle.repo_manifest, disposition);
|
|
81
|
+
const criticalFlows = buildCriticalFlowManifest(bundle.repo_manifest, surfaceManifest, disposition);
|
|
59
82
|
const riskRegister = buildRiskRegister(unitManifest, criticalFlows, externalAnalyzerResults);
|
|
60
83
|
return {
|
|
61
84
|
updated: {
|
|
@@ -75,10 +98,13 @@ export function runStructureExecutor(bundle) {
|
|
|
75
98
|
"critical_flows.json",
|
|
76
99
|
"risk_register.json",
|
|
77
100
|
],
|
|
78
|
-
progress_summary: `Built structure artifacts for ${unitManifest.units.length} units and ${criticalFlows.flows.length} critical flows
|
|
101
|
+
progress_summary: `Built structure artifacts for ${unitManifest.units.length} units and ${criticalFlows.flows.length} critical flows.` +
|
|
102
|
+
(criticalFlows.fallback_required
|
|
103
|
+
? " Deterministic flow inference did not fully meet the confidence bar."
|
|
104
|
+
: ""),
|
|
79
105
|
};
|
|
80
106
|
}
|
|
81
|
-
export function runPlanningExecutor(bundle, lineIndex = {}) {
|
|
107
|
+
export async function runPlanningExecutor(bundle, root, lineIndex = {}) {
|
|
82
108
|
if (!bundle.repo_manifest) {
|
|
83
109
|
throw new Error("Cannot run planning executor without repo_manifest");
|
|
84
110
|
}
|
|
@@ -91,16 +117,23 @@ export function runPlanningExecutor(bundle, lineIndex = {}) {
|
|
|
91
117
|
}
|
|
92
118
|
const externalAnalyzerResults = bundle.external_analyzer_results;
|
|
93
119
|
const coverage = initializeCoverageFromPlan(bundle.repo_manifest, bundle.unit_manifest, bundle.file_disposition, externalAnalyzerResults);
|
|
94
|
-
const
|
|
120
|
+
const skippedTrivialPaths = autoCompleteTrivialCoverage(coverage, lineIndex, externalAnalyzerResults);
|
|
95
121
|
const flowCoverage = buildFlowCoverage(bundle.critical_flows, coverage);
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
|
|
122
|
+
const runtimeCommand = await discoverRuntimeValidationCommand(root);
|
|
123
|
+
const runtimeValidationTasks = buildRuntimeValidationTasks({
|
|
124
|
+
unitManifest: bundle.unit_manifest,
|
|
125
|
+
criticalFlows: bundle.critical_flows,
|
|
126
|
+
flowCoverage,
|
|
127
|
+
command: runtimeCommand,
|
|
128
|
+
});
|
|
129
|
+
const runtimeValidationReport = runtimeValidationTasks.tasks.length > 0
|
|
130
|
+
? mergeRuntimeValidationReport(runtimeValidationTasks, bundle.runtime_validation_report)
|
|
131
|
+
: undefined;
|
|
132
|
+
const auditTasks = buildChunkedAuditTasks(coverage, lineIndex, {
|
|
99
133
|
external_analyzer_results: externalAnalyzerResults,
|
|
134
|
+
critical_flows: bundle.critical_flows,
|
|
100
135
|
});
|
|
101
|
-
const
|
|
102
|
-
const flowTasks = buildFlowAwareTaskAugmentations([...baseTasks, ...analyzerTasks], bundle.critical_flows, lineIndex);
|
|
103
|
-
const auditTasks = [...baseTasks, ...analyzerTasks, ...flowTasks].map((task) => ({
|
|
136
|
+
const taggedAuditTasks = auditTasks.map((task) => ({
|
|
104
137
|
...task,
|
|
105
138
|
status: task.status ?? "pending",
|
|
106
139
|
}));
|
|
@@ -112,24 +145,25 @@ export function runPlanningExecutor(bundle, lineIndex = {}) {
|
|
|
112
145
|
flow_coverage: flowCoverage,
|
|
113
146
|
runtime_validation_tasks: runtimeValidationTasks,
|
|
114
147
|
runtime_validation_report: runtimeValidationReport,
|
|
115
|
-
audit_tasks:
|
|
148
|
+
audit_tasks: taggedAuditTasks,
|
|
116
149
|
requeue_tasks: requeuePayload.tasks,
|
|
150
|
+
audit_report: undefined,
|
|
117
151
|
},
|
|
118
152
|
artifacts_written: [
|
|
119
153
|
"coverage_matrix.json",
|
|
120
154
|
"flow_coverage.json",
|
|
121
155
|
"runtime_validation_tasks.json",
|
|
122
|
-
"runtime_validation_report.json",
|
|
156
|
+
...(runtimeValidationReport ? ["runtime_validation_report.json"] : []),
|
|
123
157
|
"audit_tasks.json",
|
|
124
158
|
"requeue_tasks.json",
|
|
125
159
|
],
|
|
126
|
-
progress_summary: `Built planning artifacts; generated ${
|
|
127
|
-
(
|
|
128
|
-
? `
|
|
160
|
+
progress_summary: `Built planning artifacts; generated ${taggedAuditTasks.length} review blocks and ${requeuePayload.task_count} requeue tasks.` +
|
|
161
|
+
(skippedTrivialPaths.length > 0
|
|
162
|
+
? ` Skipped ${skippedTrivialPaths.length} trivial path${skippedTrivialPaths.length === 1 ? "" : "s"} from semantic review.`
|
|
129
163
|
: "") +
|
|
130
|
-
(
|
|
131
|
-
? `
|
|
132
|
-
: ""),
|
|
164
|
+
(runtimeCommand
|
|
165
|
+
? ` Runtime validation will use: ${runtimeCommand.join(" ")}.`
|
|
166
|
+
: " No deterministic runtime validation command was discovered."),
|
|
133
167
|
};
|
|
134
168
|
}
|
|
135
169
|
export function runResultIngestionExecutor(bundle, results) {
|
|
@@ -140,84 +174,110 @@ export function runResultIngestionExecutor(bundle, results) {
|
|
|
140
174
|
const flowCoverage = bundle.critical_flows
|
|
141
175
|
? buildFlowCoverage(bundle.critical_flows, updatedCoverageMatrix)
|
|
142
176
|
: bundle.flow_coverage;
|
|
143
|
-
const runtimeValidationTasks = bundle.unit_manifest
|
|
144
|
-
? buildRuntimeValidationTasks(bundle.unit_manifest, bundle.critical_flows, flowCoverage)
|
|
145
|
-
: bundle.runtime_validation_tasks;
|
|
146
|
-
const runtimeValidationReport = runtimeValidationTasks
|
|
147
|
-
? preserveOrPlaceholder(runtimeValidationTasks, bundle.runtime_validation_report)
|
|
148
|
-
: bundle.runtime_validation_report;
|
|
149
177
|
const requeuePayload = buildRequeuePayload(updatedCoverageMatrix, bundle.critical_flows, flowCoverage, bundle.external_analyzer_results);
|
|
150
178
|
const mergedResults = [...(bundle.audit_results ?? []), ...results];
|
|
151
179
|
const updatedAuditTasks = updateAuditTaskStatuses(bundle.audit_tasks, mergedResults);
|
|
152
|
-
const synthesisReport = buildSynthesisReport(mergedResults, runtimeValidationReport, bundle.external_analyzer_results);
|
|
153
|
-
const synthesisArtifacts = buildSynthesisArtifacts(synthesisReport);
|
|
154
180
|
return {
|
|
155
181
|
updated: {
|
|
156
182
|
...bundle,
|
|
157
183
|
coverage_matrix: updatedCoverageMatrix,
|
|
158
184
|
flow_coverage: flowCoverage,
|
|
159
|
-
runtime_validation_tasks: runtimeValidationTasks,
|
|
160
|
-
runtime_validation_report: runtimeValidationReport,
|
|
161
185
|
audit_results: mergedResults,
|
|
162
186
|
audit_tasks: updatedAuditTasks,
|
|
163
187
|
requeue_tasks: requeuePayload.tasks,
|
|
164
|
-
|
|
188
|
+
audit_report: undefined,
|
|
165
189
|
},
|
|
166
190
|
artifacts_written: [
|
|
167
191
|
"coverage_matrix.json",
|
|
168
192
|
"flow_coverage.json",
|
|
169
|
-
"runtime_validation_tasks.json",
|
|
170
|
-
"runtime_validation_report.json",
|
|
171
193
|
"audit_results.jsonl",
|
|
172
194
|
"audit_tasks.json",
|
|
173
195
|
"requeue_tasks.json",
|
|
174
|
-
"merged_findings.json",
|
|
175
|
-
"root_cause_clusters.json",
|
|
176
|
-
"synthesis_report.json",
|
|
177
196
|
],
|
|
178
197
|
progress_summary: `Ingested ${results.length} audit result entries and refreshed dependent artifacts.`,
|
|
179
198
|
};
|
|
180
199
|
}
|
|
200
|
+
export async function runRuntimeValidationExecutor(bundle, root) {
|
|
201
|
+
if (!bundle.runtime_validation_tasks) {
|
|
202
|
+
throw new Error("Cannot execute runtime validation without runtime_validation_tasks");
|
|
203
|
+
}
|
|
204
|
+
const existing = bundle.runtime_validation_report ?? { results: [] };
|
|
205
|
+
const byTaskId = new Map(existing.results.map((result) => [result.task_id, result]));
|
|
206
|
+
const byCommand = new Map();
|
|
207
|
+
for (const task of bundle.runtime_validation_tasks.tasks) {
|
|
208
|
+
const prior = byTaskId.get(task.id);
|
|
209
|
+
if (prior &&
|
|
210
|
+
["confirmed", "not_confirmed", "inconclusive", "not_required"].includes(prior.status)) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (!task.command || task.command.length === 0) {
|
|
214
|
+
byTaskId.set(task.id, {
|
|
215
|
+
task_id: task.id,
|
|
216
|
+
status: "not_required",
|
|
217
|
+
summary: `No deterministic runtime command was available for ${task.id}.`,
|
|
218
|
+
evidence: [],
|
|
219
|
+
notes: ["Runtime validation was not planned for this task."],
|
|
220
|
+
});
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
const signature = task.command.join("\0");
|
|
224
|
+
const outcome = byCommand.get(signature) ?? (await runCommand(task.command, root));
|
|
225
|
+
byCommand.set(signature, outcome);
|
|
226
|
+
byTaskId.set(task.id, {
|
|
227
|
+
task_id: task.id,
|
|
228
|
+
status: outcome.status,
|
|
229
|
+
summary: outcome.summary,
|
|
230
|
+
evidence: outcome.evidence,
|
|
231
|
+
notes: [`Target paths: ${task.target_paths.join(", ")}`],
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
updated: {
|
|
236
|
+
...bundle,
|
|
237
|
+
runtime_validation_report: {
|
|
238
|
+
results: [...byTaskId.values()].sort((a, b) => a.task_id.localeCompare(b.task_id)),
|
|
239
|
+
},
|
|
240
|
+
audit_report: undefined,
|
|
241
|
+
},
|
|
242
|
+
artifacts_written: ["runtime_validation_report.json"],
|
|
243
|
+
progress_summary: `Executed deterministic runtime validation for ${bundle.runtime_validation_tasks.tasks.length} task(s).`,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
181
246
|
export function runRuntimeValidationUpdateExecutor(bundle, updates) {
|
|
182
247
|
if (!bundle.runtime_validation_tasks) {
|
|
183
248
|
throw new Error("Cannot update runtime validation without runtime_validation_tasks");
|
|
184
249
|
}
|
|
185
|
-
const existingReport = bundle.runtime_validation_report ??
|
|
186
|
-
buildPlaceholderRuntimeValidationReport(bundle.runtime_validation_tasks);
|
|
250
|
+
const existingReport = bundle.runtime_validation_report ?? { results: [] };
|
|
187
251
|
const mergedReport = updateRuntimeValidationReport(bundle.runtime_validation_tasks, existingReport, updates);
|
|
188
|
-
const synthesisReport = buildSynthesisReport(bundle.audit_results ?? [], mergedReport, bundle.external_analyzer_results);
|
|
189
|
-
const synthesisArtifacts = buildSynthesisArtifacts(synthesisReport);
|
|
190
252
|
return {
|
|
191
253
|
updated: {
|
|
192
254
|
...bundle,
|
|
193
255
|
runtime_validation_report: mergedReport,
|
|
194
|
-
|
|
256
|
+
audit_report: undefined,
|
|
195
257
|
},
|
|
196
|
-
artifacts_written: [
|
|
197
|
-
"runtime_validation_report.json",
|
|
198
|
-
"merged_findings.json",
|
|
199
|
-
"root_cause_clusters.json",
|
|
200
|
-
"synthesis_report.json",
|
|
201
|
-
],
|
|
258
|
+
artifacts_written: ["runtime_validation_report.json"],
|
|
202
259
|
progress_summary: `Merged ${updates.results.length} runtime validation updates.`,
|
|
203
260
|
};
|
|
204
261
|
}
|
|
205
262
|
export function runSynthesisExecutor(bundle, results) {
|
|
206
263
|
const finalResults = results ?? bundle.audit_results ?? [];
|
|
207
|
-
const
|
|
208
|
-
|
|
264
|
+
const model = buildAuditReportModel({
|
|
265
|
+
results: finalResults,
|
|
266
|
+
unitManifest: bundle.unit_manifest,
|
|
267
|
+
graphBundle: bundle.graph_bundle,
|
|
268
|
+
criticalFlows: bundle.critical_flows,
|
|
269
|
+
coverageMatrix: bundle.coverage_matrix,
|
|
270
|
+
runtimeValidationReport: bundle.runtime_validation_report,
|
|
271
|
+
externalAnalyzerResults: bundle.external_analyzer_results,
|
|
272
|
+
});
|
|
209
273
|
return {
|
|
210
274
|
updated: {
|
|
211
275
|
...bundle,
|
|
212
276
|
audit_results: finalResults,
|
|
213
|
-
|
|
277
|
+
audit_report: renderAuditReportMarkdown(model),
|
|
214
278
|
},
|
|
215
|
-
artifacts_written: [
|
|
216
|
-
|
|
217
|
-
"root_cause_clusters.json",
|
|
218
|
-
"synthesis_report.json",
|
|
219
|
-
],
|
|
220
|
-
progress_summary: `Refreshed synthesis for ${finalResults.length} audit result entries.`,
|
|
279
|
+
artifacts_written: ["audit-report.md"],
|
|
280
|
+
progress_summary: `Rendered deterministic audit report for ${finalResults.length} audit result entries.`,
|
|
221
281
|
};
|
|
222
282
|
}
|
|
223
283
|
export function runExternalAnalyzerImportExecutor(bundle, externalResults) {
|
|
@@ -226,6 +286,7 @@ export function runExternalAnalyzerImportExecutor(bundle, externalResults) {
|
|
|
226
286
|
updated: {
|
|
227
287
|
...bundle,
|
|
228
288
|
external_analyzer_results: externalResults,
|
|
289
|
+
audit_report: undefined,
|
|
229
290
|
},
|
|
230
291
|
artifacts_written: ["external_analyzer_results.json"],
|
|
231
292
|
progress_summary: summary,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { applyUnitCoverage, createCoverageMatrix, markExcludedPath, } from "../coverage.js";
|
|
2
2
|
import { isAuditExcludedStatus } from "../extractors/disposition.js";
|
|
3
|
+
import { deriveRequiredLensesForPath } from "./unitBuilder.js";
|
|
3
4
|
const CATEGORY_LENS_TABLE = [
|
|
4
5
|
[["security", "secret"], ["security", "correctness"]],
|
|
5
6
|
[["dependency", "vuln"], ["security", "config_deployment"]],
|
|
@@ -21,8 +22,17 @@ function applyAnalyzerCoverage(coverage, externalAnalyzerResults) {
|
|
|
21
22
|
if (!externalAnalyzerResults) {
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
const coverageByPath = new Map(coverage.files.map((file) => [file.path, file]));
|
|
26
|
+
const results = Array.isArray(externalAnalyzerResults.results)
|
|
27
|
+
? externalAnalyzerResults.results
|
|
28
|
+
: [];
|
|
29
|
+
for (const result of results) {
|
|
30
|
+
if (!result ||
|
|
31
|
+
typeof result.path !== "string" ||
|
|
32
|
+
typeof result.category !== "string") {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const record = coverageByPath.get(result.path);
|
|
26
36
|
if (!record || record.audit_status === "excluded") {
|
|
27
37
|
continue;
|
|
28
38
|
}
|
|
@@ -44,9 +54,21 @@ export function initializeCoverageFromPlan(repoManifest, unitManifest, dispositi
|
|
|
44
54
|
markExcludedPath(coverage, file.path, status);
|
|
45
55
|
}
|
|
46
56
|
}
|
|
57
|
+
const unitIdsByPath = new Map();
|
|
47
58
|
for (const unit of unitManifest.units) {
|
|
48
59
|
for (const path of unit.files) {
|
|
49
|
-
|
|
60
|
+
const existing = unitIdsByPath.get(path) ?? [];
|
|
61
|
+
if (!existing.includes(unit.unit_id)) {
|
|
62
|
+
existing.push(unit.unit_id);
|
|
63
|
+
}
|
|
64
|
+
unitIdsByPath.set(path, existing);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const file of repoManifest.files) {
|
|
68
|
+
const unitIds = unitIdsByPath.get(file.path) ?? [];
|
|
69
|
+
const requiredLenses = deriveRequiredLensesForPath(file.path);
|
|
70
|
+
for (const unitId of unitIds) {
|
|
71
|
+
applyUnitCoverage(coverage, file.path, unitId, requiredLenses);
|
|
50
72
|
}
|
|
51
73
|
}
|
|
52
74
|
applyAnalyzerCoverage(coverage, externalAnalyzerResults);
|
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
import { buildRequeueTargets } from "../coverage.js";
|
|
2
|
-
function taskPriority(hasExternalSignal) {
|
|
3
|
-
|
|
2
|
+
function taskPriority(hasExternalSignal, lens) {
|
|
3
|
+
if (hasExternalSignal)
|
|
4
|
+
return "high";
|
|
5
|
+
if (lens === "security" || lens === "data_integrity" || lens === "reliability") {
|
|
6
|
+
return "medium";
|
|
7
|
+
}
|
|
8
|
+
return "low";
|
|
9
|
+
}
|
|
10
|
+
function getExternalSignalPaths(externalAnalyzerResults) {
|
|
11
|
+
const results = Array.isArray(externalAnalyzerResults?.results)
|
|
12
|
+
? externalAnalyzerResults.results
|
|
13
|
+
: [];
|
|
14
|
+
return new Set(results
|
|
15
|
+
.map((item) => item && typeof item.path === "string" && item.path.length > 0
|
|
16
|
+
? item.path
|
|
17
|
+
: null)
|
|
18
|
+
.filter((path) => path !== null));
|
|
4
19
|
}
|
|
5
20
|
export function buildRequeueTasks(matrix, externalAnalyzerResults) {
|
|
6
21
|
const targets = buildRequeueTargets(matrix);
|
|
7
22
|
const tasks = [];
|
|
8
|
-
const externalPaths =
|
|
23
|
+
const externalPaths = getExternalSignalPaths(externalAnalyzerResults);
|
|
9
24
|
for (const target of targets) {
|
|
10
25
|
for (const lens of target.missing_lenses) {
|
|
11
26
|
const hasExternalSignal = externalPaths.has(target.path);
|
|
@@ -15,8 +30,8 @@ export function buildRequeueTasks(matrix, externalAnalyzerResults) {
|
|
|
15
30
|
pass_id: `requeue:${lens}`,
|
|
16
31
|
lens,
|
|
17
32
|
file_paths: [target.path],
|
|
18
|
-
rationale: `
|
|
19
|
-
priority: taskPriority(hasExternalSignal),
|
|
33
|
+
rationale: `Mandatory audit coverage is still missing for ${target.path} under the ${lens} lens.`,
|
|
34
|
+
priority: taskPriority(hasExternalSignal, lens),
|
|
20
35
|
tags: hasExternalSignal ? ["external_analyzer_signal"] : [],
|
|
21
36
|
status: "pending",
|
|
22
37
|
});
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { applyFileCoverage } from "../coverage.js";
|
|
2
2
|
export function ingestAuditResults(coverageMatrix, results) {
|
|
3
3
|
const matrix = JSON.parse(JSON.stringify(coverageMatrix));
|
|
4
|
-
const
|
|
5
|
-
path:
|
|
6
|
-
|
|
7
|
-
end: range.end,
|
|
4
|
+
const fileCoverage = results.flatMap((result) => result.file_coverage.map((coverage) => ({
|
|
5
|
+
path: coverage.path,
|
|
6
|
+
total_lines: coverage.total_lines,
|
|
8
7
|
pass_id: result.pass_id,
|
|
9
8
|
lens: result.lens,
|
|
10
9
|
agent_role: result.agent_role,
|
|
11
10
|
})));
|
|
12
|
-
|
|
11
|
+
applyFileCoverage(matrix, fileCoverage);
|
|
13
12
|
return matrix;
|
|
14
13
|
}
|
|
15
14
|
export function updateAuditTaskStatuses(tasks, results) {
|
|
@@ -2,6 +2,11 @@ import type { UnitManifest } from "../types.js";
|
|
|
2
2
|
import type { FlowCoverageManifest } from "../types/flowCoverage.js";
|
|
3
3
|
import type { CriticalFlowManifest } from "../types/flows.js";
|
|
4
4
|
import type { RuntimeValidationReport, RuntimeValidationTaskManifest } from "../types/runtimeValidation.js";
|
|
5
|
-
export declare function
|
|
6
|
-
export declare function
|
|
5
|
+
export declare function discoverRuntimeValidationCommand(root: string): Promise<string[] | undefined>;
|
|
6
|
+
export declare function buildRuntimeValidationTasks(params: {
|
|
7
|
+
unitManifest: UnitManifest;
|
|
8
|
+
criticalFlows?: CriticalFlowManifest;
|
|
9
|
+
flowCoverage?: FlowCoverageManifest;
|
|
10
|
+
command?: string[];
|
|
11
|
+
}): RuntimeValidationTaskManifest;
|
|
7
12
|
export declare function mergeRuntimeValidationReport(tasks: RuntimeValidationTaskManifest, existing?: RuntimeValidationReport): RuntimeValidationReport;
|
|
@@ -1,23 +1,59 @@
|
|
|
1
|
+
import { access, readFile } from "node:fs/promises";
|
|
1
2
|
function checksForFlow(requiredLenses) {
|
|
2
3
|
const checks = [];
|
|
3
4
|
if (requiredLenses.includes("security")) {
|
|
4
5
|
checks.push("Exercise malformed or unauthorized inputs against the flow.");
|
|
5
6
|
}
|
|
6
7
|
if (requiredLenses.includes("reliability")) {
|
|
7
|
-
checks.push("Exercise retries
|
|
8
|
+
checks.push("Exercise retries or repeated submissions.");
|
|
8
9
|
}
|
|
9
10
|
if (requiredLenses.includes("correctness")) {
|
|
10
|
-
checks.push("Exercise representative success and edge-case
|
|
11
|
+
checks.push("Exercise representative success and edge-case behavior.");
|
|
11
12
|
}
|
|
12
13
|
if (requiredLenses.includes("data_integrity")) {
|
|
13
|
-
checks.push("Verify state transitions and persistence invariants
|
|
14
|
+
checks.push("Verify state transitions and persistence invariants.");
|
|
14
15
|
}
|
|
15
16
|
return checks;
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
+
async function exists(path) {
|
|
19
|
+
try {
|
|
20
|
+
await access(path);
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export async function discoverRuntimeValidationCommand(root) {
|
|
28
|
+
const packageJsonPath = `${root}/package.json`;
|
|
29
|
+
if (await exists(packageJsonPath)) {
|
|
30
|
+
try {
|
|
31
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
32
|
+
const testScript = packageJson.scripts?.test?.trim();
|
|
33
|
+
if (testScript &&
|
|
34
|
+
!/no test specified/i.test(testScript)) {
|
|
35
|
+
return ["npm", "test"];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// ignore unreadable package.json for runtime discovery
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (await exists(`${root}/go.mod`)) {
|
|
43
|
+
return ["go", "test", "./..."];
|
|
44
|
+
}
|
|
45
|
+
if (await exists(`${root}/pyproject.toml`) || await exists(`${root}/pytest.ini`)) {
|
|
46
|
+
return ["python", "-m", "pytest"];
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
export function buildRuntimeValidationTasks(params) {
|
|
51
|
+
if (!params.command) {
|
|
52
|
+
return { tasks: [] };
|
|
53
|
+
}
|
|
18
54
|
const tasks = [];
|
|
19
55
|
const seen = new Set();
|
|
20
|
-
const highRiskUnits = unitManifest.units.filter((unit) => (unit.risk_score ?? 0) >= 5 ||
|
|
56
|
+
const highRiskUnits = params.unitManifest.units.filter((unit) => (unit.risk_score ?? 0) >= 5 ||
|
|
21
57
|
unit.required_lenses.includes("security") ||
|
|
22
58
|
unit.required_lenses.includes("data_integrity"));
|
|
23
59
|
for (const unit of highRiskUnits) {
|
|
@@ -31,16 +67,17 @@ export function buildRuntimeValidationTasks(unitManifest, criticalFlows, flowCov
|
|
|
31
67
|
target_paths: unit.files,
|
|
32
68
|
reason: `Unit ${unit.unit_id} is high risk or touches sensitive concerns.`,
|
|
33
69
|
priority: (unit.risk_score ?? 0) >= 7 ? "high" : "medium",
|
|
70
|
+
command: params.command,
|
|
34
71
|
suggested_checks: [
|
|
35
|
-
"Run
|
|
36
|
-
"
|
|
72
|
+
"Run the deterministic runtime command for the repository.",
|
|
73
|
+
"Confirm the affected unit does not regress under the command output.",
|
|
37
74
|
],
|
|
38
75
|
source_artifacts: ["unit_manifest.json", "risk_register.json"],
|
|
39
76
|
});
|
|
40
77
|
}
|
|
41
|
-
if (criticalFlows && flowCoverage) {
|
|
42
|
-
const flowMap = new Map(criticalFlows.flows.map((flow) => [flow.id, flow]));
|
|
43
|
-
for (const record of flowCoverage.flows) {
|
|
78
|
+
if (params.criticalFlows && params.flowCoverage) {
|
|
79
|
+
const flowMap = new Map(params.criticalFlows.flows.map((flow) => [flow.id, flow]));
|
|
80
|
+
for (const record of params.flowCoverage.flows) {
|
|
44
81
|
if (record.status === "complete") {
|
|
45
82
|
continue;
|
|
46
83
|
}
|
|
@@ -56,53 +93,28 @@ export function buildRuntimeValidationTasks(unitManifest, criticalFlows, flowCov
|
|
|
56
93
|
id,
|
|
57
94
|
kind: "critical-flow-check",
|
|
58
95
|
target_paths: flow.paths,
|
|
59
|
-
reason: `Critical flow ${record.flow_id} is still ${record.status} and
|
|
96
|
+
reason: `Critical flow ${record.flow_id} is still ${record.status} and needs deterministic runtime validation.`,
|
|
60
97
|
priority: record.status === "pending" ? "high" : "medium",
|
|
98
|
+
command: params.command,
|
|
61
99
|
suggested_checks: checksForFlow(record.required_lenses),
|
|
62
100
|
source_artifacts: ["critical_flows.json", "flow_coverage.json"],
|
|
63
101
|
});
|
|
64
102
|
}
|
|
65
103
|
}
|
|
66
|
-
const RUNTIME_TASK_CAP = 100;
|
|
67
|
-
if (tasks.length > RUNTIME_TASK_CAP) {
|
|
68
|
-
process.stderr.write(`[runtime-validation] generated ${tasks.length} tasks which exceeds the cap of ${RUNTIME_TASK_CAP}; truncating to avoid runaway expansion\n`);
|
|
69
|
-
tasks.splice(RUNTIME_TASK_CAP);
|
|
70
|
-
}
|
|
71
104
|
return { tasks };
|
|
72
105
|
}
|
|
73
|
-
export function buildPlaceholderRuntimeValidationReport(tasks) {
|
|
74
|
-
return {
|
|
75
|
-
results: tasks.tasks.map((task) => ({
|
|
76
|
-
task_id: task.id,
|
|
77
|
-
status: "pending",
|
|
78
|
-
summary: `No runtime evidence recorded yet for ${task.id}.`,
|
|
79
|
-
evidence: [],
|
|
80
|
-
notes: ["Placeholder entry generated from runtime validation task list."],
|
|
81
|
-
})),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
106
|
export function mergeRuntimeValidationReport(tasks, existing) {
|
|
85
107
|
const existingMap = new Map((existing?.results ?? []).map((result) => [result.task_id, result]));
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
return {
|
|
100
|
-
task_id: task.id,
|
|
101
|
-
status: "pending",
|
|
102
|
-
summary: `No runtime evidence recorded yet for ${task.id}.`,
|
|
103
|
-
evidence: [],
|
|
104
|
-
notes: ["Placeholder entry generated from runtime validation task list."],
|
|
105
|
-
};
|
|
106
|
-
});
|
|
107
|
-
return { results: mergedResults };
|
|
108
|
+
return {
|
|
109
|
+
results: tasks.tasks.map((task) => {
|
|
110
|
+
const prior = existingMap.get(task.id);
|
|
111
|
+
return (prior ?? {
|
|
112
|
+
task_id: task.id,
|
|
113
|
+
status: "pending",
|
|
114
|
+
summary: `Deterministic runtime validation has not executed yet for ${task.id}.`,
|
|
115
|
+
evidence: [],
|
|
116
|
+
notes: [],
|
|
117
|
+
});
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
108
120
|
}
|
|
@@ -38,11 +38,9 @@ export function updateRuntimeValidationReport(tasks, existing, updates) {
|
|
|
38
38
|
merged.set(task.id, {
|
|
39
39
|
task_id: task.id,
|
|
40
40
|
status: "pending",
|
|
41
|
-
summary: `
|
|
41
|
+
summary: `Deterministic runtime validation has not executed yet for ${task.id}.`,
|
|
42
42
|
evidence: [],
|
|
43
|
-
notes: [
|
|
44
|
-
"Placeholder entry generated from runtime validation task list.",
|
|
45
|
-
],
|
|
43
|
+
notes: [],
|
|
46
44
|
});
|
|
47
45
|
}
|
|
48
46
|
}
|