jettypod 4.4.120 → 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 +2 -1
- 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 +54 -49
- package/apps/dashboard/app/demo/gates/page.tsx +3 -5
- package/apps/dashboard/app/design-system/page.tsx +1 -1
- package/apps/dashboard/app/globals.css +74 -2
- package/apps/dashboard/app/install-claude/page.tsx +3 -5
- package/apps/dashboard/app/login/page.tsx +17 -20
- package/apps/dashboard/app/page.tsx +101 -48
- package/apps/dashboard/app/settings/page.tsx +60 -12
- package/apps/dashboard/app/signup/page.tsx +14 -17
- 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 +12 -15
- package/apps/dashboard/app/work/[id]/page.tsx +90 -75
- package/apps/dashboard/app/work/[id]/proof/page.tsx +1489 -0
- package/apps/dashboard/components/AppShell.tsx +70 -61
- package/apps/dashboard/components/CardMenu.tsx +0 -1
- package/apps/dashboard/components/ClaudePanel.tsx +541 -283
- package/apps/dashboard/components/ClaudePanelInput.tsx +23 -4
- package/apps/dashboard/components/ConnectClaudeScreen.tsx +1 -5
- package/apps/dashboard/components/CopyableId.tsx +1 -2
- package/apps/dashboard/components/DetailReviewActions.tsx +11 -20
- package/apps/dashboard/components/DragContext.tsx +132 -62
- package/apps/dashboard/components/DraggableCard.tsx +3 -5
- package/apps/dashboard/components/DropZone.tsx +5 -6
- package/apps/dashboard/components/EditableDetailDescription.tsx +6 -12
- package/apps/dashboard/components/EditableDetailTitle.tsx +6 -13
- package/apps/dashboard/components/EditableTitle.tsx +0 -1
- package/apps/dashboard/components/ElapsedTimer.tsx +15 -3
- package/apps/dashboard/components/EpicGroup.tsx +100 -70
- package/apps/dashboard/components/GateCard.tsx +0 -1
- package/apps/dashboard/components/GateChoiceCard.tsx +1 -2
- package/apps/dashboard/components/InstallClaudeScreen.tsx +1 -5
- package/apps/dashboard/components/JettyLoader.tsx +0 -1
- package/apps/dashboard/components/KanbanBoard.tsx +319 -173
- package/apps/dashboard/components/KanbanCard.tsx +341 -107
- package/apps/dashboard/components/LazyCard.tsx +62 -0
- package/apps/dashboard/components/LazyMarkdown.tsx +0 -1
- package/apps/dashboard/components/MainNav.tsx +24 -25
- package/apps/dashboard/components/MessageBlock.tsx +93 -16
- package/apps/dashboard/components/ModeStartCard.tsx +0 -1
- package/apps/dashboard/components/OnboardingWelcome.tsx +0 -1
- package/apps/dashboard/components/PlaceholderCard.tsx +0 -1
- package/apps/dashboard/components/ProjectSwitcher.tsx +20 -20
- package/apps/dashboard/components/PrototypeTimeline.tsx +47 -26
- package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +308 -223
- package/apps/dashboard/components/RealTimeTestsWrapper.tsx +303 -160
- package/apps/dashboard/components/ReviewFooter.tsx +12 -14
- package/apps/dashboard/components/SessionList.tsx +0 -1
- package/apps/dashboard/components/SubscribeContent.tsx +40 -11
- package/apps/dashboard/components/TestTree.tsx +1 -2
- package/apps/dashboard/components/TipCard.tsx +2 -4
- package/apps/dashboard/components/Toast.tsx +0 -1
- package/apps/dashboard/components/TypeIcon.tsx +7 -8
- package/apps/dashboard/components/ViewModeToolbar.tsx +104 -0
- package/apps/dashboard/components/WaveCompletionAnimation.tsx +5 -17
- package/apps/dashboard/components/WelcomeScreen.tsx +2 -6
- package/apps/dashboard/components/WorkItemHeader.tsx +0 -1
- package/apps/dashboard/components/WorkItemTree.tsx +2 -4
- package/apps/dashboard/components/settings/AccountSection.tsx +27 -13
- 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 +20 -73
- package/apps/dashboard/components/settings/GeneralSection.tsx +137 -26
- package/apps/dashboard/components/settings/ProjectStackSection.tsx +948 -0
- package/apps/dashboard/components/settings/SettingsLayout.tsx +0 -1
- package/apps/dashboard/components/ui/Button.tsx +1 -1
- package/apps/dashboard/components/ui/Input.tsx +1 -1
- package/apps/dashboard/components.json +1 -1
- package/apps/dashboard/contexts/ClaudeSessionContext.tsx +611 -358
- package/apps/dashboard/contexts/ConnectionStatusContext.tsx +0 -1
- package/apps/dashboard/contexts/UsageContext.tsx +62 -31
- package/apps/dashboard/dev.sh +35 -0
- package/apps/dashboard/eslint.config.mjs +9 -9
- package/apps/dashboard/hooks/useWebSocket.ts +138 -83
- package/apps/dashboard/index.html +73 -0
- package/apps/dashboard/lib/data-bridge.ts +722 -0
- package/apps/dashboard/lib/db.ts +69 -1302
- 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 +226 -26
- package/apps/dashboard/lib/proof-run.ts +495 -0
- package/apps/dashboard/lib/proof-scenario-runner.ts +346 -0
- 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 +253 -122
- 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 +3 -3
- package/apps/dashboard/next-env.d.ts +1 -1
- package/apps/dashboard/package.json +21 -33
- package/apps/dashboard/public/bug-icon.png +0 -0
- package/apps/dashboard/public/buoy-icon.png +0 -0
- package/apps/dashboard/public/in-flight-seagull.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/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 +167 -30
- 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/jettypod.js +96 -4
- package/lib/bdd-preflight.js +96 -0
- package/lib/merge-lock.js +111 -253
- package/lib/migrations/030-rejection-round-columns.js +54 -0
- package/lib/migrations/031-session-isolation-index.js +17 -0
- package/lib/work-commands/index.js +58 -16
- package/lib/work-tracking/index.js +108 -8
- package/package.json +1 -1
- package/skills-templates/bug-mode/SKILL.md +43 -1
- package/skills-templates/chore-mode/SKILL.md +40 -1
- package/skills-templates/design-system-selection/SKILL.md +273 -0
- package/skills-templates/epic-planning/SKILL.md +14 -0
- package/skills-templates/feature-planning/SKILL.md +90 -1
- package/skills-templates/production-mode/SKILL.md +20 -0
- package/skills-templates/simple-improvement/SKILL.md +39 -2
- package/skills-templates/speed-mode/SKILL.md +10 -15
- package/skills-templates/stable-mode/SKILL.md +47 -0
- package/apps/dashboard/README.md +0 -36
- package/apps/dashboard/app/api/claude/[workItemId]/message/route.ts +0 -446
- package/apps/dashboard/app/api/claude/[workItemId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/[workItemId]/route.ts +0 -280
- 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 -525
- 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]/route.ts +0 -35
- package/apps/dashboard/app/api/work/[id]/status/route.ts +0 -63
- package/apps/dashboard/app/api/work/[id]/title/route.ts +0 -21
- package/apps/dashboard/app/layout.tsx +0 -55
- package/apps/dashboard/components/UpgradeBanner.tsx +0 -30
- package/apps/dashboard/electron/ipc-handlers.js +0 -1026
- package/apps/dashboard/electron/main.js +0 -2306
- package/apps/dashboard/electron/preload.js +0 -125
- package/apps/dashboard/electron/session-manager.js +0 -163
- package/apps/dashboard/electron-builder.config.js +0 -357
- package/apps/dashboard/hooks/useClaudeSessions.ts +0 -299
- package/apps/dashboard/lib/backlog-parser.ts +0 -50
- package/apps/dashboard/lib/claude-process-manager.ts +0 -529
- package/apps/dashboard/lib/db-bridge.ts +0 -283
- 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 -66
- package/apps/dashboard/postcss.config.mjs +0 -7
- package/apps/dashboard/public/bug-icon.svg +0 -9
- package/apps/dashboard/public/buoy-icon.svg +0 -9
- package/apps/dashboard/public/file.svg +0 -1
- package/apps/dashboard/public/globe.svg +0 -1
- package/apps/dashboard/public/in-flight-seagull.svg +0 -9
- package/apps/dashboard/public/next.svg +0 -1
- package/apps/dashboard/public/pier-icon.svg +0 -14
- package/apps/dashboard/public/star-icon.svg +0 -9
- package/apps/dashboard/public/vercel.svg +0 -1
- package/apps/dashboard/public/window.svg +0 -1
- package/apps/dashboard/public/wrench-icon.svg +0 -9
- package/apps/dashboard/scripts/download-node.js +0 -104
- package/apps/dashboard/scripts/upload-to-r2.js +0 -89
|
@@ -1331,13 +1331,12 @@ async function cleanupWorkItem(workItemId, options = {}) {
|
|
|
1331
1331
|
* Pushes feature branch, checks out main, merges, and pushes main
|
|
1332
1332
|
* Post-merge hook will mark work item as done and cleanup worktree
|
|
1333
1333
|
* @param {Object} options - Merge options
|
|
1334
|
-
* @param {boolean} options.withTransition - Hold lock for transition phase (BDD generation)
|
|
1335
1334
|
* @param {boolean} options.releaseLock - Release held lock only (no merge)
|
|
1336
|
-
* @returns {Promise<void
|
|
1335
|
+
* @returns {Promise<void>}
|
|
1337
1336
|
* @throws {Error} If no current work, git operations fail, or not in git repo
|
|
1338
1337
|
*/
|
|
1339
1338
|
async function mergeWork(options = {}) {
|
|
1340
|
-
const {
|
|
1339
|
+
const { releaseLock = false, featureBranch = null, workItemId = null } = options;
|
|
1341
1340
|
|
|
1342
1341
|
// Handle lock release-only mode first (can run from anywhere)
|
|
1343
1342
|
if (releaseLock) {
|
|
@@ -2014,7 +2013,7 @@ async function mergeWork(options = {}) {
|
|
|
2014
2013
|
});
|
|
2015
2014
|
console.log(`✅ ${currentWork.type.charAt(0).toUpperCase() + currentWork.type.slice(1)} #${currentWork.id} ready for review`);
|
|
2016
2015
|
} else {
|
|
2017
|
-
// Chore/bug under a feature: mark as done
|
|
2016
|
+
// Chore/bug under a feature: mark as done, then check if feature is complete
|
|
2018
2017
|
console.log(`Marking ${currentWork.type} as done...`);
|
|
2019
2018
|
const completedAt = new Date().toISOString();
|
|
2020
2019
|
await new Promise((resolve, reject) => {
|
|
@@ -2028,6 +2027,60 @@ async function mergeWork(options = {}) {
|
|
|
2028
2027
|
);
|
|
2029
2028
|
});
|
|
2030
2029
|
console.log(`✅ ${currentWork.type.charAt(0).toUpperCase() + currentWork.type.slice(1)} #${currentWork.id} marked as done`);
|
|
2030
|
+
|
|
2031
|
+
// Check if all sibling chores under the parent feature are now done
|
|
2032
|
+
// If so, and mode progression is complete, set ready_for_review on the parent
|
|
2033
|
+
const parent = await new Promise((resolve, reject) => {
|
|
2034
|
+
db.get('SELECT id, type, mode FROM work_items WHERE id = ?', [currentWork.parent_id], (err, row) => {
|
|
2035
|
+
if (err) return reject(err);
|
|
2036
|
+
resolve(row);
|
|
2037
|
+
});
|
|
2038
|
+
});
|
|
2039
|
+
|
|
2040
|
+
if (parent && parent.type === 'feature') {
|
|
2041
|
+
const siblingChores = await new Promise((resolve, reject) => {
|
|
2042
|
+
db.all(
|
|
2043
|
+
'SELECT id, status FROM work_items WHERE parent_id = ? AND type = ?',
|
|
2044
|
+
[parent.id, 'chore'],
|
|
2045
|
+
(err, rows) => {
|
|
2046
|
+
if (err) return reject(err);
|
|
2047
|
+
resolve(rows || []);
|
|
2048
|
+
}
|
|
2049
|
+
);
|
|
2050
|
+
});
|
|
2051
|
+
|
|
2052
|
+
const allChoresDone = siblingChores.length > 0 && siblingChores.every(c => c.status === 'done');
|
|
2053
|
+
if (allChoresDone) {
|
|
2054
|
+
const config = await new Promise((resolve, reject) => {
|
|
2055
|
+
db.get('SELECT project_state FROM project_config WHERE id = 1', [], (err, row) => {
|
|
2056
|
+
if (err) return reject(err);
|
|
2057
|
+
resolve(row);
|
|
2058
|
+
});
|
|
2059
|
+
});
|
|
2060
|
+
const projectState = (config && config.project_state) || 'internal';
|
|
2061
|
+
const featureMode = parent.mode || 'speed';
|
|
2062
|
+
|
|
2063
|
+
let featureComplete = false;
|
|
2064
|
+
if (projectState === 'internal') {
|
|
2065
|
+
featureComplete = (featureMode === 'stable');
|
|
2066
|
+
} else if (projectState === 'external') {
|
|
2067
|
+
featureComplete = (featureMode === 'production');
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
if (featureComplete) {
|
|
2071
|
+
await new Promise((resolve, reject) => {
|
|
2072
|
+
db.run('UPDATE work_items SET ready_for_review = 1 WHERE id = ?', [parent.id], (err) => {
|
|
2073
|
+
if (err) return reject(err);
|
|
2074
|
+
resolve();
|
|
2075
|
+
});
|
|
2076
|
+
});
|
|
2077
|
+
console.log(`✅ Feature #${parent.id} ready for review (all ${featureMode} mode chores done)`);
|
|
2078
|
+
} else {
|
|
2079
|
+
const nextMode = featureMode === 'speed' ? 'stable' : 'production';
|
|
2080
|
+
console.log(`✓ All ${featureMode} mode chores done. Feature #${parent.id} ready for ${nextMode} mode.`);
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2031
2084
|
}
|
|
2032
2085
|
}
|
|
2033
2086
|
|
|
@@ -2058,20 +2111,9 @@ async function mergeWork(options = {}) {
|
|
|
2058
2111
|
console.log(' Worktrees accumulate until cleaned up.');
|
|
2059
2112
|
}
|
|
2060
2113
|
|
|
2061
|
-
if (withTransition) {
|
|
2062
|
-
// Hold lock for transition phase (BDD generation)
|
|
2063
|
-
// Work is done and worktree is cleaned up, but lock is held so no other merges
|
|
2064
|
-
// happen while Claude generates stable scenarios on main
|
|
2065
|
-
console.log('⚠️ Merge lock held for transition phase');
|
|
2066
|
-
console.log(' Skills will release lock after generating stable scenarios');
|
|
2067
|
-
console.log(' Release lock with: jettypod work merge --release-lock');
|
|
2068
|
-
return Promise.resolve({ lockHeld: true });
|
|
2069
|
-
}
|
|
2070
|
-
|
|
2071
2114
|
return Promise.resolve();
|
|
2072
2115
|
} finally {
|
|
2073
|
-
|
|
2074
|
-
if (lock && !withTransition) {
|
|
2116
|
+
if (lock) {
|
|
2075
2117
|
try {
|
|
2076
2118
|
await lock.release();
|
|
2077
2119
|
console.log('✅ Merge lock released');
|
|
@@ -298,8 +298,12 @@ function create(type, title, description = '', parentId = null, mode = null, nee
|
|
|
298
298
|
// Set phase for features (discovery when mode=NULL, implementation when mode is set, NULL for everything else)
|
|
299
299
|
const phase = type === 'feature' ? (mode ? 'implementation' : 'discovery') : null;
|
|
300
300
|
|
|
301
|
-
|
|
302
|
-
|
|
301
|
+
// If parent has been rejected, tag this child with the current rejection round
|
|
302
|
+
let rejectionRound = null;
|
|
303
|
+
|
|
304
|
+
function doInsert() {
|
|
305
|
+
const sql = `INSERT INTO work_items (type, title, description, parent_id, epic_id, mode, needs_discovery, phase, status, plan_at_creation, rejection_round) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
|
306
|
+
db.run(sql, [type, title, description, parentId, epicId, mode, needsDiscovery ? 1 : 0, phase, 'backlog', planAtCreation, rejectionRound], function(err) {
|
|
303
307
|
if (err) {
|
|
304
308
|
return reject(err);
|
|
305
309
|
}
|
|
@@ -327,6 +331,21 @@ function create(type, title, description = '', parentId = null, mode = null, nee
|
|
|
327
331
|
resolve(newId);
|
|
328
332
|
}
|
|
329
333
|
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (parentId) {
|
|
337
|
+
db.get('SELECT rejection_count FROM work_items WHERE id = ?', [parentId], (err, parentRow) => {
|
|
338
|
+
if (err) {
|
|
339
|
+
return reject(err);
|
|
340
|
+
}
|
|
341
|
+
if (parentRow && parentRow.rejection_count > 0) {
|
|
342
|
+
rejectionRound = parentRow.rejection_count;
|
|
343
|
+
}
|
|
344
|
+
doInsert();
|
|
345
|
+
});
|
|
346
|
+
} else {
|
|
347
|
+
doInsert();
|
|
348
|
+
}
|
|
330
349
|
}
|
|
331
350
|
});
|
|
332
351
|
}
|
|
@@ -398,13 +417,32 @@ function filterProductionItems(items, itemsById) {
|
|
|
398
417
|
* Get all work items as hierarchical tree structure
|
|
399
418
|
* @param {boolean} includeCompleted - Include done/cancelled items (default: false)
|
|
400
419
|
* @param {boolean} showAll - Show all items including production (bypasses internal/external filtering)
|
|
420
|
+
* @param {number|null} sessionId - If provided, hide items owned by OTHER active sessions
|
|
401
421
|
* @returns {Promise<Array>} Root work items with nested children
|
|
402
422
|
* @throws {Error} If database query fails
|
|
403
423
|
*/
|
|
404
|
-
function getTree(includeCompleted = false, showAll = false) {
|
|
424
|
+
function getTree(includeCompleted = false, showAll = false, sessionId = null) {
|
|
405
425
|
return new Promise((resolve, reject) => {
|
|
406
|
-
const
|
|
407
|
-
|
|
426
|
+
const conditions = [];
|
|
427
|
+
const params = [];
|
|
428
|
+
|
|
429
|
+
if (!includeCompleted) {
|
|
430
|
+
conditions.push("(status NOT IN ('done', 'cancelled') OR status IS NULL)");
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Session isolation: hide items actively owned by OTHER sessions
|
|
434
|
+
if (sessionId) {
|
|
435
|
+
conditions.push(`work_items.id NOT IN (
|
|
436
|
+
SELECT cs.work_item_id FROM claude_sessions cs
|
|
437
|
+
WHERE cs.work_item_id IS NOT NULL
|
|
438
|
+
AND cs.status = 'active'
|
|
439
|
+
AND cs.id != ?
|
|
440
|
+
)`);
|
|
441
|
+
params.push(sessionId);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const whereClause = conditions.length > 0 ? 'WHERE ' + conditions.join(' AND ') : '';
|
|
445
|
+
db.all(`SELECT * FROM work_items ${whereClause} ORDER BY parent_id, id`, params, (err, rows) => {
|
|
408
446
|
if (err) {
|
|
409
447
|
return reject(new Error(`Failed to fetch work items: ${err.message}`));
|
|
410
448
|
}
|
|
@@ -901,6 +939,14 @@ function setScenario(id, scenarioFile) {
|
|
|
901
939
|
});
|
|
902
940
|
}
|
|
903
941
|
|
|
942
|
+
// Set QA steps
|
|
943
|
+
function setQaSteps(id, qaStepsJson) {
|
|
944
|
+
db.run(`UPDATE work_items SET qa_steps = ? WHERE id = ?`, [qaStepsJson, id], () => {
|
|
945
|
+
const steps = JSON.parse(qaStepsJson);
|
|
946
|
+
console.log(`Set #${id} qa_steps (${steps.length} steps)`);
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
|
|
904
950
|
// Set mode
|
|
905
951
|
function setMode(id, mode) {
|
|
906
952
|
return new Promise((resolve, reject) => {
|
|
@@ -1532,7 +1578,23 @@ async function main() {
|
|
|
1532
1578
|
expandedIds = new Set(ids);
|
|
1533
1579
|
}
|
|
1534
1580
|
|
|
1535
|
-
//
|
|
1581
|
+
// Session isolation for backlog display
|
|
1582
|
+
const sessionId = process.env.JETTYPOD_SESSION_ID ? parseInt(process.env.JETTYPOD_SESSION_ID, 10) : null;
|
|
1583
|
+
|
|
1584
|
+
// Query for ALL in_progress items (with session filtering)
|
|
1585
|
+
const activeParams = [];
|
|
1586
|
+
let activeSessionFilter = '';
|
|
1587
|
+
if (sessionId) {
|
|
1588
|
+
activeSessionFilter = `
|
|
1589
|
+
AND w.id NOT IN (
|
|
1590
|
+
SELECT cs.work_item_id FROM claude_sessions cs
|
|
1591
|
+
WHERE cs.work_item_id IS NOT NULL
|
|
1592
|
+
AND cs.status = 'active'
|
|
1593
|
+
AND cs.id != ?
|
|
1594
|
+
)`;
|
|
1595
|
+
activeParams.push(sessionId);
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1536
1598
|
const activeItems = await new Promise((resolve, reject) => {
|
|
1537
1599
|
db.all(`
|
|
1538
1600
|
SELECT w.id, w.title, w.type, w.status,
|
|
@@ -1542,8 +1604,9 @@ async function main() {
|
|
|
1542
1604
|
LEFT JOIN work_items p ON w.parent_id = p.id
|
|
1543
1605
|
LEFT JOIN work_items e ON w.epic_id = e.id
|
|
1544
1606
|
WHERE w.status = 'in_progress'
|
|
1607
|
+
${activeSessionFilter}
|
|
1545
1608
|
ORDER BY w.ready_for_review DESC, w.id ASC
|
|
1546
|
-
`,
|
|
1609
|
+
`, activeParams, (err, rows) => {
|
|
1547
1610
|
if (err) return reject(err);
|
|
1548
1611
|
resolve(rows || []);
|
|
1549
1612
|
});
|
|
@@ -1673,7 +1736,7 @@ async function main() {
|
|
|
1673
1736
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
1674
1737
|
console.log('📋 BACKLOG');
|
|
1675
1738
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
1676
|
-
const items = await getTree(false, showAll);
|
|
1739
|
+
const items = await getTree(false, showAll, sessionId);
|
|
1677
1740
|
|
|
1678
1741
|
printTree(items, '', true, expandedIds);
|
|
1679
1742
|
|
|
@@ -1749,6 +1812,40 @@ async function main() {
|
|
|
1749
1812
|
break;
|
|
1750
1813
|
}
|
|
1751
1814
|
|
|
1815
|
+
case 'set-qa-steps': {
|
|
1816
|
+
const id = parseInt(args[0]);
|
|
1817
|
+
if (isNaN(id)) {
|
|
1818
|
+
console.error('Error: Work item ID is required');
|
|
1819
|
+
console.log('Usage: jettypod work set-qa-steps <id> --from=<path>');
|
|
1820
|
+
process.exit(1);
|
|
1821
|
+
}
|
|
1822
|
+
const fromArg = args.find(a => a.startsWith('--from='));
|
|
1823
|
+
if (!fromArg) {
|
|
1824
|
+
console.error('Error: --from=<path> is required');
|
|
1825
|
+
console.log('Usage: jettypod work set-qa-steps <id> --from=<path>');
|
|
1826
|
+
process.exit(1);
|
|
1827
|
+
}
|
|
1828
|
+
const filePath = fromArg.replace('--from=', '');
|
|
1829
|
+
const fs = require('fs');
|
|
1830
|
+
if (!fs.existsSync(filePath)) {
|
|
1831
|
+
console.error(`Error: File not found: ${filePath}`);
|
|
1832
|
+
process.exit(1);
|
|
1833
|
+
}
|
|
1834
|
+
const qaJson = fs.readFileSync(filePath, 'utf8');
|
|
1835
|
+
try {
|
|
1836
|
+
const parsed = JSON.parse(qaJson);
|
|
1837
|
+
if (!Array.isArray(parsed)) {
|
|
1838
|
+
console.error('Error: QA steps must be a JSON array');
|
|
1839
|
+
process.exit(1);
|
|
1840
|
+
}
|
|
1841
|
+
} catch (e) {
|
|
1842
|
+
console.error(`Error: Invalid JSON in ${filePath}: ${e.message}`);
|
|
1843
|
+
process.exit(1);
|
|
1844
|
+
}
|
|
1845
|
+
setQaSteps(id, qaJson);
|
|
1846
|
+
break;
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1752
1849
|
case 'current': {
|
|
1753
1850
|
if (!args[0]) {
|
|
1754
1851
|
// No ID provided - show current work
|
|
@@ -2594,6 +2691,9 @@ Commands:
|
|
|
2594
2691
|
jettypod work set-scenario <id> <file>
|
|
2595
2692
|
Set scenario_file for a feature (e.g., features/my-feature.feature)
|
|
2596
2693
|
|
|
2694
|
+
jettypod work set-qa-steps <id> --from=<path>
|
|
2695
|
+
Set QA steps from a JSON file (array of {section, text, detail})
|
|
2696
|
+
|
|
2597
2697
|
jettypod work current <id>
|
|
2598
2698
|
Set as current work item
|
|
2599
2699
|
|
package/package.json
CHANGED
|
@@ -303,12 +303,36 @@ Related Tests Passing
|
|
|
303
303
|
Ready to commit and merge.
|
|
304
304
|
```
|
|
305
305
|
|
|
306
|
-
**Proceed to Step
|
|
306
|
+
**Proceed to Step 3B.**
|
|
307
307
|
|
|
308
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
309
|
|
|
310
310
|
---
|
|
311
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
|
+
|
|
332
|
+
**Proceed to Step 4.**
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
312
336
|
### Step 4: Commit and Merge
|
|
313
337
|
|
|
314
338
|
**Emit gate signal:**
|
|
@@ -339,6 +363,8 @@ Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>"
|
|
|
339
363
|
jettypod work merge <bug-id>
|
|
340
364
|
```
|
|
341
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
|
+
|
|
342
368
|
```bash
|
|
343
369
|
# Step 2: cd to main repo
|
|
344
370
|
cd <main-repo-path>
|
|
@@ -359,6 +385,22 @@ jettypod work cleanup <bug-id>
|
|
|
359
385
|
**The merge command automatically sets the bug as ready for review.**
|
|
360
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.
|
|
361
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
|
+
|
|
362
404
|
**Emit gate signal:**
|
|
363
405
|
|
|
364
406
|
```bash
|
|
@@ -479,7 +479,29 @@ Returning to iteration loop...
|
|
|
479
479
|
|
|
480
480
|
Go back to Step 5 to fix issues.
|
|
481
481
|
|
|
482
|
-
**If all verification passes → Move to Step
|
|
482
|
+
**If all verification passes → Move to Step 6B.**
|
|
483
|
+
|
|
484
|
+
### Step 6B: Prove It Works
|
|
485
|
+
|
|
486
|
+
**Before committing, demonstrate the change works in practice — not just that tests pass.**
|
|
487
|
+
|
|
488
|
+
1. **Run or build the actual code** — compile, start the server, execute the command, or trigger the relevant workflow
|
|
489
|
+
2. **Show real output** — display console output, build result, or observable behavior
|
|
490
|
+
3. **Confirm it matches expectations** — verify the output is what was requested
|
|
491
|
+
|
|
492
|
+
**Display:**
|
|
493
|
+
|
|
494
|
+
```
|
|
495
|
+
Verifying in practice...
|
|
496
|
+
|
|
497
|
+
[Actual output from running/building the code]
|
|
498
|
+
|
|
499
|
+
Verified: [what was confirmed]
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
**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.
|
|
503
|
+
|
|
504
|
+
**Move to Step 7.**
|
|
483
505
|
|
|
484
506
|
### Step 7: Complete and Merge
|
|
485
507
|
|
|
@@ -505,6 +527,8 @@ git push
|
|
|
505
527
|
jettypod work merge [chore-id]
|
|
506
528
|
```
|
|
507
529
|
|
|
530
|
+
**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.
|
|
531
|
+
|
|
508
532
|
```bash
|
|
509
533
|
# Step 2: cd to main repo
|
|
510
534
|
cd /path/to/main/repo
|
|
@@ -516,6 +540,21 @@ pwd && ls .jettypod # verify
|
|
|
516
540
|
jettypod work cleanup [chore-id]
|
|
517
541
|
```
|
|
518
542
|
|
|
543
|
+
**Generate QA checklist:**
|
|
544
|
+
|
|
545
|
+
Based on the chore work completed, generate specific QA steps. Write a JSON array to `/tmp/qa-steps.json`:
|
|
546
|
+
```json
|
|
547
|
+
[
|
|
548
|
+
{"section": "Section Name", "text": "Action to perform and expected result", "detail": "Additional context"},
|
|
549
|
+
...
|
|
550
|
+
]
|
|
551
|
+
```
|
|
552
|
+
Be specific. 3-8 steps covering what was changed and how to verify it works.
|
|
553
|
+
|
|
554
|
+
```bash
|
|
555
|
+
jettypod work set-qa-steps [chore-id] --from=/tmp/qa-steps.json
|
|
556
|
+
```
|
|
557
|
+
|
|
519
558
|
**Display:**
|
|
520
559
|
|
|
521
560
|
```
|