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,432 +0,0 @@
1
- const {
2
- generateChoresFromStandards,
3
- generateInfrastructureChores
4
- } = require('./production-chore-generator');
5
-
6
- describe('production-chore-generator', () => {
7
- describe('generateChoresFromStandards', () => {
8
- test('generates chores grouped by domain', () => {
9
- const standards = {
10
- preset: 'production-saas',
11
- standards: [
12
- {
13
- id: 'tls_enforced',
14
- domain: 'security',
15
- acceptance: 'HSTS ≥ 6mo; TLS ≥1.2',
16
- reasoning: 'External-facing app with user data'
17
- },
18
- {
19
- id: 'rate_limiting',
20
- domain: 'security',
21
- acceptance: 'Per-IP 100/min, per-token 1000/min',
22
- reasoning: 'Public internet exposure'
23
- },
24
- {
25
- id: 'performance_budgets',
26
- domain: 'performance',
27
- acceptance: 'p95 < 2s, p50 < 500ms',
28
- reasoning: '500 users expect responsive UI'
29
- }
30
- ]
31
- };
32
-
33
- const chores = generateChoresFromStandards(standards, 'Test Feature');
34
-
35
- expect(chores).toHaveLength(2); // security + performance
36
- expect(chores[0].title).toBe('Add security hardening to Test Feature');
37
- expect(chores[1].title).toBe('Add performance hardening to Test Feature');
38
- });
39
-
40
- test('includes acceptance criteria in description', () => {
41
- const standards = {
42
- standards: [
43
- {
44
- id: 'tls_enforced',
45
- domain: 'security',
46
- acceptance: 'HSTS ≥ 6mo; TLS ≥1.2'
47
- }
48
- ]
49
- };
50
-
51
- const chores = generateChoresFromStandards(standards, 'Test Feature');
52
-
53
- expect(chores[0].description).toContain('Acceptance: HSTS ≥ 6mo; TLS ≥1.2');
54
- });
55
-
56
- test('includes reasoning when available', () => {
57
- const standards = {
58
- standards: [
59
- {
60
- id: 'tls_enforced',
61
- domain: 'security',
62
- acceptance: 'TLS 1.2',
63
- reasoning: 'External-facing app with user data'
64
- }
65
- ]
66
- };
67
-
68
- const chores = generateChoresFromStandards(standards, 'Test Feature');
69
-
70
- expect(chores[0].description).toContain('Why: External-facing app with user data');
71
- });
72
-
73
- test('includes pattern when available', () => {
74
- const standards = {
75
- standards: [
76
- {
77
- id: 'tls_enforced',
78
- domain: 'security',
79
- acceptance: 'TLS 1.2',
80
- pattern: 'Use Let\'s Encrypt or cloud-managed certificates'
81
- }
82
- ]
83
- };
84
-
85
- const chores = generateChoresFromStandards(standards, 'Test Feature');
86
-
87
- expect(chores[0].description).toContain('Pattern: Use Let\'s Encrypt or cloud-managed certificates');
88
- });
89
-
90
- test('formats standard IDs as readable text', () => {
91
- const standards = {
92
- standards: [
93
- {
94
- id: 'performance_budgets_enforced',
95
- domain: 'performance',
96
- acceptance: 'test'
97
- }
98
- ]
99
- };
100
-
101
- const chores = generateChoresFromStandards(standards, 'Test Feature');
102
-
103
- expect(chores[0].description).toContain('performance budgets enforced');
104
- });
105
-
106
- test('includes scenario steps in description', () => {
107
- const standards = {
108
- standards: [
109
- {
110
- id: 'test',
111
- domain: 'security',
112
- acceptance: 'test'
113
- }
114
- ]
115
- };
116
-
117
- const chores = generateChoresFromStandards(standards, 'Test Feature');
118
-
119
- expect(chores[0].description).toContain('Scenario steps addressed:');
120
- expect(chores[0].description).toContain('Given the system is deployed to production');
121
- expect(chores[0].description).toContain('When security measures are validated');
122
- });
123
-
124
- test('includes verification instructions', () => {
125
- const standards = {
126
- standards: [
127
- {
128
- id: 'test',
129
- domain: 'security',
130
- acceptance: 'test'
131
- }
132
- ]
133
- };
134
-
135
- const chores = generateChoresFromStandards(standards, 'Test Feature');
136
-
137
- expect(chores[0].description).toContain('Verification:');
138
- expect(chores[0].description).toContain('npx cucumber-js');
139
- expect(chores[0].description).toContain('All production mode scenarios should pass');
140
- });
141
-
142
- test('handles multiple standards in same domain', () => {
143
- const standards = {
144
- standards: [
145
- {
146
- id: 'standard1',
147
- domain: 'security',
148
- acceptance: 'test1'
149
- },
150
- {
151
- id: 'standard2',
152
- domain: 'security',
153
- acceptance: 'test2'
154
- },
155
- {
156
- id: 'standard3',
157
- domain: 'security',
158
- acceptance: 'test3'
159
- }
160
- ]
161
- };
162
-
163
- const chores = generateChoresFromStandards(standards, 'Test Feature');
164
-
165
- expect(chores).toHaveLength(1); // All grouped into one security chore
166
- expect(chores[0].description).toContain('standard1');
167
- expect(chores[0].description).toContain('standard2');
168
- expect(chores[0].description).toContain('standard3');
169
- });
170
- });
171
-
172
- describe('generateInfrastructureChores', () => {
173
- test('filters standards by infrastructure scope', () => {
174
- const standards = {
175
- preset: 'production-saas',
176
- standards: [
177
- {
178
- id: 'database_backups',
179
- domain: 'infrastructure',
180
- scope: 'infrastructure',
181
- acceptance: 'Automated daily backups with 30-day retention'
182
- },
183
- {
184
- id: 'tls_enforced',
185
- domain: 'security',
186
- scope: 'feature',
187
- acceptance: 'HSTS ≥ 6mo; TLS ≥1.2'
188
- },
189
- {
190
- id: 'monitoring_setup',
191
- domain: 'infrastructure',
192
- scope: 'infrastructure',
193
- acceptance: 'Error tracking and APM configured'
194
- }
195
- ]
196
- };
197
-
198
- const chores = generateInfrastructureChores(standards);
199
-
200
- expect(chores).toHaveLength(2); // Only infrastructure-scoped
201
- expect(chores[0].title).toBe('Database Backups');
202
- expect(chores[1].title).toBe('Monitoring Setup');
203
- });
204
-
205
- test('creates one chore per standard (not grouped by domain)', () => {
206
- const standards = {
207
- standards: [
208
- {
209
- id: 'tls_enforced',
210
- domain: 'security',
211
- scope: 'infrastructure',
212
- acceptance: 'HSTS ≥ 6mo'
213
- },
214
- {
215
- id: 'rate_limiting',
216
- domain: 'security',
217
- scope: 'infrastructure',
218
- acceptance: 'Per-IP 100/min'
219
- },
220
- {
221
- id: 'monitoring_setup',
222
- domain: 'infrastructure',
223
- scope: 'infrastructure',
224
- acceptance: 'APM configured'
225
- }
226
- ]
227
- };
228
-
229
- const chores = generateInfrastructureChores(standards);
230
-
231
- expect(chores).toHaveLength(3); // One per standard, not grouped
232
- expect(chores[0].title).toBe('Tls Enforced');
233
- expect(chores[1].title).toBe('Rate Limiting');
234
- expect(chores[2].title).toBe('Monitoring Setup');
235
- });
236
-
237
- test('includes acceptance criteria in chore description', () => {
238
- const standards = {
239
- standards: [
240
- {
241
- id: 'database_backups',
242
- domain: 'infrastructure',
243
- scope: 'infrastructure',
244
- acceptance: 'Automated daily backups with 30-day retention'
245
- }
246
- ]
247
- };
248
-
249
- const chores = generateInfrastructureChores(standards);
250
-
251
- expect(chores[0].description).toContain('Automated daily backups with 30-day retention');
252
- });
253
-
254
- test('includes reasoning when available', () => {
255
- const standards = {
256
- standards: [
257
- {
258
- id: 'database_backups',
259
- domain: 'infrastructure',
260
- scope: 'infrastructure',
261
- acceptance: 'Daily backups',
262
- reasoning: 'Data loss would be catastrophic'
263
- }
264
- ]
265
- };
266
-
267
- const chores = generateInfrastructureChores(standards);
268
-
269
- expect(chores[0].description).toContain('Why:\nData loss would be catastrophic');
270
- });
271
-
272
- test('includes pattern guidance when available', () => {
273
- const standards = {
274
- standards: [
275
- {
276
- id: 'database_backups',
277
- domain: 'infrastructure',
278
- scope: 'infrastructure',
279
- acceptance: 'Daily backups',
280
- pattern: 'Use pg_dump with --clean flag'
281
- }
282
- ]
283
- };
284
-
285
- const chores = generateInfrastructureChores(standards);
286
-
287
- expect(chores[0].description).toContain('Pattern:\nUse pg_dump with --clean flag');
288
- });
289
-
290
- test('formats standard ID as readable title', () => {
291
- const standards = {
292
- standards: [
293
- {
294
- id: 'automated_database_backups_configured',
295
- domain: 'infrastructure',
296
- scope: 'infrastructure',
297
- acceptance: 'test'
298
- }
299
- ]
300
- };
301
-
302
- const chores = generateInfrastructureChores(standards);
303
-
304
- expect(chores[0].title).toBe('Automated Database Backups Configured');
305
- });
306
-
307
- test('excludes feature-scoped standards', () => {
308
- const standards = {
309
- standards: [
310
- {
311
- id: 'input_validation',
312
- domain: 'security',
313
- scope: 'feature',
314
- acceptance: 'All inputs validated'
315
- },
316
- {
317
- id: 'sql_injection_prevention',
318
- domain: 'security',
319
- scope: 'feature',
320
- acceptance: 'Parameterized queries'
321
- }
322
- ]
323
- };
324
-
325
- const chores = generateInfrastructureChores(standards);
326
-
327
- expect(chores).toHaveLength(0); // No infrastructure-scoped standards
328
- });
329
-
330
- test('handles standards without scope field', () => {
331
- const standards = {
332
- standards: [
333
- {
334
- id: 'database_backups',
335
- domain: 'infrastructure',
336
- acceptance: 'Daily backups'
337
- // Missing scope field
338
- }
339
- ]
340
- };
341
-
342
- const chores = generateInfrastructureChores(standards);
343
-
344
- expect(chores).toHaveLength(0); // No scope means not infrastructure
345
- });
346
-
347
- test('validates standards object structure', () => {
348
- expect(() => {
349
- generateInfrastructureChores(null);
350
- }).toThrow('Standards object is required');
351
-
352
- expect(() => {
353
- generateInfrastructureChores({});
354
- }).toThrow('Standards object missing "standards" array');
355
-
356
- expect(() => {
357
- generateInfrastructureChores({ standards: 'not-an-array' });
358
- }).toThrow('Standards "standards" field must be an array');
359
- });
360
-
361
- test('validates individual standard structure', () => {
362
- const standards = {
363
- standards: [
364
- {
365
- // Missing id
366
- domain: 'infrastructure',
367
- scope: 'infrastructure',
368
- acceptance: 'test'
369
- }
370
- ]
371
- };
372
-
373
- expect(() => {
374
- generateInfrastructureChores(standards);
375
- }).toThrow('Standard missing required field "id"');
376
- });
377
-
378
- test('includes domain in chore description', () => {
379
- const standards = {
380
- standards: [
381
- {
382
- id: 'monitoring_setup',
383
- domain: 'infrastructure',
384
- scope: 'infrastructure',
385
- acceptance: 'APM configured'
386
- }
387
- ]
388
- };
389
-
390
- const chores = generateInfrastructureChores(standards);
391
-
392
- expect(chores[0].domain).toBe('infrastructure');
393
- });
394
-
395
- test('handles mixed scope standards correctly', () => {
396
- const standards = {
397
- standards: [
398
- {
399
- id: 'infra1',
400
- domain: 'security',
401
- scope: 'infrastructure',
402
- acceptance: 'test1'
403
- },
404
- {
405
- id: 'feature1',
406
- domain: 'security',
407
- scope: 'feature',
408
- acceptance: 'test2'
409
- },
410
- {
411
- id: 'infra2',
412
- domain: 'performance',
413
- scope: 'infrastructure',
414
- acceptance: 'test3'
415
- },
416
- {
417
- id: 'feature2',
418
- domain: 'performance',
419
- scope: 'feature',
420
- acceptance: 'test4'
421
- }
422
- ]
423
- };
424
-
425
- const chores = generateInfrastructureChores(standards);
426
-
427
- expect(chores).toHaveLength(2);
428
- expect(chores[0].title).toBe('Infra1');
429
- expect(chores[1].title).toBe('Infra2');
430
- });
431
- });
432
- });
@@ -1,277 +0,0 @@
1
- const {
2
- detectContext,
3
- hasProductionScenarios,
4
- hasProductionChores,
5
- getTimeSinceStableCompletion
6
- } = require('./production-context-detector');
7
- const fs = require('fs');
8
- const path = require('path');
9
- const { getDb } = require('./database');
10
-
11
- describe('production-context-detector', () => {
12
- let testDir;
13
- let db;
14
- let featureId;
15
-
16
- beforeEach(() => {
17
- // Create test directory
18
- testDir = path.join(__dirname, '../test-tmp', `context-detector-${Date.now()}`);
19
- fs.mkdirSync(testDir, { recursive: true });
20
- process.chdir(testDir);
21
-
22
- // Initialize database
23
- db = getDb();
24
- });
25
-
26
- afterEach((done) => {
27
- if (db) {
28
- db.close(() => {
29
- if (testDir && fs.existsSync(testDir)) {
30
- fs.rmSync(testDir, { recursive: true, force: true });
31
- }
32
- done();
33
- });
34
- } else {
35
- if (testDir && fs.existsSync(testDir)) {
36
- fs.rmSync(testDir, { recursive: true, force: true });
37
- }
38
- done();
39
- }
40
- });
41
-
42
- describe('hasProductionScenarios', () => {
43
- test('returns false when no scenario file', async () => {
44
- const result = await hasProductionScenarios(null);
45
- expect(result).toBe(false);
46
- });
47
-
48
- test('returns false when scenario file does not exist', async () => {
49
- const result = await hasProductionScenarios('nonexistent.feature');
50
- expect(result).toBe(false);
51
- });
52
-
53
- test('returns true when file contains "# PRODUCTION MODE"', async () => {
54
- const scenarioFile = path.join(testDir, 'test.feature');
55
- fs.writeFileSync(scenarioFile, 'Feature: Test\n\n# PRODUCTION MODE\nScenario: Production test\n');
56
-
57
- const result = await hasProductionScenarios('test.feature');
58
- expect(result).toBe(true);
59
- });
60
-
61
- test('returns true when file contains "production mode"', async () => {
62
- const scenarioFile = path.join(testDir, 'test.feature');
63
- fs.writeFileSync(scenarioFile, 'Feature: Test\n\nScenario: Test (production mode)\n');
64
-
65
- const result = await hasProductionScenarios('test.feature');
66
- expect(result).toBe(true);
67
- });
68
-
69
- test('returns false when file has no production markers', async () => {
70
- const scenarioFile = path.join(testDir, 'test.feature');
71
- fs.writeFileSync(scenarioFile, 'Feature: Test\n\nScenario: Happy path\n');
72
-
73
- const result = await hasProductionScenarios('test.feature');
74
- expect(result).toBe(false);
75
- });
76
- });
77
-
78
- describe('hasProductionChores', () => {
79
- beforeEach((done) => {
80
- // Create a test feature
81
- db.run(
82
- `INSERT INTO work_items (type, title, status, mode, phase) VALUES (?, ?, ?, ?, ?)`,
83
- ['feature', 'Test Feature', 'done', 'stable', 'implementation'],
84
- function(err) {
85
- if (err) return done(err);
86
- featureId = this.lastID;
87
- done();
88
- }
89
- );
90
- });
91
-
92
- test('returns false when no production chores exist', async () => {
93
- const result = await hasProductionChores(featureId);
94
- expect(result).toBe(false);
95
- });
96
-
97
- test('returns true when production chores exist', async () => {
98
- // Create production chore
99
- await new Promise((resolve, reject) => {
100
- db.run(
101
- `INSERT INTO work_items (type, title, parent_id, status, mode) VALUES (?, ?, ?, ?, ?)`,
102
- ['chore', 'Production chore', featureId, 'todo', 'production'],
103
- (err) => {
104
- if (err) return reject(err);
105
- resolve();
106
- }
107
- );
108
- });
109
-
110
- const result = await hasProductionChores(featureId);
111
- expect(result).toBe(true);
112
- });
113
- });
114
-
115
- describe('getTimeSinceStableCompletion', () => {
116
- beforeEach((done) => {
117
- // Create a test feature
118
- db.run(
119
- `INSERT INTO work_items (type, title, status, mode, phase) VALUES (?, ?, ?, ?, ?)`,
120
- ['feature', 'Test Feature', 'done', 'stable', 'implementation'],
121
- function(err) {
122
- if (err) return done(err);
123
- featureId = this.lastID;
124
- done();
125
- }
126
- );
127
- });
128
-
129
- test('returns 0 when stable chore completed today', async () => {
130
- // Create stable chore completed today
131
- await new Promise((resolve, reject) => {
132
- db.run(
133
- `INSERT INTO work_items (type, title, parent_id, status, mode, completed_at)
134
- VALUES (?, ?, ?, ?, ?, ?)`,
135
- ['chore', 'Stable chore', featureId, 'done', 'stable', new Date().toISOString()],
136
- (err) => {
137
- if (err) return reject(err);
138
- resolve();
139
- }
140
- );
141
- });
142
-
143
- const result = await getTimeSinceStableCompletion(featureId);
144
- expect(result).toBe(0);
145
- });
146
-
147
- test('returns correct days when stable chore completed 7 days ago', async () => {
148
- const sevenDaysAgo = new Date();
149
- sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
150
-
151
- // Create stable chore completed 7 days ago
152
- await new Promise((resolve, reject) => {
153
- db.run(
154
- `INSERT INTO work_items (type, title, parent_id, status, mode, completed_at)
155
- VALUES (?, ?, ?, ?, ?, ?)`,
156
- ['chore', 'Stable chore', featureId, 'done', 'stable', sevenDaysAgo.toISOString()],
157
- (err) => {
158
- if (err) return reject(err);
159
- resolve();
160
- }
161
- );
162
- });
163
-
164
- const result = await getTimeSinceStableCompletion(featureId);
165
- expect(result).toBe(7);
166
- });
167
- });
168
-
169
- describe('detectContext', () => {
170
- beforeEach((done) => {
171
- // Create a test feature
172
- const yesterday = new Date();
173
- yesterday.setDate(yesterday.getDate() - 1);
174
-
175
- db.run(
176
- `INSERT INTO work_items (type, title, status, mode, phase, completed_at) VALUES (?, ?, ?, ?, ?, ?)`,
177
- ['feature', 'Test Feature', 'done', 'stable', 'implementation', yesterday.toISOString()],
178
- function(err) {
179
- if (err) return done(err);
180
- featureId = this.lastID;
181
- done();
182
- }
183
- );
184
- });
185
-
186
- test('detects SCENARIO_A when fresh from stable with production content', async () => {
187
- // Create scenario file with production markers
188
- const scenarioFile = path.join(testDir, 'test.feature');
189
- fs.writeFileSync(scenarioFile, 'Feature: Test\n\n# PRODUCTION MODE\nScenario: Production test\n');
190
-
191
- // Update feature with scenario file
192
- await new Promise((resolve, reject) => {
193
- db.run(
194
- `UPDATE work_items SET scenario_file = ? WHERE id = ?`,
195
- ['test.feature', featureId],
196
- (err) => {
197
- if (err) return reject(err);
198
- resolve();
199
- }
200
- );
201
- });
202
-
203
- // Create production chore
204
- await new Promise((resolve, reject) => {
205
- db.run(
206
- `INSERT INTO work_items (type, title, parent_id, status, mode) VALUES (?, ?, ?, ?, ?)`,
207
- ['chore', 'Production chore', featureId, 'todo', 'production'],
208
- (err) => {
209
- if (err) return reject(err);
210
- resolve();
211
- }
212
- );
213
- });
214
-
215
- const result = await detectContext(featureId);
216
- expect(result).toBe('SCENARIO_A');
217
- });
218
-
219
- test('detects SCENARIO_B when gap in time', async () => {
220
- // Create scenario file with production markers
221
- const scenarioFile = path.join(testDir, 'test.feature');
222
- fs.writeFileSync(scenarioFile, 'Feature: Test\n\n# PRODUCTION MODE\nScenario: Production test\n');
223
-
224
- // Update feature with old date
225
- const oldDate = new Date();
226
- oldDate.setDate(oldDate.getDate() - 10);
227
-
228
- await new Promise((resolve, reject) => {
229
- db.run(
230
- `UPDATE work_items SET scenario_file = ?, completed_at = ? WHERE id = ?`,
231
- ['test.feature', oldDate.toISOString(), featureId],
232
- (err) => {
233
- if (err) return reject(err);
234
- resolve();
235
- }
236
- );
237
- });
238
-
239
- // Create production chore
240
- await new Promise((resolve, reject) => {
241
- db.run(
242
- `INSERT INTO work_items (type, title, parent_id, status, mode) VALUES (?, ?, ?, ?, ?)`,
243
- ['chore', 'Production chore', featureId, 'todo', 'production'],
244
- (err) => {
245
- if (err) return reject(err);
246
- resolve();
247
- }
248
- );
249
- });
250
-
251
- const result = await detectContext(featureId);
252
- expect(result).toBe('SCENARIO_B');
253
- });
254
-
255
- test('detects SCENARIO_C when no production content exists', async () => {
256
- // Create scenario file WITHOUT production markers
257
- const scenarioFile = path.join(testDir, 'test.feature');
258
- fs.writeFileSync(scenarioFile, 'Feature: Test\n\nScenario: Happy path\n');
259
-
260
- await new Promise((resolve, reject) => {
261
- db.run(
262
- `UPDATE work_items SET scenario_file = ? WHERE id = ?`,
263
- ['test.feature', featureId],
264
- (err) => {
265
- if (err) return reject(err);
266
- resolve();
267
- }
268
- );
269
- });
270
-
271
- // No production chores created
272
-
273
- const result = await detectContext(featureId);
274
- expect(result).toBe('SCENARIO_C');
275
- });
276
- });
277
- });