jettypod 4.4.118 โ 4.4.121
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/.env +4 -3
- package/Cargo.lock +6450 -0
- package/Cargo.toml +35 -0
- package/README.md +5 -1
- package/TAURI-MIGRATION-PLAN.md +840 -0
- package/apps/dashboard/app/connect-claude/page.tsx +5 -6
- package/apps/dashboard/app/decision/[id]/page.tsx +63 -58
- package/apps/dashboard/app/demo/gates/page.tsx +43 -45
- package/apps/dashboard/app/design-system/page.tsx +868 -0
- package/apps/dashboard/app/globals.css +80 -4
- package/apps/dashboard/app/install-claude/page.tsx +4 -6
- package/apps/dashboard/app/login/page.tsx +72 -54
- package/apps/dashboard/app/page.tsx +101 -48
- package/apps/dashboard/app/settings/page.tsx +61 -13
- package/apps/dashboard/app/signup/page.tsx +242 -0
- package/apps/dashboard/app/subscribe/page.tsx +0 -2
- package/apps/dashboard/app/tests/page.tsx +37 -4
- package/apps/dashboard/app/welcome/page.tsx +13 -16
- package/apps/dashboard/app/work/[id]/page.tsx +117 -118
- package/apps/dashboard/app/work/[id]/proof/page.tsx +1489 -0
- package/apps/dashboard/components/AppShell.tsx +92 -85
- package/apps/dashboard/components/CardMenu.tsx +45 -12
- package/apps/dashboard/components/ClaudePanel.tsx +771 -850
- package/apps/dashboard/components/ClaudePanelInput.tsx +43 -15
- package/apps/dashboard/components/ConnectClaudeScreen.tsx +17 -34
- package/apps/dashboard/components/CopyableId.tsx +3 -4
- package/apps/dashboard/components/DetailReviewActions.tsx +100 -0
- package/apps/dashboard/components/DragContext.tsx +134 -63
- package/apps/dashboard/components/DraggableCard.tsx +3 -5
- package/apps/dashboard/components/DropZone.tsx +6 -7
- package/apps/dashboard/components/EditableDetailDescription.tsx +7 -13
- package/apps/dashboard/components/EditableDetailTitle.tsx +6 -13
- package/apps/dashboard/components/EditableTitle.tsx +26 -7
- package/apps/dashboard/components/ElapsedTimer.tsx +66 -0
- package/apps/dashboard/components/EpicGroup.tsx +359 -0
- package/apps/dashboard/components/GateCard.tsx +79 -17
- package/apps/dashboard/components/GateChoiceCard.tsx +15 -18
- package/apps/dashboard/components/InstallClaudeScreen.tsx +15 -32
- package/apps/dashboard/components/JettyLoader.tsx +37 -0
- package/apps/dashboard/components/KanbanBoard.tsx +368 -958
- package/apps/dashboard/components/KanbanCard.tsx +740 -0
- package/apps/dashboard/components/LazyCard.tsx +62 -0
- package/apps/dashboard/components/LazyMarkdown.tsx +11 -0
- package/apps/dashboard/components/MainNav.tsx +38 -73
- package/apps/dashboard/components/MessageBlock.tsx +468 -0
- package/apps/dashboard/components/ModeStartCard.tsx +15 -16
- package/apps/dashboard/components/OnboardingWelcome.tsx +213 -0
- package/apps/dashboard/components/PlaceholderCard.tsx +3 -4
- package/apps/dashboard/components/ProjectSwitcher.tsx +30 -30
- package/apps/dashboard/components/PrototypeTimeline.tsx +72 -51
- package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +406 -388
- package/apps/dashboard/components/RealTimeTestsWrapper.tsx +373 -235
- package/apps/dashboard/components/ReviewFooter.tsx +139 -0
- package/apps/dashboard/components/SessionList.tsx +19 -19
- package/apps/dashboard/components/SubscribeContent.tsx +91 -47
- package/apps/dashboard/components/TestTree.tsx +16 -16
- package/apps/dashboard/components/TipCard.tsx +16 -17
- package/apps/dashboard/components/Toast.tsx +5 -6
- package/apps/dashboard/components/TypeIcon.tsx +55 -0
- package/apps/dashboard/components/ViewModeToolbar.tsx +104 -0
- package/apps/dashboard/components/WaveCompletionAnimation.tsx +52 -65
- package/apps/dashboard/components/WelcomeScreen.tsx +19 -35
- package/apps/dashboard/components/WorkItemHeader.tsx +4 -5
- package/apps/dashboard/components/WorkItemTree.tsx +11 -32
- package/apps/dashboard/components/settings/AccountSection.tsx +55 -35
- package/apps/dashboard/components/settings/AiContextSection.tsx +89 -0
- package/apps/dashboard/components/settings/ContextDocumentsSection.tsx +317 -0
- package/apps/dashboard/components/settings/EnvVarsSection.tsx +74 -152
- package/apps/dashboard/components/settings/GeneralSection.tsx +162 -56
- package/apps/dashboard/components/settings/ProjectStackSection.tsx +948 -0
- package/apps/dashboard/components/settings/SettingsLayout.tsx +4 -5
- package/apps/dashboard/components/ui/Button.tsx +104 -0
- package/apps/dashboard/components/ui/Input.tsx +78 -0
- package/apps/dashboard/components.json +1 -1
- package/apps/dashboard/contexts/ClaudeSessionContext.tsx +711 -418
- package/apps/dashboard/contexts/ConnectionStatusContext.tsx +25 -5
- package/apps/dashboard/contexts/UsageContext.tsx +87 -32
- package/apps/dashboard/dev.sh +35 -0
- package/apps/dashboard/eslint.config.mjs +9 -9
- package/apps/dashboard/hooks/useKanbanAnimation.ts +29 -0
- package/apps/dashboard/hooks/useKanbanUndo.ts +83 -0
- package/apps/dashboard/hooks/useWebSocket.ts +138 -83
- package/apps/dashboard/index.html +73 -0
- package/apps/dashboard/lib/constants.ts +43 -0
- package/apps/dashboard/lib/data-bridge.ts +722 -0
- package/apps/dashboard/lib/db.ts +69 -1265
- package/apps/dashboard/lib/environment-config.ts +173 -0
- package/apps/dashboard/lib/environment-verification.ts +119 -0
- package/apps/dashboard/lib/kanban-utils.ts +270 -0
- package/apps/dashboard/lib/proof-run.ts +495 -0
- package/apps/dashboard/lib/proof-scenario-runner.ts +346 -0
- package/apps/dashboard/lib/run-migrations.js +27 -2
- package/apps/dashboard/lib/service-recovery.ts +326 -0
- package/apps/dashboard/lib/session-state-machine.ts +1 -0
- package/apps/dashboard/lib/session-state-utils.ts +0 -164
- package/apps/dashboard/lib/session-stream-manager.ts +308 -134
- package/apps/dashboard/lib/shadows.ts +7 -0
- package/apps/dashboard/lib/stream-manager-registry.ts +46 -6
- package/apps/dashboard/lib/tauri-bridge.ts +102 -0
- package/apps/dashboard/lib/tauri.ts +106 -0
- package/apps/dashboard/lib/utils.ts +6 -0
- package/apps/dashboard/next-env.d.ts +1 -1
- package/apps/dashboard/package.json +21 -32
- package/apps/dashboard/public/bug-icon.png +0 -0
- package/apps/dashboard/public/buoy-icon.png +0 -0
- package/apps/dashboard/public/fonts/Satoshi-Variable.woff2 +0 -0
- package/apps/dashboard/public/fonts/Satoshi-VariableItalic.woff2 +0 -0
- package/apps/dashboard/public/in-flight-seagull.png +0 -0
- package/apps/dashboard/public/jetty-icon-loading-alt.svg +11 -0
- package/apps/dashboard/public/jetty-icon-loading.svg +11 -0
- package/apps/dashboard/public/jettypod_logo.png +0 -0
- package/apps/dashboard/public/pier-icon.png +0 -0
- package/apps/dashboard/public/star-icon.png +0 -0
- package/apps/dashboard/public/wrench-icon.png +0 -0
- package/apps/dashboard/scripts/tauri-build.js +228 -0
- package/apps/dashboard/scripts/upload-tauri-to-r2.js +125 -0
- package/apps/dashboard/scripts/ws-server.js +191 -0
- package/apps/dashboard/src/main.tsx +12 -0
- package/apps/dashboard/src/router.tsx +107 -0
- package/apps/dashboard/src/vite-env.d.ts +1 -0
- package/apps/dashboard/tsconfig.json +7 -12
- package/apps/dashboard/tsconfig.tsbuildinfo +1 -1
- package/apps/dashboard/vite.config.ts +33 -0
- package/apps/update-server/src/index.ts +228 -80
- package/claude-hooks/global-guardrails.js +14 -13
- package/crates/jettypod-cli/Cargo.toml +19 -0
- package/crates/jettypod-cli/src/commands.rs +1249 -0
- package/crates/jettypod-cli/src/main.rs +595 -0
- package/crates/jettypod-core/Cargo.toml +26 -0
- package/crates/jettypod-core/build.rs +98 -0
- package/crates/jettypod-core/migrations/V1__baseline.sql +197 -0
- package/crates/jettypod-core/migrations/V2__work_items_indexes.sql +6 -0
- package/crates/jettypod-core/migrations/V3__qa_steps.sql +2 -0
- package/crates/jettypod-core/src/auth.rs +294 -0
- package/crates/jettypod-core/src/config.rs +397 -0
- package/crates/jettypod-core/src/db/mod.rs +507 -0
- package/crates/jettypod-core/src/db/recovery.rs +114 -0
- package/crates/jettypod-core/src/db/startup.rs +101 -0
- package/crates/jettypod-core/src/db/validate.rs +149 -0
- package/crates/jettypod-core/src/error.rs +76 -0
- package/crates/jettypod-core/src/git.rs +458 -0
- package/crates/jettypod-core/src/lib.rs +20 -0
- package/crates/jettypod-core/src/sessions.rs +625 -0
- package/crates/jettypod-core/src/skills.rs +556 -0
- package/crates/jettypod-core/src/work.rs +1086 -0
- package/crates/jettypod-core/src/worktree.rs +628 -0
- package/crates/jettypod-core/src/ws.rs +767 -0
- package/cucumber-test.cjs +6 -0
- package/cucumber.js +9 -3
- package/docs/COMMAND_REFERENCE.md +34 -0
- package/hooks/post-checkout +32 -75
- package/hooks/post-merge +111 -10
- package/jest.setup.js +1 -0
- package/jettypod.js +145 -116
- package/lib/bdd-preflight.js +96 -0
- package/lib/chore-taxonomy.js +33 -10
- package/lib/database.js +36 -16
- package/lib/db-watcher.js +1 -1
- package/lib/git-hooks/pre-commit +1 -1
- package/lib/jettypod-backup.js +27 -4
- package/lib/merge-lock.js +111 -253
- package/lib/migrations/027-plan-at-creation-column.js +3 -1
- package/lib/migrations/029-remove-autoincrement.js +307 -0
- package/lib/migrations/029-rename-corrupted-to-cleaned.js +149 -0
- package/lib/migrations/030-rejection-round-columns.js +54 -0
- package/lib/migrations/031-session-isolation-index.js +17 -0
- package/lib/migrations/index.js +47 -4
- package/lib/schema.js +10 -5
- package/lib/seed-onboarding.js +1 -1
- package/lib/update-command/index.js +9 -175
- package/lib/work-commands/index.js +144 -19
- package/lib/work-tracking/index.js +148 -27
- package/lib/worktree-diagnostics.js +16 -16
- package/lib/worktree-facade.js +1 -1
- package/lib/worktree-manager.js +8 -8
- package/lib/worktree-reconciler.js +5 -5
- package/package.json +9 -2
- package/scripts/ndjson-to-cucumber-json.js +152 -0
- package/scripts/postinstall.js +25 -0
- package/skills-templates/bug-mode/SKILL.md +79 -20
- package/skills-templates/bug-planning/SKILL.md +25 -29
- package/skills-templates/chore-mode/SKILL.md +171 -69
- package/skills-templates/chore-mode/verification.js +51 -10
- package/skills-templates/chore-planning/SKILL.md +47 -18
- package/skills-templates/design-system-selection/SKILL.md +273 -0
- package/skills-templates/epic-planning/SKILL.md +82 -48
- package/skills-templates/external-transition/SKILL.md +47 -47
- package/skills-templates/feature-planning/SKILL.md +173 -74
- package/skills-templates/production-mode/SKILL.md +69 -49
- package/skills-templates/request-routing/SKILL.md +4 -4
- package/skills-templates/simple-improvement/SKILL.md +74 -29
- package/skills-templates/speed-mode/SKILL.md +217 -141
- package/skills-templates/stable-mode/SKILL.md +148 -89
- package/apps/dashboard/README.md +0 -36
- package/apps/dashboard/app/api/claude/[workItemId]/message/route.ts +0 -386
- package/apps/dashboard/app/api/claude/[workItemId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/[workItemId]/route.ts +0 -167
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/content/route.ts +0 -52
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/message/route.ts +0 -378
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/sessions/cleanup/route.ts +0 -34
- package/apps/dashboard/app/api/claude/sessions/route.ts +0 -184
- package/apps/dashboard/app/api/decisions/[id]/route.ts +0 -25
- package/apps/dashboard/app/api/internal/set-project/route.ts +0 -17
- package/apps/dashboard/app/api/kanban/route.ts +0 -15
- package/apps/dashboard/app/api/settings/env-vars/route.ts +0 -125
- package/apps/dashboard/app/api/settings/general/route.ts +0 -21
- package/apps/dashboard/app/api/tests/route.ts +0 -9
- package/apps/dashboard/app/api/tests/run/route.ts +0 -82
- package/apps/dashboard/app/api/tests/run/stream/route.ts +0 -71
- package/apps/dashboard/app/api/tests/undefined/route.ts +0 -9
- package/apps/dashboard/app/api/usage/route.ts +0 -17
- package/apps/dashboard/app/api/work/[id]/description/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/epic/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/order/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/status/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/title/route.ts +0 -21
- package/apps/dashboard/app/layout.tsx +0 -43
- package/apps/dashboard/components/UpgradeBanner.tsx +0 -29
- package/apps/dashboard/electron/ipc-handlers.js +0 -1028
- package/apps/dashboard/electron/main.js +0 -2124
- package/apps/dashboard/electron/preload.js +0 -123
- package/apps/dashboard/electron/session-manager.js +0 -141
- package/apps/dashboard/electron-builder.config.js +0 -357
- package/apps/dashboard/hooks/useClaudeSessions.ts +0 -299
- package/apps/dashboard/lib/claude-process-manager.ts +0 -492
- package/apps/dashboard/lib/db-bridge.ts +0 -282
- package/apps/dashboard/lib/prototypes.ts +0 -202
- package/apps/dashboard/lib/test-results-db.ts +0 -307
- package/apps/dashboard/lib/tests.ts +0 -282
- package/apps/dashboard/next.config.js +0 -50
- package/apps/dashboard/postcss.config.mjs +0 -7
- package/apps/dashboard/public/file.svg +0 -1
- package/apps/dashboard/public/globe.svg +0 -1
- package/apps/dashboard/public/next.svg +0 -1
- package/apps/dashboard/public/vercel.svg +0 -1
- package/apps/dashboard/public/window.svg +0 -1
- package/apps/dashboard/scripts/download-node.js +0 -104
- package/apps/dashboard/scripts/upload-to-r2.js +0 -89
- package/docs/bdd-guidance.md +0 -390
|
@@ -99,7 +99,7 @@ async function runDiagnostics(db, repoPath, options = {}) {
|
|
|
99
99
|
count: reconcileResults.staleDbEntries.length,
|
|
100
100
|
message: `Found ${reconcileResults.staleDbEntries.length} database entries with missing filesystem directories`,
|
|
101
101
|
details: reconcileResults.staleDbEntries,
|
|
102
|
-
recommendation: 'Run reconciliation with cleanup=true to mark as
|
|
102
|
+
recommendation: 'Run reconciliation with cleanup=true to mark as cleaned'
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -192,23 +192,23 @@ async function runDiagnostics(db, repoPath, options = {}) {
|
|
|
192
192
|
async function checkDatabaseIntegrity(db) {
|
|
193
193
|
const issues = [];
|
|
194
194
|
|
|
195
|
-
// Check for
|
|
196
|
-
const
|
|
197
|
-
db.get('SELECT COUNT(*) as count FROM worktrees WHERE status = ?', ['
|
|
195
|
+
// Check for cleaned worktrees (terminal state โ informational only)
|
|
196
|
+
const cleanedCount = await new Promise((resolve, reject) => {
|
|
197
|
+
db.get('SELECT COUNT(*) as count FROM worktrees WHERE status = ?', ['cleaned'], (err, row) => {
|
|
198
198
|
if (err) reject(err);
|
|
199
199
|
else resolve(row.count);
|
|
200
200
|
});
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
-
if (
|
|
203
|
+
if (cleanedCount > 0) {
|
|
204
204
|
issues.push({
|
|
205
205
|
severity: SEVERITY.INFO,
|
|
206
206
|
category: 'database',
|
|
207
|
-
type: '
|
|
208
|
-
count:
|
|
209
|
-
message: `Found ${
|
|
210
|
-
details: { count:
|
|
211
|
-
recommendation: 'These
|
|
207
|
+
type: 'cleaned_worktrees',
|
|
208
|
+
count: cleanedCount,
|
|
209
|
+
message: `Found ${cleanedCount} cleaned worktree(s) in database`,
|
|
210
|
+
details: { count: cleanedCount },
|
|
211
|
+
recommendation: 'These are historical records โ no action needed'
|
|
212
212
|
});
|
|
213
213
|
}
|
|
214
214
|
|
|
@@ -232,7 +232,7 @@ async function checkDatabaseIntegrity(db) {
|
|
|
232
232
|
count: orphanedWorktrees.length,
|
|
233
233
|
message: `Found ${orphanedWorktrees.length} worktree(s) with missing work items`,
|
|
234
234
|
details: orphanedWorktrees,
|
|
235
|
-
recommendation: 'Mark these worktrees as
|
|
235
|
+
recommendation: 'Mark these worktrees as cleaned'
|
|
236
236
|
});
|
|
237
237
|
}
|
|
238
238
|
|
|
@@ -265,7 +265,7 @@ async function checkFilesystemHealth(db, repoPath) {
|
|
|
265
265
|
type: 'missing_worktree_directory',
|
|
266
266
|
message: `Active worktree directory missing: ${worktreePath}`,
|
|
267
267
|
details: { worktree },
|
|
268
|
-
recommendation: 'Mark worktree as
|
|
268
|
+
recommendation: 'Mark worktree as cleaned and recreate if needed'
|
|
269
269
|
});
|
|
270
270
|
continue;
|
|
271
271
|
}
|
|
@@ -293,7 +293,7 @@ async function checkFilesystemHealth(db, repoPath) {
|
|
|
293
293
|
type: 'permission_issue',
|
|
294
294
|
message: `Cannot read/write worktree directory: ${worktreePath}`,
|
|
295
295
|
details: { worktree, error: err.message },
|
|
296
|
-
recommendation: 'Fix directory permissions or mark worktree as
|
|
296
|
+
recommendation: 'Fix directory permissions or mark worktree as cleaned'
|
|
297
297
|
});
|
|
298
298
|
}
|
|
299
299
|
}
|
|
@@ -344,11 +344,11 @@ function generateRecommendations(report) {
|
|
|
344
344
|
});
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
if (report.issues.some(i => i.type === '
|
|
347
|
+
if (report.issues.some(i => i.type === 'cleaned_worktrees')) {
|
|
348
348
|
recommendations.push({
|
|
349
349
|
priority: 'low',
|
|
350
|
-
action: '
|
|
351
|
-
reason: '
|
|
350
|
+
action: 'Cleaned worktree entries are historical records',
|
|
351
|
+
reason: 'No action needed โ these are part of the audit trail'
|
|
352
352
|
});
|
|
353
353
|
}
|
|
354
354
|
|
package/lib/worktree-facade.js
CHANGED
|
@@ -117,7 +117,7 @@ async function startWork(workItem, options = {}) {
|
|
|
117
117
|
/**
|
|
118
118
|
* Stop work on a work item with graceful degradation
|
|
119
119
|
*
|
|
120
|
-
* Attempts to cleanup worktree. If that fails, marks as
|
|
120
|
+
* Attempts to cleanup worktree. If that fails, marks as cleaned and continues.
|
|
121
121
|
*
|
|
122
122
|
* @param {number} worktreeId - Worktree ID (can be null if working in main)
|
|
123
123
|
* @param {Object} options - Optional configuration
|
package/lib/worktree-manager.js
CHANGED
|
@@ -21,7 +21,7 @@ const config = require('./config');
|
|
|
21
21
|
/**
|
|
22
22
|
* Valid worktree statuses
|
|
23
23
|
*/
|
|
24
|
-
const VALID_STATUSES = ['active', 'merging', 'cleanup_pending', '
|
|
24
|
+
const VALID_STATUSES = ['active', 'merging', 'cleanup_pending', 'cleaned'];
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Create a worktree for a work item
|
|
@@ -352,7 +352,7 @@ async function getAllActiveWorktrees(options = {}) {
|
|
|
352
352
|
* Mark worktree status
|
|
353
353
|
*
|
|
354
354
|
* @param {number} worktreeId - Worktree ID
|
|
355
|
-
* @param {string} status - New status (active|merging|cleanup_pending|
|
|
355
|
+
* @param {string} status - New status (active|merging|cleanup_pending|cleaned)
|
|
356
356
|
* @param {Object} options - Optional configuration
|
|
357
357
|
* @param {Object} options.db - Database connection (defaults to global database)
|
|
358
358
|
* @returns {Promise<void>}
|
|
@@ -671,13 +671,13 @@ async function cleanupWorktree(worktreeId, options = {}) {
|
|
|
671
671
|
}
|
|
672
672
|
}
|
|
673
673
|
|
|
674
|
-
// Step 5: Mark as
|
|
675
|
-
// Using '
|
|
674
|
+
// Step 5: Mark as cleaned (we never delete DB entries)
|
|
675
|
+
// Using 'cleaned' as the final state for cleaned up worktrees
|
|
676
676
|
// This preserves the audit trail
|
|
677
677
|
await new Promise((resolve, reject) => {
|
|
678
678
|
db.run(
|
|
679
679
|
'UPDATE worktrees SET status = ?, updated_at = datetime(\'now\') WHERE id = ?',
|
|
680
|
-
['
|
|
680
|
+
['cleaned', worktreeId],
|
|
681
681
|
(err) => {
|
|
682
682
|
if (err) reject(err);
|
|
683
683
|
else resolve();
|
|
@@ -686,12 +686,12 @@ async function cleanupWorktree(worktreeId, options = {}) {
|
|
|
686
686
|
});
|
|
687
687
|
|
|
688
688
|
} catch (err) {
|
|
689
|
-
// On any failure, mark as
|
|
689
|
+
// On any failure, mark as cleaned (terminal state)
|
|
690
690
|
try {
|
|
691
691
|
await new Promise((resolve, reject) => {
|
|
692
692
|
db.run(
|
|
693
693
|
'UPDATE worktrees SET status = ?, updated_at = datetime(\'now\') WHERE id = ?',
|
|
694
|
-
['
|
|
694
|
+
['cleaned', worktreeId],
|
|
695
695
|
(err) => {
|
|
696
696
|
if (err) reject(err);
|
|
697
697
|
else resolve();
|
|
@@ -699,7 +699,7 @@ async function cleanupWorktree(worktreeId, options = {}) {
|
|
|
699
699
|
);
|
|
700
700
|
});
|
|
701
701
|
} catch (dbErr) {
|
|
702
|
-
console.error(`Failed to mark worktree as
|
|
702
|
+
console.error(`Failed to mark worktree as cleaned: ${dbErr.message}`);
|
|
703
703
|
}
|
|
704
704
|
|
|
705
705
|
throw new Error(`Worktree cleanup failed: ${err.message}`);
|
|
@@ -95,7 +95,7 @@ async function reconcileState(db, repoPath, options = {}) {
|
|
|
95
95
|
if (cleanup && staleEntries.length > 0) {
|
|
96
96
|
for (const entry of staleEntries) {
|
|
97
97
|
try {
|
|
98
|
-
await
|
|
98
|
+
await markWorktreeCleaned(db, entry.id);
|
|
99
99
|
results.cleanupAttempts.staleDbEntries++;
|
|
100
100
|
} catch (err) {
|
|
101
101
|
results.errors.push(`Failed to mark stale entry as corrupted: ${err.message}`);
|
|
@@ -127,7 +127,7 @@ async function reconcileState(db, repoPath, options = {}) {
|
|
|
127
127
|
results.cleanupAttempts.gitFilesystemMismatches++;
|
|
128
128
|
results.gitCommands.push(`git worktree add --force`);
|
|
129
129
|
} catch (err) {
|
|
130
|
-
await
|
|
130
|
+
await markWorktreeCleaned(db, mismatch.id);
|
|
131
131
|
results.errors.push(`Failed to re-register worktree ${mismatch.path}: ${err.message}`);
|
|
132
132
|
}
|
|
133
133
|
}
|
|
@@ -342,13 +342,13 @@ async function detectStaleDbEntries(db, repoPath) {
|
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
/**
|
|
345
|
-
* Mark worktree as
|
|
345
|
+
* Mark worktree as cleaned in database (terminal state)
|
|
346
346
|
*/
|
|
347
|
-
async function
|
|
347
|
+
async function markWorktreeCleaned(db, worktreeId) {
|
|
348
348
|
return new Promise((resolve, reject) => {
|
|
349
349
|
db.run(
|
|
350
350
|
'UPDATE worktrees SET status = ?, updated_at = datetime(\'now\') WHERE id = ?',
|
|
351
|
-
['
|
|
351
|
+
['cleaned', worktreeId],
|
|
352
352
|
(err) => {
|
|
353
353
|
if (err) reject(err);
|
|
354
354
|
else resolve();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jettypod",
|
|
3
|
-
"version": "4.4.
|
|
3
|
+
"version": "4.4.121",
|
|
4
4
|
"description": "AI-powered development workflow manager with TDD, BDD, and automatic test generation",
|
|
5
5
|
"main": "jettypod.js",
|
|
6
6
|
"bin": {
|
|
@@ -34,8 +34,10 @@
|
|
|
34
34
|
"test:unit": "NODE_ENV=test jest",
|
|
35
35
|
"test:unit:watch": "NODE_ENV=test jest --watch",
|
|
36
36
|
"test:cleanup": "node scripts/test-cleanup.js",
|
|
37
|
+
"version": "node -e \"const v=require('./package.json').version; const f='apps/dashboard/package.json'; const p=JSON.parse(require('fs').readFileSync(f)); p.version=v; require('fs').writeFileSync(f, JSON.stringify(p,null,2)+'\\n')\"",
|
|
37
38
|
"prepublishOnly": "echo '๐ Syncing skills to templates...' && cp -r .claude/skills/* skills-templates/ && (git diff --quiet skills-templates/ || (git add skills-templates/ && git commit -m 'chore: Sync skills-templates')) && echo 'โ
Skills synced' && echo '๐จ Building dashboard...' && cd apps/dashboard && npm install && npm run build && echo 'โ
Dashboard built'",
|
|
38
39
|
"postpublish": "git push origin main --tags && echo 'โ
Pushed version commit and tags'",
|
|
40
|
+
"postinstall": "node scripts/postinstall.js",
|
|
39
41
|
"dashboard": "npm run dev --prefix apps/dashboard",
|
|
40
42
|
"dashboard:build": "npm run build --prefix apps/dashboard",
|
|
41
43
|
"dashboard:start": "npm run start --prefix apps/dashboard"
|
|
@@ -48,13 +50,18 @@
|
|
|
48
50
|
"ws": "^8.18.3"
|
|
49
51
|
},
|
|
50
52
|
"jest": {
|
|
53
|
+
"setupFiles": [
|
|
54
|
+
"./jest.setup.js"
|
|
55
|
+
],
|
|
51
56
|
"testEnvironment": "node",
|
|
52
57
|
"testMatch": [
|
|
53
58
|
"**/__tests__/**/*.js",
|
|
54
59
|
"**/*.test.js"
|
|
55
60
|
],
|
|
56
61
|
"testPathIgnorePatterns": [
|
|
57
|
-
"/node_modules/"
|
|
62
|
+
"/node_modules/",
|
|
63
|
+
"/apps/dashboard/dist/",
|
|
64
|
+
"/.jettypod-work/"
|
|
58
65
|
],
|
|
59
66
|
"collectCoverage": false,
|
|
60
67
|
"coverageDirectory": "coverage"
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Convert cucumber ndjson (message format) to standard cucumber JSON format.
|
|
3
|
+
// Usage: node scripts/ndjson-to-cucumber-json.js [input.ndjson] [output.json]
|
|
4
|
+
// Defaults: cucumber-results.ndjson -> cucumber-results.json
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
function convert(ndjsonPath, jsonPath) {
|
|
10
|
+
const raw = fs.readFileSync(ndjsonPath, 'utf-8');
|
|
11
|
+
const lines = raw.split('\n').filter(l => l.trim());
|
|
12
|
+
|
|
13
|
+
// Lookup tables
|
|
14
|
+
const gherkinDocuments = new Map(); // uri -> gherkinDocument
|
|
15
|
+
const pickles = new Map(); // pickle.id -> pickle
|
|
16
|
+
const testCases = new Map(); // testCase.id -> testCase
|
|
17
|
+
const testCaseStartedMap = new Map(); // testCaseStarted.id -> testCaseStarted
|
|
18
|
+
const stepDefinitions = new Map(); // stepDefinition.id -> stepDefinition
|
|
19
|
+
|
|
20
|
+
// Results
|
|
21
|
+
const testStepResults = new Map(); // testCaseStartedId -> [{stepId, result}]
|
|
22
|
+
|
|
23
|
+
for (const line of lines) {
|
|
24
|
+
let msg;
|
|
25
|
+
try {
|
|
26
|
+
msg = JSON.parse(line);
|
|
27
|
+
} catch {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (msg.gherkinDocument) {
|
|
32
|
+
gherkinDocuments.set(msg.gherkinDocument.uri, msg.gherkinDocument);
|
|
33
|
+
}
|
|
34
|
+
if (msg.pickle) {
|
|
35
|
+
pickles.set(msg.pickle.id, msg.pickle);
|
|
36
|
+
}
|
|
37
|
+
if (msg.stepDefinition) {
|
|
38
|
+
stepDefinitions.set(msg.stepDefinition.id, msg.stepDefinition);
|
|
39
|
+
}
|
|
40
|
+
if (msg.testCase) {
|
|
41
|
+
testCases.set(msg.testCase.id, msg.testCase);
|
|
42
|
+
}
|
|
43
|
+
if (msg.testCaseStarted) {
|
|
44
|
+
testCaseStartedMap.set(msg.testCaseStarted.id, msg.testCaseStarted);
|
|
45
|
+
testStepResults.set(msg.testCaseStarted.id, []);
|
|
46
|
+
}
|
|
47
|
+
if (msg.testStepFinished) {
|
|
48
|
+
const tsf = msg.testStepFinished;
|
|
49
|
+
const arr = testStepResults.get(tsf.testCaseStartedId);
|
|
50
|
+
if (arr) {
|
|
51
|
+
arr.push({
|
|
52
|
+
testStepId: tsf.testStepId,
|
|
53
|
+
result: tsf.testStepResult,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Build cucumber JSON structure grouped by feature URI
|
|
60
|
+
const featureMap = new Map(); // uri -> { feature data }
|
|
61
|
+
|
|
62
|
+
for (const [tcsId, tcs] of testCaseStartedMap) {
|
|
63
|
+
const testCase = testCases.get(tcs.testCaseId);
|
|
64
|
+
if (!testCase) continue;
|
|
65
|
+
const pickle = pickles.get(testCase.pickleId);
|
|
66
|
+
if (!pickle) continue;
|
|
67
|
+
|
|
68
|
+
const uri = pickle.uri;
|
|
69
|
+
if (!featureMap.has(uri)) {
|
|
70
|
+
const doc = gherkinDocuments.get(uri);
|
|
71
|
+
featureMap.set(uri, {
|
|
72
|
+
uri,
|
|
73
|
+
name: doc?.feature?.name || uri,
|
|
74
|
+
description: doc?.feature?.description || '',
|
|
75
|
+
keyword: doc?.feature?.keyword || 'Feature',
|
|
76
|
+
elements: [],
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const stepResults = testStepResults.get(tcsId) || [];
|
|
81
|
+
const steps = [];
|
|
82
|
+
|
|
83
|
+
for (const testStep of testCase.testSteps) {
|
|
84
|
+
// Skip hook steps (no pickleStepId)
|
|
85
|
+
if (!testStep.pickleStepId) continue;
|
|
86
|
+
|
|
87
|
+
const pickleStep = pickle.steps.find(s => s.id === testStep.pickleStepId);
|
|
88
|
+
if (!pickleStep) continue;
|
|
89
|
+
|
|
90
|
+
// Find the result for this step
|
|
91
|
+
const stepResult = stepResults.find(r => r.testStepId === testStep.id);
|
|
92
|
+
const result = stepResult?.result;
|
|
93
|
+
|
|
94
|
+
const dur = result?.duration || { seconds: 0, nanos: 0 };
|
|
95
|
+
const durationNanos = (dur.seconds || 0) * 1e9 + (dur.nanos || 0);
|
|
96
|
+
|
|
97
|
+
// Get keyword from gherkin document
|
|
98
|
+
let keyword = '';
|
|
99
|
+
const doc = gherkinDocuments.get(uri);
|
|
100
|
+
if (doc?.feature) {
|
|
101
|
+
for (const child of doc.feature.children || []) {
|
|
102
|
+
const scenario = child.scenario;
|
|
103
|
+
if (!scenario) continue;
|
|
104
|
+
for (const gherkinStep of scenario.steps) {
|
|
105
|
+
if (pickleStep.astNodeIds?.includes(gherkinStep.id)) {
|
|
106
|
+
keyword = gherkinStep.keyword;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (keyword) break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
steps.push({
|
|
115
|
+
keyword: keyword || 'Step ',
|
|
116
|
+
name: pickleStep.text || '',
|
|
117
|
+
result: {
|
|
118
|
+
status: (result?.status || 'UNKNOWN').toLowerCase(),
|
|
119
|
+
duration: durationNanos,
|
|
120
|
+
error_message: result?.message || undefined,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
featureMap.get(uri).elements.push({
|
|
126
|
+
type: 'scenario',
|
|
127
|
+
name: pickle.name,
|
|
128
|
+
steps,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const output = Array.from(featureMap.values());
|
|
133
|
+
fs.writeFileSync(jsonPath, JSON.stringify(output, null, 2));
|
|
134
|
+
return output.reduce((sum, f) => sum + f.elements.length, 0);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// CLI entry point
|
|
138
|
+
if (require.main === module) {
|
|
139
|
+
const projectRoot = path.resolve(__dirname, '..');
|
|
140
|
+
const ndjsonPath = process.argv[2] || path.join(projectRoot, 'cucumber-results.ndjson');
|
|
141
|
+
const jsonPath = process.argv[3] || path.join(projectRoot, 'cucumber-results.json');
|
|
142
|
+
|
|
143
|
+
if (!fs.existsSync(ndjsonPath)) {
|
|
144
|
+
console.error(`No ndjson file found at ${ndjsonPath}`);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const count = convert(ndjsonPath, jsonPath);
|
|
149
|
+
console.log(`Converted ${count} scenarios to ${jsonPath}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
module.exports = { convert };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Records the source directory on global install so jettypod can auto-update.
|
|
4
|
+
// Writes ~/.jettypod-dev.json with the source path for staleness detection.
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
// Only run for global installs
|
|
11
|
+
if (process.env.npm_config_global !== 'true') process.exit(0);
|
|
12
|
+
|
|
13
|
+
const sourceDir = process.env.INIT_CWD;
|
|
14
|
+
if (!sourceDir) process.exit(0);
|
|
15
|
+
|
|
16
|
+
// Verify source dir looks like a jettypod repo (has jettypod.js)
|
|
17
|
+
if (!fs.existsSync(path.join(sourceDir, 'jettypod.js'))) process.exit(0);
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const devInfo = { sourceDir };
|
|
21
|
+
const devFile = path.join(os.homedir(), '.jettypod-dev.json');
|
|
22
|
+
fs.writeFileSync(devFile, JSON.stringify(devInfo, null, 2) + '\n');
|
|
23
|
+
} catch {
|
|
24
|
+
// Non-fatal โ skip silently
|
|
25
|
+
}
|
|
@@ -44,7 +44,7 @@ When this skill is activated, you are implementing a bug fix directly on the bug
|
|
|
44
44
|
|
|
45
45
|
Your job: Implement the fix, write a regression test, and verify everything passes.
|
|
46
46
|
|
|
47
|
-
##
|
|
47
|
+
## Critical Context
|
|
48
48
|
|
|
49
49
|
**You are working in an isolated git worktree:**
|
|
50
50
|
- `work start [bug-id]` already created a dedicated worktree
|
|
@@ -55,7 +55,7 @@ Your job: Implement the fix, write a regression test, and verify everything pass
|
|
|
55
55
|
|
|
56
56
|
---
|
|
57
57
|
|
|
58
|
-
##
|
|
58
|
+
## SHELL CWD RECOVERY
|
|
59
59
|
|
|
60
60
|
**If ALL bash commands start failing with "Error: Exit code 1" and no output:**
|
|
61
61
|
|
|
@@ -69,7 +69,7 @@ Your shell's working directory was likely inside a worktree that was deleted. Th
|
|
|
69
69
|
|
|
70
70
|
---
|
|
71
71
|
|
|
72
|
-
##
|
|
72
|
+
## PRE-FLIGHT VALIDATION (REQUIRED)
|
|
73
73
|
|
|
74
74
|
**Before proceeding, validate the worktree exists.**
|
|
75
75
|
|
|
@@ -81,9 +81,9 @@ sqlite3 .jettypod/work.db "SELECT wi.id, wi.title, wi.status, wt.worktree_path,
|
|
|
81
81
|
|
|
82
82
|
| worktree_path | What it means | Action |
|
|
83
83
|
|---------------|---------------|--------|
|
|
84
|
-
| **Has a path** |
|
|
85
|
-
| **NULL or empty** |
|
|
86
|
-
| **No rows returned** |
|
|
84
|
+
| **Has a path** | Worktree exists, ready to proceed | Continue to Step 0 |
|
|
85
|
+
| **NULL or empty** | `work start` was not called | **STOP - run `jettypod work start [bug-id]` first** |
|
|
86
|
+
| **No rows returned** | No bug is in progress | **STOP - verify the bug exists and run `work start`** |
|
|
87
87
|
|
|
88
88
|
**After pre-flight passes, emit gate signal:**
|
|
89
89
|
|
|
@@ -114,7 +114,7 @@ The bug-planning skill embedded implementation guidance:
|
|
|
114
114
|
|
|
115
115
|
```
|
|
116
116
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
117
|
-
|
|
117
|
+
BUG MODE: Fixing #[bug-id]
|
|
118
118
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
119
119
|
|
|
120
120
|
Bug: #[id] [title]
|
|
@@ -176,14 +176,14 @@ sqlite3 .jettypod/work.db "SELECT project_state FROM project_config WHERE id = 1
|
|
|
176
176
|
|
|
177
177
|
```
|
|
178
178
|
โโโ Iteration 1 โโโ
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
Reading: [file:line from breadcrumbs]
|
|
180
|
+
Change: [what you're fixing]
|
|
181
181
|
```
|
|
182
182
|
|
|
183
183
|
**After implementation:**
|
|
184
184
|
|
|
185
185
|
```
|
|
186
|
-
|
|
186
|
+
Fix Implemented
|
|
187
187
|
|
|
188
188
|
Changes made:
|
|
189
189
|
- [Change 1]
|
|
@@ -197,6 +197,16 @@ Now writing regression test...
|
|
|
197
197
|
|
|
198
198
|
---
|
|
199
199
|
|
|
200
|
+
### BDD Step Definition Constraints (ENFORCED)
|
|
201
|
+
|
|
202
|
+
Before writing any step definitions, read `templates/bdd-guidance.md`. BANNED in step defs: `setTimeout`/`sleep`, module-level `let`/`var`, raw SQL, assertions in Given/When, loops/branching, self-fulfilling mocks (setting state then asserting it). Steps must be 1-5 lines calling one helper via Cucumber World (`this`). Read `features/support/helpers/` and reuse before creating.
|
|
203
|
+
|
|
204
|
+
**Scaffolding:** If `features/support/helpers/` does not exist in the project, scaffold it before writing step definitions. Copy `templates/bdd-scaffolding/wait.js` to `features/support/helpers/wait.js` and `templates/bdd-scaffolding/world.js` to `features/support/world.js`.
|
|
205
|
+
|
|
206
|
+
**Helper Reuse:** Before writing step definitions, glob `features/support/helpers/**/*.js` and read each file. Import and call existing helpers in your steps โ never duplicate functionality that already exists.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
200
210
|
### Step 2: Write Regression Test
|
|
201
211
|
|
|
202
212
|
**Goal:** Create a test that verifies the bug is fixed and prevents regression.
|
|
@@ -239,7 +249,7 @@ npx cucumber-js features/<bug-slug>.feature
|
|
|
239
249
|
**Confirm test passes:**
|
|
240
250
|
|
|
241
251
|
```
|
|
242
|
-
|
|
252
|
+
Regression Test Passing
|
|
243
253
|
|
|
244
254
|
Test: features/<bug-slug>.feature
|
|
245
255
|
Status: PASSING
|
|
@@ -272,7 +282,7 @@ npx cucumber-js features/<related-feature>.feature 2>/dev/null || true
|
|
|
272
282
|
**If regressions found:**
|
|
273
283
|
|
|
274
284
|
```
|
|
275
|
-
|
|
285
|
+
Regression Detected
|
|
276
286
|
|
|
277
287
|
Failing test: [test name]
|
|
278
288
|
Error: [error message]
|
|
@@ -285,14 +295,40 @@ Fix the regression and re-run tests. Iterate until all related tests pass.
|
|
|
285
295
|
**When all related tests pass:**
|
|
286
296
|
|
|
287
297
|
```
|
|
288
|
-
|
|
298
|
+
Related Tests Passing
|
|
289
299
|
|
|
290
|
-
- Regression test:
|
|
291
|
-
- Related area tests:
|
|
300
|
+
- Regression test: PASS
|
|
301
|
+
- Related area tests: [X] scenarios passing
|
|
292
302
|
|
|
293
303
|
Ready to commit and merge.
|
|
294
304
|
```
|
|
295
305
|
|
|
306
|
+
**Proceed to Step 3B.**
|
|
307
|
+
|
|
308
|
+
**Quality Gate:** Before committing, review every `.steps.js` file you wrote or modified. Check for banlist violations: `setTimeout`/`sleep`, module-level `let`/`var`, raw SQL, assertions in Given/When, self-fulfilling mocks, steps longer than 5 lines. Fix violations before proceeding.
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
### Step 3B: Prove It Works
|
|
313
|
+
|
|
314
|
+
**Before committing, demonstrate the fix works in practice โ not just that tests pass.**
|
|
315
|
+
|
|
316
|
+
1. **Run or build the actual code** โ compile, start the server, execute the command, or trigger the relevant workflow
|
|
317
|
+
2. **Show real output** โ display console output, build result, or observable behavior
|
|
318
|
+
3. **Confirm the bug is fixed** โ verify the original bug scenario no longer reproduces
|
|
319
|
+
|
|
320
|
+
**Display:**
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
Verifying fix in practice...
|
|
324
|
+
|
|
325
|
+
[Actual output from running/building the code]
|
|
326
|
+
|
|
327
|
+
Verified: [what was confirmed โ original bug no longer reproduces]
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**If the change cannot be run directly** (requires hardware, external service, or manual UI interaction), state what the user should verify manually and why you can't verify it yourself.
|
|
331
|
+
|
|
296
332
|
**Proceed to Step 4.**
|
|
297
333
|
|
|
298
334
|
---
|
|
@@ -315,7 +351,7 @@ Fix: [What was changed]
|
|
|
315
351
|
|
|
316
352
|
Closes #<bug-id>
|
|
317
353
|
|
|
318
|
-
|
|
354
|
+
Generated with [Claude Code](https://claude.com/claude-code)
|
|
319
355
|
|
|
320
356
|
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>"
|
|
321
357
|
```
|
|
@@ -327,6 +363,8 @@ Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>"
|
|
|
327
363
|
jettypod work merge <bug-id>
|
|
328
364
|
```
|
|
329
365
|
|
|
366
|
+
**If merge fails with `Uncommitted changes detected`:** `git stash` from main repo CWD โ create a work item for the stashed changes โ `jettypod work start <id>` โ `git stash pop` in that worktree โ commit โ merge โ cleanup โ then retry your original merge.
|
|
367
|
+
|
|
330
368
|
```bash
|
|
331
369
|
# Step 2: cd to main repo
|
|
332
370
|
cd <main-repo-path>
|
|
@@ -347,6 +385,22 @@ jettypod work cleanup <bug-id>
|
|
|
347
385
|
**The merge command automatically sets the bug as ready for review.**
|
|
348
386
|
It will appear with accept/reject buttons on the kanban board. Do NOT call `jettypod work status <bug-id> done` โ that bypasses the review gate.
|
|
349
387
|
|
|
388
|
+
**Generate QA checklist:**
|
|
389
|
+
|
|
390
|
+
Based on the bug fix and regression test, generate specific QA steps to verify the fix and ensure no regressions. Write a JSON array to `/tmp/qa-steps.json`:
|
|
391
|
+
```json
|
|
392
|
+
[
|
|
393
|
+
{"section": "Bug Fix Verification", "text": "Reproduce the original bug scenario โ it should no longer occur", "detail": "Steps to reproduce..."},
|
|
394
|
+
{"section": "Regression Check", "text": "Verify related functionality still works", "detail": "..."},
|
|
395
|
+
...
|
|
396
|
+
]
|
|
397
|
+
```
|
|
398
|
+
Be specific. 3-8 steps covering: original bug fixed, regression scenarios, edge cases.
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
jettypod work set-qa-steps <bug-id> --from=/tmp/qa-steps.json
|
|
402
|
+
```
|
|
403
|
+
|
|
350
404
|
**Emit gate signal:**
|
|
351
405
|
|
|
352
406
|
```bash
|
|
@@ -357,11 +411,11 @@ jettypod ui gate complete --data='{"summary":"Bug fixed: [brief description]","f
|
|
|
357
411
|
|
|
358
412
|
```
|
|
359
413
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
360
|
-
|
|
414
|
+
Bug Fix Complete
|
|
361
415
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
362
416
|
|
|
363
|
-
|
|
364
|
-
|
|
417
|
+
Bug: #[bug-id] [title]
|
|
418
|
+
Regression test: features/<bug-slug>.feature
|
|
365
419
|
|
|
366
420
|
What was fixed:
|
|
367
421
|
- Root cause: [brief description]
|
|
@@ -400,4 +454,9 @@ cd <main-repo> # Change to main repo
|
|
|
400
454
|
jettypod work cleanup <bug-id> # Clean up worktree
|
|
401
455
|
```
|
|
402
456
|
|
|
403
|
-
|
|
457
|
+
**Force-cleanup stale worktree** (when merge is blocked or fix was superseded):
|
|
458
|
+
```bash
|
|
459
|
+
jettypod work cleanup --force <bug-id> # Bypasses merge requirement, prompts for confirmation
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Do NOT mark bug as done manually โ the merge command sets the review gate automatically.**
|