create-agdf 0.6.8 → 0.7.0
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 +5 -5
- package/bin/create-agdf.js +285 -58
- package/generated/.agdf/control/templates/RUN_STATE.md +21 -0
- package/generated/.github/skills/agdf-runtime-contract.md +5 -0
- package/generated/.opencode/AGDF.md +4 -4
- package/generated/.opencode/README.md +4 -4
- package/generated/.opencode/agdf-runtime-contract.md +5 -0
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/control/templates/RUN_STATE.md +21 -0
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +2 -1
- package/generated/plugins/agdf/meta/agdf-runtime-contract.md +5 -0
- package/lib/control-state/run-state-parser.js +7 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,8 +51,8 @@ If no language is provided, `create-agdf` derives the preference from the local
|
|
|
51
51
|
- `codex-repo` writes a repository-local Codex marketplace under `.agents/plugins/` and a local AGDF plugin copy under `plugins/agdf/`
|
|
52
52
|
- `claude` installs the AGDF plugin globally for Claude Code
|
|
53
53
|
- `copilot` writes `AGENTS.md`, Copilot custom instructions under `.github/`, visible repository skills under `.github/skills/`, and AGDF control templates under `.agdf/control/`
|
|
54
|
-
- `opencode` installs the AGDF npm plugin as a user-wide OpenCode
|
|
55
|
-
- `opencode-status` reports OpenCode global config, package loadability, session signals and repository surface presence
|
|
54
|
+
- `opencode` installs the AGDF npm plugin and nine native skills as a user-wide OpenCode surface
|
|
55
|
+
- `opencode-status` reports OpenCode global config, package loadability, global native-skill completeness, session signals and repository surface presence
|
|
56
56
|
- `opencode-repo` writes `opencode.json`, `.opencode/AGDF.md`, prefixed native OpenCode skills under `.opencode/skills/`, explicit edit/bash/skill permissions, and AGDF control templates under `.agdf/control/`
|
|
57
57
|
- `both` writes the Codex repository-local marketplace plus the Copilot-facing repository files
|
|
58
58
|
- `config` writes or updates only `.agdf/control/config.json` for an already installed plugin or an existing repository
|
|
@@ -75,7 +75,7 @@ Then verify the visible installation state:
|
|
|
75
75
|
npx --yes @agdf/cli@latest opencode-status --json
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
The status command reports global configuration,
|
|
78
|
+
The status command reports global configuration, package loadability, global native-skill completeness, session signals and whether the current repository has `.opencode/AGDF.md` plus the `agdf-gate-check` surface. It does not infer an active OpenCode session from config alone.
|
|
79
79
|
In JSON schema version 1, `repository_surface.gate_check_agent` remains a deprecated compatibility alias for the native `gate_check_skill` path so existing status consumers keep working during the agent-to-skill migration.
|
|
80
80
|
|
|
81
81
|
Use the `opencode-repo` target when AGDF should be available to OpenCode from repository files:
|
|
@@ -84,10 +84,10 @@ Use the `opencode-repo` target when AGDF should be available to OpenCode from re
|
|
|
84
84
|
npm create agdf@latest -- opencode-repo
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
OpenCode loads the AGDF npm plugin from global OpenCode config
|
|
87
|
+
OpenCode loads the AGDF npm plugin and global native skills from global OpenCode config, plus repository instructions from `.opencode/AGDF.md`, explicit `edit`/`bash`/`skill` permissions and canonical `agdf-` project skills under `.opencode/skills/`. Global adapters use the collision-safe `agdf-global-` prefix. AGDF for OpenCode has a global discoverability layer installed with `npx --yes @agdf/cli@latest opencode` and a repository-local governance surface generated by `opencode-repo`.
|
|
88
88
|
Those generated skills load on demand through OpenCode's native `skill` tool. Use `agdf-gate-check` first for new build/change intent or unclear approval.
|
|
89
89
|
If `opencode.json` already exists, AGDF keeps it unchanged and writes `opencode.agdf.json` as a merge fragment.
|
|
90
|
-
|
|
90
|
+
The global layer makes the nine native AGDF skills discoverable, but does not activate repository governance by itself. Global skills fail closed until the current repository has `.opencode/AGDF.md`, `.opencode/skills/agdf-gate-check/SKILL.md` and `.agdf/control/`. Use `opencode-repo` to write that repository surface.
|
|
91
91
|
|
|
92
92
|
## Control scaffold
|
|
93
93
|
|
package/bin/create-agdf.js
CHANGED
|
@@ -32,14 +32,23 @@ const generatedRoot = join(packageRoot, "generated");
|
|
|
32
32
|
const pluginDefinitionPath = join(generatedRoot, "plugins", "agdf", "meta", "agdf-plugin.definition.json");
|
|
33
33
|
const pluginDefinition = JSON.parse(readFileSync(pluginDefinitionPath, "utf8"));
|
|
34
34
|
const pluginInstallCommand = "npx --yes @agdf/cli@latest claude";
|
|
35
|
-
const
|
|
36
|
-
const
|
|
35
|
+
const testNpmCliPath = process.env.NODE_ENV === "test" ? process.env.AGDF_TEST_NPM_CLI_PATH || "" : "";
|
|
36
|
+
const npmCommand = testNpmCliPath
|
|
37
|
+
? process.execPath
|
|
38
|
+
: process.platform === "win32"
|
|
39
|
+
? process.execPath
|
|
40
|
+
: "npm";
|
|
41
|
+
const npmPrefixArgs = testNpmCliPath
|
|
42
|
+
? [testNpmCliPath]
|
|
43
|
+
: process.platform === "win32"
|
|
44
|
+
? [join(dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js")]
|
|
45
|
+
: [];
|
|
37
46
|
const allowedTargets = new Set(["codex", "codex-repo", "claude", "copilot", "opencode", "opencode-status", "opencode-repo", "both", "init", "config", "doctor", "gate-check", "delivery-map", "delivery-path-search", "run-create", "run-migrate", "run-render-legacy"]);
|
|
38
47
|
const agdfFragmentPath = "AGENTS.agdf.md";
|
|
39
48
|
const openCodeConfigFragmentPath = "opencode.agdf.json";
|
|
40
49
|
const userGateOrder = ["UR", "PRD", "SD", "TP", "QA", "UAT"];
|
|
41
50
|
const durableGateArtefacts = new Set(["UR", "PRD", "SD", "TP", "QA"]);
|
|
42
|
-
const internalStepArtefacts = new Set(["Brownfield Review"]);
|
|
51
|
+
const internalStepArtefacts = new Set(["Brownfield Review", "Brownfield Analysis", "CD+Tests", "CR"]);
|
|
43
52
|
const deliveryRelationships = [
|
|
44
53
|
{ from: "UR", relationship: "approved_by", to: "Approval: UR", requiredBy: "UR" },
|
|
45
54
|
{ from: "PRD", relationship: "derived_from", to: "UR", requiredBy: "PRD" },
|
|
@@ -62,24 +71,13 @@ const nextSkillByGate = {
|
|
|
62
71
|
UAT: "delivery-closeout",
|
|
63
72
|
OR: "release-or",
|
|
64
73
|
};
|
|
65
|
-
const gateProgressOrder = [
|
|
66
|
-
"UR",
|
|
67
|
-
"Brownfield Review",
|
|
68
|
-
"Mode/Slice Decision",
|
|
69
|
-
"Quick Task Execution",
|
|
70
|
-
"PRD",
|
|
71
|
-
"SD",
|
|
72
|
-
"TP",
|
|
73
|
-
"Brownfield Analysis",
|
|
74
|
-
"CD+Tests",
|
|
75
|
-
"CR",
|
|
76
|
-
"QA",
|
|
77
|
-
"UAT",
|
|
78
|
-
"OR",
|
|
79
|
-
];
|
|
80
74
|
const codexSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.codex.skillPrefix}${skill.slug}`);
|
|
81
75
|
const copilotSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.copilot.skillPrefix}${skill.slug}`);
|
|
82
76
|
const openCodeSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.opencode.skillPrefix}${skill.slug}`);
|
|
77
|
+
const globalOpenCodeSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.opencode.globalSkillPrefix}${skill.slug}`);
|
|
78
|
+
const globalOpenCodeSkillOwnershipMarker = "<!-- AGDF-GLOBAL-SKILL: ";
|
|
79
|
+
const globalOpenCodeInstructionsOwnershipMarker = "<!-- AGDF-GLOBAL-INSTRUCTIONS -->";
|
|
80
|
+
const globalOpenCodeRuntimeContractOwnershipMarker = "<!-- AGDF-GLOBAL-RUNTIME-CONTRACT -->";
|
|
83
81
|
const codexPluginFiles = [
|
|
84
82
|
join(".agents", "plugins", "marketplace.json"),
|
|
85
83
|
join("plugins", "agdf", ".codex-plugin", "plugin.json"),
|
|
@@ -476,6 +474,8 @@ function defaultOpenCodeConfigDir() {
|
|
|
476
474
|
}
|
|
477
475
|
|
|
478
476
|
function installOpenCodeGlobalPlugin(configDir) {
|
|
477
|
+
assertGlobalOpenCodeSurfaceWritable(configDir);
|
|
478
|
+
const previousPackage = resolveOpenCodePackage(configDir);
|
|
479
479
|
const configPath = join(configDir, "opencode.json");
|
|
480
480
|
let config = {};
|
|
481
481
|
|
|
@@ -503,17 +503,145 @@ function installOpenCodeGlobalPlugin(configDir) {
|
|
|
503
503
|
plugin: plugins,
|
|
504
504
|
};
|
|
505
505
|
|
|
506
|
+
if (nextConfig.instructions !== undefined && !Array.isArray(nextConfig.instructions)) {
|
|
507
|
+
throw new Error(`Refusing to update OpenCode config with non-array instructions field: ${configPath}`);
|
|
508
|
+
}
|
|
509
|
+
nextConfig.instructions = [...(nextConfig.instructions ?? [])];
|
|
510
|
+
if (!nextConfig.instructions.includes("AGDF.md")) nextConfig.instructions.push("AGDF.md");
|
|
511
|
+
|
|
512
|
+
if (nextConfig.permission === undefined) {
|
|
513
|
+
nextConfig.permission = { skill: { "agdf-*": "allow" } };
|
|
514
|
+
} else if (nextConfig.permission && typeof nextConfig.permission === "object" && !Array.isArray(nextConfig.permission)) {
|
|
515
|
+
nextConfig.permission = { ...nextConfig.permission };
|
|
516
|
+
if (nextConfig.permission.skill === undefined) {
|
|
517
|
+
nextConfig.permission.skill = { "agdf-*": "allow" };
|
|
518
|
+
} else if (nextConfig.permission.skill && typeof nextConfig.permission.skill === "object" && !Array.isArray(nextConfig.permission.skill)) {
|
|
519
|
+
nextConfig.permission.skill = { ...nextConfig.permission.skill };
|
|
520
|
+
if (nextConfig.permission.skill["agdf-*"] === undefined) nextConfig.permission.skill["agdf-*"] = "allow";
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
506
524
|
mkdirSync(configDir, { recursive: true });
|
|
507
525
|
try {
|
|
508
|
-
|
|
526
|
+
const packageSpecifier = `${pluginDefinition.opencode.npmPackage}@${pluginDefinition.version}`;
|
|
527
|
+
execFileSync(npmCommand, [...npmPrefixArgs, "install", "--silent", "--save-prod", "--save-exact", packageSpecifier], {
|
|
528
|
+
cwd: configDir,
|
|
529
|
+
stdio: "pipe",
|
|
530
|
+
});
|
|
509
531
|
} catch (error) {
|
|
510
532
|
throw new Error(`Failed to install ${pluginDefinition.opencode.npmPackage} into the OpenCode config directory: ${(error.stderr || error.message).toString().trim()}`);
|
|
511
533
|
}
|
|
512
534
|
writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
|
|
535
|
+
const installedPackage = resolveOpenCodePackage(configDir);
|
|
513
536
|
|
|
514
537
|
return {
|
|
515
538
|
configPath,
|
|
516
539
|
added: !alreadyInstalled,
|
|
540
|
+
transition: openCodePackageTransition(previousPackage, installedPackage),
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function globalOpenCodeConfigPaths(configDir) {
|
|
545
|
+
return {
|
|
546
|
+
instructions: join(configDir, pluginDefinition.opencode.instructionsFileName),
|
|
547
|
+
runtimeContract: join(configDir, pluginDefinition.opencode.runtimeContractFileName),
|
|
548
|
+
skills: join(configDir, "skills"),
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function globalOpenCodeOwnershipMarkerIsValid(content, marker, placement) {
|
|
553
|
+
const lines = content.split(/\r?\n/);
|
|
554
|
+
if (placement === "first-line") return lines[0] === marker;
|
|
555
|
+
const frontmatterEnd = lines.findIndex((line, index) => index > 0 && line === "---");
|
|
556
|
+
return frontmatterEnd >= 0 && lines[frontmatterEnd + 1] === marker;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function assertGlobalOpenCodeFileWritable(path, marker, placement) {
|
|
560
|
+
if (!existsSync(path)) return;
|
|
561
|
+
const existing = readFileSync(path, "utf8");
|
|
562
|
+
if (!globalOpenCodeOwnershipMarkerIsValid(existing, marker, placement)) {
|
|
563
|
+
throw new Error(`Refusing to overwrite unowned global OpenCode file: ${path}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function assertGlobalOpenCodeSurfaceWritable(configDir) {
|
|
568
|
+
const paths = globalOpenCodeConfigPaths(configDir);
|
|
569
|
+
assertGlobalOpenCodeFileWritable(paths.instructions, globalOpenCodeInstructionsOwnershipMarker, "first-line");
|
|
570
|
+
assertGlobalOpenCodeFileWritable(paths.runtimeContract, globalOpenCodeRuntimeContractOwnershipMarker, "first-line");
|
|
571
|
+
for (const skillName of globalOpenCodeSkillNames) {
|
|
572
|
+
assertGlobalOpenCodeFileWritable(
|
|
573
|
+
join(paths.skills, skillName, "SKILL.md"),
|
|
574
|
+
`${globalOpenCodeSkillOwnershipMarker}${skillName} -->`,
|
|
575
|
+
"after-frontmatter",
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function writeOwnedGlobalOpenCodeFile(path, content, marker, placement) {
|
|
581
|
+
if (existsSync(path)) {
|
|
582
|
+
assertGlobalOpenCodeFileWritable(path, marker, placement);
|
|
583
|
+
}
|
|
584
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
585
|
+
writeFileSync(path, content, "utf8");
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function globalOpenCodeBoundary() {
|
|
589
|
+
return [
|
|
590
|
+
"## Global OpenCode Surface Boundary",
|
|
591
|
+
"",
|
|
592
|
+
"This skill is globally discoverable, but global plugin or skill presence is not repository governance activation.",
|
|
593
|
+
"Before applying AGDF gates, later artefacts or implementation guidance, inspect the current repository for `.opencode/AGDF.md`, `.opencode/skills/agdf-gate-check/SKILL.md` and `.agdf/control/`.",
|
|
594
|
+
"If the repository surface is absent, stop and direct the user to `npx --yes @agdf/cli@latest opencode-repo` in this repository.",
|
|
595
|
+
"When the repository surface exists, its local instructions, native skills and `.agdf/control/` state are authoritative.",
|
|
596
|
+
"",
|
|
597
|
+
].join("\n");
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function toGlobalOpenCodeContent(content) {
|
|
601
|
+
let next = content;
|
|
602
|
+
for (const skill of pluginDefinition.skillSet) {
|
|
603
|
+
const localName = `${pluginDefinition.opencode.skillPrefix}${skill.slug}`;
|
|
604
|
+
const globalName = `${pluginDefinition.opencode.globalSkillPrefix}${skill.slug}`;
|
|
605
|
+
next = next.replaceAll(localName, globalName);
|
|
606
|
+
}
|
|
607
|
+
return next;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function installOpenCodeGlobalSurface(configDir) {
|
|
611
|
+
const paths = globalOpenCodeConfigPaths(configDir);
|
|
612
|
+
const generatedOpenCodeRoot = join(generatedRoot, ".opencode");
|
|
613
|
+
const generatedInstructions = readFileSync(join(generatedOpenCodeRoot, pluginDefinition.opencode.instructionsFileName), "utf8");
|
|
614
|
+
const generatedRuntimeContract = readFileSync(join(generatedOpenCodeRoot, pluginDefinition.opencode.runtimeContractFileName), "utf8");
|
|
615
|
+
const globalInstructions = [
|
|
616
|
+
globalOpenCodeInstructionsOwnershipMarker,
|
|
617
|
+
"# AGDF Global OpenCode instructions",
|
|
618
|
+
"",
|
|
619
|
+
"AGDF native skills are globally discoverable through OpenCode.",
|
|
620
|
+
"",
|
|
621
|
+
globalOpenCodeBoundary(),
|
|
622
|
+
"The repository-local `.opencode/AGDF.md`, `.opencode/skills/` and `.agdf/control/` files remain the source of truth for active governance.",
|
|
623
|
+
"",
|
|
624
|
+
toGlobalOpenCodeContent(generatedInstructions),
|
|
625
|
+
].join("\n");
|
|
626
|
+
const globalRuntimeContract = `${globalOpenCodeRuntimeContractOwnershipMarker}\n${generatedRuntimeContract}`;
|
|
627
|
+
|
|
628
|
+
writeOwnedGlobalOpenCodeFile(paths.instructions, globalInstructions, globalOpenCodeInstructionsOwnershipMarker, "first-line");
|
|
629
|
+
writeOwnedGlobalOpenCodeFile(paths.runtimeContract, globalRuntimeContract, globalOpenCodeRuntimeContractOwnershipMarker, "first-line");
|
|
630
|
+
|
|
631
|
+
for (let index = 0; index < openCodeSkillNames.length; index += 1) {
|
|
632
|
+
const sourceName = openCodeSkillNames[index];
|
|
633
|
+
const skillName = globalOpenCodeSkillNames[index];
|
|
634
|
+
const sourcePath = join(generatedOpenCodeRoot, "skills", sourceName, "SKILL.md");
|
|
635
|
+
const sourceContent = readFileSync(sourcePath, "utf8");
|
|
636
|
+
const marker = `${globalOpenCodeSkillOwnershipMarker}${skillName} -->`;
|
|
637
|
+
const content = toGlobalOpenCodeContent(sourceContent).replace(/^(---[\s\S]*?\n---\n)/, `$1${marker}\n\n${globalOpenCodeBoundary()}`);
|
|
638
|
+
writeOwnedGlobalOpenCodeFile(join(paths.skills, skillName, "SKILL.md"), content, marker, "after-frontmatter");
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return {
|
|
642
|
+
instructions: paths.instructions,
|
|
643
|
+
runtimeContract: paths.runtimeContract,
|
|
644
|
+
skills: paths.skills,
|
|
517
645
|
};
|
|
518
646
|
}
|
|
519
647
|
|
|
@@ -534,30 +662,86 @@ function isOpenCodeRepositorySurfacePresent(targetDir) {
|
|
|
534
662
|
function resolveOpenCodePackage(configDir) {
|
|
535
663
|
const packageName = pluginDefinition.opencode.npmPackage;
|
|
536
664
|
try {
|
|
665
|
+
const resolvedPath = execFileSync(process.execPath, ["-e", `process.stdout.write(require.resolve(${JSON.stringify(packageName)}))`], {
|
|
666
|
+
cwd: configDir,
|
|
667
|
+
encoding: "utf8",
|
|
668
|
+
stdio: "pipe",
|
|
669
|
+
});
|
|
670
|
+
let installedVersion = "";
|
|
671
|
+
try {
|
|
672
|
+
const packageManifest = JSON.parse(readFileSync(join(dirname(resolvedPath), "package.json"), "utf8"));
|
|
673
|
+
installedVersion = typeof packageManifest.version === "string" && packageManifest.version.trim() ? packageManifest.version.trim() : "";
|
|
674
|
+
} catch {
|
|
675
|
+
installedVersion = "";
|
|
676
|
+
}
|
|
537
677
|
return {
|
|
538
678
|
loadable: true,
|
|
539
|
-
path:
|
|
540
|
-
|
|
541
|
-
encoding: "utf8",
|
|
542
|
-
stdio: "pipe",
|
|
543
|
-
}),
|
|
679
|
+
path: resolvedPath,
|
|
680
|
+
installed_version: installedVersion,
|
|
544
681
|
error: "",
|
|
545
682
|
};
|
|
546
683
|
} catch (error) {
|
|
547
684
|
return {
|
|
548
685
|
loadable: false,
|
|
549
686
|
path: "",
|
|
687
|
+
installed_version: "",
|
|
550
688
|
error: (error.stderr || error.message || "package not resolvable").toString().trim(),
|
|
551
689
|
};
|
|
552
690
|
}
|
|
553
691
|
}
|
|
554
692
|
|
|
555
|
-
function
|
|
693
|
+
function openCodePackageVersionStatus(packageState) {
|
|
694
|
+
if (!packageState.loadable) return "unloadable";
|
|
695
|
+
if (!packageState.installed_version) return "unknown";
|
|
696
|
+
return packageState.installed_version === pluginDefinition.version ? "current" : "outdated";
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function openCodePackageTransition(previousPackage, installedPackage) {
|
|
700
|
+
const previousVersion = previousPackage.loadable ? previousPackage.installed_version : "";
|
|
701
|
+
const installedVersion = installedPackage.loadable ? installedPackage.installed_version : "";
|
|
702
|
+
if (!installedVersion || (previousPackage.loadable && !previousVersion)) {
|
|
703
|
+
return {
|
|
704
|
+
previous_version: previousVersion,
|
|
705
|
+
installed_version: installedVersion,
|
|
706
|
+
status: "unknown",
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
if (!previousPackage.loadable) {
|
|
710
|
+
return {
|
|
711
|
+
previous_version: "",
|
|
712
|
+
installed_version: installedVersion,
|
|
713
|
+
status: "installed",
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
return {
|
|
717
|
+
previous_version: previousVersion,
|
|
718
|
+
installed_version: installedVersion,
|
|
719
|
+
status: previousVersion === installedVersion ? "unchanged" : "updated",
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function evaluateGlobalOpenCodeSurface(configDir) {
|
|
724
|
+
const paths = globalOpenCodeConfigPaths(configDir);
|
|
725
|
+
const skillCount = globalOpenCodeSkillNames.filter((skillName) => existsSync(join(paths.skills, skillName, "SKILL.md"))).length;
|
|
726
|
+
return {
|
|
727
|
+
path: paths.skills,
|
|
728
|
+
instructions: paths.instructions,
|
|
729
|
+
runtime_contract: paths.runtimeContract,
|
|
730
|
+
expected_skill_count: globalOpenCodeSkillNames.length,
|
|
731
|
+
skill_count: skillCount,
|
|
732
|
+
present: existsSync(paths.skills),
|
|
733
|
+
complete: existsSync(paths.instructions) && existsSync(paths.runtimeContract) && skillCount === openCodeSkillNames.length,
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function evaluateOpenCodeStatus(targetDir, configDir = defaultOpenCodeConfigDir(), transition = null) {
|
|
556
738
|
const configPath = join(configDir, "opencode.json");
|
|
557
739
|
const configState = readOpenCodeConfig(configPath);
|
|
558
740
|
const plugins = Array.isArray(configState.config.plugin) ? configState.config.plugin : [];
|
|
559
741
|
const globalConfigured = plugins.includes(pluginDefinition.opencode.npmPackage);
|
|
560
742
|
const packageState = resolveOpenCodePackage(configDir);
|
|
743
|
+
const packageVersionStatus = openCodePackageVersionStatus(packageState);
|
|
744
|
+
const globalNativeSurface = evaluateGlobalOpenCodeSurface(configDir);
|
|
561
745
|
const sessionSignals = {
|
|
562
746
|
active: process.env.AGDF_PLUGIN_ACTIVE === "1",
|
|
563
747
|
version: process.env.AGDF_PLUGIN_VERSION || "",
|
|
@@ -572,6 +756,9 @@ function evaluateOpenCodeStatus(targetDir, configDir = defaultOpenCodeConfigDir(
|
|
|
572
756
|
if (configState.parseError) findings.push(`OpenCode global config is not valid JSON: ${configState.parseError}`);
|
|
573
757
|
if (!globalConfigured) findings.push(`OpenCode global config does not include ${pluginDefinition.opencode.npmPackage}.`);
|
|
574
758
|
if (!packageState.loadable) findings.push(`${pluginDefinition.opencode.npmPackage} is not loadable from the OpenCode config directory.`);
|
|
759
|
+
if (packageVersionStatus === "outdated") findings.push(`${pluginDefinition.opencode.npmPackage} version ${packageState.installed_version} is outdated; expected ${pluginDefinition.version}.`);
|
|
760
|
+
if (packageVersionStatus === "unknown" && packageState.loadable) findings.push(`${pluginDefinition.opencode.npmPackage} is loadable but its installed version is unknown.`);
|
|
761
|
+
if (!globalNativeSurface.complete) findings.push(`Global OpenCode native skill surface is incomplete (${globalNativeSurface.skill_count}/${globalNativeSurface.expected_skill_count} skills).`);
|
|
575
762
|
if (!sessionSignals.active) findings.push("No active AGDF OpenCode session signal is visible in this process.");
|
|
576
763
|
if (!repositorySurface) findings.push("Current repository does not contain the AGDF OpenCode surface.");
|
|
577
764
|
|
|
@@ -588,8 +775,13 @@ function evaluateOpenCodeStatus(targetDir, configDir = defaultOpenCodeConfigDir(
|
|
|
588
775
|
name: pluginDefinition.opencode.npmPackage,
|
|
589
776
|
loadable: packageState.loadable,
|
|
590
777
|
resolved_path: packageState.path,
|
|
778
|
+
installed_version: packageState.installed_version || null,
|
|
779
|
+
expected_version: pluginDefinition.version,
|
|
780
|
+
version_status: packageVersionStatus,
|
|
781
|
+
...(transition ? { transition } : {}),
|
|
591
782
|
error: packageState.error,
|
|
592
783
|
},
|
|
784
|
+
global_native_surface: globalNativeSurface,
|
|
593
785
|
session: sessionSignals,
|
|
594
786
|
repository_surface: {
|
|
595
787
|
path: targetDir,
|
|
@@ -600,7 +792,11 @@ function evaluateOpenCodeStatus(targetDir, configDir = defaultOpenCodeConfigDir(
|
|
|
600
792
|
},
|
|
601
793
|
visible_entrypoint: repositorySurface ? `${pluginDefinition.opencode.skillPrefix}gate-check (native skill)` : "none until opencode-repo is installed for this repository",
|
|
602
794
|
findings,
|
|
603
|
-
next_step:
|
|
795
|
+
next_step: packageVersionStatus !== "current"
|
|
796
|
+
? "Run npx --yes @agdf/cli@latest opencode to install or repair the OpenCode package version."
|
|
797
|
+
: !globalNativeSurface.complete
|
|
798
|
+
? "Run npx --yes @agdf/cli@latest opencode to install or repair the global native OpenCode skill surface."
|
|
799
|
+
: repositorySurface
|
|
604
800
|
? `Restart OpenCode if needed, then load ${pluginDefinition.opencode.skillPrefix}gate-check through the native skill tool for new build/change intent.`
|
|
605
801
|
: "Run npx --yes @agdf/cli@latest opencode-repo in repositories where AGDF governance should be active and reviewable.",
|
|
606
802
|
};
|
|
@@ -618,6 +814,20 @@ function printOpenCodeStatus(report, json) {
|
|
|
618
814
|
if (report.global_config.parse_error) console.log(`Config parse error: ${report.global_config.parse_error}`);
|
|
619
815
|
console.log(`Package loadable: ${report.package.loadable ? "yes" : "no"}`);
|
|
620
816
|
if (report.package.resolved_path) console.log(`Package path: ${report.package.resolved_path}`);
|
|
817
|
+
console.log(`Package version: ${report.package.installed_version || "unknown"}`);
|
|
818
|
+
console.log(`Expected version: ${report.package.expected_version}`);
|
|
819
|
+
console.log(`Version status: ${report.package.version_status}`);
|
|
820
|
+
if (report.package.transition?.status === "updated") {
|
|
821
|
+
console.log(`Version transition: ${report.package.transition.previous_version} -> ${report.package.transition.installed_version}`);
|
|
822
|
+
} else if (report.package.transition?.status === "installed") {
|
|
823
|
+
console.log(`Version transition: new install (${report.package.transition.installed_version || "unknown"})`);
|
|
824
|
+
} else if (report.package.transition?.status === "unchanged") {
|
|
825
|
+
console.log(`Version transition: unchanged (${report.package.transition.installed_version})`);
|
|
826
|
+
} else if (report.package.transition?.status === "unknown") {
|
|
827
|
+
console.log("Version transition: unknown");
|
|
828
|
+
}
|
|
829
|
+
console.log(`Global native skills: ${report.global_native_surface.complete ? "complete" : "incomplete"} (${report.global_native_surface.skill_count}/${report.global_native_surface.expected_skill_count})`);
|
|
830
|
+
console.log(`Global skill path: ${report.global_native_surface.path}`);
|
|
621
831
|
console.log(`Session active signal: ${report.session.active ? "yes" : "no"}`);
|
|
622
832
|
if (report.session.version) console.log(`Session plugin version: ${report.session.version}`);
|
|
623
833
|
if (report.session.control_dir) console.log(`Session control dir: ${report.session.control_dir}`);
|
|
@@ -1605,20 +1815,6 @@ function readRunState(targetDir, selection = {}) {
|
|
|
1605
1815
|
};
|
|
1606
1816
|
}
|
|
1607
1817
|
|
|
1608
|
-
function firstUnapprovedGate(approvals) {
|
|
1609
|
-
for (const gate of userGateOrder) {
|
|
1610
|
-
const status = approvals.get(gate)?.status ?? "";
|
|
1611
|
-
if (status !== "approved" && status !== "not_applicable") return gate;
|
|
1612
|
-
}
|
|
1613
|
-
return "OR";
|
|
1614
|
-
}
|
|
1615
|
-
|
|
1616
|
-
function normalizeCurrentGate(value, fallbackGate) {
|
|
1617
|
-
if (isPlaceholderValue(value)) return fallbackGate;
|
|
1618
|
-
const normalized = value.replace(/`/g, "").trim();
|
|
1619
|
-
return normalized || fallbackGate;
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
1818
|
function gateApprovalStatus(runState, gate) {
|
|
1623
1819
|
if (!userGateOrder.includes(gate)) return "not_applicable";
|
|
1624
1820
|
return runState.approvals.get(gate)?.status ?? "";
|
|
@@ -1672,7 +1868,9 @@ function analyzeDurableGateArtefactConsistency(runState) {
|
|
|
1672
1868
|
function isInternalStepSatisfied(runState, step) {
|
|
1673
1869
|
const artefact = runState.artefacts.get(step);
|
|
1674
1870
|
if (!artefact) return false;
|
|
1675
|
-
|
|
1871
|
+
if (artefact.status === "done") return true;
|
|
1872
|
+
return artefact.status === "not_applicable"
|
|
1873
|
+
&& (step === "Brownfield Review" || step === "Brownfield Analysis");
|
|
1676
1874
|
}
|
|
1677
1875
|
|
|
1678
1876
|
function modeSliceDecision(runState) {
|
|
@@ -1891,15 +2089,6 @@ function buildStatusCard({
|
|
|
1891
2089
|
};
|
|
1892
2090
|
}
|
|
1893
2091
|
|
|
1894
|
-
function effectiveCurrentGate(runState, transitionDecision) {
|
|
1895
|
-
if (transitionDecision.status !== "open") return transitionDecision.current_gate;
|
|
1896
|
-
const explicitGate = normalizeCurrentGate(runState.current_gate, "");
|
|
1897
|
-
const explicitIndex = gateProgressOrder.indexOf(explicitGate);
|
|
1898
|
-
const fallbackIndex = gateProgressOrder.indexOf(transitionDecision.current_gate);
|
|
1899
|
-
if (explicitIndex === -1 || fallbackIndex === -1) return transitionDecision.current_gate;
|
|
1900
|
-
return explicitIndex > fallbackIndex ? explicitGate : transitionDecision.current_gate;
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
2092
|
function durableArtefactBlock(gate, nextGate) {
|
|
1904
2093
|
const label = gate === "QA" ? "QA report" : `${gate} artefact`;
|
|
1905
2094
|
const stablePath = gate === "QA" ? ".agdf/control/artefacts/<key>/QA_REPORT.md" : `.agdf/control/artefacts/<key>/${gate}.md`;
|
|
@@ -2044,6 +2233,43 @@ function transitionDecisionForRunState(runState) {
|
|
|
2044
2233
|
};
|
|
2045
2234
|
}
|
|
2046
2235
|
|
|
2236
|
+
const implementationStepsRequired = gateApprovalStatus(runState, "TP") === "approved";
|
|
2237
|
+
if (implementationStepsRequired && !isInternalStepSatisfied(runState, "Brownfield Analysis")) {
|
|
2238
|
+
return {
|
|
2239
|
+
status: "open",
|
|
2240
|
+
current_gate: "Brownfield Analysis",
|
|
2241
|
+
blocking_reason: "none",
|
|
2242
|
+
missing_approval: "none",
|
|
2243
|
+
allowed: ["run Brownfield Analysis for the approved TP scope", "verify existing owners, reuse paths and regression risks"],
|
|
2244
|
+
forbidden: ["implement before Brownfield evidence supports the approved TP path", "claim QA or release readiness"],
|
|
2245
|
+
next_allowed_action: "Run Brownfield Analysis for the approved TP scope before CD+Tests.",
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
if (implementationStepsRequired && !isInternalStepSatisfied(runState, "CD+Tests")) {
|
|
2250
|
+
return {
|
|
2251
|
+
status: "open",
|
|
2252
|
+
current_gate: "CD+Tests",
|
|
2253
|
+
blocking_reason: "none",
|
|
2254
|
+
missing_approval: "none",
|
|
2255
|
+
allowed: ["implement the approved TP tasks", "run the approved test plan", "record implementation and test evidence"],
|
|
2256
|
+
forbidden: ["claim QA pass", "request UAT approval", "release"],
|
|
2257
|
+
next_allowed_action: "Implement the approved TP scope, run its tests, and record CD+Tests evidence before CR.",
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
if (implementationStepsRequired && !isInternalStepSatisfied(runState, "CR")) {
|
|
2262
|
+
return {
|
|
2263
|
+
status: "open",
|
|
2264
|
+
current_gate: "CR",
|
|
2265
|
+
blocking_reason: "none",
|
|
2266
|
+
missing_approval: "none",
|
|
2267
|
+
allowed: ["run mandatory code review", "record correctness, regression, security and maintainability findings", "fix blocking review findings"],
|
|
2268
|
+
forbidden: ["claim QA pass", "request UAT approval", "release"],
|
|
2269
|
+
next_allowed_action: "Run Code Review for the implemented TP scope and resolve blocking findings before QA.",
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2047
2273
|
if (gateApprovalStatus(runState, "QA") === "approved" && !isGateSatisfied(runState, "QA")) return durableArtefactBlock("QA", "UAT");
|
|
2048
2274
|
|
|
2049
2275
|
if (isGateSatisfied(runState, "QA") && !isGateSatisfied(runState, "UAT")) {
|
|
@@ -2072,12 +2298,12 @@ function transitionDecisionForRunState(runState) {
|
|
|
2072
2298
|
|
|
2073
2299
|
return {
|
|
2074
2300
|
status: "open",
|
|
2075
|
-
current_gate: "
|
|
2301
|
+
current_gate: "QA",
|
|
2076
2302
|
blocking_reason: "none",
|
|
2077
|
-
missing_approval: "
|
|
2078
|
-
allowed: ["run
|
|
2079
|
-
forbidden: ["
|
|
2080
|
-
next_allowed_action: "Run
|
|
2303
|
+
missing_approval: "Approval: QA",
|
|
2304
|
+
allowed: ["run QA gate", "persist or refine the QA report", "request exact QA approval"],
|
|
2305
|
+
forbidden: ["request UAT approval", "release", "claim delivery readiness before QA approval and report evidence"],
|
|
2306
|
+
next_allowed_action: "Run the QA gate, persist the QA report, and request exact approval: Approval: QA",
|
|
2081
2307
|
};
|
|
2082
2308
|
}
|
|
2083
2309
|
|
|
@@ -2090,7 +2316,7 @@ function evaluateGateCheck(targetDir, selection = {}) {
|
|
|
2090
2316
|
const doctorRevise = doctorReport.findings.find((finding) => finding.severity === "revise");
|
|
2091
2317
|
|
|
2092
2318
|
let status = transitionDecision.status;
|
|
2093
|
-
let currentGate =
|
|
2319
|
+
let currentGate = transitionDecision.current_gate;
|
|
2094
2320
|
let blockingReason = transitionDecision.blocking_reason;
|
|
2095
2321
|
let missingApproval = transitionDecision.missing_approval;
|
|
2096
2322
|
let allowed = transitionDecision.allowed;
|
|
@@ -2265,7 +2491,7 @@ function evaluateDeliveryMap(targetDir, selection = {}) {
|
|
|
2265
2491
|
const runState = readRunState(targetDir, selection);
|
|
2266
2492
|
const map = analyzeDeliveryMap(runState);
|
|
2267
2493
|
const gateDecision = transitionDecisionForRunState(runState);
|
|
2268
|
-
const currentGate =
|
|
2494
|
+
const currentGate = gateDecision.current_gate;
|
|
2269
2495
|
|
|
2270
2496
|
const severityRank = { block: 3, revise: 2, warn: 1 };
|
|
2271
2497
|
const deliverySeverity = map.findings.reduce((max, finding) => Math.max(max, severityRank[finding.severity] ?? 0), 0);
|
|
@@ -2497,8 +2723,9 @@ async function main() {
|
|
|
2497
2723
|
const configDir = options.dirExplicit ? options.dir : defaultOpenCodeConfigDir();
|
|
2498
2724
|
try {
|
|
2499
2725
|
const result = installOpenCodeGlobalPlugin(configDir);
|
|
2726
|
+
installOpenCodeGlobalSurface(configDir);
|
|
2500
2727
|
console.log(`AGDF OpenCode global plugin ${result.added ? "installed" : "already present"}: ${result.configPath}`);
|
|
2501
|
-
const report = evaluateOpenCodeStatus(options.dir, configDir);
|
|
2728
|
+
const report = evaluateOpenCodeStatus(options.dir, configDir, result.transition);
|
|
2502
2729
|
printOpenCodeStatus(report, false);
|
|
2503
2730
|
console.log("Restart OpenCode so it loads the updated global plugin config.");
|
|
2504
2731
|
} catch (error) {
|
|
@@ -37,6 +37,27 @@ Describe the trustworthy outcome.
|
|
|
37
37
|
| QA | missing | |
|
|
38
38
|
| UAT | missing | |
|
|
39
39
|
|
|
40
|
+
## Artefacts
|
|
41
|
+
|
|
42
|
+
| Type | Path | Status | Notes |
|
|
43
|
+
|---|---|---|---|
|
|
44
|
+
| UR | | draft | |
|
|
45
|
+
| Brownfield Review | | missing | |
|
|
46
|
+
| PRD | | missing | |
|
|
47
|
+
| SD | | missing | |
|
|
48
|
+
| TP | | missing | |
|
|
49
|
+
| Brownfield Analysis | | missing | |
|
|
50
|
+
| CD+Tests | | missing | |
|
|
51
|
+
| CR | | missing | |
|
|
52
|
+
| QA | | missing | |
|
|
53
|
+
|
|
54
|
+
## Mode/Slice Decision
|
|
55
|
+
|
|
56
|
+
- decision:
|
|
57
|
+
- required_next_gate:
|
|
58
|
+
- scope_reason:
|
|
59
|
+
- evidence:
|
|
60
|
+
|
|
40
61
|
## Evidence
|
|
41
62
|
|
|
42
63
|
| Evidence | Source | Covers | Strength |
|
|
@@ -233,6 +233,7 @@ section or any other existing line.
|
|
|
233
233
|
|
|
234
234
|
- User gates: `UR -> PRD -> SD -> TP -> QA -> UAT`
|
|
235
235
|
- Internal mandatory steps: `Brownfield Review -> Mode/Slice Decision -> Brownfield Analysis -> CD+Tests -> CR -> OR`
|
|
236
|
+
- On the approved-TP path, `CD+Tests` and `CR` are satisfied only by `done`; `not_applicable` may satisfy Brownfield Review or Brownfield Analysis, but it must not bypass implementation/test evidence or mandatory Code Review.
|
|
236
237
|
- Exact approval formula: `Approval: <GateName>`
|
|
237
238
|
- Legacy alias: `Freigabe: <GateName>` may be accepted when reviewing older German runs, but all new outputs must use `Approval: <GateName>`.
|
|
238
239
|
- Implicit consent is not approval. Phrases such as "ok", "go ahead", "do it", "approved", "continue", "leg los" or "looks good" are work intent, not gate approval.
|
|
@@ -293,7 +294,11 @@ This table is the canonical transition model. Skills may reference it, but must
|
|
|
293
294
|
| `SD` approved and SD artefact persisted or linked, TP missing or draft | `TP` | draft/refine Task/Test Plan, task IDs, evidence plan, persist/link TP, request `Approval: TP` | implementation, QA, release | `Approval: TP` |
|
|
294
295
|
| `TP` approved and TP artefact persisted or linked, Brownfield Analysis missing | `Brownfield Analysis` | run Brownfield Analysis for approved TP scope | implementation, QA, release | none |
|
|
295
296
|
| Brownfield Analysis passed for approved TP | `CD+Tests` | implement approved TP tasks and run tests | QA pass, UAT, release | none |
|
|
297
|
+
| `CD+Tests` done for approved TP, CR missing | `CR` | run mandatory Code Review and resolve blocking findings | QA pass, UAT, release | none |
|
|
298
|
+
| `CR` done, QA not approved | `QA` | run QA gate, persist/refine QA report, request `Approval: QA` | UAT, release | `Approval: QA` |
|
|
296
299
|
| `QA` approved but QA report missing or not pass | `QA` | persist/link QA report with `pass` decision or revise/block evidence | UAT, release | none |
|
|
300
|
+
| QA approved and QA report is `pass` or `passed`, UAT not approved | `UAT` | request `Approval: UAT`, prepare non-operative delivery summary | release and automatic VCS actions | `Approval: UAT` |
|
|
301
|
+
| UAT approved | `OR` | produce OR or delivery closeout; prepare VCS handoff only when requested | automatic commit, push, PR or release | none |
|
|
297
302
|
|
|
298
303
|
## Brownfield Modes
|
|
299
304
|
|
|
@@ -101,13 +101,13 @@ For relevant runs that change durable state, code, artefacts or gate status:
|
|
|
101
101
|
## OpenCode Files
|
|
102
102
|
|
|
103
103
|
- OpenCode loads this file through `opencode.json` `instructions`.
|
|
104
|
-
- AGDF for OpenCode has
|
|
105
|
-
- The global plugin
|
|
104
|
+
- AGDF for OpenCode has a global npm/native-skill discoverability layer and this repository-local governance surface.
|
|
105
|
+
- The global plugin and skills do not replace this file, `.opencode/skills/` or `.agdf/control/`; repository files remain the AGDF source of truth.
|
|
106
106
|
- Native OpenCode skills live under `.opencode/skills/` and use the `agdf-` prefix.
|
|
107
107
|
- The generated AGDF skills are loaded on demand through OpenCode's native `skill` tool; they are not parallel subagents or primary menu agents.
|
|
108
108
|
- For new build/change intent or unclear approval, load the `agdf-gate-check` skill before later artefacts or implementation.
|
|
109
109
|
- The AGDF OpenCode plugin is loaded from npm through `opencode.json` `plugin: ["create-agdf"]`.
|
|
110
|
-
- Use `npx --yes @agdf/cli@latest opencode-status --json` to verify global config, package loadability, active session signals and repository surface presence separately.
|
|
111
|
-
- OpenCode permissions keep `edit` and `bash` on explicit approval and allow the generated `agdf-*` skills explicitly.
|
|
110
|
+
- Use `npx --yes @agdf/cli@latest opencode-status --json` to verify global config, package loadability, global native-skill completeness, active session signals and repository surface presence separately.
|
|
111
|
+
- OpenCode permissions keep `edit` and `bash` on explicit approval and allow the generated `agdf-*` skills explicitly; global adapters use the `agdf-global-*` namespace.
|
|
112
112
|
- Shared output and gate rules live in `.opencode/agdf-runtime-contract.md`.
|
|
113
113
|
- Do not paste full control files, templates or artefact bodies into chat unless the user explicitly asks for the full content; summarize and link paths instead.
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
These files were generated from the AGDF source repository for OpenCode.
|
|
4
4
|
|
|
5
5
|
- `opencode.json` loads the npm plugin `create-agdf` and `.opencode/AGDF.md` as repository instructions.
|
|
6
|
-
- AGDF for OpenCode has
|
|
7
|
-
- The global plugin
|
|
8
|
-
- `opencode.json` keeps `edit` and `bash` on `ask` and explicitly allows the generated `agdf-*` skills.
|
|
6
|
+
- AGDF for OpenCode has a global npm/native-skill discoverability layer and this repository-local governance surface.
|
|
7
|
+
- The global plugin and skills do not replace `.opencode/AGDF.md`, `.opencode/skills/` or `.agdf/control/`; repository files remain the AGDF source of truth.
|
|
8
|
+
- `opencode.json` keeps `edit` and `bash` on `ask` and explicitly allows the generated `agdf-*` skills; global adapters use `agdf-global-*` to avoid same-name masking.
|
|
9
9
|
- `.opencode/skills/` contains native OpenCode skills generated from the canonical AGDF skills.
|
|
10
10
|
- For new build/change intent or unclear approval, load `agdf-gate-check` through OpenCode's native `skill` tool.
|
|
11
|
-
- Use `npx --yes @agdf/cli@latest opencode-status --json` to distinguish global hook configuration, package loadability, session activity and this repository surface.
|
|
11
|
+
- Use `npx --yes @agdf/cli@latest opencode-status --json` to distinguish global hook/native-skill configuration, package loadability, session activity and this repository surface.
|
|
12
12
|
- Use `npx --yes @agdf/cli@latest gate-check --status-card` for compact interactive gate state; reserve full `--json` output for automation or audit evidence.
|
|
13
13
|
- `.opencode/agdf-runtime-contract.md` is the shared gate and output contract.
|
|
14
14
|
|
|
@@ -233,6 +233,7 @@ section or any other existing line.
|
|
|
233
233
|
|
|
234
234
|
- User gates: `UR -> PRD -> SD -> TP -> QA -> UAT`
|
|
235
235
|
- Internal mandatory steps: `Brownfield Review -> Mode/Slice Decision -> Brownfield Analysis -> CD+Tests -> CR -> OR`
|
|
236
|
+
- On the approved-TP path, `CD+Tests` and `CR` are satisfied only by `done`; `not_applicable` may satisfy Brownfield Review or Brownfield Analysis, but it must not bypass implementation/test evidence or mandatory Code Review.
|
|
236
237
|
- Exact approval formula: `Approval: <GateName>`
|
|
237
238
|
- Legacy alias: `Freigabe: <GateName>` may be accepted when reviewing older German runs, but all new outputs must use `Approval: <GateName>`.
|
|
238
239
|
- Implicit consent is not approval. Phrases such as "ok", "go ahead", "do it", "approved", "continue", "leg los" or "looks good" are work intent, not gate approval.
|
|
@@ -293,7 +294,11 @@ This table is the canonical transition model. Skills may reference it, but must
|
|
|
293
294
|
| `SD` approved and SD artefact persisted or linked, TP missing or draft | `TP` | draft/refine Task/Test Plan, task IDs, evidence plan, persist/link TP, request `Approval: TP` | implementation, QA, release | `Approval: TP` |
|
|
294
295
|
| `TP` approved and TP artefact persisted or linked, Brownfield Analysis missing | `Brownfield Analysis` | run Brownfield Analysis for approved TP scope | implementation, QA, release | none |
|
|
295
296
|
| Brownfield Analysis passed for approved TP | `CD+Tests` | implement approved TP tasks and run tests | QA pass, UAT, release | none |
|
|
297
|
+
| `CD+Tests` done for approved TP, CR missing | `CR` | run mandatory Code Review and resolve blocking findings | QA pass, UAT, release | none |
|
|
298
|
+
| `CR` done, QA not approved | `QA` | run QA gate, persist/refine QA report, request `Approval: QA` | UAT, release | `Approval: QA` |
|
|
296
299
|
| `QA` approved but QA report missing or not pass | `QA` | persist/link QA report with `pass` decision or revise/block evidence | UAT, release | none |
|
|
300
|
+
| QA approved and QA report is `pass` or `passed`, UAT not approved | `UAT` | request `Approval: UAT`, prepare non-operative delivery summary | release and automatic VCS actions | `Approval: UAT` |
|
|
301
|
+
| UAT approved | `OR` | produce OR or delivery closeout; prepare VCS handoff only when requested | automatic commit, push, PR or release | none |
|
|
297
302
|
|
|
298
303
|
## Brownfield Modes
|
|
299
304
|
|
|
@@ -37,6 +37,27 @@ Describe the trustworthy outcome.
|
|
|
37
37
|
| QA | missing | |
|
|
38
38
|
| UAT | missing | |
|
|
39
39
|
|
|
40
|
+
## Artefacts
|
|
41
|
+
|
|
42
|
+
| Type | Path | Status | Notes |
|
|
43
|
+
|---|---|---|---|
|
|
44
|
+
| UR | | draft | |
|
|
45
|
+
| Brownfield Review | | missing | |
|
|
46
|
+
| PRD | | missing | |
|
|
47
|
+
| SD | | missing | |
|
|
48
|
+
| TP | | missing | |
|
|
49
|
+
| Brownfield Analysis | | missing | |
|
|
50
|
+
| CD+Tests | | missing | |
|
|
51
|
+
| CR | | missing | |
|
|
52
|
+
| QA | | missing | |
|
|
53
|
+
|
|
54
|
+
## Mode/Slice Decision
|
|
55
|
+
|
|
56
|
+
- decision:
|
|
57
|
+
- required_next_gate:
|
|
58
|
+
- scope_reason:
|
|
59
|
+
- evidence:
|
|
60
|
+
|
|
40
61
|
## Evidence
|
|
41
62
|
|
|
42
63
|
| Evidence | Source | Covers | Strength |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"id": "agdf",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0",
|
|
5
5
|
"displayName": "AI Governance & Delivery Framework",
|
|
6
6
|
"developerName": "Arndt Gold",
|
|
7
7
|
"description": "Codex-first operating system for governed AI-assisted delivery.",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"opencode": {
|
|
54
54
|
"skillPrefix": "agdf-",
|
|
55
|
+
"globalSkillPrefix": "agdf-global-",
|
|
55
56
|
"runtimeContractFileName": "agdf-runtime-contract.md",
|
|
56
57
|
"instructionsFileName": "AGDF.md",
|
|
57
58
|
"npmPackage": "create-agdf",
|
|
@@ -233,6 +233,7 @@ section or any other existing line.
|
|
|
233
233
|
|
|
234
234
|
- User gates: `UR -> PRD -> SD -> TP -> QA -> UAT`
|
|
235
235
|
- Internal mandatory steps: `Brownfield Review -> Mode/Slice Decision -> Brownfield Analysis -> CD+Tests -> CR -> OR`
|
|
236
|
+
- On the approved-TP path, `CD+Tests` and `CR` are satisfied only by `done`; `not_applicable` may satisfy Brownfield Review or Brownfield Analysis, but it must not bypass implementation/test evidence or mandatory Code Review.
|
|
236
237
|
- Exact approval formula: `Approval: <GateName>`
|
|
237
238
|
- Legacy alias: `Freigabe: <GateName>` may be accepted when reviewing older German runs, but all new outputs must use `Approval: <GateName>`.
|
|
238
239
|
- Implicit consent is not approval. Phrases such as "ok", "go ahead", "do it", "approved", "continue", "leg los" or "looks good" are work intent, not gate approval.
|
|
@@ -293,7 +294,11 @@ This table is the canonical transition model. Skills may reference it, but must
|
|
|
293
294
|
| `SD` approved and SD artefact persisted or linked, TP missing or draft | `TP` | draft/refine Task/Test Plan, task IDs, evidence plan, persist/link TP, request `Approval: TP` | implementation, QA, release | `Approval: TP` |
|
|
294
295
|
| `TP` approved and TP artefact persisted or linked, Brownfield Analysis missing | `Brownfield Analysis` | run Brownfield Analysis for approved TP scope | implementation, QA, release | none |
|
|
295
296
|
| Brownfield Analysis passed for approved TP | `CD+Tests` | implement approved TP tasks and run tests | QA pass, UAT, release | none |
|
|
297
|
+
| `CD+Tests` done for approved TP, CR missing | `CR` | run mandatory Code Review and resolve blocking findings | QA pass, UAT, release | none |
|
|
298
|
+
| `CR` done, QA not approved | `QA` | run QA gate, persist/refine QA report, request `Approval: QA` | UAT, release | `Approval: QA` |
|
|
296
299
|
| `QA` approved but QA report missing or not pass | `QA` | persist/link QA report with `pass` decision or revise/block evidence | UAT, release | none |
|
|
300
|
+
| QA approved and QA report is `pass` or `passed`, UAT not approved | `UAT` | request `Approval: UAT`, prepare non-operative delivery summary | release and automatic VCS actions | `Approval: UAT` |
|
|
301
|
+
| UAT approved | `OR` | produce OR or delivery closeout; prepare VCS handoff only when requested | automatic commit, push, PR or release | none |
|
|
297
302
|
|
|
298
303
|
## Brownfield Modes
|
|
299
304
|
|
|
@@ -101,11 +101,13 @@ export function parseControlState(
|
|
|
101
101
|
const approvals = new Map();
|
|
102
102
|
for (const h of ["Approvals", "Gate Checklist"])
|
|
103
103
|
for (const [g, s, e] of rows(section(content, h))) {
|
|
104
|
-
if (userGates.includes(g))
|
|
104
|
+
if (userGates.includes(g)) {
|
|
105
|
+
const status = clean(s);
|
|
105
106
|
approvals.set(g, {
|
|
106
|
-
status:
|
|
107
|
+
status: g === "QA" && (status === "pass" || status === "passed") ? "approved" : status,
|
|
107
108
|
evidence: e ?? "",
|
|
108
109
|
});
|
|
110
|
+
}
|
|
109
111
|
}
|
|
110
112
|
const artefacts = new Map();
|
|
111
113
|
for (const [t, p, s, n] of rows(section(content, "Artefacts"))) {
|
|
@@ -113,7 +115,9 @@ export function parseControlState(
|
|
|
113
115
|
artefacts.set(t, { path: p ?? "", status: clean(s), notes: n ?? "" });
|
|
114
116
|
}
|
|
115
117
|
const mapRows = (h, header, fn) => dataRows(content, h, header).map(fn);
|
|
116
|
-
const mode = "Mode
|
|
118
|
+
const mode = section(content, "Mode/Slice Decision").trim()
|
|
119
|
+
? "Mode/Slice Decision"
|
|
120
|
+
: "Mode / Slice Decision",
|
|
117
121
|
source = "Source And Scope State",
|
|
118
122
|
memory = "Knowledge Persistence Decision";
|
|
119
123
|
return {
|
package/package.json
CHANGED