auditor-lambda 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +23 -7
  2. package/audit-code-wrapper-lib.mjs +1605 -330
  3. package/dist/cli.js +78 -16
  4. package/dist/coverage.d.ts +2 -2
  5. package/dist/coverage.js +5 -5
  6. package/dist/extractors/disposition.js +10 -1
  7. package/dist/extractors/flows.js +7 -1
  8. package/dist/extractors/pathPatterns.d.ts +3 -0
  9. package/dist/extractors/pathPatterns.js +15 -0
  10. package/dist/extractors/risk.js +7 -1
  11. package/dist/io/artifacts.d.ts +6 -6
  12. package/dist/io/artifacts.js +14 -17
  13. package/dist/io/json.d.ts +2 -0
  14. package/dist/io/json.js +15 -0
  15. package/dist/io/runArtifacts.d.ts +1 -0
  16. package/dist/io/runArtifacts.js +18 -4
  17. package/dist/mcp/server.d.ts +1 -0
  18. package/dist/mcp/server.js +579 -0
  19. package/dist/orchestrator/advance.js +9 -2
  20. package/dist/orchestrator/dependencyMap.js +9 -13
  21. package/dist/orchestrator/executors.js +7 -2
  22. package/dist/orchestrator/flowRequeue.js +1 -1
  23. package/dist/orchestrator/internalExecutors.d.ts +2 -1
  24. package/dist/orchestrator/internalExecutors.js +120 -63
  25. package/dist/orchestrator/requeue.js +9 -4
  26. package/dist/orchestrator/resultIngestion.js +5 -6
  27. package/dist/orchestrator/runtimeValidation.d.ts +7 -2
  28. package/dist/orchestrator/runtimeValidation.js +61 -49
  29. package/dist/orchestrator/runtimeValidationUpdate.js +2 -4
  30. package/dist/orchestrator/state.js +18 -13
  31. package/dist/orchestrator/trivialAudit.js +8 -5
  32. package/dist/prompts/renderWorkerPrompt.js +3 -2
  33. package/dist/reporting/mergeFindings.js +0 -11
  34. package/dist/reporting/synthesis.d.ts +25 -22
  35. package/dist/reporting/synthesis.js +92 -59
  36. package/dist/reporting/workBlocks.d.ts +12 -3
  37. package/dist/reporting/workBlocks.js +124 -70
  38. package/dist/types/flows.d.ts +2 -0
  39. package/dist/types/runtimeValidation.d.ts +2 -1
  40. package/dist/types.d.ts +4 -7
  41. package/dist/validation/auditResults.js +64 -99
  42. package/docs/agent-integrations.md +38 -29
  43. package/docs/artifacts.md +16 -56
  44. package/docs/bootstrap-install.md +60 -30
  45. package/docs/contract.md +22 -205
  46. package/docs/next-steps.md +59 -44
  47. package/docs/packaging.md +13 -3
  48. package/docs/production-launch-bar.md +2 -2
  49. package/docs/production-readiness.md +9 -5
  50. package/docs/releasing.md +81 -0
  51. package/package.json +4 -1
  52. package/schemas/audit_result.schema.json +4 -6
  53. package/schemas/runtime_validation_report.schema.json +1 -1
  54. package/skills/audit-code/SKILL.md +11 -2
  55. package/skills/audit-code/audit-code.prompt.md +5 -8
  56. package/schemas/merged_findings.schema.json +0 -19
  57. package/schemas/root_cause_clusters.schema.json +0 -28
  58. package/schemas/synthesis_report.schema.json +0 -61
