@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,693 @@
1
+ /**
2
+ * Tests for context.cjs -- Context tier engine
3
+ *
4
+ * Uses Node.js built-in test runner (node:test) and assert (node:assert/strict).
5
+ * Tests invoke dgs-tools.cjs CLI via execSync against isolated temp fixtures
6
+ * to verify tier resolution, scope handling, and graceful degradation.
7
+ */
8
+
9
+ const { describe, it, beforeEach, afterEach } = require('node:test');
10
+ const assert = require('node:assert/strict');
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const os = require('os');
14
+ const { execSync } = require('child_process');
15
+
16
+ const { createFixture } = require('./test-helpers.cjs');
17
+ const {
18
+ parseTierDefinitions,
19
+ parseSimpleYaml,
20
+ loadTierInternal,
21
+ resetTierCache,
22
+ resolvePhaseScope,
23
+ resolveIdeaScope,
24
+ resolveSpecScope,
25
+ resolveTierFiles,
26
+ cmdContextLoadTier,
27
+ } = require('./context.cjs');
28
+
29
+ // Path to CLI entry point
30
+ const CLI = path.resolve(__dirname, '..', 'dgs-tools.cjs');
31
+
32
+ /**
33
+ * Run a dgs-tools context command in a fixture directory and return parsed JSON.
34
+ */
35
+ function runContext(cwd, args) {
36
+ const stdout = execSync(`node "${CLI}" context ${args}`, {
37
+ cwd,
38
+ encoding: 'utf-8',
39
+ stdio: ['pipe', 'pipe', 'pipe'],
40
+ });
41
+ return JSON.parse(stdout.trim());
42
+ }
43
+
44
+ /**
45
+ * Run a dgs-tools context command expecting an error, return stderr text.
46
+ */
47
+ function runContextError(cwd, args) {
48
+ try {
49
+ execSync(`node "${CLI}" context ${args}`, {
50
+ cwd,
51
+ encoding: 'utf-8',
52
+ stdio: ['pipe', 'pipe', 'pipe'],
53
+ });
54
+ return null; // No error
55
+ } catch (err) {
56
+ return err.stderr ? err.stderr.toString().trim() : err.message;
57
+ }
58
+ }
59
+
60
+ // ─── parseTierDefinitions ────────────────────────────────────────────────────
61
+
62
+ describe('parseTierDefinitions', () => {
63
+ afterEach(() => {
64
+ resetTierCache();
65
+ });
66
+
67
+ it('parses all 5 tiers from the reference doc', () => {
68
+ const tiers = parseTierDefinitions();
69
+ assert.equal(tiers.size, 5, 'should have exactly 5 tiers');
70
+ assert.ok(tiers.has('none'), 'should have none tier');
71
+ assert.ok(tiers.has('lite'), 'should have lite tier');
72
+ assert.ok(tiers.has('planning'), 'should have planning tier');
73
+ assert.ok(tiers.has('execution'), 'should have execution tier');
74
+ assert.ok(tiers.has('verification'), 'should have verification tier');
75
+ });
76
+
77
+ it('each tier has required fields', () => {
78
+ const tiers = parseTierDefinitions();
79
+ for (const [name, tier] of tiers) {
80
+ assert.ok(tier.tier, `tier ${name} should have tier field`);
81
+ assert.equal(tier.tier, name, `tier name should match key`);
82
+ }
83
+ });
84
+
85
+ it('tier inheritance chain is correct', () => {
86
+ const tiers = parseTierDefinitions();
87
+
88
+ assert.equal(tiers.get('none').includes_tier, null, 'none has no parent');
89
+ assert.equal(tiers.get('lite').includes_tier, null, 'lite has no parent');
90
+ assert.equal(tiers.get('planning').includes_tier, 'lite', 'planning inherits lite');
91
+ assert.equal(tiers.get('execution').includes_tier, 'planning', 'execution inherits planning');
92
+ assert.equal(tiers.get('verification').includes_tier, 'execution', 'verification inherits execution');
93
+ });
94
+
95
+ it('none tier has empty files array', () => {
96
+ const tiers = parseTierDefinitions();
97
+ const none = tiers.get('none');
98
+ assert.ok(Array.isArray(none.files), 'none tier files should be array');
99
+ assert.equal(none.files.length, 0, 'none tier files should be empty');
100
+ });
101
+
102
+ it('lite tier has 3 files (PROJECT.md, STATE.md, config.json)', () => {
103
+ const tiers = parseTierDefinitions();
104
+ const lite = tiers.get('lite');
105
+ assert.ok(Array.isArray(lite.files), 'lite tier files should be array');
106
+ assert.equal(lite.files.length, 3, 'lite tier should have 3 files');
107
+ const paths = lite.files.map(f => f.path);
108
+ assert.ok(paths.includes('PROJECT.md'), 'should include PROJECT.md');
109
+ assert.ok(paths.includes('STATE.md'), 'should include STATE.md');
110
+ assert.ok(paths.includes('config.json'), 'should include config.json');
111
+ });
112
+
113
+ it('planning tier has 3 own files plus dynamic codebase glob', () => {
114
+ const tiers = parseTierDefinitions();
115
+ const planning = tiers.get('planning');
116
+ assert.ok(Array.isArray(planning.files), 'planning tier files should be array');
117
+ assert.equal(planning.files.length, 3, 'planning tier should have 3 own files');
118
+ const paths = planning.files.map(f => f.path);
119
+ assert.ok(paths.includes('ROADMAP.md'), 'should include ROADMAP.md');
120
+ assert.ok(paths.includes('REQUIREMENTS.md'), 'should include REQUIREMENTS.md');
121
+ assert.ok(paths.includes('REPOS.md'), 'should include REPOS.md');
122
+
123
+ // Dynamic rules
124
+ assert.ok(Array.isArray(planning.dynamic), 'should have dynamic rules');
125
+ assert.ok(planning.dynamic.length > 0, 'should have at least one dynamic rule');
126
+ assert.equal(planning.dynamic[0].type, 'glob', 'first dynamic rule should be glob');
127
+ assert.equal(planning.dynamic[0].pattern, 'codebase/**/*.md', 'codebase glob pattern');
128
+ });
129
+
130
+ it('caches results on subsequent calls', () => {
131
+ const first = parseTierDefinitions();
132
+ const second = parseTierDefinitions();
133
+ assert.equal(first, second, 'should return same Map instance from cache');
134
+ });
135
+ });
136
+
137
+ // ─── context load-tier none ──────────────────────────────────────────────────
138
+
139
+ describe('context load-tier none', () => {
140
+ let fixture;
141
+
142
+ beforeEach(() => {
143
+ resetTierCache();
144
+ fixture = createFixture({
145
+ '.planning/config.json': JSON.stringify({}),
146
+ '.planning/STATE.md': '# State',
147
+ '.planning/PROJECT.md': '# Project',
148
+ });
149
+ });
150
+
151
+ afterEach(() => {
152
+ fixture.cleanup();
153
+ resetTierCache();
154
+ });
155
+
156
+ it('returns empty files array', () => {
157
+ const result = loadTierInternal('none', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
158
+ assert.ok(Array.isArray(result.files), 'files should be array');
159
+ assert.equal(result.files.length, 0, 'none tier should have 0 files');
160
+ });
161
+
162
+ it('returns tier name in output', () => {
163
+ const result = loadTierInternal('none', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
164
+ assert.equal(result.tier, 'none', 'tier should be "none"');
165
+ });
166
+
167
+ it('returns empty scope', () => {
168
+ const result = loadTierInternal('none', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
169
+ assert.deepEqual(result.scope, {}, 'scope should be empty');
170
+ });
171
+ });
172
+
173
+ // ─── context load-tier lite ──────────────────────────────────────────────────
174
+
175
+ describe('context load-tier lite', () => {
176
+ let fixture;
177
+
178
+ beforeEach(() => {
179
+ resetTierCache();
180
+ });
181
+
182
+ afterEach(() => {
183
+ if (fixture) fixture.cleanup();
184
+ resetTierCache();
185
+ });
186
+
187
+ it('returns files with correct paths for existing files', () => {
188
+ fixture = createFixture({
189
+ '.planning/config.json': JSON.stringify({}),
190
+ '.planning/STATE.md': '# State',
191
+ '.planning/PROJECT.md': '# Project',
192
+ });
193
+
194
+ const result = loadTierInternal('lite', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
195
+ assert.equal(result.tier, 'lite');
196
+ assert.ok(result.files.length > 0, 'should have some files');
197
+
198
+ const filePaths = result.files.map(f => f.path);
199
+ // All three should be present since they exist in fixture
200
+ assert.ok(filePaths.some(p => p.includes('PROJECT.md')), 'should include PROJECT.md');
201
+ assert.ok(filePaths.some(p => p.includes('STATE.md')), 'should include STATE.md');
202
+ assert.ok(filePaths.some(p => p.includes('config.json')), 'should include config.json');
203
+ });
204
+
205
+ it('returns category labels for each file', () => {
206
+ fixture = createFixture({
207
+ '.planning/config.json': JSON.stringify({}),
208
+ '.planning/STATE.md': '# State',
209
+ '.planning/PROJECT.md': '# Project',
210
+ });
211
+
212
+ const result = loadTierInternal('lite', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
213
+
214
+ for (const file of result.files) {
215
+ assert.ok(file.category, `file ${file.path} should have category`);
216
+ }
217
+
218
+ const categories = result.files.map(f => f.category);
219
+ assert.ok(categories.includes('project'), 'should have project category');
220
+ assert.ok(categories.includes('state'), 'should have state category');
221
+ assert.ok(categories.includes('config'), 'should have config category');
222
+ });
223
+
224
+ it('omits missing files silently', () => {
225
+ // No STATE.md in this fixture
226
+ fixture = createFixture({
227
+ '.planning/config.json': JSON.stringify({}),
228
+ '.planning/PROJECT.md': '# Project',
229
+ });
230
+
231
+ const result = loadTierInternal('lite', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
232
+ const filePaths = result.files.map(f => f.path);
233
+ assert.ok(!filePaths.some(p => p.includes('STATE.md')), 'STATE.md should be omitted when missing');
234
+ assert.ok(filePaths.some(p => p.includes('PROJECT.md')), 'PROJECT.md should still be present');
235
+ assert.ok(filePaths.some(p => p.includes('config.json')), 'config.json should still be present');
236
+ });
237
+
238
+ it('all files have exists: true', () => {
239
+ fixture = createFixture({
240
+ '.planning/config.json': JSON.stringify({}),
241
+ '.planning/STATE.md': '# State',
242
+ '.planning/PROJECT.md': '# Project',
243
+ });
244
+
245
+ const result = loadTierInternal('lite', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
246
+ for (const file of result.files) {
247
+ assert.equal(file.exists, true, `file ${file.path} should have exists: true`);
248
+ }
249
+ });
250
+ });
251
+
252
+ // ─── context load-tier planning ──────────────────────────────────────────────
253
+
254
+ describe('context load-tier planning', () => {
255
+ let fixture;
256
+
257
+ beforeEach(() => {
258
+ resetTierCache();
259
+ });
260
+
261
+ afterEach(() => {
262
+ if (fixture) fixture.cleanup();
263
+ resetTierCache();
264
+ });
265
+
266
+ it('includes all lite tier files plus planning files', () => {
267
+ fixture = createFixture({
268
+ '.planning/config.json': JSON.stringify({}),
269
+ '.planning/STATE.md': '# State',
270
+ '.planning/PROJECT.md': '# Project',
271
+ '.planning/ROADMAP.md': '# Roadmap',
272
+ '.planning/REQUIREMENTS.md': '# Requirements',
273
+ '.planning/REPOS.md': '# Repos',
274
+ });
275
+
276
+ const result = loadTierInternal('planning', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
277
+ const filePaths = result.files.map(f => f.path);
278
+
279
+ // Lite tier files
280
+ assert.ok(filePaths.some(p => p.includes('PROJECT.md')), 'should include PROJECT.md from lite tier');
281
+ assert.ok(filePaths.some(p => p.includes('STATE.md')), 'should include STATE.md from lite tier');
282
+ assert.ok(filePaths.some(p => p.includes('config.json')), 'should include config.json from lite tier');
283
+
284
+ // Planning tier files
285
+ assert.ok(filePaths.some(p => p.includes('ROADMAP.md')), 'should include ROADMAP.md');
286
+ assert.ok(filePaths.some(p => p.includes('REQUIREMENTS.md')), 'should include REQUIREMENTS.md');
287
+ assert.ok(filePaths.some(p => p.includes('REPOS.md')), 'should include REPOS.md');
288
+ });
289
+
290
+ it('includes codebase docs via dynamic glob', () => {
291
+ fixture = createFixture({
292
+ '.planning/config.json': JSON.stringify({}),
293
+ '.planning/PROJECT.md': '# Project',
294
+ '.planning/ROADMAP.md': '# Roadmap',
295
+ '.planning/codebase/CONVENTIONS.md': '# Conventions',
296
+ '.planning/codebase/TESTING.md': '# Testing',
297
+ });
298
+
299
+ const result = loadTierInternal('planning', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
300
+ const filePaths = result.files.map(f => f.path);
301
+
302
+ assert.ok(filePaths.some(p => p.includes('CONVENTIONS.md')), 'should include CONVENTIONS.md');
303
+ assert.ok(filePaths.some(p => p.includes('TESTING.md')), 'should include TESTING.md');
304
+
305
+ // Verify codebase files have correct category
306
+ const codebaseFiles = result.files.filter(f => f.category === 'codebase');
307
+ assert.ok(codebaseFiles.length >= 2, 'should have at least 2 codebase files');
308
+ });
309
+
310
+ it('missing REPOS.md is silently omitted', () => {
311
+ fixture = createFixture({
312
+ '.planning/config.json': JSON.stringify({}),
313
+ '.planning/PROJECT.md': '# Project',
314
+ '.planning/ROADMAP.md': '# Roadmap',
315
+ '.planning/REQUIREMENTS.md': '# Requirements',
316
+ // No REPOS.md
317
+ });
318
+
319
+ const result = loadTierInternal('planning', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
320
+ const filePaths = result.files.map(f => f.path);
321
+ assert.ok(!filePaths.some(p => p.includes('REPOS.md')), 'REPOS.md should be omitted when missing');
322
+ assert.ok(filePaths.some(p => p.includes('ROADMAP.md')), 'ROADMAP.md should be present');
323
+ });
324
+ });
325
+
326
+ // ─── context load-tier with scope flags ──────────────────────────────────────
327
+
328
+ describe('context load-tier with scope flags', () => {
329
+ let fixture;
330
+
331
+ beforeEach(() => {
332
+ resetTierCache();
333
+ });
334
+
335
+ afterEach(() => {
336
+ if (fixture) fixture.cleanup();
337
+ resetTierCache();
338
+ });
339
+
340
+ it('--phase adds phase-specific files to output', () => {
341
+ fixture = createFixture({
342
+ '.planning/config.json': JSON.stringify({}),
343
+ '.planning/PROJECT.md': '# Project',
344
+ '.planning/STATE.md': '# State',
345
+ '.planning/ROADMAP.md': '# Roadmap\n\n## Phases\n\n### Phase 1: Test Phase\n',
346
+ '.planning/phases/01-test-phase/01-CONTEXT.md': '# Context',
347
+ '.planning/phases/01-test-phase/01-RESEARCH.md': '# Research',
348
+ '.planning/phases/01-test-phase/01-01-PLAN.md': '---\nphase: 01\nplan: 01\n---\n# Plan',
349
+ '.planning/phases/01-test-phase/01-01-SUMMARY.md': '---\nphase: 01\nplan: 01\n---\n# Summary',
350
+ });
351
+
352
+ const result = loadTierInternal('execution', path.join(fixture.cwd, '.planning'), {
353
+ cwd: fixture.cwd,
354
+ phase: '1',
355
+ });
356
+
357
+ const filePaths = result.files.map(f => f.path);
358
+ assert.ok(filePaths.some(p => p.includes('CONTEXT.md')), 'should include phase CONTEXT.md');
359
+ assert.ok(filePaths.some(p => p.includes('PLAN.md')), 'should include phase PLAN.md');
360
+ assert.ok(filePaths.some(p => p.includes('SUMMARY.md')), 'should include phase SUMMARY.md');
361
+ assert.equal(result.scope.phase, '1', 'scope should include phase');
362
+ });
363
+
364
+ it('--idea adds idea file and docs to output', () => {
365
+ fixture = createFixture({
366
+ '.planning/config.json': JSON.stringify({}),
367
+ '.planning/PROJECT.md': '# Project',
368
+ '.planning/ROADMAP.md': '# Roadmap',
369
+ '.planning/ideas/pending/005-better-caching.md': '# Better Caching',
370
+ '.planning/ideas/pending/005-better-caching/docs/research.md': '# Research on caching',
371
+ });
372
+
373
+ const result = loadTierInternal('planning', path.join(fixture.cwd, '.planning'), {
374
+ cwd: fixture.cwd,
375
+ idea: '5',
376
+ });
377
+
378
+ const filePaths = result.files.map(f => f.path);
379
+ assert.ok(filePaths.some(p => p.includes('005-better-caching.md')), 'should include idea file');
380
+ assert.ok(filePaths.some(p => p.includes('research.md')), 'should include idea docs');
381
+ assert.equal(result.scope.idea, '5', 'scope should include idea');
382
+ });
383
+
384
+ it('--spec adds spec file and docs to output', () => {
385
+ fixture = createFixture({
386
+ '.planning/config.json': JSON.stringify({}),
387
+ '.planning/PROJECT.md': '# Project',
388
+ '.planning/ROADMAP.md': '# Roadmap',
389
+ '.planning/specs/dgs-improved-context-spec.md': '---\nid: dgs-improved-context\nstatus: draft\n---\n# Improved Context',
390
+ '.planning/specs/dgs-improved-context-spec/docs/analysis.md': '# Analysis',
391
+ });
392
+
393
+ const result = loadTierInternal('planning', path.join(fixture.cwd, '.planning'), {
394
+ cwd: fixture.cwd,
395
+ spec: 'dgs-improved-context',
396
+ });
397
+
398
+ const filePaths = result.files.map(f => f.path);
399
+ assert.ok(filePaths.some(p => p.includes('dgs-improved-context-spec.md')), 'should include spec file');
400
+ assert.ok(filePaths.some(p => p.includes('analysis.md')), 'should include spec docs');
401
+ assert.equal(result.scope.spec, 'dgs-improved-context', 'scope should include spec');
402
+ });
403
+
404
+ it('missing scope targets silently return no extra files', () => {
405
+ fixture = createFixture({
406
+ '.planning/config.json': JSON.stringify({}),
407
+ '.planning/PROJECT.md': '# Project',
408
+ '.planning/STATE.md': '# State',
409
+ '.planning/ROADMAP.md': '# Roadmap\n\n## Phases\n\n### Phase 1: Test Phase\n',
410
+ '.planning/phases/01-test-phase/01-01-PLAN.md': '# Plan',
411
+ });
412
+
413
+ // Phase 999 does not exist
414
+ const result = loadTierInternal('execution', path.join(fixture.cwd, '.planning'), {
415
+ cwd: fixture.cwd,
416
+ phase: '999',
417
+ });
418
+
419
+ // Should still have base files from execution -> planning -> lite inheritance
420
+ assert.ok(result.files.length >= 0, 'should not crash with missing phase');
421
+ assert.equal(result.scope.phase, '999', 'scope should still record phase');
422
+ // No phase-specific files should appear
423
+ const phaseFiles = result.files.filter(f => f.category === 'phase');
424
+ assert.equal(phaseFiles.length, 0, 'no phase files for non-existent phase');
425
+ });
426
+
427
+ it('approved spec is marked as truncated', () => {
428
+ fixture = createFixture({
429
+ '.planning/config.json': JSON.stringify({}),
430
+ '.planning/PROJECT.md': '# Project',
431
+ '.planning/ROADMAP.md': '# Roadmap',
432
+ '.planning/specs/my-spec.md': '---\nid: my-spec\nstatus: approved\n---\n# My Spec\n\n## Section 1\nContent\n\n## Section 2\nContent\n\n## Section 3\nContent\n\n## Section 4\nShould be truncated',
433
+ });
434
+
435
+ const result = loadTierInternal('planning', path.join(fixture.cwd, '.planning'), {
436
+ cwd: fixture.cwd,
437
+ spec: 'my-spec',
438
+ });
439
+
440
+ const specFiles = result.files.filter(f => f.category === 'spec');
441
+ assert.ok(specFiles.length > 0, 'should include spec file');
442
+ assert.equal(specFiles[0].truncated, true, 'approved spec should be marked truncated');
443
+ assert.equal(result.scope.spec_truncated, true, 'scope should note truncation');
444
+ });
445
+ });
446
+
447
+ // ─── graceful degradation ────────────────────────────────────────────────────
448
+
449
+ describe('graceful degradation', () => {
450
+ let fixture;
451
+
452
+ beforeEach(() => {
453
+ resetTierCache();
454
+ });
455
+
456
+ afterEach(() => {
457
+ if (fixture) fixture.cleanup();
458
+ resetTierCache();
459
+ });
460
+
461
+ it('empty .planning directory returns empty files for lite tier', () => {
462
+ fixture = createFixture({
463
+ '.planning/config.json': JSON.stringify({}),
464
+ // No STATE.md, PROJECT.md etc.
465
+ });
466
+
467
+ const result = loadTierInternal('lite', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
468
+ assert.equal(result.tier, 'lite');
469
+ // config.json exists but PROJECT.md and STATE.md don't
470
+ assert.ok(result.files.length <= 1, 'should have at most config.json');
471
+ });
472
+
473
+ it('non-existent tier name returns error in result', () => {
474
+ fixture = createFixture({
475
+ '.planning/config.json': JSON.stringify({}),
476
+ });
477
+
478
+ const result = loadTierInternal('invalid-name', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
479
+ assert.ok(result.error, 'should have error field');
480
+ assert.ok(result.error.includes('Unknown tier'), 'error should mention unknown tier');
481
+ assert.equal(result.files.length, 0, 'should have empty files');
482
+ });
483
+
484
+ it('invalid tier via CLI produces error on stderr', () => {
485
+ fixture = createFixture({
486
+ '.planning/config.json': JSON.stringify({}),
487
+ });
488
+
489
+ const stderr = runContextError(fixture.cwd, 'load-tier invalid-name');
490
+ assert.ok(stderr, 'should produce error');
491
+ assert.ok(stderr.includes('invalid tier'), 'error should mention invalid tier');
492
+ });
493
+
494
+ it('missing .planning/config.json falls back gracefully', () => {
495
+ fixture = createFixture({
496
+ '.planning/PROJECT.md': '# Project',
497
+ '.planning/STATE.md': '# State',
498
+ });
499
+
500
+ // The config.json from tier definitions won't be found but that's fine
501
+ const result = loadTierInternal('lite', path.join(fixture.cwd, '.planning'), { cwd: fixture.cwd });
502
+ assert.equal(result.tier, 'lite');
503
+ // PROJECT.md and STATE.md should be found
504
+ const filePaths = result.files.map(f => f.path);
505
+ assert.ok(filePaths.some(p => p.includes('PROJECT.md')), 'PROJECT.md should be present');
506
+ assert.ok(filePaths.some(p => p.includes('STATE.md')), 'STATE.md should be present');
507
+ });
508
+ });
509
+
510
+ // ─── CLI integration ─────────────────────────────────────────────────────────
511
+
512
+ describe('CLI integration', () => {
513
+ let fixture;
514
+
515
+ beforeEach(() => {
516
+ resetTierCache();
517
+ });
518
+
519
+ afterEach(() => {
520
+ if (fixture) fixture.cleanup();
521
+ resetTierCache();
522
+ });
523
+
524
+ it('context load-tier lite returns JSON with correct structure', () => {
525
+ fixture = createFixture({
526
+ '.planning/config.json': JSON.stringify({}),
527
+ '.planning/STATE.md': '# State',
528
+ '.planning/PROJECT.md': '# Project',
529
+ });
530
+
531
+ const result = runContext(fixture.cwd, 'load-tier lite');
532
+ assert.ok(Array.isArray(result.files), 'output should have files array');
533
+ assert.equal(result.tier, 'lite', 'output should have tier: "lite"');
534
+ assert.ok(result.scope !== undefined, 'output should have scope');
535
+ });
536
+
537
+ it('context load-tier none returns empty files via CLI', () => {
538
+ fixture = createFixture({
539
+ '.planning/config.json': JSON.stringify({}),
540
+ });
541
+
542
+ const result = runContext(fixture.cwd, 'load-tier none');
543
+ assert.equal(result.tier, 'none');
544
+ assert.equal(result.files.length, 0, 'none tier should have 0 files');
545
+ });
546
+
547
+ it('context load-tier planning includes inherited lite files via CLI', () => {
548
+ fixture = createFixture({
549
+ '.planning/config.json': JSON.stringify({}),
550
+ '.planning/STATE.md': '# State',
551
+ '.planning/PROJECT.md': '# Project',
552
+ '.planning/ROADMAP.md': '# Roadmap',
553
+ '.planning/REQUIREMENTS.md': '# Requirements',
554
+ });
555
+
556
+ const result = runContext(fixture.cwd, 'load-tier planning');
557
+ const filePaths = result.files.map(f => f.path);
558
+ assert.ok(filePaths.some(p => p.includes('PROJECT.md')), 'should include lite tier PROJECT.md');
559
+ assert.ok(filePaths.some(p => p.includes('ROADMAP.md')), 'should include planning tier ROADMAP.md');
560
+ });
561
+
562
+ it('context load-tier with --phase flag works via CLI', () => {
563
+ fixture = createFixture({
564
+ '.planning/config.json': JSON.stringify({}),
565
+ '.planning/PROJECT.md': '# Project',
566
+ '.planning/STATE.md': '# State',
567
+ '.planning/ROADMAP.md': '# Roadmap\n\n## Phases\n\n### Phase 1: Test Phase\n',
568
+ '.planning/REQUIREMENTS.md': '# Requirements',
569
+ '.planning/phases/01-test-phase/01-CONTEXT.md': '# Context',
570
+ '.planning/phases/01-test-phase/01-01-PLAN.md': '---\nphase: 01\nplan: 01\n---\n# Plan',
571
+ });
572
+
573
+ const result = runContext(fixture.cwd, 'load-tier execution --phase 1');
574
+ assert.equal(result.scope.phase, '1', 'scope should record phase');
575
+ const filePaths = result.files.map(f => f.path);
576
+ assert.ok(filePaths.some(p => p.includes('CONTEXT.md')), 'should include phase CONTEXT.md');
577
+ });
578
+
579
+ it('context without subcommand produces error', () => {
580
+ fixture = createFixture({
581
+ '.planning/config.json': JSON.stringify({}),
582
+ });
583
+
584
+ const stderr = runContextError(fixture.cwd, '');
585
+ assert.ok(stderr, 'should produce error');
586
+ assert.ok(stderr.includes('Unknown context command') || stderr.includes('Available: load-tier'),
587
+ 'error should mention available subcommands');
588
+ });
589
+
590
+ it('context load-tier without tier name produces usage error', () => {
591
+ fixture = createFixture({
592
+ '.planning/config.json': JSON.stringify({}),
593
+ });
594
+
595
+ const stderr = runContextError(fixture.cwd, 'load-tier');
596
+ assert.ok(stderr, 'should produce error');
597
+ assert.ok(stderr.includes('Usage:') || stderr.includes('tier-name'),
598
+ 'error should show usage');
599
+ });
600
+
601
+ it('context unknown-subcommand produces error', () => {
602
+ fixture = createFixture({
603
+ '.planning/config.json': JSON.stringify({}),
604
+ });
605
+
606
+ const stderr = runContextError(fixture.cwd, 'bogus');
607
+ assert.ok(stderr, 'should produce error');
608
+ assert.ok(stderr.includes('Unknown context command'), 'error should mention unknown command');
609
+ });
610
+ });
611
+
612
+ // ─── parseSimpleYaml ─────────────────────────────────────────────────────────
613
+
614
+ describe('parseSimpleYaml', () => {
615
+ it('parses simple key-value pairs', () => {
616
+ const result = parseSimpleYaml('tier: none\nincludes_tier: null');
617
+ assert.equal(result.tier, 'none');
618
+ assert.equal(result.includes_tier, null);
619
+ });
620
+
621
+ it('parses inline arrays', () => {
622
+ const result = parseSimpleYaml('files: []');
623
+ assert.deepEqual(result.files, []);
624
+ });
625
+
626
+ it('parses block arrays with object items', () => {
627
+ const yaml = 'files:\n - path: "PROJECT.md"\n category: "project"\n - path: "STATE.md"\n category: "state"';
628
+ const result = parseSimpleYaml(yaml);
629
+ assert.ok(Array.isArray(result.files), 'files should be array');
630
+ assert.equal(result.files.length, 2, 'should have 2 items');
631
+ assert.equal(result.files[0].path, 'PROJECT.md');
632
+ assert.equal(result.files[0].category, 'project');
633
+ assert.equal(result.files[1].path, 'STATE.md');
634
+ assert.equal(result.files[1].category, 'state');
635
+ });
636
+
637
+ it('parses boolean values', () => {
638
+ const result = parseSimpleYaml('enabled: true\ndisabled: false');
639
+ assert.equal(result.enabled, true);
640
+ assert.equal(result.disabled, false);
641
+ });
642
+
643
+ it('parses numeric values', () => {
644
+ const result = parseSimpleYaml('count: 42\npi: 3.14');
645
+ assert.equal(result.count, 42);
646
+ assert.equal(result.pi, 3.14);
647
+ });
648
+
649
+ it('handles empty objects', () => {
650
+ const result = parseSimpleYaml('scope_flags: {}');
651
+ assert.deepEqual(result.scope_flags, {});
652
+ });
653
+ });
654
+
655
+ // ─── resolveTierFiles ────────────────────────────────────────────────────────
656
+
657
+ describe('resolveTierFiles', () => {
658
+ afterEach(() => {
659
+ resetTierCache();
660
+ });
661
+
662
+ it('returns empty array for non-existent tier', () => {
663
+ const tiers = parseTierDefinitions();
664
+ const result = resolveTierFiles('nonexistent', tiers);
665
+ assert.deepEqual(result, []);
666
+ });
667
+
668
+ it('returns empty array for none tier', () => {
669
+ const tiers = parseTierDefinitions();
670
+ const result = resolveTierFiles('none', tiers);
671
+ assert.equal(result.length, 0);
672
+ });
673
+
674
+ it('returns lite files for lite tier', () => {
675
+ const tiers = parseTierDefinitions();
676
+ const result = resolveTierFiles('lite', tiers);
677
+ assert.equal(result.length, 3);
678
+ });
679
+
680
+ it('planning tier inherits lite files', () => {
681
+ const tiers = parseTierDefinitions();
682
+ const result = resolveTierFiles('planning', tiers);
683
+ // lite has 3, planning adds 3 more
684
+ assert.equal(result.length, 6, 'planning should have 6 static files (3 lite + 3 planning)');
685
+ });
686
+
687
+ it('execution tier inherits planning+lite files', () => {
688
+ const tiers = parseTierDefinitions();
689
+ const result = resolveTierFiles('execution', tiers);
690
+ // execution has files: [] itself but inherits from planning which has 6
691
+ assert.equal(result.length, 6, 'execution inherits all planning files');
692
+ });
693
+ });