auditor-lambda 0.7.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/README.md +0 -21
- 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/auditStep.js +7 -1
- 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.d.ts +0 -1
- package/dist/cli.js +14 -565
- 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 +151 -75
- package/dist/extractors/pathPatterns.js +17 -5
- package/dist/io/artifacts.d.ts +3 -1
- package/dist/io/artifacts.js +18 -2
- 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 +38 -22
- package/dist/orchestrator/artifactFreshness.js +14 -4
- package/dist/orchestrator/autoFixExecutor.d.ts +2 -2
- package/dist/orchestrator/autoFixExecutor.js +26 -8
- package/dist/orchestrator/dependencyMap.d.ts +1 -1
- package/dist/orchestrator/dependencyMap.js +7 -1
- package/dist/orchestrator/executorResult.d.ts +12 -0
- package/dist/orchestrator/executorResult.js +1 -0
- package/dist/orchestrator/fileAnchors.js +14 -3
- package/dist/orchestrator/fileIntegrity.d.ts +1 -0
- package/dist/orchestrator/fileIntegrity.js +12 -3
- package/dist/orchestrator/flowCoverage.js +1 -0
- package/dist/orchestrator/flowRequeue.js +4 -1
- package/dist/orchestrator/graphEnrichmentExecutor.d.ts +1 -1
- package/dist/orchestrator/graphEnrichmentExecutor.js +3 -1
- package/dist/orchestrator/ingestionExecutors.d.ts +11 -0
- 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/reviewPacketGraph.d.ts +31 -0
- package/dist/orchestrator/reviewPacketGraph.js +691 -0
- package/dist/orchestrator/reviewPackets.d.ts +2 -15
- package/dist/orchestrator/reviewPackets.js +3 -685
- package/dist/orchestrator/runtimeCommand.d.ts +11 -0
- package/dist/orchestrator/runtimeCommand.js +71 -0
- package/dist/orchestrator/scope.js +1 -1
- 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/syntaxResolutionExecutor.d.ts +1 -1
- package/dist/orchestrator/synthesisExecutors.d.ts +12 -0
- package/dist/orchestrator/synthesisExecutors.js +90 -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/docs/development.md +35 -139
- package/docs/history.md +26 -0
- package/docs/product.md +41 -108
- package/package.json +3 -2
- package/schemas/audit_findings.schema.json +6 -5
- package/schemas/critical_flows.schema.json +3 -2
- package/schemas/dispatch_quota.schema.json +3 -1
- package/schemas/external_analyzer_results.schema.json +2 -2
- package/schemas/graph_bundle.schema.json +1 -1
- package/schemas/repo_manifest.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.d.ts +0 -34
- package/dist/orchestrator/internalExecutors.js +0 -581
- 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
- package/docs/handoff.md +0 -204
|
@@ -54,14 +54,17 @@ function computeDependencies(params) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
for (const flow of params.criticalFlows?.flows ?? []) {
|
|
57
|
-
|
|
57
|
+
// Order blocks by first appearance along the flow path so dependency
|
|
58
|
+
// direction follows the flow's traversal order, not block-id lexical order.
|
|
59
|
+
const ordered = [];
|
|
60
|
+
const seen = new Set();
|
|
58
61
|
for (const path of flow.paths) {
|
|
59
62
|
const blockId = blockByFile.get(path);
|
|
60
|
-
if (blockId) {
|
|
61
|
-
|
|
63
|
+
if (blockId && !seen.has(blockId)) {
|
|
64
|
+
seen.add(blockId);
|
|
65
|
+
ordered.push(blockId);
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
|
-
const ordered = [...flowBlocks].sort();
|
|
65
68
|
for (let i = 1; i < ordered.length; i++) {
|
|
66
69
|
dependsOn.get(ordered[i - 1])?.add(ordered[i]);
|
|
67
70
|
}
|
|
@@ -36,6 +36,28 @@ export interface ReviewPacket {
|
|
|
36
36
|
rationale: string;
|
|
37
37
|
estimated_tokens: number;
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Aggregate quality signals for a planned set of review packets — cohesion,
|
|
41
|
+
* boundary-crossing counts, and the weakly-explained-packet diagnostics. Promoted
|
|
42
|
+
* from an inline anonymous object on {@link AuditPlanMetrics} so it can be named
|
|
43
|
+
* and referenced.
|
|
44
|
+
*/
|
|
45
|
+
export interface PacketQuality {
|
|
46
|
+
average_cohesion_score: number;
|
|
47
|
+
boundary_crossing_count: number;
|
|
48
|
+
merge_edge_kind_counts: Record<string, number>;
|
|
49
|
+
boundary_edge_kind_counts: Record<string, number>;
|
|
50
|
+
orphan_task_count: number;
|
|
51
|
+
high_fan_in_file_count: number;
|
|
52
|
+
high_fan_out_file_count: number;
|
|
53
|
+
weakly_explained_gap_counts: Record<WeaklyExplainedPacketSample["primary_gap"], number>;
|
|
54
|
+
weakly_explained_file_extension_counts: Record<string, number>;
|
|
55
|
+
weakly_explained_packet_count: number;
|
|
56
|
+
weakly_explained_packet_ids: string[];
|
|
57
|
+
weakly_explained_packet_samples: WeaklyExplainedPacketSample[];
|
|
58
|
+
largest_unexplained_packet_id?: string;
|
|
59
|
+
largest_unexplained_packet_files: number;
|
|
60
|
+
}
|
|
39
61
|
export interface AuditPlanMetrics {
|
|
40
62
|
generated_at: string;
|
|
41
63
|
task_count: number;
|
|
@@ -55,22 +77,7 @@ export interface AuditPlanMetrics {
|
|
|
55
77
|
largest_packet_id?: string;
|
|
56
78
|
lens_task_counts: Partial<Record<Lens, number>>;
|
|
57
79
|
priority_task_counts: Record<NonNullable<AuditTask["priority"]>, number>;
|
|
58
|
-
packet_quality:
|
|
59
|
-
average_cohesion_score: number;
|
|
60
|
-
boundary_crossing_count: number;
|
|
61
|
-
merge_edge_kind_counts: Record<string, number>;
|
|
62
|
-
boundary_edge_kind_counts: Record<string, number>;
|
|
63
|
-
orphan_task_count: number;
|
|
64
|
-
high_fan_in_file_count: number;
|
|
65
|
-
high_fan_out_file_count: number;
|
|
66
|
-
weakly_explained_gap_counts: Record<WeaklyExplainedPacketSample["primary_gap"], number>;
|
|
67
|
-
weakly_explained_file_extension_counts: Record<string, number>;
|
|
68
|
-
weakly_explained_packet_count: number;
|
|
69
|
-
weakly_explained_packet_ids: string[];
|
|
70
|
-
weakly_explained_packet_samples: WeaklyExplainedPacketSample[];
|
|
71
|
-
largest_unexplained_packet_id?: string;
|
|
72
|
-
largest_unexplained_packet_files: number;
|
|
73
|
-
};
|
|
80
|
+
packet_quality: PacketQuality;
|
|
74
81
|
packet_size: {
|
|
75
82
|
single_task_packets: number;
|
|
76
83
|
multi_task_packets: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describeValue, formatValidationIssues, isRecord, } from "@audit-tools/shared";
|
|
1
|
+
import { describeValue, formatValidationIssues, isRecord, VALID_LENSES, VALID_SEVERITIES, VALID_CONFIDENCES, } from "@audit-tools/shared";
|
|
2
2
|
export function normalizeCoveragePath(path) {
|
|
3
3
|
return path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
4
4
|
}
|
|
@@ -11,23 +11,11 @@ const REQUIRED_FINDING_FIELDS = [
|
|
|
11
11
|
"lens",
|
|
12
12
|
"summary",
|
|
13
13
|
];
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
// Severity / confidence / lens validity now come from the canonical shared
|
|
15
|
+
// vocabulary (`@audit-tools/shared`); previously each was re-defined here and
|
|
16
|
+
// drifted from the shared Lens / FindingSeverity / FindingConfidence types.
|
|
16
17
|
const VALID_PRIORITIES = new Set(["high", "medium", "low"]);
|
|
17
18
|
const LENS_VERIFICATION_TAG = "lens_verification";
|
|
18
|
-
const VALID_LENSES = new Set([
|
|
19
|
-
"correctness",
|
|
20
|
-
"architecture",
|
|
21
|
-
"maintainability",
|
|
22
|
-
"security",
|
|
23
|
-
"reliability",
|
|
24
|
-
"performance",
|
|
25
|
-
"data_integrity",
|
|
26
|
-
"tests",
|
|
27
|
-
"operability",
|
|
28
|
-
"config_deployment",
|
|
29
|
-
"observability",
|
|
30
|
-
]);
|
|
31
19
|
function pushIssue(issues, params) {
|
|
32
20
|
issues.push({
|
|
33
21
|
...params,
|
|
@@ -77,20 +65,12 @@ function validateExpectedStringField(value, label, expected, taskId, resultIndex
|
|
|
77
65
|
});
|
|
78
66
|
}
|
|
79
67
|
}
|
|
80
|
-
function
|
|
81
|
-
const issues = [];
|
|
82
|
-
if (!isRecord(finding)) {
|
|
83
|
-
pushIssue(issues, {
|
|
84
|
-
result_index: resultIndex,
|
|
85
|
-
task_id: taskId,
|
|
86
|
-
field: label,
|
|
87
|
-
message: `${label} must be an object, got ${describeValue(finding)}.`,
|
|
88
|
-
});
|
|
89
|
-
return issues;
|
|
90
|
-
}
|
|
68
|
+
function validateFindingRequiredFields(finding, label, taskId, resultIndex, issues) {
|
|
91
69
|
for (const field of REQUIRED_FINDING_FIELDS) {
|
|
92
70
|
validateRequiredStringField(finding[field], `${label}.${field}`, taskId, resultIndex, issues);
|
|
93
71
|
}
|
|
72
|
+
}
|
|
73
|
+
function validateFindingEnums(finding, label, taskId, resultIndex, issues) {
|
|
94
74
|
if (typeof finding.severity === "string" &&
|
|
95
75
|
!VALID_SEVERITIES.has(finding.severity)) {
|
|
96
76
|
pushIssue(issues, {
|
|
@@ -117,6 +97,8 @@ function validateFinding(finding, label, taskId, resultIndex) {
|
|
|
117
97
|
message: `Invalid lens '${finding.lens}'. Must be one of: ${[...VALID_LENSES].join(", ")}.`,
|
|
118
98
|
});
|
|
119
99
|
}
|
|
100
|
+
}
|
|
101
|
+
function validateAffectedFiles(finding, label, taskId, resultIndex, issues) {
|
|
120
102
|
const affectedFiles = finding.affected_files;
|
|
121
103
|
if (!Array.isArray(affectedFiles) || affectedFiles.length === 0) {
|
|
122
104
|
pushIssue(issues, {
|
|
@@ -125,57 +107,58 @@ function validateFinding(finding, label, taskId, resultIndex) {
|
|
|
125
107
|
field: `${label}.affected_files`,
|
|
126
108
|
message: "affected_files must be a non-empty array.",
|
|
127
109
|
});
|
|
110
|
+
return;
|
|
128
111
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
});
|
|
176
|
-
}
|
|
112
|
+
for (let k = 0; k < affectedFiles.length; k++) {
|
|
113
|
+
const item = affectedFiles[k];
|
|
114
|
+
if (!isRecord(item)) {
|
|
115
|
+
pushIssue(issues, {
|
|
116
|
+
result_index: resultIndex,
|
|
117
|
+
task_id: taskId,
|
|
118
|
+
field: `${label}.affected_files[${k}]`,
|
|
119
|
+
message: `affected_files[${k}] must be an object, got ${describeValue(item)}.`,
|
|
120
|
+
});
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (!isNonEmptyString(item.path)) {
|
|
124
|
+
pushIssue(issues, {
|
|
125
|
+
result_index: resultIndex,
|
|
126
|
+
task_id: taskId,
|
|
127
|
+
field: `${label}.affected_files[${k}].path`,
|
|
128
|
+
message: "affected_files entry has an empty path.",
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (item.line_start !== undefined &&
|
|
132
|
+
!Number.isInteger(item.line_start)) {
|
|
133
|
+
pushIssue(issues, {
|
|
134
|
+
result_index: resultIndex,
|
|
135
|
+
task_id: taskId,
|
|
136
|
+
field: `${label}.affected_files[${k}].line_start`,
|
|
137
|
+
message: `affected_files[${k}].line_start must be an integer, got ${describeValue(item.line_start)}.`,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (item.line_end !== undefined &&
|
|
141
|
+
!Number.isInteger(item.line_end)) {
|
|
142
|
+
pushIssue(issues, {
|
|
143
|
+
result_index: resultIndex,
|
|
144
|
+
task_id: taskId,
|
|
145
|
+
field: `${label}.affected_files[${k}].line_end`,
|
|
146
|
+
message: `affected_files[${k}].line_end must be an integer, got ${describeValue(item.line_end)}.`,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (Number.isInteger(item.line_start) &&
|
|
150
|
+
Number.isInteger(item.line_end) &&
|
|
151
|
+
Number(item.line_start) > Number(item.line_end)) {
|
|
152
|
+
pushIssue(issues, {
|
|
153
|
+
result_index: resultIndex,
|
|
154
|
+
task_id: taskId,
|
|
155
|
+
field: `${label}.affected_files[${k}]`,
|
|
156
|
+
message: "affected_files line_start must be less than or equal to line_end.",
|
|
157
|
+
});
|
|
177
158
|
}
|
|
178
159
|
}
|
|
160
|
+
}
|
|
161
|
+
function validateEvidence(finding, label, taskId, resultIndex, issues) {
|
|
179
162
|
const evidence = finding.evidence;
|
|
180
163
|
if (!Array.isArray(evidence) || evidence.length === 0) {
|
|
181
164
|
pushIssue(issues, {
|
|
@@ -184,33 +167,52 @@ function validateFinding(finding, label, taskId, resultIndex) {
|
|
|
184
167
|
field: `${label}.evidence`,
|
|
185
168
|
message: "evidence is empty — provide an array of plain strings such as \"src/foo.ts:42 - variable overwritten before use\".",
|
|
186
169
|
});
|
|
170
|
+
return;
|
|
187
171
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (typeof entry !== "string") {
|
|
193
|
-
pushIssue(issues, {
|
|
194
|
-
result_index: resultIndex,
|
|
195
|
-
task_id: taskId,
|
|
196
|
-
field: `${label}.evidence[${k}]`,
|
|
197
|
-
message: `evidence[${k}] must be a string, got ${describeValue(entry)}.`,
|
|
198
|
-
});
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
if (entry.trim().length > 0) {
|
|
202
|
-
hasSubstantiveEntry = true;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
if (!hasSubstantiveEntry) {
|
|
172
|
+
let hasSubstantiveEntry = false;
|
|
173
|
+
for (let k = 0; k < evidence.length; k++) {
|
|
174
|
+
const entry = evidence[k];
|
|
175
|
+
if (typeof entry !== "string") {
|
|
206
176
|
pushIssue(issues, {
|
|
207
177
|
result_index: resultIndex,
|
|
208
178
|
task_id: taskId,
|
|
209
|
-
field: `${label}.evidence`,
|
|
210
|
-
message:
|
|
179
|
+
field: `${label}.evidence[${k}]`,
|
|
180
|
+
message: `evidence[${k}] must be a string, got ${describeValue(entry)}.`,
|
|
211
181
|
});
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (entry.trim().length > 0) {
|
|
185
|
+
hasSubstantiveEntry = true;
|
|
212
186
|
}
|
|
213
187
|
}
|
|
188
|
+
if (!hasSubstantiveEntry) {
|
|
189
|
+
pushIssue(issues, {
|
|
190
|
+
result_index: resultIndex,
|
|
191
|
+
task_id: taskId,
|
|
192
|
+
field: `${label}.evidence`,
|
|
193
|
+
message: "All evidence entries are empty strings.",
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Thin orchestrator: validates one finding by delegating to the per-concern
|
|
198
|
+
// validators (object-shape, required strings, enums, affected_files, evidence),
|
|
199
|
+
// each of which pushes into the shared issues array. Behavior and messages are
|
|
200
|
+
// identical to the former single 165-line function.
|
|
201
|
+
function validateFinding(finding, label, taskId, resultIndex) {
|
|
202
|
+
const issues = [];
|
|
203
|
+
if (!isRecord(finding)) {
|
|
204
|
+
pushIssue(issues, {
|
|
205
|
+
result_index: resultIndex,
|
|
206
|
+
task_id: taskId,
|
|
207
|
+
field: label,
|
|
208
|
+
message: `${label} must be an object, got ${describeValue(finding)}.`,
|
|
209
|
+
});
|
|
210
|
+
return issues;
|
|
211
|
+
}
|
|
212
|
+
validateFindingRequiredFields(finding, label, taskId, resultIndex, issues);
|
|
213
|
+
validateFindingEnums(finding, label, taskId, resultIndex, issues);
|
|
214
|
+
validateAffectedFiles(finding, label, taskId, resultIndex, issues);
|
|
215
|
+
validateEvidence(finding, label, taskId, resultIndex, issues);
|
|
214
216
|
return issues;
|
|
215
217
|
}
|
|
216
218
|
function validateOptionalStringArray(value, label, taskId, resultIndex, issues) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type SessionConfig, type ValidationIssue } from "@audit-tools/shared";
|
|
2
2
|
export declare function validateSessionConfig(value: unknown): ValidationIssue[];
|
|
3
3
|
export declare function validateConfiguredProviderEnvironment(sessionConfig: SessionConfig, options?: {
|
|
4
|
-
commandExists?: (command: string) => boolean
|
|
4
|
+
commandExists?: (command: string) => Promise<boolean>;
|
|
5
5
|
pathExists?: (commandPath: string) => boolean;
|
|
6
|
-
}): ValidationIssue[]
|
|
6
|
+
}): Promise<ValidationIssue[]>;
|
|
7
7
|
export { formatValidationIssues } from "@audit-tools/shared";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
2
|
import { accessSync, constants } from "node:fs";
|
|
3
|
+
import { promisify } from "node:util";
|
|
3
4
|
import { ANALYZER_SETTINGS, PROVIDER_NAMES, SESSION_UI_MODES, isRecord, pushValidationIssue, } from "@audit-tools/shared";
|
|
5
|
+
const execAsync = promisify(exec);
|
|
4
6
|
const VALID_PROVIDERS = new Set(PROVIDER_NAMES);
|
|
5
7
|
const VALID_UI_MODES = new Set(SESSION_UI_MODES);
|
|
6
8
|
const VALID_ANALYZER_SETTINGS = new Set(ANALYZER_SETTINGS);
|
|
@@ -80,10 +82,15 @@ function validateAgentProviderSection(value, path, issues) {
|
|
|
80
82
|
pushIssue(issues, `${path}.dangerously_skip_permissions`, "dangerously_skip_permissions must be a boolean when provided.");
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
|
-
function commandExists(command) {
|
|
85
|
+
async function commandExists(command) {
|
|
84
86
|
const lookupCommand = process.platform === "win32" ? "where" : "which";
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
try {
|
|
88
|
+
await execAsync(`${lookupCommand} ${command}`);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
87
94
|
}
|
|
88
95
|
function configuredPathExists(commandPath) {
|
|
89
96
|
try {
|
|
@@ -183,14 +190,14 @@ export function validateSessionConfig(value) {
|
|
|
183
190
|
}
|
|
184
191
|
return issues;
|
|
185
192
|
}
|
|
186
|
-
export function validateConfiguredProviderEnvironment(sessionConfig, options = {}) {
|
|
193
|
+
export async function validateConfiguredProviderEnvironment(sessionConfig, options = {}) {
|
|
187
194
|
const issues = [];
|
|
188
195
|
const lookupCommand = options.commandExists ?? commandExists;
|
|
189
196
|
const lookupPath = options.pathExists ?? configuredPathExists;
|
|
190
197
|
const provider = sessionConfig.provider ?? "local-subprocess";
|
|
191
198
|
if (provider === "claude-code") {
|
|
192
199
|
const command = sessionConfig.claude_code?.command ?? "claude";
|
|
193
|
-
if (isBareExecutableName(command) && !lookupCommand(command)) {
|
|
200
|
+
if (isBareExecutableName(command) && !(await lookupCommand(command))) {
|
|
194
201
|
pushIssue(issues, "claude_code.command", `Configured claude-code executable was not found on PATH: ${command}.`);
|
|
195
202
|
}
|
|
196
203
|
else if (isDirectExecutablePath(command) && !lookupPath(command)) {
|
|
@@ -202,7 +209,7 @@ export function validateConfiguredProviderEnvironment(sessionConfig, options = {
|
|
|
202
209
|
}
|
|
203
210
|
if (provider === "opencode") {
|
|
204
211
|
const command = sessionConfig.opencode?.command ?? "opencode";
|
|
205
|
-
if (isBareExecutableName(command) && !lookupCommand(command)) {
|
|
212
|
+
if (isBareExecutableName(command) && !(await lookupCommand(command))) {
|
|
206
213
|
pushIssue(issues, "opencode.command", `Configured opencode executable was not found on PATH: ${command}.`);
|
|
207
214
|
}
|
|
208
215
|
else if (isDirectExecutablePath(command) && !lookupPath(command)) {
|
package/docs/development.md
CHANGED
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
|
|
13
13
|
## Agent handoff
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
Keep long-term product direction in `docs/product.md` and archival context
|
|
16
|
+
(shipped sprints, field-trial lessons) in `docs/history.md`. There is no
|
|
17
|
+
standing per-sprint handoff file; sprint notes are folded into `docs/history.md`
|
|
18
|
+
once the work ships.
|
|
19
19
|
|
|
20
20
|
## Build and test
|
|
21
21
|
|
|
@@ -30,79 +30,6 @@ The test suite is intentionally contract-heavy. Update tests when changing
|
|
|
30
30
|
schema shape, prompt contracts, dispatch behavior, installer output, or release
|
|
31
31
|
workflow semantics.
|
|
32
32
|
|
|
33
|
-
## Production-readiness workflow
|
|
34
|
-
|
|
35
|
-
Use field trials to decide what to fix next. For each representative repository,
|
|
36
|
-
run to the local review handoff, validate the artifact bundle, and compare
|
|
37
|
-
`audit_plan_metrics.json` across runs. Track at least packet count, weak packet
|
|
38
|
-
count, average cohesion, `merge_edge_kind_counts`,
|
|
39
|
-
`boundary_edge_kind_counts`, and `weakly_explained_packet_samples`.
|
|
40
|
-
|
|
41
|
-
Only promote an extractor or planner change when those metrics expose a
|
|
42
|
-
deterministic gap. Prefer improving shared graph resolution or importing
|
|
43
|
-
generic analyzer ownership roots before adding another ecosystem-specific
|
|
44
|
-
manifest parser.
|
|
45
|
-
|
|
46
|
-
The latest remediator field trial closed the remaining mixed code/schema/test
|
|
47
|
-
weak packet by adding package script links, schema contract test links, bounded
|
|
48
|
-
TypeScript type contract suites, package-script-seeded script suite links, and
|
|
49
|
-
generated test artifact disposition. Keep future suite links similarly bounded
|
|
50
|
-
and evidence-led.
|
|
51
|
-
|
|
52
|
-
The Polar field trial added `conftest-link` (conftest.py → Python files in
|
|
53
|
-
scope) and `pyproject-testpaths-link` (pyproject.toml → conftest.py via
|
|
54
|
-
`[tool.pytest.ini_options] testpaths`). `conftest-link` fires only when the
|
|
55
|
-
conftest is inside a `isTestPath` directory to avoid O(n) fan-out from
|
|
56
|
-
root-level conftests. `pyproject.toml` was also added to `shouldReadForGraph`
|
|
57
|
-
so its content is available during the filesystem-backed build path. Together
|
|
58
|
-
these raised Polar's average cohesion from 0.625 to 0.857 and reduced weak
|
|
59
|
-
packets from 5 to 3.
|
|
60
|
-
|
|
61
|
-
A second Polar field trial added `yaml-path-reference-link` (YAML/YML files
|
|
62
|
-
→ other config files referenced by explicit relative path). Resolution tries
|
|
63
|
-
repo-root-relative first, then file-directory-relative. The extractor only
|
|
64
|
-
fires for string values ending in `.yaml`, `.yml`, `.json`, or `.toml` that
|
|
65
|
-
resolve to an existing repo file. In Polar, this produced 4 edges from
|
|
66
|
-
`configs/benchmark.yaml` to its template files and raised `internal_edge_count`
|
|
67
|
-
in the `experiments-domains` packet from 90 to 94.
|
|
68
|
-
|
|
69
|
-
A third Polar field trial added `python-test-util-suite-link`, which chains
|
|
70
|
-
`.py` files co-located in `utils/`, `helpers/`, or `support/` subdirectories
|
|
71
|
-
within `isTestPath` directories (same bounded-suite pattern as the TypeScript
|
|
72
|
-
type, JSON schema, and package-script suite links). `conftest.py` is excluded
|
|
73
|
-
from the predicate. In Polar, this produced 2 intra-unit edges within the
|
|
74
|
-
`tests-utils` packet, raising its `internal_edge_count` from 0 to 2 and
|
|
75
|
-
eliminating it as a weak packet. Polar metrics improved from 0.857 to 1.000
|
|
76
|
-
cohesion and 3 to 2 weak packets. The 2 remaining weak packets share genuinely
|
|
77
|
-
isolated files (`.auditorignore`, `experiments/domains/__init__.py`,
|
|
78
|
-
`experiments/summarize_results.py`) that cannot be linked without false
|
|
79
|
-
positives; treat as the current floor. Note that intra-unit suite edges do not
|
|
80
|
-
appear in `merge_edge_kind_counts` — their effect is visible in the packet's
|
|
81
|
-
`internal_edge_count` and `unexplained_file_count` fields instead.
|
|
82
|
-
|
|
83
|
-
Before treating a build as production-ready, verify the complete review loop in
|
|
84
|
-
one real host:
|
|
85
|
-
|
|
86
|
-
```text
|
|
87
|
-
audit-code prepare-dispatch --run-id <run_id> --artifacts-dir <artifacts_dir>
|
|
88
|
-
worker reviews each packet prompt
|
|
89
|
-
audit-code submit-packet ...
|
|
90
|
-
audit-code merge-and-ingest --run-id <run_id> --artifacts-dir <artifacts_dir>
|
|
91
|
-
audit-code validate
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
On Windows, runtime validation runs package-manager shim commands such as
|
|
95
|
-
`npm`, `npx`, `pnpm`, and `yarn` through the command shell so `.cmd` wrappers
|
|
96
|
-
execute reliably. Keep that behavior covered when changing runtime command
|
|
97
|
-
execution.
|
|
98
|
-
|
|
99
|
-
If the final `audit-report.md` cannot be copied into the target repository
|
|
100
|
-
because of local permissions, completion should remain successful and the
|
|
101
|
-
artifact copy remains authoritative. Run `audit-code validate` against the
|
|
102
|
-
artifact bundle before treating the run as complete.
|
|
103
|
-
|
|
104
|
-
Then run `npm run verify:release` from a clean checkout.
|
|
105
|
-
|
|
106
33
|
## Architecture
|
|
107
34
|
|
|
108
35
|
The system separates deterministic extraction from bounded LLM judgment:
|
|
@@ -122,6 +49,8 @@ Portability rules:
|
|
|
122
49
|
- review work is attributable to files, lenses, passes, and tasks
|
|
123
50
|
- coverage gaps are machine-detectable
|
|
124
51
|
|
|
52
|
+
`AuditTask` is the coverage identity; `AuditResult[]` is the ingestion contract.
|
|
53
|
+
|
|
125
54
|
## Adding language analyzers
|
|
126
55
|
|
|
127
56
|
Language support should be adapter-based. A new analyzer should enrich shared
|
|
@@ -137,74 +66,41 @@ Preferred outputs:
|
|
|
137
66
|
- graph edges with kind, direction, confidence, and reason
|
|
138
67
|
- entrypoints and surfaces
|
|
139
68
|
- test-to-source links
|
|
140
|
-
- package/module ownership hints, including analyzer-supplied
|
|
141
|
-
|
|
142
|
-
- contract-suite links for small JSON Schema, workflow, package
|
|
69
|
+
- package/module ownership hints, including analyzer-supplied `ownership_roots`
|
|
70
|
+
that become `analyzer-ownership-root-link` graph references
|
|
71
|
+
- contract-suite links for small JSON Schema, workflow, package-script, or
|
|
143
72
|
TypeScript type suites when planner metrics show otherwise weak packets
|
|
144
73
|
- external boundary hints
|
|
145
74
|
- line counts and anchor summaries for large files
|
|
146
75
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
- planner observability before additional ecosystem breadth
|
|
150
|
-
- exercising the generic ownership-root input from analyzers or imported
|
|
151
|
-
evidence
|
|
152
|
-
- continued behavior-preserving extraction of high-concentration graph helpers
|
|
153
|
-
- JS/TS compiler-backed resolution only after the current regex edges stay
|
|
154
|
-
stable
|
|
155
|
-
- Python deterministic support beyond the current local import, package/module,
|
|
156
|
-
and pytest/unittest adjacency edges only where planner metrics show gaps
|
|
157
|
-
- generic fallback from path patterns, ctags/tree-sitter, LSP output, or
|
|
158
|
-
external analyzer results when available
|
|
159
|
-
|
|
160
|
-
Keep deep analyzers optional. Repositories should still produce useful packets
|
|
76
|
+
Keep deep analyzers optional: a repository should still produce useful packets
|
|
161
77
|
from manifests, paths, tests, and external analyzer results when a language has
|
|
162
|
-
only fallback support.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
repo-local config checks, such as `eslint.config.*`, `.eslintrc*`, or
|
|
166
|
-
`package.json` `eslintConfig`, over executing a globally installed tool and
|
|
78
|
+
only fallback support. Command-backed analyzers should prove project intent
|
|
79
|
+
before running — prefer repo-local config checks (`eslint.config.*`, `.eslintrc*`,
|
|
80
|
+
`package.json` `eslintConfig`) over executing a globally installed tool and
|
|
167
81
|
parsing its no-config failure.
|
|
168
82
|
|
|
169
|
-
Language-agnostic semantic affinity
|
|
170
|
-
|
|
171
|
-
|
|
83
|
+
Language-agnostic semantic affinity is useful for ranking adjacent context but
|
|
84
|
+
should stay low-authority: don't let shared token frequency alone force packet
|
|
85
|
+
merges; use it for `boundary_files` or candidate explanations unless a
|
|
172
86
|
deterministic edge corroborates the relationship.
|
|
173
87
|
|
|
174
|
-
##
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
fixture and real repositories before adding deeper Python framework handling
|
|
194
|
-
|
|
195
|
-
Keep `AuditTask` as the coverage identity and `AuditResult[]` as the ingestion
|
|
196
|
-
contract.
|
|
197
|
-
|
|
198
|
-
## File-splitting priorities
|
|
199
|
-
|
|
200
|
-
The largest implementation files should be split conservatively and
|
|
201
|
-
behavior-preservingly:
|
|
202
|
-
|
|
203
|
-
- move CLI command families out of `src/cli.ts`
|
|
204
|
-
- move language metadata tables out of file inventory logic
|
|
205
|
-
- move graph manifest/project-file parsers out of `src/extractors/graph.ts`
|
|
206
|
-
- split selective-deepening task builders by trigger type
|
|
207
|
-
- keep packetization, recovery, and schema changes easier to review
|
|
208
|
-
|
|
209
|
-
Run the focused tests for each area before and after a split, then run
|
|
210
|
-
`npm test`.
|
|
88
|
+
## Production readiness
|
|
89
|
+
|
|
90
|
+
Drive priorities from field trials, not speculation: run representative
|
|
91
|
+
repositories through planning, validate the bundle (`audit-code validate`), and
|
|
92
|
+
compare `audit_plan_metrics.json` (packet count, weak-packet count, cohesion,
|
|
93
|
+
merge/boundary edge kinds) across runs. Promote an extractor or planner change
|
|
94
|
+
when those metrics expose a deterministic gap — and prefer improving shared
|
|
95
|
+
graph resolution or generic analyzer ownership roots before adding another
|
|
96
|
+
ecosystem-specific parser.
|
|
97
|
+
|
|
98
|
+
Before treating a build as production-ready, verify the full review loop in one
|
|
99
|
+
real host (`prepare-dispatch` → worker reviews each packet → `submit-packet` →
|
|
100
|
+
`merge-and-ingest` → `validate`), then run `npm run verify:release` from a clean
|
|
101
|
+
checkout. On Windows, runtime validation runs package-manager shims (`npm`,
|
|
102
|
+
`npx`, `pnpm`, `yarn`) through the command shell so `.cmd` wrappers execute
|
|
103
|
+
reliably — keep that covered when changing runtime command execution. If the
|
|
104
|
+
final `audit-report.md` cannot be copied into the target repo due to local
|
|
105
|
+
permissions, completion still succeeds and the artifact copy is authoritative.
|
|
106
|
+
</content>
|
package/docs/history.md
CHANGED
|
@@ -38,3 +38,29 @@ The old remediation baseline recorded fixes across:
|
|
|
38
38
|
|
|
39
39
|
Current readiness is tracked in `docs/product.md`, `docs/operator-guide.md`,
|
|
40
40
|
`docs/contracts.md`, `docs/release.md`, and `docs/development.md`.
|
|
41
|
+
|
|
42
|
+
## Monorepo migration & drift reconciliation (2026-05 → 2026-06)
|
|
43
|
+
|
|
44
|
+
The auditor and remediator began as standalone repos (`auditor-lambda`,
|
|
45
|
+
`remediator-lambda`) and were merged into this npm-workspaces monorepo on a
|
|
46
|
+
shared `@audit-tools/shared` foundation. `providers/` and `quota/` had been
|
|
47
|
+
copy-pasted into both tools and forked in place; the ten resulting drift bugs
|
|
48
|
+
were all fixed by centralizing the forked logic into `shared` (one source of
|
|
49
|
+
truth). Durable decisions from that work:
|
|
50
|
+
|
|
51
|
+
- **Access scoping is JSON, not MCP.** `AccessDeclaration` rides on the step
|
|
52
|
+
contract, so it works with any host; the MCP servers stay compatibility
|
|
53
|
+
adapters over the same contract.
|
|
54
|
+
- **`--dangerously-skip-permissions` defaults ON for the remediator, OFF for the
|
|
55
|
+
auditor.** The remediator applies changes unattended and cannot pause; the
|
|
56
|
+
auditor is read-only. The asymmetry is intentional and the flag is overrideable.
|
|
57
|
+
- **The remediator's machine input is `audit-findings.json`, not the Markdown
|
|
58
|
+
report.** `audit-report.md` is human-facing; a Markdown file handed to the
|
|
59
|
+
remediator flows through the free-form LLM extractor, not a deterministic parse.
|
|
60
|
+
- **Prompts use one strict path** — no "or / unless / if-available" fallbacks.
|
|
61
|
+
|
|
62
|
+
Large files were then broken up as behaviour-preserving pure moves (`cli.ts` from
|
|
63
|
+
4072 lines to a thin dispatcher plus `src/cli/*` handlers; `graph.ts`,
|
|
64
|
+
`reviewPackets.ts`, `internalExecutors.ts`, and the generated language table all
|
|
65
|
+
split out). The sprint-by-sprint handoff docs that tracked this work were removed
|
|
66
|
+
once shipped; this section is their durable residue.
|