auditor-lambda 0.3.39 → 0.3.40
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/dist/cli.js +232 -36
- package/dist/validation/auditResults.d.ts +1 -0
- package/dist/validation/auditResults.js +30 -12
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2142,6 +2142,7 @@ async function cmdWorkerRun(argv) {
|
|
|
2142
2142
|
}
|
|
2143
2143
|
}
|
|
2144
2144
|
const DISPATCH_RESULT_MAP_FILENAME = "dispatch-result-map.json";
|
|
2145
|
+
const ACTIVE_DISPATCH_FILENAME = "active-dispatch.json";
|
|
2145
2146
|
function dispatchResultMapPath(runDir) {
|
|
2146
2147
|
return join(runDir, DISPATCH_RESULT_MAP_FILENAME);
|
|
2147
2148
|
}
|
|
@@ -2437,6 +2438,10 @@ async function prepareDispatchArtifacts(params) {
|
|
|
2437
2438
|
.map(([key, value]) => `input.${key}: ${value}`)
|
|
2438
2439
|
: [];
|
|
2439
2440
|
const isLensVerification = task.tags?.includes("lens_verification") ?? false;
|
|
2441
|
+
const coverageTemplate = task.file_paths.map((path) => ({
|
|
2442
|
+
path,
|
|
2443
|
+
total_lines: task.file_line_counts?.[path] ?? lineIndex[path] ?? 0,
|
|
2444
|
+
}));
|
|
2440
2445
|
return [
|
|
2441
2446
|
`### ${task.task_id}`,
|
|
2442
2447
|
`unit_id: ${task.unit_id}`,
|
|
@@ -2457,6 +2462,11 @@ async function prepareDispatchArtifacts(params) {
|
|
|
2457
2462
|
]
|
|
2458
2463
|
: []),
|
|
2459
2464
|
"",
|
|
2465
|
+
"file_coverage (copy exactly into your AuditResult for this task):",
|
|
2466
|
+
"```json",
|
|
2467
|
+
JSON.stringify(coverageTemplate),
|
|
2468
|
+
"```",
|
|
2469
|
+
"",
|
|
2460
2470
|
];
|
|
2461
2471
|
});
|
|
2462
2472
|
const submitCommand = `"${process.execPath}" "${join(packageRoot, "audit-code.mjs")}" submit-packet ` +
|
|
@@ -2501,7 +2511,7 @@ async function prepareDispatchArtifacts(params) {
|
|
|
2501
2511
|
" unit_id copy from the task metadata",
|
|
2502
2512
|
" pass_id copy from the task metadata",
|
|
2503
2513
|
" lens copy from the task metadata",
|
|
2504
|
-
" file_coverage [{path, total_lines}] -
|
|
2514
|
+
" file_coverage [{path, total_lines}] - copy the exact template from each task section above",
|
|
2505
2515
|
" findings [] or array of finding objects",
|
|
2506
2516
|
"",
|
|
2507
2517
|
"Lens verification tasks:",
|
|
@@ -2614,6 +2624,14 @@ async function prepareDispatchArtifacts(params) {
|
|
|
2614
2624
|
if (warningsPath) {
|
|
2615
2625
|
await writeJsonFile(warningsPath, warnings);
|
|
2616
2626
|
}
|
|
2627
|
+
const activeDispatch = {
|
|
2628
|
+
run_id: runId,
|
|
2629
|
+
created_at: new Date().toISOString(),
|
|
2630
|
+
packet_count: plan.length,
|
|
2631
|
+
task_count: orderedTasks.length,
|
|
2632
|
+
status: "active",
|
|
2633
|
+
};
|
|
2634
|
+
await writeJsonFile(join(artifactsDir, ACTIVE_DISPATCH_FILENAME), activeDispatch);
|
|
2617
2635
|
return {
|
|
2618
2636
|
run_id: runId,
|
|
2619
2637
|
dispatch_plan_path: dispatchPlanPath,
|
|
@@ -2661,12 +2679,23 @@ async function cmdSubmitPacket(argv) {
|
|
|
2661
2679
|
if (!resultMap) {
|
|
2662
2680
|
throw new Error(`No ${DISPATCH_RESULT_MAP_FILENAME} found for run ${runId}; run prepare-dispatch first.`);
|
|
2663
2681
|
}
|
|
2664
|
-
|
|
2682
|
+
let packetEntries = resultMap.entries.filter((entry) => entry.packet_id === packetId);
|
|
2683
|
+
let resolvedPacketId = packetId;
|
|
2665
2684
|
if (packetEntries.length === 0) {
|
|
2666
|
-
|
|
2685
|
+
const trimmed = packetId.trim();
|
|
2686
|
+
packetEntries = resultMap.entries.filter((entry) => entry.packet_id.trim().toLowerCase() === trimmed.toLowerCase());
|
|
2687
|
+
if (packetEntries.length > 0) {
|
|
2688
|
+
resolvedPacketId = packetEntries[0].packet_id;
|
|
2689
|
+
process.stderr.write(`[submit-packet] Resolved packet_id '${packetId}' → '${resolvedPacketId}' (case/whitespace normalization)\n`);
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
if (packetEntries.length === 0) {
|
|
2693
|
+
const knownIds = [...new Set(resultMap.entries.map((e) => e.packet_id))];
|
|
2694
|
+
throw new Error(`Unknown packet_id '${packetId}' for run ${runId}.\n` +
|
|
2695
|
+
`Valid packet IDs: ${knownIds.join(", ")}`);
|
|
2667
2696
|
}
|
|
2668
2697
|
if (entriesByTaskId(packetEntries).size !== packetEntries.length) {
|
|
2669
|
-
throw new Error(`Dispatch result map has duplicate task entries for packet '${
|
|
2698
|
+
throw new Error(`Dispatch result map has duplicate task entries for packet '${resolvedPacketId}'.`);
|
|
2670
2699
|
}
|
|
2671
2700
|
const allTasks = await readJsonFile(tasksPath);
|
|
2672
2701
|
const taskById = new Map(allTasks.map((task) => [task.task_id, task]));
|
|
@@ -2717,7 +2746,7 @@ async function cmdSubmitPacket(argv) {
|
|
|
2717
2746
|
}
|
|
2718
2747
|
seen.add(taskId);
|
|
2719
2748
|
if (!expectedTaskIds.has(taskId)) {
|
|
2720
|
-
resultErrors.push(`Result at index ${index} uses task_id '${taskId}', which is not assigned to packet '${
|
|
2749
|
+
resultErrors.push(`Result at index ${index} uses task_id '${taskId}', which is not assigned to packet '${resolvedPacketId}'.`);
|
|
2721
2750
|
}
|
|
2722
2751
|
}
|
|
2723
2752
|
for (const task of tasks) {
|
|
@@ -2727,7 +2756,44 @@ async function cmdSubmitPacket(argv) {
|
|
|
2727
2756
|
}
|
|
2728
2757
|
}
|
|
2729
2758
|
if (resultErrors.length > 0) {
|
|
2730
|
-
throw new Error(`submit-packet rejected ${
|
|
2759
|
+
throw new Error(`submit-packet rejected ${resolvedPacketId}:\n${resultErrors.join("\n")}`);
|
|
2760
|
+
}
|
|
2761
|
+
// Check for duplicate findings against already-submitted results in this run
|
|
2762
|
+
const existingFindingKeys = new Set();
|
|
2763
|
+
const otherEntries = resultMap.entries.filter((e) => e.packet_id !== resolvedPacketId);
|
|
2764
|
+
for (const other of otherEntries) {
|
|
2765
|
+
try {
|
|
2766
|
+
const existing = JSON.parse(await readFile(other.result_path, "utf8"));
|
|
2767
|
+
if (existing?.findings) {
|
|
2768
|
+
for (const f of existing.findings) {
|
|
2769
|
+
const key = [
|
|
2770
|
+
(f.lens ?? "").trim().toLowerCase(),
|
|
2771
|
+
(f.category ?? "").trim().toLowerCase(),
|
|
2772
|
+
(f.title ?? "").trim().toLowerCase(),
|
|
2773
|
+
f.affected_files?.[0]?.path ?? "",
|
|
2774
|
+
].join("|");
|
|
2775
|
+
existingFindingKeys.add(key);
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
catch { /* file doesn't exist yet or invalid — skip */ }
|
|
2780
|
+
}
|
|
2781
|
+
let dupCount = 0;
|
|
2782
|
+
for (const result of payload) {
|
|
2783
|
+
for (const f of result.findings ?? []) {
|
|
2784
|
+
const key = [
|
|
2785
|
+
(f.lens ?? "").trim().toLowerCase(),
|
|
2786
|
+
(f.category ?? "").trim().toLowerCase(),
|
|
2787
|
+
(f.title ?? "").trim().toLowerCase(),
|
|
2788
|
+
f.affected_files?.[0]?.path ?? "",
|
|
2789
|
+
].join("|");
|
|
2790
|
+
if (existingFindingKeys.has(key)) {
|
|
2791
|
+
dupCount++;
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
if (dupCount > 0) {
|
|
2796
|
+
process.stderr.write(`[submit-packet] Warning: ${dupCount} finding(s) appear to duplicate findings from other packets in this run.\n`);
|
|
2731
2797
|
}
|
|
2732
2798
|
const entryByTaskId = entriesByTaskId(packetEntries);
|
|
2733
2799
|
for (const result of payload) {
|
|
@@ -2740,9 +2806,10 @@ async function cmdSubmitPacket(argv) {
|
|
|
2740
2806
|
const findingCount = payload.reduce((sum, result) => sum + result.findings.length, 0);
|
|
2741
2807
|
console.log(JSON.stringify({
|
|
2742
2808
|
run_id: runId,
|
|
2743
|
-
packet_id:
|
|
2809
|
+
packet_id: resolvedPacketId,
|
|
2744
2810
|
accepted_count: payload.length,
|
|
2745
2811
|
finding_count: findingCount,
|
|
2812
|
+
...(dupCount > 0 ? { duplicate_warning_count: dupCount } : {}),
|
|
2746
2813
|
}, null, 2));
|
|
2747
2814
|
}
|
|
2748
2815
|
async function cmdMergeAndIngest(argv) {
|
|
@@ -2781,11 +2848,24 @@ async function cmdMergeAndIngest(argv) {
|
|
|
2781
2848
|
const failing = [];
|
|
2782
2849
|
const seenTaskIds = new Set();
|
|
2783
2850
|
let spuriousFileCount = 0;
|
|
2851
|
+
const fallbackByTaskId = new Map();
|
|
2784
2852
|
for (const filename of files) {
|
|
2785
2853
|
const filePath = resolve(join(taskResultsDir, filename));
|
|
2786
2854
|
if (!expectedPaths.has(filePath)) {
|
|
2787
2855
|
spuriousFileCount++;
|
|
2788
|
-
|
|
2856
|
+
try {
|
|
2857
|
+
const raw = await readFile(filePath, "utf8");
|
|
2858
|
+
const parsed = JSON.parse(raw);
|
|
2859
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2860
|
+
const tid = typeof parsed.task_id === "string"
|
|
2861
|
+
? String(parsed.task_id) : undefined;
|
|
2862
|
+
if (tid && !fallbackByTaskId.has(tid)) {
|
|
2863
|
+
fallbackByTaskId.set(tid, parsed);
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
catch { /* not parseable — skip */ }
|
|
2868
|
+
process.stderr.write(`[merge-and-ingest] Warning: unexpected file in task-results/: ${filename}\n`);
|
|
2789
2869
|
}
|
|
2790
2870
|
}
|
|
2791
2871
|
for (const task of allTasks) {
|
|
@@ -2804,15 +2884,23 @@ async function cmdMergeAndIngest(argv) {
|
|
|
2804
2884
|
}
|
|
2805
2885
|
catch (e) {
|
|
2806
2886
|
if (isFileMissingError(e)) {
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2887
|
+
const fallback = fallbackByTaskId.get(task.task_id);
|
|
2888
|
+
if (fallback) {
|
|
2889
|
+
process.stderr.write(`[merge-and-ingest] Recovered result for '${task.task_id}' from unexpected file (matched by task_id)\n`);
|
|
2890
|
+
obj = fallback;
|
|
2891
|
+
}
|
|
2892
|
+
else {
|
|
2893
|
+
failing.push({
|
|
2894
|
+
task_id: task.task_id,
|
|
2895
|
+
errors: ["Missing audit result for assigned task."],
|
|
2896
|
+
});
|
|
2897
|
+
continue;
|
|
2898
|
+
}
|
|
2811
2899
|
}
|
|
2812
2900
|
else {
|
|
2813
2901
|
failing.push({ task_id: task.task_id, errors: [`Invalid JSON: ${e.message}`] });
|
|
2902
|
+
continue;
|
|
2814
2903
|
}
|
|
2815
|
-
continue;
|
|
2816
2904
|
}
|
|
2817
2905
|
const record = obj && typeof obj === "object" && !Array.isArray(obj)
|
|
2818
2906
|
? obj
|
|
@@ -2843,45 +2931,87 @@ async function cmdMergeAndIngest(argv) {
|
|
|
2843
2931
|
}
|
|
2844
2932
|
}
|
|
2845
2933
|
await writeJsonFile(auditResultsPath, passing);
|
|
2934
|
+
const failedTasksPath = join(runDir, "failed-tasks.json");
|
|
2846
2935
|
if (failing.length > 0) {
|
|
2847
|
-
const failedTasksPath = join(runDir, "failed-tasks.json");
|
|
2848
2936
|
await writeJsonFile(failedTasksPath, failing);
|
|
2849
|
-
|
|
2937
|
+
}
|
|
2938
|
+
if (passing.length === 0 && failing.length > 0) {
|
|
2939
|
+
throw new Error(`All ${failing.length} assigned task result(s) were missing or invalid; blocked before ingestion. See ${failedTasksPath}`);
|
|
2850
2940
|
}
|
|
2851
2941
|
const findingCount = passing.reduce((sum, result) => sum + result.findings.length, 0);
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2942
|
+
let result = null;
|
|
2943
|
+
if (passing.length > 0) {
|
|
2944
|
+
result = await runAuditStep({
|
|
2945
|
+
root: workerTask.repo_root,
|
|
2946
|
+
artifactsDir,
|
|
2947
|
+
preferredExecutor: "result_ingestion_executor",
|
|
2948
|
+
auditResultsPath,
|
|
2949
|
+
});
|
|
2950
|
+
const updatedPendingTasks = await addFileLineCountHints(workerTask.repo_root, buildPendingAuditTasks(result.updated_bundle));
|
|
2951
|
+
await writeJsonFile(tasksPath, updatedPendingTasks);
|
|
2952
|
+
}
|
|
2953
|
+
const activeDispatchPath = join(artifactsDir, ACTIVE_DISPATCH_FILENAME);
|
|
2954
|
+
try {
|
|
2955
|
+
const dispatch = await readJsonFile(activeDispatchPath);
|
|
2956
|
+
if (dispatch.run_id === runId) {
|
|
2957
|
+
dispatch.status = failing.length > 0 ? "active" : "merged";
|
|
2958
|
+
await writeJsonFile(activeDispatchPath, dispatch);
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
catch { /* no active dispatch file — skip */ }
|
|
2962
|
+
let retryDispatchPath = null;
|
|
2963
|
+
if (failing.length > 0) {
|
|
2964
|
+
const failedTaskIds = new Set(failing.map((f) => f.task_id));
|
|
2965
|
+
const failedPacketIds = [
|
|
2966
|
+
...new Set(resultMap.entries
|
|
2967
|
+
.filter((e) => failedTaskIds.has(e.task_id))
|
|
2968
|
+
.map((e) => e.packet_id)),
|
|
2969
|
+
];
|
|
2970
|
+
const retryDispatch = {
|
|
2971
|
+
run_id: runId,
|
|
2972
|
+
retry_packet_ids: failedPacketIds,
|
|
2973
|
+
failed_task_count: failing.length,
|
|
2974
|
+
accepted_task_count: passing.length,
|
|
2975
|
+
};
|
|
2976
|
+
retryDispatchPath = join(runDir, "retry-dispatch.json");
|
|
2977
|
+
await writeJsonFile(retryDispatchPath, retryDispatch);
|
|
2978
|
+
process.stderr.write(`[merge-and-ingest] ${passing.length} accepted, ${failing.length} failed. ` +
|
|
2979
|
+
`Retry packets: ${failedPacketIds.join(", ")}\n`);
|
|
2980
|
+
}
|
|
2981
|
+
const status = failing.length > 0
|
|
2982
|
+
? "partial"
|
|
2983
|
+
: (result?.progress_made ? "completed" : "no_progress");
|
|
2860
2984
|
const workerResult = buildWorkerResult({
|
|
2861
2985
|
runId,
|
|
2862
2986
|
obligationId: workerTask.obligation_id,
|
|
2863
|
-
status: result
|
|
2864
|
-
progressMade: result
|
|
2865
|
-
selectedExecutor: result
|
|
2866
|
-
artifactsWritten: result
|
|
2867
|
-
summary: result.
|
|
2868
|
-
nextLikelyStep: result
|
|
2987
|
+
status: failing.length > 0 ? "no_progress" : (result?.progress_made ? "completed" : "no_progress"),
|
|
2988
|
+
progressMade: result?.progress_made ?? false,
|
|
2989
|
+
selectedExecutor: result?.selected_executor ?? null,
|
|
2990
|
+
artifactsWritten: result?.artifacts_written ?? [],
|
|
2991
|
+
summary: result?.progress_summary ?? `${failing.length} task(s) failed`,
|
|
2992
|
+
nextLikelyStep: result?.next_likely_step ?? null,
|
|
2869
2993
|
errors: [],
|
|
2870
2994
|
});
|
|
2871
2995
|
await writeJsonFile(workerTask.result_path, workerResult);
|
|
2872
2996
|
console.log(JSON.stringify({
|
|
2873
2997
|
run_id: runId,
|
|
2874
|
-
status
|
|
2998
|
+
status,
|
|
2875
2999
|
accepted_count: passing.length,
|
|
2876
|
-
rejected_count:
|
|
3000
|
+
rejected_count: failing.length,
|
|
2877
3001
|
spurious_file_count: spuriousFileCount,
|
|
2878
3002
|
finding_count: findingCount,
|
|
2879
3003
|
audit_results_path: auditResultsPath,
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
3004
|
+
...(retryDispatchPath ? { retry_dispatch_path: retryDispatchPath } : {}),
|
|
3005
|
+
...(result ? {
|
|
3006
|
+
selected_executor: workerResult.selected_executor,
|
|
3007
|
+
progress_made: workerResult.progress_made,
|
|
3008
|
+
progress_summary: workerResult.summary,
|
|
3009
|
+
next_likely_step: workerResult.next_likely_step,
|
|
3010
|
+
} : {}),
|
|
2884
3011
|
}, null, 2));
|
|
3012
|
+
if (failing.length > 0) {
|
|
3013
|
+
process.exitCode = 2;
|
|
3014
|
+
}
|
|
2885
3015
|
}
|
|
2886
3016
|
async function cmdValidateResult(argv) {
|
|
2887
3017
|
const rawRunId = getFlag(argv, "--run-id");
|
|
@@ -3357,6 +3487,69 @@ async function cmdQuota(argv) {
|
|
|
3357
3487
|
quota_state_path: getQuotaStatePath(),
|
|
3358
3488
|
}, null, 2));
|
|
3359
3489
|
}
|
|
3490
|
+
async function cmdDispatchStatus(argv) {
|
|
3491
|
+
const artifactsDir = getArtifactsDir(argv);
|
|
3492
|
+
const activeDispatchPath = join(artifactsDir, ACTIVE_DISPATCH_FILENAME);
|
|
3493
|
+
let activeDispatch = null;
|
|
3494
|
+
try {
|
|
3495
|
+
activeDispatch = await readJsonFile(activeDispatchPath);
|
|
3496
|
+
}
|
|
3497
|
+
catch (e) {
|
|
3498
|
+
if (!isFileMissingError(e))
|
|
3499
|
+
throw e;
|
|
3500
|
+
}
|
|
3501
|
+
if (!activeDispatch) {
|
|
3502
|
+
console.log(JSON.stringify({ status: "no_active_dispatch" }, null, 2));
|
|
3503
|
+
return;
|
|
3504
|
+
}
|
|
3505
|
+
const runDir = join(artifactsDir, "runs", activeDispatch.run_id);
|
|
3506
|
+
const resultMap = await loadDispatchResultMap(runDir);
|
|
3507
|
+
if (!resultMap) {
|
|
3508
|
+
console.log(JSON.stringify({
|
|
3509
|
+
status: "missing_result_map",
|
|
3510
|
+
run_id: activeDispatch.run_id,
|
|
3511
|
+
}, null, 2));
|
|
3512
|
+
return;
|
|
3513
|
+
}
|
|
3514
|
+
const packetIds = [...new Set(resultMap.entries.map((e) => e.packet_id))];
|
|
3515
|
+
const packetStatus = [];
|
|
3516
|
+
for (const pid of packetIds) {
|
|
3517
|
+
if (pid === "__prior_dispatch__")
|
|
3518
|
+
continue;
|
|
3519
|
+
const entries = resultMap.entries.filter((e) => e.packet_id === pid);
|
|
3520
|
+
let completed = 0;
|
|
3521
|
+
const missing = [];
|
|
3522
|
+
for (const entry of entries) {
|
|
3523
|
+
try {
|
|
3524
|
+
await readFile(entry.result_path, "utf8");
|
|
3525
|
+
completed++;
|
|
3526
|
+
}
|
|
3527
|
+
catch {
|
|
3528
|
+
missing.push(entry.task_id);
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
packetStatus.push({
|
|
3532
|
+
packet_id: pid,
|
|
3533
|
+
task_count: entries.length,
|
|
3534
|
+
completed_count: completed,
|
|
3535
|
+
missing_task_ids: missing,
|
|
3536
|
+
});
|
|
3537
|
+
}
|
|
3538
|
+
const totalTasks = packetStatus.reduce((s, p) => s + p.task_count, 0);
|
|
3539
|
+
const completedTasks = packetStatus.reduce((s, p) => s + p.completed_count, 0);
|
|
3540
|
+
const completedPackets = packetStatus.filter((p) => p.missing_task_ids.length === 0).length;
|
|
3541
|
+
console.log(JSON.stringify({
|
|
3542
|
+
run_id: activeDispatch.run_id,
|
|
3543
|
+
dispatch_status: activeDispatch.status,
|
|
3544
|
+
created_at: activeDispatch.created_at,
|
|
3545
|
+
total_packets: packetStatus.length,
|
|
3546
|
+
completed_packets: completedPackets,
|
|
3547
|
+
total_tasks: totalTasks,
|
|
3548
|
+
completed_tasks: completedTasks,
|
|
3549
|
+
missing_tasks: totalTasks - completedTasks,
|
|
3550
|
+
packets: packetStatus,
|
|
3551
|
+
}, null, 2));
|
|
3552
|
+
}
|
|
3360
3553
|
async function main(argv) {
|
|
3361
3554
|
const command = argv[2] ?? "sample-run";
|
|
3362
3555
|
switch (command) {
|
|
@@ -3429,9 +3622,12 @@ async function main(argv) {
|
|
|
3429
3622
|
case "status":
|
|
3430
3623
|
await cmdStatus(argv);
|
|
3431
3624
|
return;
|
|
3625
|
+
case "dispatch-status":
|
|
3626
|
+
await cmdDispatchStatus(argv);
|
|
3627
|
+
return;
|
|
3432
3628
|
default:
|
|
3433
3629
|
console.error(`Unknown command: ${command}`);
|
|
3434
|
-
console.error("Available commands: sample-run, advance-audit, next-step, run-to-completion, worker-run, import-external-analyzer, intake, plan, ingest-results, explain-task, update-runtime-validation, validate, validate-results, requeue, synthesize, cleanup, mcp, prepare-dispatch, merge-and-ingest, submit-packet, validate-result, quota, status");
|
|
3630
|
+
console.error("Available commands: sample-run, advance-audit, next-step, run-to-completion, worker-run, import-external-analyzer, intake, plan, ingest-results, explain-task, update-runtime-validation, validate, validate-results, requeue, synthesize, cleanup, mcp, prepare-dispatch, merge-and-ingest, submit-packet, validate-result, quota, status, dispatch-status");
|
|
3435
3631
|
process.exitCode = 1;
|
|
3436
3632
|
}
|
|
3437
3633
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AuditTask } from "../types.js";
|
|
2
2
|
import { type ValidationIssue } from "./basic.js";
|
|
3
3
|
export type IssueSeverity = "error" | "warning";
|
|
4
|
+
export declare function normalizeCoveragePath(path: string): string;
|
|
4
5
|
export interface AuditResultIssue extends ValidationIssue {
|
|
5
6
|
result_index: number;
|
|
6
7
|
task_id: string;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { describeValue, formatValidationIssues, isRecord, } from "./basic.js";
|
|
2
|
+
export function normalizeCoveragePath(path) {
|
|
3
|
+
return path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
4
|
+
}
|
|
2
5
|
const REQUIRED_FINDING_FIELDS = [
|
|
3
6
|
"id",
|
|
4
7
|
"title",
|
|
@@ -423,6 +426,18 @@ export function validateAuditResults(results, tasks, options = {}) {
|
|
|
423
426
|
tasks.map((item) => item.task_id).join(", "),
|
|
424
427
|
});
|
|
425
428
|
}
|
|
429
|
+
const taskNormMap = new Map();
|
|
430
|
+
if (task) {
|
|
431
|
+
for (const fp of task.file_paths) {
|
|
432
|
+
taskNormMap.set(normalizeCoveragePath(fp), fp);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
const normLineIndex = new Map();
|
|
436
|
+
if (options.lineIndex) {
|
|
437
|
+
for (const [k, v] of Object.entries(options.lineIndex)) {
|
|
438
|
+
normLineIndex.set(normalizeCoveragePath(k), v);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
426
441
|
const fileCoverage = result.file_coverage;
|
|
427
442
|
const normalizedFileCoverage = [];
|
|
428
443
|
const declaredAssignedCoveragePaths = new Set();
|
|
@@ -447,6 +462,10 @@ export function validateAuditResults(results, tasks, options = {}) {
|
|
|
447
462
|
});
|
|
448
463
|
continue;
|
|
449
464
|
}
|
|
465
|
+
const entryNorm = isNonEmptyString(entry.path)
|
|
466
|
+
? normalizeCoveragePath(entry.path)
|
|
467
|
+
: "";
|
|
468
|
+
const canonicalPath = taskNormMap.get(entryNorm);
|
|
450
469
|
if (!isNonEmptyString(entry.path)) {
|
|
451
470
|
pushIssue(issues, {
|
|
452
471
|
result_index: i,
|
|
@@ -455,7 +474,7 @@ export function validateAuditResults(results, tasks, options = {}) {
|
|
|
455
474
|
message: "file_coverage entry has an empty path.",
|
|
456
475
|
});
|
|
457
476
|
}
|
|
458
|
-
else if (task && !
|
|
477
|
+
else if (task && !canonicalPath) {
|
|
459
478
|
pushIssue(issues, {
|
|
460
479
|
result_index: i,
|
|
461
480
|
task_id: taskId,
|
|
@@ -463,7 +482,7 @@ export function validateAuditResults(results, tasks, options = {}) {
|
|
|
463
482
|
message: `file_coverage path '${entry.path}' is not listed in the task file_paths.`,
|
|
464
483
|
});
|
|
465
484
|
}
|
|
466
|
-
else if (seenCoveragePaths.has(
|
|
485
|
+
else if (seenCoveragePaths.has(entryNorm)) {
|
|
467
486
|
pushIssue(issues, {
|
|
468
487
|
result_index: i,
|
|
469
488
|
task_id: taskId,
|
|
@@ -472,11 +491,10 @@ export function validateAuditResults(results, tasks, options = {}) {
|
|
|
472
491
|
});
|
|
473
492
|
}
|
|
474
493
|
else {
|
|
475
|
-
seenCoveragePaths.add(
|
|
494
|
+
seenCoveragePaths.add(entryNorm);
|
|
476
495
|
}
|
|
477
|
-
if (
|
|
478
|
-
(
|
|
479
|
-
declaredAssignedCoveragePaths.add(entry.path);
|
|
496
|
+
if (entryNorm.length > 0 && (!task || canonicalPath)) {
|
|
497
|
+
declaredAssignedCoveragePaths.add(canonicalPath ?? entryNorm);
|
|
480
498
|
}
|
|
481
499
|
if (!Number.isInteger(entry.total_lines)) {
|
|
482
500
|
pushIssue(issues, {
|
|
@@ -495,8 +513,8 @@ export function validateAuditResults(results, tasks, options = {}) {
|
|
|
495
513
|
message: "file_coverage total_lines must be zero or greater.",
|
|
496
514
|
});
|
|
497
515
|
}
|
|
498
|
-
const expectedLineCount =
|
|
499
|
-
?
|
|
516
|
+
const expectedLineCount = entryNorm.length > 0
|
|
517
|
+
? normLineIndex.get(entryNorm)
|
|
500
518
|
: undefined;
|
|
501
519
|
if (Number.isInteger(entry.total_lines) &&
|
|
502
520
|
typeof expectedLineCount === "number" &&
|
|
@@ -509,19 +527,19 @@ export function validateAuditResults(results, tasks, options = {}) {
|
|
|
509
527
|
`(expected ${expectedLineCount}, got ${entry.total_lines}).`,
|
|
510
528
|
});
|
|
511
529
|
}
|
|
512
|
-
if (
|
|
530
|
+
if (entryNorm.length > 0 &&
|
|
513
531
|
Number.isInteger(entry.total_lines) &&
|
|
514
532
|
Number(entry.total_lines) >= 0 &&
|
|
515
|
-
(!task ||
|
|
533
|
+
(!task || canonicalPath)) {
|
|
516
534
|
normalizedFileCoverage.push({
|
|
517
|
-
path:
|
|
535
|
+
path: canonicalPath ?? entryNorm,
|
|
518
536
|
total_lines: Number(entry.total_lines),
|
|
519
537
|
});
|
|
520
538
|
}
|
|
521
539
|
}
|
|
522
540
|
if (task) {
|
|
523
541
|
for (const path of task.file_paths) {
|
|
524
|
-
if (!seenCoveragePaths.has(path)) {
|
|
542
|
+
if (!seenCoveragePaths.has(normalizeCoveragePath(path))) {
|
|
525
543
|
pushIssue(issues, {
|
|
526
544
|
result_index: i,
|
|
527
545
|
task_id: taskId,
|