auditor-lambda 0.10.2 → 0.10.7
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-build.mjs +198 -0
- package/audit-code-wrapper-install-hosts.mjs +1140 -0
- package/audit-code-wrapper-io.mjs +155 -0
- package/audit-code-wrapper-legacy.mjs +125 -0
- package/audit-code-wrapper-lib.mjs +17 -1801
- package/audit-code-wrapper-opencode.mjs +256 -0
- package/dispatch/merge-results.mjs +5 -3
- package/dispatch/validate-result.mjs +2 -2
- package/dist/adapters/coverageSummary.js +6 -2
- package/dist/adapters/normalizeExternal.js +16 -1
- package/dist/adapters/npmAudit.js +20 -9
- package/dist/adapters/semgrep.js +26 -1
- package/dist/cli/advanceAuditCommand.d.ts +1 -0
- package/dist/cli/advanceAuditCommand.js +95 -0
- package/dist/cli/args.js +1 -2
- package/dist/cli/auditStep.js +2 -2
- package/dist/cli/cleanup.d.ts +11 -1
- package/dist/cli/cleanup.js +25 -5
- package/dist/cli/cleanupCommand.d.ts +1 -0
- package/dist/cli/cleanupCommand.js +24 -0
- package/dist/cli/dispatch.d.ts +55 -31
- package/dist/cli/dispatch.js +298 -241
- package/dist/cli/dispatchStatusCommand.d.ts +1 -0
- package/dist/cli/dispatchStatusCommand.js +68 -0
- package/dist/cli/explainTaskCommand.d.ts +1 -0
- package/dist/cli/explainTaskCommand.js +33 -0
- package/dist/cli/importExternalAnalyzerCommand.d.ts +1 -0
- package/dist/cli/importExternalAnalyzerCommand.js +20 -0
- package/dist/cli/ingestResultsCommand.d.ts +1 -0
- package/dist/cli/ingestResultsCommand.js +34 -0
- package/dist/cli/intakeCommand.d.ts +1 -0
- package/dist/cli/intakeCommand.js +17 -0
- package/dist/cli/lineIndex.js +19 -12
- package/dist/cli/mergeAndIngestCommand.js +68 -26
- package/dist/cli/nextStepCommand.d.ts +139 -0
- package/dist/cli/nextStepCommand.js +281 -232
- package/dist/cli/planCommand.d.ts +1 -0
- package/dist/cli/planCommand.js +16 -0
- package/dist/cli/prepareDispatchCommand.d.ts +1 -0
- package/dist/cli/prepareDispatchCommand.js +25 -0
- package/dist/cli/quotaCommand.d.ts +1 -0
- package/dist/cli/quotaCommand.js +56 -0
- package/dist/cli/requeueCommand.d.ts +1 -0
- package/dist/cli/requeueCommand.js +10 -0
- package/dist/cli/runToCompletion.js +451 -412
- package/dist/cli/sampleRunCommand.d.ts +1 -0
- package/dist/cli/sampleRunCommand.js +93 -0
- package/dist/cli/statusCommand.js +1 -1
- package/dist/cli/steps.js +4 -1
- package/dist/cli/submitPacketCommand.js +16 -15
- package/dist/cli/synthesizeCommand.d.ts +1 -0
- package/dist/cli/synthesizeCommand.js +15 -0
- package/dist/cli/updateRuntimeValidationCommand.d.ts +1 -0
- package/dist/cli/updateRuntimeValidationCommand.js +16 -0
- package/dist/cli/validateCommand.d.ts +1 -0
- package/dist/cli/validateCommand.js +41 -0
- package/dist/cli/validateResultCommand.d.ts +1 -0
- package/dist/cli/validateResultCommand.js +63 -0
- package/dist/cli/validateResultsCommand.d.ts +1 -0
- package/dist/cli/validateResultsCommand.js +31 -0
- package/dist/cli/workerRunCommand.d.ts +15 -1
- package/dist/cli/workerRunCommand.js +40 -4
- package/dist/cli.d.ts +3 -2
- package/dist/cli.js +21 -628
- package/dist/coverage.js +7 -3
- package/dist/extractors/analyzers/css.js +2 -2
- package/dist/extractors/analyzers/html.js +2 -2
- package/dist/extractors/analyzers/python.js +2 -2
- package/dist/extractors/analyzers/registry.js +17 -36
- package/dist/extractors/analyzers/treeSitter.d.ts +10 -1
- package/dist/extractors/analyzers/treeSitter.js +28 -6
- package/dist/extractors/analyzers/typescript.js +104 -85
- package/dist/extractors/browserExtension.js +4 -1
- package/dist/extractors/designAssessment.js +21 -21
- package/dist/extractors/fsIntake.js +34 -10
- package/dist/extractors/graph.js +17 -7
- package/dist/extractors/graphManifestEdges/cargo.d.ts +4 -0
- package/dist/extractors/graphManifestEdges/cargo.js +107 -0
- package/dist/extractors/graphManifestEdges/go.d.ts +5 -0
- package/dist/extractors/graphManifestEdges/go.js +151 -0
- package/dist/extractors/graphManifestEdges/index.d.ts +8 -0
- package/dist/extractors/graphManifestEdges/index.js +11 -0
- package/dist/extractors/graphManifestEdges/jsonc.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/jsonc.js +97 -0
- package/dist/extractors/graphManifestEdges/maven.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/maven.js +73 -0
- package/dist/extractors/graphManifestEdges/packageJson.d.ts +19 -0
- package/dist/extractors/graphManifestEdges/packageJson.js +204 -0
- package/dist/extractors/graphManifestEdges/pnpm.d.ts +2 -0
- package/dist/extractors/graphManifestEdges/pnpm.js +42 -0
- package/dist/extractors/graphManifestEdges/pyproject.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/pyproject.js +83 -0
- package/dist/extractors/graphManifestEdges/toml.d.ts +4 -0
- package/dist/extractors/graphManifestEdges/toml.js +68 -0
- package/dist/extractors/graphManifestEdges/typescript.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/typescript.js +56 -0
- package/dist/extractors/graphManifestEdges/workspace.d.ts +10 -0
- package/dist/extractors/graphManifestEdges/workspace.js +72 -0
- package/dist/extractors/graphManifestEdges/yaml.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/yaml.js +59 -0
- package/dist/extractors/graphManifestEdges/yamlPaths.d.ts +4 -0
- package/dist/extractors/graphManifestEdges/yamlPaths.js +89 -0
- package/dist/extractors/graphPythonImports.js +4 -20
- package/dist/extractors/pathPatterns.js +3 -13
- package/dist/io/artifacts.d.ts +1 -1
- package/dist/io/artifacts.js +4 -1
- package/dist/io/runArtifacts.d.ts +8 -2
- package/dist/io/runArtifacts.js +103 -69
- package/dist/io/toolingManifest.js +2 -1
- package/dist/orchestrator/advance.js +36 -0
- package/dist/orchestrator/artifactFreshness.d.ts +1 -1
- package/dist/orchestrator/artifactFreshness.js +1 -1
- package/dist/orchestrator/artifactMetadata.js +5 -5
- package/dist/orchestrator/auditTaskUtils.d.ts +4 -0
- package/dist/orchestrator/auditTaskUtils.js +8 -12
- package/dist/orchestrator/autoFixExecutor.js +40 -26
- package/dist/orchestrator/dependencyMap.js +1 -1
- package/dist/orchestrator/executorResult.d.ts +33 -0
- package/dist/orchestrator/executors.d.ts +7 -0
- package/dist/orchestrator/executors.js +24 -0
- package/dist/orchestrator/fileAnchors.js +42 -29
- package/dist/orchestrator/fileIntegrity.js +6 -1
- package/dist/orchestrator/flowCoverage.js +1 -2
- package/dist/orchestrator/flowPlanning.js +8 -4
- package/dist/orchestrator/graphEnrichmentExecutor.js +67 -45
- package/dist/orchestrator/ingestionExecutors.js +9 -1
- package/dist/orchestrator/intakeExecutors.d.ts +0 -4
- package/dist/orchestrator/intakeExecutors.js +24 -14
- package/dist/orchestrator/localCommands.d.ts +1 -0
- package/dist/orchestrator/localCommands.js +10 -17
- package/dist/orchestrator/nextStep.js +3 -1
- package/dist/orchestrator/requeueCommand.js +4 -0
- package/dist/orchestrator/reviewPacketGraph.js +50 -18
- package/dist/orchestrator/reviewPackets.js +10 -8
- package/dist/orchestrator/runtimeCommand.js +35 -7
- package/dist/orchestrator/runtimeValidationUpdate.js +6 -0
- package/dist/orchestrator/selectiveDeepening/highRiskClean.js +3 -2
- package/dist/orchestrator/selectiveDeepening/lensVerification.js +44 -18
- package/dist/orchestrator/staleness.js +3 -3
- package/dist/orchestrator/state.js +1 -1
- package/dist/orchestrator/syntaxResolutionExecutor.js +17 -24
- package/dist/orchestrator/synthesisExecutors.js +1 -0
- package/dist/orchestrator/taskBuilder.js +5 -4
- package/dist/providers/claudeCodeProvider.js +4 -1
- package/dist/providers/opencodeProvider.js +4 -1
- package/dist/quota/discoveredLimits.js +3 -3
- package/dist/quota/headerExtraction.js +5 -2
- package/dist/quota/headerExtractors/claudeCodeHeaderExtractor.js +3 -0
- package/dist/quota/headerExtractors/index.js +3 -3
- package/dist/quota/index.d.ts +3 -1
- package/dist/quota/index.js +3 -0
- package/dist/reporting/findingIdentity.d.ts +21 -0
- package/dist/reporting/findingIdentity.js +72 -0
- package/dist/reporting/findingRanks.d.ts +3 -0
- package/dist/reporting/findingRanks.js +24 -0
- package/dist/reporting/mergeFindings.js +1 -24
- package/dist/reporting/synthesis.d.ts +3 -1
- package/dist/reporting/synthesis.js +36 -7
- package/dist/reporting/synthesisNarrativePrompt.js +3 -0
- package/dist/reporting/workBlocks.js +1 -14
- package/dist/supervisor/operatorHandoff.js +2 -6
- package/dist/supervisor/runLedger.js +30 -41
- package/dist/types/activeDispatch.d.ts +31 -0
- package/dist/types/activeDispatch.js +2 -0
- package/dist/types.d.ts +21 -4
- package/dist/types.js +24 -16
- package/dist/validation/artifacts.js +3 -0
- package/dist/validation/auditResults.js +8 -2
- package/package.json +2 -2
- package/schemas/audit_findings.schema.json +5 -1
- package/schemas/audit_plan_metrics.schema.json +1 -1
- package/schemas/audit_result.schema.json +5 -6
- package/schemas/audit_task.schema.json +1 -4
- package/schemas/blind_spot_register.schema.json +1 -1
- package/schemas/coverage_matrix.schema.json +2 -8
- package/schemas/finding.schema.json +3 -17
- package/schemas/flow_coverage.schema.json +2 -8
- package/schemas/graph_bundle.schema.json +31 -0
- package/schemas/lens.schema.json +7 -0
- package/schemas/review_packets.schema.json +6 -17
- package/schemas/step_contract.schema.json +8 -2
- package/schemas/unit_manifest.schema.json +1 -4
- package/scripts/postinstall.mjs +3 -1
- package/skills/audit-code/audit-code.prompt.md +2 -3
- package/dist/extractors/graphManifestEdges.d.ts +0 -12
- package/dist/extractors/graphManifestEdges.js +0 -1135
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { readJsonFile, isFileMissingError } from "@audit-tools/shared";
|
|
4
|
+
import { ACTIVE_DISPATCH_FILENAME, loadDispatchResultMap } from "./dispatch.js";
|
|
5
|
+
import { getArtifactsDir } from "./args.js";
|
|
6
|
+
export async function cmdDispatchStatus(argv) {
|
|
7
|
+
const artifactsDir = getArtifactsDir(argv);
|
|
8
|
+
const activeDispatchPath = join(artifactsDir, ACTIVE_DISPATCH_FILENAME);
|
|
9
|
+
let activeDispatch = null;
|
|
10
|
+
try {
|
|
11
|
+
activeDispatch = await readJsonFile(activeDispatchPath);
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
if (!isFileMissingError(e))
|
|
15
|
+
throw e;
|
|
16
|
+
}
|
|
17
|
+
if (!activeDispatch) {
|
|
18
|
+
console.log(JSON.stringify({ status: "no_active_dispatch" }, null, 2));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const runDir = join(artifactsDir, "runs", activeDispatch.run_id);
|
|
22
|
+
const resultMap = await loadDispatchResultMap(runDir);
|
|
23
|
+
if (!resultMap) {
|
|
24
|
+
console.log(JSON.stringify({
|
|
25
|
+
status: "missing_result_map",
|
|
26
|
+
run_id: activeDispatch.run_id,
|
|
27
|
+
}, null, 2));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const packetIds = [...new Set(resultMap.entries.map((e) => e.packet_id))];
|
|
31
|
+
const packetStatus = [];
|
|
32
|
+
for (const pid of packetIds) {
|
|
33
|
+
if (pid === "__prior_dispatch__")
|
|
34
|
+
continue;
|
|
35
|
+
const entries = resultMap.entries.filter((e) => e.packet_id === pid);
|
|
36
|
+
let completed = 0;
|
|
37
|
+
const missing = [];
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
try {
|
|
40
|
+
await readFile(entry.result_path, "utf8");
|
|
41
|
+
completed++;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
missing.push(entry.task_id);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
packetStatus.push({
|
|
48
|
+
packet_id: pid,
|
|
49
|
+
task_count: entries.length,
|
|
50
|
+
completed_count: completed,
|
|
51
|
+
missing_task_ids: missing,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const totalTasks = packetStatus.reduce((s, p) => s + p.task_count, 0);
|
|
55
|
+
const completedTasks = packetStatus.reduce((s, p) => s + p.completed_count, 0);
|
|
56
|
+
const completedPackets = packetStatus.filter((p) => p.missing_task_ids.length === 0).length;
|
|
57
|
+
console.log(JSON.stringify({
|
|
58
|
+
run_id: activeDispatch.run_id,
|
|
59
|
+
dispatch_status: activeDispatch.status,
|
|
60
|
+
created_at: activeDispatch.created_at,
|
|
61
|
+
total_packets: packetStatus.length,
|
|
62
|
+
completed_packets: completedPackets,
|
|
63
|
+
total_tasks: totalTasks,
|
|
64
|
+
completed_tasks: completedTasks,
|
|
65
|
+
missing_tasks: totalTasks - completedTasks,
|
|
66
|
+
packets: packetStatus,
|
|
67
|
+
}, null, 2));
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdExplainTask(argv: string[]): Promise<void>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { loadArtifactBundle } from "../io/artifacts.js";
|
|
2
|
+
import { getArtifactsDir, getFlag } from "./args.js";
|
|
3
|
+
export async function cmdExplainTask(argv) {
|
|
4
|
+
const artifactsDir = getArtifactsDir(argv);
|
|
5
|
+
const taskId = getFlag(argv, "--task-id") ?? argv[3];
|
|
6
|
+
if (!taskId) {
|
|
7
|
+
throw new Error("explain-task requires <task_id> or --task-id <task_id>");
|
|
8
|
+
}
|
|
9
|
+
const bundle = await loadArtifactBundle(artifactsDir);
|
|
10
|
+
const task = [...(bundle.audit_tasks ?? []), ...(bundle.requeue_tasks ?? [])].find((item) => item.task_id === taskId);
|
|
11
|
+
if (!task) {
|
|
12
|
+
throw new Error(`Unknown task_id '${taskId}'.`);
|
|
13
|
+
}
|
|
14
|
+
const coverageEntries = (bundle.coverage_matrix?.files ?? [])
|
|
15
|
+
.filter((file) => task.file_paths.includes(file.path))
|
|
16
|
+
.sort((a, b) => a.path.localeCompare(b.path));
|
|
17
|
+
const matchingResults = (bundle.audit_results ?? []).filter((result) => result.task_id === task.task_id);
|
|
18
|
+
console.log(JSON.stringify({
|
|
19
|
+
artifacts_dir: artifactsDir,
|
|
20
|
+
task_id: task.task_id,
|
|
21
|
+
task,
|
|
22
|
+
file_count: task.file_paths.length,
|
|
23
|
+
coverage_entries: coverageEntries,
|
|
24
|
+
pending_coverage: coverageEntries
|
|
25
|
+
.map((file) => ({
|
|
26
|
+
path: file.path,
|
|
27
|
+
missing_lenses: file.required_lenses.filter((lens) => !file.completed_lenses.includes(lens)),
|
|
28
|
+
}))
|
|
29
|
+
.filter((file) => file.missing_lenses.length > 0),
|
|
30
|
+
matching_result_count: matchingResults.length,
|
|
31
|
+
matching_finding_ids: matchingResults.flatMap((result) => result.findings.map((finding) => finding.id)),
|
|
32
|
+
}, null, 2));
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdImportExternalAnalyzer(argv: string[]): Promise<void>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readJsonFile } from "@audit-tools/shared";
|
|
2
|
+
import { runAuditStep } from "./auditStep.js";
|
|
3
|
+
import { getArtifactsDir, getFlag, getRootDir } from "./args.js";
|
|
4
|
+
export async function cmdImportExternalAnalyzer(argv) {
|
|
5
|
+
const artifactsDir = getArtifactsDir(argv);
|
|
6
|
+
const sourcePath = getFlag(argv, "--external-analyzer-results", `${artifactsDir}/external_analyzer_results.json`);
|
|
7
|
+
const externalAnalyzerResults = await readJsonFile(sourcePath);
|
|
8
|
+
const result = await runAuditStep({
|
|
9
|
+
root: getRootDir(argv),
|
|
10
|
+
artifactsDir,
|
|
11
|
+
preferredExecutor: "external_analyzer_import_executor",
|
|
12
|
+
externalAnalyzerPath: sourcePath,
|
|
13
|
+
});
|
|
14
|
+
console.log(JSON.stringify({
|
|
15
|
+
artifacts_dir: artifactsDir,
|
|
16
|
+
tool: externalAnalyzerResults.tool,
|
|
17
|
+
imported_count: externalAnalyzerResults.results.length,
|
|
18
|
+
selected_executor: result.selected_executor,
|
|
19
|
+
}, null, 2));
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdIngestResults(argv: string[]): Promise<void>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { runAuditStep, ingestBatchAuditResults } from "./auditStep.js";
|
|
2
|
+
import { getArtifactsDir, getBatchResultsDir, getFlag, getRootDir } from "./args.js";
|
|
3
|
+
export async function cmdIngestResults(argv) {
|
|
4
|
+
const artifactsDir = getArtifactsDir(argv);
|
|
5
|
+
const batchResultsDir = getBatchResultsDir(argv);
|
|
6
|
+
if (batchResultsDir && getFlag(argv, "--results")) {
|
|
7
|
+
throw new Error("Use either --results <file> or --batch-results <dir>, not both.");
|
|
8
|
+
}
|
|
9
|
+
if (batchResultsDir) {
|
|
10
|
+
const result = await ingestBatchAuditResults({
|
|
11
|
+
root: getRootDir(argv),
|
|
12
|
+
artifactsDir,
|
|
13
|
+
batchDir: batchResultsDir,
|
|
14
|
+
});
|
|
15
|
+
console.log(JSON.stringify({
|
|
16
|
+
artifacts_dir: artifactsDir,
|
|
17
|
+
imported_files: result.batchFiles,
|
|
18
|
+
selected_executor: result.selected_executor,
|
|
19
|
+
progress_summary: result.progress_summary,
|
|
20
|
+
}, null, 2));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const result = await runAuditStep({
|
|
24
|
+
root: getRootDir(argv),
|
|
25
|
+
artifactsDir,
|
|
26
|
+
preferredExecutor: "result_ingestion_executor",
|
|
27
|
+
auditResultsPath: getFlag(argv, "--results"),
|
|
28
|
+
});
|
|
29
|
+
console.log(JSON.stringify({
|
|
30
|
+
artifacts_dir: artifactsDir,
|
|
31
|
+
selected_executor: result.selected_executor,
|
|
32
|
+
progress_summary: result.progress_summary,
|
|
33
|
+
}, null, 2));
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdIntake(argv: string[]): Promise<void>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { runAuditStep } from "./auditStep.js";
|
|
2
|
+
import { getArtifactsDir, getRootDir, warnIfNotGitRepo } from "./args.js";
|
|
3
|
+
export async function cmdIntake(argv) {
|
|
4
|
+
const root = getRootDir(argv);
|
|
5
|
+
warnIfNotGitRepo(root);
|
|
6
|
+
const artifactsDir = getArtifactsDir(argv);
|
|
7
|
+
const result = await runAuditStep({
|
|
8
|
+
root,
|
|
9
|
+
artifactsDir,
|
|
10
|
+
preferredExecutor: "intake_executor",
|
|
11
|
+
});
|
|
12
|
+
console.log(JSON.stringify({
|
|
13
|
+
artifacts_dir: artifactsDir,
|
|
14
|
+
selected_executor: result.selected_executor,
|
|
15
|
+
progress_summary: result.progress_summary,
|
|
16
|
+
}, null, 2));
|
|
17
|
+
}
|
package/dist/cli/lineIndex.js
CHANGED
|
@@ -8,9 +8,8 @@ import { countLines } from "./args.js";
|
|
|
8
8
|
const LINE_COUNT_BATCH_SIZE = 25;
|
|
9
9
|
export async function buildLineIndex(root, repoManifest) {
|
|
10
10
|
const entries = [];
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const batch = repoManifest.files.slice(i, i + batchSize);
|
|
11
|
+
for (let i = 0; i < repoManifest.files.length; i += LINE_COUNT_BATCH_SIZE) {
|
|
12
|
+
const batch = repoManifest.files.slice(i, i + LINE_COUNT_BATCH_SIZE);
|
|
14
13
|
const results = await Promise.all(batch.map(async (file) => {
|
|
15
14
|
try {
|
|
16
15
|
return [
|
|
@@ -18,7 +17,8 @@ export async function buildLineIndex(root, repoManifest) {
|
|
|
18
17
|
await countLines(resolve(root, file.path)),
|
|
19
18
|
];
|
|
20
19
|
}
|
|
21
|
-
catch {
|
|
20
|
+
catch (err) {
|
|
21
|
+
console.warn(`[lineIndex] Failed to count lines for '${file.path}': ${err instanceof Error ? err.message : String(err)}`);
|
|
22
22
|
return [file.path, 0];
|
|
23
23
|
}
|
|
24
24
|
}));
|
|
@@ -28,14 +28,21 @@ export async function buildLineIndex(root, repoManifest) {
|
|
|
28
28
|
}
|
|
29
29
|
export async function buildLineIndexForPaths(root, paths) {
|
|
30
30
|
const uniquePaths = [...new Set(paths)].sort();
|
|
31
|
-
const entries =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
const entries = [];
|
|
32
|
+
const batchSize = LINE_COUNT_BATCH_SIZE;
|
|
33
|
+
for (let i = 0; i < uniquePaths.length; i += batchSize) {
|
|
34
|
+
const batch = uniquePaths.slice(i, i + batchSize);
|
|
35
|
+
const results = await Promise.all(batch.map(async (path) => {
|
|
36
|
+
try {
|
|
37
|
+
return [path, await countLines(resolve(root, path))];
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.warn(`[lineIndex] Failed to count lines for '${path}': ${err instanceof Error ? err.message : String(err)}`);
|
|
41
|
+
return [path, 0];
|
|
42
|
+
}
|
|
43
|
+
}));
|
|
44
|
+
entries.push(...results);
|
|
45
|
+
}
|
|
39
46
|
return Object.fromEntries(entries);
|
|
40
47
|
}
|
|
41
48
|
export async function addFileLineCountHints(root, tasks) {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { readFile, readdir } from "node:fs/promises";
|
|
1
|
+
import { readFile, readdir, rm } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
2
3
|
import { join, resolve } from "node:path";
|
|
3
4
|
import { isFileMissingError, readJsonFile, writeJsonFile } from "@audit-tools/shared";
|
|
4
5
|
import { validateAuditResults } from "../validation/auditResults.js";
|
|
5
6
|
import { runAuditStep } from "./auditStep.js";
|
|
6
7
|
import { DISPATCH_RESULT_MAP_FILENAME, ACTIVE_DISPATCH_FILENAME, loadDispatchResultMap, entriesByTaskId, buildPendingAuditTasks, } from "./dispatch.js";
|
|
7
8
|
import { addFileLineCountHints } from "./lineIndex.js";
|
|
8
|
-
import { isCanonicalResultFilename, getArtifactsDir, getFlag } from "./args.js";
|
|
9
|
+
import { isCanonicalResultFilename, taskResultPath, getArtifactsDir, getFlag } from "./args.js";
|
|
9
10
|
import { buildWorkerResult } from "./workerResult.js";
|
|
10
11
|
import { PACKET_SCHEMA_FILENAMES } from "../io/runArtifacts.js";
|
|
11
12
|
// Schema pointer files prepare-dispatch copies into task-results/ for optional
|
|
@@ -38,8 +39,31 @@ export async function cmdMergeAndIngest(argv) {
|
|
|
38
39
|
throw e;
|
|
39
40
|
}
|
|
40
41
|
if (priorSummary) {
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
// A completion marker can go stale. Selective deepening appends new pending
|
|
43
|
+
// tasks to the SAME run-id, and — in the no-progress-loop bug — their answers
|
|
44
|
+
// already sit on disk under canonical per-task names while the marker says the
|
|
45
|
+
// run is done. If any pending task has a recoverable on-disk result, the marker
|
|
46
|
+
// no longer reflects reality: discard it and re-process so those answers ingest
|
|
47
|
+
// instead of replaying a no-op forever. A genuinely terminal run (no pending
|
|
48
|
+
// tasks, or pending tasks not yet answered — e.g. a new round handled under a
|
|
49
|
+
// different run-id) still replays cleanly.
|
|
50
|
+
let pendingWithResults = 0;
|
|
51
|
+
try {
|
|
52
|
+
const pending = await readJsonFile(tasksPath);
|
|
53
|
+
for (const task of pending) {
|
|
54
|
+
if (existsSync(taskResultPath(taskResultsDir, task.task_id))) {
|
|
55
|
+
pendingWithResults++;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch { /* no pending-tasks file — treat as terminal and replay */ }
|
|
60
|
+
if (pendingWithResults === 0) {
|
|
61
|
+
console.log(JSON.stringify({ ...priorSummary, idempotent_replay: true }, null, 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
process.stderr.write(`[merge-and-ingest] completion marker for ${runId} is stale: ` +
|
|
65
|
+
`${pendingWithResults} pending task(s) have un-ingested on-disk results; re-processing.\n`);
|
|
66
|
+
await rm(mergeCompletePath, { force: true });
|
|
43
67
|
}
|
|
44
68
|
const workerTask = await readJsonFile(taskPath);
|
|
45
69
|
const resultMap = await loadDispatchResultMap(runDir);
|
|
@@ -116,36 +140,48 @@ export async function cmdMergeAndIngest(argv) {
|
|
|
116
140
|
}
|
|
117
141
|
for (const task of allTasks) {
|
|
118
142
|
const entry = entryByTaskId.get(task.task_id);
|
|
119
|
-
if (!entry) {
|
|
120
|
-
// No result-map entry => this pending task was not dispatched this round.
|
|
121
|
-
// Leave it pending for the next dispatch; it is not a failure.
|
|
122
|
-
notDispatched.push(task.task_id);
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
const filePath = entry.result_path;
|
|
126
143
|
let obj;
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
144
|
+
if (entry) {
|
|
145
|
+
const filePath = entry.result_path;
|
|
146
|
+
try {
|
|
147
|
+
obj = JSON.parse(await readFile(filePath, "utf8"));
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
if (isFileMissingError(e)) {
|
|
151
|
+
const fallback = fallbackByTaskId.get(task.task_id);
|
|
152
|
+
if (fallback) {
|
|
153
|
+
process.stderr.write(`[merge-and-ingest] Recovered result for '${task.task_id}' from unexpected file (matched by task_id)\n`);
|
|
154
|
+
obj = fallback;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
failing.push({
|
|
158
|
+
task_id: task.task_id,
|
|
159
|
+
errors: ["Missing audit result for assigned task."],
|
|
160
|
+
});
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
136
163
|
}
|
|
137
164
|
else {
|
|
138
|
-
failing.push({
|
|
139
|
-
task_id: task.task_id,
|
|
140
|
-
errors: ["Missing audit result for assigned task."],
|
|
141
|
-
});
|
|
165
|
+
failing.push({ task_id: task.task_id, errors: [`Invalid JSON: ${e.message}`] });
|
|
142
166
|
continue;
|
|
143
167
|
}
|
|
144
168
|
}
|
|
145
|
-
|
|
146
|
-
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// No result-map entry => this pending task was not dispatched this round.
|
|
172
|
+
// But its answer may already exist on disk under a canonical per-task name
|
|
173
|
+
// (e.g. a selective-deepening task answered in a prior round whose dispatch
|
|
174
|
+
// manifest was later regenerated empty — the no-progress loop this guards
|
|
175
|
+
// against). Recover it by task_id so it ingests instead of looping forever
|
|
176
|
+
// as "pending"; only when no such file exists is the task genuinely held
|
|
177
|
+
// back for the next dispatch (not a failure).
|
|
178
|
+
const fallback = fallbackByTaskId.get(task.task_id);
|
|
179
|
+
if (!fallback) {
|
|
180
|
+
notDispatched.push(task.task_id);
|
|
147
181
|
continue;
|
|
148
182
|
}
|
|
183
|
+
process.stderr.write(`[merge-and-ingest] Recovered un-dispatched task '${task.task_id}' from on-disk result file (matched by task_id)\n`);
|
|
184
|
+
obj = fallback;
|
|
149
185
|
}
|
|
150
186
|
const record = obj && typeof obj === "object" && !Array.isArray(obj)
|
|
151
187
|
? obj
|
|
@@ -278,6 +314,12 @@ export async function cmdMergeAndIngest(argv) {
|
|
|
278
314
|
// failures stay replayable for retry, and a canary (notDispatched > 0) must NOT
|
|
279
315
|
// be marked complete or the fan-out merge on the same run-id would short-circuit
|
|
280
316
|
// to an idempotent replay and silently drop the fan-out results.
|
|
317
|
+
//
|
|
318
|
+
// Selective deepening appends new pending tasks to the SAME run-id; this marker
|
|
319
|
+
// can therefore go stale once those tasks are later dispatched and answered. The
|
|
320
|
+
// replay guard at the top detects that (a pending task with an on-disk result)
|
|
321
|
+
// and re-processes, so a premature marker self-heals instead of stranding the
|
|
322
|
+
// deepening answers behind an idempotent replay (the no-progress loop).
|
|
281
323
|
if (failing.length === 0 && notDispatched.length === 0) {
|
|
282
324
|
await writeJsonFile(mergeCompletePath, summaryPayload);
|
|
283
325
|
}
|
|
@@ -1 +1,140 @@
|
|
|
1
|
+
import type { AnalyzerSetting, GraphEdge } from "@audit-tools/shared";
|
|
2
|
+
import { type ArtifactBundle } from "../io/artifacts.js";
|
|
3
|
+
import type { AuditState } from "../types/auditState.js";
|
|
4
|
+
import { type AdvanceAuditResult } from "../orchestrator/advance.js";
|
|
5
|
+
import { decideNextStep } from "../orchestrator/nextStep.js";
|
|
6
|
+
import type { AnalyzerPlanEntry } from "../extractors/analyzers/types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Read a JSON file from the `incoming/` subdirectory of `artifactsDir`.
|
|
9
|
+
* Returns `{ value, path }` when the file exists and parses successfully.
|
|
10
|
+
* Returns `undefined` when the file is absent (ENOENT-family errors).
|
|
11
|
+
* Re-throws all other IO errors unchanged.
|
|
12
|
+
*/
|
|
13
|
+
export declare function tryConsumeIncoming<T>(artifactsDir: string, filename: string): Promise<{
|
|
14
|
+
value: T;
|
|
15
|
+
path: string;
|
|
16
|
+
} | undefined>;
|
|
17
|
+
type NextStepParams = {
|
|
18
|
+
root: string;
|
|
19
|
+
artifactsDir: string;
|
|
20
|
+
selfCliPath: string;
|
|
21
|
+
timeoutMs: number;
|
|
22
|
+
maxRuns: number;
|
|
23
|
+
opentoken?: boolean;
|
|
24
|
+
narrativeEnabled?: boolean;
|
|
25
|
+
analyzers?: Record<string, AnalyzerSetting>;
|
|
26
|
+
graphLlmEdgeReasoning?: boolean;
|
|
27
|
+
since?: string;
|
|
28
|
+
};
|
|
29
|
+
type TerminalStepResult = {
|
|
30
|
+
kind: "complete";
|
|
31
|
+
state: AuditState;
|
|
32
|
+
bundle: ArtifactBundle;
|
|
33
|
+
finalReportPath: string;
|
|
34
|
+
} | {
|
|
35
|
+
kind: "blocked";
|
|
36
|
+
state: AuditState;
|
|
37
|
+
bundle: ArtifactBundle;
|
|
38
|
+
reason: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Build the terminal step for a deterministic loop that has stopped advancing
|
|
42
|
+
* (hit the run backstop or the finalization cycle guard). A rendered report is
|
|
43
|
+
* the deliverable: if synthesis already produced one — or the state is formally
|
|
44
|
+
* complete — present it instead of reporting the stopped loop as a bare
|
|
45
|
+
* "blocked" failure. A completed audit must never surface as blocked just
|
|
46
|
+
* because finalization kept churning (e.g. a runtime_validation <-> synthesis
|
|
47
|
+
* ping-pong, or revision churn from filesystem retries) after the report was
|
|
48
|
+
* written. With no report yet, the stop is a genuine block.
|
|
49
|
+
*/
|
|
50
|
+
export declare function buildTerminalStep(params: Pick<NextStepParams, "root" | "artifactsDir">, bundle: ArtifactBundle, state: AuditState, blockedReason: string): Promise<TerminalStepResult>;
|
|
51
|
+
type GraphEnrichmentBranchResult = {
|
|
52
|
+
action: "continue";
|
|
53
|
+
} | {
|
|
54
|
+
action: "return";
|
|
55
|
+
result: {
|
|
56
|
+
kind: "analyzer_install";
|
|
57
|
+
state: AuditState;
|
|
58
|
+
bundle: ArtifactBundle;
|
|
59
|
+
unresolved: AnalyzerPlanEntry[];
|
|
60
|
+
};
|
|
61
|
+
} | {
|
|
62
|
+
action: "return";
|
|
63
|
+
result: {
|
|
64
|
+
kind: "edge_reasoning";
|
|
65
|
+
state: AuditState;
|
|
66
|
+
bundle: ArtifactBundle;
|
|
67
|
+
candidates: GraphEdge[];
|
|
68
|
+
};
|
|
69
|
+
} | {
|
|
70
|
+
action: "fallthrough";
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Handle the `graph_enrichment_executor` incoming-artifact polling block.
|
|
74
|
+
* Checks for pending analyzer install decisions and edge-reasoning results.
|
|
75
|
+
* Returns an action object:
|
|
76
|
+
* - `continue` → caller should `continue` the for-loop (already consumed an artifact).
|
|
77
|
+
* - `return` → caller should return the embedded result to cmdNextStep.
|
|
78
|
+
* - `fallthrough` → no incoming artifacts; fall through to the deterministic executor.
|
|
79
|
+
*/
|
|
80
|
+
export declare function handleGraphEnrichmentBranch(params: Pick<NextStepParams, "root" | "artifactsDir" | "graphLlmEdgeReasoning" | "since" | "opentoken">, bundle: ArtifactBundle, state: AuditState, analyzersRef: {
|
|
81
|
+
value: Record<string, AnalyzerSetting> | undefined;
|
|
82
|
+
}): Promise<GraphEnrichmentBranchResult>;
|
|
83
|
+
type BranchActionResult = {
|
|
84
|
+
action: "continue";
|
|
85
|
+
} | {
|
|
86
|
+
action: "return";
|
|
87
|
+
result: {
|
|
88
|
+
kind: "design_review";
|
|
89
|
+
state: AuditState;
|
|
90
|
+
bundle: ArtifactBundle;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Handle the `design_review` incoming-artifact polling block.
|
|
95
|
+
* Returns `continue` if an incoming findings file was consumed, or `return`
|
|
96
|
+
* with a design_review kind when the host turn is still needed.
|
|
97
|
+
*/
|
|
98
|
+
export declare function handleDesignReviewBranch(params: Pick<NextStepParams, "artifactsDir">, bundle: ArtifactBundle, state: AuditState): Promise<BranchActionResult>;
|
|
99
|
+
type SynthesisNarrativeBranchResult = {
|
|
100
|
+
action: "continue";
|
|
101
|
+
} | {
|
|
102
|
+
action: "return";
|
|
103
|
+
result: {
|
|
104
|
+
kind: "synthesis_narrative";
|
|
105
|
+
state: AuditState;
|
|
106
|
+
bundle: ArtifactBundle;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Handle the `synthesis_narrative_executor` incoming-artifact polling block.
|
|
111
|
+
* Returns `continue` if an incoming narrative file was consumed, or `return`
|
|
112
|
+
* with a synthesis_narrative kind when the host turn is still needed (and
|
|
113
|
+
* narrative is enabled), or `continue` when narrative is disabled (so the
|
|
114
|
+
* deterministic omit runs below).
|
|
115
|
+
*/
|
|
116
|
+
export declare function handleSynthesisNarrativeBranch(params: Pick<NextStepParams, "root" | "artifactsDir" | "narrativeEnabled" | "opentoken">, bundle: ArtifactBundle, state: AuditState): Promise<SynthesisNarrativeBranchResult>;
|
|
117
|
+
/**
|
|
118
|
+
* Execute one deterministic audit step and record its progress. Throws (with
|
|
119
|
+
* cause) if the executor fails, preserving the existing throw-with-cause pattern.
|
|
120
|
+
*/
|
|
121
|
+
export declare function executeAndRecord(params: Pick<NextStepParams, "root" | "artifactsDir" | "graphLlmEdgeReasoning" | "since" | "opentoken" | "maxRuns">, analyzers: Record<string, AnalyzerSetting> | undefined, decision: ReturnType<typeof decideNextStep>, index: number, lastSummary: string): Promise<AdvanceAuditResult>;
|
|
122
|
+
/**
|
|
123
|
+
* Check for a finalization cycle: when iterations outrun distinct artifact
|
|
124
|
+
* states by FINALIZATION_CYCLE_TOLERANCE, the deterministic executors are
|
|
125
|
+
* revisiting states rather than progressing. Returns a terminal-step result
|
|
126
|
+
* when a cycle is detected, or undefined when the run is still progressing.
|
|
127
|
+
*/
|
|
128
|
+
export declare function checkFinalizationCycle(ctx: {
|
|
129
|
+
index: number;
|
|
130
|
+
obligationTrail: string[];
|
|
131
|
+
seenStateSignatures: Set<string>;
|
|
132
|
+
tolerance: number;
|
|
133
|
+
params: Pick<NextStepParams, "artifactsDir" | "maxRuns" | "root">;
|
|
134
|
+
bundle: ArtifactBundle;
|
|
135
|
+
state: AuditState;
|
|
136
|
+
result: AdvanceAuditResult;
|
|
137
|
+
selectedObligation: string | null | undefined;
|
|
138
|
+
}): Promise<TerminalStepResult | undefined>;
|
|
1
139
|
export declare function cmdNextStep(argv: string[]): Promise<void>;
|
|
140
|
+
export {};
|