create-agdf 0.1.7 → 0.1.9

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 CHANGED
@@ -8,6 +8,9 @@ Bootstrap AGDF repository instructions for one repository.
8
8
  npm create agdf@latest codex
9
9
  npm create agdf@latest copilot
10
10
  npm create agdf@latest both
11
+ npm create agdf@latest init
12
+ npm create agdf@latest doctor
13
+ npm create agdf@latest gate-check
11
14
  ```
12
15
 
13
16
  Optional flags:
@@ -29,7 +32,7 @@ After `npm create agdf@latest codex`, restart Codex in that repository, open `/p
29
32
 
30
33
  ## Control scaffold
31
34
 
32
- The generated `.agdf/control/templates/` files are starting points for durable AGDF state:
35
+ The generated `.agdf/control/templates/` files are reusable starting points for durable AGDF state:
33
36
 
34
37
  - `AGDF_RUN.md` for the current run dashboard
35
38
  - `MASTER_BACKLOG.md` for active delivery pointers
@@ -37,7 +40,37 @@ The generated `.agdf/control/templates/` files are starting points for durable A
37
40
  - `CONTEXT_GRAPH.md` for durable Brownfield findings, decisions, risks, evidence and exit criteria
38
41
  - `AGENT_QUALITY_CONTRACTS.json` for reusable block, revise and warning conditions
39
42
 
40
- Copy or promote templates into live `.agdf/control/` files only when the target repository is ready to own them as source-of-truth artefacts.
43
+ Use `init` to promote those templates into live control files:
44
+
45
+ ```bash
46
+ npm create agdf@latest init
47
+ ```
48
+
49
+ This writes:
50
+
51
+ - `.agdf/control/AGDF_RUN.md`
52
+ - `.agdf/control/MASTER_BACKLOG.md`
53
+ - `.agdf/control/SOT_REGISTRY.md`
54
+ - `.agdf/control/CONTEXT_GRAPH.md`
55
+ - `.agdf/control/AGENT_QUALITY_CONTRACTS.json`
56
+
57
+ Use `doctor` to check whether the live control state is actionable:
58
+
59
+ ```bash
60
+ npm create agdf@latest doctor
61
+ npm create agdf@latest doctor --json
62
+ ```
63
+
64
+ The doctor reports missing live control files, missing current gate, missing next allowed action, empty evidence, empty backlog pointer, empty source-of-truth registry, duplicate active SoT rows and invalid quality contracts. It exits non-zero only for blocking control failures.
65
+
66
+ Use `gate-check` to derive the next process decision from the doctor result and `AGDF_RUN.md`:
67
+
68
+ ```bash
69
+ npm create agdf@latest gate-check
70
+ npm create agdf@latest gate-check --json
71
+ ```
72
+
73
+ The gate check reports `open | blocked`, the current gate, blocking reason, missing exact approval, allowed outputs, forbidden outputs, next allowed action, evidence references and the embedded doctor report.
41
74
 
42
75
  ## Single source of truth
43
76
 
@@ -11,8 +11,9 @@ const generatedRoot = join(packageRoot, "generated");
11
11
  const pluginDefinitionPath = join(generatedRoot, "plugins", "agdf", "meta", "agdf-plugin.definition.json");
12
12
  const pluginDefinition = JSON.parse(readFileSync(pluginDefinitionPath, "utf8"));
13
13
  const pluginInstallCommand = "claude plugin add arndtgold/ai-native-governance-delivery-framework";
14
- const allowedTargets = new Set(["codex", "copilot", "both"]);
14
+ const allowedTargets = new Set(["codex", "copilot", "both", "init", "doctor", "gate-check"]);
15
15
  const agdfFragmentPath = "AGENTS.agdf.md";
16
+ const userGateOrder = ["UR", "PRD", "SD", "TP", "QA", "UAT"];
16
17
  const codexSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.codex.skillPrefix}${skill.slug}`);
17
18
  const copilotSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.copilot.skillPrefix}${skill.slug}`);
