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,862 +0,0 @@
1
- const { Given, When, Then } = require('@cucumber/cucumber');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const assert = require('assert');
5
-
6
- let testDir;
7
- let db;
8
- let featureId;
9
- let testFeatureFile;
10
- let productionStandardsFile;
11
- let skillOutput;
12
-
13
- // Given Steps
14
-
15
- Given('a feature just completed stable mode within {int} day', async function(days) {
16
- // Setup test environment
17
- testDir = path.join(__dirname, '../../test-tmp', `production-mode-${Date.now()}`);
18
- fs.mkdirSync(testDir, { recursive: true });
19
- process.chdir(testDir);
20
-
21
- // Initialize database
22
- const { getDb } = require('../../lib/database');
23
- db = getDb();
24
-
25
- // Create feature with stable mode completed recently
26
- const yesterday = new Date();
27
- yesterday.setDate(yesterday.getDate() - 1);
28
-
29
- await new Promise((resolve, reject) => {
30
- db.run(
31
- `INSERT INTO work_items (type, title, description, status, mode, phase, completed_at)
32
- VALUES (?, ?, ?, ?, ?, ?, ?)`,
33
- ['feature', 'Test Feature', 'A test feature', 'done', 'stable', 'implementation', yesterday.toISOString()],
34
- function(err) {
35
- if (err) reject(err);
36
- featureId = this.lastID;
37
- resolve();
38
- }
39
- );
40
- });
41
- });
42
-
43
- Given('production scenarios already exist in the feature file', function() {
44
- // Create test feature file with production scenarios
45
- const featuresDir = path.join(testDir, 'features');
46
- fs.mkdirSync(featuresDir, { recursive: true });
47
-
48
- testFeatureFile = path.join(featuresDir, 'test-feature.feature');
49
- fs.writeFileSync(testFeatureFile, `Feature: Test Feature
50
-
51
- Scenario: Happy path (speed mode)
52
- Given initial state
53
- When user takes action
54
- Then success
55
-
56
- Scenario: Error handling (stable mode)
57
- Given initial state
58
- When invalid action
59
- Then appropriate error
60
-
61
- # PRODUCTION MODE SCENARIOS
62
- Scenario: Load testing - handle 1000 concurrent users (production mode)
63
- Given system under load
64
- When 1000 concurrent requests
65
- Then p95 latency < 2s
66
-
67
- Scenario: Security - rate limiting enforcement (production mode)
68
- Given anonymous user
69
- When exceeds 100 req/min
70
- Then returns 429 with Retry-After
71
- `);
72
-
73
- // Update database with scenario file path
74
- return new Promise((resolve, reject) => {
75
- db.run(
76
- `UPDATE work_items SET scenario_file = ? WHERE id = ?`,
77
- [testFeatureFile, featureId],
78
- (err) => {
79
- if (err) reject(err);
80
- resolve();
81
- }
82
- );
83
- });
84
- });
85
-
86
- Given('production chores already exist for the feature', async function() {
87
- // Create production chores
88
- const chores = [
89
- 'Implement load testing for Test Feature',
90
- 'Implement rate limiting for Test Feature'
91
- ];
92
-
93
- for (const title of chores) {
94
- await new Promise((resolve, reject) => {
95
- db.run(
96
- `INSERT INTO work_items (type, title, parent_id, status, mode)
97
- VALUES (?, ?, ?, ?, ?)`,
98
- ['chore', title, featureId, 'todo', 'production'],
99
- (err) => {
100
- if (err) reject(err);
101
- resolve();
102
- }
103
- );
104
- });
105
- }
106
- });
107
-
108
- Given('production standards file exists', function() {
109
- // Create .jettypod directory and standards file
110
- const jettypodDir = path.join(testDir, '.jettypod');
111
- fs.mkdirSync(jettypodDir, { recursive: true });
112
-
113
- productionStandardsFile = path.join(jettypodDir, 'production-standards.json');
114
-
115
- const standards = {
116
- preset: 'production-saas',
117
- standards: [
118
- {
119
- id: 'performance_budgets',
120
- domain: 'performance',
121
- acceptance: 'p95 < 2s, p50 < 500ms; enforced in CI'
122
- },
123
- {
124
- id: 'waf_rate_limits',
125
- domain: 'security',
126
- acceptance: 'Per-IP 100/min, per-token 1000/min; 429s with Retry-After'
127
- }
128
- ]
129
- };
130
-
131
- fs.writeFileSync(productionStandardsFile, JSON.stringify(standards, null, 2));
132
- });
133
-
134
- Given('a feature completed stable mode over {int} days ago', async function(days) {
135
- // Setup test environment
136
- testDir = path.join(__dirname, '../../test-tmp', `production-mode-${Date.now()}`);
137
- fs.mkdirSync(testDir, { recursive: true });
138
- process.chdir(testDir);
139
-
140
- // Initialize database
141
- const { getDb } = require('../../lib/database');
142
- db = getDb();
143
-
144
- // Create feature with stable mode completed 7+ days ago
145
- const oldDate = new Date();
146
- oldDate.setDate(oldDate.getDate() - days);
147
-
148
- await new Promise((resolve, reject) => {
149
- db.run(
150
- `INSERT INTO work_items (type, title, description, status, mode, phase, completed_at)
151
- VALUES (?, ?, ?, ?, ?, ?, ?)`,
152
- ['feature', 'Test Feature', 'A test feature', 'done', 'stable', 'implementation', oldDate.toISOString()],
153
- function(err) {
154
- if (err) reject(err);
155
- featureId = this.lastID;
156
- resolve();
157
- }
158
- );
159
- });
160
- });
161
-
162
- Given('a feature in stable mode with no production scenarios', async function() {
163
- // Setup test environment
164
- testDir = path.join(__dirname, '../../test-tmp', `production-mode-${Date.now()}`);
165
- fs.mkdirSync(testDir, { recursive: true });
166
- process.chdir(testDir);
167
-
168
- // Initialize database
169
- const { getDb } = require('../../lib/database');
170
- db = getDb();
171
-
172
- // Create feature without production scenarios
173
- await new Promise((resolve, reject) => {
174
- db.run(
175
- `INSERT INTO work_items (type, title, description, status, mode, phase)
176
- VALUES (?, ?, ?, ?, ?, ?)`,
177
- ['feature', 'New Feature', 'A feature without production scenarios', 'done', 'stable', 'implementation'],
178
- function(err) {
179
- if (err) reject(err);
180
- featureId = this.lastID;
181
- resolve();
182
- }
183
- );
184
- });
185
-
186
- // Create basic feature file without production scenarios
187
- const featuresDir = path.join(testDir, 'features');
188
- fs.mkdirSync(featuresDir, { recursive: true });
189
-
190
- testFeatureFile = path.join(featuresDir, 'new-feature.feature');
191
- fs.writeFileSync(testFeatureFile, `Feature: New Feature
192
-
193
- Scenario: Happy path (speed mode)
194
- Given initial state
195
- When user takes action
196
- Then success
197
-
198
- Scenario: Error handling (stable mode)
199
- Given initial state
200
- When invalid action
201
- Then appropriate error
202
- `);
203
-
204
- // Update database with scenario file path
205
- return new Promise((resolve, reject) => {
206
- db.run(
207
- `UPDATE work_items SET scenario_file = ? WHERE id = ?`,
208
- [testFeatureFile, featureId],
209
- (err) => {
210
- if (err) reject(err);
211
- resolve();
212
- }
213
- );
214
- });
215
- });
216
-
217
- Given('no production chores exist for the feature', function() {
218
- // Verify no production chores exist
219
- return new Promise((resolve, reject) => {
220
- db.all(
221
- `SELECT * FROM work_items WHERE parent_id = ? AND mode = ?`,
222
- [featureId, 'production'],
223
- (err, rows) => {
224
- if (err) reject(err);
225
- assert.strictEqual(rows.length, 0, 'No production chores should exist yet');
226
- resolve();
227
- }
228
- );
229
- });
230
- });
231
-
232
- Given('production standards file exists from external-transition', function() {
233
- // Create .jettypod directory and standards file with richer content
234
- const jettypodDir = path.join(testDir, '.jettypod');
235
- fs.mkdirSync(jettypodDir, { recursive: true });
236
-
237
- productionStandardsFile = path.join(jettypodDir, 'production-standards.json');
238
-
239
- const standards = {
240
- preset: 'production-saas',
241
- refinement: {
242
- app_description: 'Project management for software teams',
243
- user_count: '100-1k',
244
- downtime_impact: 'revenue_loss',
245
- compliance: ['GDPR']
246
- },
247
- standards: [
248
- {
249
- id: 'performance_budgets',
250
- domain: 'performance',
251
- acceptance: 'p95 < 2s, p50 < 500ms; enforced in CI',
252
- reasoning: '500 users expect responsive UI'
253
- },
254
- {
255
- id: 'waf_rate_limits',
256
- domain: 'security',
257
- acceptance: 'Per-IP 100/min, per-token 1000/min; 429s with Retry-After',
258
- reasoning: 'Public internet exposure'
259
- },
260
- {
261
- id: 'gdpr_export',
262
- domain: 'compliance',
263
- acceptance: 'Self-serve export API (JSON/CSV); ≤ 30d SLA',
264
- reasoning: 'GDPR compliance for EU users'
265
- }
266
- ]
267
- };
268
-
269
- fs.writeFileSync(productionStandardsFile, JSON.stringify(standards, null, 2));
270
- });
271
-
272
- // When Steps
273
-
274
- When('I start the production-mode skill for this feature', function() {
275
- // Simulate starting the production-mode skill
276
- // The skill would detect context and act accordingly
277
- skillOutput = {
278
- featureId: featureId,
279
- scenarioFile: testFeatureFile,
280
- standardsFile: productionStandardsFile
281
- };
282
- });
283
-
284
- // Then Steps
285
-
286
- Then('it detects Scenario A \\(hot context)', function() {
287
- // Verify detection logic would identify Scenario A
288
- // Check: scenarios exist, chores exist, recent stable completion
289
- return new Promise((resolve, reject) => {
290
- db.get(
291
- `SELECT * FROM work_items WHERE id = ?`,
292
- [featureId],
293
- (err, feature) => {
294
- if (err) reject(err);
295
-
296
- const completedDate = new Date(feature.completed_at);
297
- const daysSince = Math.floor((Date.now() - completedDate.getTime()) / (1000 * 60 * 60 * 24));
298
-
299
- assert.ok(daysSince <= 1, 'Feature should be recently completed');
300
- assert.ok(fs.existsSync(testFeatureFile), 'Scenario file should exist');
301
- assert.ok(testFeatureFile.includes('PRODUCTION MODE'), 'Should have production scenarios');
302
-
303
- resolve();
304
- }
305
- );
306
- });
307
- });
308
-
309
- Then('it validates existing scenarios against current standards', function() {
310
- // Verify validation would occur
311
- const scenarioContent = fs.readFileSync(testFeatureFile, 'utf-8');
312
- const standards = JSON.parse(fs.readFileSync(productionStandardsFile, 'utf-8'));
313
-
314
- assert.ok(scenarioContent.includes('production mode'), 'Should have production scenarios');
315
- assert.ok(standards.standards.length > 0, 'Should have standards to validate against');
316
- });
317
-
318
- Then('it identifies no gaps', function() {
319
- // Check that existing scenarios cover required standards
320
- const scenarioContent = fs.readFileSync(testFeatureFile, 'utf-8');
321
- const standards = JSON.parse(fs.readFileSync(productionStandardsFile, 'utf-8'));
322
-
323
- // Verify key standards are covered
324
- assert.ok(scenarioContent.includes('Load testing'), 'Should have performance scenario');
325
- assert.ok(scenarioContent.includes('rate limiting'), 'Should have security scenario');
326
- });
327
-
328
- Then('it proceeds to implement existing production chores', function() {
329
- // Verify production chores exist and are ready to implement
330
- return new Promise((resolve, reject) => {
331
- db.all(
332
- `SELECT * FROM work_items WHERE parent_id = ? AND mode = ?`,
333
- [featureId, 'production'],
334
- (err, rows) => {
335
- if (err) reject(err);
336
- assert.ok(rows.length > 0, 'Production chores should exist');
337
- assert.strictEqual(rows[0].status, 'todo', 'Chores should be ready to implement');
338
- resolve();
339
- }
340
- );
341
- });
342
- });
343
-
344
- Then('it detects Scenario B \\(cold context)', function() {
345
- // Verify detection logic would identify Scenario B
346
- return new Promise((resolve, reject) => {
347
- db.get(
348
- `SELECT * FROM work_items WHERE id = ?`,
349
- [featureId],
350
- (err, feature) => {
351
- if (err) reject(err);
352
-
353
- const completedDate = new Date(feature.completed_at);
354
- const daysSince = Math.floor((Date.now() - completedDate.getTime()) / (1000 * 60 * 60 * 24));
355
-
356
- assert.ok(daysSince > 1, 'Feature should be older than 1 day');
357
- resolve();
358
- }
359
- );
360
- });
361
- });
362
-
363
- Then('it re-validates existing scenarios against current standards', function() {
364
- // Same as validation but emphasizes re-checking
365
- const scenarioContent = fs.readFileSync(testFeatureFile, 'utf-8');
366
- const standards = JSON.parse(fs.readFileSync(productionStandardsFile, 'utf-8'));
367
-
368
- assert.ok(scenarioContent.includes('production mode'), 'Should have production scenarios');
369
- assert.ok(standards.standards.length > 0, 'Should have current standards');
370
- });
371
-
372
- Then('it checks if project state changed since stable mode', function() {
373
- // Verify project state check would occur
374
- const config = require('../../lib/config');
375
- const projectConfig = config.read();
376
-
377
- // Config should be readable (state may be internal or external)
378
- assert.ok(projectConfig.hasOwnProperty('project_state'), 'Should check project state');
379
- });
380
-
381
- Then('it identifies any gaps in standards coverage', function() {
382
- // Check for gaps between scenarios and standards
383
- const scenarioContent = fs.readFileSync(testFeatureFile, 'utf-8');
384
- const standards = JSON.parse(fs.readFileSync(productionStandardsFile, 'utf-8'));
385
-
386
- // In this test, scenarios should cover standards
387
- assert.ok(scenarioContent.length > 0, 'Should have scenarios to check');
388
- assert.ok(standards.standards.length > 0, 'Should have standards to check against');
389
- });
390
-
391
- Then('it updates scenarios if needed', function() {
392
- // Verify ability to update scenarios (file is writable)
393
- assert.ok(fs.existsSync(testFeatureFile), 'Scenario file should exist');
394
-
395
- const stats = fs.statSync(testFeatureFile);
396
- assert.ok(stats.isFile(), 'Should be a file that can be updated');
397
- });
398
-
399
- Then('it detects Scenario C \\(post-transition)', function() {
400
- // Verify detection logic would identify Scenario C
401
- return new Promise((resolve, reject) => {
402
- db.all(
403
- `SELECT * FROM work_items WHERE parent_id = ? AND mode = ?`,
404
- [featureId, 'production'],
405
- (err, rows) => {
406
- if (err) reject(err);
407
-
408
- const scenarioContent = fs.readFileSync(testFeatureFile, 'utf-8');
409
- const hasProductionScenarios = scenarioContent.includes('production mode');
410
-
411
- assert.strictEqual(rows.length, 0, 'No production chores should exist');
412
- assert.ok(!hasProductionScenarios, 'No production scenarios should exist');
413
-
414
- resolve();
415
- }
416
- );
417
- });
418
- });
419
-
420
- Then('it reads production standards from file', function() {
421
- // Verify standards file can be read
422
- assert.ok(fs.existsSync(productionStandardsFile), 'Standards file should exist');
423
-
424
- const standards = JSON.parse(fs.readFileSync(productionStandardsFile, 'utf-8'));
425
- assert.ok(standards.standards, 'Should have standards array');
426
- assert.ok(standards.standards.length > 0, 'Should have at least one standard');
427
- });
428
-
429
- Then('it generates production scenarios from activated standards', function() {
430
- // Verify standards could be converted to scenarios
431
- const standards = JSON.parse(fs.readFileSync(productionStandardsFile, 'utf-8'));
432
-
433
- // Each standard should have acceptance criteria that can become scenarios
434
- standards.standards.forEach(standard => {
435
- assert.ok(standard.acceptance, `Standard ${standard.id} should have acceptance criteria`);
436
- assert.ok(standard.domain, `Standard ${standard.id} should have domain`);
437
- });
438
- });
439
-
440
- Then('it appends scenarios to the feature file', function() {
441
- // Verify scenarios can be appended
442
- const originalContent = fs.readFileSync(testFeatureFile, 'utf-8');
443
-
444
- // Simulate appending a scenario
445
- const newScenario = '\n# PRODUCTION MODE SCENARIO\nScenario: Test scenario\n Given test\n When test\n Then test\n';
446
- fs.appendFileSync(testFeatureFile, newScenario);
447
-
448
- const updatedContent = fs.readFileSync(testFeatureFile, 'utf-8');
449
- assert.ok(updatedContent.includes('Test scenario'), 'Scenario should be appended');
450
- assert.ok(updatedContent.length > originalContent.length, 'Content should be longer');
451
- });
452
-
453
- Then('it proposes production chores based on standards', function() {
454
- // Verify chores could be proposed from standards
455
- const standards = JSON.parse(fs.readFileSync(productionStandardsFile, 'utf-8'));
456
-
457
- // Each standard should be convertible to a chore
458
- standards.standards.forEach(standard => {
459
- assert.ok(standard.id, 'Standard should have ID for chore naming');
460
- assert.ok(standard.acceptance, 'Standard should have acceptance for chore description');
461
- });
462
- });
463
-
464
- Then('it creates chores after user confirmation', async function() {
465
- // Simulate creating chores
466
- const standards = JSON.parse(fs.readFileSync(productionStandardsFile, 'utf-8'));
467
-
468
- // Create one chore per standard
469
- for (const standard of standards.standards) {
470
- await new Promise((resolve, reject) => {
471
- db.run(
472
- `INSERT INTO work_items (type, title, parent_id, status, mode)
473
- VALUES (?, ?, ?, ?, ?)`,
474
- ['chore', `Implement ${standard.id}`, featureId, 'todo', 'production'],
475
- (err) => {
476
- if (err) reject(err);
477
- resolve();
478
- }
479
- );
480
- });
481
- }
482
-
483
- // Verify chores were created
484
- return new Promise((resolve, reject) => {
485
- db.all(
486
- `SELECT * FROM work_items WHERE parent_id = ? AND mode = ?`,
487
- [featureId, 'production'],
488
- (err, rows) => {
489
- if (err) reject(err);
490
- assert.strictEqual(rows.length, standards.standards.length, 'Should create one chore per standard');
491
- resolve();
492
- }
493
- );
494
- });
495
- });
496
-
497
- // STABLE MODE - Error Handling Steps
498
-
499
- Given('a feature in stable mode', async function() {
500
- testDir = path.join(__dirname, '../../test-tmp', `production-mode-${Date.now()}`);
501
- fs.mkdirSync(testDir, { recursive: true });
502
- process.chdir(testDir);
503
-
504
- const { getDb } = require('../../lib/database');
505
- db = getDb();
506
-
507
- await new Promise((resolve, reject) => {
508
- db.run(
509
- `INSERT INTO work_items (type, title, description, status, mode, phase)
510
- VALUES (?, ?, ?, ?, ?, ?)`,
511
- ['feature', 'Test Feature', 'A test feature', 'done', 'stable', 'implementation'],
512
- function(err) {
513
- if (err) reject(err);
514
- featureId = this.lastID;
515
- resolve();
516
- }
517
- );
518
- });
519
-
520
- // Create basic feature file
521
- const featuresDir = path.join(testDir, 'features');
522
- fs.mkdirSync(featuresDir, { recursive: true });
523
- testFeatureFile = path.join(featuresDir, 'test-feature.feature');
524
- fs.writeFileSync(testFeatureFile, 'Feature: Test Feature\n\nScenario: Happy path\n Given test\n');
525
-
526
- await new Promise((resolve, reject) => {
527
- db.run(
528
- `UPDATE work_items SET scenario_file = ? WHERE id = ?`,
529
- [testFeatureFile, featureId],
530
- (err) => {
531
- if (err) reject(err);
532
- resolve();
533
- }
534
- );
535
- });
536
- });
537
-
538
- Given('the production standards file does not exist at .jettypod\\/production-standards.json', function() {
539
- const jettypodDir = path.join(testDir, '.jettypod');
540
- if (fs.existsSync(jettypodDir)) {
541
- const standardsFile = path.join(jettypodDir, 'production-standards.json');
542
- if (fs.existsSync(standardsFile)) {
543
- fs.unlinkSync(standardsFile);
544
- }
545
- }
546
- });
547
-
548
- Given('the production standards file exists but contains malformed JSON', function() {
549
- const jettypodDir = path.join(testDir, '.jettypod');
550
- fs.mkdirSync(jettypodDir, { recursive: true });
551
-
552
- productionStandardsFile = path.join(jettypodDir, 'production-standards.json');
553
- fs.writeFileSync(productionStandardsFile, '{ "broken": json }');
554
- });
555
-
556
- Given('the production standards file exists with valid JSON', function() {
557
- const jettypodDir = path.join(testDir, '.jettypod');
558
- fs.mkdirSync(jettypodDir, { recursive: true });
559
-
560
- productionStandardsFile = path.join(jettypodDir, 'production-standards.json');
561
- fs.writeFileSync(productionStandardsFile, JSON.stringify({ valid: 'json' }, null, 2));
562
- });
563
-
564
- Given('the standards array is missing required fields', function() {
565
- const malformedStandards = {
566
- preset: 'production-saas',
567
- standards: [
568
- {
569
- id: 'test_standard'
570
- // Missing domain, acceptance
571
- }
572
- ]
573
- };
574
- fs.writeFileSync(productionStandardsFile, JSON.stringify(malformedStandards, null, 2));
575
- });
576
-
577
- Given('the feature has no scenario_file set in database', async function() {
578
- return new Promise((resolve, reject) => {
579
- db.run(
580
- `UPDATE work_items SET scenario_file = NULL WHERE id = ?`,
581
- [featureId],
582
- (err) => {
583
- if (err) reject(err);
584
- resolve();
585
- }
586
- );
587
- });
588
- });
589
-
590
- Given('the feature has scenario_file set in database', async function() {
591
- testFeatureFile = path.join(testDir, 'features', 'nonexistent.feature');
592
- return new Promise((resolve, reject) => {
593
- db.run(
594
- `UPDATE work_items SET scenario_file = ? WHERE id = ?`,
595
- [testFeatureFile, featureId],
596
- (err) => {
597
- if (err) reject(err);
598
- resolve();
599
- }
600
- );
601
- });
602
- });
603
-
604
- Given('the file does not exist at that path', function() {
605
- if (fs.existsSync(testFeatureFile)) {
606
- fs.unlinkSync(testFeatureFile);
607
- }
608
- });
609
-
610
- Given('an invalid feature ID', function() {
611
- featureId = 999999;
612
- });
613
-
614
- Given('a feature marked as done in stable mode', async function() {
615
- testDir = path.join(__dirname, '../../test-tmp', `production-mode-${Date.now()}`);
616
- fs.mkdirSync(testDir, { recursive: true });
617
- process.chdir(testDir);
618
-
619
- const { getDb } = require('../../lib/database');
620
- db = getDb();
621
-
622
- await new Promise((resolve, reject) => {
623
- db.run(
624
- `INSERT INTO work_items (type, title, description, status, mode, phase)
625
- VALUES (?, ?, ?, ?, ?, ?)`,
626
- ['feature', 'Test Feature', 'A test feature', 'done', 'stable', 'implementation'],
627
- function(err) {
628
- if (err) reject(err);
629
- featureId = this.lastID;
630
- resolve();
631
- }
632
- );
633
- });
634
- });
635
-
636
- Given('the completed_at field is null', async function() {
637
- return new Promise((resolve, reject) => {
638
- db.run(
639
- `UPDATE work_items SET completed_at = NULL WHERE id = ?`,
640
- [featureId],
641
- (err) => {
642
- if (err) reject(err);
643
- resolve();
644
- }
645
- );
646
- });
647
- });
648
-
649
- Given('the file is not readable due to permissions', function() {
650
- if (process.platform !== 'win32') {
651
- fs.chmodSync(productionStandardsFile, 0o000);
652
- } else {
653
- this.skip();
654
- }
655
- });
656
-
657
- Given('a feature in Scenario C \\(generating scenarios)', async function() {
658
- await this.runStep('Given a feature in stable mode with no production scenarios');
659
- await this.runStep('Given no production chores exist for the feature');
660
- await this.runStep('Given production standards file exists from external-transition');
661
- });
662
-
663
- Given('production standards are loaded successfully', function() {
664
- assert.ok(fs.existsSync(productionStandardsFile), 'Standards file should exist');
665
- const standards = JSON.parse(fs.readFileSync(productionStandardsFile, 'utf-8'));
666
- assert.ok(standards.standards.length > 0, 'Standards should be loaded');
667
- });
668
-
669
- Given('the scenario file is not writable due to permissions', function() {
670
- if (process.platform !== 'win32') {
671
- fs.chmodSync(testFeatureFile, 0o444);
672
- } else {
673
- this.skip();
674
- }
675
- });
676
-
677
- Given('the production standards file exists with empty standards array', function() {
678
- const jettypodDir = path.join(testDir, '.jettypod');
679
- fs.mkdirSync(jettypodDir, { recursive: true });
680
-
681
- productionStandardsFile = path.join(jettypodDir, 'production-standards.json');
682
- fs.writeFileSync(productionStandardsFile, JSON.stringify({ preset: 'test', standards: [] }, null, 2));
683
- });
684
-
685
- Given('the database file is locked or corrupted', function() {
686
- if (db) {
687
- db.close();
688
- }
689
- const dbPath = path.join(testDir, '.jettypod', 'work.db');
690
- if (fs.existsSync(dbPath)) {
691
- fs.writeFileSync(dbPath, 'CORRUPTED DATA');
692
- }
693
- });
694
-
695
- Given('production scenarios exist in the feature file', function() {
696
- const content = fs.readFileSync(testFeatureFile, 'utf-8');
697
- if (!content.includes('# PRODUCTION MODE')) {
698
- fs.appendFileSync(testFeatureFile, '\n# PRODUCTION MODE SCENARIOS\n\nScenario: Test production scenario\n Given production\n');
699
- }
700
- });
701
-
702
- Given('production chores exist for the feature', async function() {
703
- await new Promise((resolve, reject) => {
704
- db.run(
705
- `INSERT INTO work_items (type, title, parent_id, status, mode)
706
- VALUES (?, ?, ?, ?, ?)`,
707
- ['chore', 'Test production chore', featureId, 'todo', 'production'],
708
- (err) => {
709
- if (err) reject(err);
710
- resolve();
711
- }
712
- );
713
- });
714
- });
715
-
716
- When('I start the production-mode skill with that feature ID', function() {
717
- skillOutput = { featureId };
718
- });
719
-
720
- When('the context detector checks time since stable completion', function() {
721
- // This is implicitly tested by the detection logic
722
- skillOutput = { checkingCompletedAt: true };
723
- });
724
-
725
- When('attempting to append scenarios to the file', function() {
726
- try {
727
- fs.appendFileSync(testFeatureFile, '\nNew scenario');
728
- skillOutput = { error: null };
729
- } catch (err) {
730
- skillOutput = { error: err };
731
- }
732
- });
733
-
734
- Then('it fails with error {string}', function(errorMessage) {
735
- const { detectContext } = require('../../lib/production-context-detector');
736
- const { readStandards } = require('../../lib/production-standards-reader');
737
-
738
- if (errorMessage.includes('Production standards file not found')) {
739
- return readStandards().catch(err => {
740
- assert.ok(err.message.includes('not found'), 'Should error about missing file');
741
- });
742
- } else if (errorMessage.includes('Failed to parse')) {
743
- return readStandards().catch(err => {
744
- assert.ok(err.message.includes('parse') || err.message.includes('JSON'), 'Should error about parsing');
745
- });
746
- } else if (errorMessage.includes('Invalid standards format')) {
747
- return readStandards().catch(err => {
748
- assert.ok(err.message.includes('Invalid') || err.message.includes('format'), 'Should error about format');
749
- });
750
- } else if (errorMessage.includes('Feature has no scenario file')) {
751
- return detectContext(featureId).catch(err => {
752
- assert.ok(err.message.includes('scenario'), 'Should error about scenario file');
753
- });
754
- } else if (errorMessage.includes('Scenario file not found')) {
755
- const { appendScenarios } = require('../../lib/production-scenario-appender');
756
- return appendScenarios(testFeatureFile, []).catch(err => {
757
- assert.ok(err.message.includes('not found'), 'Should error about file not found');
758
- });
759
- } else if (errorMessage.includes('Feature not found')) {
760
- return detectContext(featureId).catch(err => {
761
- assert.ok(err.message.includes('not found'), 'Should error about feature not found');
762
- });
763
- }
764
- });
765
-
766
- Then('it suggests running external-transition first', function() {
767
- // Error message should suggest external-transition
768
- return require('../../lib/production-standards-reader').readStandards().catch(err => {
769
- assert.ok(err.message.includes('external-transition'), 'Should mention external-transition');
770
- });
771
- });
772
-
773
- Then('it shows the JSON parsing error details', function() {
774
- return require('../../lib/production-standards-reader').readStandards().catch(err => {
775
- assert.ok(err.message.length > 0, 'Should have error details');
776
- });
777
- });
778
-
779
- Then('it specifies which required fields are missing', function() {
780
- return require('../../lib/production-standards-reader').readStandards().catch(err => {
781
- assert.ok(err.message.includes('domain') || err.message.includes('acceptance'), 'Should specify missing fields');
782
- });
783
- });
784
-
785
- Then('it suggests the feature may not be properly initialized', function() {
786
- return require('../../lib/production-context-detector').detectContext(featureId).catch(err => {
787
- assert.ok(err.message.length > 0, 'Should have helpful error message');
788
- });
789
- });
790
-
791
- Then('it shows the expected file path', function() {
792
- const { appendScenarios } = require('../../lib/production-scenario-appender');
793
- return appendScenarios(testFeatureFile, []).catch(err => {
794
- assert.ok(err.message.includes(testFeatureFile) || err.message.includes('Scenario file'), 'Should show path');
795
- });
796
- });
797
-
798
- Then('it treats this as Scenario C \\(no production content yet)', async function() {
799
- const { detectContext } = require('../../lib/production-context-detector');
800
- const scenario = await detectContext(featureId);
801
- assert.strictEqual(scenario, 'SCENARIO_C', 'Should detect Scenario C when completed_at is null');
802
- });
803
-
804
- Then('it fails with error indicating permission denied', function() {
805
- if (process.platform === 'win32') {
806
- this.skip();
807
- }
808
- return require('../../lib/production-standards-reader').readStandards().catch(err => {
809
- assert.ok(err.message.includes('EACCES') || err.message.includes('permission'), 'Should indicate permission error');
810
- });
811
- });
812
-
813
- Then('it shows the file path with permission issues', function() {
814
- if (process.platform === 'win32') {
815
- this.skip();
816
- }
817
- return require('../../lib/production-standards-reader').readStandards().catch(err => {
818
- assert.ok(err.message.length > 0, 'Should show path or error details');
819
- });
820
- });
821
-
822
- Then('it fails with error indicating write permission denied', function() {
823
- if (process.platform === 'win32') {
824
- this.skip();
825
- }
826
- assert.ok(skillOutput.error, 'Should have error');
827
- assert.ok(skillOutput.error.message.includes('EACCES') || skillOutput.error.code === 'EACCES', 'Should be permission error');
828
- });
829
-
830
- Then('it completes successfully', function() {
831
- const standards = JSON.parse(fs.readFileSync(productionStandardsFile, 'utf-8'));
832
- assert.strictEqual(standards.standards.length, 0, 'Should handle empty standards');
833
- });
834
-
835
- Then('reports {int} standards to implement', function(count) {
836
- const standards = JSON.parse(fs.readFileSync(productionStandardsFile, 'utf-8'));
837
- assert.strictEqual(standards.standards.length, count, `Should report ${count} standards`);
838
- });
839
-
840
- Then('it fails with database error', function() {
841
- const { getDb } = require('../../lib/database');
842
- try {
843
- const brokenDb = getDb();
844
- brokenDb.get('SELECT * FROM work_items', (err) => {
845
- assert.ok(err, 'Should have database error');
846
- });
847
- } catch (err) {
848
- assert.ok(err, 'Should throw or callback with error');
849
- }
850
- });
851
-
852
- Then('it suggests checking database integrity', function() {
853
- // The error handling should suggest checking the database
854
- assert.ok(true, 'Error message should suggest integrity check');
855
- });
856
-
857
- // Cleanup
858
- process.on('exit', () => {
859
- if (testDir && fs.existsSync(testDir)) {
860
- fs.rmSync(testDir, { recursive: true, force: true });
861
- }
862
- });