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.
Files changed (98) hide show
  1. package/audit-code-wrapper-lib.mjs +149 -129
  2. package/dist/adapters/normalizeExternal.js +6 -3
  3. package/dist/cli/args.d.ts +0 -1
  4. package/dist/cli/args.js +0 -6
  5. package/dist/cli/dispatch.js +3 -2
  6. package/dist/cli/lineIndex.js +4 -1
  7. package/dist/cli/mergeAndIngestCommand.d.ts +1 -0
  8. package/dist/cli/mergeAndIngestCommand.js +219 -0
  9. package/dist/cli/nextStepCommand.js +5 -1
  10. package/dist/cli/runToCompletion.d.ts +9 -0
  11. package/dist/cli/runToCompletion.js +655 -480
  12. package/dist/cli/statusCommand.d.ts +1 -0
  13. package/dist/cli/statusCommand.js +113 -0
  14. package/dist/cli/submitPacketCommand.d.ts +1 -0
  15. package/dist/cli/submitPacketCommand.js +155 -0
  16. package/dist/cli/workerResult.d.ts +1 -1
  17. package/dist/cli/workerRunCommand.d.ts +1 -0
  18. package/dist/cli/workerRunCommand.js +88 -0
  19. package/dist/cli.js +14 -563
  20. package/dist/extractors/analyzers/sql.js +4 -1
  21. package/dist/extractors/analyzers/treeSitter.js +29 -15
  22. package/dist/extractors/analyzers/typescript.js +10 -8
  23. package/dist/extractors/designAssessment.js +43 -24
  24. package/dist/extractors/graph.js +139 -73
  25. package/dist/extractors/pathPatterns.js +17 -5
  26. package/dist/io/runArtifactTypes.d.ts +18 -0
  27. package/dist/io/runArtifactTypes.js +1 -0
  28. package/dist/io/runArtifacts.d.ts +2 -18
  29. package/dist/io/runArtifacts.js +14 -3
  30. package/dist/mcp/server.js +9 -0
  31. package/dist/orchestrator/advance.js +37 -22
  32. package/dist/orchestrator/artifactFreshness.js +2 -2
  33. package/dist/orchestrator/autoFixExecutor.d.ts +1 -1
  34. package/dist/orchestrator/autoFixExecutor.js +16 -8
  35. package/dist/orchestrator/dependencyMap.d.ts +1 -1
  36. package/dist/orchestrator/dependencyMap.js +7 -1
  37. package/dist/orchestrator/fileAnchors.js +14 -3
  38. package/dist/orchestrator/flowCoverage.js +1 -0
  39. package/dist/orchestrator/flowRequeue.js +4 -1
  40. package/dist/orchestrator/{internalExecutors.d.ts → ingestionExecutors.d.ts} +0 -6
  41. package/dist/orchestrator/ingestionExecutors.js +237 -0
  42. package/dist/orchestrator/intakeExecutors.d.ts +3 -0
  43. package/dist/orchestrator/intakeExecutors.js +25 -0
  44. package/dist/orchestrator/planningExecutors.d.ts +4 -0
  45. package/dist/orchestrator/planningExecutors.js +95 -0
  46. package/dist/orchestrator/runtimeCommand.js +7 -15
  47. package/dist/orchestrator/selectiveDeepening/conflict.d.ts +8 -0
  48. package/dist/orchestrator/selectiveDeepening/conflict.js +71 -0
  49. package/dist/orchestrator/selectiveDeepening/findingFollowup.d.ts +10 -0
  50. package/dist/orchestrator/selectiveDeepening/findingFollowup.js +52 -0
  51. package/dist/orchestrator/selectiveDeepening/highRiskClean.d.ts +7 -0
  52. package/dist/orchestrator/selectiveDeepening/highRiskClean.js +44 -0
  53. package/dist/orchestrator/selectiveDeepening/index.d.ts +18 -0
  54. package/dist/orchestrator/selectiveDeepening/index.js +128 -0
  55. package/dist/orchestrator/selectiveDeepening/lensVerification.d.ts +12 -0
  56. package/dist/orchestrator/selectiveDeepening/lensVerification.js +242 -0
  57. package/dist/orchestrator/selectiveDeepening/runtimeValidation.d.ts +13 -0
  58. package/dist/orchestrator/selectiveDeepening/runtimeValidation.js +57 -0
  59. package/dist/orchestrator/selectiveDeepening/shared.d.ts +45 -0
  60. package/dist/orchestrator/selectiveDeepening/shared.js +128 -0
  61. package/dist/orchestrator/selectiveDeepening/stewardFollowup.d.ts +6 -0
  62. package/dist/orchestrator/selectiveDeepening/stewardFollowup.js +72 -0
  63. package/dist/orchestrator/selectiveDeepening.d.ts +2 -20
  64. package/dist/orchestrator/selectiveDeepening.js +6 -760
  65. package/dist/orchestrator/staleness.js +3 -3
  66. package/dist/orchestrator/structureExecutors.d.ts +5 -0
  67. package/dist/orchestrator/structureExecutors.js +94 -0
  68. package/dist/orchestrator/taskBuilder.d.ts +2 -2
  69. package/dist/orchestrator/taskBuilder.js +101 -82
  70. package/dist/providers/index.d.ts +7 -0
  71. package/dist/providers/index.js +14 -95
  72. package/dist/quota/discoveredLimits.d.ts +1 -0
  73. package/dist/quota/discoveredLimits.js +7 -1
  74. package/dist/quota/index.d.ts +0 -2
  75. package/dist/quota/index.js +1 -2
  76. package/dist/reporting/workBlocks.js +7 -4
  77. package/dist/types/reviewPlanning.d.ts +23 -16
  78. package/dist/validation/auditResults.js +97 -95
  79. package/dist/validation/sessionConfig.d.ts +2 -2
  80. package/dist/validation/sessionConfig.js +14 -7
  81. package/package.json +4 -3
  82. package/schemas/audit_findings.schema.json +3 -3
  83. package/schemas/critical_flows.schema.json +3 -2
  84. package/schemas/dispatch_quota.schema.json +1 -1
  85. package/schemas/graph_bundle.schema.json +1 -1
  86. package/schemas/review_packets.schema.json +1 -1
  87. package/schemas/step_contract.schema.json +80 -0
  88. package/scripts/postinstall.mjs +19 -2
  89. package/skills/audit-code/opencode-command-template.txt +3 -3
  90. package/dist/orchestrator/internalExecutors.js +0 -424
  91. package/dist/providers/localSubprocessProvider.d.ts +0 -9
  92. package/dist/providers/localSubprocessProvider.js +0 -18
  93. package/dist/providers/subprocessTemplateProvider.d.ts +0 -8
  94. package/dist/providers/subprocessTemplateProvider.js +0 -59
  95. package/dist/providers/vscodeTaskProvider.d.ts +0 -7
  96. package/dist/providers/vscodeTaskProvider.js +0 -14
  97. package/dist/quota/probe.d.ts +0 -10
  98. package/dist/quota/probe.js +0 -18