@@ -20,15 +20,16 @@ export function renderWorkerPrompt(task) {
20
20
  " 2. Review the content under the specified lens.",
21
21
  " 3. Emit one AuditResult with:",
22
22
  " task_id, unit_id, pass_id, lens",
23
- " reviewed_ranges: [{path, start, end, line_count}] covering what you read",
23
+ " file_coverage: [{path, total_lines}] for every assigned file you reviewed",
24
24
  " findings: array (empty if nothing found)",
25
- " line_count must match the file's current total line count so ingestion can verify the cited ranges.",
25
+ " total_lines must match the file's current total line count.",
26
26
  " Each finding must include:",
27
27
  " id, title, category, severity, confidence, lens, summary, affected_files,",
28
28
  " evidence (an array of plain strings only, at least one excerpt or line reference from the file you read)",
29
29
  " Example evidence entry: src/foo.ts:42 - variable overwritten before use",
30
30
  " Optional finding fields: impact, likelihood, reproduction, systemic, related_findings",
31
31
  " Low-priority tasks still require a real review. Use findings: [] only when you genuinely found nothing notable.",
32
+ `Reference schema: ${task.artifacts_dir}/dispatch/audit-result.schema.json`,
32
33
  `Write the AuditResult[] JSON array to: ${task.audit_results_path}`,
33
34
  ];
34
35
  if (task.skip_worker_command) {
@@ -82,9 +82,6 @@ export function mergeFindings(results, runtimeReport, externalAnalyzerResults) {
82
82
  ...analyzerEvidence,
83
83
  ]),
84
84
  ],
85
- related_findings: finding.related_findings && finding.related_findings.length > 0
86
- ? [...new Set(finding.related_findings)]
87
- : undefined,
88
85
  });
89
86
  continue;
90
87
  }
@@ -110,14 +107,6 @@ export function mergeFindings(results, runtimeReport, externalAnalyzerResults) {
110
107
  ...analyzerEvidence,
111
108
  ]),
112
109
  ];
113
- const related = new Set([
114
- ...(existing.related_findings ?? []),
115
- ...(finding.related_findings ?? []),
116
- existing.id,
117
- finding.id,
118
- ]);
119
- existing.related_findings =
120
- related.size > 0 ? [...related].sort() : undefined;
121
110
  }
122
111
  }
