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
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: Remove AUTOINCREMENT from all tables
|
|
3
|
+
*
|
|
4
|
+
* AUTOINCREMENT maintains a persistent counter in sqlite_sequence that never
|
|
5
|
+
* decreases. If any row is ever inserted with a high explicit ID (test data,
|
|
6
|
+
* corrupted import, etc.), all future IDs inflate permanently.
|
|
7
|
+
*
|
|
8
|
+
* Plain INTEGER PRIMARY KEY still auto-assigns IDs (max(id)+1) but without
|
|
9
|
+
* the ratcheting counter. This is the SQLite-recommended approach.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
id: '029-remove-autoincrement',
|
|
14
|
+
description: 'Remove AUTOINCREMENT from all tables to prevent ID counter inflation',
|
|
15
|
+
|
|
16
|
+
async up(db) {
|
|
17
|
+
// Each table that uses AUTOINCREMENT needs to be recreated.
|
|
18
|
+
// SQLite doesn't support ALTER TABLE to change column definitions.
|
|
19
|
+
//
|
|
20
|
+
// Strategy: create _new table without AUTOINCREMENT, copy data, drop old, rename.
|
|
21
|
+
// All done inside serialize() for safety.
|
|
22
|
+
|
|
23
|
+
const tables = [
|
|
24
|
+
{
|
|
25
|
+
name: 'work_items',
|
|
26
|
+
create: `CREATE TABLE work_items_new (
|
|
27
|
+
id INTEGER PRIMARY KEY,
|
|
28
|
+
type TEXT NOT NULL,
|
|
29
|
+
title TEXT NOT NULL,
|
|
30
|
+
description TEXT,
|
|
31
|
+
status TEXT DEFAULT 'backlog',
|
|
32
|
+
parent_id INTEGER,
|
|
33
|
+
epic_id INTEGER,
|
|
34
|
+
branch_name TEXT,
|
|
35
|
+
file_paths TEXT,
|
|
36
|
+
commit_sha TEXT,
|
|
37
|
+
mode TEXT,
|
|
38
|
+
current INTEGER DEFAULT 0,
|
|
39
|
+
phase TEXT,
|
|
40
|
+
prototype_files TEXT,
|
|
41
|
+
discovery_winner TEXT,
|
|
42
|
+
discovery_rationale TEXT,
|
|
43
|
+
scenario_file TEXT,
|
|
44
|
+
completed_at TEXT,
|
|
45
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
46
|
+
needs_discovery INTEGER DEFAULT 0,
|
|
47
|
+
worktree_path TEXT,
|
|
48
|
+
display_order INTEGER,
|
|
49
|
+
architectural_decision TEXT,
|
|
50
|
+
discovery_completed_at TEXT,
|
|
51
|
+
rejection_reason TEXT,
|
|
52
|
+
rejected_at TEXT,
|
|
53
|
+
plan_at_creation TEXT DEFAULT NULL,
|
|
54
|
+
ready_for_review INTEGER DEFAULT 0,
|
|
55
|
+
conversational INTEGER DEFAULT 0
|
|
56
|
+
)`,
|
|
57
|
+
indexes: []
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'claude_sessions',
|
|
61
|
+
create: `CREATE TABLE claude_sessions_new (
|
|
62
|
+
id INTEGER PRIMARY KEY,
|
|
63
|
+
work_item_id INTEGER UNIQUE,
|
|
64
|
+
title TEXT NOT NULL,
|
|
65
|
+
session_title TEXT,
|
|
66
|
+
status TEXT NOT NULL CHECK(status IN ('active', 'completed', 'error', 'orphaned')),
|
|
67
|
+
started_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
68
|
+
completed_at TEXT,
|
|
69
|
+
content TEXT,
|
|
70
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id) ON DELETE SET NULL
|
|
71
|
+
)`,
|
|
72
|
+
indexes: [
|
|
73
|
+
'CREATE INDEX idx_claude_sessions_status ON claude_sessions(status)',
|
|
74
|
+
'CREATE INDEX idx_claude_sessions_work_item ON claude_sessions(work_item_id)'
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'discovery_decisions',
|
|
79
|
+
create: `CREATE TABLE discovery_decisions_new (
|
|
80
|
+
id INTEGER PRIMARY KEY,
|
|
81
|
+
work_item_id INTEGER NOT NULL,
|
|
82
|
+
aspect TEXT NOT NULL,
|
|
83
|
+
decision TEXT NOT NULL,
|
|
84
|
+
rationale TEXT NOT NULL,
|
|
85
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
86
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
87
|
+
)`,
|
|
88
|
+
indexes: []
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'env_vars',
|
|
92
|
+
create: `CREATE TABLE env_vars_new (
|
|
93
|
+
id INTEGER PRIMARY KEY,
|
|
94
|
+
name TEXT NOT NULL UNIQUE,
|
|
95
|
+
value TEXT NOT NULL,
|
|
96
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
97
|
+
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
98
|
+
)`,
|
|
99
|
+
indexes: []
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'external_readiness_checklist',
|
|
103
|
+
create: `CREATE TABLE external_readiness_checklist_new (
|
|
104
|
+
id INTEGER PRIMARY KEY,
|
|
105
|
+
category TEXT NOT NULL,
|
|
106
|
+
item_key TEXT NOT NULL,
|
|
107
|
+
title TEXT NOT NULL,
|
|
108
|
+
description TEXT,
|
|
109
|
+
completed INTEGER DEFAULT 0,
|
|
110
|
+
completed_at DATETIME,
|
|
111
|
+
UNIQUE(category, item_key)
|
|
112
|
+
)`,
|
|
113
|
+
indexes: []
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'merge_locks',
|
|
117
|
+
create: `CREATE TABLE merge_locks_new (
|
|
118
|
+
id INTEGER PRIMARY KEY,
|
|
119
|
+
locked_by TEXT NOT NULL,
|
|
120
|
+
locked_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
121
|
+
operation TEXT NOT NULL DEFAULT 'merging',
|
|
122
|
+
work_item_id INTEGER NOT NULL,
|
|
123
|
+
heartbeat_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
124
|
+
)`,
|
|
125
|
+
indexes: [
|
|
126
|
+
'CREATE INDEX idx_merge_locks_locked_at ON merge_locks(locked_at)'
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'skill_executions',
|
|
131
|
+
create: `CREATE TABLE skill_executions_new (
|
|
132
|
+
id INTEGER PRIMARY KEY,
|
|
133
|
+
work_item_id INTEGER NOT NULL,
|
|
134
|
+
skill_name TEXT NOT NULL,
|
|
135
|
+
status TEXT DEFAULT 'in_progress',
|
|
136
|
+
step_reached INTEGER DEFAULT 1,
|
|
137
|
+
context_json TEXT,
|
|
138
|
+
started_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
139
|
+
completed_at DATETIME,
|
|
140
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
141
|
+
)`,
|
|
142
|
+
indexes: []
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'test_runs',
|
|
146
|
+
create: `CREATE TABLE test_runs_new (
|
|
147
|
+
id INTEGER PRIMARY KEY,
|
|
148
|
+
run_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
149
|
+
total_scenarios INTEGER NOT NULL,
|
|
150
|
+
passed INTEGER NOT NULL,
|
|
151
|
+
failed INTEGER NOT NULL,
|
|
152
|
+
pending INTEGER NOT NULL,
|
|
153
|
+
duration_ms INTEGER NOT NULL
|
|
154
|
+
)`,
|
|
155
|
+
indexes: []
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'test_scenarios',
|
|
159
|
+
create: `CREATE TABLE test_scenarios_new (
|
|
160
|
+
id INTEGER PRIMARY KEY,
|
|
161
|
+
feature_file TEXT NOT NULL,
|
|
162
|
+
scenario_name TEXT NOT NULL,
|
|
163
|
+
UNIQUE(feature_file, scenario_name)
|
|
164
|
+
)`,
|
|
165
|
+
indexes: []
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'test_results',
|
|
169
|
+
create: `CREATE TABLE test_results_new (
|
|
170
|
+
id INTEGER PRIMARY KEY,
|
|
171
|
+
test_run_id INTEGER NOT NULL,
|
|
172
|
+
scenario_id INTEGER NOT NULL,
|
|
173
|
+
status TEXT NOT NULL CHECK(status IN ('passed', 'failed', 'pending')),
|
|
174
|
+
duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
175
|
+
error_message TEXT,
|
|
176
|
+
failed_step TEXT,
|
|
177
|
+
run_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
178
|
+
FOREIGN KEY (test_run_id) REFERENCES test_runs(id),
|
|
179
|
+
FOREIGN KEY (scenario_id) REFERENCES test_scenarios(id)
|
|
180
|
+
)`,
|
|
181
|
+
indexes: [
|
|
182
|
+
'CREATE INDEX idx_test_results_run ON test_results(test_run_id)',
|
|
183
|
+
'CREATE INDEX idx_test_results_scenario ON test_results(scenario_id)'
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: 'workflow_checkpoints',
|
|
188
|
+
create: `CREATE TABLE workflow_checkpoints_new (
|
|
189
|
+
id INTEGER PRIMARY KEY,
|
|
190
|
+
skill_name TEXT NOT NULL,
|
|
191
|
+
current_step INTEGER NOT NULL,
|
|
192
|
+
total_steps INTEGER,
|
|
193
|
+
context_json TEXT,
|
|
194
|
+
branch_name TEXT NOT NULL,
|
|
195
|
+
work_item_id INTEGER,
|
|
196
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
197
|
+
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
198
|
+
)`,
|
|
199
|
+
indexes: [
|
|
200
|
+
'CREATE INDEX idx_workflow_checkpoints_branch ON workflow_checkpoints(branch_name)'
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: 'workflow_gates',
|
|
205
|
+
create: `CREATE TABLE workflow_gates_new (
|
|
206
|
+
id INTEGER PRIMARY KEY,
|
|
207
|
+
work_item_id INTEGER NOT NULL,
|
|
208
|
+
gate_name TEXT NOT NULL,
|
|
209
|
+
passed_at DATETIME,
|
|
210
|
+
UNIQUE(work_item_id, gate_name),
|
|
211
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
212
|
+
)`,
|
|
213
|
+
indexes: []
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: 'worktree_sessions',
|
|
217
|
+
create: `CREATE TABLE worktree_sessions_new (
|
|
218
|
+
id INTEGER PRIMARY KEY,
|
|
219
|
+
worktree_path TEXT UNIQUE NOT NULL,
|
|
220
|
+
work_item_id INTEGER NOT NULL,
|
|
221
|
+
branch_name TEXT NOT NULL,
|
|
222
|
+
last_activity DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
223
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
224
|
+
)`,
|
|
225
|
+
indexes: [
|
|
226
|
+
'CREATE INDEX idx_worktree_sessions_path ON worktree_sessions(worktree_path)',
|
|
227
|
+
'CREATE INDEX idx_worktree_sessions_work_item ON worktree_sessions(work_item_id)'
|
|
228
|
+
]
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: 'worktrees',
|
|
232
|
+
create: `CREATE TABLE worktrees_new (
|
|
233
|
+
id INTEGER PRIMARY KEY,
|
|
234
|
+
work_item_id INTEGER NOT NULL,
|
|
235
|
+
branch_name TEXT NOT NULL,
|
|
236
|
+
worktree_path TEXT NOT NULL,
|
|
237
|
+
status TEXT NOT NULL CHECK(status IN ('active', 'merging', 'merged', 'cleanup_pending', 'corrupted')),
|
|
238
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
239
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
240
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
241
|
+
)`,
|
|
242
|
+
indexes: [
|
|
243
|
+
'CREATE INDEX idx_worktrees_work_item_id ON worktrees(work_item_id)',
|
|
244
|
+
'CREATE INDEX idx_worktrees_status ON worktrees(status)'
|
|
245
|
+
]
|
|
246
|
+
}
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
return new Promise((resolve, reject) => {
|
|
250
|
+
db.run('PRAGMA foreign_keys = OFF', (err) => {
|
|
251
|
+
if (err) return reject(err);
|
|
252
|
+
|
|
253
|
+
db.serialize(() => {
|
|
254
|
+
for (const table of tables) {
|
|
255
|
+
// 1. Create new table without AUTOINCREMENT
|
|
256
|
+
db.run(table.create, (err) => {
|
|
257
|
+
if (err) return reject(new Error(`Failed to create ${table.name}_new: ${err.message}`));
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// 2. Copy all data
|
|
261
|
+
db.run(`INSERT INTO ${table.name}_new SELECT * FROM ${table.name}`, (err) => {
|
|
262
|
+
if (err) return reject(new Error(`Failed to copy data for ${table.name}: ${err.message}`));
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// 3. Drop old indexes
|
|
266
|
+
for (const idx of table.indexes) {
|
|
267
|
+
const idxName = idx.match(/CREATE INDEX (\S+)/)?.[1];
|
|
268
|
+
if (idxName) {
|
|
269
|
+
db.run(`DROP INDEX IF EXISTS ${idxName}`, (err) => {
|
|
270
|
+
if (err) return reject(new Error(`Failed to drop index ${idxName}: ${err.message}`));
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// 4. Drop old table
|
|
276
|
+
db.run(`DROP TABLE "${table.name}"`, (err) => {
|
|
277
|
+
if (err) return reject(new Error(`Failed to drop ${table.name}: ${err.message}`));
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// 5. Rename new table
|
|
281
|
+
db.run(`ALTER TABLE ${table.name}_new RENAME TO ${table.name}`, (err) => {
|
|
282
|
+
if (err) return reject(new Error(`Failed to rename ${table.name}_new: ${err.message}`));
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// 6. Recreate indexes
|
|
286
|
+
for (const idx of table.indexes) {
|
|
287
|
+
db.run(idx, (err) => {
|
|
288
|
+
if (err) return reject(new Error(`Failed to create index: ${err.message}`));
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// 7. Drop sqlite_sequence (no longer needed without AUTOINCREMENT)
|
|
294
|
+
db.run('DROP TABLE IF EXISTS sqlite_sequence', (err) => {
|
|
295
|
+
if (err) return reject(new Error(`Failed to drop sqlite_sequence: ${err.message}`));
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// 8. Re-enable foreign keys
|
|
299
|
+
db.run('PRAGMA foreign_keys = ON', (err) => {
|
|
300
|
+
if (err) return reject(err);
|
|
301
|
+
resolve();
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: Rename worktree status 'corrupted' to 'cleaned'
|
|
3
|
+
*
|
|
4
|
+
* The 'corrupted' status was misleading — it's the terminal state for worktrees
|
|
5
|
+
* that have been successfully cleaned up, not actually corrupted data.
|
|
6
|
+
* Renaming to 'cleaned' for clarity.
|
|
7
|
+
*
|
|
8
|
+
* SQLite doesn't support ALTER CONSTRAINT, so we recreate the table.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
id: '029-rename-corrupted-to-cleaned',
|
|
13
|
+
description: 'Rename worktree status corrupted to cleaned for clarity',
|
|
14
|
+
|
|
15
|
+
async up(db) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
db.serialize(() => {
|
|
18
|
+
// 1. Update existing records
|
|
19
|
+
db.run(`UPDATE worktrees SET status = 'cleaned' WHERE status = 'corrupted'`, (err) => {
|
|
20
|
+
if (err) return reject(err);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// 2. Create new table with updated constraint
|
|
24
|
+
db.run(`
|
|
25
|
+
CREATE TABLE IF NOT EXISTS worktrees_new (
|
|
26
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
27
|
+
work_item_id INTEGER NOT NULL,
|
|
28
|
+
branch_name TEXT NOT NULL,
|
|
29
|
+
worktree_path TEXT NOT NULL,
|
|
30
|
+
status TEXT NOT NULL CHECK(status IN ('active', 'merging', 'merged', 'cleanup_pending', 'cleaned')),
|
|
31
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
32
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
33
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
34
|
+
)
|
|
35
|
+
`, (err) => {
|
|
36
|
+
if (err) return reject(err);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// 3. Copy data
|
|
40
|
+
db.run(`
|
|
41
|
+
INSERT INTO worktrees_new (id, work_item_id, branch_name, worktree_path, status, created_at, updated_at)
|
|
42
|
+
SELECT id, work_item_id, branch_name, worktree_path, status, created_at, updated_at
|
|
43
|
+
FROM worktrees
|
|
44
|
+
`, (err) => {
|
|
45
|
+
if (err) return reject(err);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// 4. Drop old indexes
|
|
49
|
+
db.run('DROP INDEX IF EXISTS idx_worktrees_work_item_id', (err) => {
|
|
50
|
+
if (err) return reject(err);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
db.run('DROP INDEX IF EXISTS idx_worktrees_status', (err) => {
|
|
54
|
+
if (err) return reject(err);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// 5. Drop old table
|
|
58
|
+
db.run('DROP TABLE worktrees', (err) => {
|
|
59
|
+
if (err) return reject(err);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 6. Rename new table
|
|
63
|
+
db.run('ALTER TABLE worktrees_new RENAME TO worktrees', (err) => {
|
|
64
|
+
if (err) return reject(err);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// 7. Recreate indexes
|
|
68
|
+
db.run(`
|
|
69
|
+
CREATE INDEX idx_worktrees_work_item_id
|
|
70
|
+
ON worktrees(work_item_id)
|
|
71
|
+
`, (err) => {
|
|
72
|
+
if (err) return reject(err);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
db.run(`
|
|
76
|
+
CREATE INDEX idx_worktrees_status
|
|
77
|
+
ON worktrees(status)
|
|
78
|
+
`, (err) => {
|
|
79
|
+
if (err) return reject(err);
|
|
80
|
+
resolve();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
async down(db) {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
db.serialize(() => {
|
|
89
|
+
db.run(`UPDATE worktrees SET status = 'corrupted' WHERE status = 'cleaned'`, (err) => {
|
|
90
|
+
if (err) return reject(err);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
db.run(`
|
|
94
|
+
CREATE TABLE IF NOT EXISTS worktrees_new (
|
|
95
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
96
|
+
work_item_id INTEGER NOT NULL,
|
|
97
|
+
branch_name TEXT NOT NULL,
|
|
98
|
+
worktree_path TEXT NOT NULL,
|
|
99
|
+
status TEXT NOT NULL CHECK(status IN ('active', 'merging', 'merged', 'cleanup_pending', 'corrupted')),
|
|
100
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
101
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
102
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
103
|
+
)
|
|
104
|
+
`, (err) => {
|
|
105
|
+
if (err) return reject(err);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
db.run(`
|
|
109
|
+
INSERT INTO worktrees_new (id, work_item_id, branch_name, worktree_path, status, created_at, updated_at)
|
|
110
|
+
SELECT id, work_item_id, branch_name, worktree_path, status, created_at, updated_at
|
|
111
|
+
FROM worktrees
|
|
112
|
+
`, (err) => {
|
|
113
|
+
if (err) return reject(err);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
db.run('DROP INDEX IF EXISTS idx_worktrees_work_item_id', (err) => {
|
|
117
|
+
if (err) return reject(err);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
db.run('DROP INDEX IF EXISTS idx_worktrees_status', (err) => {
|
|
121
|
+
if (err) return reject(err);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
db.run('DROP TABLE worktrees', (err) => {
|
|
125
|
+
if (err) return reject(err);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
db.run('ALTER TABLE worktrees_new RENAME TO worktrees', (err) => {
|
|
129
|
+
if (err) return reject(err);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
db.run(`
|
|
133
|
+
CREATE INDEX idx_worktrees_work_item_id
|
|
134
|
+
ON worktrees(work_item_id)
|
|
135
|
+
`, (err) => {
|
|
136
|
+
if (err) return reject(err);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
db.run(`
|
|
140
|
+
CREATE INDEX idx_worktrees_status
|
|
141
|
+
ON worktrees(status)
|
|
142
|
+
`, (err) => {
|
|
143
|
+
if (err) return reject(err);
|
|
144
|
+
resolve();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: Add rejection round tracking columns to work_items
|
|
3
|
+
*
|
|
4
|
+
* Purpose: Support rejection round nesting by tracking how many times
|
|
5
|
+
* a parent item has been rejected and which round spawned each child.
|
|
6
|
+
*
|
|
7
|
+
* Columns:
|
|
8
|
+
* - rejection_count INTEGER DEFAULT 0: Number of times this item has been rejected
|
|
9
|
+
* - rejection_round INTEGER: Which rejection round spawned this child item (null = original)
|
|
10
|
+
* - rejection_history TEXT: JSON array of [{round, reason, at}] for all rejections
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
id: '030-rejection-round-columns',
|
|
15
|
+
description: 'Add rejection_count, rejection_round, and rejection_history columns to work_items',
|
|
16
|
+
|
|
17
|
+
async up(db) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
db.run(`ALTER TABLE work_items ADD COLUMN rejection_count INTEGER DEFAULT 0`, (err) => {
|
|
20
|
+
if (err) return reject(err);
|
|
21
|
+
db.run(`ALTER TABLE work_items ADD COLUMN rejection_round INTEGER`, (err) => {
|
|
22
|
+
if (err) return reject(err);
|
|
23
|
+
db.run(`ALTER TABLE work_items ADD COLUMN rejection_history TEXT`, (err) => {
|
|
24
|
+
if (err) return reject(err);
|
|
25
|
+
resolve();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
async down(db) {
|
|
33
|
+
// SQLite 3.35+ supports DROP COLUMN, but for safety we use the backup approach
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
db.all(`PRAGMA table_info(work_items)`, (err, columns) => {
|
|
36
|
+
if (err) return reject(err);
|
|
37
|
+
const keepColumns = columns
|
|
38
|
+
.map(c => c.name)
|
|
39
|
+
.filter(n => n !== 'rejection_count' && n !== 'rejection_round' && n !== 'rejection_history');
|
|
40
|
+
const columnList = keepColumns.join(', ');
|
|
41
|
+
db.run(`CREATE TABLE work_items_backup AS SELECT ${columnList} FROM work_items`, (err) => {
|
|
42
|
+
if (err) return reject(err);
|
|
43
|
+
db.run(`DROP TABLE work_items`, (err) => {
|
|
44
|
+
if (err) return reject(err);
|
|
45
|
+
db.run(`ALTER TABLE work_items_backup RENAME TO work_items`, (err) => {
|
|
46
|
+
if (err) return reject(err);
|
|
47
|
+
resolve();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
id: '031-session-isolation-index',
|
|
3
|
+
description: 'Add index on claude_sessions for efficient session isolation filtering',
|
|
4
|
+
|
|
5
|
+
async up(db) {
|
|
6
|
+
await new Promise((resolve, reject) => {
|
|
7
|
+
db.run(`
|
|
8
|
+
CREATE INDEX IF NOT EXISTS idx_claude_sessions_active_work
|
|
9
|
+
ON claude_sessions(status, work_item_id)
|
|
10
|
+
WHERE work_item_id IS NOT NULL AND status = 'active'
|
|
11
|
+
`, (err) => {
|
|
12
|
+
if (err) reject(err);
|
|
13
|
+
else resolve();
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
};
|
package/lib/migrations/index.js
CHANGED
|
@@ -20,11 +20,16 @@ async function runMigrations(db) {
|
|
|
20
20
|
});
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
db.
|
|
23
|
+
// Create _meta table for schema versioning
|
|
24
|
+
await new Promise((resolve, reject) => {
|
|
25
|
+
db.run(`
|
|
26
|
+
CREATE TABLE IF NOT EXISTS _meta (
|
|
27
|
+
key TEXT PRIMARY KEY,
|
|
28
|
+
value TEXT
|
|
29
|
+
)
|
|
30
|
+
`, (err) => {
|
|
26
31
|
if (err) reject(err);
|
|
27
|
-
else resolve(
|
|
32
|
+
else resolve();
|
|
28
33
|
});
|
|
29
34
|
});
|
|
30
35
|
|
|
@@ -34,6 +39,32 @@ async function runMigrations(db) {
|
|
|
34
39
|
.filter(f => f.endsWith('.js') && f !== 'index.js' && !f.endsWith('.test.js'))
|
|
35
40
|
.sort();
|
|
36
41
|
|
|
42
|
+
const knownMigrationCount = files.length;
|
|
43
|
+
|
|
44
|
+
// Check schema version for forward-compatibility
|
|
45
|
+
const currentVersion = await new Promise((resolve, reject) => {
|
|
46
|
+
db.get('SELECT value FROM _meta WHERE key = ?', ['schema_version'], (err, row) => {
|
|
47
|
+
if (err) reject(err);
|
|
48
|
+
else resolve(row ? parseInt(row.value, 10) : 0);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (currentVersion > knownMigrationCount) {
|
|
53
|
+
console.warn(
|
|
54
|
+
`⚠️ Database schema version (${currentVersion}) is newer than this version of JettyPod supports (${knownMigrationCount}).` +
|
|
55
|
+
'\n Please update JettyPod to avoid compatibility issues.'
|
|
56
|
+
);
|
|
57
|
+
return; // Don't run migrations — the DB is from a newer version
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Get list of applied migrations
|
|
61
|
+
const applied = await new Promise((resolve, reject) => {
|
|
62
|
+
db.all('SELECT id FROM migrations', [], (err, rows) => {
|
|
63
|
+
if (err) reject(err);
|
|
64
|
+
else resolve(rows.map(r => r.id));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
37
68
|
// Run pending migrations
|
|
38
69
|
for (const file of files) {
|
|
39
70
|
const migration = require(path.join(migrationsDir, file));
|
|
@@ -69,6 +100,18 @@ async function runMigrations(db) {
|
|
|
69
100
|
throw err;
|
|
70
101
|
}
|
|
71
102
|
}
|
|
103
|
+
|
|
104
|
+
// Update schema version to reflect current state
|
|
105
|
+
await new Promise((resolve, reject) => {
|
|
106
|
+
db.run(
|
|
107
|
+
'INSERT OR REPLACE INTO _meta (key, value) VALUES (?, ?)',
|
|
108
|
+
['schema_version', String(knownMigrationCount)],
|
|
109
|
+
(err) => {
|
|
110
|
+
if (err) reject(err);
|
|
111
|
+
else resolve();
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
});
|
|
72
115
|
}
|
|
73
116
|
|
|
74
117
|
module.exports = { runMigrations };
|
package/lib/schema.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
const SCHEMA_SQL = `
|
|
13
13
|
CREATE TABLE IF NOT EXISTS work_items (
|
|
14
|
-
id INTEGER PRIMARY KEY
|
|
14
|
+
id INTEGER PRIMARY KEY,
|
|
15
15
|
type TEXT NOT NULL,
|
|
16
16
|
title TEXT NOT NULL,
|
|
17
17
|
description TEXT,
|
|
@@ -42,7 +42,7 @@ const SCHEMA_SQL = `
|
|
|
42
42
|
);
|
|
43
43
|
|
|
44
44
|
CREATE TABLE IF NOT EXISTS external_readiness_checklist (
|
|
45
|
-
id INTEGER PRIMARY KEY
|
|
45
|
+
id INTEGER PRIMARY KEY,
|
|
46
46
|
category TEXT NOT NULL,
|
|
47
47
|
item_key TEXT NOT NULL,
|
|
48
48
|
title TEXT NOT NULL,
|
|
@@ -53,7 +53,7 @@ const SCHEMA_SQL = `
|
|
|
53
53
|
);
|
|
54
54
|
|
|
55
55
|
CREATE TABLE IF NOT EXISTS discovery_decisions (
|
|
56
|
-
id INTEGER PRIMARY KEY
|
|
56
|
+
id INTEGER PRIMARY KEY,
|
|
57
57
|
work_item_id INTEGER NOT NULL,
|
|
58
58
|
aspect TEXT NOT NULL,
|
|
59
59
|
decision TEXT NOT NULL,
|
|
@@ -63,7 +63,7 @@ const SCHEMA_SQL = `
|
|
|
63
63
|
);
|
|
64
64
|
|
|
65
65
|
CREATE TABLE IF NOT EXISTS skill_executions (
|
|
66
|
-
id INTEGER PRIMARY KEY
|
|
66
|
+
id INTEGER PRIMARY KEY,
|
|
67
67
|
work_item_id INTEGER NOT NULL,
|
|
68
68
|
skill_name TEXT NOT NULL,
|
|
69
69
|
status TEXT DEFAULT 'in_progress',
|
|
@@ -75,13 +75,18 @@ const SCHEMA_SQL = `
|
|
|
75
75
|
);
|
|
76
76
|
|
|
77
77
|
CREATE TABLE IF NOT EXISTS workflow_gates (
|
|
78
|
-
id INTEGER PRIMARY KEY
|
|
78
|
+
id INTEGER PRIMARY KEY,
|
|
79
79
|
work_item_id INTEGER NOT NULL,
|
|
80
80
|
gate_name TEXT NOT NULL,
|
|
81
81
|
passed_at DATETIME,
|
|
82
82
|
UNIQUE(work_item_id, gate_name),
|
|
83
83
|
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
84
84
|
);
|
|
85
|
+
|
|
86
|
+
CREATE TABLE IF NOT EXISTS _meta (
|
|
87
|
+
key TEXT PRIMARY KEY,
|
|
88
|
+
value TEXT
|
|
89
|
+
);
|
|
85
90
|
`;
|
|
86
91
|
|
|
87
92
|
/**
|
package/lib/seed-onboarding.js
CHANGED