auditor-lambda 0.3.5 → 0.3.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-lib.mjs +318 -240
- package/dist/cli.js +85 -1
- package/dist/io/runArtifacts.js +2 -2
- package/dist/orchestrator/internalExecutors.js +1 -0
- package/dist/orchestrator/selectiveDeepening.d.ts +4 -0
- package/dist/orchestrator/selectiveDeepening.js +359 -0
- package/dist/prompts/renderWorkerPrompt.js +3 -4
- package/dist/types.d.ts +9 -0
- package/dist/validation/auditResults.js +158 -0
- package/docs/agent-integrations.md +1 -1
- package/docs/bootstrap-install.md +6 -1
- package/docs/contract.md +3 -0
- package/docs/dispatch-implementation-plan.md +19 -1
- package/docs/github-copilot.md +1 -1
- package/docs/model-selection.md +11 -0
- package/docs/next-steps.md +2 -2
- package/docs/packaging.md +4 -2
- package/docs/production-launch-bar.md +3 -1
- package/docs/production-readiness.md +6 -6
- package/package.json +1 -1
- package/schemas/audit_result.schema.json +28 -0
- package/skills/audit-code/SKILL.md +4 -0
- package/skills/audit-code/audit-code.prompt.md +5 -0
package/dist/cli.js
CHANGED
|
@@ -36,6 +36,9 @@ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
|
36
36
|
const ADVANCE_AUDIT_CONTRACT_VERSION = "audit-code/v1alpha1";
|
|
37
37
|
const WORKER_RESULT_CONTRACT_VERSION = "audit-code-worker-result/v1alpha1";
|
|
38
38
|
const LARGE_FILE_PACKET_TARGET_LINES = 2500;
|
|
39
|
+
const SMALL_MODEL_HINT_MAX_LINES = 500;
|
|
40
|
+
const SMALL_MODEL_HINT_MAX_ESTIMATED_TOKENS = 3000;
|
|
41
|
+
const DEEP_MODEL_HINT_MIN_ESTIMATED_TOKENS = 9000;
|
|
39
42
|
const DIRECT_CLI_DEFAULTS = {
|
|
40
43
|
rootDir: ".",
|
|
41
44
|
artifactsDir: ".artifacts",
|
|
@@ -1474,6 +1477,61 @@ function isIsolatedLargeFilePacket(packet) {
|
|
|
1474
1477
|
return (packet.file_paths.length === 1 &&
|
|
1475
1478
|
packet.total_lines > LARGE_FILE_PACKET_TARGET_LINES);
|
|
1476
1479
|
}
|
|
1480
|
+
function buildDispatchComplexity(packet, largeFileMode) {
|
|
1481
|
+
return {
|
|
1482
|
+
priority: packet.priority,
|
|
1483
|
+
task_count: packet.task_ids.length,
|
|
1484
|
+
file_count: packet.file_paths.length,
|
|
1485
|
+
total_lines: packet.total_lines,
|
|
1486
|
+
estimated_tokens: packet.estimated_tokens,
|
|
1487
|
+
lenses: packet.lenses,
|
|
1488
|
+
tags: packet.tags ?? [],
|
|
1489
|
+
large_file_mode: largeFileMode,
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
function buildDispatchModelHint(complexity) {
|
|
1493
|
+
const deepReasons = [];
|
|
1494
|
+
if (complexity.priority === "high")
|
|
1495
|
+
deepReasons.push("high_priority");
|
|
1496
|
+
if (complexity.large_file_mode)
|
|
1497
|
+
deepReasons.push("isolated_large_file");
|
|
1498
|
+
if (complexity.estimated_tokens >= DEEP_MODEL_HINT_MIN_ESTIMATED_TOKENS) {
|
|
1499
|
+
deepReasons.push("high_estimated_tokens");
|
|
1500
|
+
}
|
|
1501
|
+
if (complexity.tags.some((tag) => tag === "critical_flow" || tag.startsWith("critical_flow:"))) {
|
|
1502
|
+
deepReasons.push("critical_flow");
|
|
1503
|
+
}
|
|
1504
|
+
if (complexity.tags.some((tag) => tag === "external_analyzer_signal" || tag.startsWith("external_tool:"))) {
|
|
1505
|
+
deepReasons.push("external_analyzer_signal");
|
|
1506
|
+
}
|
|
1507
|
+
if (complexity.tags.includes("lens_verification")) {
|
|
1508
|
+
deepReasons.push("lens_verification");
|
|
1509
|
+
}
|
|
1510
|
+
if (deepReasons.length > 0) {
|
|
1511
|
+
return { tier: "deep", reasons: deepReasons };
|
|
1512
|
+
}
|
|
1513
|
+
const sensitiveLenses = new Set(["security", "data_integrity", "reliability"]);
|
|
1514
|
+
const hasSensitiveLens = complexity.lenses.some((lens) => sensitiveLenses.has(lens));
|
|
1515
|
+
if (complexity.priority === "low" &&
|
|
1516
|
+
complexity.total_lines <= SMALL_MODEL_HINT_MAX_LINES &&
|
|
1517
|
+
complexity.estimated_tokens <= SMALL_MODEL_HINT_MAX_ESTIMATED_TOKENS &&
|
|
1518
|
+
!hasSensitiveLens &&
|
|
1519
|
+
complexity.tags.length === 0) {
|
|
1520
|
+
return { tier: "small", reasons: ["small_low_priority_packet"] };
|
|
1521
|
+
}
|
|
1522
|
+
const reasons = [];
|
|
1523
|
+
if (complexity.priority === "medium")
|
|
1524
|
+
reasons.push("medium_priority");
|
|
1525
|
+
if (hasSensitiveLens)
|
|
1526
|
+
reasons.push("sensitive_lens");
|
|
1527
|
+
if (complexity.total_lines > SMALL_MODEL_HINT_MAX_LINES) {
|
|
1528
|
+
reasons.push("moderate_size");
|
|
1529
|
+
}
|
|
1530
|
+
return {
|
|
1531
|
+
tier: "standard",
|
|
1532
|
+
reasons: reasons.length > 0 ? reasons : ["default_review_packet"],
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1477
1535
|
function withinRoot(root, path) {
|
|
1478
1536
|
const rootPath = resolve(root);
|
|
1479
1537
|
const absolutePath = resolve(rootPath, path);
|
|
@@ -1625,15 +1683,31 @@ async function cmdPrepareDispatch(argv) {
|
|
|
1625
1683
|
: [];
|
|
1626
1684
|
const taskSections = packetTasks.flatMap((task) => {
|
|
1627
1685
|
const lensDef = lensDefs[task.lens];
|
|
1686
|
+
const inputLines = task.inputs
|
|
1687
|
+
? Object.entries(task.inputs)
|
|
1688
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
1689
|
+
.map(([key, value]) => `input.${key}: ${value}`)
|
|
1690
|
+
: [];
|
|
1691
|
+
const isLensVerification = task.tags?.includes("lens_verification") ?? false;
|
|
1628
1692
|
return [
|
|
1629
1693
|
`### ${task.task_id}`,
|
|
1630
1694
|
`unit_id: ${task.unit_id}`,
|
|
1631
1695
|
`pass_id: ${task.pass_id}`,
|
|
1632
1696
|
`lens: ${task.lens}`,
|
|
1697
|
+
...(task.tags?.length ? [`tags: ${task.tags.join(", ")}`] : []),
|
|
1698
|
+
...inputLines,
|
|
1633
1699
|
`rationale: ${task.rationale}`,
|
|
1634
1700
|
"",
|
|
1635
1701
|
`Lens guidance: ${lensDef?.description ?? task.lens}`,
|
|
1636
1702
|
`Do NOT report: ${lensDef?.do_not_report ?? "N/A"}`,
|
|
1703
|
+
...(isLensVerification
|
|
1704
|
+
? [
|
|
1705
|
+
"",
|
|
1706
|
+
"Lens verification mode: review the prior result summary in the rationale and use only targeted source checks.",
|
|
1707
|
+
"Do not redo every packet and do not write direct findings for this task.",
|
|
1708
|
+
"Return findings: [] plus verification metadata. Include followup_tasks only for bounded, specific re-review packets.",
|
|
1709
|
+
]
|
|
1710
|
+
: []),
|
|
1637
1711
|
"",
|
|
1638
1712
|
];
|
|
1639
1713
|
});
|
|
@@ -1641,6 +1715,7 @@ async function cmdPrepareDispatch(argv) {
|
|
|
1641
1715
|
`--run-id-b64 ${toBase64Url(runId)} ` +
|
|
1642
1716
|
`--packet-id-b64 ${toBase64Url(packet.packet_id)} ` +
|
|
1643
1717
|
`--artifacts-dir-b64 ${toBase64Url(artifactsDir)}`;
|
|
1718
|
+
const complexity = buildDispatchComplexity(packet, largeFileMode);
|
|
1644
1719
|
for (const task of packetTasks) {
|
|
1645
1720
|
resultMapEntries.push({
|
|
1646
1721
|
packet_id: packet.packet_id,
|
|
@@ -1679,6 +1754,13 @@ async function cmdPrepareDispatch(argv) {
|
|
|
1679
1754
|
" file_coverage [{path, total_lines}] - one entry per assigned file; use the line counts listed above",
|
|
1680
1755
|
" findings [] or array of finding objects",
|
|
1681
1756
|
"",
|
|
1757
|
+
"Lens verification tasks:",
|
|
1758
|
+
" tasks tagged lens_verification must use findings: [] and include verification:",
|
|
1759
|
+
" {verified: boolean, needs_followup: boolean, concerns?: string[],",
|
|
1760
|
+
" coverage_concerns?: string[], confidence_concerns?: string[],",
|
|
1761
|
+
" followup_tasks?: AuditTask[]}.",
|
|
1762
|
+
" Follow-up AuditTask suggestions must stay bounded to files in this packet and use the same lens.",
|
|
1763
|
+
"",
|
|
1682
1764
|
"Each finding object:",
|
|
1683
1765
|
" id unique ID, e.g. \"COR-001\"",
|
|
1684
1766
|
" title short title",
|
|
@@ -1712,6 +1794,8 @@ async function cmdPrepareDispatch(argv) {
|
|
|
1712
1794
|
description: `Audit ${packet.file_paths.length} file(s), ${packet.task_ids.length} task(s), ${packet.lenses.length} lens(es) (~${packet.total_lines} lines)` +
|
|
1713
1795
|
(largeFileMode ? " [isolated large-file mode]" : ""),
|
|
1714
1796
|
prompt_path: promptPath,
|
|
1797
|
+
complexity,
|
|
1798
|
+
model_hint: buildDispatchModelHint(complexity),
|
|
1715
1799
|
});
|
|
1716
1800
|
}
|
|
1717
1801
|
await writeJsonFile(dispatchPlanPath, plan);
|
|
@@ -2170,7 +2254,7 @@ async function cmdValidate(argv) {
|
|
|
2170
2254
|
...providerIssues,
|
|
2171
2255
|
];
|
|
2172
2256
|
const resolvedProvider = rawSessionConfig === undefined
|
|
2173
|
-
? "
|
|
2257
|
+
? "local-subprocess"
|
|
2174
2258
|
: sessionConfigIssues.length > 0
|
|
2175
2259
|
? null
|
|
2176
2260
|
: resolveFreshSessionProviderName(undefined, rawSessionConfig);
|
package/dist/io/runArtifacts.js
CHANGED
|
@@ -93,13 +93,13 @@ export async function writeDispatchBatchFiles(artifactsDir, runs, currentTasks)
|
|
|
93
93
|
"",
|
|
94
94
|
`This batch launched ${runs.length} deferred review run(s).`,
|
|
95
95
|
"Each run keeps its own task.json, prompt.md, result.json, and status.json under .audit-artifacts/runs/<run_id>/.",
|
|
96
|
-
"Use current-tasks.json for the combined task list
|
|
96
|
+
"Use current-tasks.json for the combined task list. The per-run files below are operational references for launched workers; do not read per-run prompt or schema files unless debugging a failed dispatch.",
|
|
97
97
|
"",
|
|
98
98
|
"Runs:",
|
|
99
99
|
...runs.flatMap((run) => [
|
|
100
100
|
`- ${run.run_id}`,
|
|
101
101
|
` task: ${run.task_path}`,
|
|
102
|
-
` prompt: ${run.prompt_path}`,
|
|
102
|
+
` prompt (worker-owned; do not read during normal orchestration): ${run.prompt_path}`,
|
|
103
103
|
` result: ${run.result_path}`,
|
|
104
104
|
` status: ${run.status_path}`,
|
|
105
105
|
...(run.audit_results_path
|
|
@@ -32,6 +32,7 @@ function appendSelectiveDeepeningTasks(params) {
|
|
|
32
32
|
lineIndex,
|
|
33
33
|
runtimeValidationTasks: params.bundle.runtime_validation_tasks,
|
|
34
34
|
runtimeValidationReport: params.runtimeValidationReport ?? params.bundle.runtime_validation_report,
|
|
35
|
+
externalAnalyzerResults: params.bundle.external_analyzer_results,
|
|
35
36
|
});
|
|
36
37
|
if (selectiveDeepeningTasks.length === 0) {
|
|
37
38
|
return { bundle: params.bundle, taskCount: 0, artifacts: [] };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AuditResult, AuditTask } from "../types.js";
|
|
2
|
+
import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
|
|
2
3
|
import type { RuntimeValidationReport, RuntimeValidationTaskManifest } from "../types/runtimeValidation.js";
|
|
3
4
|
export interface BuildSelectiveDeepeningTaskOptions {
|
|
4
5
|
existingTasks?: AuditTask[];
|
|
@@ -6,9 +7,12 @@ export interface BuildSelectiveDeepeningTaskOptions {
|
|
|
6
7
|
lineIndex?: Record<string, number>;
|
|
7
8
|
runtimeValidationTasks?: RuntimeValidationTaskManifest;
|
|
8
9
|
runtimeValidationReport?: RuntimeValidationReport;
|
|
10
|
+
externalAnalyzerResults?: ExternalAnalyzerResults;
|
|
9
11
|
maxTasks?: number;
|
|
10
12
|
}
|
|
11
13
|
export declare function buildSelectiveDeepeningTasks(options: BuildSelectiveDeepeningTaskOptions): AuditTask[];
|
|
12
14
|
export declare const selectiveDeepeningTestUtils: {
|
|
13
15
|
DEEPENING_TAG: string;
|
|
16
|
+
LENS_VERIFICATION_TAG: string;
|
|
17
|
+
LENS_VERIFICATION_FOLLOWUP_TAG: string;
|
|
14
18
|
};
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
const DEFAULT_MAX_DEEPENING_TASKS = 6;
|
|
3
3
|
const DEEPENING_TAG = "selective_deepening";
|
|
4
|
+
const LENS_VERIFICATION_TAG = "lens_verification";
|
|
5
|
+
const LENS_VERIFICATION_FOLLOWUP_TAG = "lens_verification_followup";
|
|
6
|
+
const MAX_LENS_VERIFICATION_FILES = 12;
|
|
7
|
+
const MAX_LENS_VERIFICATION_RESULT_SUMMARIES = 12;
|
|
8
|
+
const MAX_VERIFICATION_FOLLOWUP_TASKS_PER_RESULT = 4;
|
|
9
|
+
const IMPORTANT_LENS_VERIFICATION_LENSES = new Set([
|
|
10
|
+
"security",
|
|
11
|
+
"data_integrity",
|
|
12
|
+
"reliability",
|
|
13
|
+
]);
|
|
4
14
|
const SEVERITY_RANK = {
|
|
5
15
|
critical: 5,
|
|
6
16
|
high: 4,
|
|
@@ -27,6 +37,9 @@ function priorityRank(priority) {
|
|
|
27
37
|
function isDeepeningTask(task) {
|
|
28
38
|
return task?.tags?.includes(DEEPENING_TAG) ?? false;
|
|
29
39
|
}
|
|
40
|
+
function isLensVerificationTask(task) {
|
|
41
|
+
return task?.tags?.includes(LENS_VERIFICATION_TAG) ?? false;
|
|
42
|
+
}
|
|
30
43
|
function sanitizeSegment(value) {
|
|
31
44
|
const sanitized = value
|
|
32
45
|
.replace(/[^a-zA-Z0-9_-]+/g, "-")
|
|
@@ -85,6 +98,29 @@ function lineCountFromSources(path, tasks, results, lineIndex) {
|
|
|
85
98
|
}
|
|
86
99
|
return lineIndex?.[path] ?? 0;
|
|
87
100
|
}
|
|
101
|
+
function formatList(values, maxItems) {
|
|
102
|
+
const visible = values.slice(0, maxItems);
|
|
103
|
+
const suffix = values.length > maxItems ? `, ... (+${values.length - maxItems} more)` : "";
|
|
104
|
+
return `${visible.join(", ")}${suffix}`;
|
|
105
|
+
}
|
|
106
|
+
function priorityLabel(priority) {
|
|
107
|
+
return priority ?? "low";
|
|
108
|
+
}
|
|
109
|
+
function getExternalAnalyzerPaths(externalAnalyzerResults) {
|
|
110
|
+
return new Set((externalAnalyzerResults?.results ?? [])
|
|
111
|
+
.map((result) => result && typeof result.path === "string" && result.path.length > 0
|
|
112
|
+
? result.path
|
|
113
|
+
: null)
|
|
114
|
+
.filter((path) => path !== null));
|
|
115
|
+
}
|
|
116
|
+
function isRecord(value) {
|
|
117
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
118
|
+
}
|
|
119
|
+
function normalizedSuggestedPriority(value, fallback = "medium") {
|
|
120
|
+
return value === "high" || value === "medium" || value === "low"
|
|
121
|
+
? value
|
|
122
|
+
: fallback;
|
|
123
|
+
}
|
|
88
124
|
function buildFindingFollowupTask(params) {
|
|
89
125
|
const paths = pathsForFinding(params.finding, params.result, params.task);
|
|
90
126
|
const triggerLabel = params.triggers.join("+");
|
|
@@ -261,6 +297,309 @@ function buildRuntimeValidationFollowupTask(params) {
|
|
|
261
297
|
status: "pending",
|
|
262
298
|
};
|
|
263
299
|
}
|
|
300
|
+
function sourceTaskIds(sources) {
|
|
301
|
+
return uniqueSorted(sources.map((source) => source.result.task_id));
|
|
302
|
+
}
|
|
303
|
+
function resultFiles(source) {
|
|
304
|
+
return uniqueSorted(source.task?.file_paths && source.task.file_paths.length > 0
|
|
305
|
+
? source.task.file_paths
|
|
306
|
+
: source.result.file_coverage.map((coverage) => coverage.path));
|
|
307
|
+
}
|
|
308
|
+
function lensVerificationTriggers(params) {
|
|
309
|
+
const filePaths = uniqueSorted(params.sources.flatMap(resultFiles));
|
|
310
|
+
const findingPaths = new Set(params.sources.flatMap((source) => source.result.findings.flatMap((finding) => finding.affected_files.map((file) => file.path))));
|
|
311
|
+
const externalPathsInScope = filePaths.filter((path) => params.externalAnalyzerPaths.has(path));
|
|
312
|
+
const unresolvedExternalPaths = externalPathsInScope.filter((path) => !findingPaths.has(path));
|
|
313
|
+
const cleanResults = params.sources.filter((source) => source.result.findings.length === 0 &&
|
|
314
|
+
source.result.requires_followup !== false);
|
|
315
|
+
const highRiskCleanResults = params.sources.filter((source) => isHighRiskCleanResult(source.result, source.task));
|
|
316
|
+
const totalLines = filePaths.reduce((sum, path) => {
|
|
317
|
+
const owner = params.sources.find((source) => resultFiles(source).includes(path));
|
|
318
|
+
return (sum +
|
|
319
|
+
(owner
|
|
320
|
+
? lineCountForPath(path, owner.task, owner.result)
|
|
321
|
+
: 0));
|
|
322
|
+
}, 0);
|
|
323
|
+
const triggers = [];
|
|
324
|
+
if (params.sources.some((source) => source.task?.priority === "high")) {
|
|
325
|
+
triggers.push("high_priority_lens");
|
|
326
|
+
}
|
|
327
|
+
if (params.sources.some((source) => source.task?.tags?.some((tag) => tag === "critical_flow" || tag.startsWith("critical_flow:")))) {
|
|
328
|
+
triggers.push("critical_flow");
|
|
329
|
+
}
|
|
330
|
+
if (params.sources.some((source) => source.task?.tags?.some((tag) => tag === "external_analyzer_signal" ||
|
|
331
|
+
tag.startsWith("external_tool:"))) ||
|
|
332
|
+
externalPathsInScope.length > 0) {
|
|
333
|
+
triggers.push("external_analyzer_signal");
|
|
334
|
+
}
|
|
335
|
+
if (unresolvedExternalPaths.length > 0) {
|
|
336
|
+
triggers.push("unresolved_external_signal");
|
|
337
|
+
}
|
|
338
|
+
if (params.sources.length >= 3 ||
|
|
339
|
+
filePaths.length >= 4 ||
|
|
340
|
+
totalLines >= 2000) {
|
|
341
|
+
triggers.push("large_lens_surface");
|
|
342
|
+
}
|
|
343
|
+
if (cleanResults.length >= 2 && cleanResults.length >= params.sources.length / 2) {
|
|
344
|
+
triggers.push("many_no_finding_results");
|
|
345
|
+
}
|
|
346
|
+
if (highRiskCleanResults.length > 0) {
|
|
347
|
+
triggers.push("high_risk_clean_result");
|
|
348
|
+
}
|
|
349
|
+
if (params.sources.some((source) => source.task?.tags?.some((tag) => tag === "large_file"))) {
|
|
350
|
+
triggers.push("large_file_reviewed");
|
|
351
|
+
}
|
|
352
|
+
return uniqueSorted(triggers);
|
|
353
|
+
}
|
|
354
|
+
function hasPendingBaseTaskForLens(lens, tasks, completedResultIds) {
|
|
355
|
+
return tasks.some((task) => task.lens === lens &&
|
|
356
|
+
!isDeepeningTask(task) &&
|
|
357
|
+
!completedResultIds.has(task.task_id) &&
|
|
358
|
+
task.status !== "complete");
|
|
359
|
+
}
|
|
360
|
+
function shouldBuildLensVerificationTask(params) {
|
|
361
|
+
if (!IMPORTANT_LENS_VERIFICATION_LENSES.has(params.lens)) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
if (params.sources.length === 0 || params.triggers.length === 0) {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
const explicitlyClosedCleanScope = params.sources.every((source) => source.result.findings.length === 0 &&
|
|
368
|
+
source.result.requires_followup === false);
|
|
369
|
+
if (explicitlyClosedCleanScope &&
|
|
370
|
+
!params.triggers.some((trigger) => ["external_analyzer_signal", "unresolved_external_signal"].includes(trigger))) {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
if (hasPendingBaseTaskForLens(params.lens, params.existingTasks, params.completedResultIds)) {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
const enoughSurface = params.sources.length >= 2 ||
|
|
377
|
+
params.triggers.some((trigger) => [
|
|
378
|
+
"critical_flow",
|
|
379
|
+
"external_analyzer_signal",
|
|
380
|
+
"unresolved_external_signal",
|
|
381
|
+
"large_lens_surface",
|
|
382
|
+
].includes(trigger));
|
|
383
|
+
if (!enoughSurface) {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
const sourceSignature = sourceTaskIds(params.sources);
|
|
387
|
+
const candidateId = taskIdFor("steward", [params.lens, ...sourceSignature]);
|
|
388
|
+
return !params.existingTasks.some((task) => task.task_id === candidateId);
|
|
389
|
+
}
|
|
390
|
+
function selectLensVerificationFiles(sources, externalAnalyzerPaths) {
|
|
391
|
+
const scores = new Map();
|
|
392
|
+
function add(path, score, lines) {
|
|
393
|
+
const current = scores.get(path) ?? { score: 0, lines };
|
|
394
|
+
current.score += score;
|
|
395
|
+
current.lines = Math.max(current.lines, lines);
|
|
396
|
+
scores.set(path, current);
|
|
397
|
+
}
|
|
398
|
+
for (const source of sources) {
|
|
399
|
+
const priorityScore = priorityRank(source.task?.priority);
|
|
400
|
+
const highRiskClean = isHighRiskCleanResult(source.result, source.task);
|
|
401
|
+
for (const path of resultFiles(source)) {
|
|
402
|
+
add(path, priorityScore, lineCountForPath(path, source.task, source.result));
|
|
403
|
+
if (source.task?.tags?.includes("critical_flow"))
|
|
404
|
+
add(path, 6, 0);
|
|
405
|
+
if (source.task?.tags?.includes("external_analyzer_signal"))
|
|
406
|
+
add(path, 6, 0);
|
|
407
|
+
if (source.task?.tags?.includes("large_file"))
|
|
408
|
+
add(path, 4, 0);
|
|
409
|
+
if (highRiskClean)
|
|
410
|
+
add(path, 5, 0);
|
|
411
|
+
}
|
|
412
|
+
for (const finding of source.result.findings) {
|
|
413
|
+
for (const file of finding.affected_files) {
|
|
414
|
+
add(file.path, SEVERITY_RANK[finding.severity], 0);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
for (const path of externalAnalyzerPaths) {
|
|
419
|
+
if (scores.has(path)) {
|
|
420
|
+
add(path, 8, 0);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return [...scores.entries()]
|
|
424
|
+
.sort((a, b) => {
|
|
425
|
+
const scoreDelta = b[1].score - a[1].score;
|
|
426
|
+
if (scoreDelta !== 0)
|
|
427
|
+
return scoreDelta;
|
|
428
|
+
const lineDelta = b[1].lines - a[1].lines;
|
|
429
|
+
if (lineDelta !== 0)
|
|
430
|
+
return lineDelta;
|
|
431
|
+
return a[0].localeCompare(b[0]);
|
|
432
|
+
})
|
|
433
|
+
.slice(0, MAX_LENS_VERIFICATION_FILES)
|
|
434
|
+
.map(([path]) => path);
|
|
435
|
+
}
|
|
436
|
+
function summarizeLensVerificationSource(source) {
|
|
437
|
+
const findings = source.result.findings.length === 0
|
|
438
|
+
? "findings=none"
|
|
439
|
+
: `findings=${source.result.findings
|
|
440
|
+
.slice(0, 3)
|
|
441
|
+
.map((finding) => `${finding.id} ${finding.severity}/${finding.confidence} ${finding.category}: ${finding.title}`)
|
|
442
|
+
.join("; ")}${source.result.findings.length > 3 ? "; ..." : ""}`;
|
|
443
|
+
const tags = source.task?.tags?.length
|
|
444
|
+
? ` tags=${source.task.tags.join(",")}`
|
|
445
|
+
: "";
|
|
446
|
+
return (`- ${source.result.task_id} priority=${priorityLabel(source.task?.priority)}` +
|
|
447
|
+
`${tags} files=${formatList(resultFiles(source), 4)} ${findings}` +
|
|
448
|
+
(source.result.requires_followup === true ? " requires_followup=true" : ""));
|
|
449
|
+
}
|
|
450
|
+
function buildLensVerificationTask(params) {
|
|
451
|
+
const sourceIds = sourceTaskIds(params.sources);
|
|
452
|
+
const selectedPaths = selectLensVerificationFiles(params.sources, params.externalAnalyzerPaths);
|
|
453
|
+
const allPaths = uniqueSorted(params.sources.flatMap(resultFiles));
|
|
454
|
+
const omittedPathCount = Math.max(0, allPaths.length - selectedPaths.length);
|
|
455
|
+
const externalPathsInScope = allPaths.filter((path) => params.externalAnalyzerPaths.has(path));
|
|
456
|
+
const summaries = params.sources
|
|
457
|
+
.sort((a, b) => a.result.task_id.localeCompare(b.result.task_id))
|
|
458
|
+
.slice(0, MAX_LENS_VERIFICATION_RESULT_SUMMARIES)
|
|
459
|
+
.map(summarizeLensVerificationSource);
|
|
460
|
+
return {
|
|
461
|
+
task_id: taskIdFor("steward", [params.lens, ...sourceIds]),
|
|
462
|
+
unit_id: `lens-steward:${params.lens}`,
|
|
463
|
+
pass_id: `lens-steward:${params.lens}`,
|
|
464
|
+
lens: params.lens,
|
|
465
|
+
file_paths: selectedPaths,
|
|
466
|
+
file_line_counts: Object.fromEntries(selectedPaths.map((path) => [
|
|
467
|
+
path,
|
|
468
|
+
lineCountFromSources(path, params.sources.map((source) => source.task).filter((task) => task !== undefined), params.sources.map((source) => source.result), params.lineIndex),
|
|
469
|
+
])),
|
|
470
|
+
inputs: {
|
|
471
|
+
source_task_ids: sourceIds.join(","),
|
|
472
|
+
trigger_summary: params.triggers.join(","),
|
|
473
|
+
},
|
|
474
|
+
rationale: `Lens steward verification for ${params.lens} after ${params.sources.length} completed base result(s) across ${allPaths.length} file(s). ` +
|
|
475
|
+
`Triggers: ${params.triggers.join(", ")}. ` +
|
|
476
|
+
"Review whether high-risk packets are suspiciously clean, severity/confidence levels are consistent, external analyzer signals were resolved rather than hand-waved, cross-packet issues are visible, no-finding conclusions are believable, and related-file findings contradict each other. " +
|
|
477
|
+
"Do not write direct findings from this verification task; return findings: [] plus verification metadata with bounded follow-up AuditTask suggestions when needed.\n" +
|
|
478
|
+
`Selected verification files: ${formatList(selectedPaths, 8)}${omittedPathCount > 0 ? `; omitted ${omittedPathCount} lower-priority file(s) from direct source checks` : ""}.\n` +
|
|
479
|
+
(externalPathsInScope.length > 0
|
|
480
|
+
? `External analyzer paths in scope: ${formatList(externalPathsInScope, 8)}.\n`
|
|
481
|
+
: "") +
|
|
482
|
+
"Source result summary:\n" +
|
|
483
|
+
summaries.join("\n") +
|
|
484
|
+
(params.sources.length > MAX_LENS_VERIFICATION_RESULT_SUMMARIES
|
|
485
|
+
? `\n- ... (+${params.sources.length - MAX_LENS_VERIFICATION_RESULT_SUMMARIES} more result summaries omitted)`
|
|
486
|
+
: ""),
|
|
487
|
+
priority: "high",
|
|
488
|
+
tags: [
|
|
489
|
+
DEEPENING_TAG,
|
|
490
|
+
LENS_VERIFICATION_TAG,
|
|
491
|
+
`lens:${params.lens}`,
|
|
492
|
+
...params.triggers.map((trigger) => `trigger:${trigger}`),
|
|
493
|
+
],
|
|
494
|
+
status: "pending",
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
function buildLensVerificationTasks(params) {
|
|
498
|
+
const taskById = new Map(params.existingTasks.map((task) => [task.task_id, task]));
|
|
499
|
+
const completedResultIds = new Set(params.results.map((result) => result.task_id));
|
|
500
|
+
const externalAnalyzerPaths = getExternalAnalyzerPaths(params.externalAnalyzerResults);
|
|
501
|
+
const tasks = [];
|
|
502
|
+
for (const lens of [...IMPORTANT_LENS_VERIFICATION_LENSES].sort((a, b) => a.localeCompare(b))) {
|
|
503
|
+
const sources = params.results
|
|
504
|
+
.map((result) => ({ result, task: taskById.get(result.task_id) }))
|
|
505
|
+
.filter((source) => source.result.lens === lens &&
|
|
506
|
+
!isDeepeningTask(source.task) &&
|
|
507
|
+
!isLensVerificationTask(source.task));
|
|
508
|
+
const triggers = lensVerificationTriggers({
|
|
509
|
+
lens,
|
|
510
|
+
sources,
|
|
511
|
+
externalAnalyzerPaths,
|
|
512
|
+
});
|
|
513
|
+
if (!shouldBuildLensVerificationTask({
|
|
514
|
+
lens,
|
|
515
|
+
sources,
|
|
516
|
+
triggers,
|
|
517
|
+
existingTasks: params.existingTasks,
|
|
518
|
+
completedResultIds,
|
|
519
|
+
})) {
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
tasks.push(buildLensVerificationTask({
|
|
523
|
+
lens,
|
|
524
|
+
sources,
|
|
525
|
+
triggers,
|
|
526
|
+
externalAnalyzerPaths,
|
|
527
|
+
lineIndex: params.lineIndex,
|
|
528
|
+
}));
|
|
529
|
+
}
|
|
530
|
+
return tasks;
|
|
531
|
+
}
|
|
532
|
+
function buildVerificationFollowupTasks(params) {
|
|
533
|
+
if (!params.result.verification?.needs_followup ||
|
|
534
|
+
!Array.isArray(params.result.verification.followup_tasks) ||
|
|
535
|
+
!isLensVerificationTask(params.task)) {
|
|
536
|
+
return [];
|
|
537
|
+
}
|
|
538
|
+
const coverageByPath = new Map(params.result.file_coverage.map((coverage) => [
|
|
539
|
+
coverage.path,
|
|
540
|
+
coverage.total_lines,
|
|
541
|
+
]));
|
|
542
|
+
const concerns = [
|
|
543
|
+
...(params.result.verification.concerns ?? []),
|
|
544
|
+
...(params.result.verification.coverage_concerns ?? []),
|
|
545
|
+
...(params.result.verification.confidence_concerns ?? []),
|
|
546
|
+
];
|
|
547
|
+
const tasks = [];
|
|
548
|
+
for (let index = 0; index < params.result.verification.followup_tasks.length; index++) {
|
|
549
|
+
if (tasks.length >= MAX_VERIFICATION_FOLLOWUP_TASKS_PER_RESULT) {
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
const suggestion = params.result.verification.followup_tasks[index];
|
|
553
|
+
if (!isRecord(suggestion)) {
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
if (suggestion.lens !== params.result.lens) {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
const suggestedPaths = Array.isArray(suggestion.file_paths)
|
|
560
|
+
? suggestion.file_paths.filter((path) => typeof path === "string" && coverageByPath.has(path))
|
|
561
|
+
: [];
|
|
562
|
+
const paths = uniqueSorted(suggestedPaths);
|
|
563
|
+
if (paths.length === 0) {
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
const suggestedRationale = typeof suggestion.rationale === "string" && suggestion.rationale.trim().length > 0
|
|
567
|
+
? suggestion.rationale.trim()
|
|
568
|
+
: concerns[0] ?? "Lens steward requested bounded follow-up.";
|
|
569
|
+
const suggestedId = typeof suggestion.task_id === "string" && suggestion.task_id.trim().length > 0
|
|
570
|
+
? suggestion.task_id
|
|
571
|
+
: `suggestion-${index + 1}`;
|
|
572
|
+
tasks.push({
|
|
573
|
+
task_id: taskIdFor("steward-followup", [
|
|
574
|
+
params.result.task_id,
|
|
575
|
+
String(index),
|
|
576
|
+
suggestedId,
|
|
577
|
+
...paths,
|
|
578
|
+
suggestedRationale,
|
|
579
|
+
]),
|
|
580
|
+
unit_id: typeof suggestion.unit_id === "string" && suggestion.unit_id.trim().length > 0
|
|
581
|
+
? suggestion.unit_id
|
|
582
|
+
: params.result.unit_id,
|
|
583
|
+
pass_id: `deepening:${params.result.pass_id}`,
|
|
584
|
+
lens: params.result.lens,
|
|
585
|
+
file_paths: paths,
|
|
586
|
+
file_line_counts: Object.fromEntries(paths.map((path) => [
|
|
587
|
+
path,
|
|
588
|
+
coverageByPath.get(path) ?? params.lineIndex?.[path] ?? 0,
|
|
589
|
+
])),
|
|
590
|
+
rationale: `Lens steward follow-up from ${params.result.task_id}. ${suggestedRationale}`,
|
|
591
|
+
priority: normalizedSuggestedPriority(suggestion.priority, "medium"),
|
|
592
|
+
tags: [
|
|
593
|
+
DEEPENING_TAG,
|
|
594
|
+
LENS_VERIFICATION_FOLLOWUP_TAG,
|
|
595
|
+
"trigger:lens_verification",
|
|
596
|
+
`source_task:${sanitizeSegment(params.result.task_id)}`,
|
|
597
|
+
],
|
|
598
|
+
status: "pending",
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
return tasks;
|
|
602
|
+
}
|
|
264
603
|
function findingContexts(results, taskById) {
|
|
265
604
|
const contexts = [];
|
|
266
605
|
for (const result of results) {
|
|
@@ -345,6 +684,16 @@ export function buildSelectiveDeepeningTasks(options) {
|
|
|
345
684
|
lineIndex: options.lineIndex,
|
|
346
685
|
}));
|
|
347
686
|
}
|
|
687
|
+
for (const result of options.results) {
|
|
688
|
+
const task = taskById.get(result.task_id);
|
|
689
|
+
for (const followupTask of buildVerificationFollowupTasks({
|
|
690
|
+
result,
|
|
691
|
+
task,
|
|
692
|
+
lineIndex: options.lineIndex,
|
|
693
|
+
})) {
|
|
694
|
+
pushIfNew(followupTask);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
348
697
|
const runtimeTaskById = new Map((options.runtimeValidationTasks?.tasks ?? []).map((task) => [
|
|
349
698
|
task.id,
|
|
350
699
|
task,
|
|
@@ -369,6 +718,14 @@ export function buildSelectiveDeepeningTasks(options) {
|
|
|
369
718
|
lineIndex: options.lineIndex,
|
|
370
719
|
}));
|
|
371
720
|
}
|
|
721
|
+
for (const task of buildLensVerificationTasks({
|
|
722
|
+
existingTasks,
|
|
723
|
+
results: options.results,
|
|
724
|
+
lineIndex: options.lineIndex,
|
|
725
|
+
externalAnalyzerResults: options.externalAnalyzerResults,
|
|
726
|
+
})) {
|
|
727
|
+
pushIfNew(task);
|
|
728
|
+
}
|
|
372
729
|
const cleanResults = options.results
|
|
373
730
|
.map((result) => ({ result, task: taskById.get(result.task_id) }))
|
|
374
731
|
.filter(({ result, task }) => isHighRiskCleanResult(result, task))
|
|
@@ -389,4 +746,6 @@ export function buildSelectiveDeepeningTasks(options) {
|
|
|
389
746
|
}
|
|
390
747
|
export const selectiveDeepeningTestUtils = {
|
|
391
748
|
DEEPENING_TAG,
|
|
749
|
+
LENS_VERIFICATION_TAG,
|
|
750
|
+
LENS_VERIFICATION_FOLLOWUP_TAG,
|
|
392
751
|
};
|
|
@@ -7,13 +7,9 @@ export function renderWorkerPrompt(task) {
|
|
|
7
7
|
if (task.preferred_executor === "agent" && task.audit_results_path) {
|
|
8
8
|
const tasksPath = task.pending_audit_tasks_path ??
|
|
9
9
|
`${task.artifacts_dir}/audit_tasks.json`;
|
|
10
|
-
const resultsSchemaPath = `${task.artifacts_dir}/dispatch/audit-results.schema.json`;
|
|
11
|
-
const singleResultSchemaPath = `${task.artifacts_dir}/dispatch/audit-result.schema.json`;
|
|
12
10
|
const lines = [
|
|
13
11
|
`Audit run: ${task.run_id}`,
|
|
14
12
|
`Read: ${tasksPath}`,
|
|
15
|
-
`Array schema: ${resultsSchemaPath}`,
|
|
16
|
-
`Single-result schema: ${singleResultSchemaPath}`,
|
|
17
13
|
"Scope: review only the tasks listed in the Read file. Do not add tasks,",
|
|
18
14
|
"edit source files, remediate findings, run unrelated audits, or write result_path.",
|
|
19
15
|
"For each listed task: read the assigned file_paths under the specified lens,",
|
|
@@ -25,6 +21,9 @@ export function renderWorkerPrompt(task) {
|
|
|
25
21
|
"Each finding: id, title, category, severity, confidence, lens, summary,",
|
|
26
22
|
" affected_files [{path, line_start, line_end, symbol}] (objects, not strings; min 1 entry),",
|
|
27
23
|
" evidence [strings] (min 1 entry).",
|
|
24
|
+
"For tasks tagged lens_verification: do not write direct findings; use findings: []",
|
|
25
|
+
" and include verification {verified, needs_followup, concerns, coverage_concerns,",
|
|
26
|
+
" confidence_concerns, followup_tasks}.",
|
|
28
27
|
"Constraint: line_end must not exceed total_lines for that file.",
|
|
29
28
|
`Write only the JSON array of AuditResult objects to: ${task.audit_results_path}`,
|
|
30
29
|
];
|
package/dist/types.d.ts
CHANGED
|
@@ -88,6 +88,14 @@ export interface Finding {
|
|
|
88
88
|
systemic?: boolean;
|
|
89
89
|
related_findings?: string[];
|
|
90
90
|
}
|
|
91
|
+
export interface AuditVerification {
|
|
92
|
+
verified: boolean;
|
|
93
|
+
needs_followup: boolean;
|
|
94
|
+
concerns?: string[];
|
|
95
|
+
coverage_concerns?: string[];
|
|
96
|
+
confidence_concerns?: string[];
|
|
97
|
+
followup_tasks?: AuditTask[];
|
|
98
|
+
}
|
|
91
99
|
export interface AuditResult {
|
|
92
100
|
task_id: string;
|
|
93
101
|
unit_id: string;
|
|
@@ -102,4 +110,5 @@ export interface AuditResult {
|
|
|
102
110
|
notes?: string[];
|
|
103
111
|
requires_followup?: boolean;
|
|
104
112
|
followup_tasks?: string[];
|
|
113
|
+
verification?: AuditVerification;
|
|
105
114
|
}
|