jettypod 4.1.2 → 4.1.4

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 (179) hide show
  1. package/.nvmrc +1 -0
  2. package/docs/COMPLETE-TESTING-STRATEGY.md +970 -0
  3. package/docs/DECISIONS.md +10 -12
  4. package/docs/NODE_VERSION.md +83 -0
  5. package/docs/TDD-INFRASTRUCTURE-STRATEGY.md +1374 -0
  6. package/docs/TESTING-FOR-NON-ENGINEERS.md +1588 -0
  7. package/docs/TESTING-STRATEGY-AUDIT.md +698 -0
  8. package/hooks/post-checkout +17 -0
  9. package/hooks/post-merge +17 -0
  10. package/hooks/pre-commit +30 -0
  11. package/jettypod.js +259 -120
  12. package/lib/coverage-tracker.js +218 -0
  13. package/lib/database.js +2 -0
  14. package/lib/db-export.js +192 -0
  15. package/lib/db-import.js +193 -0
  16. package/lib/external-transition-handler.js +32 -0
  17. package/lib/git-hook-helpers.js +174 -0
  18. package/lib/git-root.js +90 -0
  19. package/lib/infrastructure-chore-generator.js +45 -0
  20. package/lib/install-hooks.js +52 -0
  21. package/lib/jettypod-backup.js +238 -0
  22. package/lib/merge-lock.js +193 -0
  23. package/lib/migrations/012-add-worktree-path.js +38 -0
  24. package/lib/migrations/013-worktrees-table.js +86 -0
  25. package/lib/migrations/014-migrate-worktree-data.js +161 -0
  26. package/lib/migrations/015-merge-locks-table.js +67 -0
  27. package/lib/pattern-finder.js +152 -0
  28. package/lib/process-manager.js +140 -0
  29. package/lib/production-standards-reader.js +13 -2
  30. package/lib/production-standards-writer.js +85 -0
  31. package/lib/skills/feature-planning/dry-run-validator.js +135 -0
  32. package/lib/skills/feature-planning/validation-formatter.js +160 -0
  33. package/lib/smart-conflict-detection.js +168 -0
  34. package/lib/smart-fetch-rebase.js +614 -0
  35. package/lib/step-definition-parser.js +76 -0
  36. package/lib/unit-test-generator.js +232 -0
  37. package/lib/verification-command-generator.js +66 -0
  38. package/lib/worktree-diagnostics.js +413 -0
  39. package/lib/worktree-facade.js +174 -0
  40. package/lib/worktree-manager.js +636 -0
  41. package/lib/worktree-reconciler.js +429 -0
  42. package/package.json +30 -3
  43. package/skills-templates/external-transition/SKILL.md +34 -3
  44. package/skills-templates/feature-planning/SKILL.md +190 -24
  45. package/skills-templates/production-mode/SKILL.md +127 -9
  46. package/skills-templates/speed-mode/SKILL.md +454 -51
  47. package/skills-templates/stable-mode/SKILL.md +285 -76
  48. package/.claude/PROTECT_SKILLS.md +0 -28
  49. package/.claude/settings.json +0 -24
  50. package/.claude/settings.local.json +0 -16
  51. package/.claude/skills/epic-planning/SKILL.md +0 -297
  52. package/.claude/skills/external-transition/SKILL.md +0 -384
  53. package/.claude/skills/feature-planning/SKILL.md +0 -464
  54. package/.claude/skills/production-mode/SKILL.md +0 -369
  55. package/.claude/skills/speed-mode/SKILL.md +0 -481
  56. package/.claude/skills/stable-mode/SKILL.md +0 -713
  57. package/.claude/skills.backup-2025-11-10T23-33-09-368Z/epic-planning/SKILL.md +0 -297
  58. package/.claude/skills.backup-2025-11-10T23-33-09-368Z/feature-planning/SKILL.md +0 -464
  59. package/.claude/skills.backup-2025-11-10T23-33-09-368Z/speed-mode/SKILL.md +0 -467
  60. package/.claude/skills.backup-2025-11-10T23-33-09-368Z/stable-mode/SKILL.md +0 -673
  61. package/.claude/skills.backup-2025-11-11T16-15-10-070Z/epic-discover/SKILL.md +0 -297
  62. package/.claude/skills.backup-2025-11-11T16-42-43-212Z/epic-planning/SKILL.md +0 -297
  63. package/.claude/skills.backup-2025-11-11T16-42-43-212Z/feature-planning/SKILL.md +0 -464
  64. package/.claude/skills.backup-2025-11-11T16-42-43-212Z/speed-mode/SKILL.md +0 -467
  65. package/.claude/skills.backup-2025-11-11T16-42-43-212Z/stable-mode/SKILL.md +0 -673
  66. package/.claude/skills.backup-2025-11-11T17-06-09-783Z/epic-planning/SKILL.md +0 -297
  67. package/.claude/skills.backup-2025-11-11T17-06-09-783Z/feature-planning/SKILL.md +0 -464
  68. package/.claude/skills.backup-2025-11-11T17-06-09-783Z/speed-mode/SKILL.md +0 -467
  69. package/.claude/skills.backup-2025-11-11T17-06-09-783Z/stable-mode/SKILL.md +0 -673
  70. package/.devpod/current-work.json +0 -10
  71. package/.devpod/work.db +0 -0
  72. package/.github/workflows/test-safety.yml +0 -85
  73. package/.jettypod/config.json +0 -5
  74. package/.jettypod/current-work.json +0 -10
  75. package/.jettypod/hooks/README.md +0 -77
  76. package/.jettypod/hooks/protect-claude-md.js +0 -338
  77. package/.jettypod/test-work.db +0 -0
  78. package/.jettypod/work.db +0 -0
  79. package/CLAUDE.md +0 -49
  80. package/SPEED-STABLE-AUDIT.md +0 -853
  81. package/SYSTEM-BEHAVIOR.md +0 -2199
  82. package/TEST_SAFETY_AUDIT.md +0 -314
  83. package/TEST_SAFETY_IMPLEMENTATION.md +0 -97
  84. package/cucumber-report.html +0 -45
  85. package/dist/devpod-linux +0 -0
  86. package/dist/devpod-macos +0 -0
  87. package/dist/devpod-win.exe +0 -0
  88. package/docs/features/jettypod-standards-explained.md +0 -543
  89. package/docs/features/standards-inventory.md +0 -257
  90. package/features/auto-generate-production-chores.feature +0 -13
  91. package/features/backlog-command.feature +0 -26
  92. package/features/backlog-filtering-production.feature +0 -10
  93. package/features/claude-md-protection/steps.js +0 -498
  94. package/features/decisions/index.js +0 -490
  95. package/features/decisions/index.test.js +0 -208
  96. package/features/fix-text-wrapping.feature +0 -42
  97. package/features/git-hooks/git-hooks.feature +0 -30
  98. package/features/git-hooks/index.js +0 -93
  99. package/features/git-hooks/index.test.js +0 -137
  100. package/features/git-hooks/post-commit +0 -56
  101. package/features/git-hooks/post-merge +0 -47
  102. package/features/git-hooks/pre-commit +0 -28
  103. package/features/git-hooks/simple-steps.js +0 -53
  104. package/features/git-hooks/simple-test.feature +0 -10
  105. package/features/git-hooks/steps.js +0 -196
  106. package/features/jettypod-update-command.feature +0 -46
  107. package/features/mode-prompts/index.js +0 -95
  108. package/features/mode-prompts/simple-steps.js +0 -44
  109. package/features/mode-prompts/simple-test.feature +0 -9
  110. package/features/mode-prompts/validation.test.js +0 -120
  111. package/features/multiple-claude-instances.feature +0 -121
  112. package/features/production-mode-skill.feature +0 -121
  113. package/features/refactor-mode/steps.js +0 -217
  114. package/features/refactor-mode.feature +0 -49
  115. package/features/simplify-external-transition.feature +0 -166
  116. package/features/skills-update/index.test.js +0 -216
  117. package/features/step_definitions/backlog-command.steps.js +0 -37
  118. package/features/step_definitions/fix-text-wrapping.steps.js +0 -271
  119. package/features/step_definitions/multiple-claude-instances.steps.js +0 -621
  120. package/features/step_definitions/production-mode-skill.steps.js +0 -862
  121. package/features/step_definitions/simplify-external-transition.steps.js +0 -370
  122. package/features/step_definitions/terminal-logo.steps.js +0 -145
  123. package/features/step_definitions/update-command.steps.js +0 -183
  124. package/features/support/hooks.js +0 -9
  125. package/features/terminal-logo/index.js +0 -39
  126. package/features/terminal-logo/terminal-logo.feature +0 -30
  127. package/features/update-command/index.js +0 -181
  128. package/features/update-command/index.test.js +0 -225
  129. package/features/work-commands/bug-workflow-display.feature +0 -22
  130. package/features/work-commands/index.js +0 -498
  131. package/features/work-commands/simple-steps.js +0 -69
  132. package/features/work-commands/stable-tests.feature +0 -57
  133. package/features/work-commands/steps.js +0 -1174
  134. package/features/work-commands/validation.test.js +0 -88
  135. package/features/work-commands/work-commands.feature +0 -13
  136. package/features/work-tracking/discovery-validation.test.js +0 -228
  137. package/features/work-tracking/index.js +0 -1921
  138. package/features/work-tracking/mode-required.feature +0 -112
  139. package/features/work-tracking/phase-tracking.test.js +0 -482
  140. package/features/work-tracking/prototype-tracking.test.js +0 -485
  141. package/features/work-tracking/tree-view.test.js +0 -310
  142. package/features/work-tracking/work-set-mode.feature +0 -71
  143. package/features/work-tracking/work-start-mode.feature +0 -88
  144. package/full-test.txt +0 -0
  145. package/lib/bug-workflow.test.js +0 -177
  146. package/lib/claudemd.test.js +0 -195
  147. package/lib/config.test.js +0 -511
  148. package/lib/constants.test.js +0 -164
  149. package/lib/current-work.test.js +0 -146
  150. package/lib/database-project-config.test.js +0 -111
  151. package/lib/database.test.js +0 -106
  152. package/lib/decisions-generator.test.js +0 -457
  153. package/lib/decisions-helpers.test.js +0 -310
  154. package/lib/git-coordinator.js +0 -167
  155. package/lib/git.test.js +0 -145
  156. package/lib/migrations/002-default-work-item-modes.test.js +0 -351
  157. package/lib/production-chore-generator.test.js +0 -432
  158. package/lib/production-context-detector.test.js +0 -277
  159. package/lib/production-scenario-appender.test.js +0 -235
  160. package/lib/production-scenario-validator.test.js +0 -246
  161. package/lib/production-standards-reader.test.js +0 -270
  162. package/lib/project-state.test.js +0 -92
  163. package/lib/push-queue.js +0 -417
  164. package/lib/queue-processor.js +0 -74
  165. package/lib/test-helpers.js +0 -202
  166. package/lib/test-helpers.test.js +0 -255
  167. package/prototypes/2025-01-11-production-mode-autonomous.js +0 -119
  168. package/prototypes/2025-01-11-production-mode-collaborative.js +0 -166
  169. package/prototypes/2025-01-11-production-mode-guided.js +0 -217
  170. package/prototypes/2025-01-11-production-mode-smart-context.js +0 -347
  171. package/prototypes/2025-01-11-production-standards-example.md +0 -204
  172. package/prototypes/2025-11-10-backlog-filtering-tree-aware.js +0 -242
  173. package/prototypes/test/index.html +0 -1
  174. package/setup-dist-repo.sh +0 -68
  175. package/test-production-standards-engine.js +0 -130
  176. package/test-results.json +0 -2195
  177. package/test-safety-check.sh +0 -80
  178. package/work-item-tracking-plan.md +0 -199
  179. /package/{.jettypod/devpod.db → jettypod.db} +0 -0
