@ktpartners/dgs-platform 2.6.2

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 (256) hide show
  1. package/LICENSE +38 -0
  2. package/README.md +851 -0
  3. package/agents/dgs-codebase-cross-analyzer.md +183 -0
  4. package/agents/dgs-codebase-mapper.md +782 -0
  5. package/agents/dgs-codebase-synthesizer.md +156 -0
  6. package/agents/dgs-debugger.md +1256 -0
  7. package/agents/dgs-executor.md +550 -0
  8. package/agents/dgs-integration-checker.md +481 -0
  9. package/agents/dgs-nyquist-auditor.md +178 -0
  10. package/agents/dgs-phase-researcher.md +563 -0
  11. package/agents/dgs-phase-verifier.md +450 -0
  12. package/agents/dgs-plan-checker.md +708 -0
  13. package/agents/dgs-planner.md +1324 -0
  14. package/agents/dgs-project-researcher.md +631 -0
  15. package/agents/dgs-research-synthesizer.md +249 -0
  16. package/agents/dgs-roadmapper.md +652 -0
  17. package/agents/dgs-verifier.md +607 -0
  18. package/bin/install.js +2073 -0
  19. package/commands/dgs/add-doc.md +45 -0
  20. package/commands/dgs/add-idea.md +38 -0
  21. package/commands/dgs/add-phase.md +43 -0
  22. package/commands/dgs/add-repo.md +54 -0
  23. package/commands/dgs/add-tests.md +41 -0
  24. package/commands/dgs/add-todo.md +47 -0
  25. package/commands/dgs/approve-spec.md +38 -0
  26. package/commands/dgs/audit-milestone.md +36 -0
  27. package/commands/dgs/audit-phase.md +37 -0
  28. package/commands/dgs/cancel-job.md +23 -0
  29. package/commands/dgs/capture-principle.md +143 -0
  30. package/commands/dgs/check-todos.md +45 -0
  31. package/commands/dgs/cleanup.md +18 -0
  32. package/commands/dgs/complete-milestone.md +136 -0
  33. package/commands/dgs/complete-project.md +70 -0
  34. package/commands/dgs/consolidate-ideas.md +50 -0
  35. package/commands/dgs/create-milestone-job.md +37 -0
  36. package/commands/dgs/debug.md +164 -0
  37. package/commands/dgs/develop-idea.md +53 -0
  38. package/commands/dgs/discuss-idea.md +41 -0
  39. package/commands/dgs/discuss-phase.md +83 -0
  40. package/commands/dgs/execute-phase.md +41 -0
  41. package/commands/dgs/fast.md +38 -0
  42. package/commands/dgs/find-related-ideas.md +43 -0
  43. package/commands/dgs/health.md +28 -0
  44. package/commands/dgs/help.md +22 -0
  45. package/commands/dgs/import-spec.md +36 -0
  46. package/commands/dgs/init-product.md +28 -0
  47. package/commands/dgs/insert-phase.md +32 -0
  48. package/commands/dgs/join-discord.md +18 -0
  49. package/commands/dgs/list-docs.md +40 -0
  50. package/commands/dgs/list-ideas.md +42 -0
  51. package/commands/dgs/list-jobs.md +22 -0
  52. package/commands/dgs/list-phase-assumptions.md +46 -0
  53. package/commands/dgs/list-projects.md +57 -0
  54. package/commands/dgs/list-specs.md +40 -0
  55. package/commands/dgs/map-codebase.md +92 -0
  56. package/commands/dgs/new-milestone.md +44 -0
  57. package/commands/dgs/new-project.md +42 -0
  58. package/commands/dgs/node-repair.md +26 -0
  59. package/commands/dgs/overlap-check.md +20 -0
  60. package/commands/dgs/pause-work.md +38 -0
  61. package/commands/dgs/plan-milestone-gaps.md +34 -0
  62. package/commands/dgs/plan-phase.md +44 -0
  63. package/commands/dgs/progress.md +24 -0
  64. package/commands/dgs/quick.md +41 -0
  65. package/commands/dgs/reactivate-project.md +70 -0
  66. package/commands/dgs/reapply-patches.md +110 -0
  67. package/commands/dgs/refine-spec.md +38 -0
  68. package/commands/dgs/reject-idea.md +43 -0
  69. package/commands/dgs/remove-doc.md +44 -0
  70. package/commands/dgs/remove-phase.md +31 -0
  71. package/commands/dgs/remove-repo.md +69 -0
  72. package/commands/dgs/research-idea.md +43 -0
  73. package/commands/dgs/research-phase.md +189 -0
  74. package/commands/dgs/restore-idea.md +45 -0
  75. package/commands/dgs/resume-work.md +40 -0
  76. package/commands/dgs/rollback-job.md +24 -0
  77. package/commands/dgs/run-job.md +35 -0
  78. package/commands/dgs/search.md +40 -0
  79. package/commands/dgs/set-profile.md +34 -0
  80. package/commands/dgs/settings.md +38 -0
  81. package/commands/dgs/switch-project.md +58 -0
  82. package/commands/dgs/undo-consolidation.md +42 -0
  83. package/commands/dgs/update-idea.md +44 -0
  84. package/commands/dgs/update.md +37 -0
  85. package/commands/dgs/validate-phase.md +35 -0
  86. package/commands/dgs/verify-work.md +39 -0
  87. package/commands/dgs/write-spec.md +49 -0
  88. package/deliver-great-systems/.planning/phases/09-backend-wiring-and-error-handling/09-01-SUMMARY.md +84 -0
  89. package/deliver-great-systems/.planning/phases/09-backend-wiring-and-error-handling/09-02-SUMMARY.md +86 -0
  90. package/deliver-great-systems/.planning/phases/10-v1-to-v2-migration-flow/10-01-SUMMARY.md +85 -0
  91. package/deliver-great-systems/bin/dgs-tools.cjs +1444 -0
  92. package/deliver-great-systems/bin/lib/auto-test.cjs +1365 -0
  93. package/deliver-great-systems/bin/lib/commands.cjs +570 -0
  94. package/deliver-great-systems/bin/lib/config.cjs +417 -0
  95. package/deliver-great-systems/bin/lib/conflict-agent.cjs +1063 -0
  96. package/deliver-great-systems/bin/lib/conflict-agent.test.cjs +554 -0
  97. package/deliver-great-systems/bin/lib/context.cjs +929 -0
  98. package/deliver-great-systems/bin/lib/context.test.cjs +693 -0
  99. package/deliver-great-systems/bin/lib/core.cjs +744 -0
  100. package/deliver-great-systems/bin/lib/core.test.cjs +822 -0
  101. package/deliver-great-systems/bin/lib/docs.cjs +919 -0
  102. package/deliver-great-systems/bin/lib/docs.test.cjs +211 -0
  103. package/deliver-great-systems/bin/lib/execution.cjs +705 -0
  104. package/deliver-great-systems/bin/lib/execution.test.cjs +1472 -0
  105. package/deliver-great-systems/bin/lib/frontmatter.cjs +324 -0
  106. package/deliver-great-systems/bin/lib/ideas.cjs +1406 -0
  107. package/deliver-great-systems/bin/lib/ideas.test.cjs +1417 -0
  108. package/deliver-great-systems/bin/lib/identity.cjs +125 -0
  109. package/deliver-great-systems/bin/lib/init.cjs +1114 -0
  110. package/deliver-great-systems/bin/lib/init.test.cjs +1271 -0
  111. package/deliver-great-systems/bin/lib/jobs.cjs +2015 -0
  112. package/deliver-great-systems/bin/lib/jobs.test.cjs +2619 -0
  113. package/deliver-great-systems/bin/lib/merge-conflicts.cjs +654 -0
  114. package/deliver-great-systems/bin/lib/merge-conflicts.test.cjs +370 -0
  115. package/deliver-great-systems/bin/lib/migration.cjs +352 -0
  116. package/deliver-great-systems/bin/lib/migration.test.cjs +582 -0
  117. package/deliver-great-systems/bin/lib/milestone.cjs +243 -0
  118. package/deliver-great-systems/bin/lib/overlap.cjs +437 -0
  119. package/deliver-great-systems/bin/lib/overlap.test.cjs +747 -0
  120. package/deliver-great-systems/bin/lib/path-audit.test.cjs +384 -0
  121. package/deliver-great-systems/bin/lib/paths.cjs +144 -0
  122. package/deliver-great-systems/bin/lib/paths.test.cjs +486 -0
  123. package/deliver-great-systems/bin/lib/phase.cjs +910 -0
  124. package/deliver-great-systems/bin/lib/projects.cjs +691 -0
  125. package/deliver-great-systems/bin/lib/projects.test.cjs +871 -0
  126. package/deliver-great-systems/bin/lib/repos.cjs +1432 -0
  127. package/deliver-great-systems/bin/lib/repos.test.cjs +1882 -0
  128. package/deliver-great-systems/bin/lib/roadmap.cjs +305 -0
  129. package/deliver-great-systems/bin/lib/search.cjs +570 -0
  130. package/deliver-great-systems/bin/lib/specs.cjs +1303 -0
  131. package/deliver-great-systems/bin/lib/state.cjs +893 -0
  132. package/deliver-great-systems/bin/lib/template.cjs +228 -0
  133. package/deliver-great-systems/bin/lib/test-helpers.cjs +291 -0
  134. package/deliver-great-systems/bin/lib/verify.cjs +796 -0
  135. package/deliver-great-systems/references/checkpoints.md +776 -0
  136. package/deliver-great-systems/references/conflict-resolution.md +66 -0
  137. package/deliver-great-systems/references/context-tiers.md +166 -0
  138. package/deliver-great-systems/references/continuation-format.md +249 -0
  139. package/deliver-great-systems/references/decimal-phase-calculation.md +67 -0
  140. package/deliver-great-systems/references/git-integration.md +250 -0
  141. package/deliver-great-systems/references/git-planning-commit.md +40 -0
  142. package/deliver-great-systems/references/model-profile-resolution.md +36 -0
  143. package/deliver-great-systems/references/model-profiles.md +95 -0
  144. package/deliver-great-systems/references/phase-argument-parsing.md +61 -0
  145. package/deliver-great-systems/references/planning-config.md +224 -0
  146. package/deliver-great-systems/references/questioning.md +162 -0
  147. package/deliver-great-systems/references/spec-review-loop.md +177 -0
  148. package/deliver-great-systems/references/tdd.md +265 -0
  149. package/deliver-great-systems/references/ui-brand.md +160 -0
  150. package/deliver-great-systems/references/verification-patterns.md +612 -0
  151. package/deliver-great-systems/templates/DEBUG.md +166 -0
  152. package/deliver-great-systems/templates/UAT.md +251 -0
  153. package/deliver-great-systems/templates/VALIDATION.md +95 -0
  154. package/deliver-great-systems/templates/claude-md.md +74 -0
  155. package/deliver-great-systems/templates/codebase/architecture.md +257 -0
  156. package/deliver-great-systems/templates/codebase/concerns.md +312 -0
  157. package/deliver-great-systems/templates/codebase/conventions.md +309 -0
  158. package/deliver-great-systems/templates/codebase/integrations.md +282 -0
  159. package/deliver-great-systems/templates/codebase/stack.md +188 -0
  160. package/deliver-great-systems/templates/codebase/structure.md +287 -0
  161. package/deliver-great-systems/templates/codebase/testing.md +482 -0
  162. package/deliver-great-systems/templates/config.json +38 -0
  163. package/deliver-great-systems/templates/context.md +354 -0
  164. package/deliver-great-systems/templates/continue-here.md +80 -0
  165. package/deliver-great-systems/templates/debug-subagent-prompt.md +93 -0
  166. package/deliver-great-systems/templates/discovery.md +148 -0
  167. package/deliver-great-systems/templates/milestone-archive.md +125 -0
  168. package/deliver-great-systems/templates/milestone.md +117 -0
  169. package/deliver-great-systems/templates/phase-prompt.md +615 -0
  170. package/deliver-great-systems/templates/planner-subagent-prompt.md +119 -0
  171. package/deliver-great-systems/templates/project.md +186 -0
  172. package/deliver-great-systems/templates/requirements.md +233 -0
  173. package/deliver-great-systems/templates/research-project/ARCHITECTURE.md +206 -0
  174. package/deliver-great-systems/templates/research-project/FEATURES.md +149 -0
  175. package/deliver-great-systems/templates/research-project/PITFALLS.md +202 -0
  176. package/deliver-great-systems/templates/research-project/STACK.md +122 -0
  177. package/deliver-great-systems/templates/research-project/SUMMARY.md +172 -0
  178. package/deliver-great-systems/templates/research.md +554 -0
  179. package/deliver-great-systems/templates/retrospective.md +54 -0
  180. package/deliver-great-systems/templates/roadmap.md +204 -0
  181. package/deliver-great-systems/templates/state.md +178 -0
  182. package/deliver-great-systems/templates/summary-complex.md +59 -0
  183. package/deliver-great-systems/templates/summary-minimal.md +41 -0
  184. package/deliver-great-systems/templates/summary-standard.md +48 -0
  185. package/deliver-great-systems/templates/summary.md +253 -0
  186. package/deliver-great-systems/templates/user-setup.md +313 -0
  187. package/deliver-great-systems/templates/verification-report.md +324 -0
  188. package/deliver-great-systems/workflows/add-doc.md +151 -0
  189. package/deliver-great-systems/workflows/add-idea.md +96 -0
  190. package/deliver-great-systems/workflows/add-phase.md +120 -0
  191. package/deliver-great-systems/workflows/add-tests.md +359 -0
  192. package/deliver-great-systems/workflows/add-todo.md +162 -0
  193. package/deliver-great-systems/workflows/approve-spec.md +194 -0
  194. package/deliver-great-systems/workflows/audit-milestone.md +364 -0
  195. package/deliver-great-systems/workflows/audit-phase.md +462 -0
  196. package/deliver-great-systems/workflows/cancel-job.md +108 -0
  197. package/deliver-great-systems/workflows/check-todos.md +181 -0
  198. package/deliver-great-systems/workflows/cleanup.md +247 -0
  199. package/deliver-great-systems/workflows/codereview.md +526 -0
  200. package/deliver-great-systems/workflows/complete-milestone.md +1298 -0
  201. package/deliver-great-systems/workflows/consolidate-ideas.md +365 -0
  202. package/deliver-great-systems/workflows/create-milestone-job.md +177 -0
  203. package/deliver-great-systems/workflows/develop-idea.md +544 -0
  204. package/deliver-great-systems/workflows/diagnose-issues.md +231 -0
  205. package/deliver-great-systems/workflows/discovery-phase.md +301 -0
  206. package/deliver-great-systems/workflows/discuss-idea.md +263 -0
  207. package/deliver-great-systems/workflows/discuss-phase.md +733 -0
  208. package/deliver-great-systems/workflows/execute-phase.md +571 -0
  209. package/deliver-great-systems/workflows/execute-plan.md +592 -0
  210. package/deliver-great-systems/workflows/find-related-ideas.md +271 -0
  211. package/deliver-great-systems/workflows/health.md +173 -0
  212. package/deliver-great-systems/workflows/help.md +997 -0
  213. package/deliver-great-systems/workflows/import-spec.md +381 -0
  214. package/deliver-great-systems/workflows/init-product.md +767 -0
  215. package/deliver-great-systems/workflows/insert-phase.md +138 -0
  216. package/deliver-great-systems/workflows/list-docs.md +119 -0
  217. package/deliver-great-systems/workflows/list-ideas.md +154 -0
  218. package/deliver-great-systems/workflows/list-jobs.md +89 -0
  219. package/deliver-great-systems/workflows/list-phase-assumptions.md +192 -0
  220. package/deliver-great-systems/workflows/list-specs.md +101 -0
  221. package/deliver-great-systems/workflows/map-codebase.md +621 -0
  222. package/deliver-great-systems/workflows/new-milestone.md +591 -0
  223. package/deliver-great-systems/workflows/new-project.md +1113 -0
  224. package/deliver-great-systems/workflows/node-repair.md +94 -0
  225. package/deliver-great-systems/workflows/overlap-check.md +86 -0
  226. package/deliver-great-systems/workflows/pause-work.md +134 -0
  227. package/deliver-great-systems/workflows/plan-milestone-gaps.md +306 -0
  228. package/deliver-great-systems/workflows/plan-phase.md +698 -0
  229. package/deliver-great-systems/workflows/progress.md +386 -0
  230. package/deliver-great-systems/workflows/quick.md +845 -0
  231. package/deliver-great-systems/workflows/refine-spec.md +275 -0
  232. package/deliver-great-systems/workflows/reject-idea.md +109 -0
  233. package/deliver-great-systems/workflows/remove-doc.md +117 -0
  234. package/deliver-great-systems/workflows/remove-phase.md +163 -0
  235. package/deliver-great-systems/workflows/research-idea.md +325 -0
  236. package/deliver-great-systems/workflows/research-phase.md +81 -0
  237. package/deliver-great-systems/workflows/restore-idea.md +101 -0
  238. package/deliver-great-systems/workflows/resume-project.md +311 -0
  239. package/deliver-great-systems/workflows/rollback-job.md +130 -0
  240. package/deliver-great-systems/workflows/run-job.md +498 -0
  241. package/deliver-great-systems/workflows/search.md +130 -0
  242. package/deliver-great-systems/workflows/set-profile.md +83 -0
  243. package/deliver-great-systems/workflows/settings.md +470 -0
  244. package/deliver-great-systems/workflows/transition.md +563 -0
  245. package/deliver-great-systems/workflows/undo-consolidation.md +155 -0
  246. package/deliver-great-systems/workflows/update-idea.md +157 -0
  247. package/deliver-great-systems/workflows/update.md +242 -0
  248. package/deliver-great-systems/workflows/validate-phase.md +177 -0
  249. package/deliver-great-systems/workflows/verify-phase.md +253 -0
  250. package/deliver-great-systems/workflows/verify-work.md +671 -0
  251. package/deliver-great-systems/workflows/write-spec.md +450 -0
  252. package/hooks/dist/dgs-check-update.js +62 -0
  253. package/hooks/dist/dgs-context-monitor.js +141 -0
  254. package/hooks/dist/dgs-statusline.js +115 -0
  255. package/package.json +60 -0
  256. package/scripts/build-hooks.js +43 -0
