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.
- package/.nvmrc +1 -0
- package/docs/COMPLETE-TESTING-STRATEGY.md +970 -0
- package/docs/DECISIONS.md +10 -12
- package/docs/NODE_VERSION.md +83 -0
- package/docs/TDD-INFRASTRUCTURE-STRATEGY.md +1374 -0
- package/docs/TESTING-FOR-NON-ENGINEERS.md +1588 -0
- package/docs/TESTING-STRATEGY-AUDIT.md +698 -0
- package/hooks/post-checkout +17 -0
- package/hooks/post-merge +17 -0
- package/hooks/pre-commit +30 -0
- package/jettypod.js +259 -120
- package/lib/coverage-tracker.js +218 -0
- package/lib/database.js +2 -0
- package/lib/db-export.js +192 -0
- package/lib/db-import.js +193 -0
- package/lib/external-transition-handler.js +32 -0
- package/lib/git-hook-helpers.js +174 -0
- package/lib/git-root.js +90 -0
- package/lib/infrastructure-chore-generator.js +45 -0
- package/lib/install-hooks.js +52 -0
- package/lib/jettypod-backup.js +238 -0
- package/lib/merge-lock.js +193 -0
- package/lib/migrations/012-add-worktree-path.js +38 -0
- package/lib/migrations/013-worktrees-table.js +86 -0
- package/lib/migrations/014-migrate-worktree-data.js +161 -0
- package/lib/migrations/015-merge-locks-table.js +67 -0
- package/lib/pattern-finder.js +152 -0
- package/lib/process-manager.js +140 -0
- package/lib/production-standards-reader.js +13 -2
- package/lib/production-standards-writer.js +85 -0
- package/lib/skills/feature-planning/dry-run-validator.js +135 -0
- package/lib/skills/feature-planning/validation-formatter.js +160 -0
- package/lib/smart-conflict-detection.js +168 -0
- package/lib/smart-fetch-rebase.js +614 -0
- package/lib/step-definition-parser.js +76 -0
- package/lib/unit-test-generator.js +232 -0
- package/lib/verification-command-generator.js +66 -0
- package/lib/worktree-diagnostics.js +413 -0
- package/lib/worktree-facade.js +174 -0
- package/lib/worktree-manager.js +636 -0
- package/lib/worktree-reconciler.js +429 -0
- package/package.json +30 -3
- package/skills-templates/external-transition/SKILL.md +34 -3
- package/skills-templates/feature-planning/SKILL.md +190 -24
- package/skills-templates/production-mode/SKILL.md +127 -9
- package/skills-templates/speed-mode/SKILL.md +454 -51
- package/skills-templates/stable-mode/SKILL.md +285 -76
- package/.claude/PROTECT_SKILLS.md +0 -28
- package/.claude/settings.json +0 -24
- package/.claude/settings.local.json +0 -16
- package/.claude/skills/epic-planning/SKILL.md +0 -297
- package/.claude/skills/external-transition/SKILL.md +0 -384
- package/.claude/skills/feature-planning/SKILL.md +0 -464
- package/.claude/skills/production-mode/SKILL.md +0 -369
- package/.claude/skills/speed-mode/SKILL.md +0 -481
- package/.claude/skills/stable-mode/SKILL.md +0 -713
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/epic-planning/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/feature-planning/SKILL.md +0 -464
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/speed-mode/SKILL.md +0 -467
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/stable-mode/SKILL.md +0 -673
- package/.claude/skills.backup-2025-11-11T16-15-10-070Z/epic-discover/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/epic-planning/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/feature-planning/SKILL.md +0 -464
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/speed-mode/SKILL.md +0 -467
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/stable-mode/SKILL.md +0 -673
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/epic-planning/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/feature-planning/SKILL.md +0 -464
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/speed-mode/SKILL.md +0 -467
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/stable-mode/SKILL.md +0 -673
- package/.devpod/current-work.json +0 -10
- package/.devpod/work.db +0 -0
- package/.github/workflows/test-safety.yml +0 -85
- package/.jettypod/config.json +0 -5
- package/.jettypod/current-work.json +0 -10
- package/.jettypod/hooks/README.md +0 -77
- package/.jettypod/hooks/protect-claude-md.js +0 -338
- package/.jettypod/test-work.db +0 -0
- package/.jettypod/work.db +0 -0
- package/CLAUDE.md +0 -49
- package/SPEED-STABLE-AUDIT.md +0 -853
- package/SYSTEM-BEHAVIOR.md +0 -2199
- package/TEST_SAFETY_AUDIT.md +0 -314
- package/TEST_SAFETY_IMPLEMENTATION.md +0 -97
- package/cucumber-report.html +0 -45
- package/dist/devpod-linux +0 -0
- package/dist/devpod-macos +0 -0
- package/dist/devpod-win.exe +0 -0
- package/docs/features/jettypod-standards-explained.md +0 -543
- package/docs/features/standards-inventory.md +0 -257
- package/features/auto-generate-production-chores.feature +0 -13
- package/features/backlog-command.feature +0 -26
- package/features/backlog-filtering-production.feature +0 -10
- package/features/claude-md-protection/steps.js +0 -498
- package/features/decisions/index.js +0 -490
- package/features/decisions/index.test.js +0 -208
- package/features/fix-text-wrapping.feature +0 -42
- package/features/git-hooks/git-hooks.feature +0 -30
- package/features/git-hooks/index.js +0 -93
- package/features/git-hooks/index.test.js +0 -137
- package/features/git-hooks/post-commit +0 -56
- package/features/git-hooks/post-merge +0 -47
- package/features/git-hooks/pre-commit +0 -28
- package/features/git-hooks/simple-steps.js +0 -53
- package/features/git-hooks/simple-test.feature +0 -10
- package/features/git-hooks/steps.js +0 -196
- package/features/jettypod-update-command.feature +0 -46
- package/features/mode-prompts/index.js +0 -95
- package/features/mode-prompts/simple-steps.js +0 -44
- package/features/mode-prompts/simple-test.feature +0 -9
- package/features/mode-prompts/validation.test.js +0 -120
- package/features/multiple-claude-instances.feature +0 -121
- package/features/production-mode-skill.feature +0 -121
- package/features/refactor-mode/steps.js +0 -217
- package/features/refactor-mode.feature +0 -49
- package/features/simplify-external-transition.feature +0 -166
- package/features/skills-update/index.test.js +0 -216
- package/features/step_definitions/backlog-command.steps.js +0 -37
- package/features/step_definitions/fix-text-wrapping.steps.js +0 -271
- package/features/step_definitions/multiple-claude-instances.steps.js +0 -621
- package/features/step_definitions/production-mode-skill.steps.js +0 -862
- package/features/step_definitions/simplify-external-transition.steps.js +0 -370
- package/features/step_definitions/terminal-logo.steps.js +0 -145
- package/features/step_definitions/update-command.steps.js +0 -183
- package/features/support/hooks.js +0 -9
- package/features/terminal-logo/index.js +0 -39
- package/features/terminal-logo/terminal-logo.feature +0 -30
- package/features/update-command/index.js +0 -181
- package/features/update-command/index.test.js +0 -225
- package/features/work-commands/bug-workflow-display.feature +0 -22
- package/features/work-commands/index.js +0 -498
- package/features/work-commands/simple-steps.js +0 -69
- package/features/work-commands/stable-tests.feature +0 -57
- package/features/work-commands/steps.js +0 -1174
- package/features/work-commands/validation.test.js +0 -88
- package/features/work-commands/work-commands.feature +0 -13
- package/features/work-tracking/discovery-validation.test.js +0 -228
- package/features/work-tracking/index.js +0 -1921
- package/features/work-tracking/mode-required.feature +0 -112
- package/features/work-tracking/phase-tracking.test.js +0 -482
- package/features/work-tracking/prototype-tracking.test.js +0 -485
- package/features/work-tracking/tree-view.test.js +0 -310
- package/features/work-tracking/work-set-mode.feature +0 -71
- package/features/work-tracking/work-start-mode.feature +0 -88
- package/full-test.txt +0 -0
- package/lib/bug-workflow.test.js +0 -177
- package/lib/claudemd.test.js +0 -195
- package/lib/config.test.js +0 -511
- package/lib/constants.test.js +0 -164
- package/lib/current-work.test.js +0 -146
- package/lib/database-project-config.test.js +0 -111
- package/lib/database.test.js +0 -106
- package/lib/decisions-generator.test.js +0 -457
- package/lib/decisions-helpers.test.js +0 -310
- package/lib/git-coordinator.js +0 -167
- package/lib/git.test.js +0 -145
- package/lib/migrations/002-default-work-item-modes.test.js +0 -351
- package/lib/production-chore-generator.test.js +0 -432
- package/lib/production-context-detector.test.js +0 -277
- package/lib/production-scenario-appender.test.js +0 -235
- package/lib/production-scenario-validator.test.js +0 -246
- package/lib/production-standards-reader.test.js +0 -270
- package/lib/project-state.test.js +0 -92
- package/lib/push-queue.js +0 -417
- package/lib/queue-processor.js +0 -74
- package/lib/test-helpers.js +0 -202
- package/lib/test-helpers.test.js +0 -255
- package/prototypes/2025-01-11-production-mode-autonomous.js +0 -119
- package/prototypes/2025-01-11-production-mode-collaborative.js +0 -166
- package/prototypes/2025-01-11-production-mode-guided.js +0 -217
- package/prototypes/2025-01-11-production-mode-smart-context.js +0 -347
- package/prototypes/2025-01-11-production-standards-example.md +0 -204
- package/prototypes/2025-11-10-backlog-filtering-tree-aware.js +0 -242
- package/prototypes/test/index.html +0 -1
- package/setup-dist-repo.sh +0 -68
- package/test-production-standards-engine.js +0 -130
- package/test-results.json +0 -2195
- package/test-safety-check.sh +0 -80
- package/work-item-tracking-plan.md +0 -199
- /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
|
-
});
|