create-agdf 0.8.5 → 0.8.7
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/bin/create-agdf.js +141 -10
- package/generated/.agdf/control/templates/artefacts/VERIFIED_CHANGE.md +10 -0
- package/generated/.github/skills/agdf-brownfield-analysis/SKILL.md +3 -3
- package/generated/.github/skills/agdf-gate-check/SKILL.md +31 -7
- package/generated/.github/skills/agdf-interaction-locales.json +98 -6
- package/generated/.github/skills/agdf-release-or/SKILL.md +4 -3
- package/generated/.github/skills/agdf-runtime-contract.md +104 -8
- package/generated/.opencode/agdf-interaction-locales.json +98 -6
- package/generated/.opencode/agdf-runtime-contract.md +104 -8
- package/generated/.opencode/skills/agdf-brownfield-analysis/SKILL.md +3 -3
- package/generated/.opencode/skills/agdf-gate-check/SKILL.md +31 -7
- package/generated/.opencode/skills/agdf-release-or/SKILL.md +4 -3
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/control/templates/artefacts/VERIFIED_CHANGE.md +10 -0
- package/generated/plugins/agdf/meta/agdf-interaction-locales.json +98 -6
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +9 -5
- package/generated/plugins/agdf/meta/agdf-runtime-contract.md +104 -8
- package/generated/plugins/agdf/skills/brownfield-analysis/SKILL.md +3 -3
- package/generated/plugins/agdf/skills/gate-check/SKILL.md +31 -7
- package/generated/plugins/agdf/skills/release-or/SKILL.md +4 -3
- package/lib/control-state/run-state-parser.js +30 -3
- package/lib/interaction-presentation.js +88 -2
- package/package.json +1 -1
package/bin/create-agdf.js
CHANGED
|
@@ -63,6 +63,7 @@ const openCodeConfigFragmentPath = "opencode.agdf.json";
|
|
|
63
63
|
const userGateOrder = ["UR", "PRD", "SD", "TP", "QA", "UAT"];
|
|
64
64
|
const durableGateArtefacts = new Set(["UR", "PRD", "SD", "TP", "QA"]);
|
|
65
65
|
const internalStepArtefacts = new Set(["Brownfield Review", "Verified Change", "Brownfield Analysis", "CD+Tests", "TP Review", "Clean Implementation Review", "Clean Review", "CR", "Code Review"]);
|
|
66
|
+
const closeoutArtefacts = new Set(["OR"]);
|
|
66
67
|
const deliveryRelationships = [
|
|
67
68
|
{ from: "UR", relationship: "approved_by", to: "Approval: UR", requiredBy: "UR" },
|
|
68
69
|
{ from: "PRD", relationship: "derived_from", to: "UR", requiredBy: "PRD" },
|
|
@@ -1754,6 +1755,9 @@ function evaluateDoctor(targetDir, selection = {}) {
|
|
|
1754
1755
|
for (const finding of analyzeDurableGateArtefactConsistency(runState)) {
|
|
1755
1756
|
addFinding(findings, finding.severity, finding.code, finding.message, finding.path, finding.next_step);
|
|
1756
1757
|
}
|
|
1758
|
+
for (const finding of analyzeArtefactRoleConsistency(targetDir, runState)) {
|
|
1759
|
+
addFinding(findings, finding.severity, finding.code, finding.message, finding.path, finding.next_step);
|
|
1760
|
+
}
|
|
1757
1761
|
for (const finding of analyzeDeliveryMap(runState).findings) {
|
|
1758
1762
|
addFinding(findings, finding.severity, finding.code, finding.message, finding.path, finding.next_step);
|
|
1759
1763
|
}
|
|
@@ -1828,6 +1832,14 @@ function gitPathList(targetDir, args) {
|
|
|
1828
1832
|
}
|
|
1829
1833
|
}
|
|
1830
1834
|
|
|
1835
|
+
function gitValue(targetDir, args) {
|
|
1836
|
+
try {
|
|
1837
|
+
return execFileSync("git", args, { cwd: targetDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
1838
|
+
} catch {
|
|
1839
|
+
return null;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1831
1843
|
function readVerifiedChangeRecord(targetDir, runState) {
|
|
1832
1844
|
const artefact = runState.artefacts.get("Verified Change");
|
|
1833
1845
|
if (!artefact?.path || isPlaceholderValue(artefact.path)) return { status: "missing", path: "", content: "" };
|
|
@@ -1870,9 +1882,14 @@ function evaluateVerifiedChange(targetDir, runState) {
|
|
|
1870
1882
|
const validationCommands = extractField(record.content, "validation_commands");
|
|
1871
1883
|
const baselineTracked = parseVerifiedChangePathList(extractField(record.content, "baseline_tracked_paths"));
|
|
1872
1884
|
const baselineUntracked = parseVerifiedChangePathList(extractField(record.content, "baseline_untracked_paths"));
|
|
1885
|
+
const baselineCommit = cleanStatusCell(extractField(record.content, "baseline_commit"));
|
|
1886
|
+
const executionChangedPaths = parseVerifiedChangePathList(extractField(record.content, "execution_changed_paths"));
|
|
1887
|
+
const executionScopeStatus = cleanStatusCell(extractField(record.content, "execution_scope_status"));
|
|
1873
1888
|
const validationStatus = cleanStatusCell(extractField(record.content, "validation_status"));
|
|
1874
1889
|
const propagationStatus = cleanStatusCell(extractField(record.content, "propagation_status"));
|
|
1875
1890
|
const urArtefact = runState.artefacts.get("UR");
|
|
1891
|
+
const lifecycle = extractField(runState.content, "lifecycle") || "active";
|
|
1892
|
+
const runId = extractField(runState.content, "run_id") || "";
|
|
1876
1893
|
|
|
1877
1894
|
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.");
|
|
1878
1895
|
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.");
|
|
@@ -1884,24 +1901,49 @@ function evaluateVerifiedChange(targetDir, runState) {
|
|
|
1884
1901
|
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.");
|
|
1885
1902
|
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.");
|
|
1886
1903
|
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.");
|
|
1904
|
+
if (["eligible", "executed"].includes(status) && !/^[0-9a-f]{40,64}$/i.test(baselineCommit)) add("AGDF_VERIFIED_CHANGE_BASELINE_COMMIT_INVALID", "Eligible or executed Verified Change requires a full Git baseline_commit.", "Capture the current full Git commit id before eligibility or escalate.");
|
|
1905
|
+
|
|
1906
|
+
if (["eligible", "executed"].includes(status) && lifecycle !== "completed" && /^[0-9a-f]{40,64}$/i.test(baselineCommit)) {
|
|
1907
|
+
const currentHead = gitValue(targetDir, ["rev-parse", "HEAD"]);
|
|
1908
|
+
if (!currentHead) {
|
|
1909
|
+
add("AGDF_VERIFIED_CHANGE_BASELINE_COMMIT_UNAVAILABLE", "Active Verified Change requires a readable current Git HEAD for baseline identity validation.", "Restore Git repository access or use the structured path.", "block");
|
|
1910
|
+
} else if (baselineCommit !== currentHead) {
|
|
1911
|
+
add("AGDF_VERIFIED_CHANGE_BASELINE_COMMIT_MISMATCH", "Active Verified Change baseline_commit does not match the current Git HEAD.", "Recapture the baseline before eligibility or escalate; do not reuse a stale or fabricated baseline identity.", "block");
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1887
1914
|
|
|
1888
1915
|
const allowedPaths = new Set([...sourcePaths, ...derivedPaths]);
|
|
1889
1916
|
const baselinePaths = new Set([...baselineTracked, ...baselineUntracked]);
|
|
1890
1917
|
const dirtyCandidate = [...allowedPaths].find((path) => baselinePaths.has(path));
|
|
1891
1918
|
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");
|
|
1892
1919
|
|
|
1893
|
-
const
|
|
1894
|
-
const
|
|
1895
|
-
|
|
1896
|
-
|
|
1920
|
+
const permittedControlPaths = new Set([record.path, runState.path, ".agdf/control/MASTER_BACKLOG.md"]);
|
|
1921
|
+
const runArtefactPrefix = `.agdf/control/artefacts/${runId}/`;
|
|
1922
|
+
for (const artefact of runState.artefacts.values()) {
|
|
1923
|
+
if (artefact.path_format === "invalid") continue;
|
|
1924
|
+
if (isSafeRepoRelativePath(artefact.path) && artefact.path.startsWith(runArtefactPrefix)) permittedControlPaths.add(artefact.path);
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
if (!(lifecycle === "completed" && status === "executed")) {
|
|
1928
|
+
const currentTracked = gitPathList(targetDir, ["diff", "HEAD", "--name-only"]);
|
|
1929
|
+
const currentUntracked = gitPathList(targetDir, ["ls-files", "--others", "--exclude-standard"]);
|
|
1930
|
+
if (currentTracked === null || currentUntracked === null) {
|
|
1931
|
+
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");
|
|
1932
|
+
} else {
|
|
1933
|
+
const introduced = [...new Set([...currentTracked, ...currentUntracked])].filter((path) => !baselinePaths.has(path)).sort();
|
|
1934
|
+
const unexpected = introduced.filter((path) => !allowedPaths.has(path) && !permittedControlPaths.has(path));
|
|
1935
|
+
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");
|
|
1936
|
+
if (status === "executed" && JSON.stringify(introduced) !== JSON.stringify([...executionChangedPaths].sort())) {
|
|
1937
|
+
add("AGDF_VERIFIED_CHANGE_EXECUTION_SCOPE_MISMATCH", "Executed Verified Change execution_changed_paths must equal the post-baseline changed-path set.", "Record the exact changed paths or escalate.", "block");
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1897
1940
|
} else {
|
|
1898
|
-
const
|
|
1899
|
-
|
|
1900
|
-
const unexpected = [...currentPaths].filter((path) => !baselinePaths.has(path) && !allowedPaths.has(path) && !permittedControlPaths.has(path));
|
|
1901
|
-
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");
|
|
1941
|
+
const unsafeExecutionPath = executionChangedPaths.find((path) => !isSafeRepoRelativePath(path) || (!allowedPaths.has(path) && !permittedControlPaths.has(path)));
|
|
1942
|
+
if (unsafeExecutionPath) add("AGDF_VERIFIED_CHANGE_EXECUTION_SCOPE_INVALID", `Recorded execution path is outside the eligible scope: ${unsafeExecutionPath}.`, "Correct the historical execution snapshot or escalate.", "block");
|
|
1902
1943
|
}
|
|
1903
1944
|
|
|
1904
1945
|
if (status === "executed") {
|
|
1946
|
+
if (executionChangedPaths.length === 0 || !executionChangedPaths.some((path) => allowedPaths.has(path)) || executionScopeStatus !== "pass") add("AGDF_VERIFIED_CHANGE_EXECUTION_EVIDENCE_MISSING", "Executed Verified Change requires a non-empty execution_changed_paths set containing a declared source/derived path and execution_scope_status: pass.", "Record complete execution-scope evidence or escalate.");
|
|
1905
1947
|
if (validationStatus !== "pass") add("AGDF_VERIFIED_CHANGE_VALIDATION_EVIDENCE_MISSING", "Executed Verified Change requires validation_status: pass.", "Record passing deterministic validation evidence or escalate.");
|
|
1906
1948
|
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.");
|
|
1907
1949
|
}
|
|
@@ -1936,6 +1978,7 @@ function readRunState(targetDir, selection = {}) {
|
|
|
1936
1978
|
const controlState = parseControlState(run.content, {
|
|
1937
1979
|
userGates: userGateOrder,
|
|
1938
1980
|
internalSteps: [...internalStepArtefacts],
|
|
1981
|
+
closeoutArtefacts: [...closeoutArtefacts],
|
|
1939
1982
|
});
|
|
1940
1983
|
return {
|
|
1941
1984
|
...run,
|
|
@@ -1974,6 +2017,7 @@ function readRunState(targetDir, selection = {}) {
|
|
|
1974
2017
|
const parsed = parseControlState(content, {
|
|
1975
2018
|
userGates: userGateOrder,
|
|
1976
2019
|
internalSteps: [...internalStepArtefacts],
|
|
2020
|
+
closeoutArtefacts: [...closeoutArtefacts],
|
|
1977
2021
|
});
|
|
1978
2022
|
|
|
1979
2023
|
return {
|
|
@@ -2033,6 +2077,51 @@ function analyzeDurableGateArtefactConsistency(runState) {
|
|
|
2033
2077
|
return findings;
|
|
2034
2078
|
}
|
|
2035
2079
|
|
|
2080
|
+
function analyzeArtefactRoleConsistency(targetDir, runState) {
|
|
2081
|
+
const findings = [];
|
|
2082
|
+
for (const [type, artefact] of runState.artefacts) {
|
|
2083
|
+
if (artefact.path_format === "invalid") {
|
|
2084
|
+
findings.push({
|
|
2085
|
+
severity: "block",
|
|
2086
|
+
code: "AGDF_ARTEFACT_PATH_FORMAT_INVALID",
|
|
2087
|
+
message: `${type} uses an invalid artefact path cell (${artefact.path_reason}).`,
|
|
2088
|
+
path: runState.path,
|
|
2089
|
+
next_step: "Use a plain repository-relative path or one complete Markdown code span.",
|
|
2090
|
+
});
|
|
2091
|
+
} else if (artefact.path && !isSafeRepoRelativePath(artefact.path)) {
|
|
2092
|
+
findings.push({
|
|
2093
|
+
severity: "block",
|
|
2094
|
+
code: "AGDF_ARTEFACT_PATH_INVALID",
|
|
2095
|
+
message: `${type} must use a normalized repository-relative artefact path.`,
|
|
2096
|
+
path: runState.path,
|
|
2097
|
+
next_step: "Remove absolute, traversal, unsupported-backslash or otherwise non-normalized path text.",
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
const rolesByPath = new Map();
|
|
2102
|
+
for (const [type, artefact] of runState.artefacts) {
|
|
2103
|
+
if (!artefact.path) continue;
|
|
2104
|
+
rolesByPath.set(artefact.path, [...(rolesByPath.get(artefact.path) ?? []), type]);
|
|
2105
|
+
}
|
|
2106
|
+
for (const [path, roles] of rolesByPath) {
|
|
2107
|
+
if (roles.length < 2) continue;
|
|
2108
|
+
const allowedRoles = new Set(["Brownfield Review", "Verified Change", "OR"]);
|
|
2109
|
+
if (modeSliceDecision(runState) !== "verified_change" || roles.some((role) => !allowedRoles.has(role))) {
|
|
2110
|
+
findings.push({ severity: "block", code: "AGDF_ARTEFACT_ROLE_ALIAS_INVALID", message: `Artefact path is reused across incompatible roles: ${roles.join(", ")}.`, path, next_step: "Use distinct artefacts or a lifecycle-consistent Verified Change compact record." });
|
|
2111
|
+
continue;
|
|
2112
|
+
}
|
|
2113
|
+
const record = readVerifiedChangeRecord(targetDir, runState);
|
|
2114
|
+
if (record.status !== "present") continue;
|
|
2115
|
+
const recordStatus = cleanStatusCell(extractField(record.content, "status"));
|
|
2116
|
+
const brownfieldComplete = extractField(record.content, "decision") === "verified_change" && filled(extractField(record.content, "scope_reason")) && filled(extractField(record.content, "evidence"));
|
|
2117
|
+
const miniCloseoutComplete = ["delivered", "intentionally_not_delivered", "residual_risk", "next_step"].every((field) => filled(extractField(record.content, field)));
|
|
2118
|
+
if (roles.includes("Brownfield Review") && (runState.artefacts.get("Brownfield Review")?.status !== "done" || !brownfieldComplete)) findings.push({ severity: "block", code: "AGDF_VERIFIED_CHANGE_BROWNFIELD_ROLE_INVALID", message: "Consolidated Brownfield Review role is incomplete.", path, next_step: "Complete Brownfield Selection before eligibility." });
|
|
2119
|
+
if (roles.includes("Verified Change") && runState.artefacts.get("Verified Change")?.status !== recordStatus) findings.push({ severity: "block", code: "AGDF_VERIFIED_CHANGE_ROLE_STATUS_MISMATCH", message: "Verified Change artefact row status does not match the compact record.", path, next_step: "Align the run-state row and record status." });
|
|
2120
|
+
if (roles.includes("OR") && runState.artefacts.get("OR")?.status === "done" && (recordStatus !== "executed" || !miniCloseoutComplete)) findings.push({ severity: "block", code: "AGDF_VERIFIED_CHANGE_OR_ROLE_INVALID", message: "Consolidated OR is complete before execution and Mini-Closeout evidence are complete.", path, next_step: "Complete execution and Mini-Closeout evidence before marking OR done." });
|
|
2121
|
+
}
|
|
2122
|
+
return findings;
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2036
2125
|
function isInternalStepSatisfied(runState, step) {
|
|
2037
2126
|
const artefact = runState.artefacts.get(step);
|
|
2038
2127
|
if (!artefact) return false;
|
|
@@ -2244,6 +2333,40 @@ function postApprovalTransition(missingApproval) {
|
|
|
2244
2333
|
};
|
|
2245
2334
|
}
|
|
2246
2335
|
|
|
2336
|
+
const BREADCRUMB_PATH_TEMPLATES = {
|
|
2337
|
+
structured_delivery: ["UR", "PRD", "SD", "TP", "QA", "UAT"],
|
|
2338
|
+
structured_slice: ["UR", "PRD", "SD", "TP", "QA", "UAT"],
|
|
2339
|
+
verified_change: ["UR", "Verified Change", "OR"],
|
|
2340
|
+
quick_task: ["UR", "Quick Task"],
|
|
2341
|
+
block: ["UR", "Block"],
|
|
2342
|
+
undecided: ["UR"],
|
|
2343
|
+
};
|
|
2344
|
+
|
|
2345
|
+
const BREADCRUMB_STANDARD_GATES = ["UR", "PRD", "SD", "TP", "QA", "UAT"];
|
|
2346
|
+
|
|
2347
|
+
function buildBreadcrumbPath(modeSliceDecision, currentGate, runState) {
|
|
2348
|
+
const gates = BREADCRUMB_PATH_TEMPLATES[modeSliceDecision] ?? ["UR"];
|
|
2349
|
+
return gates.map((gate) => {
|
|
2350
|
+
if (BREADCRUMB_STANDARD_GATES.includes(gate)) {
|
|
2351
|
+
if (gateApprovalStatus(runState, gate) === "approved") return { gate, status: "fulfilled" };
|
|
2352
|
+
if (gate === currentGate) return { gate, status: "current" };
|
|
2353
|
+
return { gate, status: "open" };
|
|
2354
|
+
}
|
|
2355
|
+
if (gate === currentGate) return { gate, status: "current" };
|
|
2356
|
+
if ((gate === "Verified Change" || gate === "Quick Task") && currentGate === "OR")
|
|
2357
|
+
return { gate, status: "fulfilled" };
|
|
2358
|
+
if (gate === "Verified Change" && modeSliceDecision === "verified_change")
|
|
2359
|
+
return { gate, status: "current" };
|
|
2360
|
+
if (gate === "Quick Task" && modeSliceDecision === "quick_task")
|
|
2361
|
+
return { gate, status: "current" };
|
|
2362
|
+
if (gate === "Block" && modeSliceDecision === "block")
|
|
2363
|
+
return { gate, status: "current" };
|
|
2364
|
+
if (gate === "OR" && currentGate === "OR")
|
|
2365
|
+
return { gate, status: "current" };
|
|
2366
|
+
return { gate, status: "open" };
|
|
2367
|
+
});
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2247
2370
|
function buildStatusCard({
|
|
2248
2371
|
status,
|
|
2249
2372
|
currentGate,
|
|
@@ -2260,7 +2383,8 @@ function buildStatusCard({
|
|
|
2260
2383
|
const postApproval = postApprovalTransition(missingApproval);
|
|
2261
2384
|
const isUserGateApproval = missingApproval !== "none";
|
|
2262
2385
|
const lifecycle = extractField(runState.content ?? "", "lifecycle") || "unknown";
|
|
2263
|
-
const nativeAttemptRequired =
|
|
2386
|
+
const nativeAttemptRequired = false;
|
|
2387
|
+
const interactionKind = status === "open" && isUserGateApproval ? "gate_approval" : status === "blocked" ? "blocked" : "status";
|
|
2264
2388
|
return {
|
|
2265
2389
|
run_id: extractField(runState.content ?? "", "run_id") || "unknown",
|
|
2266
2390
|
presentation_language: chatLanguage,
|
|
@@ -2270,6 +2394,11 @@ function buildStatusCard({
|
|
|
2270
2394
|
status,
|
|
2271
2395
|
current_gate: currentGate,
|
|
2272
2396
|
mode_slice_decision: runState.mode_slice_decision?.decision || "undecided",
|
|
2397
|
+
breadcrumb: buildBreadcrumbPath(
|
|
2398
|
+
runState.mode_slice_decision?.decision || "undecided",
|
|
2399
|
+
currentGate,
|
|
2400
|
+
runState,
|
|
2401
|
+
),
|
|
2273
2402
|
allowed_now: allowed,
|
|
2274
2403
|
forbidden_now: forbidden,
|
|
2275
2404
|
blocking_condition: blockingReason || "none",
|
|
@@ -2284,8 +2413,10 @@ function buildStatusCard({
|
|
|
2284
2413
|
next_skill: nextSkillByGate[currentGate] ?? "gate-check",
|
|
2285
2414
|
next_step: nextAllowedAction,
|
|
2286
2415
|
quality_outlook: qualityOutlook,
|
|
2287
|
-
interaction_kind:
|
|
2416
|
+
interaction_kind: interactionKind,
|
|
2288
2417
|
native_attempt_required: nativeAttemptRequired,
|
|
2418
|
+
native_preflight_outcome: isUserGateApproval ? "unavailable_before_invocation" : "not_applicable",
|
|
2419
|
+
native_preflight_reason: isUserGateApproval ? "capability_missing" : "none",
|
|
2289
2420
|
};
|
|
2290
2421
|
}
|
|
2291
2422
|
|
|
@@ -13,11 +13,21 @@ Status: `draft | eligible | executed | escalated`
|
|
|
13
13
|
- prohibited_impacts: none
|
|
14
14
|
- propagation_command: none
|
|
15
15
|
- validation_commands:
|
|
16
|
+
- baseline_commit:
|
|
16
17
|
- baseline_tracked_paths: none
|
|
17
18
|
- baseline_untracked_paths: none
|
|
19
|
+
- execution_changed_paths: none
|
|
20
|
+
- execution_scope_status: pending
|
|
18
21
|
- validation_status: pending
|
|
19
22
|
- propagation_status: not_applicable
|
|
20
23
|
|
|
24
|
+
## Brownfield Selection
|
|
25
|
+
|
|
26
|
+
- mode: post_ur_review
|
|
27
|
+
- decision: verified_change
|
|
28
|
+
- scope_reason:
|
|
29
|
+
- evidence:
|
|
30
|
+
|
|
21
31
|
## Eligibility Assertions
|
|
22
32
|
|
|
23
33
|
| Condition | Evidence | Status |
|
|
@@ -26,14 +26,14 @@ When `.agdf/control/` is present, persist or link `post_ur_review` output under
|
|
|
26
26
|
|
|
27
27
|
## Modes
|
|
28
28
|
|
|
29
|
-
- `post_ur_review`: use after approved durable UR to size and route the work. Output must decide `quick_task`, `structured_slice`, `structured_delivery`, or `block` with scope reason and evidence.
|
|
29
|
+
- `post_ur_review`: use after approved durable UR to size and route the work. Output must decide `quick_task`, `verified_change`, `structured_slice`, `structured_delivery`, or `block` with scope reason and evidence.
|
|
30
30
|
- `pre_implementation_analysis`: use after approved durable TP to verify the implementation path before `CD+Tests`. Output must focus on reuse path, owners, regression risk, test impact and minimal clean implementation.
|
|
31
31
|
|
|
32
32
|
Do not mix both modes silently. Name the active mode in the output.
|
|
33
33
|
|
|
34
34
|
## Rules
|
|
35
35
|
1. Brownfield first: understand the existing codebase before PRD/SD decisions when existing-system impact is possible, and again before implementation.
|
|
36
|
-
2. Brownfield Review after `Approval: UR` is a sizing and routing step. It must visibly decide `quick_task`, `structured_slice`, `structured_delivery`, or `block` before PRD depth or implementation is chosen.
|
|
36
|
+
2. Brownfield Review after `Approval: UR` is a sizing and routing step. It must visibly decide `quick_task`, `verified_change`, `structured_slice`, `structured_delivery`, or `block` before PRD depth or implementation is chosen.
|
|
37
37
|
3. Reuse-before-create: prefer existing modules, services, components, tables, endpoints, tests, and configuration.
|
|
38
38
|
4. Minimal clean slice: choose the smallest durable intervention, not merely the smallest technical diff.
|
|
39
39
|
5. No silent parallel structures.
|
|
@@ -106,7 +106,7 @@ Use a concise structure:
|
|
|
106
106
|
## Brownfield Analysis
|
|
107
107
|
- mode: post_ur_review | pre_implementation_analysis
|
|
108
108
|
- decision: pass | revise | block | not_applicable
|
|
109
|
-
- mode_slice_decision: quick_task | structured_slice | structured_delivery | block
|
|
109
|
+
- mode_slice_decision: quick_task | verified_change | structured_slice | structured_delivery | block
|
|
110
110
|
- required_next_gate: none | PRD | SD | TP | Brownfield Analysis
|
|
111
111
|
- artefact: .agdf/control/artefacts/<key>/BROWNFIELD_REVIEW.md | none
|
|
112
112
|
- scope:
|
|
@@ -75,17 +75,18 @@ For a ready `gate_approval`:
|
|
|
75
75
|
13. 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.
|
|
76
76
|
14. Persist accepted input only through the existing control-state workflow.
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
Before projecting a ready gate, evaluate adapter callability, deliberate wait safety and canonical
|
|
79
|
+
approval-value transport. Expose `native_attempt_required: true` only when that preflight proves
|
|
80
|
+
`exact_option_value` or `separate_label_and_value` transport with safe deliberate waiting. Decorated-only,
|
|
81
|
+
missing, conflicting or unknown capability exposes false and `unavailable_before_invocation`; unsafe
|
|
82
|
+
waiting exposes false and `unsafe_to_wait`. In both cases use exact text without invoking the adapter.
|
|
83
|
+
A hook, permission result or session context message cannot replace an eligible attempt or supply its answer.
|
|
83
84
|
|
|
84
85
|
Before composing the question, resolve the configured chat locale from `.agdf/control/config.json` through `../agdf-interaction-locales.json`: exact complete pack, language subtag, then English fallback. German and English are initial packs, not a closed language list. Keep `Approval: <GateName>` exactly unchanged in every language, and do not mix presentation languages within one interaction. Labels and descriptions use the same resolved locale; host-owned UI chrome remains host-owned and may use a different language.
|
|
85
86
|
|
|
86
87
|
For the transition card, use the complete resolved locale pack. The initial German pack includes `Bereit für deine Entscheidung`, `Jetzt freigeben` and `Danach`; the initial English pack includes `Ready for your decision`, `Approve now` and `Next`. Keep each card in one locale. Use a localized human-readable gate title rather than an internal status label, while leaving the exact gate identifier and approval value unchanged. Do not render a Markdown table, dashboard rows, raw control-state keys, diagnostic codes, evidence lists, a duplicated gate question or a false user gate in the approval-time card.
|
|
87
88
|
|
|
88
|
-
The primary option
|
|
89
|
+
The primary option is derived exactly as `Approval: <GateName>` from the evaluated current gate; only explanatory copy and non-authoritative outcome labels are localized. Concrete localized copy belongs to the user-facing interaction layer or the approved delivery artefact, not to English runtime rules.
|
|
89
90
|
|
|
90
91
|
Make exactly one native-attempt for the ready gate. If the host does not render,
|
|
91
92
|
apply or safely return the native question on that attempt, switch immediately
|
|
@@ -112,6 +113,29 @@ A free-form response must still exactly match the current gate's approval formul
|
|
|
112
113
|
|
|
113
114
|
Every primary chat card shows the selected run's `UR`, `PRD`, `SD` and `TP` in that order. Render an existing canonical path as a readable link and a missing artefact as localized non-link text; never guess paths. Resolve the human run title from current artefact heading, approved UR heading, Objective, then normalized `run_id`. Status, blocked, clarification and internal-step interactions use the same locale and artefact projection but do not show approval buttons. Keep raw process keys and machine status values in JSON or audit detail.
|
|
114
115
|
|
|
116
|
+
### Breadcrumb Rendering
|
|
117
|
+
|
|
118
|
+
The compact human Run Status Card includes a single-line path-derived breadcrumb showing the user's position in the delivery journey. Derive it from the Mode/Slice Decision and the Approvals table — not a fixed template. Use `✓` for fulfilled, `●` for current, `○` for open, separated by ` · `. Non-applicable gates are absent. See the Runtime Contract §Breadcrumb for the path templates and the derived `breadcrumb` array.
|
|
119
|
+
|
|
120
|
+
### Post-Acceptance Transition Narration
|
|
121
|
+
|
|
122
|
+
After each accepted gate approval is persisted, emit exactly one narration line using the template `<what was satisfied> → <what the agent does next internally> → <user action: yes/no>`. This generalizes the existing TP pattern (step 7 above) to every gate advancement. The narration is post-acceptance only: it never appears in the same message as the Gate Transition Card, never contains the `Approval:` value, and never repeats the decision's effect. For internal steps (Brownfield Review, Brownfield Analysis), state what the agent does next and that no user action is required, without exposing `next_user_gate: none` or asking for a second approval. See the Runtime Contract §Post-Acceptance Transition Narration for the full contract.
|
|
123
|
+
|
|
124
|
+
### Internal-State Collapse in the Human Card
|
|
125
|
+
|
|
126
|
+
When rendering the compact human Run Status Card, collapse internal sub-states (`verified_change` sub-states, `context_graph_required_action`, `multi_scope_state`) to stable human labels. `escalated`, `open_gap` and `blocked` remain explicitly visible. The full machine/audit projection retains all raw values unchanged. See the Runtime Contract §Internal-State Collapse for the complete mapping table.
|
|
127
|
+
|
|
128
|
+
### On-Demand "Why?" Response
|
|
129
|
+
|
|
130
|
+
When the user asks "why?" or "Warum?" at any gate or internal step, respond with a `status` interaction (not `gate_approval`):
|
|
131
|
+
|
|
132
|
+
1. Pull the curated rationale from `gateRationale()` in the resolved locale.
|
|
133
|
+
2. Compose one fulfilled line from the selected run's Approvals table and artefact status.
|
|
134
|
+
3. Compose one protects-against line from the gate's rationale and the existing `primary.actions` copy.
|
|
135
|
+
4. Do not show approval controls. Do not advance any gate. Do not merge with the approval envelope.
|
|
136
|
+
|
|
137
|
+
The response is deterministic: same gate, same question, same answer. The `gate_approval` options remain exactly `approve | revise | decline | cancel`; no "why" option is added. See the Runtime Contract §Gate-Rationale-Registry and §On-Demand "Why?" Interaction for the full contract.
|
|
138
|
+
|
|
115
139
|
## Rules
|
|
116
140
|
1. Fail closed when a required approval or artefact status is missing.
|
|
117
141
|
2. The earliest blocking gate wins.
|
|
@@ -125,7 +149,7 @@ Every primary chat card shows the selected run's `UR`, `PRD`, `SD` and `TP` in t
|
|
|
125
149
|
10. Approval of one user gate permits work on the next allowed gate artefact or required internal step only; it never skips directly to implementation.
|
|
126
150
|
11. New product semantics, functional change or user-visible behaviour change requires a durable UR in `.agdf/control/` or a linked authoritative repository SoT before Brownfield Review, PRD, SD, TP, Brownfield Analysis or implementation.
|
|
127
151
|
12. Approval text and durable artefact presence are separate requirements for UR, PRD, SD, TP and QA report decisions. Approval text without the corresponding persisted or linked artefact keeps the current gate at that gate.
|
|
128
|
-
13. After Brownfield Review, decide the process size before drafting PRD or implementing: `quick_task | structured_slice | structured_delivery | block`.
|
|
152
|
+
13. After Brownfield Review, decide the process size before drafting PRD or implementing: `quick_task | verified_change | structured_slice | structured_delivery | block`.
|
|
129
153
|
14. The Mode/Slice Decision must be visible and evidenced before any Quick Task execution, PRD shortcut or implementation. A decision value without scope reason and evidence is still missing.
|
|
130
154
|
15. Missing or incomplete control state must not push setup work back to the user. For a fresh request, draft the current minimal artefact in the response and request the exact approval. Initialize or write `.agdf/control/` only when durable control state is explicitly requested, already live for the repository, or required for a deterministic CLI/CI setup path. Implementation remains forbidden.
|
|
131
155
|
16. Branch names, uncommitted diffs, generated summaries and chat history are not sufficient scope proof when durable artefacts or approvals are missing or conflicting.
|
|
@@ -28,7 +28,19 @@
|
|
|
28
28
|
"lifecycle": "Delivery status",
|
|
29
29
|
"allowed": "Allowed now",
|
|
30
30
|
"forbidden": "Forbidden now",
|
|
31
|
-
"artefacts": "Artefacts"
|
|
31
|
+
"artefacts": "Artefacts",
|
|
32
|
+
"breadcrumb": "Path",
|
|
33
|
+
"breadcrumbFulfilled": "\u2713",
|
|
34
|
+
"breadcrumbCurrent": "\u25cf",
|
|
35
|
+
"breadcrumbOpen": "\u25cb",
|
|
36
|
+
"breadcrumbSeparator": " \u00b7 "
|
|
37
|
+
},
|
|
38
|
+
"internalStateLabels": {
|
|
39
|
+
"verifiedChange": "Compact change under review",
|
|
40
|
+
"verifiedChangeEscalated": "Escalated to structured delivery",
|
|
41
|
+
"contextGraphMaintained": "Project memory maintained",
|
|
42
|
+
"contextGraphOpenGap": "Graph gap open",
|
|
43
|
+
"multiScopeBlocked": "Ambiguous scope, clarification needed"
|
|
32
44
|
},
|
|
33
45
|
"qualityReadiness": {
|
|
34
46
|
"title": "Quality Readiness",
|
|
@@ -63,7 +75,10 @@
|
|
|
63
75
|
"Brownfield Analysis": "Implementation preparation",
|
|
64
76
|
"CD+Tests": "Implementation and tests",
|
|
65
77
|
"CR": "Code review",
|
|
66
|
-
"OR": "Delivery report"
|
|
78
|
+
"OR": "Delivery report",
|
|
79
|
+
"Verified Change": "Verified change",
|
|
80
|
+
"Quick Task": "Quick task",
|
|
81
|
+
"Block": "Block"
|
|
67
82
|
},
|
|
68
83
|
"gateActionTitles": {
|
|
69
84
|
"UR": "Approve user requirements",
|
|
@@ -83,7 +98,12 @@
|
|
|
83
98
|
"declineLabel": "Decline",
|
|
84
99
|
"declineDescription": "Reject this gate deliberately; authority remains unchanged.",
|
|
85
100
|
"cancelLabel": "Cancel",
|
|
86
|
-
"cancelDescription": "Close this decision without approving or declining it."
|
|
101
|
+
"cancelDescription": "Close this decision without approving or declining it.",
|
|
102
|
+
"why": {
|
|
103
|
+
"label": "Why?",
|
|
104
|
+
"fulfilledPrefix": "Fulfilled:",
|
|
105
|
+
"protectsPrefix": "Protects against:"
|
|
106
|
+
}
|
|
87
107
|
},
|
|
88
108
|
"artefacts": {
|
|
89
109
|
"notYetCreated": "not yet created"
|
|
@@ -128,7 +148,33 @@
|
|
|
128
148
|
"TP": "Run pre-implementation Brownfield Analysis; implementation starts only if it passes.",
|
|
129
149
|
"QA": "Prepare the user-acceptance decision; release remains blocked.",
|
|
130
150
|
"UAT": "Prepare the delivery report; version-control and release actions still require explicit instruction."
|
|
151
|
+
},
|
|
152
|
+
"narration": {
|
|
153
|
+
"gateSatisfied": "{gate} approved",
|
|
154
|
+
"noAction": "no user action required now",
|
|
155
|
+
"gates": {
|
|
156
|
+
"UR": { "agentNext": "Agent runs Brownfield Review next" },
|
|
157
|
+
"PRD": { "agentNext": "Agent drafts the Solution Design next" },
|
|
158
|
+
"SD": { "agentNext": "Agent drafts the Task and Test Plan next" },
|
|
159
|
+
"TP": { "agentNext": "Agent runs pre-implementation Brownfield Analysis next" },
|
|
160
|
+
"QA": { "agentNext": "Agent prepares the user-acceptance decision", "userAction": "user acceptance decision next" },
|
|
161
|
+
"UAT": { "agentNext": "Agent prepares the delivery report" }
|
|
162
|
+
}
|
|
131
163
|
}
|
|
164
|
+
},
|
|
165
|
+
"gateRationale": {
|
|
166
|
+
"UR": "UR clarifies what you want, why and for whom — before anything gets built that nobody needed.",
|
|
167
|
+
"PRD": "PRD defines product behavior before the solution design locks it in.",
|
|
168
|
+
"SD": "Solution design fixes ownership, architecture and dependencies — before code makes them permanent.",
|
|
169
|
+
"TP": "Task plan makes acceptance criteria and tests binding, so QA can measure later.",
|
|
170
|
+
"QA": "QA evaluates real evidence before a release relies on assumptions.",
|
|
171
|
+
"UAT": "UAT requires deliberate acceptance before the run counts as delivered.",
|
|
172
|
+
"Brownfield Review": "Checks whether existing systems, owners and conventions are affected, so nothing breaks or duplicates.",
|
|
173
|
+
"Mode/Slice Decision": "Decides the proportionate delivery scope, so the process fits the change — not the other way around.",
|
|
174
|
+
"Brownfield Analysis": "Verifies reuse paths, owners and regression risk before code exists.",
|
|
175
|
+
"CD+Tests": "Implementation and tests produce evidence — not just code.",
|
|
176
|
+
"CR": "Code review is mandatory before QA decides.",
|
|
177
|
+
"OR": "The delivery report makes the run auditable — not just claimed."
|
|
132
178
|
}
|
|
133
179
|
},
|
|
134
180
|
"de": {
|
|
@@ -151,7 +197,19 @@
|
|
|
151
197
|
"lifecycle": "Lieferstatus",
|
|
152
198
|
"allowed": "Jetzt erlaubt",
|
|
153
199
|
"forbidden": "Aktuell verboten",
|
|
154
|
-
"artefacts": "Artefakte"
|
|
200
|
+
"artefacts": "Artefakte",
|
|
201
|
+
"breadcrumb": "Pfad",
|
|
202
|
+
"breadcrumbFulfilled": "\u2713",
|
|
203
|
+
"breadcrumbCurrent": "\u25cf",
|
|
204
|
+
"breadcrumbOpen": "\u25cb",
|
|
205
|
+
"breadcrumbSeparator": " \u00b7 "
|
|
206
|
+
},
|
|
207
|
+
"internalStateLabels": {
|
|
208
|
+
"verifiedChange": "Kompakte Änderung wird geprüft",
|
|
209
|
+
"verifiedChangeEscalated": "Eskaliert zu structured delivery",
|
|
210
|
+
"contextGraphMaintained": "Projektgedächtnis wird gepflegt",
|
|
211
|
+
"contextGraphOpenGap": "Graph-Lücke offen",
|
|
212
|
+
"multiScopeBlocked": "Mehrdeutiger Scope, Klärung nötig"
|
|
155
213
|
},
|
|
156
214
|
"qualityReadiness": {
|
|
157
215
|
"title": "Quality Readiness",
|
|
@@ -186,7 +244,10 @@
|
|
|
186
244
|
"Brownfield Analysis": "Implementierungsvorbereitung",
|
|
187
245
|
"CD+Tests": "Implementierung und Tests",
|
|
188
246
|
"CR": "Code-Review",
|
|
189
|
-
"OR": "Lieferbericht"
|
|
247
|
+
"OR": "Lieferbericht",
|
|
248
|
+
"Verified Change": "Verified Change",
|
|
249
|
+
"Quick Task": "Quick Task",
|
|
250
|
+
"Block": "Block"
|
|
190
251
|
},
|
|
191
252
|
"gateActionTitles": {
|
|
192
253
|
"UR": "Nutzeranforderung freigeben",
|
|
@@ -206,7 +267,12 @@
|
|
|
206
267
|
"declineLabel": "Ablehnen",
|
|
207
268
|
"declineDescription": "Dieses Gate bewusst ablehnen; die Berechtigung bleibt unverändert.",
|
|
208
269
|
"cancelLabel": "Abbrechen",
|
|
209
|
-
"cancelDescription": "Diese Entscheidung ohne Freigabe oder Ablehnung schließen."
|
|
270
|
+
"cancelDescription": "Diese Entscheidung ohne Freigabe oder Ablehnung schließen.",
|
|
271
|
+
"why": {
|
|
272
|
+
"label": "Warum?",
|
|
273
|
+
"fulfilledPrefix": "Erfüllt:",
|
|
274
|
+
"protectsPrefix": "Schützt vor:"
|
|
275
|
+
}
|
|
210
276
|
},
|
|
211
277
|
"artefacts": {
|
|
212
278
|
"notYetCreated": "noch nicht erstellt"
|
|
@@ -251,7 +317,33 @@
|
|
|
251
317
|
"TP": "Die Brownfield-Analyse vor der Implementierung durchführen; die Implementierung startet nur nach bestandenem Ergebnis.",
|
|
252
318
|
"QA": "Die Nutzerabnahme vorbereiten; die Veröffentlichung bleibt gesperrt.",
|
|
253
319
|
"UAT": "Den Lieferbericht vorbereiten; Versionskontroll- und Veröffentlichungsaktionen benötigen weiterhin eine ausdrückliche Anweisung."
|
|
320
|
+
},
|
|
321
|
+
"narration": {
|
|
322
|
+
"gateSatisfied": "{gate} genehmigt",
|
|
323
|
+
"noAction": "keine Nutzeraktion erforderlich",
|
|
324
|
+
"gates": {
|
|
325
|
+
"UR": { "agentNext": "Agent führt Brownfield Review als Nächstes durch" },
|
|
326
|
+
"PRD": { "agentNext": "Agent entwirft als Nächstes das Lösungsdesign" },
|
|
327
|
+
"SD": { "agentNext": "Agent entwirft als Nächstes den Aufgaben- und Testplan" },
|
|
328
|
+
"TP": { "agentNext": "Agent führt Brownfield-Analyse vor Implementierung durch" },
|
|
329
|
+
"QA": { "agentNext": "Agent bereitet die Nutzerabnahme vor", "userAction": "Nutzerabnahme als Nächstes" },
|
|
330
|
+
"UAT": { "agentNext": "Agent bereitet den Lieferbericht vor" }
|
|
331
|
+
}
|
|
254
332
|
}
|
|
333
|
+
},
|
|
334
|
+
"gateRationale": {
|
|
335
|
+
"UR": "UR klärt, was du willst, warum und für wen — bevor etwas gebaut wird, das niemand brauchte.",
|
|
336
|
+
"PRD": "PRD definiert das Produktverhalten, bevor das Lösungsdesign es zementiert.",
|
|
337
|
+
"SD": "Lösungsdesign legt Ownership, Architektur und Abhängigkeiten fest — bevor Code sie faktisch zementiert.",
|
|
338
|
+
"TP": "Taskplan macht Akzeptanzkriterien und Tests verbindlich, damit QA später messen kann.",
|
|
339
|
+
"QA": "QA bewertet die echten Nachweise, bevor eine Freigabe auf Vermutungen basiert.",
|
|
340
|
+
"UAT": "UAT verlangt eine bewusste Abnahme, bevor der Lauf als geliefert gilt.",
|
|
341
|
+
"Brownfield Review": "Prüft, ob bestehende Systeme, Owners und Konventionen betroffen sind, damit nichts kaputt geht oder doppelt entsteht.",
|
|
342
|
+
"Mode/Slice Decision": "Entscheidet den angemessenen Lieferumfang, damit der Prozess zur Veränderung passt — nicht umgekehrt.",
|
|
343
|
+
"Brownfield Analysis": "Verifiziert Wiederverwendung, Owners und Regressionstests, bevor Code existiert.",
|
|
344
|
+
"CD+Tests": "Implementierung und Tests liefern Nachweise — nicht nur Code.",
|
|
345
|
+
"CR": "Code-Review ist verpflichtend, bevor QA entscheidet.",
|
|
346
|
+
"OR": "Der Lieferbericht macht den Lauf auditierbar — nicht nur behauptet."
|
|
255
347
|
}
|
|
256
348
|
}
|
|
257
349
|
}
|
|
@@ -43,9 +43,10 @@ Use the Runtime Contract definition of `Relevant Run` to decide whether OR is ma
|
|
|
43
43
|
9. End with the immediately permissible next step.
|
|
44
44
|
10. `CD+Tests` is not completion.
|
|
45
45
|
11. When moving a backlog item to Completed or Superseded, use the canonical human-readable table and link the final OR with a document-relative Markdown link.
|
|
46
|
-
12.
|
|
47
|
-
13.
|
|
48
|
-
14.
|
|
46
|
+
12. For an executed `verified_change`, a lifecycle-consistent complete Mini-Closeout in the linked `VERIFIED_CHANGE.md` is the compact OR target; do not require a separate `OR.md` by ritual.
|
|
47
|
+
13. Keep machine status normalization in the CLI; do not write internal snake_case status values into the human-facing backlog.
|
|
48
|
+
14. Reconcile Context Graph impact before clean closeout: report `context_graph_reconciliation: resolved | not_applicable | open_gap`.
|
|
49
|
+
15. If Context Graph work remains unresolved, report it as an explicit open gap and do not describe the run as cleanly handoff-ready.
|
|
49
50
|
15. After writing or updating `MASTER_BACKLOG.md`, run `doctor --json` (or the locally available equivalent) and resolve any `AGDF_BACKLOG_STATUS_UNKNOWN` or `AGDF_BACKLOG_ARTEFACT_LABEL_UNKNOWN` finding before closeout. See the AGDF control scaffold's `MASTER_BACKLOG.md` template Rules section for the canonical status/artefact label vocabulary.
|
|
50
51
|
|
|
51
52
|
## When To Use
|