create-agdf 0.7.0 → 0.7.2

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 (28) hide show
  1. package/README.md +43 -5
  2. package/bin/create-agdf.js +186 -10
  3. package/generated/.agdf/control/README.md +2 -1
  4. package/generated/.agdf/control/templates/AGDF_RUN.md +4 -3
  5. package/generated/.agdf/control/templates/RUN_STATE.md +2 -1
  6. package/generated/.agdf/control/templates/artefacts/BROWNFIELD_REVIEW.md +1 -1
  7. package/generated/.agdf/control/templates/artefacts/VERIFIED_CHANGE.md +48 -0
  8. package/generated/.github/skills/agdf-gate-check/SKILL.md +27 -0
  9. package/generated/.github/skills/agdf-runtime-contract.md +76 -5
  10. package/generated/.opencode/AGDF.md +5 -4
  11. package/generated/.opencode/README.md +2 -1
  12. package/generated/.opencode/agdf-runtime-contract.md +76 -5
  13. package/generated/.opencode/skills/agdf-gate-check/SKILL.md +27 -0
  14. package/generated/AGENTS.md +3 -3
  15. package/generated/opencode.json +1 -0
  16. package/generated/plugins/agdf/.codex-plugin/plugin.json +2 -2
  17. package/generated/plugins/agdf/control/README.md +2 -1
  18. package/generated/plugins/agdf/control/templates/AGDF_RUN.md +4 -3
  19. package/generated/plugins/agdf/control/templates/RUN_STATE.md +2 -1
  20. package/generated/plugins/agdf/control/templates/artefacts/BROWNFIELD_REVIEW.md +1 -1
  21. package/generated/plugins/agdf/control/templates/artefacts/VERIFIED_CHANGE.md +48 -0
  22. package/generated/plugins/agdf/meta/agdf-agent-router.md +3 -3
  23. package/generated/plugins/agdf/meta/agdf-constitution.md +3 -3
  24. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +32 -2
  25. package/generated/plugins/agdf/meta/agdf-runtime-contract.md +76 -5
  26. package/generated/plugins/agdf/meta/agdf-tenets.md +5 -0
  27. package/generated/plugins/agdf/skills/gate-check/SKILL.md +27 -0
  28. package/package.json +4 -2
package/README.md CHANGED
@@ -1,8 +1,34 @@
1
1
  # create-agdf
2
2
 
3
- Bootstrap AGDF repository instructions and a machine-checkable control loop for one repository.
3
+ Bootstrap AGDF into the coding-agent surface and repository where work actually happens. It installs or generates the surface-specific guidance, skills and optional durable control files that help teams make scope, approvals, evidence and delivery state visible.
4
4
 
5
- ## Usage
5
+ AGDF is useful when AI-assisted work can affect an existing system and a team needs a traceable answer to: what is allowed next, who approved it and what evidence supports it. It is deliberately not a substitute for engineering judgement, security review, tests, product ownership or human acceptance. For the framework's fit, limits and examples, start with the [project overview](../README.md).
6
+
7
+ ## Quick start
8
+
9
+ If you use Codex, this is the recommended first installation:
10
+
11
+ ```bash
12
+ npx --yes @agdf/cli@latest codex
13
+ ```
14
+
15
+ Restart Codex, open a new task in the repository you want to work in, and describe the intended change. AGDF starts with the smallest permitted governance step; it does not make implementation automatic.
16
+
17
+ Use a different target when Codex is not your agent surface:
18
+
19
+ | Surface or goal | Start with | What it gives you |
20
+ |---|---|---|
21
+ | Claude Code | `npx --yes @agdf/cli@latest claude` | The AGDF plugin for Claude Code. |
22
+ | OpenCode, user-wide discovery | `npx --yes @agdf/cli@latest opencode` | The npm plugin and global native skills; repository governance remains opt-in. |
23
+ | OpenCode, one repository | `npx --yes @agdf/cli@latest opencode-repo` | Repository instructions, native skills, permissions and control templates. |
24
+ | GitHub Copilot | `npm create agdf@latest -- copilot` | Repository instructions, visible skills and control templates. |
25
+ | Durable control state in an existing setup | `npx --yes @agdf/cli@latest init` | Live `.agdf/control/` state when the repository explicitly needs it. |
26
+
27
+ For prerequisites, all surface-specific flows and operational boundaries, use the authoritative [installation guide](../INSTALL.md). Do not run `init` merely to ask a fresh question: an agent can first clarify the request and ask for `Approval: UR` when durable control state is needed.
28
+
29
+ ## Full command reference
30
+
31
+ The commands below are the complete package reference. The Quick Start above is the recommended entry point; these commands cover the remaining surfaces, repository-local variants and validation paths.
6
32
 
7
33
  Preferred long-term CLI shape:
8
34
 
@@ -53,7 +79,7 @@ If no language is provided, `create-agdf` derives the preference from the local
53
79
  - `copilot` writes `AGENTS.md`, Copilot custom instructions under `.github/`, visible repository skills under `.github/skills/`, and AGDF control templates under `.agdf/control/`
54
80
  - `opencode` installs the AGDF npm plugin and nine native skills as a user-wide OpenCode surface
55
81
  - `opencode-status` reports OpenCode global config, package loadability, global native-skill completeness, session signals and repository surface presence
56
- - `opencode-repo` writes `opencode.json`, `.opencode/AGDF.md`, prefixed native OpenCode skills under `.opencode/skills/`, explicit edit/bash/skill permissions, and AGDF control templates under `.agdf/control/`
82
+ - `opencode-repo` writes `opencode.json`, `.opencode/AGDF.md`, prefixed native OpenCode skills under `.opencode/skills/`, explicit question/edit/bash/skill permissions, and AGDF control templates under `.agdf/control/`
57
83
  - `both` writes the Codex repository-local marketplace plus the Copilot-facing repository files
