auditor-lambda 0.8.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/audit-code-wrapper-lib.mjs +149 -129
- package/dist/adapters/normalizeExternal.js +6 -3
- package/dist/cli/args.d.ts +0 -1
- package/dist/cli/args.js +0 -6
- package/dist/cli/dispatch.js +3 -2
- package/dist/cli/lineIndex.js +4 -1
- package/dist/cli/mergeAndIngestCommand.d.ts +1 -0
- package/dist/cli/mergeAndIngestCommand.js +219 -0
- package/dist/cli/nextStepCommand.js +5 -1
- package/dist/cli/runToCompletion.d.ts +9 -0
- package/dist/cli/runToCompletion.js +655 -480
- package/dist/cli/statusCommand.d.ts +1 -0
- package/dist/cli/statusCommand.js +113 -0
- package/dist/cli/submitPacketCommand.d.ts +1 -0
- package/dist/cli/submitPacketCommand.js +155 -0
- package/dist/cli/workerResult.d.ts +1 -1
- package/dist/cli/workerRunCommand.d.ts +1 -0
- package/dist/cli/workerRunCommand.js +88 -0
- package/dist/cli.js +14 -563
- package/dist/extractors/analyzers/sql.js +4 -1
- package/dist/extractors/analyzers/treeSitter.js +29 -15
- package/dist/extractors/analyzers/typescript.js +10 -8
- package/dist/extractors/designAssessment.js +43 -24
- package/dist/extractors/graph.js +139 -73
- package/dist/extractors/pathPatterns.js +17 -5
- package/dist/io/runArtifactTypes.d.ts +18 -0
- package/dist/io/runArtifactTypes.js +1 -0
- package/dist/io/runArtifacts.d.ts +2 -18
- package/dist/io/runArtifacts.js +14 -3
- package/dist/mcp/server.js +9 -0
- package/dist/orchestrator/advance.js +37 -22
- package/dist/orchestrator/artifactFreshness.js +2 -2
- package/dist/orchestrator/autoFixExecutor.d.ts +1 -1
- package/dist/orchestrator/autoFixExecutor.js +16 -8
- package/dist/orchestrator/dependencyMap.d.ts +1 -1
- package/dist/orchestrator/dependencyMap.js +7 -1
- package/dist/orchestrator/fileAnchors.js +14 -3
- package/dist/orchestrator/flowCoverage.js +1 -0
- package/dist/orchestrator/flowRequeue.js +4 -1
- package/dist/orchestrator/{internalExecutors.d.ts → ingestionExecutors.d.ts} +0 -6
- package/dist/orchestrator/ingestionExecutors.js +237 -0
- package/dist/orchestrator/intakeExecutors.d.ts +3 -0
- package/dist/orchestrator/intakeExecutors.js +25 -0
- package/dist/orchestrator/planningExecutors.d.ts +4 -0
- package/dist/orchestrator/planningExecutors.js +95 -0
- package/dist/orchestrator/runtimeCommand.js +7 -15
- package/dist/orchestrator/selectiveDeepening/conflict.d.ts +8 -0
- package/dist/orchestrator/selectiveDeepening/conflict.js +71 -0
- package/dist/orchestrator/selectiveDeepening/findingFollowup.d.ts +10 -0
- package/dist/orchestrator/selectiveDeepening/findingFollowup.js +52 -0
- package/dist/orchestrator/selectiveDeepening/highRiskClean.d.ts +7 -0
- package/dist/orchestrator/selectiveDeepening/highRiskClean.js +44 -0
- package/dist/orchestrator/selectiveDeepening/index.d.ts +18 -0
- package/dist/orchestrator/selectiveDeepening/index.js +128 -0
- package/dist/orchestrator/selectiveDeepening/lensVerification.d.ts +12 -0
- package/dist/orchestrator/selectiveDeepening/lensVerification.js +242 -0
- package/dist/orchestrator/selectiveDeepening/runtimeValidation.d.ts +13 -0
- package/dist/orchestrator/selectiveDeepening/runtimeValidation.js +57 -0
- package/dist/orchestrator/selectiveDeepening/shared.d.ts +45 -0
- package/dist/orchestrator/selectiveDeepening/shared.js +128 -0
- package/dist/orchestrator/selectiveDeepening/stewardFollowup.d.ts +6 -0
- package/dist/orchestrator/selectiveDeepening/stewardFollowup.js +72 -0
- package/dist/orchestrator/selectiveDeepening.d.ts +2 -20
- package/dist/orchestrator/selectiveDeepening.js +6 -760
- package/dist/orchestrator/staleness.js +3 -3
- package/dist/orchestrator/structureExecutors.d.ts +5 -0
- package/dist/orchestrator/structureExecutors.js +94 -0
- package/dist/orchestrator/taskBuilder.d.ts +2 -2
- package/dist/orchestrator/taskBuilder.js +101 -82
- package/dist/providers/index.d.ts +7 -0
- package/dist/providers/index.js +14 -95
- package/dist/quota/discoveredLimits.d.ts +1 -0
- package/dist/quota/discoveredLimits.js +7 -1
- package/dist/quota/index.d.ts +0 -2
- package/dist/quota/index.js +1 -2
- package/dist/reporting/workBlocks.js +7 -4
- package/dist/types/reviewPlanning.d.ts +23 -16
- package/dist/validation/auditResults.js +97 -95
- package/dist/validation/sessionConfig.d.ts +2 -2
- package/dist/validation/sessionConfig.js +14 -7
- package/package.json +3 -2
- package/schemas/audit_findings.schema.json +3 -3
- package/schemas/critical_flows.schema.json +3 -2
- package/schemas/dispatch_quota.schema.json +1 -1
- package/schemas/graph_bundle.schema.json +1 -1
- package/schemas/review_packets.schema.json +1 -1
- package/schemas/step_contract.schema.json +80 -0
- package/scripts/postinstall.mjs +19 -2
- package/skills/audit-code/opencode-command-template.txt +3 -3
- package/dist/orchestrator/internalExecutors.js +0 -424
- package/dist/providers/localSubprocessProvider.d.ts +0 -9
- package/dist/providers/localSubprocessProvider.js +0 -18
- package/dist/providers/subprocessTemplateProvider.d.ts +0 -8
- package/dist/providers/subprocessTemplateProvider.js +0 -59
- package/dist/providers/vscodeTaskProvider.d.ts +0 -7
- package/dist/providers/vscodeTaskProvider.js +0 -14
- package/dist/quota/probe.d.ts +0 -10
- package/dist/quota/probe.js +0 -18
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { isHighRiskCleanResult } from "./highRiskClean.js";
|
|
2
|
+
import { DEEPENING_TAG, IMPORTANT_LENS_VERIFICATION_LENSES, LENS_VERIFICATION_TAG, MAX_LENS_VERIFICATION_FILES, MAX_LENS_VERIFICATION_RESULT_SUMMARIES, SEVERITY_RANK, formatList, getExternalAnalyzerPaths, isDeepeningTask, isLensVerificationTask, lineCountForPath, lineCountFromSources, priorityLabel, priorityRank, taskIdFor, uniqueSorted, } from "./shared.js";
|
|
3
|
+
function sourceTaskIds(sources) {
|
|
4
|
+
return uniqueSorted(sources.map((source) => source.result.task_id));
|
|
5
|
+
}
|
|
6
|
+
function resultFiles(source) {
|
|
7
|
+
return uniqueSorted(source.task?.file_paths && source.task.file_paths.length > 0
|
|
8
|
+
? source.task.file_paths
|
|
9
|
+
: source.result.file_coverage.map((coverage) => coverage.path));
|
|
10
|
+
}
|
|
11
|
+
function lensVerificationTriggers(params) {
|
|
12
|
+
const filePaths = uniqueSorted(params.sources.flatMap(resultFiles));
|
|
13
|
+
const findingPaths = new Set(params.sources.flatMap((source) => source.result.findings.flatMap((finding) => finding.affected_files.map((file) => file.path))));
|
|
14
|
+
const externalPathsInScope = filePaths.filter((path) => params.externalAnalyzerPaths.has(path));
|
|
15
|
+
const unresolvedExternalPaths = externalPathsInScope.filter((path) => !findingPaths.has(path));
|
|
16
|
+
const cleanResults = params.sources.filter((source) => source.result.findings.length === 0 &&
|
|
17
|
+
source.result.requires_followup !== false);
|
|
18
|
+
const highRiskCleanResults = params.sources.filter((source) => isHighRiskCleanResult(source.result, source.task));
|
|
19
|
+
const totalLines = filePaths.reduce((sum, path) => {
|
|
20
|
+
const owner = params.sources.find((source) => resultFiles(source).includes(path));
|
|
21
|
+
return (sum +
|
|
22
|
+
(owner
|
|
23
|
+
? lineCountForPath(path, owner.task, owner.result)
|
|
24
|
+
: 0));
|
|
25
|
+
}, 0);
|
|
26
|
+
const triggers = [];
|
|
27
|
+
if (params.sources.some((source) => source.task?.priority === "high")) {
|
|
28
|
+
triggers.push("high_priority_lens");
|
|
29
|
+
}
|
|
30
|
+
if (params.sources.some((source) => source.task?.tags?.some((tag) => tag === "critical_flow" || tag.startsWith("critical_flow:")))) {
|
|
31
|
+
triggers.push("critical_flow");
|
|
32
|
+
}
|
|
33
|
+
if (params.sources.some((source) => source.task?.tags?.some((tag) => tag === "external_analyzer_signal" ||
|
|
34
|
+
tag.startsWith("external_tool:"))) ||
|
|
35
|
+
externalPathsInScope.length > 0) {
|
|
36
|
+
triggers.push("external_analyzer_signal");
|
|
37
|
+
}
|
|
38
|
+
if (unresolvedExternalPaths.length > 0) {
|
|
39
|
+
triggers.push("unresolved_external_signal");
|
|
40
|
+
}
|
|
41
|
+
if (params.sources.length >= 3 ||
|
|
42
|
+
filePaths.length >= 4 ||
|
|
43
|
+
totalLines >= 2000) {
|
|
44
|
+
triggers.push("large_lens_surface");
|
|
45
|
+
}
|
|
46
|
+
if (cleanResults.length >= 2 && cleanResults.length >= params.sources.length / 2) {
|
|
47
|
+
triggers.push("many_no_finding_results");
|
|
48
|
+
}
|
|
49
|
+
if (highRiskCleanResults.length > 0) {
|
|
50
|
+
triggers.push("high_risk_clean_result");
|
|
51
|
+
}
|
|
52
|
+
if (params.sources.some((source) => source.task?.tags?.some((tag) => tag === "large_file"))) {
|
|
53
|
+
triggers.push("large_file_reviewed");
|
|
54
|
+
}
|
|
55
|
+
return uniqueSorted(triggers);
|
|
56
|
+
}
|
|
57
|
+
function hasPendingBaseTaskForLens(lens, tasks, completedResultIds) {
|
|
58
|
+
return tasks.some((task) => task.lens === lens &&
|
|
59
|
+
!isDeepeningTask(task) &&
|
|
60
|
+
!completedResultIds.has(task.task_id) &&
|
|
61
|
+
task.status !== "complete");
|
|
62
|
+
}
|
|
63
|
+
function shouldBuildLensVerificationTask(params) {
|
|
64
|
+
if (!IMPORTANT_LENS_VERIFICATION_LENSES.has(params.lens)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (params.sources.length === 0 || params.triggers.length === 0) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const explicitlyClosedCleanScope = params.sources.every((source) => source.result.findings.length === 0 &&
|
|
71
|
+
source.result.requires_followup === false);
|
|
72
|
+
if (explicitlyClosedCleanScope &&
|
|
73
|
+
!params.triggers.some((trigger) => ["external_analyzer_signal", "unresolved_external_signal"].includes(trigger))) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if (hasPendingBaseTaskForLens(params.lens, params.existingTasks, params.completedResultIds)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
const enoughSurface = params.sources.length >= 2 ||
|
|
80
|
+
params.triggers.some((trigger) => [
|
|
81
|
+
"critical_flow",
|
|
82
|
+
"external_analyzer_signal",
|
|
83
|
+
"unresolved_external_signal",
|
|
84
|
+
"large_lens_surface",
|
|
85
|
+
].includes(trigger));
|
|
86
|
+
if (!enoughSurface) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const sourceSignature = sourceTaskIds(params.sources);
|
|
90
|
+
const candidateId = taskIdFor("steward", [params.lens, ...sourceSignature]);
|
|
91
|
+
return !params.existingTasks.some((task) => task.task_id === candidateId);
|
|
92
|
+
}
|
|
93
|
+
function selectLensVerificationFiles(sources, externalAnalyzerPaths) {
|
|
94
|
+
const scores = new Map();
|
|
95
|
+
function add(path, score, lines) {
|
|
96
|
+
const current = scores.get(path) ?? { score: 0, lines };
|
|
97
|
+
current.score += score;
|
|
98
|
+
current.lines = Math.max(current.lines, lines);
|
|
99
|
+
scores.set(path, current);
|
|
100
|
+
}
|
|
101
|
+
for (const source of sources) {
|
|
102
|
+
const priorityScore = priorityRank(source.task?.priority);
|
|
103
|
+
const highRiskClean = isHighRiskCleanResult(source.result, source.task);
|
|
104
|
+
for (const path of resultFiles(source)) {
|
|
105
|
+
add(path, priorityScore, lineCountForPath(path, source.task, source.result));
|
|
106
|
+
if (source.task?.tags?.includes("critical_flow"))
|
|
107
|
+
add(path, 6, 0);
|
|
108
|
+
if (source.task?.tags?.includes("external_analyzer_signal"))
|
|
109
|
+
add(path, 6, 0);
|
|
110
|
+
if (source.task?.tags?.includes("large_file"))
|
|
111
|
+
add(path, 4, 0);
|
|
112
|
+
if (highRiskClean)
|
|
113
|
+
add(path, 5, 0);
|
|
114
|
+
}
|
|
115
|
+
for (const finding of source.result.findings) {
|
|
116
|
+
for (const file of finding.affected_files) {
|
|
117
|
+
add(file.path, SEVERITY_RANK[finding.severity], 0);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
for (const path of externalAnalyzerPaths) {
|
|
122
|
+
if (scores.has(path)) {
|
|
123
|
+
add(path, 8, 0);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const ranked = [...scores.entries()].sort((a, b) => {
|
|
127
|
+
const scoreDelta = b[1].score - a[1].score;
|
|
128
|
+
if (scoreDelta !== 0)
|
|
129
|
+
return scoreDelta;
|
|
130
|
+
const lineDelta = b[1].lines - a[1].lines;
|
|
131
|
+
if (lineDelta !== 0)
|
|
132
|
+
return lineDelta;
|
|
133
|
+
return a[0].localeCompare(b[0]);
|
|
134
|
+
});
|
|
135
|
+
if (ranked.length > MAX_LENS_VERIFICATION_FILES) {
|
|
136
|
+
// No RunLogger in scope here: emit the established structured-stderr signal
|
|
137
|
+
// so the silent task-budget truncation leaves a trace.
|
|
138
|
+
process.stderr.write(`[audit-code] selectiveDeepening: truncated verification-file list to ` +
|
|
139
|
+
`${MAX_LENS_VERIFICATION_FILES} of ${ranked.length}\n`);
|
|
140
|
+
}
|
|
141
|
+
return ranked.slice(0, MAX_LENS_VERIFICATION_FILES).map(([path]) => path);
|
|
142
|
+
}
|
|
143
|
+
function summarizeLensVerificationSource(source) {
|
|
144
|
+
const findings = source.result.findings.length === 0
|
|
145
|
+
? "findings=none"
|
|
146
|
+
: `findings=${source.result.findings
|
|
147
|
+
.slice(0, 3)
|
|
148
|
+
.map((finding) => `${finding.id} ${finding.severity}/${finding.confidence} ${finding.category}: ${finding.title}`)
|
|
149
|
+
.join("; ")}${source.result.findings.length > 3 ? "; ..." : ""}`;
|
|
150
|
+
const tags = source.task?.tags?.length
|
|
151
|
+
? ` tags=${source.task.tags.join(",")}`
|
|
152
|
+
: "";
|
|
153
|
+
return (`- ${source.result.task_id} priority=${priorityLabel(source.task?.priority)}` +
|
|
154
|
+
`${tags} files=${formatList(resultFiles(source), 4)} ${findings}` +
|
|
155
|
+
(source.result.requires_followup === true ? " requires_followup=true" : ""));
|
|
156
|
+
}
|
|
157
|
+
function buildLensVerificationTask(params) {
|
|
158
|
+
const sourceIds = sourceTaskIds(params.sources);
|
|
159
|
+
const selectedPaths = selectLensVerificationFiles(params.sources, params.externalAnalyzerPaths);
|
|
160
|
+
const allPaths = uniqueSorted(params.sources.flatMap(resultFiles));
|
|
161
|
+
const omittedPathCount = Math.max(0, allPaths.length - selectedPaths.length);
|
|
162
|
+
const externalPathsInScope = allPaths.filter((path) => params.externalAnalyzerPaths.has(path));
|
|
163
|
+
if (params.sources.length > MAX_LENS_VERIFICATION_RESULT_SUMMARIES) {
|
|
164
|
+
process.stderr.write(`[audit-code] selectiveDeepening: truncated result-summary list to ` +
|
|
165
|
+
`${MAX_LENS_VERIFICATION_RESULT_SUMMARIES} of ${params.sources.length}\n`);
|
|
166
|
+
}
|
|
167
|
+
const summaries = params.sources
|
|
168
|
+
.sort((a, b) => a.result.task_id.localeCompare(b.result.task_id))
|
|
169
|
+
.slice(0, MAX_LENS_VERIFICATION_RESULT_SUMMARIES)
|
|
170
|
+
.map(summarizeLensVerificationSource);
|
|
171
|
+
return {
|
|
172
|
+
task_id: taskIdFor("steward", [params.lens, ...sourceIds]),
|
|
173
|
+
unit_id: `lens-steward:${params.lens}`,
|
|
174
|
+
pass_id: `lens-steward:${params.lens}`,
|
|
175
|
+
lens: params.lens,
|
|
176
|
+
file_paths: selectedPaths,
|
|
177
|
+
file_line_counts: Object.fromEntries(selectedPaths.map((path) => [
|
|
178
|
+
path,
|
|
179
|
+
lineCountFromSources(path, params.sources.map((source) => source.task).filter((task) => task !== undefined), params.sources.map((source) => source.result), params.lineIndex),
|
|
180
|
+
])),
|
|
181
|
+
inputs: {
|
|
182
|
+
source_task_ids: sourceIds.join(","),
|
|
183
|
+
trigger_summary: params.triggers.join(","),
|
|
184
|
+
},
|
|
185
|
+
rationale: `Lens steward verification for ${params.lens} after ${params.sources.length} completed base result(s) across ${allPaths.length} file(s). ` +
|
|
186
|
+
`Triggers: ${params.triggers.join(", ")}. ` +
|
|
187
|
+
"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. " +
|
|
188
|
+
"Do not write direct findings from this verification task; return findings: [] plus verification metadata with bounded follow-up AuditTask suggestions when needed.\n" +
|
|
189
|
+
`Selected verification files: ${formatList(selectedPaths, 8)}${omittedPathCount > 0 ? `; omitted ${omittedPathCount} lower-priority file(s) from direct source checks` : ""}.\n` +
|
|
190
|
+
(externalPathsInScope.length > 0
|
|
191
|
+
? `External analyzer paths in scope: ${formatList(externalPathsInScope, 8)}.\n`
|
|
192
|
+
: "") +
|
|
193
|
+
"Source result summary:\n" +
|
|
194
|
+
summaries.join("\n") +
|
|
195
|
+
(params.sources.length > MAX_LENS_VERIFICATION_RESULT_SUMMARIES
|
|
196
|
+
? `\n- ... (+${params.sources.length - MAX_LENS_VERIFICATION_RESULT_SUMMARIES} more result summaries omitted)`
|
|
197
|
+
: ""),
|
|
198
|
+
priority: "high",
|
|
199
|
+
tags: [
|
|
200
|
+
DEEPENING_TAG,
|
|
201
|
+
LENS_VERIFICATION_TAG,
|
|
202
|
+
`lens:${params.lens}`,
|
|
203
|
+
...params.triggers.map((trigger) => `trigger:${trigger}`),
|
|
204
|
+
],
|
|
205
|
+
status: "pending",
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
export function buildLensVerificationTasks(params) {
|
|
209
|
+
const taskById = new Map(params.existingTasks.map((task) => [task.task_id, task]));
|
|
210
|
+
const completedResultIds = new Set(params.results.map((result) => result.task_id));
|
|
211
|
+
const externalAnalyzerPaths = getExternalAnalyzerPaths(params.externalAnalyzerResults);
|
|
212
|
+
const tasks = [];
|
|
213
|
+
for (const lens of [...IMPORTANT_LENS_VERIFICATION_LENSES].sort((a, b) => a.localeCompare(b))) {
|
|
214
|
+
const sources = params.results
|
|
215
|
+
.map((result) => ({ result, task: taskById.get(result.task_id) }))
|
|
216
|
+
.filter((source) => source.result.lens === lens &&
|
|
217
|
+
!isDeepeningTask(source.task) &&
|
|
218
|
+
!isLensVerificationTask(source.task));
|
|
219
|
+
const triggers = lensVerificationTriggers({
|
|
220
|
+
lens,
|
|
221
|
+
sources,
|
|
222
|
+
externalAnalyzerPaths,
|
|
223
|
+
});
|
|
224
|
+
if (!shouldBuildLensVerificationTask({
|
|
225
|
+
lens,
|
|
226
|
+
sources,
|
|
227
|
+
triggers,
|
|
228
|
+
existingTasks: params.existingTasks,
|
|
229
|
+
completedResultIds,
|
|
230
|
+
})) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
tasks.push(buildLensVerificationTask({
|
|
234
|
+
lens,
|
|
235
|
+
sources,
|
|
236
|
+
triggers,
|
|
237
|
+
externalAnalyzerPaths,
|
|
238
|
+
lineIndex: params.lineIndex,
|
|
239
|
+
}));
|
|
240
|
+
}
|
|
241
|
+
return tasks;
|
|
242
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AuditResult, AuditTask, Lens } from "../../types.js";
|
|
2
|
+
import type { RuntimeValidationStatus, RuntimeValidationTask } from "../../types/runtimeValidation.js";
|
|
3
|
+
import { type FindingContext } from "./shared.js";
|
|
4
|
+
export declare function runtimeResultNeedsFollowup(status: RuntimeValidationStatus): boolean;
|
|
5
|
+
export declare function pickRuntimeFollowupLens(relatedTasks: AuditTask[]): Lens;
|
|
6
|
+
export declare function runtimeValidationHasStrongStaticFinding(runtimeTask: RuntimeValidationTask, contexts: FindingContext[]): boolean;
|
|
7
|
+
export declare function buildRuntimeValidationFollowupTask(params: {
|
|
8
|
+
runtimeTask: RuntimeValidationTask;
|
|
9
|
+
runtimeResultStatus: RuntimeValidationStatus;
|
|
10
|
+
relatedTasks: AuditTask[];
|
|
11
|
+
results: AuditResult[];
|
|
12
|
+
lineIndex?: Record<string, number>;
|
|
13
|
+
}): AuditTask;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { DEEPENING_TAG, SEVERITY_RANK, intersects, lineCountFromSources, sanitizeSegment, taskIdFor, uniqueSorted, } from "./shared.js";
|
|
2
|
+
export function runtimeResultNeedsFollowup(status) {
|
|
3
|
+
return status === "not_confirmed" || status === "inconclusive";
|
|
4
|
+
}
|
|
5
|
+
export function pickRuntimeFollowupLens(relatedTasks) {
|
|
6
|
+
const preference = [
|
|
7
|
+
"security",
|
|
8
|
+
"data_integrity",
|
|
9
|
+
"reliability",
|
|
10
|
+
"correctness",
|
|
11
|
+
"tests",
|
|
12
|
+
"operability",
|
|
13
|
+
"config_deployment",
|
|
14
|
+
"performance",
|
|
15
|
+
"architecture",
|
|
16
|
+
"maintainability",
|
|
17
|
+
];
|
|
18
|
+
for (const lens of preference) {
|
|
19
|
+
if (relatedTasks.some((task) => task.lens === lens)) {
|
|
20
|
+
return lens;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return "correctness";
|
|
24
|
+
}
|
|
25
|
+
export function runtimeValidationHasStrongStaticFinding(runtimeTask, contexts) {
|
|
26
|
+
return contexts.some((context) => intersects(context.paths, runtimeTask.target_paths) &&
|
|
27
|
+
SEVERITY_RANK[context.finding.severity] >= SEVERITY_RANK.high);
|
|
28
|
+
}
|
|
29
|
+
export function buildRuntimeValidationFollowupTask(params) {
|
|
30
|
+
const paths = uniqueSorted(params.runtimeTask.target_paths);
|
|
31
|
+
const lens = pickRuntimeFollowupLens(params.relatedTasks);
|
|
32
|
+
const firstRelated = params.relatedTasks[0];
|
|
33
|
+
return {
|
|
34
|
+
task_id: taskIdFor("runtime", [params.runtimeTask.id]),
|
|
35
|
+
unit_id: firstRelated?.unit_id ?? `runtime:${sanitizeSegment(params.runtimeTask.id)}`,
|
|
36
|
+
pass_id: `deepening:runtime:${sanitizeSegment(params.runtimeTask.id)}`,
|
|
37
|
+
lens,
|
|
38
|
+
file_paths: paths,
|
|
39
|
+
file_line_counts: Object.fromEntries(paths.map((path) => [
|
|
40
|
+
path,
|
|
41
|
+
lineCountFromSources(path, params.relatedTasks, params.results, params.lineIndex),
|
|
42
|
+
])),
|
|
43
|
+
rationale: `Reconcile runtime validation ${params.runtimeTask.id} (${params.runtimeResultStatus}) with semantic audit output. ` +
|
|
44
|
+
"Verify the failing or inconclusive runtime evidence, map it to source behavior, and decide whether a finding should be added or escalated.",
|
|
45
|
+
priority: params.runtimeTask.priority === "high" ||
|
|
46
|
+
params.runtimeResultStatus === "not_confirmed"
|
|
47
|
+
? "high"
|
|
48
|
+
: "medium",
|
|
49
|
+
tags: [
|
|
50
|
+
DEEPENING_TAG,
|
|
51
|
+
"trigger:runtime_validation_disagreement",
|
|
52
|
+
`runtime_task:${sanitizeSegment(params.runtimeTask.id)}`,
|
|
53
|
+
`runtime_status:${params.runtimeResultStatus}`,
|
|
54
|
+
],
|
|
55
|
+
status: "pending",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AuditResult, AuditTask, Finding, Lens } from "../../types.js";
|
|
2
|
+
import type { ExternalAnalyzerResults } from "../../types/externalAnalyzer.js";
|
|
3
|
+
export declare const DEFAULT_MAX_DEEPENING_TASKS = 6;
|
|
4
|
+
export declare const DEFAULT_MAX_TOTAL_DEEPENING_TASKS = 24;
|
|
5
|
+
export declare const DEEPENING_TAG = "selective_deepening";
|
|
6
|
+
export declare const LENS_VERIFICATION_TAG = "lens_verification";
|
|
7
|
+
export declare const LENS_VERIFICATION_FOLLOWUP_TAG = "lens_verification_followup";
|
|
8
|
+
export declare const MAX_LENS_VERIFICATION_FILES = 12;
|
|
9
|
+
export declare const MAX_LENS_VERIFICATION_RESULT_SUMMARIES = 12;
|
|
10
|
+
export declare const MAX_VERIFICATION_FOLLOWUP_TASKS_PER_RESULT = 4;
|
|
11
|
+
export declare const IMPORTANT_LENS_VERIFICATION_LENSES: Set<Lens>;
|
|
12
|
+
export declare const SEVERITY_RANK: Record<Finding["severity"], number>;
|
|
13
|
+
export declare const CONFIDENCE_RANK: Record<Finding["confidence"], number>;
|
|
14
|
+
export declare function priorityRank(priority: AuditTask["priority"]): number;
|
|
15
|
+
export interface BuildSelectiveDeepeningTaskOptions {
|
|
16
|
+
existingTasks?: AuditTask[];
|
|
17
|
+
results: AuditResult[];
|
|
18
|
+
lineIndex?: Record<string, number>;
|
|
19
|
+
runtimeValidationTasks?: import("../../types/runtimeValidation.js").RuntimeValidationTaskManifest;
|
|
20
|
+
runtimeValidationReport?: import("../../types/runtimeValidation.js").RuntimeValidationReport;
|
|
21
|
+
externalAnalyzerResults?: ExternalAnalyzerResults;
|
|
22
|
+
maxTasks?: number;
|
|
23
|
+
maxTotalDeepeningTasks?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface FindingContext {
|
|
26
|
+
result: AuditResult;
|
|
27
|
+
task?: AuditTask;
|
|
28
|
+
finding: Finding;
|
|
29
|
+
paths: string[];
|
|
30
|
+
}
|
|
31
|
+
export declare function isDeepeningTask(task: AuditTask | undefined): boolean;
|
|
32
|
+
export declare function isLensVerificationTask(task: AuditTask | undefined): boolean;
|
|
33
|
+
export declare function sanitizeSegment(value: string): string;
|
|
34
|
+
export declare function shortHash(value: string): string;
|
|
35
|
+
export declare function lineCountForPath(path: string, task: AuditTask | undefined, result: AuditResult, lineIndex?: Record<string, number>): number;
|
|
36
|
+
export declare function uniqueSorted(values: Iterable<string>): string[];
|
|
37
|
+
export declare function intersects(left: string[], right: string[]): boolean;
|
|
38
|
+
export declare function pathsForFinding(finding: Finding, result: AuditResult, task: AuditTask | undefined): string[];
|
|
39
|
+
export declare function taskIdFor(prefix: string, values: string[]): string;
|
|
40
|
+
export declare function lineCountFromSources(path: string, tasks: AuditTask[], results: AuditResult[], lineIndex?: Record<string, number>): number;
|
|
41
|
+
export declare function formatList(values: string[], maxItems: number): string;
|
|
42
|
+
export declare function priorityLabel(priority: AuditTask["priority"]): NonNullable<AuditTask["priority"]>;
|
|
43
|
+
export declare function getExternalAnalyzerPaths(externalAnalyzerResults?: ExternalAnalyzerResults): Set<string>;
|
|
44
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
45
|
+
export declare function normalizedSuggestedPriority(value: unknown, fallback?: NonNullable<AuditTask["priority"]>): NonNullable<AuditTask["priority"]>;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
// Shared primitives for the selective-deepening strategies. Each strategy
|
|
3
|
+
// module under this directory builds one kind of follow-up task; they all draw
|
|
4
|
+
// the ranks, id/hash helpers, path utilities, and tag/limit constants from
|
|
5
|
+
// here. The public entry (`buildSelectiveDeepeningTasks`) lives in `index.ts`.
|
|
6
|
+
export const DEFAULT_MAX_DEEPENING_TASKS = 6;
|
|
7
|
+
export const DEFAULT_MAX_TOTAL_DEEPENING_TASKS = 24;
|
|
8
|
+
export const DEEPENING_TAG = "selective_deepening";
|
|
9
|
+
export const LENS_VERIFICATION_TAG = "lens_verification";
|
|
10
|
+
export const LENS_VERIFICATION_FOLLOWUP_TAG = "lens_verification_followup";
|
|
11
|
+
export const MAX_LENS_VERIFICATION_FILES = 12;
|
|
12
|
+
export const MAX_LENS_VERIFICATION_RESULT_SUMMARIES = 12;
|
|
13
|
+
export const MAX_VERIFICATION_FOLLOWUP_TASKS_PER_RESULT = 4;
|
|
14
|
+
export const IMPORTANT_LENS_VERIFICATION_LENSES = new Set([
|
|
15
|
+
"security",
|
|
16
|
+
"data_integrity",
|
|
17
|
+
"reliability",
|
|
18
|
+
]);
|
|
19
|
+
export const SEVERITY_RANK = {
|
|
20
|
+
critical: 5,
|
|
21
|
+
high: 4,
|
|
22
|
+
medium: 3,
|
|
23
|
+
low: 2,
|
|
24
|
+
info: 1,
|
|
25
|
+
};
|
|
26
|
+
export const CONFIDENCE_RANK = {
|
|
27
|
+
high: 3,
|
|
28
|
+
medium: 2,
|
|
29
|
+
low: 1,
|
|
30
|
+
};
|
|
31
|
+
export function priorityRank(priority) {
|
|
32
|
+
switch (priority) {
|
|
33
|
+
case "high":
|
|
34
|
+
return 3;
|
|
35
|
+
case "medium":
|
|
36
|
+
return 2;
|
|
37
|
+
case "low":
|
|
38
|
+
default:
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function isDeepeningTask(task) {
|
|
43
|
+
return task?.tags?.includes(DEEPENING_TAG) ?? false;
|
|
44
|
+
}
|
|
45
|
+
export function isLensVerificationTask(task) {
|
|
46
|
+
return task?.tags?.includes(LENS_VERIFICATION_TAG) ?? false;
|
|
47
|
+
}
|
|
48
|
+
export function sanitizeSegment(value) {
|
|
49
|
+
const sanitized = value
|
|
50
|
+
.replace(/[^a-zA-Z0-9_-]+/g, "-")
|
|
51
|
+
.replace(/^-+|-+$/g, "");
|
|
52
|
+
return sanitized.length > 0 ? sanitized : "followup";
|
|
53
|
+
}
|
|
54
|
+
export function shortHash(value) {
|
|
55
|
+
return createHash("sha1").update(value).digest("hex").slice(0, 10);
|
|
56
|
+
}
|
|
57
|
+
function resultLineIndex(result) {
|
|
58
|
+
return Object.fromEntries(result.file_coverage.map((coverage) => [
|
|
59
|
+
coverage.path,
|
|
60
|
+
coverage.total_lines,
|
|
61
|
+
]));
|
|
62
|
+
}
|
|
63
|
+
export function lineCountForPath(path, task, result, lineIndex) {
|
|
64
|
+
return (task?.file_line_counts?.[path] ??
|
|
65
|
+
resultLineIndex(result)[path] ??
|
|
66
|
+
lineIndex?.[path] ??
|
|
67
|
+
0);
|
|
68
|
+
}
|
|
69
|
+
export function uniqueSorted(values) {
|
|
70
|
+
return [...new Set(values)].sort((a, b) => a.localeCompare(b));
|
|
71
|
+
}
|
|
72
|
+
export function intersects(left, right) {
|
|
73
|
+
const rightSet = new Set(right);
|
|
74
|
+
return left.some((value) => rightSet.has(value));
|
|
75
|
+
}
|
|
76
|
+
export function pathsForFinding(finding, result, task) {
|
|
77
|
+
const assignedPaths = new Set([
|
|
78
|
+
...(task?.file_paths ?? []),
|
|
79
|
+
...result.file_coverage.map((coverage) => coverage.path),
|
|
80
|
+
]);
|
|
81
|
+
const affected = finding.affected_files
|
|
82
|
+
.map((file) => file.path)
|
|
83
|
+
.filter((path) => assignedPaths.size === 0 || assignedPaths.has(path));
|
|
84
|
+
return uniqueSorted(affected.length > 0
|
|
85
|
+
? affected
|
|
86
|
+
: result.file_coverage.map((coverage) => coverage.path));
|
|
87
|
+
}
|
|
88
|
+
export function taskIdFor(prefix, values) {
|
|
89
|
+
return `deepening:${prefix}:${shortHash(values.join("\0"))}`;
|
|
90
|
+
}
|
|
91
|
+
export function lineCountFromSources(path, tasks, results, lineIndex) {
|
|
92
|
+
for (const task of tasks) {
|
|
93
|
+
const count = task.file_line_counts?.[path];
|
|
94
|
+
if (count !== undefined) {
|
|
95
|
+
return count;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
for (const result of results) {
|
|
99
|
+
const coverage = result.file_coverage.find((item) => item.path === path);
|
|
100
|
+
if (coverage) {
|
|
101
|
+
return coverage.total_lines;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return lineIndex?.[path] ?? 0;
|
|
105
|
+
}
|
|
106
|
+
export function formatList(values, maxItems) {
|
|
107
|
+
const visible = values.slice(0, maxItems);
|
|
108
|
+
const suffix = values.length > maxItems ? `, ... (+${values.length - maxItems} more)` : "";
|
|
109
|
+
return `${visible.join(", ")}${suffix}`;
|
|
110
|
+
}
|
|
111
|
+
export function priorityLabel(priority) {
|
|
112
|
+
return priority ?? "low";
|
|
113
|
+
}
|
|
114
|
+
export function getExternalAnalyzerPaths(externalAnalyzerResults) {
|
|
115
|
+
return new Set((externalAnalyzerResults?.results ?? [])
|
|
116
|
+
.map((result) => result && typeof result.path === "string" && result.path.length > 0
|
|
117
|
+
? result.path
|
|
118
|
+
: null)
|
|
119
|
+
.filter((path) => path !== null));
|
|
120
|
+
}
|
|
121
|
+
export function isRecord(value) {
|
|
122
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
123
|
+
}
|
|
124
|
+
export function normalizedSuggestedPriority(value, fallback = "medium") {
|
|
125
|
+
return value === "high" || value === "medium" || value === "low"
|
|
126
|
+
? value
|
|
127
|
+
: fallback;
|
|
128
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DEEPENING_TAG, LENS_VERIFICATION_FOLLOWUP_TAG, MAX_VERIFICATION_FOLLOWUP_TASKS_PER_RESULT, isLensVerificationTask, isRecord, normalizedSuggestedPriority, sanitizeSegment, taskIdFor, uniqueSorted, } from "./shared.js";
|
|
2
|
+
export function buildVerificationFollowupTasks(params) {
|
|
3
|
+
if (!params.result.verification?.needs_followup ||
|
|
4
|
+
!Array.isArray(params.result.verification.followup_tasks) ||
|
|
5
|
+
!isLensVerificationTask(params.task)) {
|
|
6
|
+
return [];
|
|
7
|
+
}
|
|
8
|
+
const coverageByPath = new Map(params.result.file_coverage.map((coverage) => [
|
|
9
|
+
coverage.path,
|
|
10
|
+
coverage.total_lines,
|
|
11
|
+
]));
|
|
12
|
+
const concerns = [
|
|
13
|
+
...(params.result.verification.concerns ?? []),
|
|
14
|
+
...(params.result.verification.coverage_concerns ?? []),
|
|
15
|
+
...(params.result.verification.confidence_concerns ?? []),
|
|
16
|
+
];
|
|
17
|
+
const tasks = [];
|
|
18
|
+
for (let index = 0; index < params.result.verification.followup_tasks.length; index++) {
|
|
19
|
+
if (tasks.length >= MAX_VERIFICATION_FOLLOWUP_TASKS_PER_RESULT) {
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
const suggestion = params.result.verification.followup_tasks[index];
|
|
23
|
+
if (!isRecord(suggestion)) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (suggestion.lens !== params.result.lens) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const suggestedPaths = Array.isArray(suggestion.file_paths)
|
|
30
|
+
? suggestion.file_paths.filter((path) => typeof path === "string" && coverageByPath.has(path))
|
|
31
|
+
: [];
|
|
32
|
+
const paths = uniqueSorted(suggestedPaths);
|
|
33
|
+
if (paths.length === 0) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const suggestedRationale = typeof suggestion.rationale === "string" && suggestion.rationale.trim().length > 0
|
|
37
|
+
? suggestion.rationale.trim()
|
|
38
|
+
: concerns[0] ?? "Lens steward requested bounded follow-up.";
|
|
39
|
+
const suggestedId = typeof suggestion.task_id === "string" && suggestion.task_id.trim().length > 0
|
|
40
|
+
? suggestion.task_id
|
|
41
|
+
: `suggestion-${index + 1}`;
|
|
42
|
+
tasks.push({
|
|
43
|
+
task_id: taskIdFor("steward-followup", [
|
|
44
|
+
params.result.task_id,
|
|
45
|
+
String(index),
|
|
46
|
+
suggestedId,
|
|
47
|
+
...paths,
|
|
48
|
+
suggestedRationale,
|
|
49
|
+
]),
|
|
50
|
+
unit_id: typeof suggestion.unit_id === "string" && suggestion.unit_id.trim().length > 0
|
|
51
|
+
? suggestion.unit_id
|
|
52
|
+
: params.result.unit_id,
|
|
53
|
+
pass_id: `deepening:${params.result.pass_id}`,
|
|
54
|
+
lens: params.result.lens,
|
|
55
|
+
file_paths: paths,
|
|
56
|
+
file_line_counts: Object.fromEntries(paths.map((path) => [
|
|
57
|
+
path,
|
|
58
|
+
coverageByPath.get(path) ?? params.lineIndex?.[path] ?? 0,
|
|
59
|
+
])),
|
|
60
|
+
rationale: `Lens steward follow-up from ${params.result.task_id}. ${suggestedRationale}`,
|
|
61
|
+
priority: normalizedSuggestedPriority(suggestion.priority, "medium"),
|
|
62
|
+
tags: [
|
|
63
|
+
DEEPENING_TAG,
|
|
64
|
+
LENS_VERIFICATION_FOLLOWUP_TAG,
|
|
65
|
+
"trigger:lens_verification",
|
|
66
|
+
`source_task:${sanitizeSegment(params.result.task_id)}`,
|
|
67
|
+
],
|
|
68
|
+
status: "pending",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return tasks;
|
|
72
|
+
}
|
|
@@ -1,20 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import type { RuntimeValidationReport, RuntimeValidationTaskManifest } from "../types/runtimeValidation.js";
|
|
4
|
-
export interface BuildSelectiveDeepeningTaskOptions {
|
|
5
|
-
existingTasks?: AuditTask[];
|
|
6
|
-
results: AuditResult[];
|
|
7
|
-
lineIndex?: Record<string, number>;
|
|
8
|
-
runtimeValidationTasks?: RuntimeValidationTaskManifest;
|
|
9
|
-
runtimeValidationReport?: RuntimeValidationReport;
|
|
10
|
-
externalAnalyzerResults?: ExternalAnalyzerResults;
|
|
11
|
-
maxTasks?: number;
|
|
12
|
-
maxTotalDeepeningTasks?: number;
|
|
13
|
-
}
|
|
14
|
-
export declare function buildSelectiveDeepeningTasks(options: BuildSelectiveDeepeningTaskOptions): AuditTask[];
|
|
15
|
-
export declare const selectiveDeepeningTestUtils: {
|
|
16
|
-
DEEPENING_TAG: string;
|
|
17
|
-
LENS_VERIFICATION_TAG: string;
|
|
18
|
-
LENS_VERIFICATION_FOLLOWUP_TAG: string;
|
|
19
|
-
DEFAULT_MAX_TOTAL_DEEPENING_TASKS: number;
|
|
20
|
-
};
|
|
1
|
+
export { buildSelectiveDeepeningTasks, selectiveDeepeningTestUtils, } from "./selectiveDeepening/index.js";
|
|
2
|
+
export type { BuildSelectiveDeepeningTaskOptions } from "./selectiveDeepening/index.js";
|