18
19
  const codexPluginFiles = [
@@ -41,6 +42,35 @@ const controlFiles = [
41
42
  join(".agdf", "control", "templates", "CONTEXT_GRAPH.md"),
42
43
  join(".agdf", "control", "templates", "AGENT_QUALITY_CONTRACTS.json"),
43
44
  ];
45
+ const liveControlFiles = [
46
+ {
47
+ path: join(".agdf", "control", "AGDF_RUN.md"),
48
+ source: join(".agdf", "control", "templates", "AGDF_RUN.md"),
49
+ },
50
+ {
51
+ path: join(".agdf", "control", "MASTER_BACKLOG.md"),
52
+ source: join(".agdf", "control", "templates", "MASTER_BACKLOG.md"),
53
+ },
54
+ {
55
+ path: join(".agdf", "control", "SOT_REGISTRY.md"),
56
+ source: join(".agdf", "control", "templates", "SOT_REGISTRY.md"),
57
+ },
58
+ {
59
+ path: join(".agdf", "control", "CONTEXT_GRAPH.md"),
60
+ source: join(".agdf", "control", "templates", "CONTEXT_GRAPH.md"),
61
+ },
62
+ {
63
+ path: join(".agdf", "control", "AGENT_QUALITY_CONTRACTS.json"),
64
+ source: join(".agdf", "control", "templates", "AGENT_QUALITY_CONTRACTS.json"),
65
+ },
66
+ ];
67
+ const doctorRequiredFiles = [
68
+ join(".agdf", "control", "AGDF_RUN.md"),
69
+ join(".agdf", "control", "MASTER_BACKLOG.md"),
70
+ join(".agdf", "control", "SOT_REGISTRY.md"),
71
+ join(".agdf", "control", "CONTEXT_GRAPH.md"),
72
+ join(".agdf", "control", "AGENT_QUALITY_CONTRACTS.json"),
73
+ ];
44
74
  const copilotInstructionFiles = [
45
75
  join(".github", "copilot-instructions.md"),
46
76
  join(".github", "instructions", "agdf-governance.instructions.md"),
@@ -58,10 +88,14 @@ Usage:
58
88
  npm create agdf@latest codex
59
89
  npm create agdf@latest copilot
60
90
  npm create agdf@latest both
91
+ npm create agdf@latest init
92
+ npm create agdf@latest doctor
93
+ npm create agdf@latest gate-check
61
94
 
62
95
  Options:
63
96
  --dir <path> Write files into a specific directory
64
97
  --force Overwrite existing generated files
98
+ --json Print doctor or gate-check output as JSON
65
99
  --help Show this help
66
100
  `);
67
101
  }
@@ -71,6 +105,7 @@ function parseArgs(argv) {
71
105
  let target;
72
106
  let dir = ".";
73
107
  let force = false;
108
+ let json = false;
74
109
 
75
110
  for (let i = 0; i < args.length; i += 1) {
76
111
  const arg = args[i];
@@ -86,6 +121,11 @@ function parseArgs(argv) {
86
121
  continue;
87
122
  }
88
123
 
124
+ if (arg === "--json") {
125
+ json = true;
126
+ continue;
127
+ }
128
+
89
129
  if (arg === "--dir") {
90
130
  const next = args[i + 1];
91
131
  if (!next) {
@@ -118,7 +158,7 @@ function parseArgs(argv) {
118
158
  }
119
159
 
120
160
  if (!target || !allowedTargets.has(target)) {
121
- console.error("Please choose one target: codex, copilot or both.");
161
+ console.error("Please choose one target: codex, copilot, both, init, doctor or gate-check.");
122
162
  printUsage();
123
163
  process.exit(1);
124
164
  }
@@ -127,6 +167,7 @@ function parseArgs(argv) {
127
167
  target,
128
168
  dir: resolve(process.cwd(), dir),
129
169
  force,
170
+ json,
130
171
  };
131
172
  }
132
173
 
@@ -148,6 +189,22 @@ function writeGeneratedFile(targetDir, relativePath, content, force) {
148
189
  function generatedFilesForTarget(target, targetDir, force) {
149
190
  const files = [];
150
191
 
192
+ if (target === "init") {
193
+ files.push({
194
+ path: join(".agdf", "control", "README.md"),
195
+ content: loadAsset(join(".agdf", "control", "README.md")),
196
+ });
197
+
198
+ for (const controlPath of liveControlFiles) {
199
+ files.push({
200
+ path: controlPath.path,
201
+ content: loadAsset(controlPath.source),
202
+ });
203
+ }
204
+
205
+ return files;
206
+ }
207
+
151
208
  if (target === "codex" || target === "both") {
152
209
  for (const codexPath of codexPluginFiles) {
153
210
  files.push({
@@ -200,6 +257,12 @@ function printNextSteps(target, destination, files, wroteAgentsFragment) {
200
257
 
201
258
  console.log("");
202
259
  console.log("Next steps:");
260
+ if (target === "init") {
261
+ console.log("- Fill .agdf/control/AGDF_RUN.md with the current gate, evidence and next allowed action.");
262
+ console.log("- Run npm create agdf@latest doctor to check the control state before the next agent run.");
263
+ console.log("- Commit the live control files once they represent the repository's current delivery state.");
264
+ return;
265
+ }
203
266
  if (wroteAgentsFragment) {
204
267
  console.log(`- Existing AGENTS.md detected. Merge ${agdfFragmentPath} into your current AGENTS.md before using Copilot with AGDF.`);
205
268
  }
@@ -212,13 +275,422 @@ function printNextSteps(target, destination, files, wroteAgentsFragment) {
212
275
  }
213
276
  if (target === "copilot" || target === "both") {
214
277
  console.log("- In GitHub Copilot CLI, run /instructions after the AGENTS.md step is complete to confirm that AGDF instructions and the repository skills are visible.");
215
- console.log("- Copy the templates under .agdf/control/templates into live control files when the repository needs a durable run state, backlog pointer, SoT registry or context graph.");
278
+ console.log("- Run npm create agdf@latest init when the repository needs live AGDF control files.");
216
279
  }
217
280
  console.log("- Commit the generated files so the repository becomes the source of truth.");
218
281
  }
219
282
 
283
+ function readTargetFile(targetDir, relativePath) {
284
+ return readFileSync(join(targetDir, relativePath), "utf8");
285
+ }
286
+
287
+ function addFinding(findings, severity, code, message, path, nextStep) {
288
+ findings.push({
289
+ severity,
290
+ code,
291
+ message,
292
+ path,
293
+ next_step: nextStep,
294
+ });
295
+ }
296
+
297
+ function nonEmptyTableRows(content) {
298
+ return content
299
+ .split("\n")
300
+ .filter((line) => line.trim().startsWith("|"))
301
+ .filter((line) => !line.includes("---"))
302
+ .map((line) => line.split("|").map((cell) => cell.trim()).filter(Boolean))
303
+ .filter((cells) => cells.some((cell) => cell && !cell.startsWith("`")));
304
+ }
305
+
306
+ function tableRows(content) {
307
+ return content
308
+ .split("\n")
309
+ .filter((line) => line.trim().startsWith("|"))
310
+ .filter((line) => !line.includes("---"))
311
+ .map((line) => {
312
+ const cells = line.split("|").map((cell) => cell.trim());
313
+ if (cells[0] === "") cells.shift();
314
+ if (cells.at(-1) === "") cells.pop();
315
+ return cells;
316
+ });
317
+ }
318
+
319
+ function hasFilledTableRow(content, firstCellPattern) {
320
+ return tableRows(content)
321
+ .filter((cells) => firstCellPattern.test(cells[0] ?? ""))
322
+ .some((cells) => cells.slice(1).some((cell) => cell && !cell.startsWith("`")));
323
+ }
324
+
325
+ function isPlaceholderValue(value) {
326
+ return !value || (value.startsWith("`") && value.includes("|"));
327
+ }
328
+
329
+ function hasFilledEvidenceRow(content) {
330
+ const evidenceSection = content.match(/## Evidence([\s\S]*?)(?:\n## |\n# |$)/)?.[1] ?? "";
331
+ return tableRows(evidenceSection)
332
+ .filter((cells) => cells[0] !== "Evidence")
333
+ .some((cells) => {
334
+ const [evidence, source, covers] = cells;
335
+ return Boolean(evidence || source || covers);
336
+ });
337
+ }
338
+
339
+ function parseQualityContracts(content) {
340
+ const parsed = JSON.parse(content);
341
+ if (!Array.isArray(parsed.contracts) || parsed.contracts.length === 0) {
342
+ throw new Error("contracts must be a non-empty array");
343
+ }
344
+ for (const contract of parsed.contracts) {
345
+ if (!contract.code || !contract.impact || !contract.required_evidence) {
346
+ throw new Error(`contract ${contract.code ?? "<unknown>"} is missing code, impact or required_evidence`);
347
+ }
348
+ }
349
+ return parsed;
350
+ }
351
+
352
+ function evaluateDoctor(targetDir) {
353
+ const findings = [];
354
+ const missing = doctorRequiredFiles.filter((relativePath) => !existsSync(join(targetDir, relativePath)));
355
+
356
+ for (const relativePath of missing) {
357
+ const templatePath = join(dirname(relativePath), "templates", relativePath.split("/").at(-1));
358
+ const hasTemplate = existsSync(join(targetDir, templatePath));
359
+ addFinding(
360
+ findings,
361
+ "block",
362
+ "AGDF_CONTROL_FILE_MISSING",
363
+ hasTemplate
364
+ ? "Live control file is missing; only the template exists."
365
+ : "Required live control file is missing.",
366
+ relativePath,
367
+ "Run npm create agdf@latest init, then fill the live control files with the current repository state.",
368
+ );
369
+ }
370
+
371
+ if (missing.length === 0) {
372
+ const runPath = join(".agdf", "control", "AGDF_RUN.md");
373
+ const run = readTargetFile(targetDir, runPath);
374
+ const currentGateLine = run.match(/^- current_gate:[^\S\r\n]*(.*)$/m)?.[1]?.trim() ?? "";
375
+ const nextActionLine = run.match(/^- next_allowed_action:[^\S\r\n]*(.*)$/m)?.[1]?.trim() ?? "";
376
+ const hasEvidence = hasFilledEvidenceRow(run);
377
+
378
+ if (isPlaceholderValue(currentGateLine)) {
379
+ addFinding(
380
+ findings,
381
+ "revise",
382
+ "AGDF_CURRENT_GATE_MISSING",
383
+ "AGDF_RUN.md does not name the current gate.",
384
+ runPath,
385
+ "Set current_gate to the current delivery gate or none.",
386
+ );
387
+ }
388
+
389
+ if (isPlaceholderValue(nextActionLine)) {
390
+ addFinding(
391
+ findings,
392
+ "revise",
393
+ "AGDF_NEXT_ALLOWED_ACTION_MISSING",
394
+ "AGDF_RUN.md does not state the next allowed action.",
395
+ runPath,
396
+ "Fill the next allowed action before asking an agent to continue delivery work.",
397
+ );
398
+ }
399
+
400
+ if (!hasEvidence) {
401
+ addFinding(
402
+ findings,
403
+ "warn",
404
+ "AGDF_EVIDENCE_EMPTY",
405
+ "AGDF_RUN.md has no visible evidence row yet.",
406
+ runPath,
407
+ "Add at least one evidence row or explicitly document that no evidence exists yet.",
408
+ );
409
+ }
410
+
411
+ const backlogPath = join(".agdf", "control", "MASTER_BACKLOG.md");
412
+ const backlog = readTargetFile(targetDir, backlogPath);
413
+ if (!hasFilledTableRow(backlog, /^P[0-9]/)) {
414
+ addFinding(
415
+ findings,
416
+ "warn",
417
+ "AGDF_BACKLOG_POINTER_EMPTY",
418
+ "MASTER_BACKLOG.md does not contain an active or planned work pointer.",
419
+ backlogPath,
420
+ "Add the active item or document that no governed delivery item is active.",
421
+ );
422
+ }
423
+
424
+ const sotPath = join(".agdf", "control", "SOT_REGISTRY.md");
425
+ const sot = readTargetFile(targetDir, sotPath);
426
+ if (!hasFilledTableRow(sot, /Product intent|Architecture|Runtime contracts|UX \/ user flows|Operations \/ release/)) {
427
+ addFinding(
428
+ findings,
429
+ "warn",
430
+ "AGDF_SOT_REGISTRY_EMPTY",
431
+ "SOT_REGISTRY.md has no filled primary source-of-truth row.",
432
+ sotPath,
433
+ "Assign one primary source of truth for at least the domains relevant to the next run.",
434
+ );
435
+ }
436
+
437
+ const activeDomains = new Map();
438
+ for (const cells of nonEmptyTableRows(sot)) {
439
+ const [domain, document, status] = cells;
440
+ if (!domain || !document || status !== "active") continue;
441
+ activeDomains.set(domain, (activeDomains.get(domain) ?? 0) + 1);
442
+ }
443
+ for (const [domain, count] of activeDomains.entries()) {
444
+ if (count > 1) {
445
+ addFinding(
446
+ findings,
447
+ "block",
448
+ "AGDF_PARALLEL_SOT",
449
+ `Domain has ${count} active source-of-truth rows: ${domain}.`,
450
+ sotPath,
451
+ "Keep exactly one active primary source of truth for this domain.",
452
+ );
453
+ }
454
+ }
455
+
456
+ const contextPath = join(".agdf", "control", "CONTEXT_GRAPH.md");
457
+ const contextGraph = readTargetFile(targetDir, contextPath);
458
+ if (contextGraph.includes("### CG-001 Example")) {
459
+ addFinding(
460
+ findings,
461
+ "warn",
462
+ "AGDF_CONTEXT_GRAPH_TEMPLATE_NODE",
463
+ "CONTEXT_GRAPH.md still contains the example node.",
464
+ contextPath,
465
+ "Remove the example or replace it with an evidenced node that has an exit criterion.",
466
+ );
467
+ }
468
+
469
+ const contractsPath = join(".agdf", "control", "AGENT_QUALITY_CONTRACTS.json");
470
+ try {
471
+ parseQualityContracts(readTargetFile(targetDir, contractsPath));
472
+ } catch (error) {
473
+ addFinding(
474
+ findings,
475
+ "block",
476
+ "AGDF_QUALITY_CONTRACTS_INVALID",
477
+ `AGENT_QUALITY_CONTRACTS.json is invalid: ${error.message}`,
478
+ contractsPath,
479
+ "Restore the generated contracts or fix the JSON contract schema.",
480
+ );
481
+ }
482
+ }
483
+
484
+ const severityRank = { block: 3, revise: 2, warn: 1 };
485
+ const maxSeverity = findings.reduce((max, finding) => Math.max(max, severityRank[finding.severity] ?? 0), 0);
486
+ const status = maxSeverity >= 3 ? "block" : maxSeverity === 2 ? "revise" : maxSeverity === 1 ? "warn" : "pass";
487
+
488
+ return {
489
+ schema_version: "1",
490
+ status,
491
+ checked_at: new Date().toISOString(),
492
+ target_dir: targetDir,
493
+ summary: {
494
+ findings: findings.length,
495
+ block: findings.filter((finding) => finding.severity === "block").length,
496
+ revise: findings.filter((finding) => finding.severity === "revise").length,
497
+ warn: findings.filter((finding) => finding.severity === "warn").length,
498
+ },
499
+ findings,
500
+ };
501
+ }
502
+
503
+ function printDoctorReport(report, json) {
504
+ if (json) {
505
+ console.log(JSON.stringify(report, null, 2));
506
+ return;
507
+ }
508
+
509
+ console.log(`AGDF doctor: ${report.status}`);
510
+ console.log(`Checked: ${report.target_dir}`);
511
+ console.log(`Findings: ${report.summary.findings} (${report.summary.block} block, ${report.summary.revise} revise, ${report.summary.warn} warn)`);
512
+
513
+ for (const finding of report.findings) {
514
+ console.log("");
515
+ console.log(`[${finding.severity}] ${finding.code}`);
516
+ console.log(`Path: ${finding.path}`);
517
+ console.log(finding.message);
518
+ console.log(`Next step: ${finding.next_step}`);
519
+ }
520
+ }
521
+
522
+ function extractField(content, field) {
523
+ const pattern = new RegExp(`^- ${field}:[^\\S\\r\\n]*(.*)$`, "m");
524
+ return content.match(pattern)?.[1]?.trim() ?? "";
525
+ }
526
+
527
+ function cleanStatusCell(value) {
528
+ return value.replace(/^`|`$/g, "").trim();
529
+ }
530
+
531
+ function readRunState(targetDir) {
532
+ const runPath = join(".agdf", "control", "AGDF_RUN.md");
533
+ if (!existsSync(join(targetDir, runPath))) {
534
+ return {
535
+ path: runPath,
536
+ content: "",
537
+ current_gate: "",
538
+ next_allowed_action: "",
539
+ approvals: new Map(),
540
+ evidence_refs: [],
541
+ };
542
+ }
543
+
544
+ const content = readTargetFile(targetDir, runPath);
545
+ const approvalsSection = content.match(/## Approvals([\s\S]*?)(?:\n## |\n# |$)/)?.[1] ?? "";
546
+ const approvals = new Map();
547
+ for (const cells of tableRows(approvalsSection)) {
548
+ const [gate, status, evidence] = cells;
549
+ if (!userGateOrder.includes(gate)) continue;
550
+ approvals.set(gate, {
551
+ status: cleanStatusCell(status ?? ""),
552
+ evidence: evidence ?? "",
553
+ });
554
+ }
555
+
556
+ const evidenceSection = content.match(/## Evidence([\s\S]*?)(?:\n## |\n# |$)/)?.[1] ?? "";
557
+ const evidence_refs = tableRows(evidenceSection)
558
+ .filter((cells) => cells[0] !== "Evidence")
559
+ .filter((cells) => Boolean(cells[0] || cells[1] || cells[2]))
560
+ .map((cells) => ({
561
+ evidence: cells[0] ?? "",
562
+ source: cells[1] ?? "",
563
+ covers: cells[2] ?? "",
564
+ strength: cleanStatusCell(cells[3] ?? ""),
565
+ }));
566
+
567
+ return {
568
+ path: runPath,
569
+ content,
570
+ current_gate: extractField(content, "current_gate"),
571
+ next_allowed_action: extractField(content, "next_allowed_action"),
572
+ approvals,
573
+ evidence_refs,
574
+ };
575
+ }
576
+
577
+ function firstUnapprovedGate(approvals) {
578
+ for (const gate of userGateOrder) {
579
+ const status = approvals.get(gate)?.status ?? "";
580
+ if (status !== "approved" && status !== "not_applicable") return gate;
581
+ }
582
+ return "OR";
583
+ }
584
+
585
+ function normalizeCurrentGate(value, fallbackGate) {
586
+ if (isPlaceholderValue(value)) return fallbackGate;
587
+ const normalized = value.replace(/`/g, "").trim();
588
+ return normalized || fallbackGate;
589
+ }
590
+
591
+ function gateApprovalStatus(runState, gate) {
592
+ if (!userGateOrder.includes(gate)) return "not_applicable";
593
+ return runState.approvals.get(gate)?.status ?? "";
594
+ }
595
+
596
+ function evaluateGateCheck(targetDir) {
597
+ const doctorReport = evaluateDoctor(targetDir);
598
+ const runState = readRunState(targetDir);
599
+ const fallbackGate = firstUnapprovedGate(runState.approvals);
600
+ const currentGate = normalizeCurrentGate(runState.current_gate, fallbackGate);
601
+ const currentApprovalStatus = gateApprovalStatus(runState, currentGate);
602
+ const doctorBlocker = doctorReport.findings.find((finding) => finding.severity === "block");
603
+ const doctorRevise = doctorReport.findings.find((finding) => finding.severity === "revise");
604
+
605
+ let status = "open";
606
+ let blockingReason = "none";
607
+ let missingApproval = "none";
608
+ let allowed = [
609
+ "continue with the documented next allowed action",
610
+ "update evidence when new facts are observed",
611
+ "run doctor or gate-check again after control changes",
612
+ ];
613
+ let forbidden = [
614
+ "bypass the documented gate state",
615
+ "treat warnings as resolved without evidence",
616
+ ];
617
+ let nextAllowedAction = isPlaceholderValue(runState.next_allowed_action)
618
+ ? "Continue with the current documented gate only after setting next_allowed_action."
619
+ : runState.next_allowed_action;
620
+
621
+ if (doctorBlocker) {
622
+ status = "blocked";
623
+ blockingReason = doctorBlocker.code;
624
+ allowed = ["repair the AGDF control scaffold", "run doctor again"];
625
+ forbidden = ["continue governed delivery", "create later-gate artefacts", "implement gated work"];
626
+ nextAllowedAction = doctorBlocker.next_step;
627
+ } else if (doctorRevise) {
628
+ status = "blocked";
629
+ blockingReason = doctorRevise.code;
630
+ allowed = ["repair the incomplete control state", "run doctor again"];
631
+ forbidden = ["continue governed delivery", "create later-gate artefacts", "implement gated work"];
632
+ nextAllowedAction = doctorRevise.next_step;
633
+ } else if (userGateOrder.includes(currentGate) && currentApprovalStatus !== "approved" && currentApprovalStatus !== "not_applicable") {
634
+ status = "blocked";
635
+ blockingReason = "missing_exact_approval";
636
+ missingApproval = `Approval: ${currentGate}`;
637
+ allowed = ["clarify the current gate", "record evidence", "write OR-lite status", "request exact approval"];
638
+ forbidden = ["create later-gate artefacts", "implement gated work", "mark QA or UAT as passed"];
639
+ nextAllowedAction = `Request exact approval: Approval: ${currentGate}`;
640
+ }
641
+
642
+ return {
643
+ schema_version: "1",
644
+ status,
645
+ current_gate: currentGate,
646
+ blocking_reason: blockingReason,
647
+ missing_approval: missingApproval,
648
+ allowed,
649
+ forbidden,
650
+ next_allowed_action: nextAllowedAction,
651
+ doctor_status: doctorReport.status,
652
+ doctor_summary: doctorReport.summary,
653
+ evidence_refs: runState.evidence_refs,
654
+ doctor_report: doctorReport,
655
+ };
656
+ }
657
+
658
+ function printGateCheckReport(report, json) {
659
+ if (json) {
660
+ console.log(JSON.stringify(report, null, 2));
661
+ return;
662
+ }
663
+
664
+ console.log(`AGDF gate-check: ${report.status}`);
665
+ console.log(`Current gate: ${report.current_gate}`);
666
+ console.log(`Blocking reason: ${report.blocking_reason}`);
667
+ console.log(`Missing approval: ${report.missing_approval}`);
668
+ console.log(`Doctor: ${report.doctor_status} (${report.doctor_summary.findings} findings)`);
669
+ console.log("");
670
+ console.log("Allowed:");
671
+ for (const item of report.allowed) console.log(`- ${item}`);
672
+ console.log("");
673
+ console.log("Forbidden:");
674
+ for (const item of report.forbidden) console.log(`- ${item}`);
675
+ console.log("");
676
+ console.log(`Next allowed action: ${report.next_allowed_action}`);
677
+ }
678
+
220
679
  function main() {
221
680
  const options = parseArgs(process.argv.slice(2));
681
+
682
+ if (options.target === "doctor") {
683
+ const report = evaluateDoctor(options.dir);
684
+ printDoctorReport(report, options.json);
685
+ process.exit(report.status === "block" ? 2 : 0);
686
+ }
687
+
688
+ if (options.target === "gate-check") {
689
+ const report = evaluateGateCheck(options.dir);
690
+ printGateCheckReport(report, options.json);
691
+ process.exit(report.status === "blocked" ? 2 : 0);
692
+ }
693
+
222
694
  const files = generatedFilesForTarget(options.target, options.dir, options.force);
223
695
  const wroteAgentsFragment = files.some(file => file.path === agdfFragmentPath);
224
696
 
@@ -8,11 +8,11 @@ Use this scaffold when a target repository should keep AGDF state outside chat h
8
8
 
9
9
  | File | Purpose |
10
10
  |---|---|
11
- | `templates/AGDF_RUN.md` | Current run state: mode, gate, approvals, evidence, risks, next allowed action |
12
- | `templates/MASTER_BACKLOG.md` | Living backlog pointer: active initiatives and current UR/PRD/SD/TP artefacts |
13
- | `templates/SOT_REGISTRY.md` | Source-of-truth registry: one primary owner per domain |
14
- | `templates/CONTEXT_GRAPH.md` | Project memory: durable decisions, Brownfield findings, risks, evidence and exit criteria |
15
- | `templates/AGENT_QUALITY_CONTRACTS.json` | Machine-readable warning, revise and block contracts for review and QA |
11
+ | `templates/AGDF_RUN.md` | Template for current run state: mode, gate, approvals, evidence, risks, next allowed action |
12
+ | `templates/MASTER_BACKLOG.md` | Template for the living backlog pointer: active initiatives and current UR/PRD/SD/TP artefacts |
13
+ | `templates/SOT_REGISTRY.md` | Template for the source-of-truth registry: one primary owner per domain |
14
+ | `templates/CONTEXT_GRAPH.md` | Template for project memory: durable decisions, Brownfield findings, risks, evidence and exit criteria |
15
+ | `templates/AGENT_QUALITY_CONTRACTS.json` | Template for machine-readable warning, revise and block contracts for review and QA |
16
16
 
17
17
  ## Recommended Target Layout
18
18
 
@@ -28,6 +28,23 @@ Use this scaffold when a target repository should keep AGDF state outside chat h
28
28
 
29
29
  Keep these files small and reviewable. They are control artefacts, not a second documentation site.
30
30
 
31
+ Create the live files with:
32
+
33
+ ```bash
34
+ npm create agdf@latest init
35
+ ```
36
+
37
+ Then check whether the live state is actionable:
38
+
39
+ ```bash
40
+ npm create agdf@latest doctor
41
+ npm create agdf@latest doctor --json
42
+ npm create agdf@latest gate-check --json
43
+ ```
44
+
45
+ `doctor` does not replace agent judgment. It catches basic control failures: missing live files, missing current gate, missing next allowed action, empty evidence, empty backlog pointer, empty source-of-truth registry, duplicate active SoT rows and invalid quality contracts.
46
+ `gate-check` consumes the doctor result and live run state to report whether the next process step is `open` or `blocked`.
47
+
31
48
  ## Operating Rules
32
49
 
33
50
  - `AGDF_RUN.md` is the current run dashboard.
@@ -20,6 +20,19 @@ This skill must not create later artefacts such as PRD, SD, TP, CD, CR, QA, or U
20
20
  ## Runtime Contract
21
21
  Use `../agdf-runtime-contract.md` for gate terms, closeout discipline, and non-duplication rules.
22
22
 
23
+ ## Machine-Readable Control Path
24
+
25
+ When the target repository contains live AGDF control files, prefer the executable control path before deriving a gate decision manually:
26
+
27
+ ```bash
28
+ npm create agdf@latest doctor --json
29
+ npm create agdf@latest gate-check --json
30
+ ```
31
+
32
+ `doctor` checks whether `.agdf/control/` is actionable. `agdf-gate-check` consumes that result and `AGDF_RUN.md` to report the operative process decision: `open | blocked`, current gate, blocking reason, missing approval, allowed outputs, forbidden outputs, next allowed action and evidence references.
33
+
34
+ The CLI report is evidence for this skill, not a second rule system. If the report says `blocked`, do not continue with later-gate artefacts until the reported blocker is resolved.
35
+
23
36
  ## Rules
24
37
  1. Fail closed when a required approval or artefact status is missing.
25
38
  2. The earliest blocking gate wins.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agdf",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Codex-first workflow skills for gated, auditable AI-assisted software delivery.",
5
5
  "author": {
6
6
  "name": "AI-native Governance & Delivery Framework",
@@ -8,11 +8,11 @@ Use this scaffold when a target repository should keep AGDF state outside chat h
8
8
 
9
9
  | File | Purpose |
10
10
  |---|---|
11
- | `templates/AGDF_RUN.md` | Current run state: mode, gate, approvals, evidence, risks, next allowed action |
12
- | `templates/MASTER_BACKLOG.md` | Living backlog pointer: active initiatives and current UR/PRD/SD/TP artefacts |
13
- | `templates/SOT_REGISTRY.md` | Source-of-truth registry: one primary owner per domain |
14
- | `templates/CONTEXT_GRAPH.md` | Project memory: durable decisions, Brownfield findings, risks, evidence and exit criteria |
15
- | `templates/AGENT_QUALITY_CONTRACTS.json` | Machine-readable warning, revise and block contracts for review and QA |
11
+ | `templates/AGDF_RUN.md` | Template for current run state: mode, gate, approvals, evidence, risks, next allowed action |
12
+ | `templates/MASTER_BACKLOG.md` | Template for the living backlog pointer: active initiatives and current UR/PRD/SD/TP artefacts |
13
+ | `templates/SOT_REGISTRY.md` | Template for the source-of-truth registry: one primary owner per domain |
14
+ | `templates/CONTEXT_GRAPH.md` | Template for project memory: durable decisions, Brownfield findings, risks, evidence and exit criteria |
15
+ | `templates/AGENT_QUALITY_CONTRACTS.json` | Template for machine-readable warning, revise and block contracts for review and QA |
16
16
 
17
17
  ## Recommended Target Layout
18
18
 
@@ -28,6 +28,23 @@ Use this scaffold when a target repository should keep AGDF state outside chat h
28
28
 
29
29
  Keep these files small and reviewable. They are control artefacts, not a second documentation site.
30
30
 
31
+ Create the live files with:
32
+
33
+ ```bash
34
+ npm create agdf@latest init
35
+ ```
36
+
37
+ Then check whether the live state is actionable:
38
+
39
+ ```bash
40
+ npm create agdf@latest doctor
41
+ npm create agdf@latest doctor --json
42
+ npm create agdf@latest gate-check --json
43
+ ```
44
+
45
+ `doctor` does not replace agent judgment. It catches basic control failures: missing live files, missing current gate, missing next allowed action, empty evidence, empty backlog pointer, empty source-of-truth registry, duplicate active SoT rows and invalid quality contracts.
46
+ `gate-check` consumes the doctor result and live run state to report whether the next process step is `open` or `blocked`.
47
+
31
48
  ## Operating Rules
32
49
 
33
50
  - `AGDF_RUN.md` is the current run dashboard.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "id": "agdf",
4
- "version": "0.1.7",
4
+ "version": "0.1.9",
5
5
  "displayName": "AI Governance & Delivery Framework",
6
6
  "developerName": "AI-native Governance & Delivery Framework",
7
7
  "description": "Codex-first workflow skills for gated, auditable AI-assisted software delivery.",
@@ -20,6 +20,19 @@ This skill must not create later artefacts such as PRD, SD, TP, CD, CR, QA, or U
20
20
  ## Runtime Contract
21
21
  Use `../../meta/agdf-runtime-contract.md` for gate terms, closeout discipline, and non-duplication rules.
22
22
 
23
+ ## Machine-Readable Control Path
24
+
25
+ When the target repository contains live AGDF control files, prefer the executable control path before deriving a gate decision manually:
26
+
27
+ ```bash
28
+ npm create agdf@latest doctor --json
29
+ npm create agdf@latest gate-check --json
30
+ ```
31
+
32
+ `doctor` checks whether `.agdf/control/` is actionable. `gate-check` consumes that result and `AGDF_RUN.md` to report the operative process decision: `open | blocked`, current gate, blocking reason, missing approval, allowed outputs, forbidden outputs, next allowed action and evidence references.
33
+
34
+ The CLI report is evidence for this skill, not a second rule system. If the report says `blocked`, do not continue with later-gate artefacts until the reported blocker is resolved.
35
+
23
36
  ## Rules
24
37
  1. Fail closed when a required approval or artefact status is missing.
25
38
  2. The earliest blocking gate wins.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agdf",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Bootstrap AGDF repository-local Codex plugin marketplaces, Copilot instructions, skills and control templates.",
5
5
  "type": "module",
6
6
  "files": [