create-agdf 0.4.9 → 0.6.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.
Files changed (43) hide show
  1. package/README.md +9 -7
  2. package/bin/create-agdf.js +284 -152
  3. package/generated/.agdf/control/README.md +15 -3
  4. package/generated/.agdf/control/templates/RUN_STATE.md +48 -0
  5. package/generated/.github/skills/agdf-brownfield-analysis/SKILL.md +3 -3
  6. package/generated/.github/skills/agdf-delivery-path-search/SKILL.md +14 -0
  7. package/generated/.github/skills/agdf-gate-check/SKILL.md +15 -10
  8. package/generated/.github/skills/agdf-release-or/SKILL.md +1 -1
  9. package/generated/.github/skills/agdf-runtime-contract.md +24 -8
  10. package/generated/.opencode/agdf-runtime-contract.md +24 -8
  11. package/generated/.opencode/agents/agdf-brownfield-analysis.md +2 -2
  12. package/generated/.opencode/agents/agdf-delivery-path-search.md +14 -0
  13. package/generated/.opencode/agents/agdf-gate-check.md +15 -10
  14. package/generated/.opencode/agents/agdf-release-or.md +1 -1
  15. package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
  16. package/generated/plugins/agdf/control/README.md +15 -3
  17. package/generated/plugins/agdf/control/templates/RUN_STATE.md +48 -0
  18. package/generated/plugins/agdf/hooks/session-start.sh +2 -0
  19. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  20. package/generated/plugins/agdf/meta/agdf-runtime-contract.md +24 -8
  21. package/generated/plugins/agdf/skills/brownfield-analysis/SKILL.md +3 -3
  22. package/generated/plugins/agdf/skills/delivery-path-search/SKILL.md +14 -0
  23. package/generated/plugins/agdf/skills/gate-check/SKILL.md +15 -10
  24. package/generated/plugins/agdf/skills/release-or/SKILL.md +1 -1
  25. package/lib/control-state/aggregate.js +18 -0
  26. package/lib/control-state/index.js +6 -0
  27. package/lib/control-state/legacy-migration.js +94 -0
  28. package/lib/control-state/run-state-parser.js +196 -0
  29. package/lib/control-state/run-state-repository.js +68 -0
  30. package/lib/control-state/run-state-resolver.js +45 -0
  31. package/lib/control-state/run-state-writer.js +96 -0
  32. package/lib/delivery-path-search/candidate-policy.js +67 -2
  33. package/lib/delivery-path-search/contracts.js +127 -0
  34. package/lib/delivery-path-search/evaluators/claude.js +2 -16
  35. package/lib/delivery-path-search/evaluators/codex.js +2 -16
  36. package/lib/delivery-path-search/generators/claude.js +40 -0
  37. package/lib/delivery-path-search/generators/codex.js +38 -0
  38. package/lib/delivery-path-search/generators/protocol.js +12 -0
  39. package/lib/delivery-path-search/persistence.js +5 -0
  40. package/lib/delivery-path-search/search-engine.js +64 -2
  41. package/lib/delivery-path-search/state-adapter.js +119 -16
  42. package/lib/delivery-path-search/transports/read-only-guard.js +43 -0
  43. package/package.json +4 -2
@@ -11,8 +11,20 @@ import { runDeliveryPathSearch } from "../lib/delivery-path-search/search-engine
11
11
  import { codexEvaluator } from "../lib/delivery-path-search/evaluators/codex.js";
12
12
  import { claudeEvaluator } from "../lib/delivery-path-search/evaluators/claude.js";
13
13
  import { fixtureEvaluator } from "../lib/delivery-path-search/evaluators/protocol.js";
14
+ import { codexGenerator } from "../lib/delivery-path-search/generators/codex.js";
15
+ import { claudeGenerator } from "../lib/delivery-path-search/generators/claude.js";
16
+ import { fixtureGenerator } from "../lib/delivery-path-search/generators/protocol.js";
14
17
  import { enforcementForSurface } from "../lib/delivery-path-search/surfaces/capabilities.js";
15
18
  import { persistSearchResult } from "../lib/delivery-path-search/persistence.js";
19
+ import {
20
+ aggregate,
21
+ createRun,
22
+ resolveRuns,
23
+ writeLegacyProjection,
24
+ migrateLegacy,
25
+ parseControlState,
26
+ verifyLegacyProjection,
27
+ } from "../lib/control-state/index.js";
16
28
 
17
29
  const __dirname = dirname(fileURLToPath(import.meta.url));
18
30
  const packageRoot = resolve(__dirname, "..");
@@ -22,7 +34,7 @@ const pluginDefinition = JSON.parse(readFileSync(pluginDefinitionPath, "utf8"));
22
34
  const pluginInstallCommand = "npx --yes @agdf/cli@latest claude";
23
35
  const npmCommand = process.platform === "win32" ? process.execPath : "npm";
