create-agdf 0.14.1 → 0.14.3
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 +13 -3
- package/generated/.opencode/contracts/control-scaffold.md +17 -2
- package/generated/.opencode/skills/agdf-delivery-path-search/SKILL.md +16 -4
- package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +6 -1
- package/generated/plugins/agdf/meta/contracts/control-scaffold.md +17 -2
- package/generated/plugins/agdf/meta/copilot-payload-baseline.json +7 -0
- package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +6 -1
- package/generated/plugins/agdf/runtime/create-agdf/lib/cli/delivery-path-search-command.js +51 -19
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/contracts.js +37 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/persistence.js +14 -2
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js +126 -36
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/state-adapter.js +40 -92
- package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/local-validator.js +29 -17
- package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/plugin-provenance.js +50 -1
- package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
- package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
- package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +2 -1
- package/generated/plugins/agdf/skills/delivery-path-search/SKILL.md +16 -4
- package/generated/plugins/agdf/submission/openai/availability.md +1 -1
- package/generated/plugins/agdf/submission/openai/capability-matrix.json +1 -1
- package/generated/plugins/agdf/submission/openai/release-notes.md +7 -1
- package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
- package/generated/plugins/copilot/agdf/.agdf-payload-inventory.json +771 -0
- package/generated/plugins/copilot/agdf/LICENSE +201 -0
- package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-delivery-path-search/SKILL.md +16 -4
- package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/control-scaffold.md +17 -2
- package/generated/plugins/copilot/agdf/meta/agdf-agent-router.md +129 -0
- package/generated/plugins/copilot/agdf/meta/agdf-constitution.md +329 -0
- package/generated/plugins/copilot/agdf/meta/agdf-plugin.definition.json +331 -0
- package/generated/plugins/copilot/agdf/meta/agdf-runtime-contract.md +16 -0
- package/generated/plugins/{agdf → copilot/agdf}/plugin.json +1 -1
- package/generated/plugins/copilot/agdf/runtime/agdf-local.js +18 -0
- package/generated/plugins/copilot/agdf/runtime/agdf-session-check.js +97 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/NOTICE +10 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/bin/agdf-validator.js +11 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +512 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +331 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/command-registry.js +144 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/delivery-path-search-command.js +171 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/parse-args.js +173 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/runtime-context.js +83 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/validation-handlers.js +46 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/delivery-map.js +301 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/doctor.js +272 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/gate-check.js +455 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/gate-policy.js +305 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/parent-reconciliation.js +284 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/required-files.js +9 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/run-state.js +217 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/shared.js +345 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/verified-change.js +156 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/aggregate.js +18 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/gate-approval-validator.js +49 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/index.js +7 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/legacy-migration.js +95 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-parser.js +265 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-repository.js +68 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-resolver.js +45 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-writer.js +97 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/candidate-policy.js +99 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/contracts.js +274 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/claude.js +35 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/codex.js +44 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/opencode.js +234 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/prompt.js +13 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/protocol.js +13 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/generators/claude.js +40 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/generators/codex.js +38 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/generators/protocol.js +12 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/persistence.js +51 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/scoring.js +21 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js +245 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/state-adapter.js +109 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/surfaces/capabilities.js +27 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/transports/read-only-guard.js +43 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/interaction-presentation.js +842 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/local-validator.js +239 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/plugin-provenance.js +255 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/validator-application.js +40 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime-check-consent/contract.js +73 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/package.json +6 -0
- package/generated/plugins/copilot/agdf/runtime/runtime-manifest.json +8 -0
- package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +6 -1
- package/generated/submissions/openai/agdf/meta/contracts/control-scaffold.md +17 -2
- package/generated/submissions/openai/agdf/meta/copilot-payload-baseline.json +7 -0
- package/generated/submissions/openai/agdf/skills/delivery-path-search/SKILL.md +16 -4
- package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
- package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/inventory.json +15 -11
- package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
- package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
- package/generated/submissions/openai/agdf/submission/openai/release-notes.md +7 -1
- package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
- package/lib/cli/delivery-path-search-command.js +51 -19
- package/lib/delivery-path-search/contracts.js +37 -0
- package/lib/delivery-path-search/persistence.js +14 -2
- package/lib/delivery-path-search/search-engine.js +126 -36
- package/lib/delivery-path-search/state-adapter.js +40 -92
- package/lib/installers/local-marketplace.js +112 -30
- package/lib/installers/plugin-installers.js +45 -9
- package/lib/public-plugin/copilot-profile.js +126 -0
- package/lib/release/version-coherence.js +4 -1
- package/lib/runtime/local-validator.js +29 -17
- package/lib/runtime/plugin-provenance.js +50 -1
- package/package.json +3 -2
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-brownfield-analysis/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-clean-implementation-review/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-code-review/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-delivery-closeout/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-gate-check/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-interaction-locales.json +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-qa-gate/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-release-or/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-runtime-contract.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-task-plan-review/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-ux-intent-definition/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/closeout.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/context-graph.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/gate-transition.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/interaction.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/modes.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/quality.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/task-target-resolution.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/hooks/copilot-hooks.json +0 -0
package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/verified-change.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { isAbsolute, posix, relative, resolve } from "node:path";
|
|
4
|
+
import { cleanStatusCell, isPlaceholderValue } from "./shared.js";
|
|
5
|
+
|
|
6
|
+
export function extractField(content, field) {
|
|
7
|
+
const pattern = new RegExp(`^- ${field}:[^\\S\\r\\n]*(.*)$`, "m");
|
|
8
|
+
return content.match(pattern)?.[1]?.trim() ?? "";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const verifiedChangeStatuses = new Set(["draft", "eligible", "executed", "escalated"]);
|
|
12
|
+
export const verifiedChangeEscalationTargets = new Set(["structured_slice", "structured_delivery"]);
|
|
13
|
+
|
|
14
|
+
export function parseVerifiedChangePathList(value) {
|
|
15
|
+
const cleaned = cleanStatusCell(value ?? "");
|
|
16
|
+
if (!cleaned || cleaned === "none") return [];
|
|
17
|
+
return cleaned.split(",").map((item) => item.trim()).filter(Boolean);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isSafeRepoRelativePath(value) {
|
|
21
|
+
if (!value || value === "none" || value.startsWith("/") || value.includes("\\")) return false;
|
|
22
|
+
const normalized = posix.normalize(value);
|
|
23
|
+
return normalized !== "." && normalized !== ".." && !normalized.startsWith("../") && normalized === value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function gitPathList(targetDir, args) {
|
|
27
|
+
try {
|
|
28
|
+
return execFileSync("git", args, { cwd: targetDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] })
|
|
29
|
+
.split("\n")
|
|
30
|
+
.map((item) => item.trim())
|
|
31
|
+
.filter(Boolean);
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function gitValue(targetDir, args) {
|
|
38
|
+
try {
|
|
39
|
+
return execFileSync("git", args, { cwd: targetDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function readVerifiedChangeRecord(targetDir, runState) {
|
|
46
|
+
const artefact = runState.artefacts.get("Verified Change");
|
|
47
|
+
if (!artefact?.path || isPlaceholderValue(artefact.path)) return { status: "missing", path: "", content: "" };
|
|
48
|
+
if (!isSafeRepoRelativePath(artefact.path)) return { status: "invalid", path: artefact.path, content: "", error: "record_path_invalid" };
|
|
49
|
+
const absolutePath = resolve(targetDir, artefact.path);
|
|
50
|
+
const relativePath = relative(resolve(targetDir), absolutePath);
|
|
51
|
+
if (isAbsolute(relativePath) || relativePath === ".." || relativePath.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || !existsSync(absolutePath)) {
|
|
52
|
+
return { status: "missing", path: artefact.path, content: "", error: "record_missing" };
|
|
53
|
+
}
|
|
54
|
+
return { status: "present", path: artefact.path, content: readFileSync(absolutePath, "utf8") };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function evaluateVerifiedChange(targetDir, runState) {
|
|
58
|
+
const record = readVerifiedChangeRecord(targetDir, runState);
|
|
59
|
+
const findings = [];
|
|
60
|
+
const add = (code, message, nextStep, severity = "revise") => findings.push({
|
|
61
|
+
severity,
|
|
62
|
+
code,
|
|
63
|
+
message,
|
|
64
|
+
path: record.path || runState.path,
|
|
65
|
+
next_step: nextStep,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (record.status === "missing") {
|
|
69
|
+
return { status: "missing", record, findings };
|
|
70
|
+
}
|
|
71
|
+
if (record.status === "invalid") {
|
|
72
|
+
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");
|
|
73
|
+
return { status: "invalid", record, findings };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const status = cleanStatusCell(extractField(record.content, "status"));
|
|
77
|
+
const relatedUr = cleanStatusCell(extractField(record.content, "related_ur"));
|
|
78
|
+
const escalationTarget = cleanStatusCell(extractField(record.content, "escalation_target"));
|
|
79
|
+
const canonicalOwner = cleanStatusCell(extractField(record.content, "canonical_owner"));
|
|
80
|
+
const sourcePaths = parseVerifiedChangePathList(extractField(record.content, "allowed_source_paths"));
|
|
81
|
+
const derivedPaths = parseVerifiedChangePathList(extractField(record.content, "allowed_derived_paths"));
|
|
82
|
+
const prohibitedImpacts = cleanStatusCell(extractField(record.content, "prohibited_impacts"));
|
|
83
|
+
const propagationCommand = extractField(record.content, "propagation_command");
|
|
84
|
+
const validationCommands = extractField(record.content, "validation_commands");
|
|
85
|
+
const baselineTracked = parseVerifiedChangePathList(extractField(record.content, "baseline_tracked_paths"));
|
|
86
|
+
const baselineUntracked = parseVerifiedChangePathList(extractField(record.content, "baseline_untracked_paths"));
|
|
87
|
+
const baselineCommit = cleanStatusCell(extractField(record.content, "baseline_commit"));
|
|
88
|
+
const executionChangedPaths = parseVerifiedChangePathList(extractField(record.content, "execution_changed_paths"));
|
|
89
|
+
const executionScopeStatus = cleanStatusCell(extractField(record.content, "execution_scope_status"));
|
|
90
|
+
const validationStatus = cleanStatusCell(extractField(record.content, "validation_status"));
|
|
91
|
+
const propagationStatus = cleanStatusCell(extractField(record.content, "propagation_status"));
|
|
92
|
+
const urArtefact = runState.artefacts.get("UR");
|
|
93
|
+
const lifecycle = extractField(runState.content, "lifecycle") || "active";
|
|
94
|
+
const runId = extractField(runState.content, "run_id") || "";
|
|
95
|
+
|
|
96
|
+
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.");
|
|
97
|
+
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.");
|
|
98
|
+
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.");
|
|
99
|
+
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.");
|
|
100
|
+
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.");
|
|
101
|
+
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.");
|
|
102
|
+
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.");
|
|
103
|
+
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.");
|
|
104
|
+
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.");
|
|
105
|
+
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.");
|
|
106
|
+
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.");
|
|
107
|
+
|
|
108
|
+
if (["eligible", "executed"].includes(status) && lifecycle !== "completed" && /^[0-9a-f]{40,64}$/i.test(baselineCommit)) {
|
|
109
|
+
const currentHead = gitValue(targetDir, ["rev-parse", "HEAD"]);
|
|
110
|
+
if (!currentHead) {
|
|
111
|
+
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");
|
|
112
|
+
} else if (baselineCommit !== currentHead) {
|
|
113
|
+
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");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const allowedPaths = new Set([...sourcePaths, ...derivedPaths]);
|
|
118
|
+
const baselinePaths = new Set([...baselineTracked, ...baselineUntracked]);
|
|
119
|
+
const dirtyCandidate = [...allowedPaths].find((path) => baselinePaths.has(path));
|
|
120
|
+
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");
|
|
121
|
+
|
|
122
|
+
const permittedControlPaths = new Set([record.path, runState.path, ".agdf/control/MASTER_BACKLOG.md"]);
|
|
123
|
+
const runArtefactPrefix = `.agdf/control/artefacts/${runId}/`;
|
|
124
|
+
for (const artefact of runState.artefacts.values()) {
|
|
125
|
+
if (artefact.path_format === "invalid") continue;
|
|
126
|
+
if (isSafeRepoRelativePath(artefact.path) && artefact.path.startsWith(runArtefactPrefix)) permittedControlPaths.add(artefact.path);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!(lifecycle === "completed" && status === "executed")) {
|
|
130
|
+
const currentTracked = gitPathList(targetDir, ["diff", "HEAD", "--name-only"]);
|
|
131
|
+
const currentUntracked = gitPathList(targetDir, ["ls-files", "--others", "--exclude-standard"]);
|
|
132
|
+
if (currentTracked === null || currentUntracked === null) {
|
|
133
|
+
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");
|
|
134
|
+
} else {
|
|
135
|
+
const introduced = [...new Set([...currentTracked, ...currentUntracked])].filter((path) => !baselinePaths.has(path)).sort();
|
|
136
|
+
const unexpected = introduced.filter((path) => !allowedPaths.has(path) && !permittedControlPaths.has(path));
|
|
137
|
+
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");
|
|
138
|
+
if (status === "executed" && JSON.stringify(introduced) !== JSON.stringify([...executionChangedPaths].sort())) {
|
|
139
|
+
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");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
const unsafeExecutionPath = executionChangedPaths.find((path) => !isSafeRepoRelativePath(path) || (!allowedPaths.has(path) && !permittedControlPaths.has(path)));
|
|
144
|
+
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");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (status === "executed") {
|
|
148
|
+
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.");
|
|
149
|
+
if (validationStatus !== "pass") add("AGDF_VERIFIED_CHANGE_VALIDATION_EVIDENCE_MISSING", "Executed Verified Change requires validation_status: pass.", "Record passing deterministic validation evidence or escalate.");
|
|
150
|
+
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.");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (status === "escalated") return { status: "escalated", record, escalation_target: escalationTarget, findings };
|
|
154
|
+
if (findings.length > 0) return { status: "invalid", record, escalation_target: escalationTarget, findings };
|
|
155
|
+
return { status, record, escalation_target: escalationTarget, findings };
|
|
156
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const STATUS_RANK = { pass: 0, warn: 1, revise: 2, block: 3 };
|
|
2
|
+
|
|
3
|
+
export function aggregate(results, { allowNoActiveRuns = false } = {}) {
|
|
4
|
+
const runs = [...results].sort((left, right) =>
|
|
5
|
+
left.run_id.localeCompare(right.run_id),
|
|
6
|
+
);
|
|
7
|
+
const status = runs.length
|
|
8
|
+
? runs.reduce(
|
|
9
|
+
(current, run) =>
|
|
10
|
+
STATUS_RANK[run.status] > STATUS_RANK[current] ? run.status : current,
|
|
11
|
+
"pass",
|
|
12
|
+
)
|
|
13
|
+
: allowNoActiveRuns
|
|
14
|
+
? "pass"
|
|
15
|
+
: "revise";
|
|
16
|
+
|
|
17
|
+
return { status, runs };
|
|
18
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { normalizeInteractionOutcome } from "../interaction-presentation.js";
|
|
2
|
+
|
|
3
|
+
export function validateGateApprovalResponse({
|
|
4
|
+
response,
|
|
5
|
+
responseOutcome,
|
|
6
|
+
responseOrigin,
|
|
7
|
+
expectedApproval,
|
|
8
|
+
expectedRunId,
|
|
9
|
+
currentRunId,
|
|
10
|
+
expectedGate,
|
|
11
|
+
currentGate,
|
|
12
|
+
expectedRevisionId,
|
|
13
|
+
currentRevisionId,
|
|
14
|
+
durableArtefactReady,
|
|
15
|
+
timedOut = false,
|
|
16
|
+
noResponse = false,
|
|
17
|
+
}) {
|
|
18
|
+
const checks = [
|
|
19
|
+
[responseOrigin === "deliberate_user_input", "non_deliberate_response"],
|
|
20
|
+
[expectedApproval === `Approval: ${expectedGate}`, "approval_formula_mismatch"],
|
|
21
|
+
[Boolean(expectedRunId) && expectedRunId === currentRunId, "changed_or_wrong_run"],
|
|
22
|
+
[Boolean(expectedGate) && expectedGate === currentGate, "changed_or_wrong_gate"],
|
|
23
|
+
[Boolean(expectedRevisionId) && expectedRevisionId === currentRevisionId, "stale_revision"],
|
|
24
|
+
[durableArtefactReady === true, "durable_artefact_not_ready"],
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const failed = checks.find(([accepted]) => !accepted);
|
|
28
|
+
if (failed) return { accepted: false, reason: failed[1] };
|
|
29
|
+
|
|
30
|
+
const outcomeCandidate = responseOutcome && responseOutcome !== "approve" ? responseOutcome : response;
|
|
31
|
+
const outcome = normalizeInteractionOutcome(outcomeCandidate, {
|
|
32
|
+
expectedApproval,
|
|
33
|
+
timedOut,
|
|
34
|
+
noResponse,
|
|
35
|
+
});
|
|
36
|
+
const rejectionReasons = {
|
|
37
|
+
revise: "revision_requested",
|
|
38
|
+
decline: "declined",
|
|
39
|
+
cancel: "cancelled",
|
|
40
|
+
no_response: "no_response",
|
|
41
|
+
timeout: "timed_out",
|
|
42
|
+
empty: "empty_response",
|
|
43
|
+
invalid: "wrong_or_non_approval_response",
|
|
44
|
+
stale: "stale_response",
|
|
45
|
+
};
|
|
46
|
+
return outcome === "approve"
|
|
47
|
+
? { accepted: true, reason: "accepted" }
|
|
48
|
+
: { accepted: false, reason: rejectionReasons[outcome] ?? "wrong_or_non_approval_response" };
|
|
49
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./run-state-parser.js";
|
|
2
|
+
export * from "./run-state-repository.js";
|
|
3
|
+
export * from "./run-state-resolver.js";
|
|
4
|
+
export * from "./run-state-writer.js";
|
|
5
|
+
export * from "./legacy-migration.js";
|
|
6
|
+
export * from "./aggregate.js";
|
|
7
|
+
export * from "./gate-approval-validator.js";
|
package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/legacy-migration.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, rmdirSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
discoverRuns,
|
|
6
|
+
renderRunState,
|
|
7
|
+
runPath,
|
|
8
|
+
} from "./run-state-repository.js";
|
|
9
|
+
import {
|
|
10
|
+
parseRunState,
|
|
11
|
+
RUN_ID_PATTERN,
|
|
12
|
+
scalarFields,
|
|
13
|
+
semanticBody,
|
|
14
|
+
semanticFingerprint,
|
|
15
|
+
} from "./run-state-parser.js";
|
|
16
|
+
import { atomicWrite } from "./run-state-writer.js";
|
|
17
|
+
export function migrateLegacy(root, requested, io = {}) {
|
|
18
|
+
const writeCanonical = io.atomicWrite ?? atomicWrite;
|
|
19
|
+
const readCanonical = io.readFile ?? readFileSync;
|
|
20
|
+
const lp = join(root, ".agdf", "control", "AGDF_RUN.md");
|
|
21
|
+
if (!existsSync(lp)) throw Error("AGDF_LEGACY_RUN_MISSING");
|
|
22
|
+
const legacy = readFileSync(lp, "utf8");
|
|
23
|
+
if (legacy.startsWith("<!-- AGDF LEGACY PROJECTION"))
|
|
24
|
+
throw Error("AGDF_LEGACY_ALREADY_PROJECTED");
|
|
25
|
+
if (!legacy.startsWith("# AGDF Run State") || !semanticBody(legacy))
|
|
26
|
+
throw Error("AGDF_LEGACY_RUN_INVALID");
|
|
27
|
+
const id = requested || scalarFields(legacy).values.get("run_id");
|
|
28
|
+
if (!RUN_ID_PATTERN.test(id ?? "")) throw Error("AGDF_RUN_ID_INVALID");
|
|
29
|
+
const legacyMeta = Object.fromEntries(scalarFields(legacy).values);
|
|
30
|
+
const dest = runPath(root, id),
|
|
31
|
+
next = renderRunState(id, semanticBody(legacy), legacyMeta);
|
|
32
|
+
if (existsSync(dest)) {
|
|
33
|
+
if (
|
|
34
|
+
semanticFingerprint(readFileSync(dest, "utf8")) ===
|
|
35
|
+
semanticFingerprint(next)
|
|
36
|
+
)
|
|
37
|
+
return { status: "already_migrated", path: dest };
|
|
38
|
+
throw Error("AGDF_RUN_COLLISION");
|
|
39
|
+
}
|
|
40
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
41
|
+
try {
|
|
42
|
+
writeCanonical(dest, next);
|
|
43
|
+
const persisted = readCanonical(dest, "utf8"),
|
|
44
|
+
parsed = parseRunState(persisted, id);
|
|
45
|
+
if (
|
|
46
|
+
!parsed.valid ||
|
|
47
|
+
semanticFingerprint(persisted) !== semanticFingerprint(next)
|
|
48
|
+
) throw Error("AGDF_MIGRATION_VERIFICATION_FAILED");
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (existsSync(dest)) unlinkSync(dest);
|
|
51
|
+
try {
|
|
52
|
+
rmdirSync(dirname(dest));
|
|
53
|
+
} catch (cleanupError) {
|
|
54
|
+
if (!["ENOENT", "ENOTEMPTY"].includes(cleanupError.code)) throw cleanupError;
|
|
55
|
+
}
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
return { status: "migrated", path: dest };
|
|
59
|
+
}
|
|
60
|
+
export function renderLegacyProjection(path, root = process.cwd()) {
|
|
61
|
+
const content = readFileSync(path, "utf8"),
|
|
62
|
+
parsed = parseRunState(content),
|
|
63
|
+
digest = createHash("sha256").update(content).digest("hex");
|
|
64
|
+
if (!parsed.valid) throw Error("AGDF_RUN_NOT_SELECTABLE");
|
|
65
|
+
const canonicalSource = relative(resolve(root), resolve(path)).replaceAll("\\", "/");
|
|
66
|
+
return `<!-- AGDF LEGACY PROJECTION: NON-AUTHORITATIVE -->\n<!-- canonical_source: ${canonicalSource} -->\n<!-- run_id: ${parsed.meta.run_id} -->\n<!-- revision_id: ${parsed.meta.revision_id} -->\n<!-- sha256: ${digest} -->\n${content}`;
|
|
67
|
+
}
|
|
68
|
+
export function writeLegacyProjection(path, canonical) {
|
|
69
|
+
atomicWrite(path, renderLegacyProjection(canonical, resolve(dirname(path), "../..")));
|
|
70
|
+
}
|
|
71
|
+
export function verifyLegacyProjection(root) {
|
|
72
|
+
const path = join(root, ".agdf", "control", "AGDF_RUN.md");
|
|
73
|
+
if (!existsSync(path)) return { status: "absent" };
|
|
74
|
+
const p = readFileSync(path, "utf8"),
|
|
75
|
+
source = p.match(/canonical_source:\s*(.*?)\s*-->/)?.[1],
|
|
76
|
+
runId = p.match(/<!-- run_id:\s*(.*?)\s*-->/)?.[1],
|
|
77
|
+
digest = p.match(/sha256:\s*([0-9a-f]{64})/)?.[1],
|
|
78
|
+
projectedStart = p.indexOf("# AGDF Run State"),
|
|
79
|
+
projected = projectedStart >= 0 ? p.slice(projectedStart) : undefined;
|
|
80
|
+
if (!source || !runId || !digest || projected === undefined)
|
|
81
|
+
return { status: discoverRuns(root).length ? "mixed_authority" : "legacy" };
|
|
82
|
+
const target = resolve(root, source.replaceAll("\\", "/")),
|
|
83
|
+
rel = relative(resolve(root), target),
|
|
84
|
+
expected = relative(resolve(root), resolve(runPath(root, runId)));
|
|
85
|
+
if (rel.startsWith("..") || !rel || rel !== expected || !existsSync(target))
|
|
86
|
+
return { status: "projection_source_invalid" };
|
|
87
|
+
const canonical = readFileSync(target, "utf8"),
|
|
88
|
+
canonicalDigest = createHash("sha256").update(canonical).digest("hex");
|
|
89
|
+
return {
|
|
90
|
+
status:
|
|
91
|
+
canonicalDigest === digest && projected === canonical
|
|
92
|
+
? "valid"
|
|
93
|
+
: "legacy_projection_drift",
|
|
94
|
+
};
|
|
95
|
+
}
|
package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-parser.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
export const RUN_ID_PATTERN = /^[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
2
|
+
export const LIFECYCLES = new Set([
|
|
3
|
+
"active",
|
|
4
|
+
"completed",
|
|
5
|
+
"superseded",
|
|
6
|
+
"abandoned",
|
|
7
|
+
]);
|
|
8
|
+
export function scalarFields(content) {
|
|
9
|
+
const values = new Map(),
|
|
10
|
+
duplicates = [];
|
|
11
|
+
const runMeta = content.match(/(?:^|\n)## Run Meta\s*\n([\s\S]*?)(?=\n## |$)/)?.[1] ?? "";
|
|
12
|
+
for (const [, k, v] of runMeta.matchAll(/^- ([a-z_]+):\s*(.*?)\s*$/gm)) {
|
|
13
|
+
if (values.has(k)) duplicates.push(k);
|
|
14
|
+
else values.set(k, v.replace(/^`|`$/g, ""));
|
|
15
|
+
}
|
|
16
|
+
return { values, duplicates };
|
|
17
|
+
}
|
|
18
|
+
export function parseRunState(content, expected) {
|
|
19
|
+
const { values, duplicates } = scalarFields(content),
|
|
20
|
+
findings = duplicates.map((field) => ({
|
|
21
|
+
code: "AGDF_RUN_FIELD_DUPLICATE",
|
|
22
|
+
field,
|
|
23
|
+
})),
|
|
24
|
+
id = values.get("run_id") ?? "";
|
|
25
|
+
if (content.split(/\r?\n/).some((line) => line.trimStart().startsWith("|") && !line.trimEnd().endsWith("|"))) {
|
|
26
|
+
findings.push({ code: "AGDF_RUN_TABLE_INVALID" });
|
|
27
|
+
}
|
|
28
|
+
if (!RUN_ID_PATTERN.test(id)) findings.push({ code: "AGDF_RUN_ID_INVALID" });
|
|
29
|
+
if (expected && id !== expected)
|
|
30
|
+
findings.push({ code: "AGDF_RUN_PATH_MISMATCH" });
|
|
31
|
+
if (values.get("control_state_version") !== "2")
|
|
32
|
+
findings.push({ code: "AGDF_RUN_VERSION_UNSUPPORTED" });
|
|
33
|
+
if (!LIFECYCLES.has(values.get("lifecycle")))
|
|
34
|
+
findings.push({ code: "AGDF_RUN_LIFECYCLE_INVALID" });
|
|
35
|
+
if (!/^[1-9]\d*$/.test(values.get("revision") ?? ""))
|
|
36
|
+
findings.push({ code: "AGDF_RUN_REVISION_INVALID" });
|
|
37
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(values.get("revision_id") ?? ""))
|
|
38
|
+
findings.push({ code: "AGDF_RUN_REVISION_ID_INVALID" });
|
|
39
|
+
return {
|
|
40
|
+
content,
|
|
41
|
+
meta: Object.fromEntries(values),
|
|
42
|
+
findings,
|
|
43
|
+
valid: !findings.length,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function semanticBody(content) {
|
|
47
|
+
return content
|
|
48
|
+
.replace(/^# AGDF Run State\s*/m, "")
|
|
49
|
+
.replace(/## Run Meta[\s\S]*?(?=\n## )/, "")
|
|
50
|
+
.trim();
|
|
51
|
+
}
|
|
52
|
+
export function semanticFingerprint(content) {
|
|
53
|
+
return semanticBody(content).replace(/\s+/g, " ");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function section(c, h) {
|
|
57
|
+
const e = h.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
58
|
+
return (
|
|
59
|
+
c.match(
|
|
60
|
+
new RegExp(`(?:^|\\n)## ${e}\\s*\\n([\\s\\S]*?)(?:\\n## |\\n# |$)`),
|
|
61
|
+
)?.[1] ?? ""
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function rows(s) {
|
|
65
|
+
return s
|
|
66
|
+
.split(/\r?\n/)
|
|
67
|
+
.filter((l) => l.trim().startsWith("|"))
|
|
68
|
+
.map((l) =>
|
|
69
|
+
l
|
|
70
|
+
.split("|")
|
|
71
|
+
.slice(1, -1)
|
|
72
|
+
.map((x) => x.trim()),
|
|
73
|
+
)
|
|
74
|
+
.filter((r) => !r.every((x) => /^[-:]+$/.test(x)));
|
|
75
|
+
}
|
|
76
|
+
function clean(v = "") {
|
|
77
|
+
return v.replace(/^`|`$/g, "").trim();
|
|
78
|
+
}
|
|
79
|
+
export function parseArtefactPathCell(value = "") {
|
|
80
|
+
const raw = String(value).trim();
|
|
81
|
+
if (!raw) return { raw, path: "", format: "plain", reason: "empty" };
|
|
82
|
+
const ticks = [...raw].filter((character) => character === "`").length;
|
|
83
|
+
if (ticks === 0) return { raw, path: raw, format: "plain", reason: "none" };
|
|
84
|
+
if (ticks === 2 && raw.startsWith("`") && raw.endsWith("`")) {
|
|
85
|
+
const path = raw.slice(1, -1).trim();
|
|
86
|
+
if (path && !path.includes("`")) return { raw, path, format: "code_span", reason: "none" };
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
raw,
|
|
90
|
+
path: "",
|
|
91
|
+
format: "invalid",
|
|
92
|
+
reason: ticks === 1 || raw.startsWith("`") !== raw.endsWith("`")
|
|
93
|
+
? "unmatched_delimiter"
|
|
94
|
+
: "embedded_delimiter",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function meaningful(v = "") {
|
|
98
|
+
return Boolean(v && !(v.startsWith("`") && v.includes("|")));
|
|
99
|
+
}
|
|
100
|
+
function field(c, k) {
|
|
101
|
+
return (
|
|
102
|
+
c.match(new RegExp(`^- ${k}:[^\\S\\r\\n]*(.*)$`, "m"))?.[1]?.trim() ?? ""
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
function sectionField(c, h, k) {
|
|
106
|
+
return field(section(c, h), k);
|
|
107
|
+
}
|
|
108
|
+
function sectionScalar(c, h, k) {
|
|
109
|
+
const body = section(c, h);
|
|
110
|
+
const escaped = k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
111
|
+
const matches = [...body.matchAll(new RegExp(`^- ${escaped}:[^\\S\\r\\n]*(.*)$`, "gm"))];
|
|
112
|
+
return {
|
|
113
|
+
value: clean(matches[0]?.[1] ?? ""),
|
|
114
|
+
count: matches.length,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function dataRows(c, h, header) {
|
|
118
|
+
return rows(section(c, h)).filter(
|
|
119
|
+
(r) =>
|
|
120
|
+
r[0] !== header &&
|
|
121
|
+
(h === "Evidence" ? r.slice(0, 3).some(meaningful) : r.some(meaningful)),
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
export function parseControlState(
|
|
125
|
+
content,
|
|
126
|
+
{ userGates = [], internalSteps = [], closeoutArtefacts = [] } = {},
|
|
127
|
+
) {
|
|
128
|
+
const metaValues = scalarFields(content).values;
|
|
129
|
+
const approvals = new Map();
|
|
130
|
+
for (const h of ["Approvals", "Gate Checklist"])
|
|
131
|
+
for (const [g, s, e] of rows(section(content, h))) {
|
|
132
|
+
if (userGates.includes(g)) {
|
|
133
|
+
const status = clean(s);
|
|
134
|
+
approvals.set(g, {
|
|
135
|
+
status: g === "QA" && (status === "pass" || status === "passed") ? "approved" : status,
|
|
136
|
+
evidence: e ?? "",
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const artefacts = new Map();
|
|
141
|
+
for (const [t, p, s, n] of rows(section(content, "Artefacts"))) {
|
|
142
|
+
if (userGates.includes(t) || internalSteps.includes(t) || closeoutArtefacts.includes(t)) {
|
|
143
|
+
const parsedPath = parseArtefactPathCell(p ?? "");
|
|
144
|
+
artefacts.set(t, {
|
|
145
|
+
path: parsedPath.path,
|
|
146
|
+
status: clean(s),
|
|
147
|
+
notes: n ?? "",
|
|
148
|
+
raw_path: parsedPath.raw,
|
|
149
|
+
path_format: parsedPath.format,
|
|
150
|
+
path_reason: parsedPath.reason,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const mapRows = (h, header, fn) => dataRows(content, h, header).map(fn);
|
|
155
|
+
const mode = section(content, "Mode/Slice Decision").trim()
|
|
156
|
+
? "Mode/Slice Decision"
|
|
157
|
+
: "Mode / Slice Decision",
|
|
158
|
+
source = "Source And Scope State",
|
|
159
|
+
memory = "Knowledge Persistence Decision",
|
|
160
|
+
handoff = "Parent Reconciliation Handoff",
|
|
161
|
+
aggregation = "Programme Aggregation Readiness";
|
|
162
|
+
const disposition = sectionScalar(content, handoff, "parent_reconciliation_disposition");
|
|
163
|
+
const reconciliationNextAction = sectionScalar(content, handoff, "parent_reconciliation_next_action");
|
|
164
|
+
const acceptanceRef = sectionScalar(content, aggregation, "programme_acceptance_ref");
|
|
165
|
+
const aggregationEvidence = sectionScalar(content, aggregation, "programme_aggregation_evidence");
|
|
166
|
+
const aggregationMissingEvidence = sectionScalar(content, aggregation, "programme_aggregation_missing_evidence");
|
|
167
|
+
return {
|
|
168
|
+
run_id: clean(metaValues.get("run_id") ?? ""),
|
|
169
|
+
current_gate: field(content, "current_gate"),
|
|
170
|
+
next_allowed_action: field(content, "next_allowed_action"),
|
|
171
|
+
approvals,
|
|
172
|
+
artefacts,
|
|
173
|
+
evidence_refs: mapRows("Evidence", "Evidence", (r) => ({
|
|
174
|
+
evidence: r[0] ?? "",
|
|
175
|
+
source: r[1] ?? "",
|
|
176
|
+
covers: r[2] ?? "",
|
|
177
|
+
strength: clean(r[3]),
|
|
178
|
+
})),
|
|
179
|
+
artefact_chain: mapRows("Artefact Chain", "From", (r) => ({
|
|
180
|
+
from: r[0] ?? "",
|
|
181
|
+
relationship: clean(r[1]),
|
|
182
|
+
to: clean(r[2]),
|
|
183
|
+
evidence: r[3] ?? "",
|
|
184
|
+
})),
|
|
185
|
+
missing_evidence: mapRows("Missing Evidence", "Missing evidence", (r) => ({
|
|
186
|
+
missing_evidence: r[0] ?? "",
|
|
187
|
+
impact: clean(r[1]),
|
|
188
|
+
required_next_step: r[2] ?? "",
|
|
189
|
+
})),
|
|
190
|
+
risks: mapRows("Risks", "Risk", (r) => ({
|
|
191
|
+
risk: r[0] ?? "",
|
|
192
|
+
impact: clean(r[1]),
|
|
193
|
+
mitigation_or_owner: r[2] ?? "",
|
|
194
|
+
})),
|
|
195
|
+
context_graph: {
|
|
196
|
+
impact: clean(field(content, "context_graph_impact")),
|
|
197
|
+
refs: field(content, "context_graph_refs"),
|
|
198
|
+
required_action: clean(field(content, "context_graph_required_action")),
|
|
199
|
+
gate_effect: clean(field(content, "context_graph_gate_effect")),
|
|
200
|
+
evidence: field(content, "context_graph_evidence"),
|
|
201
|
+
},
|
|
202
|
+
quality_outlook: field(content, "quality_outlook"),
|
|
203
|
+
mode_slice_decision: {
|
|
204
|
+
decision: clean(sectionField(content, mode, "decision")),
|
|
205
|
+
required_next_gate: clean(
|
|
206
|
+
sectionField(content, mode, "required_next_gate"),
|
|
207
|
+
),
|
|
208
|
+
scope_reason: sectionField(content, mode, "scope_reason"),
|
|
209
|
+
evidence: sectionField(content, mode, "evidence"),
|
|
210
|
+
},
|
|
211
|
+
source_scope: {
|
|
212
|
+
normative_instruction_source: sectionField(
|
|
213
|
+
content,
|
|
214
|
+
source,
|
|
215
|
+
"normative_instruction_source",
|
|
216
|
+
),
|
|
217
|
+
multi_scope_state: clean(
|
|
218
|
+
sectionField(content, source, "multi_scope_state"),
|
|
219
|
+
),
|
|
220
|
+
active_scope_evidence: sectionField(
|
|
221
|
+
content,
|
|
222
|
+
source,
|
|
223
|
+
"active_scope_evidence",
|
|
224
|
+
),
|
|
225
|
+
competing_scope_lines: sectionField(
|
|
226
|
+
content,
|
|
227
|
+
source,
|
|
228
|
+
"competing_scope_lines",
|
|
229
|
+
),
|
|
230
|
+
branch_workspace_evidence: sectionField(
|
|
231
|
+
content,
|
|
232
|
+
source,
|
|
233
|
+
"branch_workspace_evidence",
|
|
234
|
+
),
|
|
235
|
+
branch_workspace_scope_effect: clean(
|
|
236
|
+
sectionField(content, source, "branch_workspace_scope_effect"),
|
|
237
|
+
),
|
|
238
|
+
},
|
|
239
|
+
memory: {
|
|
240
|
+
target: clean(sectionField(content, memory, "memory_target")),
|
|
241
|
+
reason: sectionField(content, memory, "memory_reason"),
|
|
242
|
+
refs: sectionField(content, memory, "memory_refs"),
|
|
243
|
+
},
|
|
244
|
+
parent_reconciliation: {
|
|
245
|
+
present: Boolean(section(content, handoff).trim()),
|
|
246
|
+
disposition: disposition.value,
|
|
247
|
+
next_action: reconciliationNextAction.value,
|
|
248
|
+
field_counts: {
|
|
249
|
+
disposition: disposition.count,
|
|
250
|
+
next_action: reconciliationNextAction.count,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
programme_aggregation: {
|
|
254
|
+
present: Boolean(section(content, aggregation).trim()),
|
|
255
|
+
acceptance_ref: acceptanceRef.value,
|
|
256
|
+
evidence: aggregationEvidence.value,
|
|
257
|
+
missing_evidence: aggregationMissingEvidence.value,
|
|
258
|
+
field_counts: {
|
|
259
|
+
acceptance_ref: acceptanceRef.count,
|
|
260
|
+
evidence: aggregationEvidence.count,
|
|
261
|
+
missing_evidence: aggregationMissingEvidence.count,
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
}
|
package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-repository.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
lstatSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
11
|
+
import { parseRunState, RUN_ID_PATTERN } from "./run-state-parser.js";
|
|
12
|
+
export function runPath(root, id) {
|
|
13
|
+
if (!RUN_ID_PATTERN.test(id)) throw Error(`invalid run_id: ${id}`);
|
|
14
|
+
return join(root, ".agdf", "control", "runs", id, "RUN_STATE.md");
|
|
15
|
+
}
|
|
16
|
+
export function discoverRuns(root) {
|
|
17
|
+
const dir = join(root, ".agdf", "control", "runs");
|
|
18
|
+
if (!existsSync(dir)) return [];
|
|
19
|
+
const rootStats = lstatSync(dir);
|
|
20
|
+
if (rootStats.isSymbolicLink() || !rootStats.isDirectory()) {
|
|
21
|
+
return [invalidRun("<repository>", dir)];
|
|
22
|
+
}
|
|
23
|
+
return readdirSync(dir)
|
|
24
|
+
.filter((x) => !x.startsWith("."))
|
|
25
|
+
.sort()
|
|
26
|
+
.map((id) => {
|
|
27
|
+
const d = join(dir, id),
|
|
28
|
+
path = join(d, "RUN_STATE.md");
|
|
29
|
+
const directoryStats = lstatSync(d);
|
|
30
|
+
if (
|
|
31
|
+
!RUN_ID_PATTERN.test(id) ||
|
|
32
|
+
directoryStats.isSymbolicLink() ||
|
|
33
|
+
!directoryStats.isDirectory() ||
|
|
34
|
+
!existsSync(path)
|
|
35
|
+
)
|
|
36
|
+
return invalidRun(id, path);
|
|
37
|
+
|
|
38
|
+
const stateStats = lstatSync(path);
|
|
39
|
+
if (stateStats.isSymbolicLink() || !stateStats.isFile()) {
|
|
40
|
+
return invalidRun(id, path);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
run_id: id,
|
|
44
|
+
path,
|
|
45
|
+
...parseRunState(readFileSync(path, "utf8"), id),
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function invalidRun(runId, path) {
|
|
51
|
+
return {
|
|
52
|
+
run_id: runId,
|
|
53
|
+
path,
|
|
54
|
+
valid: false,
|
|
55
|
+
findings: [{ code: "AGDF_RUN_PATH_INVALID", path }],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const DEFAULT_BODY = `## Objective\n\nDescribe the trustworthy outcome.\n\n## Current Control State\n\n| Question | Answer |\n|---|---|\n| What is known? | New run created. |\n| What is approved? | Nothing yet. |\n| What is missing? | Durable UR and exact approval. |\n| What is the next allowed action? | Draft the UR. |\n| What is explicitly forbidden right now? | Later artefacts and implementation. |\n\n## Evidence\n\n| Evidence | Source | Covers | Strength |\n|---|---|---|---|\n| Run creation | run-create | Control initialization | direct |\n\n## Closeout\n\n- next_allowed_action: Draft the UR.\n- quality_outlook: Establish evidence before later gates.\n`;
|
|
59
|
+
export function renderRunState(id, body = DEFAULT_BODY, meta = {}) {
|
|
60
|
+
return `# AGDF Run State\n\n## Run Meta\n\n- control_state_version: 2\n- run_id: ${id}\n- lifecycle: ${meta.lifecycle ?? "active"}\n- revision: 1\n- revision_id: ${randomUUID()}\n- mode: ${meta.mode ?? "structured_delivery"}\n- current_gate: ${meta.current_gate ?? "UR"}\n- decision: ${meta.decision ?? "in_progress"}\n- owner: ${meta.owner ?? "agent"}\n\n${body}`;
|
|
61
|
+
}
|
|
62
|
+
export function createRun(root, id, body = "") {
|
|
63
|
+
const path = runPath(root, id);
|
|
64
|
+
if (existsSync(path)) throw Error("AGDF_RUN_COLLISION");
|
|
65
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
66
|
+
writeFileSync(path, renderRunState(id, body), { flag: "wx" });
|
|
67
|
+
return path;
|
|
68
|
+
}
|