123
112
  return [...merged.values()].sort((a, b) => {
@@ -1,24 +1,27 @@
1
- import type { AuditResult } from "../types.js";
2
- import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
1
+ import type { AuditResult, CoverageMatrix, Finding, UnitManifest } from "../types.js";
2
+ import type { CriticalFlowManifest } from "../types/flows.js";
3
+ import type { GraphBundle } from "../types/graph.js";
3
4
  import type { RuntimeValidationReport } from "../types/runtimeValidation.js";
4
- import { mergeFindings } from "./mergeFindings.js";
5
- import { buildRootCauseClusters } from "./rootCause.js";
6
- import { buildWorkBlocks } from "./workBlocks.js";
7
- export interface SynthesisReport {
8
- summary: {
9
- finding_count: number;
10
- cluster_count: number;
11
- work_block_count: number;
12
- runtime_validation_status_breakdown: Record<string, number>;
13
- notes: string[];
14
- severity_breakdown: Record<string, number>;
15
- external_analyzer_summary: {
16
- tool_count: number;
17
- result_count: number;
18
- };
19
- };
20
- merged_findings: ReturnType<typeof mergeFindings>;
21
- root_cause_clusters: ReturnType<typeof buildRootCauseClusters>;
22
- work_blocks: ReturnType<typeof buildWorkBlocks>;
5
+ import { type WorkBlock } from "./workBlocks.js";
6
+ export interface AuditReportSummary {
7
+ finding_count: number;
8
+ work_block_count: number;
9
+ severity_breakdown: Record<string, number>;
10
+ audited_file_count: number;
11
+ excluded_file_count: number;
12
+ runtime_validation_status_breakdown: Record<string, number>;
23
13
  }
24
- export declare function buildSynthesisReport(results: AuditResult[], runtimeReport?: RuntimeValidationReport, externalAnalyzerResults?: ExternalAnalyzerResults): SynthesisReport;
14
+ export interface AuditReportModel {
15
+ summary: AuditReportSummary;
16
+ findings: Finding[];
17
+ work_blocks: WorkBlock[];
18
+ }
19
+ export declare function buildAuditReportModel(params: {
20
+ results: AuditResult[];
21
+ unitManifest?: UnitManifest;
22
+ graphBundle?: GraphBundle;
23
+ criticalFlows?: CriticalFlowManifest;
24
+ coverageMatrix?: CoverageMatrix;
25
+ runtimeValidationReport?: RuntimeValidationReport;
26
+ }): AuditReportModel;
27
+ export declare function renderAuditReportMarkdown(model: AuditReportModel): string;
@@ -1,13 +1,5 @@
1
- import { mergeFindings } from "./mergeFindings.js";
2
- import { buildRootCauseClusters } from "./rootCause.js";
3
1
  import { buildWorkBlocks } from "./workBlocks.js";
4
- function statusBreakdown(report) {
5
- const breakdown = {};
6
- for (const result of report?.results ?? []) {
7
- breakdown[result.status] = (breakdown[result.status] ?? 0) + 1;
8
- }
9
- return breakdown;
10
- }
2
+ import { mergeFindings } from "./mergeFindings.js";
11
3
  function severityBreakdown(findings) {
12
4
  const breakdown = {};
13
5
  for (const finding of findings) {
@@ -15,63 +7,104 @@ function severityBreakdown(findings) {
15
7
  }
16
8
  return breakdown;
17
9
  }
18
- function zeroFindingLensNotes(results) {
19
- const tasksByLens = new Map();
20
- const findingsByLens = new Map();
21
- for (const result of results) {
22
- tasksByLens.set(result.lens, (tasksByLens.get(result.lens) ?? 0) + 1);
23
- findingsByLens.set(result.lens, (findingsByLens.get(result.lens) ?? 0) + result.findings.length);
10
+ function runtimeStatusBreakdown(report) {
11
+ const breakdown = {};
12
+ for (const result of report?.results ?? []) {
13
+ breakdown[result.status] = (breakdown[result.status] ?? 0) + 1;
24
14
  }
25
- const zeroLenses = [...tasksByLens.entries()]
26
- .filter(([lens, count]) => count > 0 && (findingsByLens.get(lens) ?? 0) === 0)
27
- .map(([lens]) => lens)
28
- .sort();
29
- if (zeroLenses.length === 0)
30
- return [];
31
- return [
32
- `Zero findings across all reviewed tasks for lens(es): ${zeroLenses.join(", ")}. Verify these tasks were genuinely reviewed rather than batch-generated.`,
33
- ];
15
+ return breakdown;
34
16
  }
35
- function externalSummary(results) {
36
- if (!results) {
37
- return { tool_count: 0, result_count: 0 };
38
- }
17
+ function coverageSummary(coverage) {
18
+ const files = coverage?.files ?? [];
39
19
  return {
40
- tool_count: results.tool ? 1 : 0,
41
- result_count: results.results.length,
20
+ audited_file_count: files.filter((file) => file.audit_status === "complete").length,
21
+ excluded_file_count: files.filter((file) => file.audit_status === "excluded").length,
42
22
  };
43
23
  }
44
- export function buildSynthesisReport(results, runtimeReport, externalAnalyzerResults) {
45
- const merged_findings = mergeFindings(results, runtimeReport, externalAnalyzerResults);
46
- const root_cause_clusters = buildRootCauseClusters(merged_findings, runtimeReport, externalAnalyzerResults);
47
- const work_blocks = buildWorkBlocks(merged_findings);
48
- const runtimeBreakdown = statusBreakdown(runtimeReport);
49
- const sevBreakdown = severityBreakdown(merged_findings);
50
- const extSummary = externalSummary(externalAnalyzerResults);
24
+ function formatSeverityList(summary) {
25
+ const ordered = ["critical", "high", "medium", "low", "info"];
26
+ const parts = ordered
27
+ .filter((severity) => (summary[severity] ?? 0) > 0)
28
+ .map((severity) => `${severity}: ${summary[severity]}`);
29
+ return parts.length > 0 ? parts.join(", ") : "none";
30
+ }
31
+ export function buildAuditReportModel(params) {
32
+ const findings = mergeFindings(params.results, params.runtimeValidationReport);
33
+ const workBlocks = buildWorkBlocks({
34
+ findings,
35
+ unitManifest: params.unitManifest,
36
+ graphBundle: params.graphBundle,
37
+ criticalFlows: params.criticalFlows,
38
+ });
39
+ const coverage = coverageSummary(params.coverageMatrix);
51
40
  return {
52
41
  summary: {
53
- finding_count: merged_findings.length,
54
- cluster_count: root_cause_clusters.length,
55
- work_block_count: work_blocks.length,
56
- runtime_validation_status_breakdown: runtimeBreakdown,
57
- severity_breakdown: sevBreakdown,
58
- external_analyzer_summary: extSummary,
59
- notes: [
60
- ...(Object.keys(runtimeBreakdown).length === 0
61
- ? ["No runtime validation evidence attached."]
62
- : [
63
- "Runtime validation evidence has been incorporated into synthesis.",
64
- ]),
65
- ...(extSummary.result_count > 0
66
- ? [
67
- `External analyzer signals incorporated: ${extSummary.result_count} result(s) from ${externalAnalyzerResults?.tool}.`,
68
- ]
69
- : []),
70
- ...zeroFindingLensNotes(results),
71
- ],
42
+ finding_count: findings.length,
43
+ work_block_count: workBlocks.length,
44
+ severity_breakdown: severityBreakdown(findings),
45
+ audited_file_count: coverage.audited_file_count,
46
+ excluded_file_count: coverage.excluded_file_count,
47
+ runtime_validation_status_breakdown: runtimeStatusBreakdown(params.runtimeValidationReport),
72
48
  },
73
- merged_findings,
74
- root_cause_clusters,
75
- work_blocks,
49
+ findings,
50
+ work_blocks: workBlocks,
76
51
  };
77
52
  }
53
+ export function renderAuditReportMarkdown(model) {
54
+ const lines = [
55
+ "# Audit Report",
56
+ "",
57
+ "## Summary",
58
+ "",
59
+ `- Findings: ${model.summary.finding_count}`,
60
+ `- Work blocks: ${model.summary.work_block_count}`,
61
+ `- Severity breakdown: ${formatSeverityList(model.summary.severity_breakdown)}`,
62
+ `- Fully audited files: ${model.summary.audited_file_count}`,
63
+ `- Excluded non-auditable files: ${model.summary.excluded_file_count}`,
64
+ "",
65
+ "## Work Blocks",
66
+ "",
67
+ ];
68
+ if (model.work_blocks.length === 0) {
69
+ lines.push("No remediation work blocks were generated.", "");
70
+ }
71
+ else {
72
+ for (const block of model.work_blocks) {
73
+ lines.push(`### ${block.id}`);
74
+ lines.push("");
75
+ lines.push(`- Max severity: ${block.max_severity}`);
76
+ lines.push(`- Units: ${block.unit_ids.join(", ")}`);
77
+ lines.push(`- Owned files: ${block.owned_files.join(", ")}`);
78
+ lines.push(`- Findings: ${block.finding_ids.join(", ")}`);
79
+ lines.push(`- Depends on: ${block.depends_on.length > 0 ? block.depends_on.join(", ") : "none"}`);
80
+ lines.push(`- Rationale: ${block.rationale}`);
81
+ lines.push("");
82
+ }
83
+ }
84
+ lines.push("## Findings", "");
85
+ if (model.findings.length === 0) {
86
+ lines.push("No findings were recorded.", "");
87
+ }
88
+ else {
89
+ for (const finding of model.findings) {
90
+ lines.push(`### ${finding.id} — ${finding.title}`);
91
+ lines.push("");
92
+ lines.push(`- Severity: ${finding.severity}`);
93
+ lines.push(`- Confidence: ${finding.confidence}`);
94
+ lines.push(`- Lens: ${finding.lens}`);
95
+ lines.push(`- Files: ${finding.affected_files.map((file) => file.path).join(", ")}`);
96
+ lines.push(`- Summary: ${finding.summary}`);
97
+ if (finding.evidence && finding.evidence.length > 0) {
98
+ lines.push("- Evidence:");
99
+ for (const evidence of finding.evidence) {
100
+ lines.push(` - ${evidence}`);
101
+ }
102
+ }
103
+ lines.push("");
104
+ }
105
+ }
106
+ lines.push("## Scope and Coverage", "");
107
+ lines.push("This report is deterministic output from the completed audit. Non-auditable files were excluded from scope before task generation.");
108
+ lines.push("");
109
+ return lines.join("\n");
110
+ }
@@ -1,9 +1,18 @@
1
- import type { Finding } from "../types.js";
1
+ import type { Finding, UnitManifest } from "../types.js";
2
+ import type { CriticalFlowManifest } from "../types/flows.js";
3
+ import type { GraphBundle } from "../types/graph.js";
2
4
  export interface WorkBlock {
3
5
  id: string;
4
6
  finding_ids: string[];
5
- affected_files: string[];
7
+ unit_ids: string[];
8
+ owned_files: string[];
6
9
  max_severity: Finding["severity"];
7
10
  rationale: string;
11
+ depends_on: string[];
8
12
  }
9
- export declare function buildWorkBlocks(findings: Finding[]): WorkBlock[];
13
+ export declare function buildWorkBlocks(params: {
14
+ findings: Finding[];
15
+ unitManifest?: UnitManifest;
16
+ graphBundle?: GraphBundle;
17
+ criticalFlows?: CriticalFlowManifest;
18
+ }): WorkBlock[];
@@ -12,98 +12,152 @@ function severityRank(severity) {
12
12
  return 1;
13
13
  }
14
14
  }
15
- export function buildWorkBlocks(findings) {
16
- if (findings.length === 0)
15
+ function buildFileUnitMap(unitManifest) {
16
+ const map = new Map();
17
+ for (const unit of unitManifest?.units ?? []) {
18
+ for (const path of unit.files) {
19
+ if (!map.has(path)) {
20
+ map.set(path, unit.unit_id);
21
+ }
22
+ }
23
+ }
24
+ return map;
25
+ }
26
+ function normalizeOwnedUnits(finding, fileUnitMap) {
27
+ const unitIds = new Set();
28
+ for (const file of finding.affected_files) {
29
+ const mapped = fileUnitMap.get(file.path);
30
+ unitIds.add(mapped ?? `file:${file.path}`);
31
+ }
32
+ return [...unitIds].sort();
33
+ }
34
+ function computeDependencies(params) {
35
+ const blockByFile = new Map();
36
+ for (const block of params.blocks) {
37
+ for (const path of block.owned_files) {
38
+ blockByFile.set(path, block.id);
39
+ }
40
+ }
41
+ const dependsOn = new Map();
42
+ for (const block of params.blocks) {
43
+ dependsOn.set(block.id, new Set());
44
+ }
45
+ const graphEdges = [
46
+ ...(params.graphBundle?.graphs.imports ?? []),
47
+ ...(params.graphBundle?.graphs.calls ?? []),
48
+ ];
49
+ for (const edge of graphEdges) {
50
+ const fromBlock = blockByFile.get(edge.from);
51
+ const toBlock = blockByFile.get(edge.to);
52
+ if (fromBlock && toBlock && fromBlock !== toBlock) {
53
+ dependsOn.get(fromBlock)?.add(toBlock);
54
+ }
55
+ }
56
+ for (const flow of params.criticalFlows?.flows ?? []) {
57
+ const flowBlocks = new Set();
58
+ for (const path of flow.paths) {
59
+ const blockId = blockByFile.get(path);
60
+ if (blockId) {
61
+ flowBlocks.add(blockId);
62
+ }
63
+ }
64
+ const ordered = [...flowBlocks].sort();
65
+ for (let i = 1; i < ordered.length; i++) {
66
+ dependsOn.get(ordered[i - 1])?.add(ordered[i]);
67
+ }
68
+ }
69
+ return params.blocks.map((block) => ({
70
+ ...block,
71
+ depends_on: [...(dependsOn.get(block.id) ?? [])].sort(),
72
+ }));
73
+ }
74
+ export function buildWorkBlocks(params) {
75
+ if (params.findings.length === 0) {
17
76
  return [];
77
+ }
78
+ const fileUnitMap = buildFileUnitMap(params.unitManifest);
18
79
  const parent = new Map();
80
+ const findingUnits = new Map();
19
81
  function find(id) {
20
82
  if (!parent.has(id))
21
83
  parent.set(id, id);
22
- const p = parent.get(id);
23
- if (p === id)
84
+ const current = parent.get(id);
85
+ if (current === id)
24
86
  return id;
25
- const root = find(p);
87
+ const root = find(current);
26
88
  parent.set(id, root);
27
89
  return root;
28
90
  }
29
91
  function union(a, b) {
30
- const ra = find(a);
31
- const rb = find(b);
32
- if (ra !== rb)
33
- parent.set(ra, rb);
34
- }
35
- for (const finding of findings)
36
- find(finding.id);
37
- // Union findings that share affected files
38
- const fileToIds = new Map();
39
- for (const finding of findings) {
40
- for (const af of finding.affected_files) {
41
- const ids = fileToIds.get(af.path) ?? [];
42
- ids.push(finding.id);
43
- fileToIds.set(af.path, ids);
92
+ const rootA = find(a);
93
+ const rootB = find(b);
94
+ if (rootA !== rootB) {
95
+ parent.set(rootA, rootB);
44
96
  }
45
97
  }
46
- for (const ids of fileToIds.values()) {
47
- for (let i = 1; i < ids.length; i++) {
48
- union(ids[0], ids[i]);
98
+ const unitToFindingIds = new Map();
99
+ for (const finding of params.findings) {
100
+ parent.set(finding.id, finding.id);
101
+ const ownedUnits = normalizeOwnedUnits(finding, fileUnitMap);
102
+ findingUnits.set(finding.id, ownedUnits);
103
+ for (const unitId of ownedUnits) {
104
+ const ids = unitToFindingIds.get(unitId) ?? [];
105
+ ids.push(finding.id);
106
+ unitToFindingIds.set(unitId, ids);
49
107
  }
50
108
  }
51
- // Union findings explicitly linked via related_findings
52
- const knownIds = new Set(findings.map((f) => f.id));
53
- for (const finding of findings) {
54
- for (const relId of finding.related_findings ?? []) {
55
- if (knownIds.has(relId))
56
- union(finding.id, relId);
109
+ for (const ids of unitToFindingIds.values()) {
110
+ for (let index = 1; index < ids.length; index++) {
111
+ union(ids[0], ids[index]);
57
112
  }
58
113
  }
59
- // Collect connected components
60
- const groups = new Map();
61
- for (const finding of findings) {
114
+ const grouped = new Map();
115
+ for (const finding of params.findings) {
62
116
  const root = find(finding.id);
63
- const group = groups.get(root) ?? [];
117
+ const group = grouped.get(root) ?? [];
64
118
  group.push(finding);
65
- groups.set(root, group);
119
+ grouped.set(root, group);
66
120
  }
67
- const blocks = [];
68
- for (const group of groups.values()) {
69
- // Within a block: severity desc, systemic first, then title
70
- const sorted = [...group].sort((a, b) => {
71
- const sevDelta = severityRank(b.severity) - severityRank(a.severity);
72
- if (sevDelta !== 0)
73
- return sevDelta;
74
- const sysA = a.systemic ? 1 : 0;
75
- const sysB = b.systemic ? 1 : 0;
76
- if (sysA !== sysB)
77
- return sysB - sysA;
78
- return a.title.localeCompare(b.title);
121
+ const blocks = [...grouped.values()].map((group, index) => {
122
+ const orderedFindings = [...group].sort((a, b) => {
123
+ const severityDelta = severityRank(b.severity) - severityRank(a.severity);
124
+ if (severityDelta !== 0)
125
+ return severityDelta;
126
+ return a.id.localeCompare(b.id);
79
127
  });
80
- const affectedFiles = [
81
- ...new Set(sorted.flatMap((f) => f.affected_files.map((af) => af.path))),
128
+ const unitIds = [
129
+ ...new Set(group.flatMap((finding) => findingUnits.get(finding.id) ?? [])),
82
130
  ].sort();
83
- const maxSeverity = sorted[0].severity;
84
- const rationale = affectedFiles.length === 1
85
- ? `${sorted.length} finding(s) affect the same file — fix together to avoid conflicts.`
86
- : `${sorted.length} finding(s) share ${affectedFiles.length} file(s) or are linked via related_findings — fix as a unit so one change does not invalidate another.`;
87
- blocks.push({
88
- id: "",
89
- finding_ids: sorted.map((f) => f.id),
90
- affected_files: affectedFiles,
91
- max_severity: maxSeverity,
92
- rationale,
93
- });
94
- }
95
- // Sort blocks: highest severity first, then largest group, then stable by first finding id
131
+ const ownedFiles = [
132
+ ...new Set(group.flatMap((finding) => finding.affected_files.map((file) => file.path))),
133
+ ].sort();
134
+ return {
135
+ id: `block-${index + 1}`,
136
+ finding_ids: orderedFindings.map((finding) => finding.id),
137
+ unit_ids: unitIds,
138
+ owned_files: ownedFiles,
139
+ max_severity: orderedFindings[0].severity,
140
+ rationale: unitIds.length === 1
141
+ ? "All findings map to the same owned unit and should be remediated together."
142
+ : "Findings share owned units transitively and should remain one non-overlapping remediation block.",
143
+ depends_on: [],
144
+ };
145
+ });
96
146
  blocks.sort((a, b) => {
97
- const sevDelta = severityRank(b.max_severity) - severityRank(a.max_severity);
98
- if (sevDelta !== 0)
99
- return sevDelta;
100
- const lenDelta = b.finding_ids.length - a.finding_ids.length;
101
- if (lenDelta !== 0)
102
- return lenDelta;
103
- return (a.finding_ids[0] ?? "").localeCompare(b.finding_ids[0] ?? "");
147
+ const severityDelta = severityRank(b.max_severity) - severityRank(a.max_severity);
148
+ if (severityDelta !== 0)
149
+ return severityDelta;
150
+ const findingDelta = b.finding_ids.length - a.finding_ids.length;
151
+ if (findingDelta !== 0)
152
+ return findingDelta;
153
+ return a.id.localeCompare(b.id);
104
154
  });
105
- for (let i = 0; i < blocks.length; i++) {
106
- blocks[i].id = `block-${i + 1}`;
155
+ for (let index = 0; index < blocks.length; index++) {
156
+ blocks[index].id = `block-${index + 1}`;
107
157
  }
108
- return blocks;
158
+ return computeDependencies({
159
+ blocks,
160
+ graphBundle: params.graphBundle,
161
+ criticalFlows: params.criticalFlows,
162
+ });
109
163
  }
@@ -4,8 +4,10 @@ export interface CriticalFlow {
4
4
  entrypoints: string[];
5
5
  paths: string[];
6
6
  concerns: string[];
7
+ confidence?: "high" | "low";
7
8
  notes?: string[];
8
9
  }
9
10
  export interface CriticalFlowManifest {
10
11
  flows: CriticalFlow[];
12
+ fallback_required?: boolean;
11
13
  }
@@ -5,6 +5,7 @@ export interface RuntimeValidationTask {
5
5
  target_paths: string[];
6
6
  reason: string;
7
7
  priority: "high" | "medium" | "low";
8
+ command?: string[];
8
9
  suggested_checks?: string[];
9
10
  source_artifacts?: string[];
10
11
  }
@@ -13,7 +14,7 @@ export interface RuntimeValidationTaskManifest {
13
14
  }
14
15
  export interface RuntimeValidationResult {
15
16
  task_id: string;
16
- status: "pending" | "confirmed" | "not_confirmed" | "inconclusive";
17
+ status: "pending" | "confirmed" | "not_confirmed" | "inconclusive" | "not_required";
17
18
  summary: string;
18
19
  evidence?: string[];
19
20
  notes?: string[];
package/dist/types.d.ts CHANGED
@@ -28,10 +28,9 @@ export interface AuditUnit {
28
28
  export interface UnitManifest {
29
29
  units: AuditUnit[];
30
30
  }
31
- export interface ReviewedLineRange {
31
+ export interface FileCoverageRecord {
32
32
  path: string;
33
- start: number;
34
- end: number;
33
+ total_lines: number;
35
34
  pass_id: string;
36
35
  lens?: Lens;
37
36
  agent_role?: string;
@@ -94,11 +93,9 @@ export interface AuditResult {
94
93
  pass_id: string;
95
94
  lens: Lens;
96
95
  agent_role?: string;
97
- reviewed_ranges: Array<{
96
+ file_coverage: Array<{
98
97
  path: string;
99
- start: number;
100
- end: number;
101
- line_count: number;
98
+ total_lines: number;
102
99
  }>;
103
100
  findings: Finding[];
104
101
  notes?: string[];