@@ -1,112 +0,0 @@
1
- Feature: Mode defaults to discovery on work item creation
2
- As a developer
3
- I want new work items to default to discovery mode
4
- So I can start exploring without specifying mode every time
5
-
6
- # Epic scenarios - epics don't have mode
7
- Scenario: Create epic without mode
8
- Given jettypod is initialized
9
- When I create an epic "Test Epic" without mode
10
- Then the work item is created successfully
11
- And the work item has NULL mode
12
-
13
- Scenario: Create epic with mode (optional)
14
- Given jettypod is initialized
15
- When I create an epic "Test Epic" with mode "stable"
16
- Then the work item is created successfully
17
- And the work item has mode "stable"
18
-
19
- # Feature scenarios - defaults to discovery
20
- Scenario: Create feature with explicit mode
21
- Given jettypod is initialized
22
- When I create a feature "Test Feature" with mode "speed"
23
- Then the work item is created successfully
24
- And the work item has mode "speed"
25
-
26
- Scenario: Create feature without mode defaults to discovery
27
- Given jettypod is initialized
28
- When I create a feature "Test Feature" without mode
29
- Then the work item is created successfully
30
- And the work item has mode "discovery"
31
-
32
- Scenario: Create feature with invalid mode
33
- Given jettypod is initialized
34
- When I try to create a feature "Test Feature" with mode "invalid"
35
- Then I get an error "Invalid mode"
36
- And no work item is created
37
-
38
- # Bug scenarios - bugs don't have modes
39
- Scenario: Create bug with mode fails
40
- Given jettypod is initialized
41
- When I try to create a bug "Test Bug" with mode "stable"
42
- Then I get an error "Bugs cannot have modes"
43
- And no work item is created
44
-
45
- Scenario: Create bug without mode succeeds with NULL
46
- Given jettypod is initialized
47
- When I create a bug "Test Bug" without mode
48
- Then the work item is created successfully
49
- And the work item has no mode
50
-
51
- # Chore scenarios - chores don't have modes
52
- Scenario: Create chore with mode fails
53
- Given jettypod is initialized
54
- When I try to create a chore "Test Chore" with mode "production"
55
- Then I get an error "Chores do not have modes"
56
- And no work item is created
57
-
58
- Scenario: Create chore without mode succeeds with NULL
59
- Given jettypod is initialized
60
- When I create a chore "Test Chore" without mode
61
- Then the work item is created successfully
62
- And the work item has NULL mode
63
-
64
- # All valid modes
65
- Scenario Outline: Create feature with all valid modes
66
- Given jettypod is initialized
67
- When I create a feature "Test Feature" with mode "<mode>"
68
- Then the work item is created successfully
69
- And the work item has mode "<mode>"
70
-
71
- Examples:
72
- | mode |
73
- | speed |
74
- | discovery |
75
- | stable |
76
- | production |
77
-
78
- # Integration - hierarchical structure
79
- Scenario: Epic with children in different modes
80
- Given jettypod is initialized
81
- And I create an epic "Test Epic" without mode
82
- And I create a feature "Speed Feature" with mode "speed" and parent epic
83
- And I create a bug "Test Bug" with parent epic
84
- And I create a chore "Test Chore" without mode and parent epic
85
- When I view the backlog
86
- Then I see the epic without mode indicator
87
- And I see the feature with mode "speed"
88
- And I see the bug without mode indicator
89
- And I see the chore without mode indicator
90
-
91
- # Edge cases
92
- Scenario: Create multiple items with same title but different modes
93
- Given jettypod is initialized
94
- When I create a feature "Duplicate" with mode "speed"
95
- And I create a feature "Duplicate" with mode "stable"
96
- Then both work items are created successfully
97
- And they have different modes
98
-
99
- Scenario: Mode is case-sensitive
100
- Given jettypod is initialized
101
- When I try to create a feature "Test" with mode "Speed"
102
- Then I get an error "Invalid mode"
103
-
104
- Scenario: Mode with whitespace is invalid
105
- Given jettypod is initialized
106
- When I try to create a feature "Test" with mode " speed "
107
- Then I get an error "Invalid mode"
108
-
109
- Scenario: Empty string mode is treated as NULL
110
- Given jettypod is initialized
111
- When I try to create a feature "Test" with mode ""
112
- Then I get an error "Invalid mode"
@@ -1,482 +0,0 @@
1
- const { createTestEnvironment } = require('../../lib/test-helpers');
2
- const { getDb, closeDb, resetDb } = require('../../lib/database');
3
-
4
- describe('Feature Phase Tracking', () => {
5
- let testEnv;
6
- let db;
7
-
8
- beforeEach(() => {
9
- resetDb();
10
- testEnv = createTestEnvironment();
11
- process.chdir(testEnv.testDir);
12
- db = getDb();
13
- });
14
-
15
- afterEach(async () => {
16
-
17
-
18
- await closeDb();
19
- testEnv.cleanup();
20
- resetDb();
21
- });
22
-
23
- describe('Database Schema', () => {
24
- test('phase column exists in work_items table', async () => {
25
- await new Promise((resolve) => {
26
- db.get('SELECT sql FROM sqlite_master WHERE type="table" AND name="work_items"', [], (err, row) => {
27
- expect(err).toBeNull();
28
- expect(row.sql).toContain('phase');
29
- resolve();
30
- });
31
- });
32
- });
33
-
34
- test('phase column allows NULL values', async () => {
35
- await new Promise((resolve) => {
36
- db.run('INSERT INTO work_items (type, title, phase) VALUES (?, ?, ?)', ['epic', 'Test Epic', null], function(err) {
37
- expect(err).toBeNull();
38
- resolve();
39
- });
40
- });
41
- });
42
-
43
- test('phase column accepts discovery value', async () => {
44
- await new Promise((resolve) => {
45
- db.run('INSERT INTO work_items (type, title, phase) VALUES (?, ?, ?)', ['feature', 'Test Feature', 'discovery'], function(err) {
46
- expect(err).toBeNull();
47
- resolve();
48
- });
49
- });
50
- });
51
-
52
- test('phase column accepts implementation value', async () => {
53
- await new Promise((resolve) => {
54
- db.run('INSERT INTO work_items (type, title, phase) VALUES (?, ?, ?)', ['feature', 'Test Feature', 'implementation'], function(err) {
55
- expect(err).toBeNull();
56
- resolve();
57
- });
58
- });
59
- });
60
- });
61
-
62
- describe('Feature Creation with Default Phase', () => {
63
- test('new features default to discovery phase', async () => {
64
- await new Promise((resolve) => {
65
- db.run('INSERT INTO work_items (type, title, description, parent_id, mode, needs_discovery, phase) VALUES (?, ?, ?, ?, ?, ?, ?)',
66
- ['feature', 'Test Feature', 'Description', null, null, 0, 'discovery'],
67
- function(err) {
68
- expect(err).toBeNull();
69
- const featureId = this.lastID;
70
-
71
- db.get('SELECT phase FROM work_items WHERE id = ?', [featureId], (err, row) => {
72
- expect(err).toBeNull();
73
- expect(row.phase).toBe('discovery');
74
- resolve();
75
- });
76
- }
77
- );
78
- });
79
- });
80
-
81
- test('epics have NULL phase', async () => {
82
- await new Promise((resolve) => {
83
- db.run('INSERT INTO work_items (type, title, description, parent_id, mode, needs_discovery, phase) VALUES (?, ?, ?, ?, ?, ?, ?)',
84
- ['epic', 'Test Epic', 'Description', null, null, 0, null],
85
- function(err) {
86
- expect(err).toBeNull();
87
- const epicId = this.lastID;
88
-
89
- db.get('SELECT phase FROM work_items WHERE id = ?', [epicId], (err, row) => {
90
- expect(err).toBeNull();
91
- expect(row.phase).toBeNull();
92
- resolve();
93
- });
94
- }
95
- );
96
- });
97
- });
98
-
99
- test('chores have NULL phase', async () => {
100
- await new Promise((resolve) => {
101
- db.run('INSERT INTO work_items (type, title, description, parent_id, mode, needs_discovery, phase) VALUES (?, ?, ?, ?, ?, ?, ?)',
102
- ['chore', 'Test Chore', 'Description', null, null, 0, null],
103
- function(err) {
104
- expect(err).toBeNull();
105
- const choreId = this.lastID;
106
-
107
- db.get('SELECT phase FROM work_items WHERE id = ?', [choreId], (err, row) => {
108
- expect(err).toBeNull();
109
- expect(row.phase).toBeNull();
110
- resolve();
111
- });
112
- }
113
- );
114
- });
115
- });
116
-
117
- test('bugs have NULL phase', async () => {
118
- await new Promise((resolve) => {
119
- db.run('INSERT INTO work_items (type, title, description, parent_id, mode, needs_discovery, phase) VALUES (?, ?, ?, ?, ?, ?, ?)',
120
- ['bug', 'Test Bug', 'Description', null, 'stable', 0, null],
121
- function(err) {
122
- expect(err).toBeNull();
123
- const bugId = this.lastID;
124
-
125
- db.get('SELECT phase FROM work_items WHERE id = ?', [bugId], (err, row) => {
126
- expect(err).toBeNull();
127
- expect(row.phase).toBeNull();
128
- resolve();
129
- });
130
- }
131
- );
132
- });
133
- });
134
- });
135
-
136
- describe('Phase Transition (Discovery → Implementation)', () => {
137
- test('can transition feature from discovery to implementation', async () => {
138
- // Create feature in discovery phase
139
- await new Promise((resolve) => {
140
- db.run('INSERT INTO work_items (type, title, phase, mode) VALUES (?, ?, ?, ?)',
141
- ['feature', 'Test Feature', 'discovery', null],
142
- resolve
143
- );
144
- });
145
-
146
- // Transition to implementation
147
- await new Promise((resolve) => {
148
- db.run('UPDATE work_items SET phase = ?, mode = ? WHERE id = ?',
149
- ['implementation', 'speed', 1],
150
- resolve
151
- );
152
- });
153
-
154
- // Verify phase and mode
155
- await new Promise((resolve) => {
156
- db.get('SELECT phase, mode FROM work_items WHERE id = ?', [1], (err, row) => {
157
- expect(err).toBeNull();
158
- expect(row.phase).toBe('implementation');
159
- expect(row.mode).toBe('speed');
160
- resolve();
161
- });
162
- });
163
- });
164
-
165
- test('implementation phase sets mode to speed', async () => {
166
- await new Promise((resolve) => {
167
- db.run('INSERT INTO work_items (type, title, phase, mode) VALUES (?, ?, ?, ?)',
168
- ['feature', 'Test Feature', 'discovery', null],
169
- function(err) {
170
- const featureId = this.lastID;
171
-
172
- db.run('UPDATE work_items SET phase = ?, mode = ? WHERE id = ?',
173
- ['implementation', 'speed', featureId],
174
- () => {
175
- db.get('SELECT mode FROM work_items WHERE id = ?', [featureId], (err, row) => {
176
- expect(row.mode).toBe('speed');
177
- resolve();
178
- });
179
- }
180
- );
181
- }
182
- );
183
- });
184
- });
185
-
186
- test('cannot transition epic (has NULL phase)', async () => {
187
- await new Promise((resolve) => {
188
- db.run('INSERT INTO work_items (type, title, phase) VALUES (?, ?, ?)',
189
- ['epic', 'Test Epic', null],
190
- function(err) {
191
- const epicId = this.lastID;
192
-
193
- db.get('SELECT phase, type FROM work_items WHERE id = ?', [epicId], (err, row) => {
194
- expect(row.phase).toBeNull();
195
- expect(row.type).toBe('epic');
196
- resolve();
197
- });
198
- }
199
- );
200
- });
201
- });
202
-
203
- test('cannot transition chore (has NULL phase)', async () => {
204
- await new Promise((resolve) => {
205
- db.run('INSERT INTO work_items (type, title, phase, mode) VALUES (?, ?, ?, ?)',
206
- ['chore', 'Test Chore', null, null],
207
- function(err) {
208
- const choreId = this.lastID;
209
-
210
- db.get('SELECT phase, type FROM work_items WHERE id = ?', [choreId], (err, row) => {
211
- expect(row.phase).toBeNull();
212
- expect(row.type).toBe('chore');
213
- resolve();
214
- });
215
- }
216
- );
217
- });
218
- });
219
- });
220
-
221
- describe('Phase Display Logic', () => {
222
- test('discovery phase shows with 🔍 icon', () => {
223
- const item = { type: 'feature', phase: 'discovery', mode: null };
224
-
225
- let modeIndicator = '';
226
- if (item.type === 'feature') {
227
- if (item.phase === 'discovery') {
228
- modeIndicator = ' [🔍 discovery]';
229
- } else if (item.mode) {
230
- modeIndicator = ` [${item.mode}]`;
231
- }
232
- }
233
-
234
- expect(modeIndicator).toBe(' [🔍 discovery]');
235
- });
236
-
237
- test('implementation phase shows mode', () => {
238
- const item = { type: 'feature', phase: 'implementation', mode: 'speed' };
239
-
240
- let modeIndicator = '';
241
- if (item.type === 'feature') {
242
- if (item.phase === 'discovery') {
243
- modeIndicator = ' [🔍 discovery]';
244
- } else if (item.mode) {
245
- modeIndicator = ` [${item.mode}]`;
246
- }
247
- }
248
-
249
- expect(modeIndicator).toBe(' [speed]');
250
- });
251
-
252
- test('implementation phase with stable mode', () => {
253
- const item = { type: 'feature', phase: 'implementation', mode: 'stable' };
254
-
255
- let modeIndicator = '';
256
- if (item.type === 'feature') {
257
- if (item.phase === 'discovery') {
258
- modeIndicator = ' [🔍 discovery]';
259
- } else if (item.mode) {
260
- modeIndicator = ` [${item.mode}]`;
261
- }
262
- }
263
-
264
- expect(modeIndicator).toBe(' [stable]');
265
- });
266
-
267
- test('implementation phase with production mode', () => {
268
- const item = { type: 'feature', phase: 'implementation', mode: 'production' };
269
-
270
- let modeIndicator = '';
271
- if (item.type === 'feature') {
272
- if (item.phase === 'discovery') {
273
- modeIndicator = ' [🔍 discovery]';
274
- } else if (item.mode) {
275
- modeIndicator = ` [${item.mode}]`;
276
- }
277
- }
278
-
279
- expect(modeIndicator).toBe(' [production]');
280
- });
281
-
282
- test('epic shows no phase indicator', () => {
283
- const item = { type: 'epic', phase: null, mode: null };
284
-
285
- let modeIndicator = '';
286
- if (item.type === 'feature') {
287
- if (item.phase === 'discovery') {
288
- modeIndicator = ' [🔍 discovery]';
289
- } else if (item.mode) {
290
- modeIndicator = ` [${item.mode}]`;
291
- }
292
- } else if (item.type !== 'epic' && item.mode) {
293
- modeIndicator = ` [${item.mode}]`;
294
- }
295
-
296
- expect(modeIndicator).toBe('');
297
- });
298
-
299
- test('chore shows mode indicator', () => {
300
- const item = { type: 'chore', phase: null, mode: 'speed' };
301
-
302
- let modeIndicator = '';
303
- if (item.type === 'feature') {
304
- if (item.phase === 'discovery') {
305
- modeIndicator = ' [🔍 discovery]';
306
- } else if (item.mode) {
307
- modeIndicator = ` [${item.mode}]`;
308
- }
309
- } else if (item.type !== 'epic' && item.mode) {
310
- modeIndicator = ` [${item.mode}]`;
311
- }
312
-
313
- expect(modeIndicator).toBe(' [speed]');
314
- });
315
-
316
- test('bug shows mode indicator', () => {
317
- const item = { type: 'bug', phase: null, mode: 'stable' };
318
-
319
- let modeIndicator = '';
320
- if (item.type === 'feature') {
321
- if (item.phase === 'discovery') {
322
- modeIndicator = ' [🔍 discovery]';
323
- } else if (item.mode) {
324
- modeIndicator = ` [${item.mode}]`;
325
- }
326
- } else if (item.type !== 'epic' && item.mode) {
327
- modeIndicator = ` [${item.mode}]`;
328
- }
329
-
330
- expect(modeIndicator).toBe(' [stable]');
331
- });
332
- });
333
-
334
- describe('Edge Cases', () => {
335
- test('feature with NULL phase treated as implementation', async () => {
336
- await new Promise((resolve) => {
337
- db.run('INSERT INTO work_items (type, title, phase, mode) VALUES (?, ?, ?, ?)',
338
- ['feature', 'Legacy Feature', null, 'stable'],
339
- function(err) {
340
- const featureId = this.lastID;
341
-
342
- db.get('SELECT phase, mode FROM work_items WHERE id = ?', [featureId], (err, row) => {
343
- expect(row.phase).toBeNull();
344
- expect(row.mode).toBe('stable');
345
- resolve();
346
- });
347
- }
348
- );
349
- });
350
- });
351
-
352
- test('feature in discovery with NULL mode', async () => {
353
- await new Promise((resolve) => {
354
- db.run('INSERT INTO work_items (type, title, phase, mode) VALUES (?, ?, ?, ?)',
355
- ['feature', 'Test Feature', 'discovery', null],
356
- function(err) {
357
- const featureId = this.lastID;
358
-
359
- db.get('SELECT phase, mode FROM work_items WHERE id = ?', [featureId], (err, row) => {
360
- expect(row.phase).toBe('discovery');
361
- expect(row.mode).toBeNull();
362
- resolve();
363
- });
364
- }
365
- );
366
- });
367
- });
368
-
369
- test('feature in implementation with NULL mode defaults to speed', async () => {
370
- await new Promise((resolve) => {
371
- db.run('INSERT INTO work_items (type, title, phase, mode) VALUES (?, ?, ?, ?)',
372
- ['feature', 'Test Feature', 'implementation', null],
373
- function(err) {
374
- const featureId = this.lastID;
375
-
376
- // Simulate what implement command would do
377
- db.run('UPDATE work_items SET mode = ? WHERE id = ? AND mode IS NULL',
378
- ['speed', featureId],
379
- () => {
380
- db.get('SELECT phase, mode FROM work_items WHERE id = ?', [featureId], (err, row) => {
381
- expect(row.phase).toBe('implementation');
382
- expect(row.mode).toBe('speed');
383
- resolve();
384
- });
385
- }
386
- );
387
- }
388
- );
389
- });
390
- });
391
-
392
- test('handles empty phase value', async () => {
393
- await new Promise((resolve) => {
394
- db.run('INSERT INTO work_items (type, title, phase, mode) VALUES (?, ?, ?, ?)',
395
- ['feature', 'Test Feature', '', null],
396
- function(err) {
397
- const featureId = this.lastID;
398
-
399
- db.get('SELECT phase FROM work_items WHERE id = ?', [featureId], (err, row) => {
400
- // Empty string is stored as is
401
- expect(row.phase).toBe('');
402
- resolve();
403
- });
404
- }
405
- );
406
- });
407
- });
408
-
409
- test('multiple features can be in discovery simultaneously', async () => {
410
- await new Promise((resolve) => {
411
- db.serialize(() => {
412
- db.run('INSERT INTO work_items (type, title, phase) VALUES (?, ?, ?)', ['feature', 'Feature 1', 'discovery']);
413
- db.run('INSERT INTO work_items (type, title, phase) VALUES (?, ?, ?)', ['feature', 'Feature 2', 'discovery']);
414
- db.run('INSERT INTO work_items (type, title, phase) VALUES (?, ?, ?)', ['feature', 'Feature 3', 'discovery'], () => {
415
- db.all('SELECT COUNT(*) as count FROM work_items WHERE phase = ?', ['discovery'], (err, rows) => {
416
- expect(rows[0].count).toBe(3);
417
- resolve();
418
- });
419
- });
420
- });
421
- });
422
- });
423
-
424
- test('multiple features can be in implementation simultaneously', async () => {
425
- await new Promise((resolve) => {
426
- db.serialize(() => {
427
- db.run('INSERT INTO work_items (type, title, phase, mode) VALUES (?, ?, ?, ?)', ['feature', 'Feature 1', 'implementation', 'speed']);
428
- db.run('INSERT INTO work_items (type, title, phase, mode) VALUES (?, ?, ?, ?)', ['feature', 'Feature 2', 'implementation', 'stable']);
429
- db.run('INSERT INTO work_items (type, title, phase, mode) VALUES (?, ?, ?, ?)', ['feature', 'Feature 3', 'implementation', 'production'], () => {
430
- db.all('SELECT COUNT(*) as count FROM work_items WHERE phase = ?', ['implementation'], (err, rows) => {
431
- expect(rows[0].count).toBe(3);
432
- resolve();
433
- });
434
- });
435
- });
436
- });
437
- });
438
- });
439
-
440
- describe('Backwards Compatibility', () => {
441
- test('existing features without phase field work correctly', async () => {
442
- // Simulate old feature (before migration)
443
- await new Promise((resolve) => {
444
- db.run('INSERT INTO work_items (type, title, mode) VALUES (?, ?, ?)',
445
- ['feature', 'Legacy Feature', 'stable'],
446
- function(err) {
447
- const featureId = this.lastID;
448
-
449
- db.get('SELECT phase, mode FROM work_items WHERE id = ?', [featureId], (err, row) => {
450
- // Phase will be NULL or default value from schema
451
- expect(row.mode).toBe('stable');
452
- resolve();
453
- });
454
- }
455
- );
456
- });
457
- });
458
-
459
- test('migration sets existing features to implementation phase', async () => {
460
- // Create feature without phase
461
- await new Promise((resolve) => {
462
- db.run('INSERT INTO work_items (type, title, mode, phase) VALUES (?, ?, ?, ?)',
463
- ['feature', 'Old Feature', 'stable', null],
464
- function(err) {
465
- const featureId = this.lastID;
466
-
467
- // Simulate migration
468
- db.run('UPDATE work_items SET phase = ? WHERE type = ? AND phase IS NULL',
469
- ['implementation', 'feature'],
470
- () => {
471
- db.get('SELECT phase FROM work_items WHERE id = ?', [featureId], (err, row) => {
472
- expect(row.phase).toBe('implementation');
473
- resolve();
474
- });
475
- }
476
- );
477
- }
478
- );
479
- });
480
- });
481
- });
482
- });