@ktpartners/dgs-platform 2.9.0 → 3.3.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.
Files changed (166) hide show
  1. package/CHANGELOG.md +197 -0
  2. package/README.md +34 -2
  3. package/agents/dgs-executor.md +124 -3
  4. package/agents/dgs-idea-researcher.md +447 -0
  5. package/agents/dgs-plan-checker.md +61 -3
  6. package/agents/dgs-planner.md +51 -8
  7. package/bin/install.js +44 -0
  8. package/commands/dgs/abandon-quick.md +28 -0
  9. package/commands/dgs/add-tests.md +2 -2
  10. package/commands/dgs/audit-milestone.md +4 -3
  11. package/commands/dgs/capture-principle.md +11 -11
  12. package/commands/dgs/cleanup.md +2 -2
  13. package/commands/dgs/complete-milestone.md +11 -11
  14. package/commands/dgs/complete-quick.md +28 -0
  15. package/commands/dgs/create-milestone-job.md +2 -2
  16. package/commands/dgs/debug.md +3 -3
  17. package/commands/dgs/develop-idea.md +1 -1
  18. package/commands/dgs/diff-report.md +124 -0
  19. package/commands/dgs/fast.md +3 -1
  20. package/commands/dgs/health.md +1 -1
  21. package/commands/dgs/map-codebase.md +6 -6
  22. package/commands/dgs/new-milestone.md +5 -5
  23. package/commands/dgs/new-project.md +8 -21
  24. package/commands/dgs/package-scan.md +43 -0
  25. package/commands/dgs/plan-milestone-gaps.md +1 -1
  26. package/commands/dgs/progress.md +3 -3
  27. package/commands/dgs/quick-abandon.md +8 -0
  28. package/commands/dgs/quick-complete.md +8 -0
  29. package/commands/dgs/quick.md +10 -3
  30. package/commands/dgs/research-idea.md +3 -2
  31. package/commands/dgs/research-phase.md +3 -3
  32. package/commands/dgs/switch-project.md +14 -1
  33. package/commands/dgs/write-spec.md +3 -3
  34. package/deliver-great-systems/bin/dgs-tools.cjs +401 -32
  35. package/deliver-great-systems/bin/lib/audit-tolerance.cjs +77 -0
  36. package/deliver-great-systems/bin/lib/audit-tolerance.test.cjs +101 -0
  37. package/deliver-great-systems/bin/lib/commands.cjs +626 -46
  38. package/deliver-great-systems/bin/lib/commands.test.cjs +451 -0
  39. package/deliver-great-systems/bin/lib/commit-verify.test.cjs +236 -0
  40. package/deliver-great-systems/bin/lib/config.cjs +80 -6
  41. package/deliver-great-systems/bin/lib/config.test.cjs +309 -0
  42. package/deliver-great-systems/bin/lib/context.cjs +120 -0
  43. package/deliver-great-systems/bin/lib/core.cjs +35 -14
  44. package/deliver-great-systems/bin/lib/core.test.cjs +79 -1
  45. package/deliver-great-systems/bin/lib/execution.cjs +49 -17
  46. package/deliver-great-systems/bin/lib/fast-routing.cjs +199 -0
  47. package/deliver-great-systems/bin/lib/fast-routing.test.cjs +108 -0
  48. package/deliver-great-systems/bin/lib/final-commit-precondition.test.cjs +87 -0
  49. package/deliver-great-systems/bin/lib/fixtures/package-scan/bundler-audit-gemfile.json +21 -0
  50. package/deliver-great-systems/bin/lib/fixtures/package-scan/gate-parity-expected.md +186 -0
  51. package/deliver-great-systems/bin/lib/fixtures/package-scan/gate-parity-runresult.json +235 -0
  52. package/deliver-great-systems/bin/lib/fixtures/package-scan/govulncheck-import.json +3 -0
  53. package/deliver-great-systems/bin/lib/fixtures/package-scan/npm-audit-v10.json +37 -0
  54. package/deliver-great-systems/bin/lib/fixtures/package-scan/osv-clean.json +3 -0
  55. package/deliver-great-systems/bin/lib/fixtures/package-scan/osv-vulns.json +77 -0
  56. package/deliver-great-systems/bin/lib/fixtures/package-scan/pip-audit-requirements.json +28 -0
  57. package/deliver-great-systems/bin/lib/fixtures/package-scan/snyk-lodash.json +30 -0
  58. package/deliver-great-systems/bin/lib/fixtures/package-scan/snyk-workspaces.json +55 -0
  59. package/deliver-great-systems/bin/lib/flat-migration.test.cjs +396 -0
  60. package/deliver-great-systems/bin/lib/frontmatter.cjs +1 -1
  61. package/deliver-great-systems/bin/lib/governance.cjs +211 -0
  62. package/deliver-great-systems/bin/lib/governance.test.cjs +339 -0
  63. package/deliver-great-systems/bin/lib/health-untracked-phase.test.cjs +269 -0
  64. package/deliver-great-systems/bin/lib/ideas.cjs +206 -91
  65. package/deliver-great-systems/bin/lib/ideas.test.cjs +244 -1
  66. package/deliver-great-systems/bin/lib/init.cjs +357 -61
  67. package/deliver-great-systems/bin/lib/init.test.cjs +625 -8
  68. package/deliver-great-systems/bin/lib/jobs.cjs +131 -25
  69. package/deliver-great-systems/bin/lib/jobs.test.cjs +193 -74
  70. package/deliver-great-systems/bin/lib/migration.cjs +409 -1
  71. package/deliver-great-systems/bin/lib/migration.test.cjs +158 -1
  72. package/deliver-great-systems/bin/lib/milestone.cjs +154 -31
  73. package/deliver-great-systems/bin/lib/milestone.test.cjs +203 -0
  74. package/deliver-great-systems/bin/lib/package-adapters.cjs +530 -0
  75. package/deliver-great-systems/bin/lib/package-adapters.test.cjs +618 -0
  76. package/deliver-great-systems/bin/lib/package-ecosystems.cjs +350 -0
  77. package/deliver-great-systems/bin/lib/package-ecosystems.test.cjs +348 -0
  78. package/deliver-great-systems/bin/lib/package-runner.cjs +199 -0
  79. package/deliver-great-systems/bin/lib/package-runner.test.cjs +198 -0
  80. package/deliver-great-systems/bin/lib/package-scan-provenance.cjs +56 -0
  81. package/deliver-great-systems/bin/lib/package-scan-provenance.test.cjs +103 -0
  82. package/deliver-great-systems/bin/lib/package-scan-report.cjs +1140 -0
  83. package/deliver-great-systems/bin/lib/package-scan-report.test.cjs +1963 -0
  84. package/deliver-great-systems/bin/lib/package-scan-skill.cjs +96 -0
  85. package/deliver-great-systems/bin/lib/package-scan-skill.test.cjs +136 -0
  86. package/deliver-great-systems/bin/lib/package-scan.cjs +919 -0
  87. package/deliver-great-systems/bin/lib/package-scan.test.cjs +2147 -0
  88. package/deliver-great-systems/bin/lib/phase.cjs +146 -3
  89. package/deliver-great-systems/bin/lib/phase.test.cjs +420 -0
  90. package/deliver-great-systems/bin/lib/plan-number-validity.test.cjs +48 -0
  91. package/deliver-great-systems/bin/lib/projects.cjs +65 -10
  92. package/deliver-great-systems/bin/lib/projects.test.cjs +198 -2
  93. package/deliver-great-systems/bin/lib/quick.cjs +739 -0
  94. package/deliver-great-systems/bin/lib/quick.test.cjs +730 -0
  95. package/deliver-great-systems/bin/lib/repos.cjs +37 -13
  96. package/deliver-great-systems/bin/lib/review.cjs +1821 -0
  97. package/deliver-great-systems/bin/lib/roadmap.cjs +34 -13
  98. package/deliver-great-systems/bin/lib/specs.cjs +3 -81
  99. package/deliver-great-systems/bin/lib/state-transition-gate.test.cjs +160 -0
  100. package/deliver-great-systems/bin/lib/state.cjs +147 -55
  101. package/deliver-great-systems/bin/lib/summary-frontmatter.cjs +54 -0
  102. package/deliver-great-systems/bin/lib/summary-frontmatter.test.cjs +78 -0
  103. package/deliver-great-systems/bin/lib/sweep-scope.test.cjs +263 -0
  104. package/deliver-great-systems/bin/lib/sync.cjs +75 -0
  105. package/deliver-great-systems/bin/lib/verify.cjs +198 -7
  106. package/deliver-great-systems/bin/lib/verify.test.cjs +82 -0
  107. package/deliver-great-systems/bin/lib/wave-0-template-rename.test.cjs +40 -0
  108. package/deliver-great-systems/bin/lib/worktrees.cjs +790 -0
  109. package/deliver-great-systems/bin/lib/worktrees.test.cjs +963 -0
  110. package/deliver-great-systems/references/agent-step-reliability.md +60 -0
  111. package/deliver-great-systems/references/conflict-resolution.md +4 -0
  112. package/deliver-great-systems/references/context-tiers.md +4 -0
  113. package/deliver-great-systems/references/package-scan-config.md +151 -0
  114. package/deliver-great-systems/references/questioning.md +0 -30
  115. package/deliver-great-systems/references/spec-review-loop.md +1 -2
  116. package/deliver-great-systems/references/workflow-conventions.md +29 -0
  117. package/deliver-great-systems/skills/dgs-tests/package-scan.md +44 -0
  118. package/deliver-great-systems/templates/REVIEW.md +35 -0
  119. package/deliver-great-systems/templates/VALIDATION.md +1 -1
  120. package/deliver-great-systems/templates/claude-md.md +27 -0
  121. package/deliver-great-systems/templates/package-scan-report.md +108 -0
  122. package/deliver-great-systems/templates/project.md +6 -170
  123. package/deliver-great-systems/templates/summary.md +3 -1
  124. package/deliver-great-systems/workflows/abandon-quick.md +89 -0
  125. package/deliver-great-systems/workflows/add-idea.md +3 -3
  126. package/deliver-great-systems/workflows/add-phase.md +5 -0
  127. package/deliver-great-systems/workflows/add-tests.md +14 -0
  128. package/deliver-great-systems/workflows/add-todo.md +1 -0
  129. package/deliver-great-systems/workflows/approve-spec.md +25 -4
  130. package/deliver-great-systems/workflows/audit-milestone.md +66 -10
  131. package/deliver-great-systems/workflows/audit-phase.md +15 -5
  132. package/deliver-great-systems/workflows/cancel-job.md +2 -2
  133. package/deliver-great-systems/workflows/check-todos.md +2 -3
  134. package/deliver-great-systems/workflows/codereview.md +103 -9
  135. package/deliver-great-systems/workflows/complete-milestone.md +218 -24
  136. package/deliver-great-systems/workflows/complete-quick.md +106 -0
  137. package/deliver-great-systems/workflows/consolidate-ideas.md +1 -1
  138. package/deliver-great-systems/workflows/create-milestone-job.md +4 -4
  139. package/deliver-great-systems/workflows/develop-idea.md +11 -11
  140. package/deliver-great-systems/workflows/diagnose-issues.md +14 -0
  141. package/deliver-great-systems/workflows/discuss-idea.md +1 -1
  142. package/deliver-great-systems/workflows/discuss-phase.md +3 -2
  143. package/deliver-great-systems/workflows/execute-phase.md +209 -33
  144. package/deliver-great-systems/workflows/execute-plan.md +22 -22
  145. package/deliver-great-systems/workflows/help.md +53 -20
  146. package/deliver-great-systems/workflows/import-spec.md +65 -7
  147. package/deliver-great-systems/workflows/init-product.md +45 -167
  148. package/deliver-great-systems/workflows/new-milestone.md +140 -33
  149. package/deliver-great-systems/workflows/new-project.md +60 -331
  150. package/deliver-great-systems/workflows/package-scan.md +59 -0
  151. package/deliver-great-systems/workflows/plan-phase.md +79 -1
  152. package/deliver-great-systems/workflows/progress-all.md +133 -0
  153. package/deliver-great-systems/workflows/quick-abandon.md +89 -0
  154. package/deliver-great-systems/workflows/quick-complete.md +106 -0
  155. package/deliver-great-systems/workflows/quick.md +328 -26
  156. package/deliver-great-systems/workflows/refine-spec.md +1 -1
  157. package/deliver-great-systems/workflows/research-idea.md +77 -139
  158. package/deliver-great-systems/workflows/resume-project.md +2 -2
  159. package/deliver-great-systems/workflows/run-job.md +29 -43
  160. package/deliver-great-systems/workflows/settings.md +13 -77
  161. package/deliver-great-systems/workflows/validate-phase.md +39 -1
  162. package/deliver-great-systems/workflows/verify-work.md +14 -0
  163. package/deliver-great-systems/workflows/write-spec.md +11 -13
  164. package/hooks/dist/dgs-enforce-discipline.js +196 -0
  165. package/package.json +1 -1
  166. package/scripts/build-hooks.js +1 -0