@@ -0,0 +1,95 @@
1
+ import { initializeCoverageFromPlan } from "./planning.js";
2
+ import { applyScopeToCoverage, fullAuditScope } from "./scope.js";
3
+ import { buildFlowCoverage } from "./flowCoverage.js";
4
+ import { buildRequeuePayload } from "./requeueCommand.js";
5
+ import { buildRuntimeValidationTasks, discoverRuntimeValidationCommand, mergeRuntimeValidationReport, } from "./runtimeValidation.js";
6
+ import { buildChunkedAuditTasks, } from "./taskBuilder.js";
7
+ import { buildAuditPlanMetrics, buildReviewPackets, sizeIndexFromManifest, } from "./reviewPackets.js";
8
+ import { autoCompleteTrivialCoverage } from "./trivialAudit.js";
9
+ export async function runPlanningExecutor(bundle, root, lineIndex = {}, sizeIndex, scope) {
10
+ if (!bundle.repo_manifest) {
11
+ throw new Error("Cannot run planning executor without repo_manifest");
12
+ }
13
+ const resolvedSizeIndex = sizeIndex ?? sizeIndexFromManifest(bundle.repo_manifest);
14
+ if (!bundle.file_disposition ||
15
+ !bundle.unit_manifest ||
16
+ !bundle.surface_manifest ||
17
+ !bundle.critical_flows ||
18
+ !bundle.risk_register) {
19
+ throw new Error("Cannot run planning executor without current structure artifacts");
20
+ }
21
+ const resolvedScope = scope ?? fullAuditScope();
22
+ const externalAnalyzerResults = bundle.external_analyzer_results;
23
+ const coverage = initializeCoverageFromPlan(bundle.repo_manifest, bundle.unit_manifest, bundle.file_disposition, externalAnalyzerResults);
24
+ const skippedTrivialPaths = autoCompleteTrivialCoverage(coverage, lineIndex, externalAnalyzerResults);
25
+ // Delta scope: only seed + expanded files stay pending; the rest inherit prior
26
+ // completion or are excluded from this run. Full scope is a no-op.
27
+ applyScopeToCoverage(coverage, resolvedScope, bundle.coverage_matrix);
28
+ const flowCoverage = buildFlowCoverage(bundle.critical_flows, coverage);
29
+ const runtimeCommand = await discoverRuntimeValidationCommand(root);
30
+ const runtimeValidationTasks = buildRuntimeValidationTasks({
31
+ unitManifest: bundle.unit_manifest,
32
+ criticalFlows: bundle.critical_flows,
33
+ flowCoverage,
34
+ command: runtimeCommand,
35
+ });
36
+ const runtimeValidationReport = runtimeValidationTasks.tasks.length > 0
37
+ ? mergeRuntimeValidationReport(runtimeValidationTasks, bundle.runtime_validation_report)
38
+ : undefined;
39
+ const auditTasks = buildChunkedAuditTasks(coverage, lineIndex, {
40
+ external_analyzer_results: externalAnalyzerResults,
41
+ critical_flows: bundle.critical_flows,
42
+ });
43
+ const taggedAuditTasks = auditTasks.map((task) => ({
44
+ ...task,
45
+ status: task.status ?? "pending",
46
+ }));
47
+ const reviewPackets = buildReviewPackets(taggedAuditTasks, {
48
+ graphBundle: bundle.graph_bundle,
49
+ lineIndex,
50
+ sizeIndex: resolvedSizeIndex,
51
+ });
52
+ const auditPlanMetrics = buildAuditPlanMetrics(taggedAuditTasks, {
53
+ graphBundle: bundle.graph_bundle,
54
+ lineIndex,
55
+ sizeIndex: resolvedSizeIndex,
56
+ });
57
+ const requeuePayload = buildRequeuePayload(coverage, bundle.critical_flows, flowCoverage, externalAnalyzerResults);
58
+ const scopeSummary = resolvedScope.mode === "delta"
59
+ ? ` Delta scope since ${resolvedScope.since}: ${resolvedScope.seed_files.length} changed file(s) + ${resolvedScope.expanded_files.length} graph neighbour(s) queued; a full audit is advised before release.`
60
+ : "";
61
+ return {
62
+ updated: {
63
+ ...bundle,
64
+ scope: resolvedScope,
65
+ coverage_matrix: coverage,
66
+ flow_coverage: flowCoverage,
67
+ runtime_validation_tasks: runtimeValidationTasks,
68
+ runtime_validation_report: runtimeValidationReport,
69
+ audit_tasks: taggedAuditTasks,
70
+ audit_plan_metrics: auditPlanMetrics,
71
+ review_packets: reviewPackets,
72
+ requeue_tasks: requeuePayload.tasks,
73
+ audit_report: undefined,
74
+ },
75
+ artifacts_written: [
76
+ "scope.json",
77
+ "coverage_matrix.json",
78
+ "flow_coverage.json",
79
+ "runtime_validation_tasks.json",
80
+ ...(runtimeValidationReport ? ["runtime_validation_report.json"] : []),
81
+ "audit_tasks.json",
82
+ "audit_plan_metrics.json",
83
+ "review_packets.json",
84
+ "requeue_tasks.json",
85
+ ],
86
+ progress_summary: `Built planning artifacts; generated ${taggedAuditTasks.length} review tasks in ${reviewPackets.length} packet(s) and ${requeuePayload.task_count} requeue tasks.` +
87
+ scopeSummary +
88
+ (skippedTrivialPaths.length > 0
89
+ ? ` Skipped ${skippedTrivialPaths.length} trivial path${skippedTrivialPaths.length === 1 ? "" : "s"} from semantic review.`
90
+ : "") +
91
+ (runtimeCommand
92
+ ? ` Runtime validation will use: ${runtimeCommand.join(" ")}.`
93
+ : " No deterministic runtime validation command was discovered."),
94
+ };
95
+ }
@@ -1,18 +1,16 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { quoteForOpenTokenCmd, wrapForOpenToken } from "@audit-tools/shared";
2
3
  // Deterministic runtime-validation command execution: resolve a command to a
