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