58
84
  - `config` writes or updates only `.agdf/control/config.json` for an already installed plugin or an existing repository
59
85
 
@@ -84,9 +110,9 @@ Use the `opencode-repo` target when AGDF should be available to OpenCode from re
84
110
  npm create agdf@latest -- opencode-repo
85
111
  ```
86
112
 
87
- OpenCode loads the AGDF npm plugin and global native skills from global OpenCode config, plus repository instructions from `.opencode/AGDF.md`, explicit `edit`/`bash`/`skill` permissions and canonical `agdf-` project skills under `.opencode/skills/`. Global adapters use the collision-safe `agdf-global-` prefix. AGDF for OpenCode has a global discoverability layer installed with `npx --yes @agdf/cli@latest opencode` and a repository-local governance surface generated by `opencode-repo`.
113
+ OpenCode loads the AGDF npm plugin and global native skills from global OpenCode config, plus repository instructions from `.opencode/AGDF.md`, explicit `question`/`edit`/`bash`/`skill` permissions and canonical `agdf-` project skills under `.opencode/skills/`. Global adapters use the collision-safe `agdf-global-` prefix. AGDF for OpenCode has a global discoverability layer installed with `npx --yes @agdf/cli@latest opencode` and a repository-local governance surface generated by `opencode-repo`.
88
114
  Those generated skills load on demand through OpenCode's native `skill` tool. Use `agdf-gate-check` first for new build/change intent or unclear approval.
89
- If `opencode.json` already exists, AGDF keeps it unchanged and writes `opencode.agdf.json` as a merge fragment.
115
+ The built-in `question` tool can present a gate choice, but `.agdf/control/` and exact post-response validation remain authoritative; technical permission and auto-mode outcomes never approve an AGDF gate. If `opencode.json` already exists, AGDF keeps it unchanged and writes `opencode.agdf.json` as a merge fragment. Preserve any explicit `permission.question` decision when reviewing that fragment; `deny` uses exact-text fallback.
90
116
  The global layer makes the nine native AGDF skills discoverable, but does not activate repository governance by itself. Global skills fail closed until the current repository has `.opencode/AGDF.md`, `.opencode/skills/agdf-gate-check/SKILL.md` and `.agdf/control/`. Use `opencode-repo` to write that repository surface.
91
117
 
92
118
  ## Control scaffold
@@ -203,6 +229,18 @@ npm run test:routing
203
229
 
204
230
  The routing test installs `both` into a temporary target repository and checks that plugin routing stays unprefixed while Copilot routing receives the configured `agdf-` prefix.
205
231
 
232
+ ## Project, contribution and support
233
+
234
+ This README is the package guide. Keep framework rationale, limits and examples in the [project overview](../README.md); keep target-specific installation in [INSTALL.md](../INSTALL.md); and use [RELEASE.md](../RELEASE.md) only when maintaining a published AGDF release.
235
+
236
+ Non-sensitive feedback, examples and contributions are welcome through [GitHub Issues](https://github.com/ArndtGold/ai-native-governance-delivery-framework/issues). To validate a local package change before proposing it, run:
237
+
238
+ ```bash
239
+ npm --prefix create-agdf run smoke-test
240
+ ```
241
+
242
+ The repository is licensed under [Apache-2.0](../LICENSE). No separate public `CONTRIBUTING.md`, `SECURITY.md` or private security-reporting channel is currently published. Do not treat a public issue as a private vulnerability disclosure.
243
+
206
244
  ## Publishing
207
245
 
208
246
  The repository publishes this package and the primary user-facing `@agdf/cli` wrapper as one coupled AGDF release.
@@ -3,7 +3,7 @@
3
3
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { execFileSync } from "node:child_process";
5
5
  import { homedir } from "node:os";
6
- import { dirname, join, posix, resolve } from "node:path";
6
+ import { dirname, isAbsolute, join, posix, relative, resolve } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import process from "node:process";
9
9
  import { searchInputFromControl } from "../lib/delivery-path-search/state-adapter.js";
@@ -48,7 +48,7 @@ const agdfFragmentPath = "AGENTS.agdf.md";
48
48
  const openCodeConfigFragmentPath = "opencode.agdf.json";
49
49
  const userGateOrder = ["UR", "PRD", "SD", "TP", "QA", "UAT"];
50
50
  const durableGateArtefacts = new Set(["UR", "PRD", "SD", "TP", "QA"]);
51
- const internalStepArtefacts = new Set(["Brownfield Review", "Brownfield Analysis", "CD+Tests", "CR"]);
51
+ const internalStepArtefacts = new Set(["Brownfield Review", "Verified Change", "Brownfield Analysis", "CD+Tests", "CR"]);
52
52
  const deliveryRelationships = [
53
53
  { from: "UR", relationship: "approved_by", to: "Approval: UR", requiredBy: "UR" },
54
54
  { from: "PRD", relationship: "derived_from", to: "UR", requiredBy: "PRD" },
@@ -61,6 +61,7 @@ const nextSkillByGate = {
61
61
  "Brownfield Review": "brownfield-analysis",
62
62
  "Mode/Slice Decision": "gate-check",
63
63
  "Quick Task Execution": "none",
64
+ "Verified Change Execution": "gate-check",
64
65
  PRD: "gate-check",
65
66
  SD: "gate-check",
66
67
  TP: "gate-check",
@@ -90,6 +91,7 @@ const codexPluginFiles = [
90
91
  join("plugins", "agdf", "control", "templates", "AGENT_QUALITY_CONTRACTS.json"),
91
92
  join("plugins", "agdf", "control", "templates", "artefacts", "UR.md"),
92
93
  join("plugins", "agdf", "control", "templates", "artefacts", "BROWNFIELD_REVIEW.md"),
94
+ join("plugins", "agdf", "control", "templates", "artefacts", "VERIFIED_CHANGE.md"),
93
95
  join("plugins", "agdf", "control", "templates", "artefacts", "PRD.md"),
94
96
  join("plugins", "agdf", "control", "templates", "artefacts", "SD.md"),
95
97
  join("plugins", "agdf", "control", "templates", "artefacts", "TP.md"),
@@ -114,6 +116,7 @@ const controlFiles = [
114
116
  join(".agdf", "control", "templates", "AGENT_QUALITY_CONTRACTS.json"),
115
117
  join(".agdf", "control", "templates", "artefacts", "UR.md"),
116
118
  join(".agdf", "control", "templates", "artefacts", "BROWNFIELD_REVIEW.md"),
119
+ join(".agdf", "control", "templates", "artefacts", "VERIFIED_CHANGE.md"),
117
120
  join(".agdf", "control", "templates", "artefacts", "PRD.md"),
118
121
  join(".agdf", "control", "templates", "artefacts", "SD.md"),
119
122
  join(".agdf", "control", "templates", "artefacts", "TP.md"),
@@ -145,6 +148,7 @@ const liveControlFiles = [
145
148
  const artefactTemplateFiles = [
146
149
  join(".agdf", "control", "templates", "artefacts", "UR.md"),
147
150
  join(".agdf", "control", "templates", "artefacts", "BROWNFIELD_REVIEW.md"),
151
+ join(".agdf", "control", "templates", "artefacts", "VERIFIED_CHANGE.md"),
148
152
  join(".agdf", "control", "templates", "artefacts", "PRD.md"),
149
153
  join(".agdf", "control", "templates", "artefacts", "SD.md"),
150
154
  join(".agdf", "control", "templates", "artefacts", "TP.md"),
@@ -510,9 +514,10 @@ function installOpenCodeGlobalPlugin(configDir) {
510
514
  if (!nextConfig.instructions.includes("AGDF.md")) nextConfig.instructions.push("AGDF.md");
511
515
 
512
516
  if (nextConfig.permission === undefined) {
513
- nextConfig.permission = { skill: { "agdf-*": "allow" } };
517
+ nextConfig.permission = { question: "allow", skill: { "agdf-*": "allow" } };
514
518
  } else if (nextConfig.permission && typeof nextConfig.permission === "object" && !Array.isArray(nextConfig.permission)) {
515
519
  nextConfig.permission = { ...nextConfig.permission };
520
+ if (nextConfig.permission.question === undefined) nextConfig.permission.question = "allow";
516
521
  if (nextConfig.permission.skill === undefined) {
517
522
  nextConfig.permission.skill = { "agdf-*": "allow" };
518
523
  } else if (nextConfig.permission.skill && typeof nextConfig.permission.skill === "object" && !Array.isArray(nextConfig.permission.skill)) {
@@ -1161,7 +1166,7 @@ function printNextSteps(target, destination, files, wroteAgentsFragment, wroteOp
1161
1166
  }
1162
1167
  if (target === "opencode-repo") {
1163
1168
  if (wroteOpenCodeConfigFragment) {
1164
- console.log(`- Existing opencode.json detected. Merge ${openCodeConfigFragmentPath} into your current opencode.json so OpenCode loads .opencode/AGDF.md.`);
1169
+ console.log(`- Existing opencode.json detected. Review ${openCodeConfigFragmentPath} and merge its owned entries so OpenCode loads .opencode/AGDF.md; preserve an explicit permission.question decision.`);
1165
1170
  }
1166
1171
  console.log(`- OpenCode will install the ${pluginDefinition.opencode.npmPackage} npm plugin from opencode.json at startup.`);
1167
1172
  console.log("- Optional: also add create-agdf to ~/.config/opencode/opencode.json plugin[] for a user-wide OpenCode hook.");
@@ -1709,6 +1714,12 @@ function evaluateDoctor(targetDir, selection = {}) {
1709
1714
  }
1710
1715
 
1711
1716
  const runState = selectedRunState;
1717
+ if (modeSliceDecision(runState) === "verified_change") {
1718
+ const verifiedChange = evaluateVerifiedChange(targetDir, runState);
1719
+ for (const finding of verifiedChange.findings) {
1720
+ addFinding(findings, finding.severity, finding.code, finding.message, finding.path, finding.next_step);
1721
+ }
1722
+ }
1712
1723
  for (const finding of analyzeDurableGateArtefactConsistency(runState)) {
1713
1724
  addFinding(findings, finding.severity, finding.code, finding.message, finding.path, finding.next_step);
1714
1725
  }
@@ -1760,6 +1771,115 @@ function extractField(content, field) {
1760
1771
  return content.match(pattern)?.[1]?.trim() ?? "";
1761
1772
  }
1762
1773
 
1774
+ const verifiedChangeStatuses = new Set(["draft", "eligible", "executed", "escalated"]);
1775
+ const verifiedChangeEscalationTargets = new Set(["structured_slice", "structured_delivery"]);
1776
+
1777
+ function parseVerifiedChangePathList(value) {
1778
+ const cleaned = cleanStatusCell(value ?? "");
1779
+ if (!cleaned || cleaned === "none") return [];
1780
+ return cleaned.split(",").map((item) => item.trim()).filter(Boolean);
1781
+ }
1782
+
1783
+ function isSafeRepoRelativePath(value) {
1784
+ if (!value || value === "none" || value.startsWith("/") || value.includes("\\")) return false;
1785
+ const normalized = posix.normalize(value);
1786
+ return normalized !== "." && normalized !== ".." && !normalized.startsWith("../") && normalized === value;
1787
+ }
1788
+
1789
+ function gitPathList(targetDir, args) {
1790
+ try {
1791
+ return execFileSync("git", args, { cwd: targetDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] })
1792
+ .split("\n")
1793
+ .map((item) => item.trim())
1794
+ .filter(Boolean);
1795
+ } catch {
1796
+ return null;
1797
+ }
1798
+ }
1799
+
1800
+ function readVerifiedChangeRecord(targetDir, runState) {
1801
+ const artefact = runState.artefacts.get("Verified Change");
1802
+ if (!artefact?.path || isPlaceholderValue(artefact.path)) return { status: "missing", path: "", content: "" };
1803
+ if (!isSafeRepoRelativePath(artefact.path)) return { status: "invalid", path: artefact.path, content: "", error: "record_path_invalid" };
1804
+ const absolutePath = resolve(targetDir, artefact.path);
1805
+ const relativePath = relative(resolve(targetDir), absolutePath);
1806
+ if (isAbsolute(relativePath) || relativePath === ".." || relativePath.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || !existsSync(absolutePath)) {
1807
+ return { status: "missing", path: artefact.path, content: "", error: "record_missing" };
1808
+ }
1809
+ return { status: "present", path: artefact.path, content: readFileSync(absolutePath, "utf8") };
1810
+ }
1811
+
1812
+ function evaluateVerifiedChange(targetDir, runState) {
1813
+ const record = readVerifiedChangeRecord(targetDir, runState);
1814
+ const findings = [];
1815
+ const add = (code, message, nextStep, severity = "revise") => findings.push({
1816
+ severity,
1817
+ code,
1818
+ message,
1819
+ path: record.path || runState.path,
1820
+ next_step: nextStep,
1821
+ });
1822
+
1823
+ if (record.status === "missing") {
1824
+ return { status: "missing", record, findings };
1825
+ }
1826
+ if (record.status === "invalid") {
1827
+ add("AGDF_VERIFIED_CHANGE_RECORD_PATH_INVALID", "Verified Change record path must be a repository-relative path inside the target repository.", "Link a stable repository-relative VERIFIED_CHANGE.md artefact from the selected run state.", "block");
1828
+ return { status: "invalid", record, findings };
1829
+ }
1830
+
1831
+ const status = cleanStatusCell(extractField(record.content, "status"));
1832
+ const relatedUr = cleanStatusCell(extractField(record.content, "related_ur"));
1833
+ const escalationTarget = cleanStatusCell(extractField(record.content, "escalation_target"));
1834
+ const canonicalOwner = cleanStatusCell(extractField(record.content, "canonical_owner"));
1835
+ const sourcePaths = parseVerifiedChangePathList(extractField(record.content, "allowed_source_paths"));
1836
+ const derivedPaths = parseVerifiedChangePathList(extractField(record.content, "allowed_derived_paths"));
1837
+ const prohibitedImpacts = cleanStatusCell(extractField(record.content, "prohibited_impacts"));
1838
+ const propagationCommand = extractField(record.content, "propagation_command");
1839
+ const validationCommands = extractField(record.content, "validation_commands");
1840
+ const baselineTracked = parseVerifiedChangePathList(extractField(record.content, "baseline_tracked_paths"));
1841
+ const baselineUntracked = parseVerifiedChangePathList(extractField(record.content, "baseline_untracked_paths"));
1842
+ const validationStatus = cleanStatusCell(extractField(record.content, "validation_status"));
1843
+ const propagationStatus = cleanStatusCell(extractField(record.content, "propagation_status"));
1844
+ const urArtefact = runState.artefacts.get("UR");
1845
+
1846
+ if (!verifiedChangeStatuses.has(status)) add("AGDF_VERIFIED_CHANGE_STATUS_INVALID", "Verified Change record must use status draft, eligible, executed or escalated.", "Set a supported record status or escalate to the declared structured target.");
1847
+ if (!isSafeRepoRelativePath(relatedUr) || relatedUr !== urArtefact?.path) add("AGDF_VERIFIED_CHANGE_RELATED_UR_INVALID", "Verified Change record must link exactly to the selected run's repository-relative UR artefact.", "Set related_ur to the selected run's durable UR artefact path.");
1848
+ if (!verifiedChangeEscalationTargets.has(escalationTarget)) add("AGDF_VERIFIED_CHANGE_ESCALATION_INVALID", "Verified Change record must declare structured_slice or structured_delivery as its escalation target.", "Record the Brownfield-selected structured escalation target.");
1849
+ if (!canonicalOwner || canonicalOwner.includes(",") || !isSafeRepoRelativePath(canonicalOwner)) add("AGDF_VERIFIED_CHANGE_OWNER_INVALID", "Verified Change requires exactly one repository-relative canonical_owner.", "Name one canonical owner path; otherwise escalate.");
1850
+ if (sourcePaths.length === 0 || sourcePaths.some((path) => !isSafeRepoRelativePath(path))) add("AGDF_VERIFIED_CHANGE_SOURCE_PATHS_INVALID", "Verified Change requires non-empty normalized allowed_source_paths.", "Declare a bounded comma-separated source path list; otherwise escalate.");
1851
+ if (derivedPaths.some((path) => !isSafeRepoRelativePath(path))) add("AGDF_VERIFIED_CHANGE_DERIVED_PATHS_INVALID", "Verified Change derived paths must be normalized repository-relative paths or none.", "Correct the derived path list or escalate.");
1852
+ if (prohibitedImpacts !== "none") add("AGDF_VERIFIED_CHANGE_IMPACTS_INVALID", "Verified Change must declare prohibited_impacts: none after checking gates, permissions, security, persistence, architecture, external API, CLI and release behavior.", "Use the structured path when any prohibited impact applies.");
1853
+ if (!validationCommands || validationCommands === "none") add("AGDF_VERIFIED_CHANGE_VALIDATION_MISSING", "Verified Change requires at least one deterministic validation command.", "Record a deterministic acceptance or consistency check, or escalate.");
1854
+ if (derivedPaths.length > 0 && (!propagationCommand || propagationCommand === "none")) add("AGDF_VERIFIED_CHANGE_PROPAGATION_MISSING", "Derived paths require a deterministic propagation command.", "Record the propagation command or escalate.");
1855
+ if (!extractField(record.content, "baseline_tracked_paths") || !extractField(record.content, "baseline_untracked_paths")) add("AGDF_VERIFIED_CHANGE_BASELINE_MISSING", "Verified Change requires both tracked and untracked baseline path fields, using none when empty.", "Capture the worktree baseline before marking the record eligible.");
1856
+
1857
+ const allowedPaths = new Set([...sourcePaths, ...derivedPaths]);
1858
+ const baselinePaths = new Set([...baselineTracked, ...baselineUntracked]);
1859
+ const dirtyCandidate = [...allowedPaths].find((path) => baselinePaths.has(path));
1860
+ if (dirtyCandidate) add("AGDF_VERIFIED_CHANGE_BASELINE_CANDIDATE_DIRTY", `Declared candidate path is already dirty at baseline: ${dirtyCandidate}.`, "Escalate or start from a clean candidate path; do not adopt pre-existing edits.", "block");
1861
+
1862
+ const currentTracked = gitPathList(targetDir, ["diff", "HEAD", "--name-only"]);
1863
+ const currentUntracked = gitPathList(targetDir, ["ls-files", "--others", "--exclude-standard"]);
1864
+ if (currentTracked === null || currentUntracked === null) {
1865
+ add("AGDF_VERIFIED_CHANGE_GIT_BASELINE_UNAVAILABLE", "Verified Change requires a readable Git worktree for scoped baseline validation.", "Use the structured path or restore Git worktree access.", "block");
1866
+ } else {
1867
+ const currentPaths = new Set([...currentTracked, ...currentUntracked]);
1868
+ const permittedControlPaths = new Set([record.path, runState.path, ".agdf/control/MASTER_BACKLOG.md"]);
1869
+ const unexpected = [...currentPaths].filter((path) => !baselinePaths.has(path) && !allowedPaths.has(path) && !permittedControlPaths.has(path));
1870
+ if (unexpected.length > 0) add("AGDF_VERIFIED_CHANGE_SCOPE_ESCAPE", `Verified Change introduced unlisted path(s): ${unexpected.join(", ")}.`, "Mark the record escalated and continue at the declared structured target.", "block");
1871
+ }
1872
+
1873
+ if (status === "executed") {
1874
+ if (validationStatus !== "pass") add("AGDF_VERIFIED_CHANGE_VALIDATION_EVIDENCE_MISSING", "Executed Verified Change requires validation_status: pass.", "Record passing deterministic validation evidence or escalate.");
1875
+ if (derivedPaths.length > 0 && propagationStatus !== "pass") add("AGDF_VERIFIED_CHANGE_PROPAGATION_EVIDENCE_MISSING", "Executed Verified Change with derived paths requires propagation_status: pass.", "Record successful propagation evidence or escalate.");
1876
+ }
1877
+
1878
+ if (status === "escalated") return { status: "escalated", record, escalation_target: escalationTarget, findings };
1879
+ if (findings.length > 0) return { status: "invalid", record, escalation_target: escalationTarget, findings };
1880
+ return { status, record, escalation_target: escalationTarget, findings };
1881
+ }
1882
+
1763
1883
  function cleanStatusCell(value) {
1764
1884
  return value.replace(/^`|`$/g, "").trim();
1765
1885
  }
@@ -2117,7 +2237,7 @@ function isGateSatisfied(runState, gate) {
2117
2237
  return isDurableGateArtefactSatisfied(runState, gate);
2118
2238
  }
2119
2239
 
2120
- function transitionDecisionForRunState(runState) {
2240
+ function transitionDecisionForRunState(runState, verifiedChange = null) {
2121
2241
  if (gateApprovalStatus(runState, "UR") === "approved" && !isGateSatisfied(runState, "UR")) return durableArtefactBlock("UR", "PRD");
2122
2242
 
2123
2243
  if (!isGateSatisfied(runState, "UR")) {
@@ -2159,7 +2279,7 @@ function transitionDecisionForRunState(runState) {
2159
2279
  blocking_reason: "none",
2160
2280
  missing_approval: "none",
2161
2281
  allowed: [
2162
- "decide whether the approved UR is quick_task, structured_slice, structured_delivery or block",
2282
+ "decide whether the approved UR is quick_task, verified_change, structured_slice, structured_delivery or block",
2163
2283
  "record scope reason, evidence and required next gate depth in the selected RUN_STATE.md",
2164
2284
  "choose the next required gate depth before drafting PRD or implementing",
2165
2285
  ],
@@ -2192,6 +2312,53 @@ function transitionDecisionForRunState(runState) {
2192
2312
  };
2193
2313
  }
2194
2314
 
2315
+ if (modeDecision === "verified_change") {
2316
+ const state = verifiedChange?.status ?? "missing";
2317
+ const escalationTarget = verifiedChange?.escalation_target ?? "structured_slice";
2318
+ if (state === "executed") {
2319
+ return {
2320
+ status: "open",
2321
+ current_gate: "OR",
2322
+ blocking_reason: "none",
2323
+ missing_approval: "none",
2324
+ allowed: ["use the Verified Change mini-closeout", "offer delivery closeout when requested"],
2325
+ forbidden: ["create PRD, SD, TP, QA or UAT by ritual", "commit, push, open PR or release automatically"],
2326
+ next_allowed_action: "Close the executed Verified Change with its compact record and offer delivery closeout; do not perform VCS actions automatically.",
2327
+ };
2328
+ }
2329
+ if (state === "escalated" || (state === "invalid" && verifiedChangeEscalationTargets.has(escalationTarget))) {
2330
+ return {
2331
+ status: "open",
2332
+ current_gate: "PRD",
2333
+ blocking_reason: state === "escalated" ? "verified_change_escalated" : "verified_change_invalid_escalated",
2334
+ missing_approval: "Approval: PRD",
2335
+ allowed: ["draft the structured PRD required by the declared escalation target", "retain the Verified Change escalation evidence"],
2336
+ forbidden: ["implement through Verified Change", "create SD or TP before PRD approval", "claim QA or release readiness"],
2337
+ next_allowed_action: `Proceed as ${escalationTarget}: draft the required PRD and request exact approval: Approval: PRD.`,
2338
+ };
2339
+ }
2340
+ if (state === "eligible") {
2341
+ return {
2342
+ status: "open",
2343
+ current_gate: "Verified Change Execution",
2344
+ blocking_reason: "none",
2345
+ missing_approval: "none",
2346
+ allowed: ["implement only declared source and derived paths", "run declared propagation and validation commands", "record execution evidence and mini-closeout"],
2347
+ forbidden: ["touch unlisted paths", "add prohibited impacts", "claim QA, UAT or release readiness"],
2348
+ next_allowed_action: "Implement only the eligible Verified Change record scope, then record passing propagation and validation evidence.",
2349
+ };
2350
+ }
2351
+ return {
2352
+ status: state === "missing" || state === "draft" ? "open" : "blocked",
2353
+ current_gate: "Verified Change Execution",
2354
+ blocking_reason: state === "invalid" ? "verified_change_invalid" : "verified_change_record_required",
2355
+ missing_approval: "none",
2356
+ allowed: ["create or refine the compact Verified Change record", "capture baseline paths", "prove fail-closed eligibility with doctor and gate-check"],
2357
+ forbidden: ["implement candidate changes", "claim validation, QA, UAT or release readiness"],
2358
+ next_allowed_action: "Create or repair VERIFIED_CHANGE.md, capture the baseline and satisfy every eligibility check before implementation; escalate when any condition cannot be proven.",
2359
+ };
2360
+ }
2361
+
2195
2362
  return {
2196
2363
  status: "open",
2197
2364
  current_gate: "PRD",
@@ -2310,10 +2477,16 @@ function transitionDecisionForRunState(runState) {
2310
2477
  function evaluateGateCheck(targetDir, selection = {}) {
2311
2478
  const doctorReport = evaluateDoctor(targetDir, selection);
2312
2479
  const runState = readRunState(targetDir, selection);
2313
- const transitionDecision = transitionDecisionForRunState(runState);
2480
+ const verifiedChange = modeSliceDecision(runState) === "verified_change"
2481
+ ? evaluateVerifiedChange(targetDir, runState)
2482
+ : null;
2483
+ const transitionDecision = transitionDecisionForRunState(runState, verifiedChange);
2314
2484
  const deliveryMap = analyzeDeliveryMap(runState);
2315
2485
  const doctorBlocker = doctorReport.findings.find((finding) => finding.severity === "block");
2316
2486
  const doctorRevise = doctorReport.findings.find((finding) => finding.severity === "revise");
2487
+ const routesInvalidVerifiedChange = modeSliceDecision(runState) === "verified_change"
2488
+ && verifiedChange?.status === "invalid"
2489
+ && verifiedChangeEscalationTargets.has(verifiedChange.escalation_target);
2317
2490
 
2318
2491
  let status = transitionDecision.status;
2319
2492
  let currentGate = transitionDecision.current_gate;
@@ -2321,7 +2494,9 @@ function evaluateGateCheck(targetDir, selection = {}) {
2321
2494
  let missingApproval = transitionDecision.missing_approval;
2322
2495
  let allowed = transitionDecision.allowed;
2323
2496
  let forbidden = transitionDecision.forbidden;
2324
- let nextAllowedAction = isPlaceholderValue(runState.next_allowed_action)
2497
+ let nextAllowedAction = modeSliceDecision(runState) === "verified_change"
2498
+ ? transitionDecision.next_allowed_action
2499
+ : isPlaceholderValue(runState.next_allowed_action)
2325
2500
  ? transitionDecision.next_allowed_action
2326
2501
  : runState.next_allowed_action;
2327
2502
 
@@ -2337,13 +2512,13 @@ function evaluateGateCheck(targetDir, selection = {}) {
2337
2512
  ];
2338
2513
  forbidden = ["create PRD", "create SD", "create TP", "run Brownfield Analysis", "implement code", "claim QA or release readiness"];
2339
2514
  nextAllowedAction = "Draft the minimal UR for the request in the response, then ask for exact approval: Approval: UR. Do not write a full .agdf/control scaffold unless durable control state or deterministic setup is explicitly needed.";
2340
- } else if (doctorBlocker) {
2515
+ } else if (doctorBlocker && !routesInvalidVerifiedChange) {
2341
2516
  status = "blocked";
2342
2517
  blockingReason = doctorBlocker.code;
2343
2518
  allowed = ["repair the AGDF control scaffold", ...transitionDecision.allowed, "run doctor again"];
2344
2519
  forbidden = ["create later-gate artefacts beyond the current allowed gate", "implement gated work", "claim QA or release readiness"];
2345
2520
  nextAllowedAction = doctorBlocker.next_step;
2346
- } else if (doctorRevise) {
2521
+ } else if (doctorRevise && !routesInvalidVerifiedChange) {
2347
2522
  status = "blocked";
2348
2523
  blockingReason = doctorRevise.code;
2349
2524
  allowed = [...new Set(["complete the current control-state fields", ...transitionDecision.allowed, "run doctor again"])];
@@ -2389,6 +2564,7 @@ function evaluateGateCheck(targetDir, selection = {}) {
2389
2564
  memory: deliveryMap.memory,
2390
2565
  findings: deliveryMap.findings,
2391
2566
  },
2567
+ verified_change: verifiedChange,
2392
2568
  doctor_report: doctorReport,
2393
2569
  };
2394
2570
  }
@@ -14,6 +14,7 @@ Use this scaffold when a target repository should keep AGDF state outside chat h
14
14
  | `templates/MASTER_BACKLOG.md` | Template for the living backlog pointer: active initiatives and current UR/Brownfield/PRD/SD/TP/QA/OR artefacts |
15
15
  | `templates/artefacts/UR.md` | Template for the durable user requirement artefact of a work item |
16
16
  | `templates/artefacts/BROWNFIELD_REVIEW.md` | Template for the durable Brownfield Review and Mode/Slice Decision |
17
+ | `templates/artefacts/VERIFIED_CHANGE.md` | Compact fail-closed record for an eligible Verified Change |
17
18
  | `templates/artefacts/PRD.md` | Template for the durable product requirements artefact |
18
19
  | `templates/artefacts/SD.md` | Template for the durable solution design artefact |
19
20
  | `templates/artefacts/TP.md` | Template for the durable task and test plan artefact |
@@ -84,7 +85,7 @@ npx --yes @agdf/cli@latest gate-check --json
84
85
  - The selected `runs/<run_id>/RUN_STATE.md` is the current run dashboard.
85
86
  - `config.json` stores the project preference for artefact and chat language; runtime rules stay English.
86
87
  - `MASTER_BACKLOG.md` points to active delivery work; detailed artefacts live beside the work item.
87
- - Brownfield Review records the post-UR Mode/Slice Decision before PRD depth or Quick Task execution is chosen.
88
+ - Brownfield Review records the post-UR Mode/Slice Decision before PRD depth, Verified Change execution or Quick Task execution is chosen.
88
89
  - UR, PRD, SD, TP and QA decisions require durable artefacts or links to the authoritative repository source of truth before the next gate can open.
89
90
  - OR records the run closeout and next permissible step; it does not approve later gates.
90
91
  - `SOT_REGISTRY.md` decides which document owns which domain.
@@ -4,8 +4,8 @@
4
4
 
5
5
  - run_id:
6
6
  - started_at:
7
- - mode: `quick_task | structured_delivery`
8
- - current_gate: `none | UR | Brownfield Review | Mode/Slice Decision | PRD | SD | TP | Quick Task Execution | Brownfield Analysis | CD+Tests | CR | QA | UAT | OR`
7
+ - mode: `quick_task | verified_change | structured_delivery`
8
+ - current_gate: `none | UR | Brownfield Review | Mode/Slice Decision | PRD | SD | TP | Quick Task Execution | Verified Change Execution | Brownfield Analysis | CD+Tests | CR | QA | UAT | OR`
9
9
  - decision: `pass | revise | block | in_progress`
10
10
  - owner:
11
11
 
@@ -68,6 +68,7 @@ Valid approval format for new runs: `Approval: <GateName>`.
68
68
  | SD | | `draft | approved | superseded | not_applicable` | |
69
69
  | TP | | `draft | approved | superseded | not_applicable` | |
70
70
  | Brownfield Review | | `missing | draft | done | not_applicable | superseded` | |
71
+ | Verified Change | | `missing | draft | eligible | executed | escalated` | |
71
72
  | Review | | `missing | done | not_applicable` | |
72
73
  | QA | | `missing | pass | revise | block | not_applicable` | |
73
74
  | OR | | `missing | done | not_applicable` | |
@@ -77,7 +78,7 @@ Valid approval format for new runs: `Approval: <GateName>`.
77
78
  Set this after Brownfield Review. Do not assume the full gate chain before the existing-system impact is understood.
78
79
  Quick Task execution or implementation is not allowed until this decision is visible with scope reason and evidence.
79
80
 
80
- - decision: `undecided | quick_task | structured_slice | structured_delivery | block`
81
+ - decision: `undecided | quick_task | verified_change | structured_slice | structured_delivery | block`
81
82
  - required_next_gate: `none | PRD | SD | TP | Brownfield Analysis`
82
83
  - scope_reason:
83
84
  - evidence:
@@ -43,6 +43,7 @@ Describe the trustworthy outcome.
43
43
  |---|---|---|---|
44
44
  | UR | | draft | |
45
45
  | Brownfield Review | | missing | |
46
+ | Verified Change | | missing | |
46
47
  | PRD | | missing | |
47
48
  | SD | | missing | |
48
49
  | TP | | missing | |
@@ -53,7 +54,7 @@ Describe the trustworthy outcome.
53
54
 
54
55
  ## Mode/Slice Decision
55
56
 
56
- - decision:
57
+ - decision: `quick_task | verified_change | structured_slice | structured_delivery | block`
57
58
  - required_next_gate:
58
59
  - scope_reason:
59
60
  - evidence:
@@ -37,7 +37,7 @@ What approved UR scope is being sized and routed?
37
37
 
38
38
  ## Mode / Slice Decision
39
39
 
40
- - decision: `quick_task | structured_slice | structured_delivery | block`
40
+ - decision: `quick_task | verified_change | structured_slice | structured_delivery | block`
41
41
  - required_next_gate: `none | PRD | SD | TP | Brownfield Analysis`
42
42
  - scope_reason:
43
43
  - evidence:
@@ -0,0 +1,48 @@
1
+ # Verified Change: <Title>
2
+
3
+ Status: `draft | eligible | executed | escalated`
4
+
5
+ ## Record
6
+
7
+ - status: draft
8
+ - related_ur:
9
+ - escalation_target: `structured_slice | structured_delivery`
10
+ - canonical_owner:
11
+ - allowed_source_paths:
12
+ - allowed_derived_paths: none
13
+ - prohibited_impacts: none
14
+ - propagation_command: none
15
+ - validation_commands:
16
+ - baseline_tracked_paths: none
17
+ - baseline_untracked_paths: none
18
+ - validation_status: pending
19
+ - propagation_status: not_applicable
20
+
21
+ ## Eligibility Assertions
22
+
23
+ | Condition | Evidence | Status |
24
+ |---|---|---|
25
+ | Exactly one canonical owner | | `pass | fail` |
26
+ | Source and derived paths are bounded | | `pass | fail` |
27
+ | No gate, permission, security, persistence, architecture, external API, CLI or release impact | | `pass | fail` |
28
+ | Deterministic propagation is defined when derived paths exist | | `pass | fail | not_applicable` |
29
+ | Deterministic validation is defined | | `pass | fail` |
30
+ | Candidate paths are clean at baseline | | `pass | fail` |
31
+
32
+ ## Execution Evidence
33
+
34
+ | Evidence | Source | Result |
35
+ |---|---|---|
36
+ | Changed paths since baseline | | `pass | fail` |
37
+ | Propagation command | | `pass | fail | not_applicable` |
38
+ | Validation commands | | `pass | fail` |
39
+
40
+ ## Mini-Closeout
41
+
42
+ - delivered:
43
+ - intentionally_not_delivered:
44
+ - escalation_result: none
45
+ - residual_risk: none
46
+ - next_step:
47
+
48
+ This compact record is valid only for a Brownfield-selected `verified_change`. Any missing, failed or ambiguous condition must set `status: escalated` and continue at `escalation_target`.
@@ -52,6 +52,32 @@ npx --yes @agdf/cli@latest delivery-map --json
52
52
 
53
53
  The CLI reports are validators and JSON evidence, not the primary user experience, not a required ritual for normal work and not a second rule system. If a report says `blocked`, do not continue with later-gate artefacts until the reported blocker is resolved.
54
54
 
55
+ ## Native Interaction Path
56
+
57
+ Use the Runtime Contract's Native Interaction Contract to classify every candidate interaction as `clarification`, `tool_permission` or `gate_approval` before selecting a host control.
58
+
59
+ Prefer inspection over asking. Do not invoke a structured question for status reporting, facts available in the repository, routine read-only work or a gate that is not ready. Do not repeatedly prompt for the same non-ready gate.
60
+
61
+ For a ready `gate_approval`:
62
+
63
+ 1. Resolve exactly one selected run and evaluate the current gate.
64
+ 2. Confirm the required durable artefact is present and ready before presenting a question.
65
+ 3. Ask exactly one question that names the selected `run_id` and `current_gate`.
66
+ 4. Offer the exact approving option `Approval: <GateName>` plus bounded revise and cancel/decline outcomes.
67
+ 5. Use the surface adapter declared in the canonical plugin definition only when it can wait for deliberate input without auto-resolution; otherwise request the same exact approval in concise text.
68
+ 6. Re-run gate evaluation for the same run and expected gate after the response and immediately before persistence.
69
+ 7. Reject a missing artefact, ambiguous or wrong run, wrong gate, stale expected gate, timeout/default, hook-supplied answer, agent message, technical permission outcome or plan approval.
70
+ 8. Persist accepted input only through the existing control-state workflow.
71
+
72
+ Surface behavior:
73
+
74
+ - Codex: use the native short-question/request-user-input control when callable, ask one gate question and omit auto-resolution.
75
+ - Claude Code: use `AskUserQuestion` only when no timeout can auto-continue and no hook supplies `answers` or `updatedInput`; otherwise use exact text. Claude permissions and `ExitPlanMode` remain separate.
76
+ - OpenCode: use built-in `question` when permitted. Preserve explicit `permission.question` denial and use exact text in that case. `once`, `always`, `reject` and auto mode never become gate input.
77
+ - Other, unavailable or non-interactive surfaces: use exact textual approval and wait for a new explicit user response.
78
+
79
+ A free-form response must still exactly match the current gate's approval formula after revalidation. Revise, decline and cancel never advance the gate. Native presentation, host permission and plan approval are inputs or separate authority domains, never durable AGDF authority.
80
+
55
81
  ## Rules
56
82
  1. Fail closed when a required approval or artefact status is missing.
57
83
  2. The earliest blocking gate wins.
@@ -72,6 +98,7 @@ The CLI reports are validators and JSON evidence, not the primary user experienc
72
98
  17. If multiple active scopes are plausible, list the evidenced competing lines and keep the current step at scope clarification or the earliest common safe gate.
73
99
  18. When maintaining `MASTER_BACKLOG.md`, use its canonical compact columns, readable status labels and document-relative Markdown links. Do not expose raw long paths or internal snake_case statuses in the human-facing table. The canonical status/artefact label vocabulary is defined in the AGDF control scaffold's `MASTER_BACKLOG.md` template Rules section; do not invent other labels.
74
100
  19. Preserve legacy backlog compatibility through the CLI parser; do not create a second surface-specific backlog format.
101
+ 20. Native interaction must follow the Runtime Contract's interaction envelope, readiness check, deliberate-user-input requirement and post-response revalidation boundary.
75
102
 
76
103
  ## Gate Evaluation
77
104