24
36
  const npmPrefixArgs = process.platform === "win32" ? [join(dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js")] : [];
25
- const allowedTargets = new Set(["codex", "codex-repo", "claude", "copilot", "opencode", "opencode-status", "opencode-repo", "both", "init", "config", "doctor", "gate-check", "delivery-map", "delivery-path-search"]);
37
+ const allowedTargets = new Set(["codex", "codex-repo", "claude", "copilot", "opencode", "opencode-status", "opencode-repo", "both", "init", "config", "doctor", "gate-check", "delivery-map", "delivery-path-search", "run-create", "run-migrate", "run-render-legacy"]);
26
38
  const agdfFragmentPath = "AGENTS.agdf.md";
27
39
  const openCodeConfigFragmentPath = "opencode.agdf.json";
28
40
  const userGateOrder = ["UR", "PRD", "SD", "TP", "QA", "UAT"];
@@ -73,6 +85,7 @@ const codexPluginFiles = [
73
85
  join("plugins", "agdf", ".codex-plugin", "plugin.json"),
74
86
  join("plugins", "agdf", "control", "README.md"),
75
87
  join("plugins", "agdf", "control", "templates", "AGDF_RUN.md"),
88
+ join("plugins", "agdf", "control", "templates", "RUN_STATE.md"),
76
89
  join("plugins", "agdf", "control", "templates", "MASTER_BACKLOG.md"),
77
90
  join("plugins", "agdf", "control", "templates", "SOT_REGISTRY.md"),
78
91
  join("plugins", "agdf", "control", "templates", "CONTEXT_GRAPH.md"),
@@ -96,6 +109,7 @@ const codexPluginFiles = [
96
109
  const controlFiles = [
97
110
  join(".agdf", "control", "README.md"),
98
111
  join(".agdf", "control", "templates", "AGDF_RUN.md"),
112
+ join(".agdf", "control", "templates", "RUN_STATE.md"),
99
113
  join(".agdf", "control", "templates", "MASTER_BACKLOG.md"),
100
114
  join(".agdf", "control", "templates", "SOT_REGISTRY.md"),
101
115
  join(".agdf", "control", "templates", "CONTEXT_GRAPH.md"),
@@ -177,6 +191,9 @@ Preferred AGDF CLI:
177
191
  npx --yes @agdf/cli@latest gate-check --json
178
192
  npx --yes @agdf/cli@latest delivery-path-search --surface codex --json
179
193
  npx --yes @agdf/cli@latest delivery-path-search --surface claude --json
194
+ npx --yes @agdf/cli@latest run-create --run <run_id>
195
+ npx --yes @agdf/cli@latest run-migrate [--run <run_id>]
196
+ npx --yes @agdf/cli@latest run-render-legacy --run <run_id>
180
197
 
181
198
  Scaffold-compatible npm create usage:
182
199
  npm create agdf@latest -- codex
@@ -207,12 +224,21 @@ Options:
207
224
  --lang <de|en> Alias for --language
208
225
  --json Print machine-readable command output as JSON
209
226
  --status-card Print compact gate-check status-card output for interactive use
227
+ --run <run_id> Select one canonical run
228
+ --all-active Evaluate every active run (doctor and delivery-map only)
210
229
  --surface <codex|claude|copilot|opencode|generic>
211
230
  Declare the active Delivery Path Search surface
212
231
  --fixture <path>
213
232
  Use deterministic evaluator/candidate fixtures instead of a live evaluator
214
233
  --persist Persist the redacted Delivery Path Search result under the current scope
215
234
  --model <id> Optional Codex evaluator model
235
+ --generate-candidates
236
+ Add one bounded AI-native candidate-generation call before evaluation
237
+ --generator-model <id>
238
+ Optional candidate-generator model override
239
+ --max-generated-candidates <1-5>
240
+ --generation-timeout-ms <1-30000>
241
+ --generation-cost-units <1-5>
216
242
  --help Show this help
217
243
  `);
218
244
  }
@@ -281,6 +307,13 @@ function parseArgs(argv) {
281
307
  let fixture;
282
308
  let persist = false;
283
309
  let model;
310
+ let generateCandidates = false;
311
+ let generatorModel;
312
+ let maxGeneratedCandidates = 5;
313
+ let generationTimeoutMs = 30000;
314
+ let generationCostUnits = 5;
315
+ let runId;
316
+ let allActive = false;
284
317
 
285
318
  for (let i = 0; i < args.length; i += 1) {
286
319
  const arg = args[i];
@@ -311,7 +344,28 @@ function parseArgs(argv) {
311
344
  continue;
312
345
  }
313
346
 
314
- if (["--surface", "--fixture", "--model"].includes(arg)) {
347
+ if (arg === "--generate-candidates") {
348
+ generateCandidates = true;
349
+ continue;
350
+ }
351
+
352
+ if (arg === "--all-active") {
353
+ allActive = true;
354
+ continue;
355
+ }
356
+
357
+ if (arg === "--run") {
358
+ const next = args[i + 1];
359
+ if (!next) {
360
+ console.error("Missing value for --run");
361
+ process.exit(1);
362
+ }
363
+ runId = next;
364
+ i += 1;
365
+ continue;
366
+ }
367
+
368
+ if (["--surface", "--fixture", "--model", "--generator-model", "--max-generated-candidates", "--generation-timeout-ms", "--generation-cost-units"].includes(arg)) {
315
369
  const next = args[i + 1];
316
370
  if (!next) {
317
371
  console.error(`Missing value for ${arg}`);
@@ -324,7 +378,19 @@ function parseArgs(argv) {
324
378
  }
325
379
  surface = next;
326
380
  } else if (arg === "--fixture") fixture = next;
327
- else model = next;
381
+ else if (arg === "--model") model = next;
382
+ else if (arg === "--generator-model") generatorModel = next;
383
+ else {
384
+ const value = Number(next);
385
+ const maximum = arg === "--generation-timeout-ms" ? 30000 : 5;
386
+ if (!Number.isInteger(value) || value < 1 || value > maximum) {
387
+ console.error(`${arg} must be an integer from 1 to ${maximum}.`);
388
+ process.exit(1);
389
+ }
390
+ if (arg === "--max-generated-candidates") maxGeneratedCandidates = value;
391
+ else if (arg === "--generation-timeout-ms") generationTimeoutMs = value;
392
+ else generationCostUnits = value;
393
+ }
328
394
  i += 1;
329
395
  continue;
330
396
  }
@@ -395,6 +461,13 @@ function parseArgs(argv) {
395
461
  fixture: fixture ? resolve(process.cwd(), fixture) : null,
396
462
  persist,
397
463
  model,
464
+ generateCandidates,
465
+ runId,
466
+ allActive,
467
+ generatorModel,
468
+ maxGeneratedCandidates,
469
+ generationTimeoutMs,
470
+ generationCostUnits,
398
471
  };
399
472
  }
400
473
 
@@ -825,7 +898,7 @@ function printNextSteps(target, destination, files, wroteAgentsFragment, wroteOp
825
898
  console.log(`- AGDF language preference: artefacts=${language.artifact_language}, chat=${language.chat_language}, runtime=${language.runtime_language}.`);
826
899
  }
827
900
  if (target === "init") {
828
- console.log("- Fill .agdf/control/AGDF_RUN.md with the current gate, evidence and next allowed action.");
901
+ console.log("- Create or migrate a canonical run, then fill its RUN_STATE.md with the current gate, evidence and next allowed action.");
829
902
  console.log("- Run npx --yes @agdf/cli@latest doctor to check the control state before the next agent run.");
830
903
  console.log("- Commit the live control files once they represent the repository's current delivery state.");
831
904
  return;
@@ -1136,6 +1209,16 @@ function hasFilledEvidenceRow(content) {
1136
1209
  });
1137
1210
  }
1138
1211
 
1212
+ function allowNoActiveRuns(targetDir) {
1213
+ const configPath = join(targetDir, ".agdf", "control", "config.json");
1214
+ if (!existsSync(configPath)) return false;
1215
+ try {
1216
+ return JSON.parse(readFileSync(configPath, "utf8")).allow_no_active_runs === true;
1217
+ } catch {
1218
+ return false;
1219
+ }
1220
+ }
1221
+
1139
1222
  function markdownSection(content, heading) {
1140
1223
  const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1141
1224
  return content.match(new RegExp(`(?:^|\\n)## ${escaped}\\s*\\n([\\s\\S]*?)(?:\\n## |\\n# |$)`))?.[1] ?? "";
@@ -1145,12 +1228,6 @@ function filled(value) {
1145
1228
  return Boolean(value && !isPlaceholderValue(value) && value.trim() !== "");
1146
1229
  }
1147
1230
 
1148
- function nonTemplateRows(section, headerCell) {
1149
- return tableRows(section)
1150
- .filter((cells) => cells[0] !== headerCell)
1151
- .filter((cells) => cells.some((cell) => filled(cell)));
1152
- }
1153
-
1154
1231
  function parseQualityContracts(content) {
1155
1232
  const parsed = JSON.parse(content);
1156
1233
  if (!Array.isArray(parsed.contracts) || parsed.contracts.length === 0) {
@@ -1164,9 +1241,61 @@ function parseQualityContracts(content) {
1164
1241
  return parsed;
1165
1242
  }
1166
1243
 
1167
- function evaluateDoctor(targetDir) {
1244
+ function evaluateDoctor(targetDir, selection = {}) {
1245
+ if (selection.allActive) {
1246
+ const selected = resolveRuns(targetDir, { allActive: true });
1247
+ const runs = selected.runs.map((run) => ({
1248
+ run_id: run.run_id,
1249
+ report: evaluateDoctor(targetDir, { runId: run.run_id }),
1250
+ }));
1251
+ const status = selected.findings.length
1252
+ ? "block"
1253
+ : aggregate(
1254
+ runs.map((item) => ({ run_id: item.run_id, status: item.report.status })),
1255
+ { allowNoActiveRuns: allowNoActiveRuns(targetDir) },
1256
+ ).status;
1257
+ const findings = [
1258
+ ...selected.findings.map((finding) => ({
1259
+ ...finding,
1260
+ severity: "block",
1261
+ message: "Invalid canonical run entry.",
1262
+ next_step: "Repair or remove the invalid run entry.",
1263
+ })),
1264
+ ...runs.flatMap((item) => item.report.findings.map((finding) => ({ ...finding, run_id: item.run_id }))),
1265
+ ];
1266
+ return {
1267
+ schema_version: "1",
1268
+ status,
1269
+ checked_at: new Date().toISOString(),
1270
+ target_dir: targetDir,
1271
+ summary: {
1272
+ findings: findings.length,
1273
+ block: findings.filter((finding) => finding.severity === "block").length,
1274
+ revise: findings.filter((finding) => finding.severity === "revise").length,
1275
+ warn: findings.filter((finding) => finding.severity === "warn").length,
1276
+ },
1277
+ findings,
1278
+ runs,
1279
+ };
1280
+ }
1168
1281
  const findings = [];
1169
- const missing = doctorRequiredFiles.filter((relativePath) => !existsSync(join(targetDir, relativePath)));
1282
+ const hasCanonicalRuns = existsSync(join(targetDir, ".agdf", "control", "runs"));
1283
+ if (hasCanonicalRuns) {
1284
+ const projection = verifyLegacyProjection(targetDir);
1285
+ if (!["absent", "valid"].includes(projection.status)) {
1286
+ addFinding(
1287
+ findings,
1288
+ "block",
1289
+ "AGDF_LEGACY_PROJECTION_DRIFT",
1290
+ `Legacy compatibility state is not a valid projection: ${projection.status}.`,
1291
+ join(".agdf", "control", "AGDF_RUN.md"),
1292
+ "Regenerate the explicit legacy projection or remove it after legacy consumers are retired.",
1293
+ );
1294
+ }
1295
+ }
1296
+ const missing = doctorRequiredFiles.filter(
1297
+ (relativePath) => !(relativePath.endsWith("AGDF_RUN.md") && hasCanonicalRuns) && !existsSync(join(targetDir, relativePath)),
1298
+ );
1170
1299
 
1171
1300
  for (const relativePath of missing) {
1172
1301
  const templatePath = join(dirname(relativePath), "templates", relativePath.split("/").at(-1));
@@ -1184,8 +1313,9 @@ function evaluateDoctor(targetDir) {
1184
1313
  }
1185
1314
 
1186
1315
  if (missing.length === 0) {
1187
- const runPath = join(".agdf", "control", "AGDF_RUN.md");
1188
- const run = readTargetFile(targetDir, runPath);
1316
+ const selectedRunState = readRunState(targetDir, selection);
1317
+ const runPath = selectedRunState.path;
1318
+ const run = selectedRunState.content;
1189
1319
  const currentGateLine = run.match(/^- current_gate:[^\S\r\n]*(.*)$/m)?.[1]?.trim() ?? "";
1190
1320
  const nextActionLine = run.match(/^- next_allowed_action:[^\S\r\n]*(.*)$/m)?.[1]?.trim() ?? "";
1191
1321
  const hasEvidence = hasFilledEvidenceRow(run);
@@ -1195,7 +1325,7 @@ function evaluateDoctor(targetDir) {
1195
1325
  findings,
1196
1326
  "revise",
1197
1327
  "AGDF_CURRENT_GATE_MISSING",
1198
- "AGDF_RUN.md does not name the current gate.",
1328
+ "The selected run state does not name the current gate.",
1199
1329
  runPath,
1200
1330
  "Set current_gate to the current delivery gate or none.",
1201
1331
  );
@@ -1206,7 +1336,7 @@ function evaluateDoctor(targetDir) {
1206
1336
  findings,
1207
1337
  "revise",
1208
1338
  "AGDF_NEXT_ALLOWED_ACTION_MISSING",
1209
- "AGDF_RUN.md does not state the next allowed action.",
1339
+ "The selected run state does not state the next allowed action.",
1210
1340
  runPath,
1211
1341
  "Fill the next allowed action before asking an agent to continue delivery work.",
1212
1342
  );
@@ -1217,7 +1347,7 @@ function evaluateDoctor(targetDir) {
1217
1347
  findings,
1218
1348
  "warn",
1219
1349
  "AGDF_EVIDENCE_EMPTY",
1220
- "AGDF_RUN.md has no visible evidence row yet.",
1350
+ "The selected run state has no visible evidence row yet.",
1221
1351
  runPath,
1222
1352
  "Add at least one evidence row or explicitly document that no evidence exists yet.",
1223
1353
  );
@@ -1300,7 +1430,7 @@ function evaluateDoctor(targetDir) {
1300
1430
  );
1301
1431
  }
1302
1432
 
1303
- const runState = readRunState(targetDir);
1433
+ const runState = selectedRunState;
1304
1434
  for (const finding of analyzeDurableGateArtefactConsistency(runState)) {
1305
1435
  addFinding(findings, finding.severity, finding.code, finding.message, finding.path, finding.next_step);
1306
1436
  }
@@ -1352,34 +1482,22 @@ function extractField(content, field) {
1352
1482
  return content.match(pattern)?.[1]?.trim() ?? "";
1353
1483
  }
1354
1484
 
1355
- function extractSectionField(section, field) {
1356
- const pattern = new RegExp(`^- ${field}:[^\\S\\r\\n]*(.*)$`, "m");
1357
- return section.match(pattern)?.[1]?.trim() ?? "";
1358
- }
1359
-
1360
1485
  function cleanStatusCell(value) {
1361
1486
  return value.replace(/^`|`$/g, "").trim();
1362
1487
  }
1363
1488
 
1364
- function normalizeGateStatus(gate, status) {
1365
- const normalized = cleanStatusCell(status ?? "");
1366
- if (gate === "QA" && normalized === "passed") return "approved";
1367
- return normalized;
1368
- }
1369
-
1370
- function addApprovalRows(approvals, section) {
1371
- for (const cells of tableRows(section)) {
1372
- const [gate, status, evidence] = cells;
1373
- if (!userGateOrder.includes(gate)) continue;
1374
- approvals.set(gate, {
1375
- status: normalizeGateStatus(gate, status),
1376
- evidence: evidence ?? "",
1489
+ function readRunState(targetDir, selection = {}) {
1490
+ let runPath = join(".agdf", "control", "AGDF_RUN.md");
1491
+ const canonicalRoot = join(targetDir, ".agdf", "control", "runs");
1492
+ if (existsSync(canonicalRoot)) {
1493
+ const selected = resolveRuns(targetDir, {
1494
+ runIdArg: selection.runId,
1495
+ runIdEnv: process.env.AGDF_RUN_ID,
1377
1496
  });
1497
+ runPath = selected.run.path.startsWith(targetDir)
1498
+ ? selected.run.path.slice(targetDir.length + 1)
1499
+ : selected.run.path;
1378
1500
  }
1379
- }
1380
-
1381
- function readRunState(targetDir) {
1382
- const runPath = join(".agdf", "control", "AGDF_RUN.md");
1383
1501
  if (!existsSync(join(targetDir, runPath))) {
1384
1502
  return {
1385
1503
  path: runPath,
@@ -1401,107 +1519,15 @@ function readRunState(targetDir) {
1401
1519
  }
1402
1520
 
1403
1521
  const content = readTargetFile(targetDir, runPath);
1404
- const approvals = new Map();
1405
- addApprovalRows(approvals, content.match(/## Approvals([\s\S]*?)(?:\n## |\n# |$)/)?.[1] ?? "");
1406
- addApprovalRows(approvals, content.match(/## Gate Checklist([\s\S]*?)(?:\n## |\n# |$)/)?.[1] ?? "");
1407
-
1408
- const artefacts = new Map();
1409
- const artefactsSection = markdownSection(content, "Artefacts");
1410
- for (const cells of tableRows(artefactsSection)) {
1411
- const [type, path, status, notes] = cells;
1412
- if (!userGateOrder.includes(type) && !internalStepArtefacts.has(type)) continue;
1413
- artefacts.set(type, {
1414
- path: path ?? "",
1415
- status: cleanStatusCell(status ?? ""),
1416
- notes: notes ?? "",
1417
- });
1418
- }
1419
-
1420
- const artefactChain = nonTemplateRows(markdownSection(content, "Artefact Chain"), "From")
1421
- .map((cells) => ({
1422
- from: cells[0] ?? "",
1423
- relationship: cleanStatusCell(cells[1] ?? ""),
1424
- to: cleanStatusCell(cells[2] ?? ""),
1425
- evidence: cells[3] ?? "",
1426
- }));
1427
-
1428
- const evidence_refs = tableRows(markdownSection(content, "Evidence"))
1429
- .filter((cells) => cells[0] !== "Evidence")
1430
- .filter((cells) => filled(cells[0]) || filled(cells[1]) || filled(cells[2]))
1431
- .map((cells) => ({
1432
- evidence: cells[0] ?? "",
1433
- source: cells[1] ?? "",
1434
- covers: cells[2] ?? "",
1435
- strength: cleanStatusCell(cells[3] ?? ""),
1436
- }));
1437
-
1438
- const missingEvidence = tableRows(markdownSection(content, "Missing Evidence"))
1439
- .filter((cells) => cells[0] !== "Missing evidence")
1440
- .filter((cells) => filled(cells[0]))
1441
- .map((cells) => ({
1442
- missing_evidence: cells[0] ?? "",
1443
- impact: cleanStatusCell(cells[1] ?? ""),
1444
- required_next_step: cells[2] ?? "",
1445
- }));
1446
-
1447
- const risks = tableRows(markdownSection(content, "Risks"))
1448
- .filter((cells) => cells[0] !== "Risk")
1449
- .filter((cells) => filled(cells[0]))
1450
- .map((cells) => ({
1451
- risk: cells[0] ?? "",
1452
- impact: cleanStatusCell(cells[1] ?? ""),
1453
- mitigation_or_owner: cells[2] ?? "",
1454
- }));
1455
-
1456
- const contextGraph = {
1457
- impact: cleanStatusCell(extractField(content, "context_graph_impact")),
1458
- refs: extractField(content, "context_graph_refs"),
1459
- required_action: cleanStatusCell(extractField(content, "context_graph_required_action")),
1460
- gate_effect: cleanStatusCell(extractField(content, "context_graph_gate_effect")),
1461
- evidence: extractField(content, "context_graph_evidence"),
1462
- };
1463
-
1464
- const modeSliceSection = markdownSection(content, "Mode / Slice Decision");
1465
- const modeSliceDecision = {
1466
- decision: cleanStatusCell(extractSectionField(modeSliceSection, "decision")),
1467
- required_next_gate: cleanStatusCell(extractSectionField(modeSliceSection, "required_next_gate")),
1468
- scope_reason: extractSectionField(modeSliceSection, "scope_reason"),
1469
- evidence: extractSectionField(modeSliceSection, "evidence"),
1470
- };
1471
-
1472
- const sourceScopeSection = markdownSection(content, "Source And Scope State");
1473
- const sourceScope = {
1474
- normative_instruction_source: extractSectionField(sourceScopeSection, "normative_instruction_source"),
1475
- multi_scope_state: cleanStatusCell(extractSectionField(sourceScopeSection, "multi_scope_state")),
1476
- active_scope_evidence: extractSectionField(sourceScopeSection, "active_scope_evidence"),
1477
- competing_scope_lines: extractSectionField(sourceScopeSection, "competing_scope_lines"),
1478
- branch_workspace_evidence: extractSectionField(sourceScopeSection, "branch_workspace_evidence"),
1479
- branch_workspace_scope_effect: cleanStatusCell(extractSectionField(sourceScopeSection, "branch_workspace_scope_effect")),
1480
- };
1481
-
1482
- const memorySection = markdownSection(content, "Knowledge Persistence Decision");
1483
- const memory = {
1484
- target: cleanStatusCell(extractSectionField(memorySection, "memory_target")),
1485
- reason: extractSectionField(memorySection, "memory_reason"),
1486
- refs: extractSectionField(memorySection, "memory_refs"),
1487
- };
1522
+ const parsed = parseControlState(content, {
1523
+ userGates: userGateOrder,
1524
+ internalSteps: [...internalStepArtefacts],
1525
+ });
1488
1526
 
1489
1527
  return {
1490
1528
  path: runPath,
1491
1529
  content,
1492
- current_gate: extractField(content, "current_gate"),
1493
- next_allowed_action: extractField(content, "next_allowed_action"),
1494
- approvals,
1495
- artefacts,
1496
- evidence_refs,
1497
- artefact_chain: artefactChain,
1498
- mode_slice_decision: modeSliceDecision,
1499
- missing_evidence: missingEvidence,
1500
- risks,
1501
- context_graph: contextGraph,
1502
- quality_outlook: extractField(content, "quality_outlook"),
1503
- source_scope: sourceScope,
1504
- memory,
1530
+ ...parsed,
1505
1531
  };
1506
1532
  }
1507
1533
 
@@ -1562,7 +1588,7 @@ function analyzeDurableGateArtefactConsistency(runState) {
1562
1588
  code: "AGDF_GATE_ARTEFACT_STATUS_INCONSISTENT",
1563
1589
  message: `${gate} approval is recorded, but the durable artefact row uses status \`${artefact.status}\`; expected ${describeDurableArtefactStatuses(gate)}.`,
1564
1590
  path: runState.path,
1565
- next_step: `Update the ${gate} artefact row in AGDF_RUN.md to use the gate-specific durable status vocabulary.`,
1591
+ next_step: `Update the ${gate} artefact row in the selected RUN_STATE.md to use the gate-specific durable status vocabulary.`,
1566
1592
  });
1567
1593
  }
1568
1594
 
@@ -1810,7 +1836,7 @@ function durableArtefactBlock(gate, nextGate) {
1810
1836
  missing_approval: "none",
1811
1837
  allowed: [
1812
1838
  `persist the approved ${label} in a stable artefact path such as ${stablePath}`,
1813
- "link the artefact from AGDF_RUN.md and MASTER_BACKLOG.md",
1839
+ "link the artefact from the selected RUN_STATE.md and MASTER_BACKLOG.md",
1814
1840
  "run gate-check again",
1815
1841
  ],
1816
1842
  forbidden: nextGate
@@ -1855,7 +1881,7 @@ function transitionDecisionForRunState(runState) {
1855
1881
  allowed: [
1856
1882
  "run Brownfield Review after G-00",
1857
1883
  "identify existing workstream, owners, SoT, reuse risks and open PRD/SD questions",
1858
- "mark Brownfield Review as done or not_applicable in AGDF_RUN.md",
1884
+ "mark Brownfield Review as done or not_applicable in the selected RUN_STATE.md",
1859
1885
  ],
1860
1886
  forbidden: ["create PRD before Brownfield Review is resolved", "create SD", "create TP", "implement code", "claim QA or release readiness"],
1861
1887
  next_allowed_action: "Run Brownfield Review after G-00 before drafting PRD, or mark Brownfield Review not_applicable with evidence.",
@@ -1871,7 +1897,7 @@ function transitionDecisionForRunState(runState) {
1871
1897
  missing_approval: "none",
1872
1898
  allowed: [
1873
1899
  "decide whether the approved UR is quick_task, structured_slice, structured_delivery or block",
1874
- "record scope reason, evidence and required next gate depth in AGDF_RUN.md",
1900
+ "record scope reason, evidence and required next gate depth in the selected RUN_STATE.md",
1875
1901
  "choose the next required gate depth before drafting PRD or implementing",
1876
1902
  ],
1877
1903
  forbidden: ["create PRD before process size is decided", "create SD", "create TP", "implement code", "claim QA or release readiness"],
@@ -1981,9 +2007,9 @@ function transitionDecisionForRunState(runState) {
1981
2007
  };
1982
2008
  }
1983
2009
 
1984
- function evaluateGateCheck(targetDir) {
1985
- const doctorReport = evaluateDoctor(targetDir);
1986
- const runState = readRunState(targetDir);
2010
+ function evaluateGateCheck(targetDir, selection = {}) {
2011
+ const doctorReport = evaluateDoctor(targetDir, selection);
2012
+ const runState = readRunState(targetDir, selection);
1987
2013
  const transitionDecision = transitionDecisionForRunState(runState);
1988
2014
  const deliveryMap = analyzeDeliveryMap(runState);
1989
2015
  const doctorBlocker = doctorReport.findings.find((finding) => finding.severity === "block");
@@ -2118,9 +2144,51 @@ function readBacklogPointers(targetDir) {
2118
2144
  return parseBacklogSection(activeSection);
2119
2145
  }
2120
2146
 
2121
- function evaluateDeliveryMap(targetDir) {
2122
- const doctorReport = evaluateDoctor(targetDir);
2123
- const runState = readRunState(targetDir);
2147
+ function evaluateDeliveryMap(targetDir, selection = {}) {
2148
+ if (selection.allActive) {
2149
+ const selected = resolveRuns(targetDir, { allActive: true });
2150
+ const runs = selected.runs.map((run) => ({
2151
+ run_id: run.run_id,
2152
+ report: evaluateDeliveryMap(targetDir, { runId: run.run_id }),
2153
+ }));
2154
+ const status = selected.findings.length
2155
+ ? "block"
2156
+ : aggregate(
2157
+ runs.map((item) => ({
2158
+ run_id: item.run_id,
2159
+ status: item.report.status,
2160
+ })),
2161
+ { allowNoActiveRuns: allowNoActiveRuns(targetDir) },
2162
+ ).status;
2163
+ const findings = [
2164
+ ...selected.findings.map((finding) => ({
2165
+ ...finding,
2166
+ severity: "block",
2167
+ message: "Invalid canonical run entry.",
2168
+ next_step: "Repair or remove the invalid run entry.",
2169
+ })),
2170
+ ...runs.flatMap((item) =>
2171
+ item.report.findings.map((finding) => ({
2172
+ ...finding,
2173
+ run_id: item.run_id,
2174
+ })),
2175
+ ),
2176
+ ];
2177
+ return {
2178
+ schema_version: "1",
2179
+ status,
2180
+ current_gate: "all-active",
2181
+ next_allowed_action: runs.length
2182
+ ? "Resolve per-run findings."
2183
+ : "Create or activate a governed run.",
2184
+ quality_outlook: "Keep every active run independently actionable.",
2185
+ relationships: runs.flatMap((item) => item.report.relationships),
2186
+ findings,
2187
+ runs,
2188
+ };
2189
+ }
2190
+ const doctorReport = evaluateDoctor(targetDir, selection);
2191
+ const runState = readRunState(targetDir, selection);
2124
2192
  const map = analyzeDeliveryMap(runState);
2125
2193
  const gateDecision = transitionDecisionForRunState(runState);
2126
2194
  const currentGate = effectiveCurrentGate(runState, gateDecision);
@@ -2198,7 +2266,28 @@ function printDeliveryMapReport(report, json) {
2198
2266
  async function executeDeliveryPathSearch(options) {
2199
2267
  const fixture = options.fixture ? JSON.parse(readFileSync(options.fixture, "utf8")) : null;
2200
2268
  const enforcement = fixture?.input?.enforcement ?? enforcementForSurface(options.surface);
2201
- const input = fixture?.input ?? searchInputFromControl(options.dir, { enforcement });
2269
+ const fixtureGeneration = fixture?.generator_response;
2270
+ const generationEnabled = Boolean(fixtureGeneration || options.generateCandidates);
2271
+ const baseInput = fixture?.input ?? searchInputFromControl(options.dir, {
2272
+ scopeKey: options.runId,
2273
+ enforcement,
2274
+ generation: generationEnabled ? {
2275
+ enabled: true,
2276
+ maxProposals: options.maxGeneratedCandidates,
2277
+ maxDurationMs: options.generationTimeoutMs,
2278
+ maxCostUnits: options.generationCostUnits,
2279
+ } : undefined,
2280
+ });
2281
+ const input = generationEnabled && !baseInput.generation ? {
2282
+ ...baseInput,
2283
+ generation: {
2284
+ enabled: true,
2285
+ max_calls: 1,
2286
+ max_proposals: options.maxGeneratedCandidates,
2287
+ max_duration_ms: options.generationTimeoutMs,
2288
+ max_cost_units: options.generationCostUnits,
2289
+ },
2290
+ } : baseInput;
2202
2291
  const evaluator = fixture
2203
2292
  ? fixtureEvaluator(fixture.evaluations ?? {})
2204
2293
  : options.surface === "codex"
@@ -2209,7 +2298,17 @@ async function executeDeliveryPathSearch(options) {
2209
2298
  if (!evaluator) {
2210
2299
  throw new Error(`${options.surface} has no executable evaluator in this release. Codex and Claude are the reference adapters; --fixture is available for deterministic contract testing.`);
2211
2300
  }
2212
- const result = await runDeliveryPathSearch(input, evaluator, { candidates: fixture?.candidates });
2301
+ const generator = fixtureGeneration
2302
+ ? fixtureGenerator(fixtureGeneration)
2303
+ : options.generateCandidates && options.surface === "codex"
2304
+ ? codexGenerator({ cwd: options.dir, model: options.generatorModel, timeoutMs: options.generationTimeoutMs })
2305
+ : options.generateCandidates && options.surface === "claude"
2306
+ ? claudeGenerator({ cwd: options.dir, model: options.generatorModel, timeoutMs: options.generationTimeoutMs })
2307
+ : null;
2308
+ if (options.generateCandidates && !generator) {
2309
+ throw new Error(`${options.surface} has no executable candidate generator; Codex and Claude are tool-enforced reference transports.`);
2310
+ }
2311
+ const result = await runDeliveryPathSearch(input, evaluator, { candidates: fixture?.candidates, generator });
2213
2312
  if (options.persist) result.persistence = persistSearchResult(options.dir, result);
2214
2313
  if (options.json) {
2215
2314
  console.log(JSON.stringify(result, null, 2));
@@ -2220,6 +2319,9 @@ async function executeDeliveryPathSearch(options) {
2220
2319
  console.log(`Enforcement: ${result.enforcement.level}`);
2221
2320
  console.log(`Recommendation: ${result.recommendation?.action ?? "none"}`);
2222
2321
  console.log(`Evaluations: ${result.budgets.evaluations}`);
2322
+ console.log(`Generation: ${result.generation.status} (${result.generation.accepted}/${result.generation.returned} accepted)`);
2323
+ console.log(`Generation budget: ${result.generation.cost_units} cost units, ${result.generation.duration_ms} ms`);
2324
+ if (result.generation.failure_code) console.log(`Generation failure: ${result.generation.failure_code}`);
2223
2325
  console.log(`Stopping reason: ${result.stopping_reason}`);
2224
2326
  console.log(result.next_gate_action);
2225
2327
  return result;
@@ -2228,20 +2330,50 @@ async function executeDeliveryPathSearch(options) {
2228
2330
  async function main() {
2229
2331
  const options = parseArgs(process.argv.slice(2));
2230
2332
 
2333
+ if (
2334
+ options.allActive &&
2335
+ !["doctor", "delivery-map"].includes(options.target)
2336
+ ) {
2337
+ throw new Error(
2338
+ "--all-active is supported only by doctor and delivery-map",
2339
+ );
2340
+ }
2341
+
2342
+ if (options.target === "run-create") {
2343
+ if (!options.runId || options.allActive)
2344
+ throw new Error("run-create requires --run and rejects --all-active");
2345
+ console.log(createRun(options.dir, options.runId));
2346
+ return;
2347
+ }
2348
+ if (options.target === "run-migrate") {
2349
+ console.log(
2350
+ JSON.stringify(migrateLegacy(options.dir, options.runId), null, 2),
2351
+ );
2352
+ return;
2353
+ }
2354
+ if (options.target === "run-render-legacy") {
2355
+ if (!options.runId) throw new Error("run-render-legacy requires --run");
2356
+ const selected = resolveRuns(options.dir, { runIdArg: options.runId });
2357
+ const output = join(options.dir, ".agdf", "control", "AGDF_RUN.md");
2358
+ writeLegacyProjection(output, selected.run.path);
2359
+ console.log(output);
2360
+ return;
2361
+ }
2362
+
2231
2363
  if (options.target === "doctor") {
2232
- const report = evaluateDoctor(options.dir);
2364
+ const report = evaluateDoctor(options.dir, options);
2233
2365
  printDoctorReport(report, options.json);
2234
2366
  process.exit(report.status === "block" ? 2 : 0);
2235
2367
  }
2236
2368
 
2237
2369
  if (options.target === "gate-check") {
2238
- const report = evaluateGateCheck(options.dir);
2370
+ const report = evaluateGateCheck(options.dir, options);
2239
2371
  printGateCheckReport(report, options.json, options.statusCard);
2240
2372
  process.exit(report.status === "blocked" ? 2 : 0);
2241
2373
  }
2242
2374
 
2243
2375
  if (options.target === "delivery-map") {
2244
- const report = evaluateDeliveryMap(options.dir);
2376
+ const report = evaluateDeliveryMap(options.dir, options);
2245
2377
  printDeliveryMapReport(report, options.json);
2246
2378
  process.exit(report.status === "block" ? 2 : 0);
2247
2379
  }