@@ -550,16 +550,13 @@ function cmdValidateHealth(cwd, options, raw) {
550
550
  return;
551
551
  }
552
552
 
553
- // ─── Check 2: PROJECT.md exists and has required sections ─────────────────
553
+ // ─── Check 2: PROJECT.md exists and has a heading ────────────────────────
554
554
  if (!fs.existsSync(projectPath)) {
555
555
  addIssue('error', 'E002', 'PROJECT.md not found', 'Run /dgs:new-project to create');
556
556
  } else {
557
557
  const content = fs.readFileSync(projectPath, 'utf-8');
558
- const requiredSections = ['## What This Is', '## Core Value', '## Requirements'];
559
- for (const section of requiredSections) {
560
- if (!content.includes(section)) {
561
- addIssue('warning', 'W001', `PROJECT.md missing section: ${section}`, 'Add section manually');
562
- }
558
+ if (!/^#\s+\S/m.test(content)) {
559
+ addIssue('warning', 'W001', 'PROJECT.md missing top-level heading', 'Add a # Title line');
563
560
  }
564
561
  }
565
562
 
@@ -708,6 +705,201 @@ function cmdValidateHealth(cwd, options, raw) {
708
705
  }
709
706
  }
710
707
 
708
+ // ─── Check 10: Uncommitted tracking files in completed phases ─────────────
709
+ // Surface the condition `phase finalize` was designed to prevent: a completed
710
+ // phase whose ROADMAP.md / STATE.md / REQUIREMENTS.md / VERIFICATION.md are
711
+ // modified in the working tree but not committed (e.g., Claude dropped the
712
+ // commit step between updating trackers and invoking `commit`).
713
+ try {
714
+ const { execGit } = require('./core.cjs');
715
+ const isGit = execGit(cwd, ['rev-parse', '--is-inside-work-tree']);
716
+ if (isGit.exitCode === 0 && isGit.stdout.trim() === 'true') {
717
+ // git resolves symlinks when reporting paths; normalize both sides against
718
+ // the real git root so relative paths compare correctly on macOS
719
+ // (/var/folders → /private/var/folders).
720
+ const gitRootRes = execGit(cwd, ['rev-parse', '--show-toplevel']);
721
+ const gitRoot = gitRootRes.exitCode === 0 ? gitRootRes.stdout.trim() : cwd;
722
+ const statusRes = execGit(cwd, ['status', '--porcelain']);
723
+ if (statusRes.exitCode === 0) {
724
+ // `git status --porcelain` emits "XY path" lines (X=index, Y=worktree,
725
+ // column 3 is a separator space, path starts at column 4). Note:
726
+ // execGit.stdout is already .trim()'d so lines may have lost a leading
727
+ // space when X=' ' (worktree-only changes). Extract the path from
728
+ // whatever follows the first whitespace group.
729
+ const dirtyPaths = statusRes.stdout
730
+ .split('\n')
731
+ .filter(Boolean)
732
+ .map(l => {
733
+ // Match "XY<space>path" OR "<space>Y<space>path" (leading space may be trimmed).
734
+ // A path after 2 status chars + 1 space begins at index 3 of the original,
735
+ // but lines like " M file" got left-trimmed to "M file" (only one status char).
736
+ // Normalize: skip the leading non-space + exactly one space.
737
+ const m = l.match(/^\S{1,2}\s(.+)$/);
738
+ return m ? m[1].trim() : l.trim();
739
+ });
740
+
741
+ // Identify completed phases from ROADMAP.md checkboxes
742
+ const completedPhases = new Set();
743
+ if (fs.existsSync(roadmapPath)) {
744
+ const rm = fs.readFileSync(roadmapPath, 'utf-8');
745
+ const re = /-\s*\[x\]\s*.*Phase\s+(\d+[A-Z]?(?:\.\d+)*)/gi;
746
+ let m;
747
+ while ((m = re.exec(rm)) !== null) completedPhases.add(m[1]);
748
+ }
749
+
750
+ // Tracking files at the planning root — compute relative to gitRoot
751
+ const trackingRoot = ['ROADMAP.md', 'STATE.md', 'REQUIREMENTS.md']
752
+ .map(f => path.relative(gitRoot, path.join(planningDir, f)));
753
+
754
+ // VERIFICATION.md files inside completed phase dirs
755
+ const trackingVerif = [];
756
+ if (completedPhases.size > 0) {
757
+ try {
758
+ const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
759
+ for (const e of entries) {
760
+ if (!e.isDirectory()) continue;
761
+ const dm = e.name.match(/^(\d+[A-Z]?(?:\.\d+)*)/i);
762
+ if (!dm) continue;
763
+ const phaseNum = dm[1];
764
+ const unpadded = String(parseInt(phaseNum, 10));
765
+ if (!completedPhases.has(phaseNum) && !completedPhases.has(unpadded)) continue;
766
+ const phaseFiles = fs.readdirSync(path.join(phasesDir, e.name));
767
+ for (const f of phaseFiles) {
768
+ if (/-VERIFICATION\.md$/i.test(f)) {
769
+ trackingVerif.push(path.relative(gitRoot, path.join(phasesDir, e.name, f)));
770
+ }
771
+ }
772
+ }
773
+ } catch { /* ignore */ }
774
+ }
775
+
776
+ const allTracking = [...trackingRoot, ...trackingVerif];
777
+ const dirtyTracking = dirtyPaths.filter(p => allTracking.includes(p));
778
+ if (dirtyTracking.length > 0) {
779
+ addIssue('warning', 'W009',
780
+ `Uncommitted tracking files for completed phase(s): ${dirtyTracking.join(', ')}`,
781
+ 'Run `dgs-tools phase finalize <phase>` for the relevant phase, or commit manually'
782
+ );
783
+ }
784
+ }
785
+ }
786
+ } catch { /* non-git or git error — skip silently */ }
787
+
788
+ // ─── Check 11: Untracked scaffolding (.gitkeep) — REL-12 ────────────────
789
+ // Walks planning root for `.gitkeep` files in standard scaffold locations
790
+ // (specs/, docs/product/, quick/, projects/*/{phases,quick,debug,research}/)
791
+ // and warns if any exist on disk but aren't tracked in git. User-facing analog
792
+ // of REL-09 (init-product .gitkeep commit) — REL-09 prevented the leak,
793
+ // REL-12 catches any reintroduction immediately on the next health-check run.
794
+ try {
795
+ const { execGit } = require('./core.cjs');
796
+ const isGitWT = (() => {
797
+ try {
798
+ const res = execGit(cwd, ['rev-parse', '--is-inside-work-tree']);
799
+ return res && res.exitCode === 0 && (res.stdout || '').trim() === 'true';
800
+ } catch { return false; }
801
+ })();
802
+
803
+ if (isGitWT) {
804
+ const candidates = [];
805
+
806
+ // Direct scaffold locations under planning root
807
+ const directDirs = ['specs', 'docs/product', 'quick'];
808
+ for (const d of directDirs) {
809
+ const p = path.join(planningDir, d, '.gitkeep');
810
+ if (fs.existsSync(p)) candidates.push(path.join(d, '.gitkeep'));
811
+ }
812
+
813
+ // Per-project scaffold locations: projects/*/{phases,quick,debug,research}/.gitkeep
814
+ const projectsDir = path.join(planningDir, 'projects');
815
+ if (fs.existsSync(projectsDir)) {
816
+ try {
817
+ const projects = fs.readdirSync(projectsDir, { withFileTypes: true });
818
+ for (const proj of projects) {
819
+ if (!proj.isDirectory()) continue;
820
+ for (const sub of ['phases', 'quick', 'debug', 'research']) {
821
+ const p = path.join(planningDir, 'projects', proj.name, sub, '.gitkeep');
822
+ if (fs.existsSync(p)) {
823
+ candidates.push(path.join('projects', proj.name, sub, '.gitkeep'));
824
+ }
825
+ }
826
+ }
827
+ } catch { /* ignore */ }
828
+ }
829
+
830
+ // For each candidate, check whether it's tracked in git via
831
+ // `git ls-files <rel>` — empty stdout means untracked.
832
+ const untracked = [];
833
+ for (const rel of candidates) {
834
+ const lsResult = execGit(cwd, ['ls-files', '--', rel]);
835
+ const tracked = lsResult && lsResult.exitCode === 0 && (lsResult.stdout || '').trim().length > 0;
836
+ if (!tracked) untracked.push(rel);
837
+ }
838
+
839
+ if (untracked.length > 0) {
840
+ addIssue(
841
+ 'warning',
842
+ 'untracked-scaffolding',
843
+ `Found ${untracked.length} untracked .gitkeep file(s) in standard scaffold locations: ${untracked.join(', ')}`,
844
+ `Run 'git add ${untracked.join(' ')} && git commit -m "docs: track scaffolding files"' to commit them; closes idea #29 (init-product .gitkeep leak) on the next health-check run.`,
845
+ false
846
+ );
847
+ }
848
+ }
849
+ } catch { /* non-git or git error — skip silently per existing convention */ }
850
+
851
+ // ─── Check 12: untracked-phase-artifacts (REL-11, Phase 156) ───────────────
852
+ // Walk every phase directory under projects/<project>/phases/ (or
853
+ // top-level phases/ in v1) and report any uncommitted PLAN.md /
854
+ // CONTEXT.md / RESEARCH.md / UAT.md / VERIFICATION.md files. This is
855
+ // the loud-fail safety net for REL-01 / REL-02 — if either of those
856
+ // commit-step contracts ever regresses, this health check surfaces
857
+ // the dangling artifact on the next /dgs:health run.
858
+ try {
859
+ const untracked = [];
860
+ if (fs.existsSync(phasesDir)) {
861
+ const ARTIFACT_RE = /^[0-9].*-(PLAN|CONTEXT|RESEARCH|UAT|VERIFICATION)\.md$/;
862
+ const phaseEntries = fs.readdirSync(phasesDir, { withFileTypes: true });
863
+ for (const entry of phaseEntries) {
864
+ if (!entry.isDirectory()) continue;
865
+ const phaseDirAbs = path.join(phasesDir, entry.name);
866
+ let files;
867
+ try { files = fs.readdirSync(phaseDirAbs); } catch { continue; }
868
+ for (const f of files) {
869
+ if (!ARTIFACT_RE.test(f)) continue;
870
+ const absPath = path.join(phaseDirAbs, f);
871
+ const relPath = path.relative(planningDir, absPath);
872
+ // `git ls-files -- <relPath>` returns the path if tracked,
873
+ // empty string if untracked. Run with cwd = planningDir so
874
+ // the relative path resolves correctly.
875
+ const lsResult = execGit(planningDir, ['ls-files', '--', relPath]);
876
+ const tracked = lsResult.exitCode === 0 && (lsResult.stdout || '').trim().length > 0;
877
+ if (!tracked) untracked.push(relPath);
878
+ }
879
+ }
880
+ }
881
+ if (untracked.length > 0) {
882
+ const remediation =
883
+ `Run 'node $TOOLS commit "docs: catch up untracked phase artifacts" --files ${untracked.join(' ')}' ` +
884
+ `or run /dgs:health --repair`;
885
+ addIssue(
886
+ 'warning',
887
+ 'W010',
888
+ `untracked-phase-artifacts: ${untracked.length} uncommitted phase artifact(s) — ${untracked.join(', ')}`,
889
+ remediation,
890
+ false
891
+ );
892
+ }
893
+ } catch (err) {
894
+ addIssue(
895
+ 'warning',
896
+ 'W011',
897
+ `untracked-phase-artifacts check failed: ${err.message}`,
898
+ 'Investigate verify.cjs walker',
899
+ false
900
+ );
901
+ }
902
+
711
903
  // ─── Perform repairs if requested ─────────────────────────────────────────
