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 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 hook
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, whether `create-agdf` is loadable from the OpenCode config directory, whether the current process exposes AGDF 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.
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 or repository `opencode.json`, AGENTS-style rules from `.opencode/AGDF.md`, explicit `edit`/`bash`/`skill` permissions and the `agdf-` prefixed native skills under `.opencode/skills/`. AGDF for OpenCode has two layers: a global npm plugin hook installed with `npx --yes @agdf/cli@latest opencode` and a repository-local governance surface generated by the `opencode-repo` target.
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
- Using OpenCode's plugin installer or global config for `create-agdf` can add the npm plugin hook, but AGDF still needs the repository instructions and native skills. The `opencode-repo` target writes the repository files together.
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
 
@@ -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 npmCommand = process.platform === "win32" ? process.execPath : "npm";
36
- const npmPrefixArgs = process.platform === "win32" ? [join(dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js")] : [];
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
- execFileSync(npmCommand, [...npmPrefixArgs, "install", "--silent", "--save-prod", "--prefix", configDir, packageRoot], { stdio: "pipe" });
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: execFileSync(process.execPath, ["-e", `process.stdout.write(require.resolve(${JSON.stringify(packageName)}))`], {
540
- cwd: configDir,
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 evaluateOpenCodeStatus(targetDir, configDir = defaultOpenCodeConfigDir()) {
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: repositorySurface
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
- return artefact.status === "done" || artefact.status === "not_applicable";
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: "Brownfield Analysis",
2301
+ current_gate: "QA",
2076
2302
  blocking_reason: "none",
2077
- missing_approval: "none",
2078
- allowed: ["run Brownfield Analysis for the approved TP scope", "verify existing owners, reuse paths and regression risks"],
2079
- forbidden: ["implement before Brownfield evidence supports the approved TP path", "claim QA or release readiness"],
2080
- next_allowed_action: "Run Brownfield Analysis for the approved TP scope before CD+Tests.",
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 = effectiveCurrentGate(runState, transitionDecision);
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 = effectiveCurrentGate(runState, gateDecision);
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 two layers: an optional global npm plugin hook and this repository-local governance surface.
105
- - The global plugin hook does not replace this file, `.opencode/skills/` or `.agdf/control/`; repository files remain the AGDF source of truth.
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 two layers: an optional global npm plugin hook and this repository-local governance surface.
7
- - The global plugin hook does 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.
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
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agdf",
3
- "version": "0.6.8",
3
+ "version": "0.7.0",
4
4
  "description": "Codex-first operating system for governed AI-assisted delivery.",
5
5
  "author": {
6
6
  "name": "Arndt Gold",
@@ -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.6.8",
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: clean(s) === "passed" && g === "QA" ? "approved" : clean(s),
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 / Slice Decision",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agdf",
3
- "version": "0.6.8",
3
+ "version": "0.7.0",
4
4
  "description": "Bootstrap AGDF repository-local plugin files, Copilot/OpenCode instructions, skills, control files and machine-readable checks.",
5
5
  "type": "module",
6
6
  "files": [