auditor-lambda 0.2.5 → 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 (71) hide show
  1. package/README.md +35 -7
  2. package/audit-code-wrapper-lib.mjs +1612 -331
  3. package/dist/cli.js +397 -38
  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 +3 -1
  16. package/dist/io/runArtifacts.js +20 -5
  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.d.ts +2 -2
  23. package/dist/orchestrator/flowRequeue.js +16 -3
  24. package/dist/orchestrator/internalExecutors.d.ts +2 -1
  25. package/dist/orchestrator/internalExecutors.js +129 -48
  26. package/dist/orchestrator/requeue.js +10 -4
  27. package/dist/orchestrator/requeueCommand.js +15 -2
  28. package/dist/orchestrator/resultIngestion.d.ts +2 -1
  29. package/dist/orchestrator/resultIngestion.js +26 -6
  30. package/dist/orchestrator/runtimeValidation.d.ts +7 -2
  31. package/dist/orchestrator/runtimeValidation.js +61 -49
  32. package/dist/orchestrator/runtimeValidationUpdate.js +2 -4
  33. package/dist/orchestrator/state.js +28 -14
  34. package/dist/orchestrator/taskBuilder.js +4 -2
  35. package/dist/orchestrator/trivialAudit.d.ts +4 -0
  36. package/dist/orchestrator/trivialAudit.js +49 -0
  37. package/dist/prompts/renderWorkerPrompt.js +6 -2
  38. package/dist/providers/spawnLoggedCommand.js +17 -0
  39. package/dist/reporting/mergeFindings.js +3 -11
  40. package/dist/reporting/rootCause.js +92 -9
  41. package/dist/reporting/synthesis.d.ts +25 -22
  42. package/dist/reporting/synthesis.js +92 -59
  43. package/dist/reporting/workBlocks.d.ts +12 -3
  44. package/dist/reporting/workBlocks.js +124 -70
  45. package/dist/supervisor/sessionConfig.js +4 -2
  46. package/dist/types/flows.d.ts +2 -0
  47. package/dist/types/runtimeValidation.d.ts +2 -1
  48. package/dist/types.d.ts +8 -6
  49. package/dist/validation/auditResults.d.ts +5 -2
  50. package/dist/validation/auditResults.js +335 -43
  51. package/docs/agent-integrations.md +38 -29
  52. package/docs/artifacts.md +18 -51
  53. package/docs/bootstrap-install.md +60 -30
  54. package/docs/contract.md +25 -117
  55. package/docs/field-trial-bug-report.md +237 -0
  56. package/docs/next-steps.md +59 -44
  57. package/docs/packaging.md +13 -3
  58. package/docs/production-launch-bar.md +2 -2
  59. package/docs/production-readiness.md +9 -5
  60. package/docs/releasing.md +81 -0
  61. package/docs/session-config.md +20 -1
  62. package/docs/usage.md +22 -0
  63. package/package.json +4 -1
  64. package/schemas/audit_result.schema.json +4 -5
  65. package/schemas/audit_task.schema.json +10 -0
  66. package/schemas/runtime_validation_report.schema.json +1 -1
  67. package/skills/audit-code/SKILL.md +11 -2
  68. package/skills/audit-code/audit-code.prompt.md +11 -10
  69. package/schemas/merged_findings.schema.json +0 -19
  70. package/schemas/root_cause_clusters.schema.json +0 -28
  71. package/schemas/synthesis_report.schema.json +0 -61
@@ -9,77 +9,193 @@ const REQUIRED_FINDING_FIELDS = [
9
9
  ];
10
10
  const VALID_SEVERITIES = new Set(["critical", "high", "medium", "low", "info"]);
11
11
  const VALID_CONFIDENCES = new Set(["high", "medium", "low"]);