712
904
  const repairActions = [];
713
905
  if (options.repair && repairs.length > 0) {
@@ -720,7 +912,6 @@ function cmdValidateHealth(cwd, options, raw) {
720
912
  model_profile: 'balanced',
721
913
  commit_docs: true,
722
914
  search_gitignored: false,
723
- branching_strategy: 'none',
724
915
  research: true,
725
916
  plan_checker: true,
726
917
  verifier: true,
@@ -0,0 +1,82 @@
1
+ // deliver-great-systems/bin/lib/verify.test.cjs
2
+ // REL-12 untracked-scaffolding health check — initially RED. Turns GREEN after plan 04
3
+ // adds Check 11 to bin/lib/verify.cjs::cmdValidateHealth.
4
+
5
+ const test = require('node:test');
6
+ const assert = require('node:assert');
7
+ const fs = require('node:fs');
8
+ const path = require('node:path');
9
+ const os = require('node:os');
10
+ const { execSync } = require('node:child_process');
11
+
12
+ function setupREL12Fixture({ trackedKeeps, untrackedKeeps }) {
13
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'rel12-'));
14
+ execSync('git init -q', { cwd: root });
15
+ execSync('git config user.email test@test', { cwd: root });
16
+ execSync('git config user.name test', { cwd: root });
17
+ fs.writeFileSync(path.join(root, 'PROJECT.md'),
18
+ '# Test\n## What This Is\n## Core Value\n## Requirements\n');
19
+ fs.writeFileSync(path.join(root, 'ROADMAP.md'), '# Roadmap\n');
20
+ fs.writeFileSync(path.join(root, 'STATE.md'), '# State\n');
21
+ fs.mkdirSync(path.join(root, 'phases'), { recursive: true });
22
+ // Create + track requested .gitkeep paths
23
+ for (const p of trackedKeeps) {
24
+ fs.mkdirSync(path.join(root, path.dirname(p)), { recursive: true });
25
+ fs.writeFileSync(path.join(root, p), '');
26
+ execSync(`git add "${p}"`, { cwd: root });
27
+ }
28
+ // Always commit at least PROJECT.md so HEAD exists even when no keeps are tracked
29
+ execSync('git add PROJECT.md ROADMAP.md STATE.md', { cwd: root });
30
+ execSync('git commit -q -m "init"', { cwd: root });
31
+ // Create untracked .gitkeep paths (not added to git)
32
+ for (const p of untrackedKeeps) {
33
+ fs.mkdirSync(path.join(root, path.dirname(p)), { recursive: true });
34
+ fs.writeFileSync(path.join(root, p), '');
35
+ }
36
+ return root;
37
+ }
38
+
39
+ function captureHealthOutput(root) {
40
+ // cmdValidateHealth calls output() which process.exit(0)s — run in a child
41
+ // process and capture stdout JSON. Pattern matches health-untracked-phase.test.cjs.
42
+ const verifyPath = path.resolve(__dirname, 'verify.cjs');
43
+ const shim = [
44
+ `const v = require(${JSON.stringify(verifyPath)});`,
45
+ `v.cmdValidateHealth(${JSON.stringify(root)}, { raw: true }, true);`,
46
+ ].join('\n');
47
+ const shimPath = path.join(os.tmpdir(), `rel12-shim-${process.pid}-${Date.now()}.cjs`);
48
+ fs.writeFileSync(shimPath, shim);
49
+ try {
50
+ const stdout = execSync(`node ${JSON.stringify(shimPath)}`, { encoding: 'utf-8' });
51
+ try { return JSON.parse(stdout); } catch { return { stdout, warnings: [] }; }
52
+ } catch (err) {
53
+ const out = err.stdout && err.stdout.toString();
54
+ try { return JSON.parse(out); } catch { return { stdout: out, warnings: [] }; }
55
+ } finally {
56
+ try { fs.unlinkSync(shimPath); } catch { /* ignore */ }
57
+ }
58
+ }
59
+
60
+ test('REL-12: cmdValidateHealth flags untracked .gitkeep in standard scaffold locations (untracked-scaffolding)', () => {
61
+ const root = setupREL12Fixture({
62
+ trackedKeeps: ['specs/.gitkeep'],
63
+ untrackedKeeps: ['docs/product/.gitkeep', 'quick/.gitkeep'],
64
+ });
65
+ const result = captureHealthOutput(root);
66
+ const scaffoldingWarnings = (result.warnings || []).filter(w => w.code === 'untracked-scaffolding');
67
+ assert.strictEqual(scaffoldingWarnings.length, 1, 'exactly one untracked-scaffolding warning expected');
68
+ assert.match(scaffoldingWarnings[0].message, /docs\/product\/\.gitkeep/);
69
+ assert.match(scaffoldingWarnings[0].message, /quick\/\.gitkeep/);
70
+ });
71
+
72
+ test('REL-12: cmdValidateHealth does NOT flag tracked .gitkeep files', () => {
73
+ const root = setupREL12Fixture({
74
+ trackedKeeps: ['specs/.gitkeep', 'docs/product/.gitkeep', 'quick/.gitkeep'],
75
+ untrackedKeeps: [],
76
+ });
77
+ const result = captureHealthOutput(root);
78
+ const scaffoldingWarnings = (result.warnings || []).filter(w => w.code === 'untracked-scaffolding');
79
+ assert.strictEqual(scaffoldingWarnings.length, 0, 'no untracked-scaffolding warning when all keeps tracked');
80
+ });
81
+
82
+ // REL-12 sentinel — flag this block as a Wave-0 RED scaffold for plan 04.
@@ -0,0 +1,40 @@
1
+ // deliver-great-systems/bin/lib/wave-0-template-rename.test.cjs
2
+ // REL-03 convergence test scaffold — initially RED. Turns GREEN after plan 02
3
+ // lands the planner-prompt edits and the VALIDATION.md template rename.
4
+
5
+ const test = require('node:test');
6
+ const assert = require('node:assert');
7
+ const fs = require('node:fs');
8
+ const path = require('node:path');
9
+
10
+ const repoRoot = path.resolve(__dirname, '../../..');
11
+ const planner = path.join(repoRoot, 'agents/dgs-planner.md');
12
+ const template = path.join(repoRoot, 'deliver-great-systems/templates/VALIDATION.md');
13
+
14
+ test('REL-03: VALIDATION template uses heading "Wave 0 Task Requirements"', () => {
15
+ const content = fs.readFileSync(template, 'utf-8');
16
+ assert.match(content, /## Wave 0 Task Requirements/, 'template must use new heading');
17
+ });
18
+
19
+ test('REL-03: VALIDATION template no longer uses "Wave 0 Requirements" as a section heading', () => {
20
+ const content = fs.readFileSync(template, 'utf-8');
21
+ // The OLD heading must NOT exist as a section heading (paragraph mentions are OK)
22
+ assert.doesNotMatch(content, /^## Wave 0 Requirements\s*$/m, 'old heading must be gone');
23
+ });
24
+
25
+ test('REL-03: planner prompt unambiguously states "Plans are numbered starting at 01"', () => {
26
+ const content = fs.readFileSync(planner, 'utf-8');
27
+ assert.match(content, /Plans are numbered starting at 01/, 'planner must state plan-numbering rule');
28
+ });
29
+
30
+ test('REL-03: planner prompt explicitly forbids plan: 00 and wave: 0', () => {
31
+ const content = fs.readFileSync(planner, 'utf-8');
32
+ assert.match(content, /Never create a plan with `plan: 00` or `wave: 0`/, 'planner must forbid plan: 00 / wave: 0');
33
+ });
34
+
35
+ test('REL-03: planner prompt mentions Wave 0 lives as Task 0 inside plan 01', () => {
36
+ const content = fs.readFileSync(planner, 'utf-8');
37
+ assert.match(content, /Wave 0 work lives as Task 0 inside plan 01/, 'planner must locate Wave 0 inside plan 01');
38
+ });
39
+
40
+ // REL-03 sentinel — flag this file as a Wave-0 RED scaffold for plan 02.