auditor-lambda 0.10.8 → 0.11.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 +5 -0
- package/dist/cli/args.d.ts +3 -3
- package/dist/cli/args.js +4 -7
- package/dist/cli/dispatch.d.ts +1 -0
- package/dist/cli/dispatch.js +24 -5
- package/dist/cli/prompts.js +7 -6
- package/dist/cli/resynthesizeCommand.d.ts +1 -0
- package/dist/cli/resynthesizeCommand.js +50 -0
- package/dist/cli.js +5 -1
- package/dist/io/artifacts.d.ts +3 -1
- package/dist/io/artifacts.js +1 -0
- package/dist/orchestrator/advance.js +6 -0
- package/dist/orchestrator/dependencyMap.js +7 -0
- package/dist/orchestrator/designReviewPrompt.js +31 -10
- package/dist/orchestrator/executors.js +6 -0
- package/dist/orchestrator/ingestionExecutors.js +29 -1
- package/dist/orchestrator/intentCheckpointExecutor.d.ts +3 -0
- package/dist/orchestrator/intentCheckpointExecutor.js +29 -0
- package/dist/orchestrator/lensSelection.d.ts +32 -0
- package/dist/orchestrator/lensSelection.js +69 -0
- package/dist/orchestrator/planningExecutors.js +18 -3
- package/dist/orchestrator.d.ts +1 -1
- package/dist/orchestrator.js +3 -4
- package/dist/prompts/renderWorkerPrompt.js +6 -0
- package/dist/quota/index.d.ts +5 -3
- package/dist/quota/index.js +1 -1
- package/dist/reporting/synthesis.d.ts +10 -0
- package/dist/reporting/synthesis.js +23 -0
- package/dist/reporting/synthesisNarrativePrompt.js +3 -1
- package/docs/development.md +6 -0
- package/package.json +2 -1
- package/schemas/audit_findings.schema.json +14 -1
- package/schemas/dispatch_quota.schema.json +86 -2
- package/schemas/finding.schema.json +14 -1
package/README.md
CHANGED
|
@@ -212,11 +212,16 @@ Optional backend config:
|
|
|
212
212
|
|
|
213
213
|
```bash
|
|
214
214
|
npm install
|
|
215
|
+
npm run test:single -- tests/next-step.test.mjs
|
|
215
216
|
npm run verify:release
|
|
216
217
|
npm run release:patch
|
|
217
218
|
npm run release:patch:publish
|
|
218
219
|
```
|
|
219
220
|
|
|
221
|
+
When developing from a fresh clone or git worktree, run repo-root `npm install`
|
|
222
|
+
before package checks. Missing workspace links can look like stale
|
|
223
|
+
`@audit-tools/shared` export or type errors.
|
|
224
|
+
|
|
220
225
|
For GitHub Actions publication and npm Trusted Publishing setup, see `docs/release.md`.
|
|
221
226
|
|
|
222
227
|
## Key Docs
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { toPromptPathToken, quotePromptCommandArg, type SessionConfig } from "@audit-tools/shared";
|
|
2
2
|
export type UiMode = "visible" | "headless";
|
|
3
3
|
export declare const DIRECT_CLI_DEFAULTS: {
|
|
4
4
|
rootDir: string;
|
|
@@ -23,7 +23,7 @@ export declare function fromBase64Url(value: string): string;
|
|
|
23
23
|
export declare function digestId(value: string): string;
|
|
24
24
|
export declare function safeArtifactStem(value: string): string;
|
|
25
25
|
export declare function artifactNameForId(value: string, extension: string): string;
|
|
26
|
-
export declare
|
|
26
|
+
export declare const quoteCommandArg: typeof quotePromptCommandArg;
|
|
27
27
|
/**
|
|
28
28
|
* Normalize a generated command token to POSIX path separators. These command
|
|
29
29
|
* strings are embedded in step prompts and `allowed_commands` and run by the
|
|
@@ -34,7 +34,7 @@ export declare function quoteCommandArg(value: string): string;
|
|
|
34
34
|
* are touched, and no non-path argument in this CLI contains one, so this is a
|
|
35
35
|
* targeted normalization rather than a blanket rewrite.
|
|
36
36
|
*/
|
|
37
|
-
export declare
|
|
37
|
+
export declare const toPosixCommandToken: typeof toPromptPathToken;
|
|
38
38
|
export declare function renderCommand(argv: string[]): string;
|
|
39
39
|
export declare function summarizeLaunchExit(result: {
|
|
40
40
|
accepted?: boolean;
|
package/dist/cli/args.js
CHANGED
|
@@ -3,6 +3,7 @@ import { readdir } from "node:fs/promises";
|
|
|
3
3
|
import { Buffer } from "node:buffer";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { join, resolve } from "node:path";
|
|
6
|
+
import { renderPromptCommand, toPromptPathToken, quotePromptCommandArg, } from "@audit-tools/shared";
|
|
6
7
|
import { resolveFreshSessionProviderName } from "../providers/index.js";
|
|
7
8
|
export const DIRECT_CLI_DEFAULTS = {
|
|
8
9
|
rootDir: ".",
|
|
@@ -76,9 +77,7 @@ export function safeArtifactStem(value) {
|
|
|
76
77
|
export function artifactNameForId(value, extension) {
|
|
77
78
|
return `${safeArtifactStem(value)}_${digestId(value)}.${extension}`;
|
|
78
79
|
}
|
|
79
|
-
export
|
|
80
|
-
return /[\s"]/u.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
|
|
81
|
-
}
|
|
80
|
+
export const quoteCommandArg = quotePromptCommandArg;
|
|
82
81
|
/**
|
|
83
82
|
* Normalize a generated command token to POSIX path separators. These command
|
|
84
83
|
* strings are embedded in step prompts and `allowed_commands` and run by the
|
|
@@ -89,11 +88,9 @@ export function quoteCommandArg(value) {
|
|
|
89
88
|
* are touched, and no non-path argument in this CLI contains one, so this is a
|
|
90
89
|
* targeted normalization rather than a blanket rewrite.
|
|
91
90
|
*/
|
|
92
|
-
export
|
|
93
|
-
return value.includes("\\") ? value.replace(/\\/g, "/") : value;
|
|
94
|
-
}
|
|
91
|
+
export const toPosixCommandToken = toPromptPathToken;
|
|
95
92
|
export function renderCommand(argv) {
|
|
96
|
-
return argv
|
|
93
|
+
return renderPromptCommand(argv);
|
|
97
94
|
}
|
|
98
95
|
export function summarizeLaunchExit(result) {
|
|
99
96
|
if (result.accepted !== false && !result.error) {
|
package/dist/cli/dispatch.d.ts
CHANGED
package/dist/cli/dispatch.js
CHANGED
|
@@ -10,7 +10,7 @@ import { orderTasksForPacketReview, buildReviewPackets, sizeIndexFromManifest, }
|
|
|
10
10
|
import { buildFileAnchorSummary } from "../orchestrator/fileAnchors.js";
|
|
11
11
|
import { resolveFreshSessionProviderName } from "../providers/index.js";
|
|
12
12
|
import { loadSessionConfig } from "../supervisor/sessionConfig.js";
|
|
13
|
-
import { computeDispatchCapacity, buildProviderModelKey, resolveHostModel, readQuotaState, resolveHostActiveSubagentLimit, lookupDiscoveredLimits, mergeDiscoveredLimits, } from "../quota/index.js";
|
|
13
|
+
import { computeDispatchCapacity, buildProviderModelKey, resolveHostModel, readQuotaState, resolveHostActiveSubagentLimit, lookupDiscoveredLimits, mergeDiscoveredLimits, summarizeDispatchCapacityPools, } from "../quota/index.js";
|
|
14
14
|
import { taskResultPath, packetPromptPath, artifactNameForId, toBase64Url, fromBase64Url, getFlag, } from "./args.js";
|
|
15
15
|
export const LARGE_FILE_PACKET_TARGET_LINES = 2500;
|
|
16
16
|
export const SMALL_MODEL_HINT_MAX_LINES = 500;
|
|
@@ -303,10 +303,12 @@ export function buildTaskSections(packetTasks, lensDefs, lineIndex) {
|
|
|
303
303
|
* Wraps the 75-line array-join block and returns the assembled prompt string.
|
|
304
304
|
*/
|
|
305
305
|
export function buildPacketPrompt(params) {
|
|
306
|
-
const { packet, fileList, largeFileSection, taskSections, submitCommand } = params;
|
|
306
|
+
const { packet, fileList, largeFileSection, taskSections, submitCommand, repoRoot } = params;
|
|
307
307
|
const largeFileMode = isIsolatedLargeFilePacket(packet);
|
|
308
308
|
return [
|
|
309
309
|
"You are a code auditor. Review this packet once, then submit exactly one result per listed task.",
|
|
310
|
+
repoRoot ? `Repository root: ${repoRoot}` : "Repository root: use the root from the step contract.",
|
|
311
|
+
"Set the shell/tool workdir to the repository root when running backend commands.",
|
|
310
312
|
"",
|
|
311
313
|
"## Packet",
|
|
312
314
|
`packet_id: ${packet.packet_id}`,
|
|
@@ -316,8 +318,8 @@ export function buildPacketPrompt(params) {
|
|
|
316
318
|
"",
|
|
317
319
|
"## Files to read",
|
|
318
320
|
largeFileMode
|
|
319
|
-
? "Use targeted Read/Grep calls. Paths are repo-relative
|
|
320
|
-
: "Use your Read tool. Paths are repo-relative
|
|
321
|
+
? "Use targeted Read/Grep calls. Paths are repo-relative to the repository root above."
|
|
322
|
+
: "Use your Read tool. Paths are repo-relative to the repository root above.",
|
|
321
323
|
"Use host Read and Grep tools for source inspection. Do not use shell search commands.",
|
|
322
324
|
fileList,
|
|
323
325
|
"",
|
|
@@ -331,6 +333,8 @@ export function buildPacketPrompt(params) {
|
|
|
331
333
|
"packet-*-result.json / audit_result_*.json) — the submit-packet command below is the only",
|
|
332
334
|
"way to record results, and it writes them inside the artifacts directory for you.",
|
|
333
335
|
"Produce one JSON array containing exactly one AuditResult object for each listed task.",
|
|
336
|
+
"Windows PowerShell: do not pipe an inline foreach statement directly into ConvertTo-Json.",
|
|
337
|
+
"Assign the foreach output to a variable first, then pipe that variable to ConvertTo-Json.",
|
|
334
338
|
"",
|
|
335
339
|
"Schema file (resolve relative to this prompt's directory): audit_result.schema.json",
|
|
336
340
|
" $refs resolved from the same directory: finding.schema.json, audit_task.schema.json",
|
|
@@ -437,6 +441,8 @@ async function computeDispatchQuota(params) {
|
|
|
437
441
|
wave_size: dispatchCapacity.total_slots,
|
|
438
442
|
estimated_wave_tokens: dispatchCapacity.estimated_wave_tokens,
|
|
439
443
|
cooldown_until: dispatchCapacity.cooldown_until,
|
|
444
|
+
binding_cap: dispatchCapacity.binding_cap,
|
|
445
|
+
capacity_pools: summarizeDispatchCapacityPools(dispatchCapacity),
|
|
440
446
|
quota_source_snapshot: waveSchedule.quota_source_snapshot ?? null,
|
|
441
447
|
backoff_state: null,
|
|
442
448
|
};
|
|
@@ -633,8 +639,11 @@ export async function prepareDispatchArtifacts(params) {
|
|
|
633
639
|
result_path: resultPathByTaskId.get(task.task_id),
|
|
634
640
|
});
|
|
635
641
|
}
|
|
636
|
-
const prompt = buildPacketPrompt({ packet, packetTasks, fileList, largeFileSection, taskSections, submitCommand });
|
|
642
|
+
const prompt = buildPacketPrompt({ packet, packetTasks, fileList, largeFileSection, taskSections, submitCommand, repoRoot: reviewRoot });
|
|
637
643
|
await writeFile(promptPath, prompt, "utf8");
|
|
644
|
+
const packetWritePaths = packetTasks
|
|
645
|
+
.map((task) => resultPathByTaskId.get(task.task_id))
|
|
646
|
+
.filter((p) => p !== undefined);
|
|
638
647
|
plan.push({
|
|
639
648
|
packet_id: packet.packet_id,
|
|
640
649
|
description: `Audit ${packet.file_paths.length} file(s), ${packet.task_ids.length} task(s), ${packet.lenses.length} lens(es) (~${packet.total_lines} lines)` +
|
|
@@ -642,6 +651,16 @@ export async function prepareDispatchArtifacts(params) {
|
|
|
642
651
|
prompt_path: promptPath,
|
|
643
652
|
complexity,
|
|
644
653
|
model_hint: buildDispatchModelHint(complexity),
|
|
654
|
+
access: {
|
|
655
|
+
read_paths: [
|
|
656
|
+
promptPath,
|
|
657
|
+
...(reviewRoot
|
|
658
|
+
? packet.file_paths.map((p) => join(reviewRoot, p))
|
|
659
|
+
: packet.file_paths),
|
|
660
|
+
],
|
|
661
|
+
write_paths: packetWritePaths,
|
|
662
|
+
forbidden_patterns: ["packet-*-result.json", "audit_result_*.json"],
|
|
663
|
+
},
|
|
645
664
|
});
|
|
646
665
|
}
|
|
647
666
|
await writeJsonFile(dispatchPlanPath, plan);
|
package/dist/cli/prompts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DO_NOT_TOKEN_WRAP_NOTE } from "@audit-tools/shared";
|
|
1
|
+
import { DO_NOT_TOKEN_WRAP_NOTE, DISPATCH_PROMPT_HANDOFF_NOTE } from "@audit-tools/shared";
|
|
2
2
|
import { renderCommand } from "./args.js";
|
|
3
3
|
/**
|
|
4
4
|
* Token prefix the host should use to re-invoke the backend in generated
|
|
@@ -93,7 +93,7 @@ export function renderDispatchReviewPrompt(params) {
|
|
|
93
93
|
...dispatchDataLines,
|
|
94
94
|
...canaryLines,
|
|
95
95
|
"",
|
|
96
|
-
|
|
96
|
+
DISPATCH_PROMPT_HANDOFF_NOTE,
|
|
97
97
|
"",
|
|
98
98
|
"Subagent prompt shape:",
|
|
99
99
|
"",
|
|
@@ -104,7 +104,7 @@ export function renderDispatchReviewPrompt(params) {
|
|
|
104
104
|
"",
|
|
105
105
|
"Each subagent must submit its packet through the submit command printed in its packet prompt and stop after successful submission.",
|
|
106
106
|
"",
|
|
107
|
-
"**File access pre-approval:** Each dispatch plan entry includes an `access` object with `read_paths` and `
|
|
107
|
+
"**File access pre-approval:** Each dispatch plan entry includes an `access` object with `read_paths`, `write_paths`, and `forbidden_patterns`. If your host supports per-subagent file access restrictions, pre-approve exactly `entry.access.read_paths` and `entry.access.write_paths` for each subagent. Do not grant broad workspace or task-results directory write access. Workers should not access files outside their declared paths.",
|
|
108
108
|
"",
|
|
109
109
|
"**After all waves complete:**",
|
|
110
110
|
"",
|
|
@@ -172,10 +172,11 @@ export function renderEdgeReasoningDispatchPrompt(params) {
|
|
|
172
172
|
"optional pass: it only rewrites the `reason` string of those edges — it never",
|
|
173
173
|
"adds, removes, re-targets, or re-weights an edge.",
|
|
174
174
|
"",
|
|
175
|
-
"Dispatch exactly ONE subagent (via the `task` tool or equivalent).
|
|
176
|
-
"prompt file path; do not load the file into this orchestrator context:",
|
|
175
|
+
"Dispatch exactly ONE subagent (via the `task` tool or equivalent).",
|
|
177
176
|
"",
|
|
178
|
-
|
|
177
|
+
DISPATCH_PROMPT_HANDOFF_NOTE,
|
|
178
|
+
"",
|
|
179
|
+
` Prompt path: ${params.promptPath}`,
|
|
179
180
|
"",
|
|
180
181
|
"Subagent prompt shape:",
|
|
181
182
|
"",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdResynthesize(argv: string[]): Promise<void>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
4
|
+
import { getRootDir } from "./args.js";
|
|
5
|
+
import { normalizeExistingFindingsReport, renderAuditReportMarkdown, } from "../reporting/synthesis.js";
|
|
6
|
+
const AUDIT_TOOLS_DIR = ".audit-tools";
|
|
7
|
+
const FINDINGS_FILENAME = "audit-findings.json";
|
|
8
|
+
const REPORT_FILENAME = "audit-report.md";
|
|
9
|
+
function getFlagValue(argv, flag) {
|
|
10
|
+
const idx = argv.indexOf(flag);
|
|
11
|
+
return idx >= 0 && idx + 1 < argv.length ? argv[idx + 1] : undefined;
|
|
12
|
+
}
|
|
13
|
+
export async function cmdResynthesize(argv) {
|
|
14
|
+
const root = getRootDir(argv);
|
|
15
|
+
const auditToolsDir = join(root, AUDIT_TOOLS_DIR);
|
|
16
|
+
const defaultInput = join(auditToolsDir, FINDINGS_FILENAME);
|
|
17
|
+
const inputPath = getFlagValue(argv, "--input") ?? defaultInput;
|
|
18
|
+
if (!existsSync(inputPath)) {
|
|
19
|
+
console.error(`resynthesize: ${FINDINGS_FILENAME} not found at ${inputPath}. ` +
|
|
20
|
+
`Run the full audit first, or supply --input <path>.`);
|
|
21
|
+
process.exitCode = 1;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const raw = await readFile(inputPath, "utf8");
|
|
25
|
+
let report;
|
|
26
|
+
try {
|
|
27
|
+
report = JSON.parse(raw);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
31
|
+
console.error(`resynthesize: could not parse ${inputPath}: ${msg}`);
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const normalized = normalizeExistingFindingsReport(report);
|
|
36
|
+
const markdown = renderAuditReportMarkdown(normalized);
|
|
37
|
+
await mkdir(auditToolsDir, { recursive: true });
|
|
38
|
+
const outputFindingsPath = join(auditToolsDir, FINDINGS_FILENAME);
|
|
39
|
+
const outputReportPath = join(auditToolsDir, REPORT_FILENAME);
|
|
40
|
+
await writeFile(outputFindingsPath, JSON.stringify(normalized, null, 2), "utf8");
|
|
41
|
+
await writeFile(outputReportPath, markdown, "utf8");
|
|
42
|
+
console.log(JSON.stringify({
|
|
43
|
+
source: inputPath,
|
|
44
|
+
findings_output: outputFindingsPath,
|
|
45
|
+
report_output: outputReportPath,
|
|
46
|
+
finding_count: normalized.summary.finding_count,
|
|
47
|
+
work_block_count: normalized.summary.work_block_count,
|
|
48
|
+
contract_version: normalized.contract_version,
|
|
49
|
+
}, null, 2));
|
|
50
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -23,6 +23,7 @@ import { cmdValidate } from "./cli/validateCommand.js";
|
|
|
23
23
|
import { cmdValidateResults } from "./cli/validateResultsCommand.js";
|
|
24
24
|
import { cmdRequeue } from "./cli/requeueCommand.js";
|
|
25
25
|
import { cmdSynthesize } from "./cli/synthesizeCommand.js";
|
|
26
|
+
import { cmdResynthesize } from "./cli/resynthesizeCommand.js";
|
|
26
27
|
import { cmdCleanup } from "./cli/cleanupCommand.js";
|
|
27
28
|
import { cmdQuota } from "./cli/quotaCommand.js";
|
|
28
29
|
import { cmdDispatchStatus } from "./cli/dispatchStatusCommand.js";
|
|
@@ -93,6 +94,9 @@ async function main(argv) {
|
|
|
93
94
|
case "synthesize":
|
|
94
95
|
await cmdSynthesize(argv);
|
|
95
96
|
return;
|
|
97
|
+
case "resynthesize":
|
|
98
|
+
await cmdResynthesize(argv);
|
|
99
|
+
return;
|
|
96
100
|
case "cleanup":
|
|
97
101
|
await cmdCleanup(argv);
|
|
98
102
|
return;
|
|
@@ -119,7 +123,7 @@ async function main(argv) {
|
|
|
119
123
|
return;
|
|
120
124
|
default:
|
|
121
125
|
console.error(`Unknown command: ${command}`);
|
|
122
|
-
console.error("Available commands: sample-run, advance-audit, next-step, run-to-completion, worker-run, import-external-analyzer, intake, plan, ingest-results, explain-task, update-runtime-validation, validate, validate-results, requeue, synthesize, cleanup, prepare-dispatch, merge-and-ingest, submit-packet, validate-result, quota, status, dispatch-status");
|
|
126
|
+
console.error("Available commands: sample-run, advance-audit, next-step, run-to-completion, worker-run, import-external-analyzer, intake, plan, ingest-results, explain-task, update-runtime-validation, validate, validate-results, requeue, synthesize, resynthesize, cleanup, prepare-dispatch, merge-and-ingest, submit-packet, validate-result, quota, status, dispatch-status");
|
|
123
127
|
process.exitCode = 1;
|
|
124
128
|
}
|
|
125
129
|
}
|
package/dist/io/artifacts.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { cp, rm } from "node:fs/promises";
|
|
|
2
2
|
import type { AuditResult, AuditTask, CoverageMatrix, RepoManifest, UnitManifest } from "../types.js";
|
|
3
3
|
import type { AuditState } from "../types/auditState.js";
|
|
4
4
|
import type { ArtifactMetadataManifest } from "../types/artifactMetadata.js";
|
|
5
|
-
import type { AuditFindingsReport, FileDisposition, CriticalFlowManifest, GraphBundle, RiskRegister, SurfaceManifest } from "@audit-tools/shared";
|
|
5
|
+
import type { AuditFindingsReport, FileDisposition, CriticalFlowManifest, GraphBundle, RiskRegister, SurfaceManifest, IntentCheckpoint } from "@audit-tools/shared";
|
|
6
6
|
import type { SynthesisNarrativeRecord } from "../types/synthesisNarrative.js";
|
|
7
7
|
import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
|
|
8
8
|
import type { FlowCoverageManifest } from "../types/flowCoverage.js";
|
|
@@ -17,6 +17,7 @@ type ArtifactPayloadMap = {
|
|
|
17
17
|
repo_manifest: RepoManifest;
|
|
18
18
|
file_disposition: FileDisposition;
|
|
19
19
|
auto_fixes_applied: unknown;
|
|
20
|
+
intent_checkpoint: IntentCheckpoint;
|
|
20
21
|
unit_manifest: UnitManifest;
|
|
21
22
|
graph_bundle: GraphBundle;
|
|
22
23
|
surface_manifest: SurfaceManifest;
|
|
@@ -68,6 +69,7 @@ export declare const ARTIFACT_DEFINITIONS: {
|
|
|
68
69
|
readonly repo_manifest: ArtifactDefinition<"repo_manifest">;
|
|
69
70
|
readonly file_disposition: ArtifactDefinition<"file_disposition">;
|
|
70
71
|
readonly auto_fixes_applied: ArtifactDefinition<"auto_fixes_applied">;
|
|
72
|
+
readonly intent_checkpoint: ArtifactDefinition<"intent_checkpoint">;
|
|
71
73
|
readonly unit_manifest: ArtifactDefinition<"unit_manifest">;
|
|
72
74
|
readonly graph_bundle: ArtifactDefinition<"graph_bundle">;
|
|
73
75
|
readonly surface_manifest: ArtifactDefinition<"surface_manifest">;
|
package/dist/io/artifacts.js
CHANGED
|
@@ -34,6 +34,7 @@ export const ARTIFACT_DEFINITIONS = {
|
|
|
34
34
|
repo_manifest: jsonArtifact("repo_manifest.json", "intake"),
|
|
35
35
|
file_disposition: jsonArtifact("file_disposition.json", "intake"),
|
|
36
36
|
auto_fixes_applied: jsonArtifact("auto_fixes_applied.json", "intake"),
|
|
37
|
+
intent_checkpoint: jsonArtifact("intent_checkpoint.json", "intake"),
|
|
37
38
|
unit_manifest: jsonArtifact("unit_manifest.json", "analysis"),
|
|
38
39
|
graph_bundle: jsonArtifact("graph_bundle.json", "analysis"),
|
|
39
40
|
surface_manifest: jsonArtifact("surface_manifest.json", "analysis"),
|
|
@@ -3,6 +3,7 @@ import { decideNextStep, findObligation } from "./nextStep.js";
|
|
|
3
3
|
import { deriveAuditState } from "./state.js";
|
|
4
4
|
import { computeArtifactMetadata } from "./artifactMetadata.js";
|
|
5
5
|
import { runIntakeExecutor } from "./intakeExecutors.js";
|
|
6
|
+
import { runIntentCheckpointExecutor } from "./intentCheckpointExecutor.js";
|
|
6
7
|
import { runStructureExecutor, runDesignAssessmentExecutor, runDesignReviewAutoComplete, } from "./structureExecutors.js";
|
|
7
8
|
import { runPlanningExecutor } from "./planningExecutors.js";
|
|
8
9
|
import { runResultIngestionExecutor, runRuntimeValidationExecutor, runRuntimeValidationUpdateExecutor, runExternalAnalyzerImportExecutor, } from "./ingestionExecutors.js";
|
|
@@ -91,6 +92,11 @@ export async function advanceAudit(bundle, options = {}) {
|
|
|
91
92
|
run = await runIntakeExecutor(bundle, root);
|
|
92
93
|
break;
|
|
93
94
|
}
|
|
95
|
+
case "intent_checkpoint_executor": {
|
|
96
|
+
const root = requireRoot(options.root, "intent_checkpoint_executor");
|
|
97
|
+
run = await runIntentCheckpointExecutor(bundle, root, options.since);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
94
100
|
case "structure_executor":
|
|
95
101
|
// root is intentionally optional: present → buildGraphBundleFromFs, absent → manifest-only buildGraphBundle
|
|
96
102
|
run = await runStructureExecutor(bundle, options.root);
|
|
@@ -8,6 +8,13 @@ export const ARTIFACT_DEPENDENTS_MAP = {
|
|
|
8
8
|
"tooling_manifest.json": [
|
|
9
9
|
"repo_manifest.json",
|
|
10
10
|
],
|
|
11
|
+
"intent_checkpoint.json": [
|
|
12
|
+
"coverage_matrix.json",
|
|
13
|
+
"audit_tasks.json",
|
|
14
|
+
"audit_plan_metrics.json",
|
|
15
|
+
"review_packets.json",
|
|
16
|
+
"requeue_tasks.json",
|
|
17
|
+
],
|
|
11
18
|
"repo_manifest.json": [
|
|
12
19
|
"file_disposition.json",
|
|
13
20
|
"unit_manifest.json",
|
|
@@ -26,12 +26,17 @@ function summarizeGraph(bundle) {
|
|
|
26
26
|
return "Dependency graph is empty.";
|
|
27
27
|
return `Dependency graph: ${counts.join(", ")}.`;
|
|
28
28
|
}
|
|
29
|
-
function summarizeFlows(bundle) {
|
|
29
|
+
function summarizeFlows(bundle, max = 15) {
|
|
30
30
|
const flows = bundle.critical_flows?.flows ?? [];
|
|
31
31
|
if (flows.length === 0)
|
|
32
32
|
return "No critical flows identified.";
|
|
33
|
-
const
|
|
34
|
-
|
|
33
|
+
const shown = flows.slice(0, max);
|
|
34
|
+
const lines = shown.map((flow) => `- ${flow.name}: ${flow.paths.length} files, concerns: ${flow.concerns.join(", ") || "none"}`);
|
|
35
|
+
return [
|
|
36
|
+
`${flows.length} critical flows:`,
|
|
37
|
+
...lines,
|
|
38
|
+
...(flows.length > max ? [` ... and ${flows.length - max} more`] : []),
|
|
39
|
+
].join("\n");
|
|
35
40
|
}
|
|
36
41
|
function summarizeRisk(bundle) {
|
|
37
42
|
const items = bundle.risk_register?.items ?? [];
|
|
@@ -78,12 +83,17 @@ function buildPrioritizedReadingList(bundle, maxUnits) {
|
|
|
78
83
|
...lines,
|
|
79
84
|
].join("\n");
|
|
80
85
|
}
|
|
81
|
-
function summarizeSurfaces(bundle) {
|
|
86
|
+
function summarizeSurfaces(bundle, max = 20) {
|
|
82
87
|
const surfaces = bundle.surface_manifest?.surfaces ?? [];
|
|
83
88
|
if (surfaces.length === 0)
|
|
84
89
|
return "No externally reachable surfaces identified.";
|
|
85
|
-
const
|
|
86
|
-
|
|
90
|
+
const shown = surfaces.slice(0, max);
|
|
91
|
+
const lines = shown.map((surface) => `- ${surface.id} (${surface.kind}): ${surface.entrypoint}${surface.methods?.length ? ` [${surface.methods.join(", ")}]` : ""}`);
|
|
92
|
+
return [
|
|
93
|
+
`${surfaces.length} surfaces:`,
|
|
94
|
+
...lines,
|
|
95
|
+
...(surfaces.length > max ? [` ... and ${surfaces.length - max} more`] : []),
|
|
96
|
+
].join("\n");
|
|
87
97
|
}
|
|
88
98
|
function summarizeFiles(bundle) {
|
|
89
99
|
const files = bundle.repo_manifest?.files ?? [];
|
|
@@ -100,13 +110,15 @@ function summarizeFiles(bundle) {
|
|
|
100
110
|
.join(", ");
|
|
101
111
|
return `${files.length} files (${langSummary}).`;
|
|
102
112
|
}
|
|
103
|
-
function formatDeterministicFindings(findings) {
|
|
113
|
+
function formatDeterministicFindings(findings, max = 20) {
|
|
104
114
|
if (findings.length === 0)
|
|
105
115
|
return "No structural issues detected by deterministic analysis.";
|
|
106
|
-
const
|
|
116
|
+
const shown = findings.slice(0, max);
|
|
117
|
+
const lines = shown.map((finding) => `- [${finding.severity}] ${finding.title}: ${finding.summary}`);
|
|
107
118
|
return [
|
|
108
119
|
`${findings.length} structural findings from deterministic analysis:`,
|
|
109
120
|
...lines,
|
|
121
|
+
...(findings.length > max ? [` ... and ${findings.length - max} more`] : []),
|
|
110
122
|
].join("\n");
|
|
111
123
|
}
|
|
112
124
|
export function renderDesignReviewPrompt(bundle, options = {}) {
|
|
@@ -118,7 +130,7 @@ export function renderDesignReviewPrompt(bundle, options = {}) {
|
|
|
118
130
|
return [
|
|
119
131
|
"# Project design review",
|
|
120
132
|
"",
|
|
121
|
-
"You are reviewing the overall design of this project. The deterministic audit pipeline has already analyzed the codebase structure. Your job is to provide qualitative,
|
|
133
|
+
"You are reviewing the overall design of this project. The deterministic audit pipeline has already analyzed the codebase structure. Your job is to provide qualitative observations in two distinct modes: contract assessment for inferred or existing project contracts, and conceptual design critique for broader architecture ideas that static analysis cannot produce.",
|
|
122
134
|
"",
|
|
123
135
|
"## Project context",
|
|
124
136
|
"",
|
|
@@ -160,6 +172,15 @@ export function renderDesignReviewPrompt(bundle, options = {}) {
|
|
|
160
172
|
"",
|
|
161
173
|
prioritizedReadingList,
|
|
162
174
|
"",
|
|
175
|
+
"### Contract assessment",
|
|
176
|
+
"",
|
|
177
|
+
"- Infer existing contracts from the repository artifacts and code you inspect: invariants, trust boundaries, preconditions, postconditions, data lifecycle obligations, and critical-flow guarantees.",
|
|
178
|
+
"- Attack those inferred contracts with concrete counterexamples. Report evidenced gaps where the code appears to rely on an invariant or boundary that is missing, unenforced, unclear, or uncovered for a critical flow.",
|
|
179
|
+
"- Use contract-assessment categories such as inferred_contract_gap, trust_boundary_gap, invariant_counterexample, and critical_invariant_coverage_gap when those best describe the finding.",
|
|
180
|
+
"- Stay observational: do not invent a new contract DSL, write a remediation plan, remediate code, or turn the audit into an implementation pipeline.",
|
|
181
|
+
"",
|
|
182
|
+
"### Conceptual design critique",
|
|
183
|
+
"",
|
|
163
184
|
"- **Tool and library opportunities**: third-party tools, libraries, or frameworks that would improve the project. Concrete suggestions with rationale, not generic advice.",
|
|
164
185
|
"- **Architecture pattern improvements**: structural changes that would improve extensibility, testability, or maintainability. Consider whether the current abstractions match the problem domain.",
|
|
165
186
|
"- **Design simplification**: areas where the design is over-engineered or where simpler alternatives would work. Conversely, areas that are under-designed for their importance.",
|
|
@@ -174,7 +195,7 @@ export function renderDesignReviewPrompt(bundle, options = {}) {
|
|
|
174
195
|
"{",
|
|
175
196
|
' "id": "DR-001",',
|
|
176
197
|
' "title": "short descriptive title",',
|
|
177
|
-
' "category": "one of: tool_opportunity, architecture_pattern, design_simplification, integration, missing_capability",',
|
|
198
|
+
' "category": "one of: inferred_contract_gap, trust_boundary_gap, invariant_counterexample, critical_invariant_coverage_gap, tool_opportunity, architecture_pattern, design_simplification, integration, missing_capability",',
|
|
178
199
|
' "severity": "one of: critical, high, medium, low, info",',
|
|
179
200
|
' "confidence": "one of: high, medium, low",',
|
|
180
201
|
' "lens": "architecture",',
|
|
@@ -14,6 +14,12 @@ export const EXECUTOR_REGISTRY = [
|
|
|
14
14
|
obligation_ids: ["repo_manifest", "file_disposition"],
|
|
15
15
|
description: "Create intake artifacts for repository discovery and disposition.",
|
|
16
16
|
},
|
|
17
|
+
{
|
|
18
|
+
id: "intent_checkpoint_executor",
|
|
19
|
+
kind: "deterministic",
|
|
20
|
+
obligation_ids: ["intent_checkpoint_current"],
|
|
21
|
+
description: "Write intent_checkpoint.json with confirmed scope and intent.",
|
|
22
|
+
},
|
|
17
23
|
{
|
|
18
24
|
id: "structure_executor",
|
|
19
25
|
kind: "deterministic",
|
|
@@ -112,9 +112,36 @@ export function runResultIngestionExecutor(bundle, results) {
|
|
|
112
112
|
excludeArtifacts: ["audit_tasks.json"],
|
|
113
113
|
});
|
|
114
114
|
const requeuePayload = buildRequeuePayload(updatedCoverageMatrix, selectiveDeepening.bundle.critical_flows, selectiveDeepening.bundle.flow_coverage, selectiveDeepening.bundle.external_analyzer_results);
|
|
115
|
+
// Fold pending requeue tasks into the dispatch task list so mandatory
|
|
116
|
+
// coverage gaps produce actual dispatch packets. Enrich with line-count
|
|
117
|
+
// hints and dedupe against existing tasks by task_id.
|
|
118
|
+
const deepenedTasks = selectiveDeepening.bundle.audit_tasks ?? [];
|
|
119
|
+
const lineIndex = lineIndexFromTasks(deepenedTasks);
|
|
120
|
+
const sizeIndex = sizeIndexFromManifest(selectiveDeepening.bundle.repo_manifest);
|
|
121
|
+
const existingTaskIds = new Set(deepenedTasks.map((t) => t.task_id));
|
|
122
|
+
const pendingRequeueTasks = requeuePayload.tasks
|
|
123
|
+
.filter((t) => t.status === "pending")
|
|
124
|
+
.filter((t) => !existingTaskIds.has(t.task_id))
|
|
125
|
+
.map((t) => ({
|
|
126
|
+
...t,
|
|
127
|
+
file_line_counts: Object.fromEntries(t.file_paths
|
|
128
|
+
.filter((p) => lineIndex[p] != null)
|
|
129
|
+
.map((p) => [p, lineIndex[p]])),
|
|
130
|
+
}));
|
|
131
|
+
const allDispatchTasks = [...deepenedTasks, ...pendingRequeueTasks];
|
|
115
132
|
const finalBundle = {
|
|
116
133
|
...selectiveDeepening.bundle,
|
|
117
134
|
requeue_tasks: requeuePayload.tasks,
|
|
135
|
+
audit_plan_metrics: buildAuditPlanMetrics(allDispatchTasks, {
|
|
136
|
+
graphBundle: selectiveDeepening.bundle.graph_bundle,
|
|
137
|
+
lineIndex,
|
|
138
|
+
sizeIndex,
|
|
139
|
+
}),
|
|
140
|
+
review_packets: buildReviewPackets(allDispatchTasks, {
|
|
141
|
+
graphBundle: selectiveDeepening.bundle.graph_bundle,
|
|
142
|
+
lineIndex,
|
|
143
|
+
sizeIndex,
|
|
144
|
+
}),
|
|
118
145
|
};
|
|
119
146
|
return {
|
|
120
147
|
updated: finalBundle,
|
|
@@ -125,7 +152,8 @@ export function runResultIngestionExecutor(bundle, results) {
|
|
|
125
152
|
...(runtimeValidationReport ? ["runtime_validation_report.json"] : []),
|
|
126
153
|
"audit_results.jsonl",
|
|
127
154
|
"audit_tasks.json",
|
|
128
|
-
|
|
155
|
+
"audit_plan_metrics.json",
|
|
156
|
+
"review_packets.json",
|
|
129
157
|
"requeue_tasks.json",
|
|
130
158
|
],
|
|
131
159
|
progress_summary: `Ingested ${results.length} audit result entries and refreshed dependent artifacts.` +
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { resolveAuditScope } from "./scope.js";
|
|
2
|
+
import { isAuditExcludedStatus } from "../extractors/disposition.js";
|
|
3
|
+
export async function runIntentCheckpointExecutor(bundle, root, since) {
|
|
4
|
+
const scope = resolveAuditScope({ root, since, bundle });
|
|
5
|
+
let filesInScope = 0;
|
|
6
|
+
if (scope.mode === "delta") {
|
|
7
|
+
filesInScope = scope.seed_files.length + scope.expanded_files.length;
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
// Count auditable files in disposition. Fall back to manifest or 0.
|
|
11
|
+
const auditableCount = bundle.file_disposition?.files.filter((file) => !isAuditExcludedStatus(file.status)).length ?? (bundle.repo_manifest?.files.length ?? 0);
|
|
12
|
+
filesInScope = auditableCount;
|
|
13
|
+
}
|
|
14
|
+
const intent = {
|
|
15
|
+
schema_version: "intent-checkpoint/v1",
|
|
16
|
+
confirmed_at: new Date().toISOString(),
|
|
17
|
+
scope_summary: `Root: ${root}${scope.since ? ` (since ${scope.since})` : ""}, files in scope: ${filesInScope}`,
|
|
18
|
+
intent_summary: scope.mode === "delta" ? `delta-audit since ${scope.since}` : "full-audit",
|
|
19
|
+
confirmed_by: "host",
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
updated: {
|
|
23
|
+
...bundle,
|
|
24
|
+
intent_checkpoint: intent,
|
|
25
|
+
},
|
|
26
|
+
artifacts_written: ["intent_checkpoint.json"],
|
|
27
|
+
progress_summary: `Recorded scope/intent checkpoint: ${intent.scope_summary} (${intent.intent_summary}).`,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lens selection resolver for operator-selected audit lenses.
|
|
3
|
+
*
|
|
4
|
+
* Accepts the `lenses.selected` array from session-config, validates it,
|
|
5
|
+
* de-duplicates, sorts with the canonical LENSES registry order, and always
|
|
6
|
+
* unions in the mandatory base set required for cross-perspective coverage.
|
|
7
|
+
*/
|
|
8
|
+
import { type Lens } from "@audit-tools/shared";
|
|
9
|
+
import type { ValidationIssue } from "@audit-tools/shared";
|
|
10
|
+
/**
|
|
11
|
+
* The mandatory base lenses that are always included regardless of the
|
|
12
|
+
* operator's selection. These are required for cross-perspective obligations
|
|
13
|
+
* that every audit must satisfy.
|
|
14
|
+
*/
|
|
15
|
+
export declare const MANDATORY_LENSES: readonly Lens[];
|
|
16
|
+
/**
|
|
17
|
+
* Validate the `lenses.selected` session-config value and return any errors.
|
|
18
|
+
* Returns an empty array when the value is undefined (meaning "all lenses").
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateLensSelection(value: unknown, path?: string): ValidationIssue[];
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the effective lens set from the operator-selected lenses.
|
|
23
|
+
*
|
|
24
|
+
* - When `selected` is undefined/null, returns all lenses (current behavior).
|
|
25
|
+
* - When `selected` is an array of valid lens names, unions in the mandatory
|
|
26
|
+
* base lenses, de-duplicates, and sorts to the canonical LENSES order.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveEffectiveLenses(selected: string[] | undefined | null): Lens[];
|
|
29
|
+
/** Returns true when the given lens is in the effective set. */
|
|
30
|
+
export declare function isLensEffective(lens: Lens, effectiveLenses: Lens[]): boolean;
|
|
31
|
+
/** Returns true when the given lens is in the mandatory base set. */
|
|
32
|
+
export declare function isMandatoryLens(lens: Lens): boolean;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lens selection resolver for operator-selected audit lenses.
|
|
3
|
+
*
|
|
4
|
+
* Accepts the `lenses.selected` array from session-config, validates it,
|
|
5
|
+
* de-duplicates, sorts with the canonical LENSES registry order, and always
|
|
6
|
+
* unions in the mandatory base set required for cross-perspective coverage.
|
|
7
|
+
*/
|
|
8
|
+
import { LENSES, VALID_LENSES } from "@audit-tools/shared";
|
|
9
|
+
import { pushValidationIssue } from "@audit-tools/shared";
|
|
10
|
+
/**
|
|
11
|
+
* The mandatory base lenses that are always included regardless of the
|
|
12
|
+
* operator's selection. These are required for cross-perspective obligations
|
|
13
|
+
* that every audit must satisfy.
|
|
14
|
+
*/
|
|
15
|
+
export const MANDATORY_LENSES = [
|
|
16
|
+
"security",
|
|
17
|
+
"correctness",
|
|
18
|
+
"reliability",
|
|
19
|
+
"data_integrity",
|
|
20
|
+
];
|
|
21
|
+
const MANDATORY_LENS_SET = new Set(MANDATORY_LENSES);
|
|
22
|
+
/**
|
|
23
|
+
* Validate the `lenses.selected` session-config value and return any errors.
|
|
24
|
+
* Returns an empty array when the value is undefined (meaning "all lenses").
|
|
25
|
+
*/
|
|
26
|
+
export function validateLensSelection(value, path = "lenses.selected") {
|
|
27
|
+
const issues = [];
|
|
28
|
+
if (value === undefined || value === null) {
|
|
29
|
+
return issues;
|
|
30
|
+
}
|
|
31
|
+
if (!Array.isArray(value)) {
|
|
32
|
+
pushValidationIssue(issues, path, `${path} must be an array of lens names.`);
|
|
33
|
+
return issues;
|
|
34
|
+
}
|
|
35
|
+
for (const [index, item] of value.entries()) {
|
|
36
|
+
if (typeof item !== "string" || !VALID_LENSES.has(item)) {
|
|
37
|
+
pushValidationIssue(issues, `${path}[${index}]`, `${path}[${index}] "${String(item)}" is not a valid lens. Valid lenses: ${[...LENSES].join(", ")}.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return issues;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the effective lens set from the operator-selected lenses.
|
|
44
|
+
*
|
|
45
|
+
* - When `selected` is undefined/null, returns all lenses (current behavior).
|
|
46
|
+
* - When `selected` is an array of valid lens names, unions in the mandatory
|
|
47
|
+
* base lenses, de-duplicates, and sorts to the canonical LENSES order.
|
|
48
|
+
*/
|
|
49
|
+
export function resolveEffectiveLenses(selected) {
|
|
50
|
+
if (selected === undefined || selected === null) {
|
|
51
|
+
// Default: all lenses.
|
|
52
|
+
return [...LENSES];
|
|
53
|
+
}
|
|
54
|
+
// Filter to valid lenses only (invalid ones are caught by validation; tolerate
|
|
55
|
+
// them here so the runtime path doesn't throw on a misconfigured file).
|
|
56
|
+
const validSelected = selected.filter((s) => VALID_LENSES.has(s));
|
|
57
|
+
// Union with mandatory base lenses.
|
|
58
|
+
const combined = new Set([...validSelected, ...MANDATORY_LENSES]);
|
|
59
|
+
// Sort to canonical registry order.
|
|
60
|
+
return LENSES.filter((lens) => combined.has(lens));
|
|
61
|
+
}
|
|
62
|
+
/** Returns true when the given lens is in the effective set. */
|
|
63
|
+
export function isLensEffective(lens, effectiveLenses) {
|
|
64
|
+
return effectiveLenses.includes(lens);
|
|
65
|
+
}
|
|
66
|
+
/** Returns true when the given lens is in the mandatory base set. */
|
|
67
|
+
export function isMandatoryLens(lens) {
|
|
68
|
+
return MANDATORY_LENS_SET.has(lens);
|
|
69
|
+
}
|
|
@@ -44,17 +44,32 @@ export async function runPlanningExecutor(bundle, root, lineIndex = {}, sizeInde
|
|
|
44
44
|
...task,
|
|
45
45
|
status: task.status ?? "pending",
|
|
46
46
|
}));
|
|
47
|
-
const
|
|
47
|
+
const requeuePayload = buildRequeuePayload(coverage, bundle.critical_flows, flowCoverage, externalAnalyzerResults);
|
|
48
|
+
// Fold pending requeue tasks into the dispatch task list so mandatory coverage
|
|
49
|
+
// gaps produce actual dispatch packets. Enrich with line-count hints from the
|
|
50
|
+
// index and dedupe against existing audit tasks by task_id so each task
|
|
51
|
+
// appears exactly once in the merged list.
|
|
52
|
+
const existingTaskIds = new Set(taggedAuditTasks.map((t) => t.task_id));
|
|
53
|
+
const pendingRequeueTasks = requeuePayload.tasks
|
|
54
|
+
.filter((t) => t.status === "pending")
|
|
55
|
+
.filter((t) => !existingTaskIds.has(t.task_id))
|
|
56
|
+
.map((t) => ({
|
|
57
|
+
...t,
|
|
58
|
+
file_line_counts: Object.fromEntries(t.file_paths
|
|
59
|
+
.filter((p) => lineIndex[p] != null)
|
|
60
|
+
.map((p) => [p, lineIndex[p]])),
|
|
61
|
+
}));
|
|
62
|
+
const allDispatchTasks = [...taggedAuditTasks, ...pendingRequeueTasks];
|
|
63
|
+
const reviewPackets = buildReviewPackets(allDispatchTasks, {
|
|
48
64
|
graphBundle: bundle.graph_bundle,
|
|
49
65
|
lineIndex,
|
|
50
66
|
sizeIndex: resolvedSizeIndex,
|
|
51
67
|
});
|
|
52
|
-
const auditPlanMetrics = buildAuditPlanMetrics(
|
|
68
|
+
const auditPlanMetrics = buildAuditPlanMetrics(allDispatchTasks, {
|
|
53
69
|
graphBundle: bundle.graph_bundle,
|
|
54
70
|
lineIndex,
|
|
55
71
|
sizeIndex: resolvedSizeIndex,
|
|
56
72
|
});
|
|
57
|
-
const requeuePayload = buildRequeuePayload(coverage, bundle.critical_flows, flowCoverage, externalAnalyzerResults);
|
|
58
73
|
const scopeSummary = resolvedScope.mode === "delta"
|
|
59
74
|
? ` Delta scope since ${resolvedScope.since}: ${resolvedScope.seed_files.length} changed file(s) + ${resolvedScope.expanded_files.length} graph neighbour(s) queued; a full audit is advised before release.`
|
|
60
75
|
: "";
|
package/dist/orchestrator.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export interface TaskBuildOptions {
|
|
|
3
3
|
pass_prefix?: string;
|
|
4
4
|
limit_lenses?: Lens[];
|
|
5
5
|
}
|
|
6
|
-
export declare function buildAuditTasks(unitManifest: UnitManifest, options?: TaskBuildOptions): AuditTask[];
|
|
6
|
+
export declare function buildAuditTasks(unitManifest: UnitManifest, options?: TaskBuildOptions | string): AuditTask[];
|
package/dist/orchestrator.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isLens } from "./types.js";
|
|
2
|
+
import { coerceJsonObjectArg } from "@audit-tools/shared";
|
|
2
3
|
const DEFAULT_LENS_ORDER = [
|
|
3
4
|
"correctness",
|
|
4
5
|
"architecture",
|
|
@@ -43,10 +44,8 @@ function assertUnitManifest(value) {
|
|
|
43
44
|
assertLensArray(unit.required_lenses, `${label}.required_lenses`);
|
|
44
45
|
});
|
|
45
46
|
}
|
|
46
|
-
function normalizedOptions(
|
|
47
|
-
|
|
48
|
-
throw new TypeError("buildAuditTasks options must be an object.");
|
|
49
|
-
}
|
|
47
|
+
function normalizedOptions(rawOptions) {
|
|
48
|
+
const options = coerceJsonObjectArg(rawOptions, "buildAuditTasks options");
|
|
50
49
|
if (options.pass_prefix !== undefined && typeof options.pass_prefix !== "string") {
|
|
51
50
|
throw new TypeError("buildAuditTasks options.pass_prefix must be a string.");
|
|
52
51
|
}
|
|
@@ -23,6 +23,8 @@ export function renderWorkerPrompt(task) {
|
|
|
23
23
|
`${task.artifacts_dir}/audit_tasks.json`;
|
|
24
24
|
const lines = [
|
|
25
25
|
`Audit run: ${task.run_id}`,
|
|
26
|
+
`Repository root: ${task.repo_root}`,
|
|
27
|
+
"Set the shell/tool workdir to the repository root before executing worker_command.",
|
|
26
28
|
`Read: ${tasksPath}`,
|
|
27
29
|
"Scope: review only the tasks listed in the Read file. Do not add tasks,",
|
|
28
30
|
"edit source files, remediate findings, run unrelated audits, or write result_path.",
|
|
@@ -40,6 +42,8 @@ export function renderWorkerPrompt(task) {
|
|
|
40
42
|
" and include verification {verified, needs_followup, concerns, coverage_concerns,",
|
|
41
43
|
" confidence_concerns, followup_tasks}.",
|
|
42
44
|
"Constraint: line_end must not exceed total_lines for that file.",
|
|
45
|
+
"Windows PowerShell: do not pipe an inline foreach statement directly into ConvertTo-Json.",
|
|
46
|
+
"Assign the foreach output to a variable first, then pipe that variable to ConvertTo-Json.",
|
|
43
47
|
`Write only the JSON array of AuditResult objects to: ${task.audit_results_path}`,
|
|
44
48
|
];
|
|
45
49
|
if (usesDeferredWorkerCommand(task)) {
|
|
@@ -56,6 +60,8 @@ export function renderWorkerPrompt(task) {
|
|
|
56
60
|
return [
|
|
57
61
|
`Task: ${task.run_id}`,
|
|
58
62
|
`Executor: ${task.preferred_executor}`,
|
|
63
|
+
`Repository root: ${task.repo_root}`,
|
|
64
|
+
"Set the shell/tool workdir to the repository root before executing worker_command.",
|
|
59
65
|
"Execute worker_command from task.json exactly.",
|
|
60
66
|
`Command: ${commandArgv}`,
|
|
61
67
|
"Write result to: " + task.result_path,
|
package/dist/quota/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ResolvedLimits as _ResolvedLimits, LimitConfidence as _LimitConfidence, LimitSource as _LimitSource, HostConcurrencyLimit as _HostConcurrencyLimit, QuotaUsageSnapshot as _QuotaUsageSnapshot, BackoffState as _BackoffState } from "@audit-tools/shared";
|
|
2
|
-
export { resolveLimits, lookupKnownModel, classifyProvider, readQuotaState, writeQuotaState, computeMaxSafeConcurrency, recordWaveOutcome, getQuotaStatePath, decayWeight, applyDecayToEntry, computeBackoffCooldownMs, computeBackoffFailureWeight, computeRampUpConcurrency, setQuotaStateDir, detectRateLimitError, computeCooldownUntil, acquireLock, releaseLock, withFileLock, FileLockTimeoutError, runSlidingWindow, LearnedQuotaSource, CompositeQuotaSource, GenericErrorParser, ClaudeCodeErrorParser, getErrorParserForProvider, } from "@audit-tools/shared";
|
|
3
|
-
export type { LimitResolutionResult, ResolveLimitsOptions, ProviderType, ResolvedLimits, LimitSource, LimitConfidence, HostConcurrencyLimit, HostConcurrencyLimitSource, QuotaState, QuotaStateEntry, ConcurrencyBucket, WaveSchedule, BackoffState, ObservedWaveOutcome, RateLimitDetectionResult, SlidingWindowResult, QuotaSource, QuotaUsageSnapshot, ErrorParser, } from "@audit-tools/shared";
|
|
1
|
+
import type { ResolvedLimits as _ResolvedLimits, LimitConfidence as _LimitConfidence, LimitSource as _LimitSource, HostConcurrencyLimit as _HostConcurrencyLimit, QuotaUsageSnapshot as _QuotaUsageSnapshot, BackoffState as _BackoffState, WaveBindingCap as _WaveBindingCap, DispatchCapacityPoolSummary as _DispatchCapacityPoolSummary } from "@audit-tools/shared";
|
|
2
|
+
export { resolveLimits, lookupKnownModel, classifyProvider, readQuotaState, writeQuotaState, computeMaxSafeConcurrency, recordWaveOutcome, getQuotaStatePath, decayWeight, applyDecayToEntry, computeBackoffCooldownMs, computeBackoffFailureWeight, computeRampUpConcurrency, setQuotaStateDir, detectRateLimitError, computeCooldownUntil, acquireLock, releaseLock, withFileLock, FileLockTimeoutError, runSlidingWindow, LearnedQuotaSource, CompositeQuotaSource, GenericErrorParser, ClaudeCodeErrorParser, getErrorParserForProvider, summarizeDispatchCapacityPools, } from "@audit-tools/shared";
|
|
3
|
+
export type { LimitResolutionResult, ResolveLimitsOptions, ProviderType, ResolvedLimits, LimitSource, LimitConfidence, HostConcurrencyLimit, HostConcurrencyLimitSource, QuotaState, QuotaStateEntry, ConcurrencyBucket, WaveSchedule, BackoffState, ObservedWaveOutcome, RateLimitDetectionResult, SlidingWindowResult, QuotaSource, QuotaUsageSnapshot, ErrorParser, WaveBindingCap, DispatchCapacityPoolSummary, } from "@audit-tools/shared";
|
|
4
4
|
export { scheduleWave, buildProviderModelKey, resolveHostModel } from "@audit-tools/shared";
|
|
5
5
|
export type { ScheduleWaveOptions } from "@audit-tools/shared";
|
|
6
6
|
export { computeDispatchCapacity } from "@audit-tools/shared";
|
|
@@ -25,6 +25,8 @@ export interface DispatchQuota {
|
|
|
25
25
|
wave_size: number;
|
|
26
26
|
estimated_wave_tokens: number;
|
|
27
27
|
cooldown_until: string | null;
|
|
28
|
+
binding_cap?: _WaveBindingCap;
|
|
29
|
+
capacity_pools?: _DispatchCapacityPoolSummary[];
|
|
28
30
|
quota_source_snapshot?: _QuotaUsageSnapshot | null;
|
|
29
31
|
backoff_state?: _BackoffState | null;
|
|
30
32
|
}
|
package/dist/quota/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Re-exported from @audit-tools/shared
|
|
2
|
-
export { resolveLimits, lookupKnownModel, classifyProvider, readQuotaState, writeQuotaState, computeMaxSafeConcurrency, recordWaveOutcome, getQuotaStatePath, decayWeight, applyDecayToEntry, computeBackoffCooldownMs, computeBackoffFailureWeight, computeRampUpConcurrency, setQuotaStateDir, detectRateLimitError, computeCooldownUntil, acquireLock, releaseLock, withFileLock, FileLockTimeoutError, runSlidingWindow, LearnedQuotaSource, CompositeQuotaSource, GenericErrorParser, ClaudeCodeErrorParser, getErrorParserForProvider, } from "@audit-tools/shared";
|
|
2
|
+
export { resolveLimits, lookupKnownModel, classifyProvider, readQuotaState, writeQuotaState, computeMaxSafeConcurrency, recordWaveOutcome, getQuotaStatePath, decayWeight, applyDecayToEntry, computeBackoffCooldownMs, computeBackoffFailureWeight, computeRampUpConcurrency, setQuotaStateDir, detectRateLimitError, computeCooldownUntil, acquireLock, releaseLock, withFileLock, FileLockTimeoutError, runSlidingWindow, LearnedQuotaSource, CompositeQuotaSource, GenericErrorParser, ClaudeCodeErrorParser, getErrorParserForProvider, summarizeDispatchCapacityPools, } from "@audit-tools/shared";
|
|
3
3
|
// Wave scheduler now lives in @audit-tools/shared (single source of truth for
|
|
4
4
|
// both orchestrators). Auditor passes its discovered-limits via the structural
|
|
5
5
|
// DiscoveredRateLimitsInput the shared scheduler accepts.
|
|
@@ -73,3 +73,13 @@ export interface RenderAuditReportOptions {
|
|
|
73
73
|
scope?: AuditScopeManifest;
|
|
74
74
|
}
|
|
75
75
|
export declare function renderAuditReportMarkdown(report: RenderableAuditReport, options?: RenderAuditReportOptions): string;
|
|
76
|
+
/**
|
|
77
|
+
* Re-derive the summary fields that can be computed from the existing findings
|
|
78
|
+
* and work_blocks, bump the contract_version to the current constant, and leave
|
|
79
|
+
* upstream-derived fields that cannot be reconstructed (audited/excluded counts,
|
|
80
|
+
* runtime validation breakdown) untouched.
|
|
81
|
+
*
|
|
82
|
+
* Safe to call on already-promoted `audit-findings.json` files without access to
|
|
83
|
+
* the pruned `.audit-tools/audit` working-bundle intermediates.
|
|
84
|
+
*/
|
|
85
|
+
export declare function normalizeExistingFindingsReport(report: AuditFindingsReport): AuditFindingsReport;
|
|
@@ -200,6 +200,7 @@ export function renderAuditReportMarkdown(report, options = {}) {
|
|
|
200
200
|
lines.push(`- Severity: ${finding.severity}`);
|
|
201
201
|
lines.push(`- Confidence: ${finding.confidence}`);
|
|
202
202
|
lines.push(`- Lens: ${finding.lens}`);
|
|
203
|
+
lines.push(`- Category: ${finding.category}`);
|
|
203
204
|
if (finding.theme_id) {
|
|
204
205
|
lines.push(`- Theme: ${finding.theme_id}`);
|
|
205
206
|
}
|
|
@@ -236,3 +237,25 @@ export function renderAuditReportMarkdown(report, options = {}) {
|
|
|
236
237
|
lines.push("");
|
|
237
238
|
return lines.join("\n");
|
|
238
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Re-derive the summary fields that can be computed from the existing findings
|
|
242
|
+
* and work_blocks, bump the contract_version to the current constant, and leave
|
|
243
|
+
* upstream-derived fields that cannot be reconstructed (audited/excluded counts,
|
|
244
|
+
* runtime validation breakdown) untouched.
|
|
245
|
+
*
|
|
246
|
+
* Safe to call on already-promoted `audit-findings.json` files without access to
|
|
247
|
+
* the pruned `.audit-tools/audit` working-bundle intermediates.
|
|
248
|
+
*/
|
|
249
|
+
export function normalizeExistingFindingsReport(report) {
|
|
250
|
+
return {
|
|
251
|
+
...report,
|
|
252
|
+
contract_version: AUDIT_FINDINGS_CONTRACT_VERSION,
|
|
253
|
+
summary: {
|
|
254
|
+
...report.summary,
|
|
255
|
+
finding_count: report.findings.length,
|
|
256
|
+
work_block_count: report.work_blocks.length,
|
|
257
|
+
severity_breakdown: severityBreakdown(report.findings),
|
|
258
|
+
lens_breakdown: lensBreakdown(report.findings),
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
@@ -4,7 +4,7 @@ function summarizeFinding(finding) {
|
|
|
4
4
|
.map((file) => file.path)
|
|
5
5
|
.slice(0, 4)
|
|
6
6
|
.join(", ");
|
|
7
|
-
return `- ${finding.id} [${finding.severity}/${finding.lens}] ${finding.title} — ${finding.summary}${files ? ` (files: ${files})` : ""}`;
|
|
7
|
+
return `- ${finding.id} [${finding.severity}/${finding.lens}/${finding.category}] ${finding.title} — ${finding.summary}${files ? ` (files: ${files})` : ""}`;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* Prompt for the optional synthesis-narrative pass. The host groups the
|
|
@@ -27,6 +27,8 @@ export function renderSynthesisNarrativePrompt(report) {
|
|
|
27
27
|
"",
|
|
28
28
|
"Do not re-audit the code, change severities, or invent new findings. Use only the findings below; reference them by their exact `id`.",
|
|
29
29
|
"",
|
|
30
|
+
"When categories distinguish observational contract assessment findings from conceptual design critique findings, keep that distinction visible in themes and top risks instead of flattening them into one architecture bucket.",
|
|
31
|
+
"",
|
|
30
32
|
"## Summary",
|
|
31
33
|
"",
|
|
32
34
|
`- Findings: ${report.summary.finding_count}`,
|
package/docs/development.md
CHANGED
|
@@ -23,9 +23,15 @@ once the work ships.
|
|
|
23
23
|
npm install
|
|
24
24
|
npm run check
|
|
25
25
|
npm test
|
|
26
|
+
npm run test:single -- tests/next-step.test.mjs
|
|
26
27
|
npm run verify:release
|
|
27
28
|
```
|
|
28
29
|
|
|
30
|
+
Run `npm install` from the repository root before running build, check, test, or
|
|
31
|
+
package-scoped workflows in a fresh clone or git worktree. Missing
|
|
32
|
+
`node_modules` can surface as misleading `@audit-tools/shared` export or type
|
|
33
|
+
errors because dependents may resolve stale compiled `dist/` output.
|
|
34
|
+
|
|
29
35
|
The test suite is intentionally contract-heavy. Update tests when changing
|
|
30
36
|
schema shape, prompt contracts, dispatch behavior, installer output, or release
|
|
31
37
|
workflow semantics.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auditor-lambda",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Portable hybrid code-auditing framework for arbitrary repositories.",
|
|
6
6
|
"type": "module",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"build": "tsc -p tsconfig.json",
|
|
25
25
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
26
26
|
"test": "npm run build && node --import tsx/esm --test tests/*.test.mjs",
|
|
27
|
+
"test:single": "npm run build && node --import tsx/esm --test",
|
|
27
28
|
"release:patch": "node scripts/release-and-publish.mjs patch --bump-only",
|
|
28
29
|
"release:minor": "node scripts/release-and-publish.mjs minor --bump-only",
|
|
29
30
|
"release:major": "node scripts/release-and-publish.mjs major --bump-only",
|
|
@@ -86,7 +86,20 @@
|
|
|
86
86
|
"reproduction": { "type": "array", "minItems": 1, "items": { "type": "string" } },
|
|
87
87
|
"systemic": { "type": "boolean" },
|
|
88
88
|
"related_findings": { "type": "array", "minItems": 1, "items": { "type": "string" } },
|
|
89
|
-
"theme_id": { "type": "string" }
|
|
89
|
+
"theme_id": { "type": "string" },
|
|
90
|
+
"contract_goal_id": { "type": "string" },
|
|
91
|
+
"contract_obligation_ids": {
|
|
92
|
+
"type": "array",
|
|
93
|
+
"items": { "type": "string" }
|
|
94
|
+
},
|
|
95
|
+
"verification_obligation_ids": {
|
|
96
|
+
"type": "array",
|
|
97
|
+
"items": { "type": "string" }
|
|
98
|
+
},
|
|
99
|
+
"targeted_commands": {
|
|
100
|
+
"type": "array",
|
|
101
|
+
"items": { "type": "string" }
|
|
102
|
+
}
|
|
90
103
|
},
|
|
91
104
|
"additionalProperties": false
|
|
92
105
|
}
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"source": {
|
|
58
58
|
"type": "string",
|
|
59
|
-
"enum": ["explicit_config", "cli_flags", "known_metadata", "learned", "default"],
|
|
59
|
+
"enum": ["explicit_config", "cli_flags", "known_metadata", "provider_default", "learned", "default"],
|
|
60
60
|
"description": "Where the resolved limits came from."
|
|
61
61
|
},
|
|
62
62
|
"host_concurrency_limit": {
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
},
|
|
76
76
|
"source": {
|
|
77
77
|
"type": "string",
|
|
78
|
-
"enum": ["cli_flags", "session_config", "environment"]
|
|
78
|
+
"enum": ["cli_flags", "host_reported", "session_config", "environment"]
|
|
79
79
|
},
|
|
80
80
|
"description": {
|
|
81
81
|
"type": "string",
|
|
@@ -98,6 +98,90 @@
|
|
|
98
98
|
"format": "date-time",
|
|
99
99
|
"description": "If non-null, the host should wait until this timestamp before launching the next wave."
|
|
100
100
|
},
|
|
101
|
+
"binding_cap": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"enum": ["rpm", "tpm", "learned", "fallback", "first_contact", "cooldown", "host_concurrency", "none"],
|
|
104
|
+
"description": "The most constraining cap across capacity pools."
|
|
105
|
+
},
|
|
106
|
+
"capacity_pools": {
|
|
107
|
+
"type": "array",
|
|
108
|
+
"description": "Per-pool allocation summaries when dispatch capacity was computed across one or more pools.",
|
|
109
|
+
"items": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"required": [
|
|
112
|
+
"pool_id",
|
|
113
|
+
"slots",
|
|
114
|
+
"model",
|
|
115
|
+
"confidence",
|
|
116
|
+
"source",
|
|
117
|
+
"resolved_limits",
|
|
118
|
+
"host_concurrency_limit",
|
|
119
|
+
"cooldown_until",
|
|
120
|
+
"estimated_wave_tokens",
|
|
121
|
+
"binding_cap"
|
|
122
|
+
],
|
|
123
|
+
"additionalProperties": false,
|
|
124
|
+
"properties": {
|
|
125
|
+
"pool_id": { "type": "string", "minLength": 1 },
|
|
126
|
+
"slots": { "type": "integer", "minimum": 1 },
|
|
127
|
+
"model": { "type": ["string", "null"] },
|
|
128
|
+
"confidence": { "type": "string", "enum": ["high", "medium", "low"] },
|
|
129
|
+
"source": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"enum": ["explicit_config", "cli_flags", "known_metadata", "provider_default", "learned", "default"]
|
|
132
|
+
},
|
|
133
|
+
"resolved_limits": {
|
|
134
|
+
"type": "object",
|
|
135
|
+
"required": [
|
|
136
|
+
"context_tokens",
|
|
137
|
+
"output_tokens",
|
|
138
|
+
"requests_per_minute",
|
|
139
|
+
"input_tokens_per_minute",
|
|
140
|
+
"output_tokens_per_minute"
|
|
141
|
+
],
|
|
142
|
+
"additionalProperties": false,
|
|
143
|
+
"properties": {
|
|
144
|
+
"context_tokens": { "type": "integer", "minimum": 1 },
|
|
145
|
+
"output_tokens": { "type": "integer", "minimum": 1 },
|
|
146
|
+
"requests_per_minute": { "type": ["integer", "null"], "minimum": 1 },
|
|
147
|
+
"input_tokens_per_minute": { "type": ["integer", "null"], "minimum": 1 },
|
|
148
|
+
"output_tokens_per_minute": { "type": ["integer", "null"], "minimum": 1 }
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"host_concurrency_limit": {
|
|
152
|
+
"type": ["object", "null"],
|
|
153
|
+
"required": ["active_subagents", "source", "description"],
|
|
154
|
+
"additionalProperties": false,
|
|
155
|
+
"properties": {
|
|
156
|
+
"active_subagents": { "type": "integer", "minimum": 1 },
|
|
157
|
+
"source": {
|
|
158
|
+
"type": "string",
|
|
159
|
+
"enum": ["cli_flags", "host_reported", "session_config", "environment"]
|
|
160
|
+
},
|
|
161
|
+
"description": { "type": "string", "minLength": 1 }
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"cooldown_until": { "type": ["string", "null"], "format": "date-time" },
|
|
165
|
+
"estimated_wave_tokens": { "type": "integer", "minimum": 0 },
|
|
166
|
+
"binding_cap": {
|
|
167
|
+
"type": "string",
|
|
168
|
+
"enum": ["rpm", "tpm", "learned", "fallback", "first_contact", "cooldown", "host_concurrency", "none"]
|
|
169
|
+
},
|
|
170
|
+
"quota_source_snapshot": {
|
|
171
|
+
"type": ["object", "null"],
|
|
172
|
+
"additionalProperties": false,
|
|
173
|
+
"properties": {
|
|
174
|
+
"remaining_pct": { "type": ["number", "null"] },
|
|
175
|
+
"reset_at": { "type": ["string", "null"], "format": "date-time" },
|
|
176
|
+
"requests_remaining": { "type": ["integer", "null"] },
|
|
177
|
+
"tokens_remaining": { "type": ["integer", "null"] },
|
|
178
|
+
"captured_at": { "type": "string", "format": "date-time" },
|
|
179
|
+
"source": { "type": "string" }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
101
185
|
"quota_source_snapshot": {
|
|
102
186
|
"type": ["object", "null"],
|
|
103
187
|
"description": "Real-time usage snapshot from a QuotaSource, if available.",
|
|
@@ -59,7 +59,20 @@
|
|
|
59
59
|
"minItems": 1,
|
|
60
60
|
"items": { "type": "string" }
|
|
61
61
|
},
|
|
62
|
-
"theme_id": { "type": "string" }
|
|
62
|
+
"theme_id": { "type": "string" },
|
|
63
|
+
"contract_goal_id": { "type": "string" },
|
|
64
|
+
"contract_obligation_ids": {
|
|
65
|
+
"type": "array",
|
|
66
|
+
"items": { "type": "string" }
|
|
67
|
+
},
|
|
68
|
+
"verification_obligation_ids": {
|
|
69
|
+
"type": "array",
|
|
70
|
+
"items": { "type": "string" }
|
|
71
|
+
},
|
|
72
|
+
"targeted_commands": {
|
|
73
|
+
"type": "array",
|
|
74
|
+
"items": { "type": "string" }
|
|
75
|
+
}
|
|
63
76
|
},
|
|
64
77
|
"additionalProperties": false
|
|
65
78
|
}
|