12
+ const VALID_LENSES = new Set([
13
+ "correctness",
14
+ "architecture",
15
+ "maintainability",
16
+ "security",
17
+ "reliability",
18
+ "performance",
19
+ "data_integrity",
20
+ "tests",
21
+ "operability",
22
+ "config_deployment",
23
+ ]);
24
+ function pushIssue(issues, params) {
25
+ issues.push({
26
+ ...params,
27
+ severity: params.severity ?? "error",
28
+ });
29
+ }
30
+ function describeValue(value) {
31
+ if (Array.isArray(value)) {
32
+ return "array";
33
+ }
34
+ if (value === null) {
35
+ return "null";
36
+ }
37
+ return typeof value;
38
+ }
39
+ function isRecord(value) {
40
+ return typeof value === "object" && value !== null && !Array.isArray(value);
41
+ }
42
+ function isNonEmptyString(value) {
43
+ return typeof value === "string" && value.trim().length > 0;
44
+ }
45
+ function issueTaskId(record, resultIndex) {
46
+ const taskId = record.task_id;
47
+ return typeof taskId === "string" && taskId.trim().length > 0
48
+ ? taskId
49
+ : `result[${resultIndex}]`;
50
+ }
51
+ function validateRequiredStringField(value, label, taskId, resultIndex, issues) {
52
+ if (typeof value !== "string") {
53
+ pushIssue(issues, {
54
+ result_index: resultIndex,
55
+ task_id: taskId,
56
+ field: label,
57
+ message: `${label} must be a string, got ${describeValue(value)}.`,
58
+ });
59
+ return;
60
+ }
61
+ if (value.trim().length === 0) {
62
+ pushIssue(issues, {
63
+ result_index: resultIndex,
64
+ task_id: taskId,
65
+ field: label,
66
+ message: `${label} must not be empty.`,
67
+ });
68
+ }
69
+ }
12
70
  function validateFinding(finding, label, taskId, resultIndex) {
13
71
  const issues = [];
72
+ if (!isRecord(finding)) {
73
+ pushIssue(issues, {
74
+ result_index: resultIndex,
75
+ task_id: taskId,
76
+ field: label,
77
+ message: `${label} must be an object, got ${describeValue(finding)}.`,
78
+ });
79
+ return issues;
80
+ }
14
81
  for (const field of REQUIRED_FINDING_FIELDS) {
15
- const value = finding[field];
16
- if (value === undefined || value === null || String(value).trim() === "") {
17
- issues.push({
18
- result_index: resultIndex,
19
- task_id: taskId,
20
- severity: "error",
21
- field: `${label}.${field}`,
22
- message: `Required field '${field}' is missing or empty.`,
23
- });
24
- }
82
+ validateRequiredStringField(finding[field], `${label}.${field}`, taskId, resultIndex, issues);
25
83
  }
26
- if (finding.severity && !VALID_SEVERITIES.has(finding.severity)) {
27
- issues.push({
84
+ if (typeof finding.severity === "string" &&
85
+ !VALID_SEVERITIES.has(finding.severity)) {
86
+ pushIssue(issues, {
28
87
  result_index: resultIndex,
29
88
  task_id: taskId,
30
- severity: "error",
31
89
  field: `${label}.severity`,
32
90
  message: `Invalid severity '${finding.severity}'. Must be one of: ${[...VALID_SEVERITIES].join(", ")}.`,
33
91
  });
34
92
  }
35
- if (finding.confidence && !VALID_CONFIDENCES.has(finding.confidence)) {
36
- issues.push({
93
+ if (typeof finding.confidence === "string" &&
94
+ !VALID_CONFIDENCES.has(finding.confidence)) {
95
+ pushIssue(issues, {
37
96
  result_index: resultIndex,
38
97
  task_id: taskId,
39
- severity: "error",
40
98
  field: `${label}.confidence`,
41
99
  message: `Invalid confidence '${finding.confidence}'. Must be one of: ${[...VALID_CONFIDENCES].join(", ")}.`,
42
100
  });
43
101
  }
44
- if (!finding.affected_files || finding.affected_files.length === 0) {
45
- issues.push({
102
+ if (typeof finding.lens === "string" && !VALID_LENSES.has(finding.lens)) {
103
+ pushIssue(issues, {
104
+ result_index: resultIndex,
105
+ task_id: taskId,
106
+ field: `${label}.lens`,
107
+ message: `Invalid lens '${finding.lens}'. Must be one of: ${[...VALID_LENSES].join(", ")}.`,
108
+ });
109
+ }
110
+ const affectedFiles = finding.affected_files;
111
+ if (!Array.isArray(affectedFiles) || affectedFiles.length === 0) {
112
+ pushIssue(issues, {
46
113
  result_index: resultIndex,
47
114
  task_id: taskId,
48
- severity: "error",
49
115
  field: `${label}.affected_files`,
50
- message: "affected_files is empty at least one file location is required.",
116
+ message: "affected_files must be a non-empty array.",
51
117
  });
52
118
  }
53
119
  else {
54
- for (let k = 0; k < finding.affected_files.length; k++) {
55
- const af = finding.affected_files[k];
56
- if (!af.path?.trim()) {
57
- issues.push({
120
+ for (let k = 0; k < affectedFiles.length; k++) {
121
+ const item = affectedFiles[k];
122
+ if (!isRecord(item)) {
123
+ pushIssue(issues, {
124
+ result_index: resultIndex,
125
+ task_id: taskId,
126
+ field: `${label}.affected_files[${k}]`,
127
+ message: `affected_files[${k}] must be an object, got ${describeValue(item)}.`,
128
+ });
129
+ continue;
130
+ }
131
+ if (!isNonEmptyString(item.path)) {
132
+ pushIssue(issues, {
58
133
  result_index: resultIndex,
59
134
  task_id: taskId,
60
- severity: "error",
61
135
  field: `${label}.affected_files[${k}].path`,
62
136
  message: "affected_files entry has an empty path.",
63
137
  });
64
138
  }
139
+ if (item.line_start !== undefined &&
140
+ !Number.isInteger(item.line_start)) {
141
+ pushIssue(issues, {
142
+ result_index: resultIndex,
143
+ task_id: taskId,
144
+ field: `${label}.affected_files[${k}].line_start`,
145
+ message: `affected_files[${k}].line_start must be an integer, got ${describeValue(item.line_start)}.`,
146
+ });
147
+ }
148
+ if (item.line_end !== undefined &&
149
+ !Number.isInteger(item.line_end)) {
150
+ pushIssue(issues, {
151
+ result_index: resultIndex,
152
+ task_id: taskId,
153
+ field: `${label}.affected_files[${k}].line_end`,
154
+ message: `affected_files[${k}].line_end must be an integer, got ${describeValue(item.line_end)}.`,
155
+ });
156
+ }
157
+ if (Number.isInteger(item.line_start) &&
158
+ Number.isInteger(item.line_end) &&
159
+ Number(item.line_start) > Number(item.line_end)) {
160
+ pushIssue(issues, {
161
+ result_index: resultIndex,
162
+ task_id: taskId,
163
+ field: `${label}.affected_files[${k}]`,
164
+ message: "affected_files line_start must be less than or equal to line_end.",
165
+ });
166
+ }
65
167
  }
66
168
  }
67
- if (!finding.evidence || finding.evidence.length === 0) {
68
- issues.push({
169
+ const evidence = finding.evidence;
170
+ if (!Array.isArray(evidence) || evidence.length === 0) {
171
+ pushIssue(issues, {
69
172
  result_index: resultIndex,
70
173
  task_id: taskId,
71
- severity: "error",
72
174
  field: `${label}.evidence`,
73
- message: "evidence is empty — at least one quoted or referenced excerpt from the reviewed file is required for every finding.",
175
+ message: "evidence is empty — provide an array of plain strings such as \"src/foo.ts:42 - variable overwritten before use\".",
74
176
  });
75
177
  }
76
178
  else {
77
- const hasSubstantiveEntry = finding.evidence.some((e) => e.trim().length > 0);
179
+ let hasSubstantiveEntry = false;
180
+ for (let k = 0; k < evidence.length; k++) {
181
+ const entry = evidence[k];
182
+ if (typeof entry !== "string") {
183
+ pushIssue(issues, {
184
+ result_index: resultIndex,
185
+ task_id: taskId,
186
+ field: `${label}.evidence[${k}]`,
187
+ message: `evidence[${k}] must be a string, got ${describeValue(entry)}.`,
188
+ });
189
+ continue;
190
+ }
191
+ if (entry.trim().length > 0) {
192
+ hasSubstantiveEntry = true;
193
+ }
194
+ }
78
195
  if (!hasSubstantiveEntry) {
79
- issues.push({
196
+ pushIssue(issues, {
80
197
  result_index: resultIndex,
81
198
  task_id: taskId,
82
- severity: "error",
83
199
  field: `${label}.evidence`,
84
200
  message: "All evidence entries are empty strings.",
85
201
  });
@@ -87,26 +203,202 @@ function validateFinding(finding, label, taskId, resultIndex) {
87
203
  }
88
204
  return issues;
89
205
  }
90
- export function validateAuditResults(results, tasks) {
206
+ function coversAffectedSpan(coverage, path, start, end) {
207
+ return coverage.some((entry) => entry.path === path &&
208
+ start > 0 &&
209
+ end > 0 &&
210
+ end <= entry.total_lines);
211
+ }
212
+ export function validateAuditResults(results, tasks, options = {}) {
91
213
  const issues = [];
92
- const taskMap = new Map(tasks.map((t) => [t.task_id, t]));
214
+ const taskMap = new Map(tasks.map((task) => [task.task_id, task]));
215
+ if (!Array.isArray(results)) {
216
+ pushIssue(issues, {
217
+ result_index: -1,
218
+ task_id: "results",
219
+ field: "results",
220
+ message: `Audit results payload must be a JSON array, got ${describeValue(results)}.`,
221
+ });
222
+ return issues;
223
+ }
93
224
  for (let i = 0; i < results.length; i++) {
94
225
  const result = results[i];
95
- const taskId = result.task_id ?? `result[${i}]`;
96
- if (!result.reviewed_ranges || result.reviewed_ranges.length === 0) {
97
- issues.push({
226
+ if (!isRecord(result)) {
227
+ pushIssue(issues, {
228
+ result_index: i,
229
+ task_id: `result[${i}]`,
230
+ field: `results[${i}]`,
231
+ message: `Each audit result must be an object, got ${describeValue(result)}.`,
232
+ });
233
+ continue;
234
+ }
235
+ const taskId = issueTaskId(result, i);
236
+ const task = taskMap.get(taskId);
237
+ validateRequiredStringField(result.task_id, "task_id", taskId, i, issues);
238
+ validateRequiredStringField(result.unit_id, "unit_id", taskId, i, issues);
239
+ validateRequiredStringField(result.pass_id, "pass_id", taskId, i, issues);
240
+ validateRequiredStringField(result.lens, "lens", taskId, i, issues);
241
+ if (typeof result.lens === "string" &&
242
+ !VALID_LENSES.has(result.lens)) {
243
+ pushIssue(issues, {
244
+ result_index: i,
245
+ task_id: taskId,
246
+ field: "lens",
247
+ message: `Invalid lens '${result.lens}'. Must be one of: ${[...VALID_LENSES].join(", ")}.`,
248
+ });
249
+ }
250
+ if (tasks.length > 0 && !task) {
251
+ pushIssue(issues, {
252
+ result_index: i,
253
+ task_id: taskId,
254
+ field: "task_id",
255
+ message: `Unknown task_id '${taskId}'. Use the active task manifest for valid ids: ` +
256
+ tasks.map((item) => item.task_id).join(", "),
257
+ });
258
+ }
259
+ const fileCoverage = result.file_coverage;
260
+ const normalizedFileCoverage = [];
261
+ if (!Array.isArray(fileCoverage) || fileCoverage.length === 0) {
262
+ pushIssue(issues, {
98
263
  result_index: i,
99
264
  task_id: taskId,
100
- severity: "error",
101
- field: "reviewed_ranges",
102
- message: "reviewed_ranges is empty — no proof of file reading was recorded. " +
103
- "Each result must include the line ranges actually read.",
265
+ field: "file_coverage",
266
+ message: "file_coverage is empty — each result must declare every assigned file it reviewed and the file's total line count.",
104
267
  });
105
268
  }
106
- for (let j = 0; j < (result.findings ?? []).length; j++) {
107
- const finding = result.findings[j];
269
+ else {
270
+ const seenCoveragePaths = new Set();
271
+ for (let j = 0; j < fileCoverage.length; j++) {
272
+ const entry = fileCoverage[j];
273
+ if (!isRecord(entry)) {
274
+ pushIssue(issues, {
275
+ result_index: i,
276
+ task_id: taskId,
277
+ field: `file_coverage[${j}]`,
278
+ message: `file_coverage[${j}] must be an object, got ${describeValue(entry)}.`,
279
+ });
280
+ continue;
281
+ }
282
+ if (!isNonEmptyString(entry.path)) {
283
+ pushIssue(issues, {
284
+ result_index: i,
285
+ task_id: taskId,
286
+ field: `file_coverage[${j}].path`,
287
+ message: "file_coverage entry has an empty path.",
288
+ });
289
+ }
290
+ else if (task && !task.file_paths.includes(entry.path)) {
291
+ pushIssue(issues, {
292
+ result_index: i,
293
+ task_id: taskId,
294
+ severity: "warning",
295
+ field: `file_coverage[${j}].path`,
296
+ message: `file_coverage path '${entry.path}' is not listed in the task file_paths.`,
297
+ });
298
+ }
299
+ else if (seenCoveragePaths.has(entry.path)) {
300
+ pushIssue(issues, {
301
+ result_index: i,
302
+ task_id: taskId,
303
+ field: `file_coverage[${j}].path`,
304
+ message: `file_coverage path '${entry.path}' is duplicated. Declare each file once.`,
305
+ });
306
+ }
307
+ else {
308
+ seenCoveragePaths.add(entry.path);
309
+ }
310
+ if (!Number.isInteger(entry.total_lines)) {
311
+ pushIssue(issues, {
312
+ result_index: i,
313
+ task_id: taskId,
314
+ field: `file_coverage[${j}].total_lines`,
315
+ message: `file_coverage[${j}].total_lines must be an integer, got ${describeValue(entry.total_lines)}.`,
316
+ });
317
+ }
318
+ if (Number.isInteger(entry.total_lines) &&
319
+ Number(entry.total_lines) <= 0) {
320
+ pushIssue(issues, {
321
+ result_index: i,
322
+ task_id: taskId,
323
+ field: `file_coverage[${j}].total_lines`,
324
+ message: "file_coverage total_lines must be greater than zero.",
325
+ });
326
+ }
327
+ const expectedLineCount = typeof entry.path === "string"
328
+ ? options.lineIndex?.[entry.path]
329
+ : undefined;
330
+ if (Number.isInteger(entry.total_lines) &&
331
+ typeof expectedLineCount === "number" &&
332
+ Number(entry.total_lines) !== expectedLineCount) {
333
+ pushIssue(issues, {
334
+ result_index: i,
335
+ task_id: taskId,
336
+ field: `file_coverage[${j}].total_lines`,
337
+ message: `file_coverage[${j}].total_lines must match the current file line count for '${entry.path}' ` +
338
+ `(expected ${expectedLineCount}, got ${entry.total_lines}).`,
339
+ });
340
+ }
341
+ if (isNonEmptyString(entry.path) &&
342
+ Number.isInteger(entry.total_lines) &&
343
+ Number(entry.total_lines) > 0) {
344
+ normalizedFileCoverage.push({
345
+ path: entry.path,
346
+ total_lines: Number(entry.total_lines),
347
+ });
348
+ }
349
+ }
350
+ if (task) {
351
+ for (const path of task.file_paths) {
352
+ if (!seenCoveragePaths.has(path)) {
353
+ pushIssue(issues, {
354
+ result_index: i,
355
+ task_id: taskId,
356
+ field: "file_coverage",
357
+ message: `file_coverage must include every assigned file. Missing '${path}'.`,
358
+ });
359
+ }
360
+ }
361
+ }
362
+ }
363
+ const findings = result.findings;
364
+ if (!Array.isArray(findings)) {
365
+ pushIssue(issues, {
366
+ result_index: i,
367
+ task_id: taskId,
368
+ field: "findings",
369
+ message: `findings must be an array, got ${describeValue(findings)}.`,
370
+ });
371
+ continue;
372
+ }
373
+ for (let j = 0; j < findings.length; j++) {
108
374
  const label = `findings[${j}]`;
375
+ const finding = findings[j];
109
376
  issues.push(...validateFinding(finding, label, taskId, i));
377
+ if (!isRecord(finding) || !Array.isArray(finding.affected_files)) {
378
+ continue;
379
+ }
380
+ for (let k = 0; k < finding.affected_files.length; k++) {
381
+ const affected = finding.affected_files[k];
382
+ if (!isRecord(affected) || !isNonEmptyString(affected.path)) {
383
+ continue;
384
+ }
385
+ if (!Number.isInteger(affected.line_start)) {
386
+ continue;
387
+ }
388
+ const start = Number(affected.line_start);
389
+ const end = Number.isInteger(affected.line_end)
390
+ ? Number(affected.line_end)
391
+ : start;
392
+ if (!coversAffectedSpan(normalizedFileCoverage, affected.path, start, end)) {
393
+ pushIssue(issues, {
394
+ result_index: i,
395
+ task_id: taskId,
396
+ field: `${label}.affected_files[${k}]`,
397
+ message: `affected_files line span ${affected.path}:${start}-${end} falls outside the declared file_coverage. ` +
398
+ "Fix the affected_files location or correct file_coverage.total_lines.",
399
+ });
400
+ }
401
+ }
110
402
  }
111
403
  }
112
404
  return issues;
@@ -26,8 +26,8 @@ The preferred bootstrap path is:
26
26
  audit-code install
27
27
  ```
28
28
 
29
- That installs repo-local `/audit-code` surfaces for VS Code / Copilot, OpenCode, Claude Code, and compatibility instruction files such as `AGENTS.md` and `CLAUDE.md`.
30
- It also writes `.audit-code/install/GETTING-STARTED.md` with dedicated quick-start sections for VS Code, OpenCode, Claude Code, Claude Desktop, and Antigravity.
29
+ That installs repo-local `/audit-code` surfaces and MCP-oriented support assets for Codex, Claude Desktop, OpenCode, VS Code, and Antigravity.
30
+ It also writes `.audit-code/install/GETTING-STARTED.md` with dedicated quick-start sections for each host plus `.audit-code/install/manifest.json` and a shared repo-local MCP launcher.
31
31
 
32
32
  Use one of these supported ways to obtain the raw prompt asset directly when you need prompt import instead:
33
33
 
@@ -44,58 +44,56 @@ This is the intended product surface.
44
44
 
45
45
  Use `/audit-code` in conversation, treat the active conversation model as the default model, and treat project files plus attached repository context as the default context.
46
46
 
47
- ### GitHub Copilot
47
+ ### Codex
48
48
 
49
- Use `audit-code install` from the target repository root.
49
+ Use `audit-code install --host codex` or the default `audit-code install` from the target repository root.
50
50
 
51
- That writes `.github/prompts/audit-code.prompt.md` and `.github/copilot-instructions.md` so the repository carries the canonical `/audit-code` instructions instead of relying on manual copy-paste.
52
- The generated prompt file explicitly sets `agent: agent` so `/audit-code` lands in the tool-capable Copilot agent flow.
51
+ That writes a repo-local Codex skill bundle, updates `AGENTS.md` through a managed block when needed, and emits Codex-specific MCP setup guidance plus an automation recipe in `.audit-code/install/codex/`.
52
+ The intended operator flow is still conversational first, with the generated skill and AGENTS guidance steering the active Codex session toward `/audit-code` and the MCP-backed workflow.
53
53
 
54
- The narrower `audit-code install-host --host copilot` alias still exists, but it is no longer the preferred setup path.
54
+ The Codex automation recipe should still be treated as optional follow-through after the basic local flow is validated in the real app.
55
55
 
56
- ### OpenCode
56
+ ### Claude Desktop
57
57
 
58
- Use `audit-code install` from the target repository root.
58
+ Use `audit-code install --host claude-desktop` or the default `audit-code install` from the target repository root.
59
59
 
60
- That writes `.opencode/commands/audit-code.md` plus `AGENTS.md` and compatibility skills so `/audit-code` is available in the repository with no extra provider flags.
61
- The generated OpenCode command now sets `agent: build` and keeps the current model selection, which makes the slash command behave more like the intended autonomous editing flow.
62
- The generated `.audit-code/install/GETTING-STARTED.md` file also includes an OpenCode-specific quick start so the repo-local command path is obvious after bootstrap.
60
+ This repository now treats Claude Desktop as an MCP-first host. The installer writes:
63
61
 
64
- ### Claude Code
62
+ - `.audit-code/install/claude-desktop/PROJECT-TEMPLATE.md`
63
+ - `.audit-code/install/claude-desktop/remote-mcp-connector.json`
64
+ - generated local bundle artifacts including `auditor-lambda.dxt` and `auditor-lambda.mcpb`
65
+
66
+ The intended path is to install or reference the generated local MCP bundle, then use the shared prompt and project-template guidance to run `/audit-code` conversationally.
67
+ Manual prompt import remains a fallback, not the primary documented path.
68
+
69
+ ### OpenCode
65
70
 
66
71
  Use `audit-code install` from the target repository root.
67
72
 
68
- That writes `.claude/commands/audit-code.md`, `CLAUDE.md`, and compatibility skills so `/audit-code` is available in the repository with no extra provider flags.
69
- The generated `.audit-code/install/GETTING-STARTED.md` file also includes a Claude Code-specific quick start so the repo-local command path is obvious after bootstrap.
73
+ That writes `.opencode/commands/audit-code.md`, a repo-local OpenCode skill bundle, and `opencode.json` so `/audit-code` is available in the repository with no extra provider flags.
74
+ The generated OpenCode assets also point OpenCode toward the shared auditor MCP server instead of rebuilding backend state ad hoc.
70
75
 
71
76
  ### VS Code
72
77
 
73
78
  Run `audit-code install` from the target repository root, then open `.audit-code/install/GETTING-STARTED.md` if you want the exact repo-local path that bootstrap created for VS Code chat surfaces.
74
79
 
80
+ That writes `.github/prompts/audit-code.prompt.md`, `.github/copilot-instructions.md`, `.github/agents/auditor.agent.md`, and `.vscode/mcp.json`.
75
81
  The expected happy path is still to invoke `/audit-code` from chat, not to start from the backend CLI.
76
82
 
77
- ### Claude Desktop
78
-
79
- Run `audit-code install` from the target repository root, then open `.audit-code/install/GETTING-STARTED.md`.
80
-
81
- There is no verified project-local slash-command install surface for Claude Desktop in this repository today, so the intended path is:
82
-
83
- 1. import `.audit-code/install/audit-code.import.md` into Claude Desktop's prompt or instruction surface
84
- 2. invoke `/audit-code` conversationally inside Claude Desktop
85
-
86
83
  ### Antigravity
87
84
 
88
85
  Run `audit-code install` from the target repository root, then open `.audit-code/install/GETTING-STARTED.md`.
89
86
 
90
- There is no verified repo-local slash-command install surface for Antigravity in this repository today, so the intended path is:
87
+ There is still no documented native repo-local saved-workflow surface for Antigravity in this repository today, so the intended path is:
91
88
 
92
- 1. import `.audit-code/install/audit-code.import.md` into Antigravity's prompt or instruction surface when available
93
- 2. invoke `/audit-code` conversationally inside Antigravity
94
- 3. fall back to `audit-code` from an Antigravity-managed terminal only when you intentionally need the repo-local backend wrapper
89
+ 1. use the generated planning-mode and MCP setup guidance
90
+ 2. invoke `/audit-code` conversationally inside Antigravity when the host surface allows it
91
+ 3. use the shared MCP tools and resources when structured state exchange is needed
92
+ 4. fall back to `audit-code` from an Antigravity-managed terminal only when you intentionally need the repo-local backend wrapper
95
93
 
96
94
  ### Similar manual-import hosts
97
95
 
98
- Use the same installed prompt asset and repo-local guide pattern as Claude Desktop and Antigravity.
96
+ Use the same installed prompt asset and repo-local guide pattern as Antigravity, or the same MCP-first bundle pattern as Claude Desktop, depending on what the host actually supports.
99
97
 
100
98
  The backend CLI remains optional fallback infrastructure.
101
99
 
@@ -224,6 +222,17 @@ Current recommended usage is one of these:
224
222
 
225
223
  That keeps the product usable in Antigravity now without pretending that a native adapter already exists.
226
224
 
225
+ ## Remaining steps
226
+
227
+ The current implementation shipped the shared installer and MCP substrate. The remaining work is operational validation and fit-and-finish, not a fresh redesign.
228
+
229
+ Highest-value follow-through:
230
+
231
+ 1. validate the generated Codex, Claude Desktop, OpenCode, and VS Code assets inside the real products they target
232
+ 2. tighten generated quick-start guidance anywhere those host smoke tests expose ambiguity
233
+ 3. document exactly how Antigravity artifacts should map into `import_results` and `import_runtime_updates`
234
+ 4. keep host claims conservative until those end-to-end product checks are complete
235
+
227
236
  ## Model-selection rule
228
237
 
229
238
  The product direction remains skill-first:
package/docs/artifacts.md CHANGED
@@ -1,8 +1,10 @@
1
- # Core artifacts
1
+ # Core Artifacts
2
2
 
3
- These JSON artifacts are the stable contract between deterministic tooling and LLM audit passes.
3
+ This document follows [audit-goals.md](C:/Code/auditor-lambda/spec/audit-goals.md).
4
4
 
5
- ## Core files
5
+ ## Incomplete-run artifacts
6
+
7
+ During an incomplete or blocked audit, `.audit-artifacts/` may contain:
6
8
 
7
9
  - `repo_manifest.json`
8
10
  - `file_disposition.json`
@@ -13,57 +15,22 @@ These JSON artifacts are the stable contract between deterministic tooling and L
13
15
  - `flow_coverage.json`
14
16
  - `risk_register.json`
15
17
  - `coverage_matrix.json`
16
- - `runtime_validation_tasks.json`
17
- - `runtime_validation_report.json`
18
+ - `runtime_validation_tasks.json` when deterministic runtime validation is planned
19
+ - `runtime_validation_report.json` when runtime validation has executed or been updated
18
20
  - `external_analyzer_results.json`
19
- - `audit_results.json`
21
+ - `audit_tasks.json`
22
+ - `audit_results.jsonl`
20
23
  - `requeue_tasks.json`
21
- - `merged_findings.json`
22
- - `root_cause_clusters.json`
23
- - `synthesis_report.json`
24
-
25
- ## Design rule
26
-
27
- Tool-specific collectors should write into these normalized formats so that the agent layer can remain portable across runtimes.
28
-
29
- ## Coverage rule
30
-
31
- Coverage is not based only on test instrumentation. It is based on explicit audit accounting:
32
-
33
- - file classification
34
- - file disposition
35
- - unit assignment
36
- - required lenses
37
- - reviewed source ranges
38
- - completed passes
39
- - requeue targets for missing review
40
- - critical-flow coverage state
41
-
42
- ## Excluded artifact behavior
43
-
44
- Files marked as generated, vendor, binary, doc-only, or explicitly excluded should remain visible in manifests and disposition tracking, but should not receive normal audit-unit assignment or requeue tasks.
45
-
46
- ## Critical flow role
47
-
48
- `critical_flows.json` is intended to bridge deterministic planning and higher-order semantic review. It gives LLM agents a bounded way to inspect important end-to-end paths without reading the entire repository at once.
49
-
50
- `flow_coverage.json` tracks whether those important paths have received the intended lenses, which allows the planner to treat critical-flow review as a first-class coverage requirement rather than a loose advisory layer.
51
-
52
- ## Runtime validation role
53
-
54
- `runtime_validation_tasks.json` turns unresolved high-risk units and incomplete critical flows into explicit dynamic follow-up work.
55
-
56
- `runtime_validation_report.json` is where evidence from those checks should land so that later synthesis can distinguish confirmed, not-confirmed, and inconclusive concerns.
24
+ - dispatch files for the currently active worker task
57
25
 
58
- ## External analyzer role
26
+ ## Scope rule
59
27
 
60
- `external_analyzer_results.json` is the normalized landing zone for third-party tools such as SAST analyzers, coverage summaries, lint diagnostics, dependency scanners, and similar sources. Downstream prompts should prefer this normalized form over raw tool-native payloads.
28
+ Excluded files remain visible in deterministic intake/disposition where useful,
29
+ but they must not create audit work. This includes logs, licenses, lockfiles,
30
+ generated artifacts, vendored artifacts, binaries, and trivial non-code files.
61
31
 
62
- External analyzer results now also influence:
32
+ ## Completion rule
63
33
 
64
- - risk scoring
65
- - required lenses in coverage
66
- - task priority
67
- - dedicated analyzer follow-up tasks
68
- - requeue priority
69
- - synthesis evidence and summaries
34
+ These artifacts are transient implementation state only. When the audit
35
+ completes, `.audit-artifacts/` is removed and only repo-root `audit-report.md`
36
+ remains.