auditor-lambda 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -21
- 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/auditStep.js +7 -1
- 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.d.ts +0 -1
- package/dist/cli.js +14 -565
- 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 +151 -75
- package/dist/extractors/pathPatterns.js +17 -5
- package/dist/io/artifacts.d.ts +3 -1
- package/dist/io/artifacts.js +18 -2
- 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 +38 -22
- package/dist/orchestrator/artifactFreshness.js +14 -4
- package/dist/orchestrator/autoFixExecutor.d.ts +2 -2
- package/dist/orchestrator/autoFixExecutor.js +26 -8
- package/dist/orchestrator/dependencyMap.d.ts +1 -1
- package/dist/orchestrator/dependencyMap.js +7 -1
- package/dist/orchestrator/executorResult.d.ts +12 -0
- package/dist/orchestrator/executorResult.js +1 -0
- package/dist/orchestrator/fileAnchors.js +14 -3
- package/dist/orchestrator/fileIntegrity.d.ts +1 -0
- package/dist/orchestrator/fileIntegrity.js +12 -3
- package/dist/orchestrator/flowCoverage.js +1 -0
- package/dist/orchestrator/flowRequeue.js +4 -1
- package/dist/orchestrator/graphEnrichmentExecutor.d.ts +1 -1
- package/dist/orchestrator/graphEnrichmentExecutor.js +3 -1
- package/dist/orchestrator/ingestionExecutors.d.ts +11 -0
- 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/reviewPacketGraph.d.ts +31 -0
- package/dist/orchestrator/reviewPacketGraph.js +691 -0
- package/dist/orchestrator/reviewPackets.d.ts +2 -15
- package/dist/orchestrator/reviewPackets.js +3 -685
- package/dist/orchestrator/runtimeCommand.d.ts +11 -0
- package/dist/orchestrator/runtimeCommand.js +71 -0
- package/dist/orchestrator/scope.js +1 -1
- 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/syntaxResolutionExecutor.d.ts +1 -1
- package/dist/orchestrator/synthesisExecutors.d.ts +12 -0
- package/dist/orchestrator/synthesisExecutors.js +90 -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/docs/development.md +35 -139
- package/docs/history.md +26 -0
- package/docs/product.md +41 -108
- package/package.json +3 -2
- package/schemas/audit_findings.schema.json +6 -5
- package/schemas/critical_flows.schema.json +3 -2
- package/schemas/dispatch_quota.schema.json +3 -1
- package/schemas/external_analyzer_results.schema.json +2 -2
- package/schemas/graph_bundle.schema.json +1 -1
- package/schemas/repo_manifest.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.d.ts +0 -34
- package/dist/orchestrator/internalExecutors.js +0 -581
- 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/docs/handoff.md +0 -204
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function runCommand(command: string[], cwd: string, options?: {
|
|
2
|
+
opentoken?: boolean;
|
|
3
|
+
}): Promise<{
|
|
4
|
+
status: "confirmed" | "not_confirmed" | "inconclusive";
|
|
5
|
+
summary: string;
|
|
6
|
+
evidence: string[];
|
|
7
|
+
}>;
|
|
8
|
+
export declare function resolveRuntimeValidationSpawnCommand(command: string[], platform?: NodeJS.Platform, shellCommand?: string): {
|
|
9
|
+
command: string;
|
|
10
|
+
args: string[];
|
|
11
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { quoteForOpenTokenCmd, wrapForOpenToken } from "@audit-tools/shared";
|
|
3
|
+
// Deterministic runtime-validation command execution: resolve a command to a
|
|
4
|
+
// platform-correct spawn invocation (Windows package-manager shims need a
|
|
5
|
+
// cmd.exe wrapper), optionally wrap it for opentoken accounting, and run it
|
|
6
|
+
// capturing a confirmed/not_confirmed/inconclusive outcome. Hoisted out of
|
|
7
|
+
// internalExecutors.ts as a shared, side-effect-only helper module.
|
|
8
|
+
//
|
|
9
|
+
// The cmd.exe quoting (both for the opentoken wrap and the package-manager
|
|
10
|
+
// batch path) reuses the canonical exec.ts helpers so the safe-character set
|
|
11
|
+
// stays unified — the two formerly-private copies here diverged on `@`.
|
|
12
|
+
function resolveOpentokenWrap(resolved, platform = process.platform) {
|
|
13
|
+
return wrapForOpenToken(resolved.command, resolved.args, "opentoken", platform);
|
|
14
|
+
}
|
|
15
|
+
export async function runCommand(command, cwd, options = {}) {
|
|
16
|
+
let spawnCommand = resolveRuntimeValidationSpawnCommand(command);
|
|
17
|
+
if (options.opentoken) {
|
|
18
|
+
spawnCommand = resolveOpentokenWrap(spawnCommand);
|
|
19
|
+
}
|
|
20
|
+
const displayCommand = command.join(" ");
|
|
21
|
+
return await new Promise((resolve) => {
|
|
22
|
+
const child = spawn(spawnCommand.command, spawnCommand.args, {
|
|
23
|
+
cwd,
|
|
24
|
+
env: process.env,
|
|
25
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
26
|
+
});
|
|
27
|
+
let stdout = "";
|
|
28
|
+
let stderr = "";
|
|
29
|
+
child.stdout.on("data", (chunk) => {
|
|
30
|
+
stdout += String(chunk);
|
|
31
|
+
});
|
|
32
|
+
child.stderr.on("data", (chunk) => {
|
|
33
|
+
stderr += String(chunk);
|
|
34
|
+
});
|
|
35
|
+
child.on("error", (error) => {
|
|
36
|
+
resolve({
|
|
37
|
+
status: "inconclusive",
|
|
38
|
+
summary: `Failed to execute ${displayCommand}: ${error.message}`,
|
|
39
|
+
evidence: [],
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
child.on("exit", (code) => {
|
|
43
|
+
const output = `${stdout}\n${stderr}`.trim();
|
|
44
|
+
const evidence = output.length > 0 ? output.split(/\r?\n/).slice(-10) : [];
|
|
45
|
+
resolve({
|
|
46
|
+
status: code === 0 ? "confirmed" : "not_confirmed",
|
|
47
|
+
summary: code === 0
|
|
48
|
+
? `Deterministic runtime command succeeded: ${displayCommand}`
|
|
49
|
+
: `Deterministic runtime command failed with exit code ${code}: ${displayCommand}`,
|
|
50
|
+
evidence,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
export function resolveRuntimeValidationSpawnCommand(command, platform = process.platform, shellCommand = process.env.ComSpec ?? "cmd.exe") {
|
|
56
|
+
const [executable, ...args] = command;
|
|
57
|
+
if (!executable) {
|
|
58
|
+
return { command: "", args: [] };
|
|
59
|
+
}
|
|
60
|
+
if (platform !== "win32") {
|
|
61
|
+
return { command: executable, args };
|
|
62
|
+
}
|
|
63
|
+
const packageManager = executable.replace(/\.(cmd|bat)$/i, "").toLowerCase();
|
|
64
|
+
if (["npm", "npx", "pnpm", "yarn"].includes(packageManager)) {
|
|
65
|
+
return {
|
|
66
|
+
command: shellCommand,
|
|
67
|
+
args: ["/d", "/s", "/c", command.map(quoteForOpenTokenCmd).join(" ")],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return { command: executable, args };
|
|
71
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { changedFiles, gitRefExists, isGitRepo } from "@audit-tools/shared";
|
|
2
2
|
import { buildDispositionMap } from "../extractors/disposition.js";
|
|
3
3
|
import { buildPathLookup } from "../extractors/graph.js";
|
|
4
|
-
import { HIGH_FAN_DEGREE_THRESHOLD, buildGraphDegreeIndex, collectGraphEdges, graphEdgeConfidence, normalizeGraphPath, } from "./
|
|
4
|
+
import { HIGH_FAN_DEGREE_THRESHOLD, buildGraphDegreeIndex, collectGraphEdges, graphEdgeConfidence, normalizeGraphPath, } from "./reviewPacketGraph.js";
|
|
5
5
|
/** Default cap on in-scope files (seeds + expanded) before expansion stops. */
|
|
6
6
|
export const DEFAULT_SCOPE_MAX_FILES = 200;
|
|
7
7
|
/** Graph edges below this confidence are never traversed during expansion. */
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AuditTask } from "../../types.js";
|
|
2
|
+
import { type FindingContext } from "./shared.js";
|
|
3
|
+
export declare function buildConflictFollowupTask(params: {
|
|
4
|
+
contexts: FindingContext[];
|
|
5
|
+
conflictKey: string;
|
|
6
|
+
lineIndex?: Record<string, number>;
|
|
7
|
+
}): AuditTask;
|
|
8
|
+
export declare function conflictGroups(contexts: FindingContext[]): Map<string, FindingContext[]>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { CONFIDENCE_RANK, DEEPENING_TAG, SEVERITY_RANK, lineCountForPath, sanitizeSegment, taskIdFor, uniqueSorted, } from "./shared.js";
|
|
2
|
+
export function buildConflictFollowupTask(params) {
|
|
3
|
+
const [first] = params.contexts;
|
|
4
|
+
const paths = uniqueSorted(params.contexts.flatMap((context) => context.paths));
|
|
5
|
+
const maxSeverity = Math.max(...params.contexts.map((context) => SEVERITY_RANK[context.finding.severity]));
|
|
6
|
+
const lineSources = new Map();
|
|
7
|
+
for (const context of params.contexts) {
|
|
8
|
+
for (const path of context.paths) {
|
|
9
|
+
if (!lineSources.has(path)) {
|
|
10
|
+
lineSources.set(path, { task: context.task, result: context.result });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const sourceTaskIds = uniqueSorted(params.contexts.map((context) => context.result.task_id));
|
|
15
|
+
const findingIds = uniqueSorted(params.contexts.map((context) => context.finding.id));
|
|
16
|
+
return {
|
|
17
|
+
task_id: taskIdFor("conflict", [
|
|
18
|
+
params.conflictKey,
|
|
19
|
+
...sourceTaskIds,
|
|
20
|
+
...findingIds,
|
|
21
|
+
]),
|
|
22
|
+
unit_id: first?.result.unit_id ?? "selective-deepening",
|
|
23
|
+
pass_id: `deepening:${first?.result.pass_id ?? "conflict"}`,
|
|
24
|
+
lens: (first?.result.lens ?? "correctness"),
|
|
25
|
+
file_paths: paths,
|
|
26
|
+
file_line_counts: Object.fromEntries(paths.map((path) => {
|
|
27
|
+
const source = lineSources.get(path);
|
|
28
|
+
return [
|
|
29
|
+
path,
|
|
30
|
+
source
|
|
31
|
+
? lineCountForPath(path, source.task, source.result, params.lineIndex)
|
|
32
|
+
: (params.lineIndex?.[path] ?? 0),
|
|
33
|
+
];
|
|
34
|
+
})),
|
|
35
|
+
rationale: `Reconcile conflicting audit output for ${params.conflictKey}. ` +
|
|
36
|
+
`Compare source tasks ${sourceTaskIds.join(", ")} and decide the correct severity, confidence, and evidence-backed conclusion.`,
|
|
37
|
+
priority: maxSeverity >= SEVERITY_RANK.high ? "high" : "medium",
|
|
38
|
+
tags: [
|
|
39
|
+
DEEPENING_TAG,
|
|
40
|
+
"trigger:conflicting_output",
|
|
41
|
+
...sourceTaskIds.slice(0, 3).map((id) => `source_task:${sanitizeSegment(id)}`),
|
|
42
|
+
],
|
|
43
|
+
status: "pending",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function conflictGroups(contexts) {
|
|
47
|
+
const groups = new Map();
|
|
48
|
+
for (const context of contexts) {
|
|
49
|
+
for (const path of context.paths) {
|
|
50
|
+
const key = [
|
|
51
|
+
context.result.lens,
|
|
52
|
+
context.finding.category,
|
|
53
|
+
path.toLowerCase(),
|
|
54
|
+
].join(":");
|
|
55
|
+
const group = groups.get(key) ?? [];
|
|
56
|
+
group.push(context);
|
|
57
|
+
groups.set(key, group);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
for (const [key, group] of groups) {
|
|
61
|
+
const uniqueTasks = new Set(group.map((context) => context.result.task_id));
|
|
62
|
+
const severities = group.map((context) => SEVERITY_RANK[context.finding.severity]);
|
|
63
|
+
const confidences = group.map((context) => CONFIDENCE_RANK[context.finding.confidence]);
|
|
64
|
+
const severitySpread = Math.max(...severities) - Math.min(...severities);
|
|
65
|
+
const confidenceSpread = Math.max(...confidences) - Math.min(...confidences);
|
|
66
|
+
if (uniqueTasks.size < 2 || (severitySpread < 2 && confidenceSpread < 2)) {
|
|
67
|
+
groups.delete(key);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return groups;
|
|
71
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AuditResult, AuditTask, Finding } from "../../types.js";
|
|
2
|
+
import { type FindingContext } from "./shared.js";
|
|
3
|
+
export declare function buildFindingFollowupTask(params: {
|
|
4
|
+
result: AuditResult;
|
|
5
|
+
task?: AuditTask;
|
|
6
|
+
finding: Finding;
|
|
7
|
+
triggers: string[];
|
|
8
|
+
lineIndex?: Record<string, number>;
|
|
9
|
+
}): AuditTask;
|
|
10
|
+
export declare function findingContexts(results: AuditResult[], taskById: Map<string, AuditTask>): FindingContext[];
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { DEEPENING_TAG, SEVERITY_RANK, isDeepeningTask, lineCountForPath, pathsForFinding, sanitizeSegment, taskIdFor, } from "./shared.js";
|
|
2
|
+
export function buildFindingFollowupTask(params) {
|
|
3
|
+
const paths = pathsForFinding(params.finding, params.result, params.task);
|
|
4
|
+
const triggerLabel = params.triggers.join("+");
|
|
5
|
+
const taskId = taskIdFor("finding", [
|
|
6
|
+
params.result.task_id,
|
|
7
|
+
params.finding.id,
|
|
8
|
+
triggerLabel,
|
|
9
|
+
]);
|
|
10
|
+
const priority = SEVERITY_RANK[params.finding.severity] >= SEVERITY_RANK.high
|
|
11
|
+
? "high"
|
|
12
|
+
: "medium";
|
|
13
|
+
return {
|
|
14
|
+
task_id: taskId,
|
|
15
|
+
unit_id: params.result.unit_id,
|
|
16
|
+
pass_id: `deepening:${params.result.pass_id}`,
|
|
17
|
+
lens: params.result.lens,
|
|
18
|
+
file_paths: paths,
|
|
19
|
+
file_line_counts: Object.fromEntries(paths.map((path) => [
|
|
20
|
+
path,
|
|
21
|
+
lineCountForPath(path, params.task, params.result, params.lineIndex),
|
|
22
|
+
])),
|
|
23
|
+
rationale: `Follow up on ${params.finding.id} (${params.finding.severity}/${params.finding.confidence}) from ${params.result.task_id}. ` +
|
|
24
|
+
"Verify impact, evidence quality, affected scope, and whether the finding should stand, narrow, or be downgraded.",
|
|
25
|
+
priority,
|
|
26
|
+
tags: [
|
|
27
|
+
DEEPENING_TAG,
|
|
28
|
+
...params.triggers.map((trigger) => `trigger:${trigger}`),
|
|
29
|
+
`source_task:${sanitizeSegment(params.result.task_id)}`,
|
|
30
|
+
`finding:${sanitizeSegment(params.finding.id)}`,
|
|
31
|
+
],
|
|
32
|
+
status: "pending",
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function findingContexts(results, taskById) {
|
|
36
|
+
const contexts = [];
|
|
37
|
+
for (const result of results) {
|
|
38
|
+
const task = taskById.get(result.task_id);
|
|
39
|
+
if (isDeepeningTask(task)) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
for (const finding of result.findings) {
|
|
43
|
+
contexts.push({
|
|
44
|
+
result,
|
|
45
|
+
task,
|
|
46
|
+
finding,
|
|
47
|
+
paths: pathsForFinding(finding, result, task),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return contexts;
|
|
52
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AuditResult, AuditTask } from "../../types.js";
|
|
2
|
+
export declare function isHighRiskCleanResult(result: AuditResult, task: AuditTask | undefined): boolean;
|
|
3
|
+
export declare function buildHighRiskCleanFollowupTask(params: {
|
|
4
|
+
result: AuditResult;
|
|
5
|
+
task?: AuditTask;
|
|
6
|
+
lineIndex?: Record<string, number>;
|
|
7
|
+
}): AuditTask;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { DEEPENING_TAG, isDeepeningTask, lineCountForPath, sanitizeSegment, taskIdFor, uniqueSorted, } from "./shared.js";
|
|
2
|
+
export function isHighRiskCleanResult(result, task) {
|
|
3
|
+
if (result.findings.length > 0 ||
|
|
4
|
+
result.requires_followup === false ||
|
|
5
|
+
isDeepeningTask(task)) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
if (!task) {
|
|
9
|
+
return (result.requires_followup === true &&
|
|
10
|
+
(result.lens === "security" || result.lens === "data_integrity"));
|
|
11
|
+
}
|
|
12
|
+
if (task.priority === "high") {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
if (task.tags?.some((tag) => ["critical_flow", "external_analyzer_signal"].includes(tag))) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
return result.requires_followup === true && task.priority === "medium";
|
|
19
|
+
}
|
|
20
|
+
export function buildHighRiskCleanFollowupTask(params) {
|
|
21
|
+
const paths = uniqueSorted((params.task?.file_paths.length ?? 0) > 0
|
|
22
|
+
? (params.task?.file_paths ?? [])
|
|
23
|
+
: params.result.file_coverage.map((coverage) => coverage.path));
|
|
24
|
+
return {
|
|
25
|
+
task_id: taskIdFor("clean", [params.result.task_id, params.result.lens]),
|
|
26
|
+
unit_id: params.result.unit_id,
|
|
27
|
+
pass_id: `deepening:${params.result.pass_id}`,
|
|
28
|
+
lens: params.result.lens,
|
|
29
|
+
file_paths: paths,
|
|
30
|
+
file_line_counts: Object.fromEntries(paths.map((path) => [
|
|
31
|
+
path,
|
|
32
|
+
lineCountForPath(path, params.task, params.result, params.lineIndex),
|
|
33
|
+
])),
|
|
34
|
+
rationale: `Sample high-risk no-finding result from ${params.result.task_id}. ` +
|
|
35
|
+
"Re-review the assigned files for missed edge cases, hidden runtime failures, and whether the clean conclusion should stand.",
|
|
36
|
+
priority: params.task?.priority === "high" ? "high" : "medium",
|
|
37
|
+
tags: [
|
|
38
|
+
DEEPENING_TAG,
|
|
39
|
+
"trigger:high_risk_no_finding",
|
|
40
|
+
`source_task:${sanitizeSegment(params.result.task_id)}`,
|
|
41
|
+
],
|
|
42
|
+
status: "pending",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AuditTask } from "../../types.js";
|
|
2
|
+
import { type BuildSelectiveDeepeningTaskOptions } from "./shared.js";
|
|
3
|
+
export type { BuildSelectiveDeepeningTaskOptions } from "./shared.js";
|
|
4
|
+
/**
|
|
5
|
+
* Build the bounded set of selective-deepening follow-up tasks. Each strategy
|
|
6
|
+
* (finding-followup, conflict, steward-followup, runtime-validation, lens-
|
|
7
|
+
* verification, high-risk-clean) lives in its own module under this directory;
|
|
8
|
+
* this entry sequences them and enforces the shared budget (`effectiveMax`,
|
|
9
|
+
* `maxTotalDeepeningTasks`) and dedupe (via `pushIfNew`). Task ordering, caps,
|
|
10
|
+
* and dedupe are unchanged from the former single-file implementation.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildSelectiveDeepeningTasks(options: BuildSelectiveDeepeningTaskOptions): AuditTask[];
|
|
13
|
+
export declare const selectiveDeepeningTestUtils: {
|
|
14
|
+
DEEPENING_TAG: string;
|
|
15
|
+
LENS_VERIFICATION_TAG: string;
|
|
16
|
+
LENS_VERIFICATION_FOLLOWUP_TAG: string;
|
|
17
|
+
DEFAULT_MAX_TOTAL_DEEPENING_TASKS: number;
|
|
18
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { DEFAULT_MAX_DEEPENING_TASKS, DEFAULT_MAX_TOTAL_DEEPENING_TASKS, DEEPENING_TAG, LENS_VERIFICATION_FOLLOWUP_TAG, LENS_VERIFICATION_TAG, SEVERITY_RANK, intersects, isDeepeningTask, priorityRank, } from "./shared.js";
|
|
2
|
+
import { buildFindingFollowupTask, findingContexts } from "./findingFollowup.js";
|
|
3
|
+
import { buildConflictFollowupTask, conflictGroups } from "./conflict.js";
|
|
4
|
+
import { buildHighRiskCleanFollowupTask, isHighRiskCleanResult, } from "./highRiskClean.js";
|
|
5
|
+
import { buildRuntimeValidationFollowupTask, runtimeResultNeedsFollowup, runtimeValidationHasStrongStaticFinding, } from "./runtimeValidation.js";
|
|
6
|
+
import { buildLensVerificationTasks } from "./lensVerification.js";
|
|
7
|
+
import { buildVerificationFollowupTasks } from "./stewardFollowup.js";
|
|
8
|
+
/**
|
|
9
|
+
* Build the bounded set of selective-deepening follow-up tasks. Each strategy
|
|
10
|
+
* (finding-followup, conflict, steward-followup, runtime-validation, lens-
|
|
11
|
+
* verification, high-risk-clean) lives in its own module under this directory;
|
|
12
|
+
* this entry sequences them and enforces the shared budget (`effectiveMax`,
|
|
13
|
+
* `maxTotalDeepeningTasks`) and dedupe (via `pushIfNew`). Task ordering, caps,
|
|
14
|
+
* and dedupe are unchanged from the former single-file implementation.
|
|
15
|
+
*/
|
|
16
|
+
export function buildSelectiveDeepeningTasks(options) {
|
|
17
|
+
const taskById = new Map((options.existingTasks ?? []).map((task) => [task.task_id, task]));
|
|
18
|
+
const existingTasks = options.existingTasks ?? [];
|
|
19
|
+
const existingIds = new Set(taskById.keys());
|
|
20
|
+
const maxTasks = options.maxTasks ?? DEFAULT_MAX_DEEPENING_TASKS;
|
|
21
|
+
const maxTotalDeepeningTasks = options.maxTotalDeepeningTasks ?? DEFAULT_MAX_TOTAL_DEEPENING_TASKS;
|
|
22
|
+
const existingDeepeningCount = existingTasks.filter((task) => isDeepeningTask(task)).length;
|
|
23
|
+
if (existingDeepeningCount >= maxTotalDeepeningTasks) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
const remainingBudget = maxTotalDeepeningTasks - existingDeepeningCount;
|
|
27
|
+
const effectiveMax = Math.min(maxTasks, remainingBudget);
|
|
28
|
+
const created = [];
|
|
29
|
+
function pushIfNew(task) {
|
|
30
|
+
if (created.length >= effectiveMax || existingIds.has(task.task_id)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
existingIds.add(task.task_id);
|
|
34
|
+
created.push(task);
|
|
35
|
+
}
|
|
36
|
+
const contexts = findingContexts(options.results, taskById);
|
|
37
|
+
for (const context of contexts) {
|
|
38
|
+
const triggers = [];
|
|
39
|
+
if (SEVERITY_RANK[context.finding.severity] >= SEVERITY_RANK.high) {
|
|
40
|
+
triggers.push("high_severity");
|
|
41
|
+
}
|
|
42
|
+
if (context.finding.confidence === "low") {
|
|
43
|
+
triggers.push("low_confidence");
|
|
44
|
+
}
|
|
45
|
+
if (triggers.length === 0) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
pushIfNew(buildFindingFollowupTask({
|
|
49
|
+
result: context.result,
|
|
50
|
+
task: context.task,
|
|
51
|
+
finding: context.finding,
|
|
52
|
+
triggers,
|
|
53
|
+
lineIndex: options.lineIndex,
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
for (const [key, group] of [...conflictGroups(contexts).entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
57
|
+
pushIfNew(buildConflictFollowupTask({
|
|
58
|
+
contexts: group,
|
|
59
|
+
conflictKey: key,
|
|
60
|
+
lineIndex: options.lineIndex,
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
for (const result of options.results) {
|
|
64
|
+
const task = taskById.get(result.task_id);
|
|
65
|
+
for (const followupTask of buildVerificationFollowupTasks({
|
|
66
|
+
result,
|
|
67
|
+
task,
|
|
68
|
+
lineIndex: options.lineIndex,
|
|
69
|
+
})) {
|
|
70
|
+
pushIfNew(followupTask);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const runtimeTaskById = new Map((options.runtimeValidationTasks?.tasks ?? []).map((task) => [
|
|
74
|
+
task.id,
|
|
75
|
+
task,
|
|
76
|
+
]));
|
|
77
|
+
for (const result of [...(options.runtimeValidationReport?.results ?? [])].sort((a, b) => a.task_id.localeCompare(b.task_id))) {
|
|
78
|
+
if (!runtimeResultNeedsFollowup(result.status)) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const runtimeTask = runtimeTaskById.get(result.task_id);
|
|
82
|
+
if (!runtimeTask || runtimeTask.target_paths.length === 0) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (runtimeValidationHasStrongStaticFinding(runtimeTask, contexts)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const relatedTasks = existingTasks.filter((task) => !isDeepeningTask(task) && intersects(task.file_paths, runtimeTask.target_paths));
|
|
89
|
+
pushIfNew(buildRuntimeValidationFollowupTask({
|
|
90
|
+
runtimeTask,
|
|
91
|
+
runtimeResultStatus: result.status,
|
|
92
|
+
relatedTasks,
|
|
93
|
+
results: options.results,
|
|
94
|
+
lineIndex: options.lineIndex,
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
for (const task of buildLensVerificationTasks({
|
|
98
|
+
existingTasks,
|
|
99
|
+
results: options.results,
|
|
100
|
+
lineIndex: options.lineIndex,
|
|
101
|
+
externalAnalyzerResults: options.externalAnalyzerResults,
|
|
102
|
+
})) {
|
|
103
|
+
pushIfNew(task);
|
|
104
|
+
}
|
|
105
|
+
const cleanResults = options.results
|
|
106
|
+
.map((result) => ({ result, task: taskById.get(result.task_id) }))
|
|
107
|
+
.filter(({ result, task }) => isHighRiskCleanResult(result, task))
|
|
108
|
+
.sort((a, b) => {
|
|
109
|
+
const priorityDelta = priorityRank(b.task?.priority) - priorityRank(a.task?.priority);
|
|
110
|
+
if (priorityDelta !== 0)
|
|
111
|
+
return priorityDelta;
|
|
112
|
+
return a.result.task_id.localeCompare(b.result.task_id);
|
|
113
|
+
});
|
|
114
|
+
for (const { result, task } of cleanResults) {
|
|
115
|
+
pushIfNew(buildHighRiskCleanFollowupTask({
|
|
116
|
+
result,
|
|
117
|
+
task,
|
|
118
|
+
lineIndex: options.lineIndex,
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
return created;
|
|
122
|
+
}
|
|
123
|
+
export const selectiveDeepeningTestUtils = {
|
|
124
|
+
DEEPENING_TAG,
|
|
125
|
+
LENS_VERIFICATION_TAG,
|
|
126
|
+
LENS_VERIFICATION_FOLLOWUP_TAG,
|
|
127
|
+
DEFAULT_MAX_TOTAL_DEEPENING_TASKS,
|
|
128
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AuditResult, AuditTask } from "../../types.js";
|
|
2
|
+
import type { ExternalAnalyzerResults } from "../../types/externalAnalyzer.js";
|
|
3
|
+
export interface LensVerificationSource {
|
|
4
|
+
result: AuditResult;
|
|
5
|
+
task?: AuditTask;
|
|
6
|
+
}
|
|
7
|
+
export declare function buildLensVerificationTasks(params: {
|
|
8
|
+
existingTasks: AuditTask[];
|
|
9
|
+
results: AuditResult[];
|
|
10
|
+
lineIndex?: Record<string, number>;
|
|
11
|
+
externalAnalyzerResults?: ExternalAnalyzerResults;
|
|
12
|
+
}): AuditTask[];
|