@@ -0,0 +1,486 @@
1
+ /**
2
+ * Tests for paths.cjs — Planning root detection and PATHS singleton
3
+ *
4
+ * Uses Node.js built-in test runner (node:test) and assert (node:assert/strict).
5
+ * Each test creates an isolated temp directory fixture and cleans up after.
6
+ *
7
+ * Covers PATH-01 through PATH-05 requirements.
8
+ */
9
+
10
+ const { describe, it, afterEach } = require('node:test');
11
+ const assert = require('node:assert/strict');
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const os = require('os');
15
+
16
+ const { getPlanningRoot, getPaths, initPaths, resetPaths, PROJECTS_DIR } = require('./paths.cjs');
17
+
18
+ // ─── Helpers ────────────────────────────────────────────────────────────────
19
+
20
+ function makeTempDir() {
21
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'dgs-paths-test-'));
22
+ }
23
+
24
+ function removeTempDir(dir) {
25
+ fs.rmSync(dir, { recursive: true, force: true });
26
+ }
27
+
28
+ // ─── 1. Detection cascade — .planning/ layout (PATH-01, PATH-02) ────────────
29
+
30
+ describe('detection cascade — .planning/ layout', () => {
31
+ let cwd;
32
+
33
+ afterEach(() => {
34
+ resetPaths();
35
+ if (cwd) removeTempDir(cwd);
36
+ cwd = null;
37
+ });
38
+
39
+ it('detects .planning/ layout from .planning/dgs.config.json', () => {
40
+ cwd = makeTempDir();
41
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
42
+ fs.writeFileSync(path.join(cwd, '.planning', 'dgs.config.json'), '{}');
43
+
44
+ const root = getPlanningRoot(cwd);
45
+ assert.equal(root, path.join(cwd, '.planning'));
46
+ });
47
+
48
+ it('detects .planning/ layout from .planning/config.json', () => {
49
+ cwd = makeTempDir();
50
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
51
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
52
+
53
+ const root = getPlanningRoot(cwd);
54
+ assert.equal(root, path.join(cwd, '.planning'));
55
+ });
56
+
57
+ it('detects .planning/ layout when both config files exist', () => {
58
+ cwd = makeTempDir();
59
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
60
+ fs.writeFileSync(path.join(cwd, '.planning', 'dgs.config.json'), '{}');
61
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
62
+
63
+ const root = getPlanningRoot(cwd);
64
+ assert.equal(root, path.join(cwd, '.planning'));
65
+ });
66
+ });
67
+
68
+ // ─── 2. Detection cascade — root layout (PATH-01, PATH-02) ──────────────────
69
+
70
+ describe('detection cascade — root layout', () => {
71
+ let cwd;
72
+
73
+ afterEach(() => {
74
+ resetPaths();
75
+ if (cwd) removeTempDir(cwd);
76
+ cwd = null;
77
+ });
78
+
79
+ it('detects root layout from dgs.config.json with planningRoot "."', () => {
80
+ cwd = makeTempDir();
81
+ fs.writeFileSync(
82
+ path.join(cwd, 'dgs.config.json'),
83
+ JSON.stringify({ planningRoot: '.' })
84
+ );
85
+
86
+ const root = getPlanningRoot(cwd);
87
+ assert.equal(root, cwd);
88
+ });
89
+
90
+ it('returns .planning/ when root dgs.config.json has no planningRoot field', () => {
91
+ cwd = makeTempDir();
92
+ fs.writeFileSync(
93
+ path.join(cwd, 'dgs.config.json'),
94
+ JSON.stringify({ mode: 'interactive' })
95
+ );
96
+
97
+ const root = getPlanningRoot(cwd);
98
+ assert.equal(root, path.join(cwd, '.planning'));
99
+ });
100
+
101
+ it('falls through when root dgs.config.json has malformed JSON', () => {
102
+ cwd = makeTempDir();
103
+ fs.writeFileSync(path.join(cwd, 'dgs.config.json'), '{not valid json!!!');
104
+
105
+ // Should NOT crash — falls through to default
106
+ const root = getPlanningRoot(cwd);
107
+ // Malformed config falls through; no PROJECT.md => default .planning/
108
+ assert.equal(root, path.join(cwd, '.planning'));
109
+ });
110
+
111
+ it('auto-detects root layout when PROJECT.md exists at root with no .planning/ dir', () => {
112
+ cwd = makeTempDir();
113
+ fs.writeFileSync(path.join(cwd, 'PROJECT.md'), '# Project: Test');
114
+
115
+ const root = getPlanningRoot(cwd);
116
+ assert.equal(root, cwd);
117
+ });
118
+
119
+ it('returns .planning/ when PROJECT.md exists at root AND .planning/ directory exists', () => {
120
+ cwd = makeTempDir();
121
+ fs.writeFileSync(path.join(cwd, 'PROJECT.md'), '# Project: Test');
122
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
123
+
124
+ const root = getPlanningRoot(cwd);
125
+ assert.equal(root, path.join(cwd, '.planning'));
126
+ });
127
+ });
128
+
129
+ // ─── 3. Detection cascade — default (PATH-02) ───────────────────────────────
130
+
131
+ describe('detection cascade — default', () => {
132
+ let cwd;
133
+
134
+ afterEach(() => {
135
+ resetPaths();
136
+ if (cwd) removeTempDir(cwd);
137
+ cwd = null;
138
+ });
139
+
140
+ it('defaults to .planning/ when no signals are found', () => {
141
+ cwd = makeTempDir();
142
+
143
+ const root = getPlanningRoot(cwd);
144
+ assert.equal(root, path.join(cwd, '.planning'));
145
+ });
146
+ });
147
+
148
+ // ─── 4. Detection cascade — priority (PATH-02) ──────────────────────────────
149
+
150
+ describe('detection cascade — priority', () => {
151
+ let cwd;
152
+
153
+ afterEach(() => {
154
+ resetPaths();
155
+ if (cwd) removeTempDir(cwd);
156
+ cwd = null;
157
+ });
158
+
159
+ it('.planning/config.json takes precedence over root dgs.config.json with planningRoot "."', () => {
160
+ cwd = makeTempDir();
161
+ // .planning/ layout signal
162
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
163
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
164
+ // Root layout signal
165
+ fs.writeFileSync(
166
+ path.join(cwd, 'dgs.config.json'),
167
+ JSON.stringify({ planningRoot: '.' })
168
+ );
169
+
170
+ const root = getPlanningRoot(cwd);
171
+ assert.equal(root, path.join(cwd, '.planning'));
172
+ });
173
+ });
174
+
175
+ // ─── 5. PATHS object shape (PATH-03) ────────────────────────────────────────
176
+
177
+ describe('PATHS object shape', () => {
178
+ let cwd;
179
+
180
+ afterEach(() => {
181
+ resetPaths();
182
+ if (cwd) removeTempDir(cwd);
183
+ cwd = null;
184
+ });
185
+
186
+ const EXPECTED_KEYS = [
187
+ 'ROOT', 'PHASES', 'IDEAS', 'SPECS', 'JOBS', 'DOCS',
188
+ 'CODEBASE', 'MILESTONES', 'CONFIG', 'CONFIG_LEGACY',
189
+ 'QUICK', 'TODOS', 'RESEARCH', 'DEBUG', 'ARCHIVE', 'PROJECTS', 'LAYOUT',
190
+ ];
191
+
192
+ it('returns object with all 17 expected keys', () => {
193
+ cwd = makeTempDir();
194
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
195
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
196
+
197
+ const paths = getPaths(cwd);
198
+ assert.deepEqual(Object.keys(paths).sort(), EXPECTED_KEYS.slice().sort());
199
+ });
200
+
201
+ it('for .planning/ layout: ROOT ends with .planning and LAYOUT is dotplanning', () => {
202
+ cwd = makeTempDir();
203
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
204
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
205
+
206
+ const paths = getPaths(cwd);
207
+ assert.ok(paths.ROOT.endsWith('.planning'));
208
+ assert.equal(paths.LAYOUT, 'dotplanning');
209
+ });
210
+
211
+ it('for root layout: ROOT equals cwd and LAYOUT is root', () => {
212
+ cwd = makeTempDir();
213
+ fs.writeFileSync(
214
+ path.join(cwd, 'dgs.config.json'),
215
+ JSON.stringify({ planningRoot: '.' })
216
+ );
217
+
218
+ const paths = getPaths(cwd);
219
+ assert.equal(paths.ROOT, cwd);
220
+ assert.equal(paths.LAYOUT, 'root');
221
+ });
222
+
223
+ it('all path values (except LAYOUT) are absolute', () => {
224
+ cwd = makeTempDir();
225
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
226
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
227
+
228
+ const paths = getPaths(cwd);
229
+ for (const [key, value] of Object.entries(paths)) {
230
+ if (key === 'LAYOUT') continue;
231
+ assert.ok(
232
+ path.isAbsolute(value),
233
+ `Expected ${key} to be absolute, got: ${value}`
234
+ );
235
+ }
236
+ });
237
+
238
+ it('CONFIG ends with dgs.config.json', () => {
239
+ cwd = makeTempDir();
240
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
241
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
242
+
243
+ const paths = getPaths(cwd);
244
+ assert.ok(paths.CONFIG.endsWith('dgs.config.json'));
245
+ });
246
+
247
+ it('CONFIG_LEGACY ends with config.json', () => {
248
+ cwd = makeTempDir();
249
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
250
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
251
+
252
+ const paths = getPaths(cwd);
253
+ assert.ok(paths.CONFIG_LEGACY.endsWith('config.json'));
254
+ });
255
+
256
+ it('subdirectory paths are derived from ROOT', () => {
257
+ cwd = makeTempDir();
258
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
259
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
260
+
261
+ const paths = getPaths(cwd);
262
+ assert.equal(paths.PHASES, path.join(paths.ROOT, 'phases'));
263
+ assert.equal(paths.IDEAS, path.join(paths.ROOT, 'ideas'));
264
+ assert.equal(paths.SPECS, path.join(paths.ROOT, 'specs'));
265
+ assert.equal(paths.JOBS, path.join(paths.ROOT, 'jobs'));
266
+ assert.equal(paths.DOCS, path.join(paths.ROOT, 'docs'));
267
+ assert.equal(paths.CODEBASE, path.join(paths.ROOT, 'codebase'));
268
+ assert.equal(paths.MILESTONES, path.join(paths.ROOT, 'milestones'));
269
+ assert.equal(paths.QUICK, path.join(paths.ROOT, 'quick'));
270
+ assert.equal(paths.TODOS, path.join(paths.ROOT, 'todos'));
271
+ assert.equal(paths.RESEARCH, path.join(paths.ROOT, 'research'));
272
+ assert.equal(paths.DEBUG, path.join(paths.ROOT, 'debug'));
273
+ assert.equal(paths.ARCHIVE, path.join(paths.ROOT, 'archive'));
274
+ assert.equal(paths.PROJECTS, path.join(paths.ROOT, 'projects'));
275
+ });
276
+ });
277
+
278
+ // ─── 6. Object.freeze (PATH-03) ─────────────────────────────────────────────
279
+
280
+ describe('Object.freeze', () => {
281
+ let cwd;
282
+
283
+ afterEach(() => {
284
+ resetPaths();
285
+ if (cwd) removeTempDir(cwd);
286
+ cwd = null;
287
+ });
288
+
289
+ it('getPaths returns a frozen object', () => {
290
+ cwd = makeTempDir();
291
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
292
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
293
+
294
+ const paths = getPaths(cwd);
295
+ assert.equal(Object.isFrozen(paths), true);
296
+ });
297
+
298
+ it('attempting to add a property does not modify the object', () => {
299
+ cwd = makeTempDir();
300
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
301
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
302
+
303
+ const paths = getPaths(cwd);
304
+ try {
305
+ paths.NEW_PROP = 'value';
306
+ } catch {
307
+ // strict mode would throw
308
+ }
309
+ assert.equal(paths.NEW_PROP, undefined);
310
+ });
311
+ });
312
+
313
+ // ─── 7. Caching (PATH-03, PATH-04) ──────────────────────────────────────────
314
+
315
+ describe('caching', () => {
316
+ let cwdA, cwdB;
317
+
318
+ afterEach(() => {
319
+ resetPaths();
320
+ if (cwdA) removeTempDir(cwdA);
321
+ if (cwdB) removeTempDir(cwdB);
322
+ cwdA = null;
323
+ cwdB = null;
324
+ });
325
+
326
+ it('same cwd returns same object (referential equality)', () => {
327
+ cwdA = makeTempDir();
328
+ fs.mkdirSync(path.join(cwdA, '.planning'), { recursive: true });
329
+ fs.writeFileSync(path.join(cwdA, '.planning', 'config.json'), '{}');
330
+
331
+ const first = getPaths(cwdA);
332
+ const second = getPaths(cwdA);
333
+ assert.equal(first === second, true, 'Expected referential equality');
334
+ });
335
+
336
+ it('different cwd returns different object', () => {
337
+ cwdA = makeTempDir();
338
+ fs.mkdirSync(path.join(cwdA, '.planning'), { recursive: true });
339
+ fs.writeFileSync(path.join(cwdA, '.planning', 'config.json'), '{}');
340
+
341
+ cwdB = makeTempDir();
342
+ fs.mkdirSync(path.join(cwdB, '.planning'), { recursive: true });
343
+ fs.writeFileSync(path.join(cwdB, '.planning', 'config.json'), '{}');
344
+
345
+ const pathsA = getPaths(cwdA);
346
+ const pathsB = getPaths(cwdB);
347
+ assert.notEqual(pathsA === pathsB, true, 'Expected different objects');
348
+ });
349
+
350
+ it('resetPaths clears the cache (new reference after reset)', () => {
351
+ cwdA = makeTempDir();
352
+ fs.mkdirSync(path.join(cwdA, '.planning'), { recursive: true });
353
+ fs.writeFileSync(path.join(cwdA, '.planning', 'config.json'), '{}');
354
+
355
+ const before = getPaths(cwdA);
356
+ resetPaths();
357
+ const after = getPaths(cwdA);
358
+ assert.notEqual(before === after, true, 'Expected different reference after reset');
359
+ });
360
+ });
361
+
362
+ // ─── 8. initPaths / resetPaths (PATH-04) ─────────────────────────────────────
363
+
364
+ describe('initPaths / resetPaths', () => {
365
+ let cwd;
366
+
367
+ afterEach(() => {
368
+ resetPaths();
369
+ if (cwd) removeTempDir(cwd);
370
+ cwd = null;
371
+ });
372
+
373
+ it('initPaths returns the same PATHS object as getPaths', () => {
374
+ cwd = makeTempDir();
375
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
376
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
377
+
378
+ const fromInit = initPaths(cwd);
379
+ const fromGet = getPaths(cwd);
380
+ assert.equal(fromInit === fromGet, true, 'initPaths and getPaths should return same reference');
381
+ });
382
+
383
+ it('resetPaths clears cache (verified by reference comparison)', () => {
384
+ cwd = makeTempDir();
385
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
386
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
387
+
388
+ const before = initPaths(cwd);
389
+ resetPaths();
390
+ const after = initPaths(cwd);
391
+ assert.notEqual(before === after, true, 'Expected new reference after resetPaths');
392
+ });
393
+ });
394
+
395
+ // ─── 9. Leaf module constraint (PATH-05) ─────────────────────────────────────
396
+
397
+ describe('leaf module constraint', () => {
398
+ it('paths.cjs has no DGS module imports (no require("./") patterns)', () => {
399
+ const source = fs.readFileSync(
400
+ path.join(__dirname, 'paths.cjs'),
401
+ 'utf-8'
402
+ );
403
+ // Check for require('./anything') — would indicate DGS module import
404
+ const dgsImports = source.match(/require\s*\(\s*['"]\.\/[^'"]*['"]\s*\)/g);
405
+ assert.equal(dgsImports, null, 'paths.cjs should have zero DGS module imports');
406
+ });
407
+
408
+ it('paths.cjs uses fs and path builtins', () => {
409
+ const source = fs.readFileSync(
410
+ path.join(__dirname, 'paths.cjs'),
411
+ 'utf-8'
412
+ );
413
+ assert.ok(
414
+ /require\s*\(\s*['"]fs['"]\s*\)/.test(source),
415
+ 'Expected require("fs") in paths.cjs'
416
+ );
417
+ assert.ok(
418
+ /require\s*\(\s*['"]path['"]\s*\)/.test(source),
419
+ 'Expected require("path") in paths.cjs'
420
+ );
421
+ });
422
+ });
423
+
424
+ // ─── 10. Edge cases ──────────────────────────────────────────────────────────
425
+
426
+ describe('edge cases', () => {
427
+ let cwd;
428
+
429
+ afterEach(() => {
430
+ resetPaths();
431
+ if (cwd) removeTempDir(cwd);
432
+ cwd = null;
433
+ });
434
+
435
+ it('paths returned even when subdirectories do not exist on disk', () => {
436
+ cwd = makeTempDir();
437
+ // No .planning/ directory created, no config files — default case
438
+
439
+ const paths = getPaths(cwd);
440
+ // Should still return all paths even though none of these directories exist
441
+ assert.ok(paths.PHASES);
442
+ assert.ok(paths.IDEAS);
443
+ assert.ok(paths.SPECS);
444
+ assert.ok(paths.ROOT);
445
+ });
446
+
447
+ it('PROJECTS_DIR constant equals "projects"', () => {
448
+ assert.equal(PROJECTS_DIR, 'projects');
449
+ });
450
+
451
+ it('getPaths().PROJECTS returns absolute path ending in /projects', () => {
452
+ cwd = makeTempDir();
453
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
454
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
455
+
456
+ const paths = getPaths(cwd);
457
+ assert.ok(path.isAbsolute(paths.PROJECTS), 'Expected PROJECTS to be absolute');
458
+ assert.ok(paths.PROJECTS.endsWith('/projects') || paths.PROJECTS.endsWith('\\projects'),
459
+ 'Expected PROJECTS path to end with /projects');
460
+ });
461
+
462
+ it('relative cwd is normalized to absolute path', () => {
463
+ // This test verifies that passing a relative cwd gives the same result
464
+ // as passing the equivalent absolute path
465
+ cwd = makeTempDir();
466
+ fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
467
+ fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
468
+
469
+ // Get paths with absolute cwd
470
+ const absResult = getPaths(cwd);
471
+ resetPaths();
472
+
473
+ // Calling with '.' should resolve to process.cwd(), which is different from
474
+ // our temp dir, but the key behavior is that relative paths get resolved
475
+ // to absolute via path.resolve()
476
+ const dotResult = getPaths('.');
477
+ // The important assertion: all returned paths are absolute regardless of input
478
+ for (const [key, value] of Object.entries(dotResult)) {
479
+ if (key === 'LAYOUT') continue;
480
+ assert.ok(
481
+ path.isAbsolute(value),
482
+ `Expected ${key} to be absolute even with relative cwd input`
483
+ );
484
+ }
485
+ });
486
+ });