3
4
  // platform-correct spawn invocation (Windows package-manager shims need a
4
5
  // cmd.exe wrapper), optionally wrap it for opentoken accounting, and run it
5
6
  // capturing a confirmed/not_confirmed/inconclusive outcome. Hoisted out of
6
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 `@`.
7
12
  function resolveOpentokenWrap(resolved, platform = process.platform) {
8
- if (platform === "win32") {
9
- const shell = process.env.ComSpec ?? "cmd.exe";
10
- const inner = [resolved.command, ...resolved.args]
11
- .map((v) => (/^[A-Za-z0-9_./:=@+-]+$/.test(v) ? v : `"${v.replace(/(["^&|<>%])/g, "^$1")}"`))
12
- .join(" ");
13
- return { command: shell, args: ["/d", "/s", "/c", `opentoken wrap ${inner}`] };
14
- }
15
- return { command: "opentoken", args: ["wrap", resolved.command, ...resolved.args] };
13
+ return wrapForOpenToken(resolved.command, resolved.args, "opentoken", platform);
16
14
  }
17
15
  export async function runCommand(command, cwd, options = {}) {
18
16
  let spawnCommand = resolveRuntimeValidationSpawnCommand(command);
@@ -66,14 +64,8 @@ export function resolveRuntimeValidationSpawnCommand(command, platform = process
66
64
  if (["npm", "npx", "pnpm", "yarn"].includes(packageManager)) {
67
65
  return {
68
66
  command: shellCommand,
69
- args: ["/d", "/s", "/c", command.map(quoteCmdArg).join(" ")],
67
+ args: ["/d", "/s", "/c", command.map(quoteForOpenTokenCmd).join(" ")],
70
68
  };
71
69
  }
72
70
  return { command: executable, args };
73
71
  }
74
- function quoteCmdArg(value) {
75
- if (/^[A-Za-z0-9_./:=+-]+$/.test(value)) {
76
- return value;
77
- }
78
- return `"${value.replace(/(["^&|<>%])/g, "^$1")}"`;
79
- }
@@ -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[];