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.
- package/README.md +43 -5
- package/bin/create-agdf.js +186 -10
- package/generated/.agdf/control/README.md +2 -1
- package/generated/.agdf/control/templates/AGDF_RUN.md +4 -3
- package/generated/.agdf/control/templates/RUN_STATE.md +2 -1
- package/generated/.agdf/control/templates/artefacts/BROWNFIELD_REVIEW.md +1 -1
- package/generated/.agdf/control/templates/artefacts/VERIFIED_CHANGE.md +48 -0
- package/generated/.github/skills/agdf-gate-check/SKILL.md +27 -0
- package/generated/.github/skills/agdf-runtime-contract.md +76 -5
- package/generated/.opencode/AGDF.md +5 -4
- package/generated/.opencode/README.md +2 -1
- package/generated/.opencode/agdf-runtime-contract.md +76 -5
- package/generated/.opencode/skills/agdf-gate-check/SKILL.md +27 -0
- package/generated/AGENTS.md +3 -3
- package/generated/opencode.json +1 -0
- package/generated/plugins/agdf/.codex-plugin/plugin.json +2 -2
- package/generated/plugins/agdf/control/README.md +2 -1
- package/generated/plugins/agdf/control/templates/AGDF_RUN.md +4 -3
- package/generated/plugins/agdf/control/templates/RUN_STATE.md +2 -1
- package/generated/plugins/agdf/control/templates/artefacts/BROWNFIELD_REVIEW.md +1 -1
- package/generated/plugins/agdf/control/templates/artefacts/VERIFIED_CHANGE.md +48 -0
- package/generated/plugins/agdf/meta/agdf-agent-router.md +3 -3
- package/generated/plugins/agdf/meta/agdf-constitution.md +3 -3
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +32 -2
- package/generated/plugins/agdf/meta/agdf-runtime-contract.md +76 -5
- package/generated/plugins/agdf/meta/agdf-tenets.md +5 -0
- package/generated/plugins/agdf/skills/gate-check/SKILL.md +27 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
# create-agdf
|
|
2
2
|
|
|
3
|
-
Bootstrap AGDF
|
|
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
|
-
|
|
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.
|
package/bin/create-agdf.js
CHANGED
|
@@ -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.
|
|
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
|
|
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 =
|
|
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
|
|