ultra-builder-pro-cli 0.25.1 → 0.26.0
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/.ultra-template/contexts/TEMPLATE.md +87 -0
- package/.ultra-template/decisions/.gitkeep +1 -0
- package/.ultra-template/evidence/.gitkeep +1 -0
- package/.ultra-template/north-star.md +46 -0
- package/.ultra-template/research/.gitkeep +1 -0
- package/.ultra-template/specs/discovery.md +2 -2
- package/.ultra-template/tasks.json +3 -0
- package/.ultra-template/test-report.json +27 -0
- package/CHANGELOG.md +40 -0
- package/README.md +217 -579
- package/adapters/_shared/delegate-profiles/kimi-read-only.md +15 -0
- package/adapters/_shared/delegate-profiles/kimi-write.md +17 -0
- package/adapters/_shared/file-ops.cjs +76 -2
- package/adapters/_shared/host-profile.cjs +97 -0
- package/adapters/_shared/plugin-core.cjs +204 -0
- package/adapters/_shared/runtime-assets.cjs +31 -96
- package/adapters/claude.js +92 -291
- package/adapters/codex.js +309 -842
- package/adapters/grok.js +143 -1469
- package/adapters/kimi.js +124 -1053
- package/adapters/opencode.js +230 -742
- package/bin/delegate-worker.cjs +323 -0
- package/bin/delegate.cjs +456 -0
- package/bin/install.js +38 -11
- package/docs/ARCHITECTURE.md +122 -216
- package/docs/ARTIFACT-AUTHORITY.md +90 -268
- package/docs/DECISIONS.md +75 -87
- package/docs/PHILOSOPHY.md +177 -0
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +62 -39
- package/docs/RUNTIME-COMPAT-MATRIX.md +99 -156
- package/docs/SKILL-AUTHORING.md +94 -73
- package/docs/WORKFLOW-LIFECYCLE.md +93 -189
- package/hooks/README.md +17 -37
- package/hooks/_common.py +113 -0
- package/hooks/adapters/codex.py +9 -16
- package/hooks/adapters/grok.py +5 -5
- package/hooks/adapters/kimi.py +8 -14
- package/hooks/block_dangerous_commands.py +95 -0
- package/hooks/compact_context.py +65 -0
- package/hooks/mid_workflow_recall.py +25 -0
- package/hooks/post_edit_guard.py +105 -0
- package/hooks/session_context.py +30 -0
- package/package.json +17 -52
- package/skills/ultra-change/SKILL.md +97 -50
- package/skills/ultra-change/references/change-contract.md +40 -1
- package/skills/ultra-delegate/SKILL.md +82 -0
- package/skills/ultra-delegate/references/delegation-contract.md +51 -0
- package/skills/ultra-delegate/scripts/delegate_wait.py +86 -0
- package/skills/ultra-deliver/SKILL.md +61 -42
- package/skills/ultra-deliver/references/baseline-reconciliation.md +39 -20
- package/skills/ultra-dev/SKILL.md +96 -62
- package/skills/ultra-dev/references/debugging.md +22 -0
- package/skills/ultra-domain-modeling/SKILL.md +79 -0
- package/skills/ultra-grilling/SKILL.md +79 -0
- package/skills/ultra-init/SKILL.md +94 -37
- package/skills/ultra-init/references/brownfield-adoption.md +33 -83
- package/skills/ultra-init/scripts/init_project.cjs +84 -0
- package/skills/ultra-plan/SKILL.md +88 -68
- package/skills/ultra-research/SKILL.md +62 -48
- package/skills/ultra-research/references/00-problem-validation.md +5 -5
- package/skills/ultra-research/references/01-opportunity-discovery.md +3 -3
- package/skills/ultra-research/references/02-market-assessment.md +5 -4
- package/skills/ultra-research/references/03-competitive-landscape.md +3 -3
- package/skills/ultra-research/references/04-product-strategy.md +3 -3
- package/skills/ultra-research/references/05-assumptions-validation.md +5 -5
- package/skills/ultra-research/references/10-user-personas.md +3 -2
- package/skills/ultra-research/references/11-user-scenarios.md +3 -3
- package/skills/ultra-research/references/20-user-stories.md +4 -3
- package/skills/ultra-research/references/21-features-scope.md +3 -2
- package/skills/ultra-research/references/22-success-metrics.md +3 -3
- package/skills/ultra-research/references/30-architecture-context.md +3 -3
- package/skills/ultra-research/references/31-solution-strategy.md +3 -3
- package/skills/ultra-research/references/32-building-blocks.md +3 -3
- package/skills/ultra-research/references/40-deployment.md +3 -3
- package/skills/ultra-research/references/41-quality-risks.md +3 -3
- package/skills/ultra-research/references/99-synthesis.md +7 -7
- package/skills/ultra-review/SKILL.md +59 -32
- package/skills/ultra-review/references/code.md +17 -0
- package/skills/ultra-review/references/comments.md +12 -0
- package/skills/ultra-review/references/design.md +12 -0
- package/skills/ultra-review/references/errors.md +13 -0
- package/skills/ultra-review/references/spec.md +13 -0
- package/skills/ultra-review/references/tests.md +12 -0
- package/skills/ultra-review/references/worker-packet.md +40 -0
- package/skills/ultra-review/scripts/review_wait.py +26 -15
- package/skills/ultra-status/SKILL.md +61 -36
- package/skills/ultra-tdd/SKILL.md +81 -0
- package/skills/ultra-tdd/references/templates/README.md +20 -0
- package/skills/ultra-tdd/references/templates/feature-flag-default-audit.sh +79 -0
- package/skills/ultra-tdd/references/templates/persistence-real.ts +78 -0
- package/skills/ultra-tdd/references/templates/testcontainer-postgres.py +78 -0
- package/skills/ultra-tdd/references/templates/testcontainer-postgres.ts +73 -0
- package/skills/ultra-tdd/references/templates/vertical-slice.ts +66 -0
- package/skills/ultra-tdd/references/test-execution.md +18 -0
- package/skills/ultra-test/SKILL.md +70 -30
- package/skills/ultra-test/references/export-syntax.md +18 -0
- package/skills/ultra-test/scripts/worktree_digest.cjs +54 -0
- package/skills/ultra-think/SKILL.md +49 -27
- package/skills/ultra-think/references/autonomy-boundary.md +48 -0
- package/skills/ultra-think/references/interaction-boundary.md +18 -33
- package/.ultra-template/docs/research/README.md +0 -55
- package/.ultra-template/reports/templates/delivery-report.json +0 -17
- package/.ultra-template/reports/templates/test-report.json +0 -18
- package/.ultra-template/tasks/tasks.json +0 -15
- package/.ultra-template/templates/task-context.md +0 -19
- package/adapters/_shared/codex-assets.cjs +0 -725
- package/adapters/_shared/codex-ultra-tools-entry.cjs +0 -11
- package/adapters/_shared/interaction-contract.cjs +0 -129
- package/adapters/_shared/path-rewrite.cjs +0 -79
- package/adapters/_shared/runtime-native.cjs +0 -132
- package/adapters/_shared/settings-merge.cjs +0 -76
- package/agents/code-reviewer.md +0 -42
- package/agents/debugger.md +0 -40
- package/agents/review-code.md +0 -49
- package/agents/review-comments.md +0 -38
- package/agents/review-coordinator.md +0 -56
- package/agents/review-design.md +0 -40
- package/agents/review-errors.md +0 -43
- package/agents/review-spec.md +0 -39
- package/agents/review-tests.md +0 -40
- package/agents/tdd-runner.md +0 -38
- package/bin/orchestrator.js +0 -383
- package/commands/ultra-change.md +0 -10
- package/commands/ultra-deliver.md +0 -10
- package/commands/ultra-dev.md +0 -10
- package/commands/ultra-doctor.md +0 -10
- package/commands/ultra-init.md +0 -10
- package/commands/ultra-plan.md +0 -10
- package/commands/ultra-research.md +0 -10
- package/commands/ultra-review.md +0 -10
- package/commands/ultra-status.md +0 -10
- package/commands/ultra-test.md +0 -10
- package/commands/ultra-think.md +0 -10
- package/docs/AGENT-CONTEXT.md +0 -191
- package/docs/COMMIT-HASH-BACKFILL.md +0 -51
- package/docs/LEGACY-CLI-CROSSWALK.md +0 -175
- package/docs/ROADMAP.md +0 -101
- package/docs/STATE-DB-ACCESS-POLICY.md +0 -161
- package/hooks/active_task_context.py +0 -152
- package/hooks/context_envelope.py +0 -100
- package/hooks/health_check.py +0 -196
- package/hooks/pre_stop_check.py +0 -66
- package/hooks/runtime_paths.py +0 -396
- package/hooks/subagent_tracker.py +0 -88
- package/hooks/workflow_checkpoint.py +0 -76
- package/hooks/workflow_context.py +0 -48
- package/hooks/workflow_resume.py +0 -84
- package/mcp-server/hook-context.cjs +0 -38
- package/mcp-server/hook-event.cjs +0 -34
- package/mcp-server/lib/archive-journal.cjs +0 -1119
- package/mcp-server/lib/archive-mutation-worker.py +0 -365
- package/mcp-server/lib/artifact-registry.cjs +0 -1232
- package/mcp-server/lib/baseline-workflow.cjs +0 -1193
- package/mcp-server/lib/canonical-json.cjs +0 -19
- package/mcp-server/lib/change-authority.cjs +0 -26
- package/mcp-server/lib/change-packet.cjs +0 -790
- package/mcp-server/lib/change-workflow.cjs +0 -65
- package/mcp-server/lib/context-envelope.cjs +0 -581
- package/mcp-server/lib/context-paths.cjs +0 -142
- package/mcp-server/lib/decision-records.cjs +0 -423
- package/mcp-server/lib/delivery-transaction.cjs +0 -471
- package/mcp-server/lib/doctor-backup-worker.cjs +0 -328
- package/mcp-server/lib/doctor.cjs +0 -627
- package/mcp-server/lib/git-bootstrap.cjs +0 -651
- package/mcp-server/lib/init-project.cjs +0 -1021
- package/mcp-server/lib/kernel-change-workflow.cjs +0 -2029
- package/mcp-server/lib/legacy-change-workflow.cjs +0 -3032
- package/mcp-server/lib/managed-file-write.cjs +0 -172
- package/mcp-server/lib/plan-checkpoint.cjs +0 -177
- package/mcp-server/lib/plan-publication-worker.cjs +0 -830
- package/mcp-server/lib/plan-store.cjs +0 -794
- package/mcp-server/lib/prd-parser.cjs +0 -83
- package/mcp-server/lib/pricing.cjs +0 -44
- package/mcp-server/lib/project-context.cjs +0 -178
- package/mcp-server/lib/projector.cjs +0 -386
- package/mcp-server/lib/provider-refs.cjs +0 -42
- package/mcp-server/lib/runtime-paths.cjs +0 -1714
- package/mcp-server/lib/runtime-state.cjs +0 -222
- package/mcp-server/lib/safe-project-file.cjs +0 -385
- package/mcp-server/lib/stage-checkpoints.cjs +0 -463
- package/mcp-server/lib/state-authority.cjs +0 -95
- package/mcp-server/lib/state-db.cjs +0 -1580
- package/mcp-server/lib/state-ops.cjs +0 -1428
- package/mcp-server/lib/task-contract.cjs +0 -404
- package/mcp-server/lib/task-expander.cjs +0 -105
- package/mcp-server/lib/task-ledger.cjs +0 -1964
- package/mcp-server/lib/telemetry.cjs +0 -85
- package/mcp-server/lib/topo.cjs +0 -131
- package/mcp-server/lib/ultra-facade.cjs +0 -2684
- package/mcp-server/lib/worker-packet.cjs +0 -358
- package/mcp-server/server.cjs +0 -437
- package/orchestrator/auto-merge.cjs +0 -128
- package/orchestrator/daemon.cjs +0 -264
- package/orchestrator/dispatch-rules.cjs +0 -78
- package/orchestrator/parallel-orchestrator.cjs +0 -903
- package/orchestrator/planner/plan-builder.cjs +0 -125
- package/orchestrator/recovery.cjs +0 -188
- package/orchestrator/session-close-journal-worker.cjs +0 -5
- package/orchestrator/session-close-journal.cjs +0 -1014
- package/orchestrator/session-runner.cjs +0 -1153
- package/orchestrator/worktree-manager.cjs +0 -282
- package/settings.json +0 -7
- package/skills/cc-collab/SKILL.md +0 -42
- package/skills/code-review-expert/SKILL.md +0 -43
- package/skills/code-review-expert/references/correctness-reliability.md +0 -30
- package/skills/code-review-expert/references/design-boundaries.md +0 -26
- package/skills/code-review-expert/references/removal-plan.md +0 -24
- package/skills/codex-collab/SKILL.md +0 -49
- package/skills/codex-collab/evals/evals.json +0 -35
- package/skills/integration-rules/SKILL.md +0 -32
- package/skills/security-rules/SKILL.md +0 -38
- package/skills/testing-rules/SKILL.md +0 -32
- package/skills/ultra-doctor/SKILL.md +0 -41
- package/skills/ultra-plan/references/semantic-preflight.md +0 -20
- package/skills/ultra-verify/SKILL.md +0 -46
- package/skills/ultra-verify/evals/evals.json +0 -46
- package/skills/ultra-verify/references/cross-verify-modes.md +0 -28
- package/skills/ultra-verify/references/evidence-status.md +0 -21
- package/skills/ultra-verify/scripts/verify_wait.py +0 -94
- package/spec/cli-protocol.md +0 -102
- package/spec/mcp-tools.yaml +0 -312
- package/spec/schemas/baseline-reconciliation.v1.schema.json +0 -73
- package/spec/schemas/change-delta.v1.schema.json +0 -153
- package/spec/schemas/command-manifest.schema.json +0 -29
- package/spec/schemas/context-file.v4.5.schema.json +0 -54
- package/spec/schemas/delivery-report.v1.schema.json +0 -66
- package/spec/schemas/documentation-reconciliation.v1.schema.json +0 -106
- package/spec/schemas/mcp-tools.schema.json +0 -136
- package/spec/schemas/skill-manifest.schema.json +0 -24
- package/spec/schemas/state-db.sql +0 -641
- package/spec/schemas/task-ledger.v2.schema.json +0 -296
- package/spec/schemas/tasks.v4.5.schema.json +0 -96
- package/spec/schemas/test-report.v1.schema.json +0 -225
- package/ultra-tools/cli.cjs +0 -129
- package/ultra-tools/commands/db.cjs +0 -226
- package/ultra-tools/commands/legacy-memory.cjs +0 -159
- package/ultra-tools/commands/migrate.cjs +0 -1386
- package/ultra-tools/commands/session.cjs +0 -213
- package/ultra-tools/commands/status.cjs +0 -559
- package/ultra-tools/commands/system.cjs +0 -498
- package/ultra-tools/commands/task.cjs +0 -105
|
@@ -1,1580 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('node:fs');
|
|
4
|
-
const path = require('node:path');
|
|
5
|
-
|
|
6
|
-
const Database = require('better-sqlite3');
|
|
7
|
-
|
|
8
|
-
const REPO_ROOT = process.env.UBP_RUNTIME_ROOT
|
|
9
|
-
? path.resolve(process.env.UBP_RUNTIME_ROOT)
|
|
10
|
-
: path.resolve(__dirname, '..', '..');
|
|
11
|
-
const SCHEMA_FILE = path.join(REPO_ROOT, 'spec', 'schemas', 'state-db.sql');
|
|
12
|
-
const EXPECTED_VERSION = '23.0';
|
|
13
|
-
const KIMI_SCHEMA_VERSION = '9.1';
|
|
14
|
-
const CONTEXT_SCHEMA_VERSION = '10.0';
|
|
15
|
-
const BASELINE_SCHEMA_VERSION = '11.0';
|
|
16
|
-
const ADOPTION_SCHEMA_VERSION = '12.0';
|
|
17
|
-
const WORKFLOW_SCHEMA_VERSION = '13.0';
|
|
18
|
-
const AUTHORITY_SCHEMA_VERSION = '14.0';
|
|
19
|
-
const SEMANTIC_SCHEMA_VERSION = '15.0';
|
|
20
|
-
const DIALOGUE_SCHEMA_VERSION = '16.0';
|
|
21
|
-
const GIT_AUTHORITY_SCHEMA_VERSION = '17.0';
|
|
22
|
-
const ADAPTIVE_SCHEMA_VERSION = '18.0';
|
|
23
|
-
const DECISION_COMPLETION_SCHEMA_VERSION = '19.0';
|
|
24
|
-
const ARTIFACT_REGISTRY_SCHEMA_VERSION = '20.0';
|
|
25
|
-
const KERNEL_SCHEMA_VERSION = '21.0';
|
|
26
|
-
const CHECKPOINT_NATIVE_SCHEMA_VERSION = '22.0';
|
|
27
|
-
const SEMANTIC_KERNEL_SCHEMA_VERSION = '23.0';
|
|
28
|
-
|
|
29
|
-
const MIGRATED_GAPS = Object.freeze([{
|
|
30
|
-
id: 'legacy-rebaseline-required',
|
|
31
|
-
category: 'baseline_blocker',
|
|
32
|
-
status: 'open',
|
|
33
|
-
blocking: true,
|
|
34
|
-
summary: 'Legacy Ultra state requires evidence-backed brownfield re-adoption.',
|
|
35
|
-
evidence_refs: [],
|
|
36
|
-
owner: null,
|
|
37
|
-
}]);
|
|
38
|
-
|
|
39
|
-
const REQUIRED_TABLES = Object.freeze([
|
|
40
|
-
'baselines',
|
|
41
|
-
'tasks',
|
|
42
|
-
'events',
|
|
43
|
-
'sessions',
|
|
44
|
-
'schema_version',
|
|
45
|
-
'migration_history',
|
|
46
|
-
'telemetry',
|
|
47
|
-
'specs_refs',
|
|
48
|
-
'circuit_breaker',
|
|
49
|
-
'changes',
|
|
50
|
-
'artifacts',
|
|
51
|
-
'artifact_edges',
|
|
52
|
-
'context_snapshots',
|
|
53
|
-
'spec_learning_candidates',
|
|
54
|
-
'trace_links',
|
|
55
|
-
'incidents',
|
|
56
|
-
'projection_jobs',
|
|
57
|
-
'event_consumers',
|
|
58
|
-
'workflow_runs',
|
|
59
|
-
'workflow_steps',
|
|
60
|
-
'decision_threads',
|
|
61
|
-
'decision_items',
|
|
62
|
-
'stage_checkpoints',
|
|
63
|
-
'decision_records',
|
|
64
|
-
'context_envelopes',
|
|
65
|
-
'worker_packets',
|
|
66
|
-
]);
|
|
67
|
-
|
|
68
|
-
class ActiveSessionLeaseConflictError extends Error {
|
|
69
|
-
constructor(conflicts) {
|
|
70
|
-
super(
|
|
71
|
-
'state.db has duplicate active task leases; resolve the conflicting sessions before upgrade: '
|
|
72
|
-
+ conflicts.map((row) => `${row.task_id}=[${row.session_ids.join(',')}]`).join('; '),
|
|
73
|
-
);
|
|
74
|
-
this.name = 'ActiveSessionLeaseConflictError';
|
|
75
|
-
this.code = 'ACTIVE_SESSION_LEASE_CONFLICT';
|
|
76
|
-
this.details = { conflicts };
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
class ArtifactAuthorityConflictError extends Error {
|
|
81
|
-
constructor(conflicts) {
|
|
82
|
-
super(
|
|
83
|
-
'state.db has duplicate active artifact authority for canonical paths: '
|
|
84
|
-
+ conflicts.map((row) => `${row.path}=[${row.artifact_ids.join(',')}]`).join('; '),
|
|
85
|
-
);
|
|
86
|
-
this.name = 'ArtifactAuthorityConflictError';
|
|
87
|
-
this.code = 'ARTIFACT_AUTHORITY_CONFLICT';
|
|
88
|
-
this.details = { conflicts };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function canonicalArtifactPath(value) {
|
|
93
|
-
const raw = String(value || '').trim().replaceAll('\\', '/');
|
|
94
|
-
if (!raw || path.posix.isAbsolute(raw)) {
|
|
95
|
-
throw new ArtifactAuthorityConflictError([{
|
|
96
|
-
path: raw || '(empty)',
|
|
97
|
-
artifact_ids: [],
|
|
98
|
-
reason: 'invalid_path',
|
|
99
|
-
}]);
|
|
100
|
-
}
|
|
101
|
-
const normalized = path.posix.normalize(raw).replace(/^\.\//, '');
|
|
102
|
-
if (normalized === '..' || normalized.startsWith('../') || normalized.includes('/../')) {
|
|
103
|
-
throw new ArtifactAuthorityConflictError([{
|
|
104
|
-
path: raw,
|
|
105
|
-
artifact_ids: [],
|
|
106
|
-
reason: 'path_escape',
|
|
107
|
-
}]);
|
|
108
|
-
}
|
|
109
|
-
return normalized;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function activeArtifactPathConflicts(db) {
|
|
113
|
-
if (!tableNames(db).includes('artifacts')) return [];
|
|
114
|
-
const columns = columnNames(db, 'artifacts');
|
|
115
|
-
if (!columns.has('id') || !columns.has('path')) return [];
|
|
116
|
-
const hasStatus = columns.has('status');
|
|
117
|
-
const rows = db.prepare(
|
|
118
|
-
`SELECT id, path FROM artifacts${hasStatus ? " WHERE status <> 'archived'" : ''}
|
|
119
|
-
ORDER BY id`,
|
|
120
|
-
).all();
|
|
121
|
-
const grouped = new Map();
|
|
122
|
-
for (const row of rows) {
|
|
123
|
-
const canonical = canonicalArtifactPath(row.path);
|
|
124
|
-
const ids = grouped.get(canonical) || [];
|
|
125
|
-
ids.push(row.id);
|
|
126
|
-
grouped.set(canonical, ids);
|
|
127
|
-
}
|
|
128
|
-
return [...grouped.entries()]
|
|
129
|
-
.filter(([, ids]) => ids.length > 1)
|
|
130
|
-
.map(([artifactPath, ids]) => ({ path: artifactPath, artifact_ids: ids }));
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function assertNoDuplicateActiveArtifactPaths(db) {
|
|
134
|
-
const conflicts = activeArtifactPathConflicts(db);
|
|
135
|
-
if (conflicts.length > 0) throw new ArtifactAuthorityConflictError(conflicts);
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function normalizeStoredArtifactPaths(db) {
|
|
140
|
-
if (!tableNames(db).includes('artifacts')) return false;
|
|
141
|
-
assertNoDuplicateActiveArtifactPaths(db);
|
|
142
|
-
const rows = db.prepare('SELECT id, path FROM artifacts ORDER BY id').all();
|
|
143
|
-
let changed = false;
|
|
144
|
-
const update = db.prepare('UPDATE artifacts SET path = ? WHERE id = ?');
|
|
145
|
-
for (const row of rows) {
|
|
146
|
-
const canonical = canonicalArtifactPath(row.path);
|
|
147
|
-
if (canonical !== row.path) {
|
|
148
|
-
update.run(canonical, row.id);
|
|
149
|
-
changed = true;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return changed;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function applyBaselineUpgrade(db, { legacyState = false } = {}) {
|
|
156
|
-
db.exec(`
|
|
157
|
-
CREATE TABLE IF NOT EXISTS baselines (
|
|
158
|
-
id TEXT PRIMARY KEY,
|
|
159
|
-
project_name TEXT NOT NULL,
|
|
160
|
-
project_type TEXT,
|
|
161
|
-
stack TEXT,
|
|
162
|
-
mode TEXT NOT NULL CHECK (mode IN ('greenfield', 'brownfield', 'migrated')),
|
|
163
|
-
status TEXT NOT NULL DEFAULT 'draft'
|
|
164
|
-
CHECK (status IN ('draft', 'adopting', 'blocked', 'ready', 'superseded')),
|
|
165
|
-
repository_root TEXT NOT NULL DEFAULT '.',
|
|
166
|
-
scope_json TEXT NOT NULL DEFAULT '["."]',
|
|
167
|
-
repository_revision TEXT,
|
|
168
|
-
repository_branch TEXT,
|
|
169
|
-
worktree_state TEXT NOT NULL DEFAULT 'unavailable'
|
|
170
|
-
CHECK (worktree_state IN ('clean', 'dirty', 'unborn', 'unavailable')),
|
|
171
|
-
worktree_digest TEXT,
|
|
172
|
-
worktree_files_json TEXT NOT NULL DEFAULT '[]',
|
|
173
|
-
worktree_accepted INTEGER NOT NULL DEFAULT 0 CHECK (worktree_accepted IN (0, 1)),
|
|
174
|
-
known_red_accepted INTEGER NOT NULL DEFAULT 0 CHECK (known_red_accepted IN (0, 1)),
|
|
175
|
-
spec_refs_json TEXT NOT NULL DEFAULT '[]',
|
|
176
|
-
evidence_json TEXT NOT NULL DEFAULT '[]',
|
|
177
|
-
verification_json TEXT NOT NULL DEFAULT '[]',
|
|
178
|
-
unknowns_json TEXT NOT NULL DEFAULT '[]',
|
|
179
|
-
gaps_json TEXT NOT NULL DEFAULT '[]',
|
|
180
|
-
classification_json TEXT NOT NULL DEFAULT '{}',
|
|
181
|
-
provider_refs_json TEXT NOT NULL DEFAULT '{}',
|
|
182
|
-
research_run_id TEXT,
|
|
183
|
-
research_checkpoint_id TEXT,
|
|
184
|
-
approved_by TEXT,
|
|
185
|
-
approval_note TEXT,
|
|
186
|
-
started_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
187
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
188
|
-
converged_at TEXT
|
|
189
|
-
);
|
|
190
|
-
CREATE INDEX IF NOT EXISTS baselines_status ON baselines(status, updated_at);
|
|
191
|
-
`);
|
|
192
|
-
if (legacyState && db.prepare('SELECT COUNT(*) AS count FROM baselines').get().count === 0) {
|
|
193
|
-
db.prepare(
|
|
194
|
-
`INSERT OR IGNORE INTO baselines
|
|
195
|
-
(id, project_name, mode, status, gaps_json, approval_note)
|
|
196
|
-
VALUES ('migrated-baseline', 'Migrated Ultra project', 'migrated', 'adopting', ?,
|
|
197
|
-
'Legacy project preserved; owner re-adoption is required')`,
|
|
198
|
-
).run(JSON.stringify(MIGRATED_GAPS));
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function addColumnIfMissing(db, table, columns, name, definition) {
|
|
203
|
-
if (!columns.has(name)) {
|
|
204
|
-
db.exec(`ALTER TABLE ${table} ADD COLUMN ${name} ${definition}`);
|
|
205
|
-
columns.add(name);
|
|
206
|
-
return true;
|
|
207
|
-
}
|
|
208
|
-
return false;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function applyBaselineColumns(db) {
|
|
212
|
-
if (!tableNames(db).includes('baselines')) return false;
|
|
213
|
-
const columns = columnNames(db, 'baselines');
|
|
214
|
-
let changed = false;
|
|
215
|
-
changed = addColumnIfMissing(db, 'baselines', columns, 'repository_branch', 'TEXT') || changed;
|
|
216
|
-
changed = addColumnIfMissing(
|
|
217
|
-
db, 'baselines', columns, 'worktree_state',
|
|
218
|
-
"TEXT NOT NULL DEFAULT 'unavailable' CHECK (worktree_state IN ('clean', 'dirty', 'unborn', 'unavailable'))",
|
|
219
|
-
) || changed;
|
|
220
|
-
changed = addColumnIfMissing(db, 'baselines', columns, 'worktree_digest', 'TEXT') || changed;
|
|
221
|
-
changed = addColumnIfMissing(
|
|
222
|
-
db, 'baselines', columns, 'worktree_files_json', "TEXT NOT NULL DEFAULT '[]'",
|
|
223
|
-
) || changed;
|
|
224
|
-
changed = addColumnIfMissing(
|
|
225
|
-
db, 'baselines', columns, 'worktree_accepted',
|
|
226
|
-
'INTEGER NOT NULL DEFAULT 0 CHECK (worktree_accepted IN (0, 1))',
|
|
227
|
-
) || changed;
|
|
228
|
-
changed = addColumnIfMissing(
|
|
229
|
-
db, 'baselines', columns, 'known_red_accepted',
|
|
230
|
-
'INTEGER NOT NULL DEFAULT 0 CHECK (known_red_accepted IN (0, 1))',
|
|
231
|
-
) || changed;
|
|
232
|
-
changed = addColumnIfMissing(db, 'baselines', columns, 'gaps_json', "TEXT NOT NULL DEFAULT '[]'") || changed;
|
|
233
|
-
changed = addColumnIfMissing(
|
|
234
|
-
db, 'baselines', columns, 'classification_json', "TEXT NOT NULL DEFAULT '{}'",
|
|
235
|
-
) || changed;
|
|
236
|
-
changed = addColumnIfMissing(db, 'baselines', columns, 'research_run_id', 'TEXT') || changed;
|
|
237
|
-
changed = addColumnIfMissing(
|
|
238
|
-
db,
|
|
239
|
-
'baselines',
|
|
240
|
-
columns,
|
|
241
|
-
'research_checkpoint_id',
|
|
242
|
-
'TEXT',
|
|
243
|
-
) || changed;
|
|
244
|
-
if (columns.has('research_checkpoint_id')
|
|
245
|
-
&& tableNames(db).includes('stage_checkpoints')) {
|
|
246
|
-
const result = db.prepare(
|
|
247
|
-
`UPDATE baselines
|
|
248
|
-
SET research_checkpoint_id = research_run_id
|
|
249
|
-
WHERE research_checkpoint_id IS NULL
|
|
250
|
-
AND research_run_id IS NOT NULL
|
|
251
|
-
AND EXISTS (
|
|
252
|
-
SELECT 1 FROM stage_checkpoints
|
|
253
|
-
WHERE stage_checkpoints.id = baselines.research_run_id
|
|
254
|
-
AND stage_checkpoints.stage = 'research'
|
|
255
|
-
)`,
|
|
256
|
-
).run();
|
|
257
|
-
changed = result.changes > 0 || changed;
|
|
258
|
-
}
|
|
259
|
-
return changed;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function applyKernelIntegrityColumns(db) {
|
|
263
|
-
const tables = new Set(tableNames(db));
|
|
264
|
-
let changed = false;
|
|
265
|
-
if (tables.has('context_envelopes')) {
|
|
266
|
-
const columns = columnNames(db, 'context_envelopes');
|
|
267
|
-
changed = addColumnIfMissing(
|
|
268
|
-
db,
|
|
269
|
-
'context_envelopes',
|
|
270
|
-
columns,
|
|
271
|
-
'file_digest',
|
|
272
|
-
'TEXT',
|
|
273
|
-
) || changed;
|
|
274
|
-
}
|
|
275
|
-
if (tables.has('worker_packets')) {
|
|
276
|
-
const columns = columnNames(db, 'worker_packets');
|
|
277
|
-
for (const [name, definition] of [
|
|
278
|
-
['file_digest', 'TEXT'],
|
|
279
|
-
['status', "TEXT NOT NULL DEFAULT 'assigned' CHECK (status IN ('pending', 'assigned', 'abandoned'))"],
|
|
280
|
-
['assigned_at', 'TEXT'],
|
|
281
|
-
['abandoned_at', 'TEXT'],
|
|
282
|
-
['abandon_reason', 'TEXT'],
|
|
283
|
-
]) {
|
|
284
|
-
changed = addColumnIfMissing(
|
|
285
|
-
db,
|
|
286
|
-
'worker_packets',
|
|
287
|
-
columns,
|
|
288
|
-
name,
|
|
289
|
-
definition,
|
|
290
|
-
) || changed;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return changed;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
function applyChangeColumns(db) {
|
|
297
|
-
if (!tableNames(db).includes('changes')) return false;
|
|
298
|
-
const columns = columnNames(db, 'changes');
|
|
299
|
-
let changed = false;
|
|
300
|
-
changed = addColumnIfMissing(db, 'changes', columns, 'baseline_bypass_json', 'TEXT') || changed;
|
|
301
|
-
changed = addColumnIfMissing(db, 'changes', columns, 'contract_json', "TEXT NOT NULL DEFAULT '{}'") || changed;
|
|
302
|
-
changed = addColumnIfMissing(db, 'changes', columns, 'classification_json', "TEXT NOT NULL DEFAULT '{}'") || changed;
|
|
303
|
-
changed = addColumnIfMissing(
|
|
304
|
-
db, 'changes', columns, 'research_disposition_json', "TEXT NOT NULL DEFAULT '{}'",
|
|
305
|
-
) || changed;
|
|
306
|
-
changed = addColumnIfMissing(
|
|
307
|
-
db, 'changes', columns, 'alignment_thread_id',
|
|
308
|
-
'TEXT REFERENCES decision_threads(id) ON DELETE SET NULL',
|
|
309
|
-
) || changed;
|
|
310
|
-
changed = addColumnIfMissing(
|
|
311
|
-
db, 'changes', columns, 'supersedes_id',
|
|
312
|
-
'TEXT REFERENCES changes(id) ON DELETE SET NULL',
|
|
313
|
-
) || changed;
|
|
314
|
-
return changed;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function upgradeSemanticKernelConstraints(db) {
|
|
318
|
-
const tables = new Set(tableNames(db));
|
|
319
|
-
if (!tables.has('changes') || !tables.has('tasks')) return false;
|
|
320
|
-
const taskColumns = columnNames(db, 'tasks');
|
|
321
|
-
const preservesLegacyComplexityHint = taskColumns.has('complexity_hint');
|
|
322
|
-
const changesSql = db.prepare(
|
|
323
|
-
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'changes'",
|
|
324
|
-
).get()?.sql || '';
|
|
325
|
-
const tasksSql = db.prepare(
|
|
326
|
-
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'tasks'",
|
|
327
|
-
).get()?.sql || '';
|
|
328
|
-
const current = /length\s*\(\s*trim\s*\(\s*kind\s*\)\s*\)\s+BETWEEN\s+1\s+AND\s+80/i
|
|
329
|
-
.test(changesSql)
|
|
330
|
-
&& /length\s*\(\s*trim\s*\(\s*type\s*\)\s*\)\s+BETWEEN\s+1\s+AND\s+80/i
|
|
331
|
-
.test(tasksSql)
|
|
332
|
-
&& /length\s*\(\s*trim\s*\(\s*priority\s*\)\s*\)\s+BETWEEN\s+1\s+AND\s+80/i
|
|
333
|
-
.test(tasksSql)
|
|
334
|
-
&& /length\s*\(\s*trim\s*\(\s*slice_kind\s*\)\s*\)\s+BETWEEN\s+1\s+AND\s+80/i
|
|
335
|
-
.test(tasksSql)
|
|
336
|
-
&& columnNames(db, 'changes').has('supersedes_id');
|
|
337
|
-
if (current) return false;
|
|
338
|
-
|
|
339
|
-
const foreignKeys = db.pragma('foreign_keys', { simple: true });
|
|
340
|
-
db.pragma('foreign_keys = OFF');
|
|
341
|
-
try {
|
|
342
|
-
db.transaction(() => {
|
|
343
|
-
db.exec(`
|
|
344
|
-
DROP TABLE IF EXISTS changes_semantic_kernel_upgrade;
|
|
345
|
-
CREATE TABLE changes_semantic_kernel_upgrade (
|
|
346
|
-
id TEXT PRIMARY KEY,
|
|
347
|
-
title TEXT NOT NULL,
|
|
348
|
-
kind TEXT NOT NULL CHECK (length(trim(kind)) BETWEEN 1 AND 80),
|
|
349
|
-
status TEXT NOT NULL DEFAULT 'active'
|
|
350
|
-
CHECK (status IN ('active', 'blocked', 'ready', 'archived', 'cancelled')),
|
|
351
|
-
intent TEXT NOT NULL,
|
|
352
|
-
docs_impact_json TEXT NOT NULL DEFAULT '{"status":"unknown","files":[],"rationale":null}',
|
|
353
|
-
provider_refs_json TEXT NOT NULL DEFAULT '{}',
|
|
354
|
-
baseline_bypass_json TEXT,
|
|
355
|
-
contract_json TEXT NOT NULL DEFAULT '{}',
|
|
356
|
-
classification_json TEXT NOT NULL DEFAULT '{}',
|
|
357
|
-
research_disposition_json TEXT NOT NULL DEFAULT '{}',
|
|
358
|
-
alignment_thread_id TEXT REFERENCES decision_threads(id) ON DELETE SET NULL,
|
|
359
|
-
base_commit TEXT,
|
|
360
|
-
supersedes_id TEXT REFERENCES changes_semantic_kernel_upgrade(id) ON DELETE SET NULL,
|
|
361
|
-
artifact_root TEXT NOT NULL,
|
|
362
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
363
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
364
|
-
closed_at TEXT
|
|
365
|
-
);
|
|
366
|
-
INSERT INTO changes_semantic_kernel_upgrade (
|
|
367
|
-
id, title, kind, status, intent, docs_impact_json, provider_refs_json,
|
|
368
|
-
baseline_bypass_json, contract_json, classification_json,
|
|
369
|
-
research_disposition_json, alignment_thread_id, base_commit,
|
|
370
|
-
supersedes_id, artifact_root, created_at, updated_at, closed_at
|
|
371
|
-
)
|
|
372
|
-
SELECT
|
|
373
|
-
id, title, kind, status, intent, docs_impact_json, provider_refs_json,
|
|
374
|
-
baseline_bypass_json, contract_json, classification_json,
|
|
375
|
-
research_disposition_json, alignment_thread_id, base_commit,
|
|
376
|
-
supersedes_id, artifact_root, created_at, updated_at, closed_at
|
|
377
|
-
FROM changes;
|
|
378
|
-
|
|
379
|
-
DROP TABLE IF EXISTS tasks_semantic_kernel_upgrade;
|
|
380
|
-
CREATE TABLE tasks_semantic_kernel_upgrade (
|
|
381
|
-
id TEXT PRIMARY KEY,
|
|
382
|
-
title TEXT NOT NULL,
|
|
383
|
-
type TEXT NOT NULL CHECK (length(trim(type)) BETWEEN 1 AND 80),
|
|
384
|
-
priority TEXT NOT NULL CHECK (length(trim(priority)) BETWEEN 1 AND 80),
|
|
385
|
-
complexity INTEGER CHECK (complexity BETWEEN 1 AND 10),
|
|
386
|
-
estimated_days REAL CHECK (estimated_days IS NULL OR estimated_days > 0),
|
|
387
|
-
status TEXT NOT NULL DEFAULT 'pending'
|
|
388
|
-
CHECK (status IN ('pending', 'in_progress', 'completed', 'blocked', 'expanded')),
|
|
389
|
-
deps TEXT,
|
|
390
|
-
files_modified TEXT,
|
|
391
|
-
session_id TEXT,
|
|
392
|
-
stale INTEGER NOT NULL DEFAULT 0 CHECK (stale IN (0, 1)),
|
|
393
|
-
${preservesLegacyComplexityHint ? 'complexity_hint TEXT,' : ''}
|
|
394
|
-
tag TEXT,
|
|
395
|
-
trace_to TEXT,
|
|
396
|
-
outcome TEXT,
|
|
397
|
-
slice_kind TEXT CHECK (
|
|
398
|
-
slice_kind IS NULL OR length(trim(slice_kind)) BETWEEN 1 AND 80
|
|
399
|
-
),
|
|
400
|
-
public_seam TEXT,
|
|
401
|
-
verification_command TEXT,
|
|
402
|
-
acceptance_json TEXT NOT NULL DEFAULT '[]',
|
|
403
|
-
context_refs_json TEXT NOT NULL DEFAULT '[]',
|
|
404
|
-
docs_impact_json TEXT NOT NULL DEFAULT '{"status":"unknown","files":[],"rationale":null}',
|
|
405
|
-
ownership_json TEXT NOT NULL DEFAULT '{}',
|
|
406
|
-
context_file TEXT,
|
|
407
|
-
completion_commit TEXT,
|
|
408
|
-
change_id TEXT REFERENCES changes_semantic_kernel_upgrade(id) ON DELETE SET NULL,
|
|
409
|
-
parent_id TEXT REFERENCES tasks_semantic_kernel_upgrade(id) ON DELETE SET NULL,
|
|
410
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
411
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
412
|
-
);
|
|
413
|
-
INSERT INTO tasks_semantic_kernel_upgrade (
|
|
414
|
-
id, title, type, priority, complexity, estimated_days, status, deps,
|
|
415
|
-
files_modified, session_id, stale,
|
|
416
|
-
${preservesLegacyComplexityHint ? 'complexity_hint,' : ''}
|
|
417
|
-
tag, trace_to, outcome, slice_kind,
|
|
418
|
-
public_seam, verification_command, acceptance_json, context_refs_json,
|
|
419
|
-
docs_impact_json, ownership_json, context_file, completion_commit,
|
|
420
|
-
change_id, parent_id, created_at, updated_at
|
|
421
|
-
)
|
|
422
|
-
SELECT
|
|
423
|
-
id, title, type, priority, complexity, estimated_days, status, deps,
|
|
424
|
-
files_modified, session_id, stale,
|
|
425
|
-
${preservesLegacyComplexityHint ? 'complexity_hint,' : ''}
|
|
426
|
-
tag, trace_to, outcome, slice_kind,
|
|
427
|
-
public_seam, verification_command, acceptance_json, context_refs_json,
|
|
428
|
-
docs_impact_json, ownership_json, context_file, completion_commit,
|
|
429
|
-
change_id, parent_id, created_at, updated_at
|
|
430
|
-
FROM tasks;
|
|
431
|
-
|
|
432
|
-
DROP TABLE tasks;
|
|
433
|
-
DROP TABLE changes;
|
|
434
|
-
ALTER TABLE changes_semantic_kernel_upgrade RENAME TO changes;
|
|
435
|
-
ALTER TABLE tasks_semantic_kernel_upgrade RENAME TO tasks;
|
|
436
|
-
CREATE INDEX changes_status ON changes(status, created_at);
|
|
437
|
-
CREATE INDEX changes_kind ON changes(kind, created_at);
|
|
438
|
-
CREATE INDEX tasks_status ON tasks(status);
|
|
439
|
-
CREATE INDEX tasks_tag ON tasks(tag);
|
|
440
|
-
CREATE INDEX tasks_session ON tasks(session_id) WHERE session_id IS NOT NULL;
|
|
441
|
-
CREATE INDEX tasks_stale ON tasks(stale) WHERE stale = 1;
|
|
442
|
-
CREATE INDEX tasks_parent ON tasks(parent_id) WHERE parent_id IS NOT NULL;
|
|
443
|
-
CREATE INDEX tasks_change ON tasks(change_id) WHERE change_id IS NOT NULL;
|
|
444
|
-
`);
|
|
445
|
-
const violations = db.pragma('foreign_key_check');
|
|
446
|
-
if (violations.length > 0) {
|
|
447
|
-
throw new Error(
|
|
448
|
-
`semantic kernel migration produced ${violations.length} foreign key violation(s)`,
|
|
449
|
-
);
|
|
450
|
-
}
|
|
451
|
-
})();
|
|
452
|
-
} finally {
|
|
453
|
-
db.pragma(`foreign_keys = ${foreignKeys ? 'ON' : 'OFF'}`);
|
|
454
|
-
}
|
|
455
|
-
return true;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function applySemanticAuthorityColumns(db) {
|
|
459
|
-
let changed = false;
|
|
460
|
-
if (tableNames(db).includes('workflow_steps')) {
|
|
461
|
-
const columns = columnNames(db, 'workflow_steps');
|
|
462
|
-
changed = addColumnIfMissing(
|
|
463
|
-
db, 'workflow_steps', columns, 'semantic_records_json', "TEXT NOT NULL DEFAULT '[]'",
|
|
464
|
-
) || changed;
|
|
465
|
-
}
|
|
466
|
-
if (tableNames(db).includes('spec_learning_candidates')) {
|
|
467
|
-
const columns = columnNames(db, 'spec_learning_candidates');
|
|
468
|
-
for (const [name, definition] of [
|
|
469
|
-
['applied_ref', 'TEXT'],
|
|
470
|
-
['before_digest', 'TEXT'],
|
|
471
|
-
['after_digest', 'TEXT'],
|
|
472
|
-
['apply_evidence_json', "TEXT NOT NULL DEFAULT '[]'"],
|
|
473
|
-
]) {
|
|
474
|
-
changed = addColumnIfMissing(db, 'spec_learning_candidates', columns, name, definition) || changed;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
return changed;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
function quoteSqlString(value) {
|
|
481
|
-
return `'${String(value).replaceAll("'", "''")}'`;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
function migrationBackup(db, dbPath, fromVersion, existingTables) {
|
|
485
|
-
if (existingTables.size === 0 || fromVersion === EXPECTED_VERSION) return null;
|
|
486
|
-
const parent = path.dirname(dbPath);
|
|
487
|
-
const backupDir = path.basename(parent) === '.ultra' ? path.join(parent, 'backups') : path.join(parent, 'backups');
|
|
488
|
-
fs.mkdirSync(backupDir, { recursive: true });
|
|
489
|
-
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
490
|
-
const version = String(fromVersion || 'unknown').replace(/[^a-zA-Z0-9_.-]/g, '_');
|
|
491
|
-
const backupPath = path.join(backupDir, `state-pre-${version}-to-${EXPECTED_VERSION}-${stamp}.db`);
|
|
492
|
-
db.exec(`VACUUM INTO ${quoteSqlString(backupPath)}`);
|
|
493
|
-
return backupPath;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
function readSchemaSql() {
|
|
497
|
-
if (!fs.existsSync(SCHEMA_FILE)) {
|
|
498
|
-
throw new Error(`state-db schema missing at ${SCHEMA_FILE}`);
|
|
499
|
-
}
|
|
500
|
-
return fs.readFileSync(SCHEMA_FILE, 'utf8');
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
function applyPragmas(db) {
|
|
504
|
-
const journalMode = db.pragma('journal_mode = WAL', { simple: true });
|
|
505
|
-
if (String(journalMode).toLowerCase() !== 'wal') {
|
|
506
|
-
const error = new Error(
|
|
507
|
-
`state.db requires SQLite WAL mode; storage returned ${String(journalMode || 'unknown')}`,
|
|
508
|
-
);
|
|
509
|
-
error.code = 'STATE_DB_WAL_UNAVAILABLE';
|
|
510
|
-
throw error;
|
|
511
|
-
}
|
|
512
|
-
db.pragma('synchronous = NORMAL');
|
|
513
|
-
db.pragma('busy_timeout = 5000');
|
|
514
|
-
db.pragma('foreign_keys = ON');
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
function runScript(db, sql) {
|
|
518
|
-
const runner = db.exec.bind(db);
|
|
519
|
-
db.transaction(() => runner(sql))();
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
function openStateDb(dbPath) {
|
|
523
|
-
if (!dbPath) throw new Error('openStateDb: dbPath required');
|
|
524
|
-
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
525
|
-
const db = new Database(dbPath);
|
|
526
|
-
applyPragmas(db);
|
|
527
|
-
return db;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
function tableNames(db) {
|
|
531
|
-
return db
|
|
532
|
-
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'")
|
|
533
|
-
.all()
|
|
534
|
-
.map((r) => r.name);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
function applySchema(db) {
|
|
538
|
-
assertNoDuplicateActiveSessionLeases(db);
|
|
539
|
-
runScript(db, readSchemaSql());
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
function columnNames(db, table) {
|
|
543
|
-
return new Set(db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name));
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
function latestSchemaVersion(db) {
|
|
547
|
-
if (!tableNames(db).includes('schema_version')) return null;
|
|
548
|
-
return db.prepare(
|
|
549
|
-
'SELECT version FROM schema_version ORDER BY applied_at DESC, rowid DESC LIMIT 1',
|
|
550
|
-
).get()?.version || null;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
function applyCompatibleColumns(db) {
|
|
554
|
-
const tables = new Set(tableNames(db));
|
|
555
|
-
if (!tables.has('tasks')) return;
|
|
556
|
-
const taskColumns = columnNames(db, 'tasks');
|
|
557
|
-
if (!taskColumns.has('estimated_days')) {
|
|
558
|
-
db.exec('ALTER TABLE tasks ADD COLUMN estimated_days REAL CHECK (estimated_days IS NULL OR estimated_days > 0)');
|
|
559
|
-
}
|
|
560
|
-
if (!taskColumns.has('change_id')) {
|
|
561
|
-
db.exec('ALTER TABLE tasks ADD COLUMN change_id TEXT REFERENCES changes(id) ON DELETE SET NULL');
|
|
562
|
-
}
|
|
563
|
-
const taskAdditions = [
|
|
564
|
-
['outcome', 'TEXT'],
|
|
565
|
-
['slice_kind', 'TEXT CHECK (slice_kind IS NULL OR length(trim(slice_kind)) BETWEEN 1 AND 80)'],
|
|
566
|
-
['public_seam', 'TEXT'],
|
|
567
|
-
['verification_command', 'TEXT'],
|
|
568
|
-
['acceptance_json', "TEXT NOT NULL DEFAULT '[]'"],
|
|
569
|
-
['context_refs_json', "TEXT NOT NULL DEFAULT '[]'"],
|
|
570
|
-
['docs_impact_json', "TEXT NOT NULL DEFAULT '{\"status\":\"unknown\",\"files\":[],\"rationale\":null}'"],
|
|
571
|
-
['ownership_json', "TEXT NOT NULL DEFAULT '{}'"],
|
|
572
|
-
];
|
|
573
|
-
for (const [name, definition] of taskAdditions) {
|
|
574
|
-
if (!taskColumns.has(name)) db.exec(`ALTER TABLE tasks ADD COLUMN ${name} ${definition}`);
|
|
575
|
-
}
|
|
576
|
-
if (tables.has('events')) {
|
|
577
|
-
const eventColumns = columnNames(db, 'events');
|
|
578
|
-
if (!eventColumns.has('change_id')) db.exec('ALTER TABLE events ADD COLUMN change_id TEXT');
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
function applyContextSpineUpgrade(db) {
|
|
583
|
-
const tables = new Set(tableNames(db));
|
|
584
|
-
if (!tables.has('context_snapshots')) return false;
|
|
585
|
-
let changed = !tables.has('spec_learning_candidates');
|
|
586
|
-
const columns = columnNames(db, 'context_snapshots');
|
|
587
|
-
const additions = [
|
|
588
|
-
['role', "TEXT NOT NULL DEFAULT 'plan' CHECK (role IN ('plan', 'implement', 'check', 'review'))"],
|
|
589
|
-
['gate', "TEXT NOT NULL DEFAULT 'alignment' CHECK (gate IN ('alignment', 'planning', 'implementation', 'verification', 'review', 'convergence', 'recovery'))"],
|
|
590
|
-
['next_action', "TEXT NOT NULL DEFAULT ''"],
|
|
591
|
-
['allowed_transitions_json', "TEXT NOT NULL DEFAULT '[]'"],
|
|
592
|
-
['required_transition', 'TEXT'],
|
|
593
|
-
['readiness', "TEXT NOT NULL DEFAULT 'ready' CHECK (readiness IN ('ready', 'blocked'))"],
|
|
594
|
-
['blockers_json', "TEXT NOT NULL DEFAULT '[]'"],
|
|
595
|
-
['context_json', "TEXT NOT NULL DEFAULT '{}'"],
|
|
596
|
-
['token_estimate', 'INTEGER NOT NULL DEFAULT 0 CHECK (token_estimate >= 0)'],
|
|
597
|
-
['token_budget', 'INTEGER NOT NULL DEFAULT 12000 CHECK (token_budget > 0)'],
|
|
598
|
-
];
|
|
599
|
-
for (const [name, definition] of additions) {
|
|
600
|
-
if (!columns.has(name)) {
|
|
601
|
-
db.exec(`ALTER TABLE context_snapshots ADD COLUMN ${name} ${definition}`);
|
|
602
|
-
changed = true;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
db.exec(`
|
|
606
|
-
CREATE TABLE IF NOT EXISTS spec_learning_candidates (
|
|
607
|
-
id TEXT PRIMARY KEY,
|
|
608
|
-
change_id TEXT NOT NULL REFERENCES changes(id) ON DELETE CASCADE,
|
|
609
|
-
task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
|
|
610
|
-
target_ref TEXT NOT NULL,
|
|
611
|
-
summary TEXT NOT NULL,
|
|
612
|
-
evidence_json TEXT NOT NULL DEFAULT '[]',
|
|
613
|
-
status TEXT NOT NULL DEFAULT 'proposed'
|
|
614
|
-
CHECK (status IN ('proposed', 'approved', 'rejected', 'applied')),
|
|
615
|
-
resolution TEXT,
|
|
616
|
-
proposed_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
617
|
-
resolved_at TEXT,
|
|
618
|
-
applied_at TEXT
|
|
619
|
-
);
|
|
620
|
-
CREATE INDEX IF NOT EXISTS spec_learning_change
|
|
621
|
-
ON spec_learning_candidates(change_id, status, proposed_at);
|
|
622
|
-
`);
|
|
623
|
-
return changed;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
function migrateAdaptiveWorkflowRuns(db, fromVersion = latestSchemaVersion(db)) {
|
|
627
|
-
const tables = new Set(tableNames(db));
|
|
628
|
-
if (!tables.has('workflow_runs') || !tables.has('workflow_steps')) return false;
|
|
629
|
-
const existing = db.prepare(
|
|
630
|
-
"SELECT 1 FROM migration_history WHERE to_version = '18.0' AND status = 'success' LIMIT 1",
|
|
631
|
-
).get();
|
|
632
|
-
if ([
|
|
633
|
-
ADAPTIVE_SCHEMA_VERSION, DECISION_COMPLETION_SCHEMA_VERSION,
|
|
634
|
-
ARTIFACT_REGISTRY_SCHEMA_VERSION, KERNEL_SCHEMA_VERSION,
|
|
635
|
-
CHECKPOINT_NATIVE_SCHEMA_VERSION, SEMANTIC_KERNEL_SCHEMA_VERSION,
|
|
636
|
-
].includes(fromVersion) || existing) {
|
|
637
|
-
return false;
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
const obsoleteSteps = Object.freeze({
|
|
641
|
-
init: ['establish-baseline'],
|
|
642
|
-
change: ['plan-change', 'compile-context', 'verify-readiness'],
|
|
643
|
-
plan: ['select-posture', 'approve-plan'],
|
|
644
|
-
deliver: ['release-if-authorized'],
|
|
645
|
-
});
|
|
646
|
-
const activeRuns = db.prepare(
|
|
647
|
-
`SELECT id, kind, status FROM workflow_runs
|
|
648
|
-
WHERE status IN ('active', 'blocked', 'ready')`,
|
|
649
|
-
).all();
|
|
650
|
-
const isCompleted = db.prepare(
|
|
651
|
-
"SELECT status FROM workflow_steps WHERE run_id = ? AND step_id = ?",
|
|
652
|
-
);
|
|
653
|
-
const currentRequired = db.prepare(
|
|
654
|
-
`SELECT step_id FROM workflow_steps
|
|
655
|
-
WHERE run_id = ? AND required = 1 AND status NOT IN ('completed', 'skipped')
|
|
656
|
-
ORDER BY position ASC LIMIT 1`,
|
|
657
|
-
);
|
|
658
|
-
const skipObsolete = db.prepare(
|
|
659
|
-
`UPDATE workflow_steps
|
|
660
|
-
SET required = 0,
|
|
661
|
-
status = CASE WHEN status IN ('pending', 'in_progress', 'blocked') THEN 'skipped' ELSE status END,
|
|
662
|
-
blockers_json = CASE WHEN status IN ('pending', 'in_progress', 'blocked') THEN '[]' ELSE blockers_json END,
|
|
663
|
-
skip_reason = CASE
|
|
664
|
-
WHEN status IN ('pending', 'in_progress', 'blocked')
|
|
665
|
-
THEN 'Removed by schema 18 adaptive workflow migration; durable evidence is preserved.'
|
|
666
|
-
ELSE skip_reason
|
|
667
|
-
END,
|
|
668
|
-
completed_at = CASE
|
|
669
|
-
WHEN status IN ('pending', 'in_progress', 'blocked')
|
|
670
|
-
THEN strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
671
|
-
ELSE completed_at
|
|
672
|
-
END,
|
|
673
|
-
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
674
|
-
WHERE run_id = ? AND step_id = ?`,
|
|
675
|
-
);
|
|
676
|
-
const completeRun = db.prepare(
|
|
677
|
-
`UPDATE workflow_runs
|
|
678
|
-
SET status = 'completed', current_step = NULL, blockers_json = '[]',
|
|
679
|
-
definition_version = '2.0', summary_json = ?,
|
|
680
|
-
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
|
|
681
|
-
completed_at = COALESCE(completed_at, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
682
|
-
WHERE id = ?`,
|
|
683
|
-
);
|
|
684
|
-
for (const run of activeRuns) {
|
|
685
|
-
for (const stepId of obsoleteSteps[run.kind] || []) skipObsolete.run(run.id, stepId);
|
|
686
|
-
|
|
687
|
-
if (run.kind === 'init') {
|
|
688
|
-
const scaffoldReady = ['inspect-authority', 'classify-repository', 'scaffold-authority']
|
|
689
|
-
.every((stepId) => isCompleted.get(run.id, stepId)?.status === 'completed');
|
|
690
|
-
if (scaffoldReady) {
|
|
691
|
-
db.prepare(
|
|
692
|
-
`UPDATE workflow_steps
|
|
693
|
-
SET status = 'completed',
|
|
694
|
-
evidence_json = CASE WHEN evidence_json = '[]' THEN ? ELSE evidence_json END,
|
|
695
|
-
blockers_json = '[]',
|
|
696
|
-
started_at = COALESCE(started_at, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
697
|
-
completed_at = COALESCE(completed_at, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
698
|
-
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
699
|
-
WHERE run_id = ? AND step_id = 'verify-initialization'
|
|
700
|
-
AND status IN ('pending', 'in_progress', 'blocked')`,
|
|
701
|
-
).run(JSON.stringify([{
|
|
702
|
-
kind: 'migration',
|
|
703
|
-
ref: 'schema:18.0',
|
|
704
|
-
summary: 'Existing scaffold and durable authority were preserved during adaptive migration.',
|
|
705
|
-
}]), run.id);
|
|
706
|
-
if (!currentRequired.get(run.id)) {
|
|
707
|
-
completeRun.run(JSON.stringify({
|
|
708
|
-
migrated: true,
|
|
709
|
-
authority_basis: 'initialized_scaffold',
|
|
710
|
-
research_started: false,
|
|
711
|
-
}), run.id);
|
|
712
|
-
continue;
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
if (run.kind === 'change') {
|
|
718
|
-
const contractCaptured = ['bind-baseline', 'classify-change', 'record-intent']
|
|
719
|
-
.every((stepId) => isCompleted.get(run.id, stepId)?.status === 'completed');
|
|
720
|
-
if (contractCaptured && !currentRequired.get(run.id)) {
|
|
721
|
-
completeRun.run(JSON.stringify({
|
|
722
|
-
migrated: true,
|
|
723
|
-
authority_basis: 'accepted_change_contract',
|
|
724
|
-
}), run.id);
|
|
725
|
-
continue;
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
const next = currentRequired.get(run.id)?.step_id || null;
|
|
730
|
-
db.prepare(
|
|
731
|
-
`UPDATE workflow_runs
|
|
732
|
-
SET definition_version = '2.0', current_step = ?, blockers_json = '[]',
|
|
733
|
-
status = CASE
|
|
734
|
-
WHEN ? IS NULL AND status IN ('active', 'blocked') THEN 'ready'
|
|
735
|
-
WHEN ? IS NOT NULL AND status = 'blocked' THEN 'active'
|
|
736
|
-
ELSE status
|
|
737
|
-
END,
|
|
738
|
-
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
739
|
-
WHERE id = ?`,
|
|
740
|
-
).run(next, next, next, run.id);
|
|
741
|
-
}
|
|
742
|
-
db.prepare(
|
|
743
|
-
`UPDATE context_snapshots
|
|
744
|
-
SET allowed_transitions_json = COALESCE(allowed_transitions_json, '[]'),
|
|
745
|
-
required_transition = NULL
|
|
746
|
-
WHERE allowed_transitions_json IS NULL OR required_transition IS NOT NULL`,
|
|
747
|
-
).run();
|
|
748
|
-
return true;
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
function tableSupportsRuntime(db, table, runtime) {
|
|
752
|
-
const row = db.prepare(
|
|
753
|
-
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?",
|
|
754
|
-
).get(table);
|
|
755
|
-
return typeof row?.sql === 'string' && row.sql.includes(`'${runtime}'`);
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
function baselineSupportsUnbornGit(db) {
|
|
759
|
-
const row = db.prepare(
|
|
760
|
-
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'baselines'",
|
|
761
|
-
).get();
|
|
762
|
-
return typeof row?.sql === 'string' && row.sql.includes("'unborn'");
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
function decisionThreadsSupportCompletion(db) {
|
|
766
|
-
const row = db.prepare(
|
|
767
|
-
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'decision_threads'",
|
|
768
|
-
).get();
|
|
769
|
-
if (typeof row?.sql !== 'string' || !row.sql.includes("'completed'")) return false;
|
|
770
|
-
return columnNames(db, 'decision_threads').has('completed_at');
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
function upgradeDecisionThreadLifecycle(db) {
|
|
774
|
-
if (!tableNames(db).includes('decision_threads') || decisionThreadsSupportCompletion(db)) {
|
|
775
|
-
return false;
|
|
776
|
-
}
|
|
777
|
-
const foreignKeys = db.pragma('foreign_keys', { simple: true });
|
|
778
|
-
db.pragma('foreign_keys = OFF');
|
|
779
|
-
try {
|
|
780
|
-
db.transaction(() => {
|
|
781
|
-
db.exec(`
|
|
782
|
-
DROP TABLE IF EXISTS decision_threads_completion_upgrade;
|
|
783
|
-
CREATE TABLE decision_threads_completion_upgrade (
|
|
784
|
-
id TEXT PRIMARY KEY,
|
|
785
|
-
purpose TEXT NOT NULL,
|
|
786
|
-
mode TEXT NOT NULL DEFAULT 'guided'
|
|
787
|
-
CHECK (mode IN ('guided', 'fast', 'autonomous', 'diagnostic')),
|
|
788
|
-
status TEXT NOT NULL DEFAULT 'active'
|
|
789
|
-
CHECK (status IN ('active', 'checkpoint_ready', 'completed', 'confirmed', 'cancelled')),
|
|
790
|
-
baseline_id TEXT REFERENCES baselines(id) ON DELETE SET NULL,
|
|
791
|
-
change_id TEXT REFERENCES changes(id) ON DELETE CASCADE,
|
|
792
|
-
workflow_run_id TEXT REFERENCES workflow_runs(id) ON DELETE CASCADE,
|
|
793
|
-
summary_json TEXT NOT NULL DEFAULT '{}',
|
|
794
|
-
checkpoint_json TEXT NOT NULL DEFAULT '{}',
|
|
795
|
-
started_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
796
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
797
|
-
completed_at TEXT,
|
|
798
|
-
confirmed_at TEXT,
|
|
799
|
-
CHECK (baseline_id IS NOT NULL OR change_id IS NOT NULL OR workflow_run_id IS NOT NULL)
|
|
800
|
-
);
|
|
801
|
-
INSERT INTO decision_threads_completion_upgrade (
|
|
802
|
-
id, purpose, mode, status, baseline_id, change_id, workflow_run_id,
|
|
803
|
-
summary_json, checkpoint_json, started_at, updated_at, completed_at, confirmed_at
|
|
804
|
-
)
|
|
805
|
-
SELECT
|
|
806
|
-
id, purpose, mode, status, baseline_id, change_id, workflow_run_id,
|
|
807
|
-
summary_json, checkpoint_json, started_at, updated_at, NULL, confirmed_at
|
|
808
|
-
FROM decision_threads;
|
|
809
|
-
DROP TABLE decision_threads;
|
|
810
|
-
ALTER TABLE decision_threads_completion_upgrade RENAME TO decision_threads;
|
|
811
|
-
CREATE INDEX decision_threads_status
|
|
812
|
-
ON decision_threads(status, updated_at);
|
|
813
|
-
CREATE INDEX decision_threads_authority
|
|
814
|
-
ON decision_threads(baseline_id, change_id, workflow_run_id, status);
|
|
815
|
-
`);
|
|
816
|
-
db.prepare(
|
|
817
|
-
`UPDATE decision_threads
|
|
818
|
-
SET status = 'completed',
|
|
819
|
-
summary_json = CASE
|
|
820
|
-
WHEN summary_json = '{}' THEN ?
|
|
821
|
-
ELSE json_set(summary_json, '$.completion_kind', 'migrated_settled_thread')
|
|
822
|
-
END,
|
|
823
|
-
completed_at = updated_at
|
|
824
|
-
WHERE status = 'active'
|
|
825
|
-
AND EXISTS (
|
|
826
|
-
SELECT 1 FROM decision_items WHERE decision_items.thread_id = decision_threads.id
|
|
827
|
-
)
|
|
828
|
-
AND NOT EXISTS (
|
|
829
|
-
SELECT 1 FROM decision_items
|
|
830
|
-
WHERE decision_items.thread_id = decision_threads.id AND status = 'open'
|
|
831
|
-
)
|
|
832
|
-
AND NOT EXISTS (
|
|
833
|
-
SELECT 1 FROM decision_items
|
|
834
|
-
WHERE decision_items.thread_id = decision_threads.id
|
|
835
|
-
AND blocking = 1 AND status = 'deferred'
|
|
836
|
-
)`,
|
|
837
|
-
).run(JSON.stringify({
|
|
838
|
-
text: 'Existing normalized decision state was completed during schema migration.',
|
|
839
|
-
completion_kind: 'migrated_settled_thread',
|
|
840
|
-
}));
|
|
841
|
-
const violations = db.pragma('foreign_key_check');
|
|
842
|
-
if (violations.length > 0) {
|
|
843
|
-
throw new Error(
|
|
844
|
-
`decision completion migration produced ${violations.length} foreign key violation(s)`,
|
|
845
|
-
);
|
|
846
|
-
}
|
|
847
|
-
})();
|
|
848
|
-
} finally {
|
|
849
|
-
db.pragma(`foreign_keys = ${foreignKeys ? 'ON' : 'OFF'}`);
|
|
850
|
-
}
|
|
851
|
-
return true;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
function upgradeBaselineWorktreeConstraint(db, fromVersion = latestSchemaVersion(db)) {
|
|
855
|
-
if (!tableNames(db).includes('baselines') || baselineSupportsUnbornGit(db)) return false;
|
|
856
|
-
const previousVersion = fromVersion || 'unknown';
|
|
857
|
-
const foreignKeys = db.pragma('foreign_keys', { simple: true });
|
|
858
|
-
db.pragma('foreign_keys = OFF');
|
|
859
|
-
try {
|
|
860
|
-
db.transaction(() => {
|
|
861
|
-
db.exec(`
|
|
862
|
-
DROP TABLE IF EXISTS baselines_git_upgrade;
|
|
863
|
-
CREATE TABLE baselines_git_upgrade (
|
|
864
|
-
id TEXT PRIMARY KEY,
|
|
865
|
-
project_name TEXT NOT NULL,
|
|
866
|
-
project_type TEXT,
|
|
867
|
-
stack TEXT,
|
|
868
|
-
mode TEXT NOT NULL CHECK (mode IN ('greenfield', 'brownfield', 'migrated')),
|
|
869
|
-
status TEXT NOT NULL DEFAULT 'draft'
|
|
870
|
-
CHECK (status IN ('draft', 'adopting', 'blocked', 'ready', 'superseded')),
|
|
871
|
-
repository_root TEXT NOT NULL DEFAULT '.',
|
|
872
|
-
scope_json TEXT NOT NULL DEFAULT '["."]',
|
|
873
|
-
repository_revision TEXT,
|
|
874
|
-
repository_branch TEXT,
|
|
875
|
-
worktree_state TEXT NOT NULL DEFAULT 'unavailable'
|
|
876
|
-
CHECK (worktree_state IN ('clean', 'dirty', 'unborn', 'unavailable')),
|
|
877
|
-
worktree_digest TEXT,
|
|
878
|
-
worktree_files_json TEXT NOT NULL DEFAULT '[]',
|
|
879
|
-
worktree_accepted INTEGER NOT NULL DEFAULT 0 CHECK (worktree_accepted IN (0, 1)),
|
|
880
|
-
known_red_accepted INTEGER NOT NULL DEFAULT 0 CHECK (known_red_accepted IN (0, 1)),
|
|
881
|
-
spec_refs_json TEXT NOT NULL DEFAULT '[]',
|
|
882
|
-
evidence_json TEXT NOT NULL DEFAULT '[]',
|
|
883
|
-
verification_json TEXT NOT NULL DEFAULT '[]',
|
|
884
|
-
unknowns_json TEXT NOT NULL DEFAULT '[]',
|
|
885
|
-
gaps_json TEXT NOT NULL DEFAULT '[]',
|
|
886
|
-
classification_json TEXT NOT NULL DEFAULT '{}',
|
|
887
|
-
provider_refs_json TEXT NOT NULL DEFAULT '{}',
|
|
888
|
-
research_run_id TEXT,
|
|
889
|
-
research_checkpoint_id TEXT,
|
|
890
|
-
approved_by TEXT,
|
|
891
|
-
approval_note TEXT,
|
|
892
|
-
started_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
893
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
894
|
-
converged_at TEXT
|
|
895
|
-
);
|
|
896
|
-
INSERT INTO baselines_git_upgrade (
|
|
897
|
-
id, project_name, project_type, stack, mode, status, repository_root, scope_json,
|
|
898
|
-
repository_revision, repository_branch, worktree_state, worktree_digest,
|
|
899
|
-
worktree_files_json, worktree_accepted, known_red_accepted, spec_refs_json,
|
|
900
|
-
evidence_json, verification_json, unknowns_json, gaps_json, classification_json,
|
|
901
|
-
provider_refs_json, research_run_id, research_checkpoint_id,
|
|
902
|
-
approved_by, approval_note, started_at,
|
|
903
|
-
updated_at, converged_at
|
|
904
|
-
)
|
|
905
|
-
SELECT
|
|
906
|
-
id, project_name, project_type, stack, mode, status, repository_root, scope_json,
|
|
907
|
-
repository_revision, repository_branch, worktree_state, worktree_digest,
|
|
908
|
-
worktree_files_json, worktree_accepted, known_red_accepted, spec_refs_json,
|
|
909
|
-
evidence_json, verification_json, unknowns_json, gaps_json, classification_json,
|
|
910
|
-
provider_refs_json, research_run_id, research_checkpoint_id,
|
|
911
|
-
approved_by, approval_note, started_at,
|
|
912
|
-
updated_at, converged_at
|
|
913
|
-
FROM baselines;
|
|
914
|
-
DROP TABLE baselines;
|
|
915
|
-
ALTER TABLE baselines_git_upgrade RENAME TO baselines;
|
|
916
|
-
CREATE INDEX baselines_status ON baselines(status, updated_at);
|
|
917
|
-
`);
|
|
918
|
-
const violations = db.pragma('foreign_key_check');
|
|
919
|
-
if (violations.length > 0) {
|
|
920
|
-
throw new Error(
|
|
921
|
-
`baseline Git-state migration produced ${violations.length} foreign key violation(s)`,
|
|
922
|
-
);
|
|
923
|
-
}
|
|
924
|
-
})();
|
|
925
|
-
} finally {
|
|
926
|
-
db.pragma(`foreign_keys = ${foreignKeys ? 'ON' : 'OFF'}`);
|
|
927
|
-
}
|
|
928
|
-
return previousVersion;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
function upgradeRuntimeConstraints(db, fromVersion = latestSchemaVersion(db)) {
|
|
932
|
-
assertNoDuplicateActiveSessionLeases(db);
|
|
933
|
-
const missingKimi = !tableSupportsRuntime(db, 'events', 'kimi')
|
|
934
|
-
|| !tableSupportsRuntime(db, 'sessions', 'kimi');
|
|
935
|
-
const missingGrok = !tableSupportsRuntime(db, 'events', 'grok')
|
|
936
|
-
|| !tableSupportsRuntime(db, 'sessions', 'grok');
|
|
937
|
-
if (!missingKimi && !missingGrok) return false;
|
|
938
|
-
const previousVersion = fromVersion || 'unknown';
|
|
939
|
-
const foreignKeys = db.pragma('foreign_keys', { simple: true });
|
|
940
|
-
db.pragma('foreign_keys = OFF');
|
|
941
|
-
try {
|
|
942
|
-
db.transaction(() => {
|
|
943
|
-
db.exec(`
|
|
944
|
-
DROP TABLE IF EXISTS events_runtime_upgrade;
|
|
945
|
-
CREATE TABLE events_runtime_upgrade (
|
|
946
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
947
|
-
ts TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
948
|
-
type TEXT NOT NULL,
|
|
949
|
-
task_id TEXT,
|
|
950
|
-
change_id TEXT,
|
|
951
|
-
session_id TEXT,
|
|
952
|
-
runtime TEXT CHECK (runtime IS NULL OR runtime IN ('claude', 'opencode', 'codex', 'kimi', 'grok')),
|
|
953
|
-
payload_json TEXT
|
|
954
|
-
);
|
|
955
|
-
INSERT INTO events_runtime_upgrade
|
|
956
|
-
(id, ts, type, task_id, change_id, session_id, runtime, payload_json)
|
|
957
|
-
SELECT id, ts, type, task_id, change_id, session_id, runtime, payload_json FROM events;
|
|
958
|
-
DROP TABLE events;
|
|
959
|
-
ALTER TABLE events_runtime_upgrade RENAME TO events;
|
|
960
|
-
CREATE INDEX events_ts_type ON events(ts, type);
|
|
961
|
-
CREATE INDEX events_task ON events(task_id, id);
|
|
962
|
-
CREATE INDEX events_session ON events(session_id, id);
|
|
963
|
-
CREATE INDEX events_change ON events(change_id, id);
|
|
964
|
-
|
|
965
|
-
DROP TABLE IF EXISTS sessions_runtime_upgrade;
|
|
966
|
-
CREATE TABLE sessions_runtime_upgrade (
|
|
967
|
-
sid TEXT PRIMARY KEY,
|
|
968
|
-
task_id TEXT NOT NULL REFERENCES tasks(id),
|
|
969
|
-
runtime TEXT NOT NULL CHECK (runtime IN ('claude', 'opencode', 'codex', 'kimi', 'grok')),
|
|
970
|
-
pid INTEGER,
|
|
971
|
-
worktree_path TEXT NOT NULL,
|
|
972
|
-
artifact_dir TEXT NOT NULL,
|
|
973
|
-
status TEXT NOT NULL DEFAULT 'running'
|
|
974
|
-
CHECK (status IN ('running', 'completed', 'crashed', 'orphan')),
|
|
975
|
-
lease_expires_at TEXT NOT NULL,
|
|
976
|
-
heartbeat_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
977
|
-
started_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
978
|
-
);
|
|
979
|
-
INSERT INTO sessions_runtime_upgrade
|
|
980
|
-
(sid, task_id, runtime, pid, worktree_path, artifact_dir, status,
|
|
981
|
-
lease_expires_at, heartbeat_at, started_at)
|
|
982
|
-
SELECT sid, task_id, runtime, pid, worktree_path, artifact_dir, status,
|
|
983
|
-
lease_expires_at, heartbeat_at, started_at FROM sessions;
|
|
984
|
-
DROP TABLE sessions;
|
|
985
|
-
ALTER TABLE sessions_runtime_upgrade RENAME TO sessions;
|
|
986
|
-
CREATE INDEX sessions_active ON sessions(status, task_id);
|
|
987
|
-
CREATE INDEX sessions_lease ON sessions(lease_expires_at) WHERE status = 'running';
|
|
988
|
-
CREATE UNIQUE INDEX sessions_one_active_task
|
|
989
|
-
ON sessions(task_id) WHERE status = 'running';
|
|
990
|
-
`);
|
|
991
|
-
if (missingKimi) {
|
|
992
|
-
db.prepare(
|
|
993
|
-
`INSERT INTO migration_history
|
|
994
|
-
(from_version, to_version, direction, status, notes)
|
|
995
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
996
|
-
).run(
|
|
997
|
-
previousVersion,
|
|
998
|
-
KIMI_SCHEMA_VERSION,
|
|
999
|
-
'Add Kimi to durable event and session runtime constraints',
|
|
1000
|
-
);
|
|
1001
|
-
}
|
|
1002
|
-
if (missingGrok) {
|
|
1003
|
-
db.prepare(
|
|
1004
|
-
`INSERT INTO migration_history
|
|
1005
|
-
(from_version, to_version, direction, status, notes)
|
|
1006
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1007
|
-
).run(
|
|
1008
|
-
missingKimi ? KIMI_SCHEMA_VERSION : previousVersion,
|
|
1009
|
-
KERNEL_SCHEMA_VERSION,
|
|
1010
|
-
'Add Grok to durable event and session runtime constraints',
|
|
1011
|
-
);
|
|
1012
|
-
}
|
|
1013
|
-
const violations = db.pragma('foreign_key_check');
|
|
1014
|
-
if (violations.length > 0) {
|
|
1015
|
-
throw new Error(`runtime constraint migration produced ${violations.length} foreign key violation(s)`);
|
|
1016
|
-
}
|
|
1017
|
-
})();
|
|
1018
|
-
} finally {
|
|
1019
|
-
db.pragma(`foreign_keys = ${foreignKeys ? 'ON' : 'OFF'}`);
|
|
1020
|
-
}
|
|
1021
|
-
return true;
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
function activeSessionLeaseConflicts(db) {
|
|
1025
|
-
if (!tableNames(db).includes('sessions')) return [];
|
|
1026
|
-
const columns = columnNames(db, 'sessions');
|
|
1027
|
-
if (!['sid', 'task_id', 'status'].every((name) => columns.has(name))) return [];
|
|
1028
|
-
const rows = db.prepare(
|
|
1029
|
-
`SELECT task_id, sid
|
|
1030
|
-
FROM sessions
|
|
1031
|
-
WHERE status = 'running'
|
|
1032
|
-
ORDER BY task_id, sid`,
|
|
1033
|
-
).all();
|
|
1034
|
-
const byTask = new Map();
|
|
1035
|
-
for (const row of rows) {
|
|
1036
|
-
const ids = byTask.get(row.task_id) || [];
|
|
1037
|
-
ids.push(row.sid);
|
|
1038
|
-
byTask.set(row.task_id, ids);
|
|
1039
|
-
}
|
|
1040
|
-
return [...byTask.entries()]
|
|
1041
|
-
.filter(([, sessionIds]) => sessionIds.length > 1)
|
|
1042
|
-
.map(([taskId, sessionIds]) => ({
|
|
1043
|
-
task_id: taskId,
|
|
1044
|
-
lease_count: sessionIds.length,
|
|
1045
|
-
session_ids: sessionIds,
|
|
1046
|
-
}));
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
function assertNoDuplicateActiveSessionLeases(db) {
|
|
1050
|
-
const conflicts = activeSessionLeaseConflicts(db);
|
|
1051
|
-
if (conflicts.length > 0) throw new ActiveSessionLeaseConflictError(conflicts);
|
|
1052
|
-
return true;
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
function ensureActiveSessionLeaseIndex(db) {
|
|
1056
|
-
if (!tableNames(db).includes('sessions')) return false;
|
|
1057
|
-
assertNoDuplicateActiveSessionLeases(db);
|
|
1058
|
-
const existing = db.prepare(
|
|
1059
|
-
"SELECT 1 AS present FROM sqlite_master WHERE type = 'index' AND name = 'sessions_one_active_task'",
|
|
1060
|
-
).get();
|
|
1061
|
-
if (existing) return false;
|
|
1062
|
-
db.exec(
|
|
1063
|
-
`CREATE UNIQUE INDEX sessions_one_active_task
|
|
1064
|
-
ON sessions(task_id) WHERE status = 'running'`,
|
|
1065
|
-
);
|
|
1066
|
-
return true;
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
function applyArtifactRegistryUpgrade(db) {
|
|
1070
|
-
if (!tableNames(db).includes('artifacts')) return false;
|
|
1071
|
-
assertNoDuplicateActiveArtifactPaths(db);
|
|
1072
|
-
const columns = columnNames(db, 'artifacts');
|
|
1073
|
-
let changed = false;
|
|
1074
|
-
if (![
|
|
1075
|
-
'owner_type', 'owner_id', 'digest', 'before_digest', 'after_digest',
|
|
1076
|
-
'provenance_json', 'managed',
|
|
1077
|
-
].every((column) => columns.has(column))) {
|
|
1078
|
-
db.exec(`
|
|
1079
|
-
DROP TABLE IF EXISTS artifacts_registry_upgrade;
|
|
1080
|
-
CREATE TABLE artifacts_registry_upgrade (
|
|
1081
|
-
id TEXT PRIMARY KEY,
|
|
1082
|
-
owner_type TEXT NOT NULL
|
|
1083
|
-
CHECK (owner_type IN ('project', 'baseline', 'change', 'task', 'workflow')),
|
|
1084
|
-
owner_id TEXT NOT NULL,
|
|
1085
|
-
change_id TEXT REFERENCES changes(id) ON DELETE SET NULL,
|
|
1086
|
-
task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
|
|
1087
|
-
kind TEXT NOT NULL,
|
|
1088
|
-
path TEXT NOT NULL,
|
|
1089
|
-
digest TEXT,
|
|
1090
|
-
content_hash TEXT,
|
|
1091
|
-
before_digest TEXT,
|
|
1092
|
-
after_digest TEXT,
|
|
1093
|
-
provenance_json TEXT NOT NULL DEFAULT '{}',
|
|
1094
|
-
metadata_json TEXT,
|
|
1095
|
-
managed INTEGER NOT NULL DEFAULT 0 CHECK (managed IN (0, 1)),
|
|
1096
|
-
status TEXT NOT NULL DEFAULT 'current'
|
|
1097
|
-
CHECK (status IN ('current', 'stale', 'terminal', 'archived')),
|
|
1098
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
1099
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
1100
|
-
UNIQUE(owner_type, owner_id, kind, path)
|
|
1101
|
-
);
|
|
1102
|
-
INSERT INTO artifacts_registry_upgrade
|
|
1103
|
-
(id, owner_type, owner_id, change_id, task_id, kind, path, digest,
|
|
1104
|
-
content_hash, before_digest, after_digest, provenance_json,
|
|
1105
|
-
metadata_json, managed, status, created_at, updated_at)
|
|
1106
|
-
SELECT id,
|
|
1107
|
-
CASE WHEN task_id IS NOT NULL THEN 'task' ELSE 'change' END,
|
|
1108
|
-
CASE WHEN task_id IS NOT NULL THEN task_id ELSE change_id END,
|
|
1109
|
-
change_id, task_id, kind, path, content_hash, content_hash,
|
|
1110
|
-
NULL, content_hash, '{"migration":"schema-20.0"}',
|
|
1111
|
-
metadata_json, 0, status, created_at, updated_at
|
|
1112
|
-
FROM artifacts;
|
|
1113
|
-
DROP TABLE artifacts;
|
|
1114
|
-
ALTER TABLE artifacts_registry_upgrade RENAME TO artifacts;
|
|
1115
|
-
`);
|
|
1116
|
-
changed = true;
|
|
1117
|
-
}
|
|
1118
|
-
if (normalizeStoredArtifactPaths(db)) changed = true;
|
|
1119
|
-
db.exec(`
|
|
1120
|
-
CREATE INDEX IF NOT EXISTS artifacts_change ON artifacts(change_id, kind);
|
|
1121
|
-
CREATE INDEX IF NOT EXISTS artifacts_owner ON artifacts(owner_type, owner_id, status);
|
|
1122
|
-
CREATE INDEX IF NOT EXISTS artifacts_path ON artifacts(path, status);
|
|
1123
|
-
CREATE UNIQUE INDEX IF NOT EXISTS artifacts_one_active_path
|
|
1124
|
-
ON artifacts(path) WHERE status <> 'archived';
|
|
1125
|
-
CREATE TABLE IF NOT EXISTS artifact_edges (
|
|
1126
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1127
|
-
source_type TEXT NOT NULL
|
|
1128
|
-
CHECK (source_type IN ('artifact', 'project', 'baseline', 'change', 'task', 'workflow', 'external')),
|
|
1129
|
-
source_id TEXT NOT NULL,
|
|
1130
|
-
target_type TEXT NOT NULL
|
|
1131
|
-
CHECK (target_type IN ('artifact', 'project', 'baseline', 'change', 'task', 'workflow', 'external')),
|
|
1132
|
-
target_id TEXT NOT NULL,
|
|
1133
|
-
relation TEXT NOT NULL,
|
|
1134
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
1135
|
-
UNIQUE(source_type, source_id, target_type, target_id, relation)
|
|
1136
|
-
);
|
|
1137
|
-
CREATE INDEX IF NOT EXISTS artifact_edges_source
|
|
1138
|
-
ON artifact_edges(source_type, source_id);
|
|
1139
|
-
CREATE INDEX IF NOT EXISTS artifact_edges_target
|
|
1140
|
-
ON artifact_edges(target_type, target_id);
|
|
1141
|
-
`);
|
|
1142
|
-
return changed;
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
function applyCompatibleUpgrades(db, fromVersion = latestSchemaVersion(db), { legacyState = false } = {}) {
|
|
1146
|
-
assertNoDuplicateActiveSessionLeases(db);
|
|
1147
|
-
const runtimeChanged = upgradeRuntimeConstraints(db, fromVersion);
|
|
1148
|
-
if (tableNames(db).includes('baselines') && !baselineSupportsUnbornGit(db)) {
|
|
1149
|
-
db.transaction(() => {
|
|
1150
|
-
applyBaselineUpgrade(db, { legacyState });
|
|
1151
|
-
applyBaselineColumns(db);
|
|
1152
|
-
})();
|
|
1153
|
-
}
|
|
1154
|
-
const gitStateChangedFrom = upgradeBaselineWorktreeConstraint(db, fromVersion);
|
|
1155
|
-
const decisionCompletionChanged = upgradeDecisionThreadLifecycle(db);
|
|
1156
|
-
db.transaction(() => {
|
|
1157
|
-
applyCompatibleColumns(db);
|
|
1158
|
-
applyChangeColumns(db);
|
|
1159
|
-
})();
|
|
1160
|
-
const semanticKernelChanged = upgradeSemanticKernelConstraints(db);
|
|
1161
|
-
db.transaction(() => {
|
|
1162
|
-
applyCompatibleColumns(db);
|
|
1163
|
-
const contextChanged = applyContextSpineUpgrade(db);
|
|
1164
|
-
const contextMigration = db.prepare(
|
|
1165
|
-
"SELECT 1 FROM migration_history WHERE to_version = '10.0' AND status = 'success' LIMIT 1",
|
|
1166
|
-
).get();
|
|
1167
|
-
const needsContextMigration = Boolean(
|
|
1168
|
-
fromVersion && ![
|
|
1169
|
-
CONTEXT_SCHEMA_VERSION, BASELINE_SCHEMA_VERSION, ADOPTION_SCHEMA_VERSION,
|
|
1170
|
-
WORKFLOW_SCHEMA_VERSION, AUTHORITY_SCHEMA_VERSION, SEMANTIC_SCHEMA_VERSION,
|
|
1171
|
-
DIALOGUE_SCHEMA_VERSION, GIT_AUTHORITY_SCHEMA_VERSION, ADAPTIVE_SCHEMA_VERSION,
|
|
1172
|
-
DECISION_COMPLETION_SCHEMA_VERSION, ARTIFACT_REGISTRY_SCHEMA_VERSION,
|
|
1173
|
-
KERNEL_SCHEMA_VERSION, CHECKPOINT_NATIVE_SCHEMA_VERSION,
|
|
1174
|
-
SEMANTIC_KERNEL_SCHEMA_VERSION,
|
|
1175
|
-
].includes(fromVersion)
|
|
1176
|
-
&& !contextMigration,
|
|
1177
|
-
);
|
|
1178
|
-
if (contextChanged || needsContextMigration) {
|
|
1179
|
-
db.prepare(
|
|
1180
|
-
`INSERT INTO migration_history
|
|
1181
|
-
(from_version, to_version, direction, status, notes)
|
|
1182
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1183
|
-
).run(
|
|
1184
|
-
runtimeChanged ? KIMI_SCHEMA_VERSION : (fromVersion || 'unknown'),
|
|
1185
|
-
CONTEXT_SCHEMA_VERSION,
|
|
1186
|
-
'Add Context Spine role/gate readiness, execution contracts, breadcrumbs, and specification learning',
|
|
1187
|
-
);
|
|
1188
|
-
}
|
|
1189
|
-
applyBaselineUpgrade(db, { legacyState });
|
|
1190
|
-
applyBaselineColumns(db);
|
|
1191
|
-
applyChangeColumns(db);
|
|
1192
|
-
applySemanticAuthorityColumns(db);
|
|
1193
|
-
applyKernelIntegrityColumns(db);
|
|
1194
|
-
const artifactRegistryChanged = applyArtifactRegistryUpgrade(db);
|
|
1195
|
-
const baselineMigration = db.prepare(
|
|
1196
|
-
"SELECT 1 FROM migration_history WHERE to_version = '11.0' AND status = 'success' LIMIT 1",
|
|
1197
|
-
).get();
|
|
1198
|
-
if (fromVersion && ![
|
|
1199
|
-
BASELINE_SCHEMA_VERSION, ADOPTION_SCHEMA_VERSION, WORKFLOW_SCHEMA_VERSION,
|
|
1200
|
-
AUTHORITY_SCHEMA_VERSION, SEMANTIC_SCHEMA_VERSION, DIALOGUE_SCHEMA_VERSION,
|
|
1201
|
-
GIT_AUTHORITY_SCHEMA_VERSION, ADAPTIVE_SCHEMA_VERSION,
|
|
1202
|
-
DECISION_COMPLETION_SCHEMA_VERSION, ARTIFACT_REGISTRY_SCHEMA_VERSION,
|
|
1203
|
-
KERNEL_SCHEMA_VERSION, CHECKPOINT_NATIVE_SCHEMA_VERSION,
|
|
1204
|
-
SEMANTIC_KERNEL_SCHEMA_VERSION,
|
|
1205
|
-
].includes(fromVersion)
|
|
1206
|
-
&& !baselineMigration) {
|
|
1207
|
-
db.prepare(
|
|
1208
|
-
`INSERT INTO migration_history
|
|
1209
|
-
(from_version, to_version, direction, status, notes)
|
|
1210
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1211
|
-
).run(
|
|
1212
|
-
fromVersion === CONTEXT_SCHEMA_VERSION ? fromVersion : CONTEXT_SCHEMA_VERSION,
|
|
1213
|
-
BASELINE_SCHEMA_VERSION,
|
|
1214
|
-
'Add authoritative greenfield and brownfield baseline adoption, evidence, convergence, and drift state',
|
|
1215
|
-
);
|
|
1216
|
-
}
|
|
1217
|
-
if (legacyState) {
|
|
1218
|
-
const legacyBaselines = db.prepare(
|
|
1219
|
-
"SELECT id, mode, status, classification_json, gaps_json FROM baselines WHERE status != 'superseded'",
|
|
1220
|
-
).all();
|
|
1221
|
-
for (const baseline of legacyBaselines) {
|
|
1222
|
-
let classification = {};
|
|
1223
|
-
let gaps = [];
|
|
1224
|
-
try { classification = JSON.parse(baseline.classification_json || '{}'); } catch { classification = {}; }
|
|
1225
|
-
try { gaps = JSON.parse(baseline.gaps_json || '[]'); } catch { gaps = []; }
|
|
1226
|
-
if (!gaps.some((gap) => gap?.id === MIGRATED_GAPS[0].id)) gaps.push(MIGRATED_GAPS[0]);
|
|
1227
|
-
classification.migration = {
|
|
1228
|
-
previous_mode: baseline.mode,
|
|
1229
|
-
previous_status: baseline.status,
|
|
1230
|
-
from_schema: fromVersion || 'unknown',
|
|
1231
|
-
requires_research_workflow: true,
|
|
1232
|
-
};
|
|
1233
|
-
db.prepare(
|
|
1234
|
-
`UPDATE baselines SET mode = 'migrated', status = 'adopting',
|
|
1235
|
-
classification_json = ?, gaps_json = ?, approved_by = NULL, approval_note = ?,
|
|
1236
|
-
converged_at = NULL, research_run_id = NULL, research_checkpoint_id = NULL,
|
|
1237
|
-
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
1238
|
-
WHERE id = ?`,
|
|
1239
|
-
).run(
|
|
1240
|
-
JSON.stringify(classification), JSON.stringify(gaps),
|
|
1241
|
-
'Legacy project preserved; evidence-backed brownfield re-adoption is required',
|
|
1242
|
-
baseline.id,
|
|
1243
|
-
);
|
|
1244
|
-
}
|
|
1245
|
-
db.prepare(
|
|
1246
|
-
`UPDATE baselines SET status = 'adopting', approved_by = NULL, approval_note = ?,
|
|
1247
|
-
converged_at = NULL, worktree_state = 'unavailable', worktree_digest = NULL,
|
|
1248
|
-
worktree_files_json = '[]', worktree_accepted = 0,
|
|
1249
|
-
gaps_json = CASE WHEN gaps_json IS NULL OR gaps_json = '[]' THEN ? ELSE gaps_json END,
|
|
1250
|
-
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
1251
|
-
WHERE mode = 'migrated' AND status != 'superseded'`,
|
|
1252
|
-
).run(
|
|
1253
|
-
'Legacy project preserved; owner re-adoption is required',
|
|
1254
|
-
JSON.stringify(MIGRATED_GAPS),
|
|
1255
|
-
);
|
|
1256
|
-
const adoptionMigration = db.prepare(
|
|
1257
|
-
"SELECT 1 FROM migration_history WHERE to_version = '12.0' AND status = 'success' LIMIT 1",
|
|
1258
|
-
).get();
|
|
1259
|
-
if (!adoptionMigration) {
|
|
1260
|
-
db.prepare(
|
|
1261
|
-
`INSERT INTO migration_history
|
|
1262
|
-
(from_version, to_version, direction, status, notes)
|
|
1263
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1264
|
-
).run(
|
|
1265
|
-
BASELINE_SCHEMA_VERSION,
|
|
1266
|
-
ADOPTION_SCHEMA_VERSION,
|
|
1267
|
-
'Add repository snapshots, authoritative gap ledger, incident governance, and required re-adoption',
|
|
1268
|
-
);
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
const workflowMigration = db.prepare(
|
|
1272
|
-
"SELECT 1 FROM migration_history WHERE to_version = '13.0' AND status = 'success' LIMIT 1",
|
|
1273
|
-
).get();
|
|
1274
|
-
if (fromVersion && ![
|
|
1275
|
-
WORKFLOW_SCHEMA_VERSION, AUTHORITY_SCHEMA_VERSION, SEMANTIC_SCHEMA_VERSION,
|
|
1276
|
-
DIALOGUE_SCHEMA_VERSION, GIT_AUTHORITY_SCHEMA_VERSION, ADAPTIVE_SCHEMA_VERSION,
|
|
1277
|
-
DECISION_COMPLETION_SCHEMA_VERSION, ARTIFACT_REGISTRY_SCHEMA_VERSION,
|
|
1278
|
-
KERNEL_SCHEMA_VERSION, CHECKPOINT_NATIVE_SCHEMA_VERSION,
|
|
1279
|
-
SEMANTIC_KERNEL_SCHEMA_VERSION,
|
|
1280
|
-
].includes(fromVersion) && !workflowMigration) {
|
|
1281
|
-
db.prepare(
|
|
1282
|
-
`INSERT INTO migration_history
|
|
1283
|
-
(from_version, to_version, direction, status, notes)
|
|
1284
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1285
|
-
).run(
|
|
1286
|
-
fromVersion === ADOPTION_SCHEMA_VERSION ? fromVersion : ADOPTION_SCHEMA_VERSION,
|
|
1287
|
-
WORKFLOW_SCHEMA_VERSION,
|
|
1288
|
-
'Add durable cross-stage workflow runs, step evidence, output digests, blockers, and recovery',
|
|
1289
|
-
);
|
|
1290
|
-
}
|
|
1291
|
-
const authorityMigration = db.prepare(
|
|
1292
|
-
"SELECT 1 FROM migration_history WHERE to_version = '14.0' AND status = 'success' LIMIT 1",
|
|
1293
|
-
).get();
|
|
1294
|
-
if (fromVersion && ![
|
|
1295
|
-
AUTHORITY_SCHEMA_VERSION, SEMANTIC_SCHEMA_VERSION, DIALOGUE_SCHEMA_VERSION,
|
|
1296
|
-
GIT_AUTHORITY_SCHEMA_VERSION, ADAPTIVE_SCHEMA_VERSION, DECISION_COMPLETION_SCHEMA_VERSION,
|
|
1297
|
-
ARTIFACT_REGISTRY_SCHEMA_VERSION, KERNEL_SCHEMA_VERSION,
|
|
1298
|
-
CHECKPOINT_NATIVE_SCHEMA_VERSION, SEMANTIC_KERNEL_SCHEMA_VERSION,
|
|
1299
|
-
].includes(fromVersion) && !authorityMigration) {
|
|
1300
|
-
db.prepare(
|
|
1301
|
-
`INSERT INTO migration_history
|
|
1302
|
-
(from_version, to_version, direction, status, notes)
|
|
1303
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1304
|
-
).run(
|
|
1305
|
-
fromVersion === WORKFLOW_SCHEMA_VERSION ? fromVersion : WORKFLOW_SCHEMA_VERSION,
|
|
1306
|
-
AUTHORITY_SCHEMA_VERSION,
|
|
1307
|
-
'Persist known-red acceptance and continuously revalidate ready baseline authority',
|
|
1308
|
-
);
|
|
1309
|
-
}
|
|
1310
|
-
const semanticMigration = db.prepare(
|
|
1311
|
-
"SELECT 1 FROM migration_history WHERE to_version = '15.0' AND status = 'success' LIMIT 1",
|
|
1312
|
-
).get();
|
|
1313
|
-
if (fromVersion && ![
|
|
1314
|
-
SEMANTIC_SCHEMA_VERSION, DIALOGUE_SCHEMA_VERSION, GIT_AUTHORITY_SCHEMA_VERSION,
|
|
1315
|
-
ADAPTIVE_SCHEMA_VERSION, DECISION_COMPLETION_SCHEMA_VERSION,
|
|
1316
|
-
ARTIFACT_REGISTRY_SCHEMA_VERSION, KERNEL_SCHEMA_VERSION,
|
|
1317
|
-
CHECKPOINT_NATIVE_SCHEMA_VERSION, SEMANTIC_KERNEL_SCHEMA_VERSION,
|
|
1318
|
-
].includes(fromVersion) && !semanticMigration) {
|
|
1319
|
-
db.prepare(
|
|
1320
|
-
`INSERT INTO migration_history
|
|
1321
|
-
(from_version, to_version, direction, status, notes)
|
|
1322
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1323
|
-
).run(
|
|
1324
|
-
fromVersion === AUTHORITY_SCHEMA_VERSION ? fromVersion : AUTHORITY_SCHEMA_VERSION,
|
|
1325
|
-
SEMANTIC_SCHEMA_VERSION,
|
|
1326
|
-
'Add typed research semantics, complete change contracts, verified learning application, and reconciliation provenance',
|
|
1327
|
-
);
|
|
1328
|
-
}
|
|
1329
|
-
const dialogueMigration = db.prepare(
|
|
1330
|
-
"SELECT 1 FROM migration_history WHERE to_version = '16.0' AND status = 'success' LIMIT 1",
|
|
1331
|
-
).get();
|
|
1332
|
-
if (fromVersion && ![
|
|
1333
|
-
DIALOGUE_SCHEMA_VERSION, GIT_AUTHORITY_SCHEMA_VERSION, ADAPTIVE_SCHEMA_VERSION,
|
|
1334
|
-
DECISION_COMPLETION_SCHEMA_VERSION, ARTIFACT_REGISTRY_SCHEMA_VERSION,
|
|
1335
|
-
KERNEL_SCHEMA_VERSION, CHECKPOINT_NATIVE_SCHEMA_VERSION,
|
|
1336
|
-
SEMANTIC_KERNEL_SCHEMA_VERSION,
|
|
1337
|
-
].includes(fromVersion)
|
|
1338
|
-
&& !dialogueMigration) {
|
|
1339
|
-
db.prepare(
|
|
1340
|
-
`INSERT INTO migration_history
|
|
1341
|
-
(from_version, to_version, direction, status, notes)
|
|
1342
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1343
|
-
).run(
|
|
1344
|
-
fromVersion === SEMANTIC_SCHEMA_VERSION ? fromVersion : SEMANTIC_SCHEMA_VERSION,
|
|
1345
|
-
DIALOGUE_SCHEMA_VERSION,
|
|
1346
|
-
'Add durable one-question decision dialogue, owner checkpoints, and workflow alignment gates',
|
|
1347
|
-
);
|
|
1348
|
-
}
|
|
1349
|
-
const gitStateMigration = db.prepare(
|
|
1350
|
-
"SELECT 1 FROM migration_history WHERE to_version = '17.0' AND status = 'success' LIMIT 1",
|
|
1351
|
-
).get();
|
|
1352
|
-
if (fromVersion && ![
|
|
1353
|
-
GIT_AUTHORITY_SCHEMA_VERSION, ADAPTIVE_SCHEMA_VERSION, DECISION_COMPLETION_SCHEMA_VERSION,
|
|
1354
|
-
ARTIFACT_REGISTRY_SCHEMA_VERSION, KERNEL_SCHEMA_VERSION,
|
|
1355
|
-
CHECKPOINT_NATIVE_SCHEMA_VERSION, SEMANTIC_KERNEL_SCHEMA_VERSION,
|
|
1356
|
-
].includes(fromVersion)
|
|
1357
|
-
&& !gitStateMigration) {
|
|
1358
|
-
db.prepare(
|
|
1359
|
-
`INSERT INTO migration_history
|
|
1360
|
-
(from_version, to_version, direction, status, notes)
|
|
1361
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1362
|
-
).run(
|
|
1363
|
-
gitStateChangedFrom || DIALOGUE_SCHEMA_VERSION,
|
|
1364
|
-
GIT_AUTHORITY_SCHEMA_VERSION,
|
|
1365
|
-
'Add explicit unborn Git authority and require an owner-authorized checkpoint before baseline recording',
|
|
1366
|
-
);
|
|
1367
|
-
}
|
|
1368
|
-
const adaptiveChanged = migrateAdaptiveWorkflowRuns(db, fromVersion);
|
|
1369
|
-
const adaptiveMigration = db.prepare(
|
|
1370
|
-
"SELECT 1 FROM migration_history WHERE to_version = '18.0' AND status = 'success' LIMIT 1",
|
|
1371
|
-
).get();
|
|
1372
|
-
if (fromVersion && ![
|
|
1373
|
-
ADAPTIVE_SCHEMA_VERSION, DECISION_COMPLETION_SCHEMA_VERSION,
|
|
1374
|
-
ARTIFACT_REGISTRY_SCHEMA_VERSION, KERNEL_SCHEMA_VERSION,
|
|
1375
|
-
CHECKPOINT_NATIVE_SCHEMA_VERSION, SEMANTIC_KERNEL_SCHEMA_VERSION,
|
|
1376
|
-
].includes(fromVersion) && !adaptiveMigration) {
|
|
1377
|
-
db.prepare(
|
|
1378
|
-
`INSERT INTO migration_history
|
|
1379
|
-
(from_version, to_version, direction, status, notes)
|
|
1380
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1381
|
-
).run(
|
|
1382
|
-
fromVersion === GIT_AUTHORITY_SCHEMA_VERSION ? fromVersion : GIT_AUTHORITY_SCHEMA_VERSION,
|
|
1383
|
-
ADAPTIVE_SCHEMA_VERSION,
|
|
1384
|
-
adaptiveChanged
|
|
1385
|
-
? 'Migrate active rigid workflows to adaptive capability transitions and independent initialization'
|
|
1386
|
-
: 'Add adaptive capability transitions and independent initialization authority',
|
|
1387
|
-
);
|
|
1388
|
-
}
|
|
1389
|
-
const completionMigration = db.prepare(
|
|
1390
|
-
"SELECT 1 FROM migration_history WHERE to_version = '19.0' AND status = 'success' LIMIT 1",
|
|
1391
|
-
).get();
|
|
1392
|
-
if (fromVersion && ![
|
|
1393
|
-
DECISION_COMPLETION_SCHEMA_VERSION, ARTIFACT_REGISTRY_SCHEMA_VERSION,
|
|
1394
|
-
KERNEL_SCHEMA_VERSION, CHECKPOINT_NATIVE_SCHEMA_VERSION,
|
|
1395
|
-
SEMANTIC_KERNEL_SCHEMA_VERSION,
|
|
1396
|
-
].includes(fromVersion) && !completionMigration) {
|
|
1397
|
-
db.prepare(
|
|
1398
|
-
`INSERT INTO migration_history
|
|
1399
|
-
(from_version, to_version, direction, status, notes)
|
|
1400
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1401
|
-
).run(
|
|
1402
|
-
ADAPTIVE_SCHEMA_VERSION,
|
|
1403
|
-
DECISION_COMPLETION_SCHEMA_VERSION,
|
|
1404
|
-
decisionCompletionChanged
|
|
1405
|
-
? 'Add non-ceremonial decision completion and migrate settled active threads'
|
|
1406
|
-
: 'Add non-ceremonial decision completion authority',
|
|
1407
|
-
);
|
|
1408
|
-
}
|
|
1409
|
-
const artifactRegistryMigration = db.prepare(
|
|
1410
|
-
"SELECT 1 FROM migration_history WHERE to_version = '20.0' AND status = 'success' LIMIT 1",
|
|
1411
|
-
).get();
|
|
1412
|
-
if (fromVersion && fromVersion !== ARTIFACT_REGISTRY_SCHEMA_VERSION
|
|
1413
|
-
&& fromVersion !== KERNEL_SCHEMA_VERSION
|
|
1414
|
-
&& fromVersion !== CHECKPOINT_NATIVE_SCHEMA_VERSION
|
|
1415
|
-
&& fromVersion !== SEMANTIC_KERNEL_SCHEMA_VERSION
|
|
1416
|
-
&& !artifactRegistryMigration) {
|
|
1417
|
-
db.prepare(
|
|
1418
|
-
`INSERT INTO migration_history
|
|
1419
|
-
(from_version, to_version, direction, status, notes)
|
|
1420
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1421
|
-
).run(
|
|
1422
|
-
DECISION_COMPLETION_SCHEMA_VERSION,
|
|
1423
|
-
ARTIFACT_REGISTRY_SCHEMA_VERSION,
|
|
1424
|
-
artifactRegistryChanged
|
|
1425
|
-
? 'Migrate legacy change artifacts into the typed artifact registry and dependency graph'
|
|
1426
|
-
: 'Add the typed artifact registry, dependency graph, and orphan diagnostics',
|
|
1427
|
-
);
|
|
1428
|
-
}
|
|
1429
|
-
const kernelMigration = db.prepare(
|
|
1430
|
-
"SELECT 1 FROM migration_history WHERE to_version = '21.0' AND status = 'success' LIMIT 1",
|
|
1431
|
-
).get();
|
|
1432
|
-
if (fromVersion
|
|
1433
|
-
&& fromVersion !== KERNEL_SCHEMA_VERSION
|
|
1434
|
-
&& fromVersion !== CHECKPOINT_NATIVE_SCHEMA_VERSION
|
|
1435
|
-
&& fromVersion !== SEMANTIC_KERNEL_SCHEMA_VERSION
|
|
1436
|
-
&& !kernelMigration) {
|
|
1437
|
-
db.prepare(
|
|
1438
|
-
`INSERT INTO migration_history
|
|
1439
|
-
(from_version, to_version, direction, status, notes)
|
|
1440
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1441
|
-
).run(
|
|
1442
|
-
ARTIFACT_REGISTRY_SCHEMA_VERSION,
|
|
1443
|
-
KERNEL_SCHEMA_VERSION,
|
|
1444
|
-
'Add the seven-tool persistence kernel, reversible stage checkpoints, canonical context envelopes, normalized decisions, worker packets, and Grok runtime authority',
|
|
1445
|
-
);
|
|
1446
|
-
}
|
|
1447
|
-
const checkpointNativeMigration = db.prepare(
|
|
1448
|
-
"SELECT 1 FROM migration_history WHERE to_version = '22.0' AND status = 'success' LIMIT 1",
|
|
1449
|
-
).get();
|
|
1450
|
-
if (fromVersion
|
|
1451
|
-
&& fromVersion !== CHECKPOINT_NATIVE_SCHEMA_VERSION
|
|
1452
|
-
&& fromVersion !== SEMANTIC_KERNEL_SCHEMA_VERSION
|
|
1453
|
-
&& !checkpointNativeMigration) {
|
|
1454
|
-
db.prepare(
|
|
1455
|
-
`INSERT INTO migration_history
|
|
1456
|
-
(from_version, to_version, direction, status, notes)
|
|
1457
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1458
|
-
).run(
|
|
1459
|
-
KERNEL_SCHEMA_VERSION,
|
|
1460
|
-
CHECKPOINT_NATIVE_SCHEMA_VERSION,
|
|
1461
|
-
'Bind Baseline research to Stage Checkpoints and add byte digests for Context Envelopes and Worker Packets',
|
|
1462
|
-
);
|
|
1463
|
-
}
|
|
1464
|
-
const semanticKernelMigration = db.prepare(
|
|
1465
|
-
"SELECT 1 FROM migration_history WHERE to_version = '23.0' AND status = 'success' LIMIT 1",
|
|
1466
|
-
).get();
|
|
1467
|
-
if (fromVersion
|
|
1468
|
-
&& fromVersion !== SEMANTIC_KERNEL_SCHEMA_VERSION
|
|
1469
|
-
&& !semanticKernelMigration) {
|
|
1470
|
-
db.prepare(
|
|
1471
|
-
`INSERT INTO migration_history
|
|
1472
|
-
(from_version, to_version, direction, status, notes)
|
|
1473
|
-
VALUES (?, ?, 'forward', 'success', ?)`,
|
|
1474
|
-
).run(
|
|
1475
|
-
CHECKPOINT_NATIVE_SCHEMA_VERSION,
|
|
1476
|
-
SEMANTIC_KERNEL_SCHEMA_VERSION,
|
|
1477
|
-
semanticKernelChanged
|
|
1478
|
-
? 'Move Task and Change business vocabulary out of SQLite enums and add immutable Change successor linkage'
|
|
1479
|
-
: 'Record semantic-kernel vocabulary and Change successor authority',
|
|
1480
|
-
);
|
|
1481
|
-
}
|
|
1482
|
-
db.exec('CREATE INDEX IF NOT EXISTS tasks_change ON tasks(change_id) WHERE change_id IS NOT NULL');
|
|
1483
|
-
db.exec('CREATE INDEX IF NOT EXISTS events_change ON events(change_id, id)');
|
|
1484
|
-
ensureActiveSessionLeaseIndex(db);
|
|
1485
|
-
db.prepare(
|
|
1486
|
-
'INSERT OR IGNORE INTO schema_version (version, description) VALUES (?, ?)',
|
|
1487
|
-
).run(
|
|
1488
|
-
EXPECTED_VERSION,
|
|
1489
|
-
'Persistence and safety kernel with open semantic vocabulary and immutable Change successors',
|
|
1490
|
-
);
|
|
1491
|
-
})();
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
function ensureSchemaVersion(db) {
|
|
1495
|
-
// schema_version is an audit trail — multiple rows across phase upgrades.
|
|
1496
|
-
// Guard by checking whether the expected version row exists rather than
|
|
1497
|
-
// relying on applied_at ordering (same-tick inserts from the seed block
|
|
1498
|
-
// make `ORDER BY applied_at DESC LIMIT 1` non-deterministic).
|
|
1499
|
-
const row = db.prepare('SELECT version FROM schema_version WHERE version = ?').get(EXPECTED_VERSION);
|
|
1500
|
-
if (!row) {
|
|
1501
|
-
const latest = db.prepare('SELECT version FROM schema_version ORDER BY applied_at DESC LIMIT 1').get();
|
|
1502
|
-
throw new Error(
|
|
1503
|
-
`state.db schema_version mismatch: expected '${EXPECTED_VERSION}', file has '${latest ? latest.version : '(empty)'}'`,
|
|
1504
|
-
);
|
|
1505
|
-
}
|
|
1506
|
-
return row.version;
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
function initStateDb(dbPath) {
|
|
1510
|
-
let db;
|
|
1511
|
-
let backupPath = null;
|
|
1512
|
-
try {
|
|
1513
|
-
db = openStateDb(dbPath);
|
|
1514
|
-
const existing = new Set(tableNames(db));
|
|
1515
|
-
const fromVersion = latestSchemaVersion(db);
|
|
1516
|
-
assertNoDuplicateActiveArtifactPaths(db);
|
|
1517
|
-
const legacyState = existing.size > 0
|
|
1518
|
-
&& ![
|
|
1519
|
-
WORKFLOW_SCHEMA_VERSION, AUTHORITY_SCHEMA_VERSION, SEMANTIC_SCHEMA_VERSION,
|
|
1520
|
-
DIALOGUE_SCHEMA_VERSION, GIT_AUTHORITY_SCHEMA_VERSION, ADAPTIVE_SCHEMA_VERSION,
|
|
1521
|
-
DECISION_COMPLETION_SCHEMA_VERSION, ARTIFACT_REGISTRY_SCHEMA_VERSION,
|
|
1522
|
-
KERNEL_SCHEMA_VERSION, CHECKPOINT_NATIVE_SCHEMA_VERSION,
|
|
1523
|
-
SEMANTIC_KERNEL_SCHEMA_VERSION,
|
|
1524
|
-
].includes(fromVersion);
|
|
1525
|
-
backupPath = migrationBackup(db, dbPath, fromVersion, existing);
|
|
1526
|
-
// This must precede every ALTER, table rebuild, or index creation. Runtime
|
|
1527
|
-
// constraint upgrades rebuild sessions and would otherwise surface a
|
|
1528
|
-
// generic SQLITE_CONSTRAINT instead of the complete authority conflict.
|
|
1529
|
-
assertNoDuplicateActiveSessionLeases(db);
|
|
1530
|
-
applyCompatibleColumns(db);
|
|
1531
|
-
if (existing.has('artifacts')) {
|
|
1532
|
-
db.transaction(() => applyArtifactRegistryUpgrade(db))();
|
|
1533
|
-
}
|
|
1534
|
-
const missing = REQUIRED_TABLES.filter((t) => !existing.has(t));
|
|
1535
|
-
if (missing.length > 0) {
|
|
1536
|
-
applySchema(db);
|
|
1537
|
-
}
|
|
1538
|
-
applyCompatibleUpgrades(db, fromVersion, { legacyState });
|
|
1539
|
-
const version = ensureSchemaVersion(db);
|
|
1540
|
-
return {
|
|
1541
|
-
db,
|
|
1542
|
-
path: dbPath,
|
|
1543
|
-
schema_version: version,
|
|
1544
|
-
created: missing.length > 0,
|
|
1545
|
-
backup_path: backupPath,
|
|
1546
|
-
tables: tableNames(db).sort(),
|
|
1547
|
-
};
|
|
1548
|
-
} catch (error) {
|
|
1549
|
-
if (backupPath && error && typeof error === 'object') {
|
|
1550
|
-
error.migration_backup_path = backupPath;
|
|
1551
|
-
}
|
|
1552
|
-
closeStateDb(db);
|
|
1553
|
-
throw error;
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
|
|
1557
|
-
function closeStateDb(db) {
|
|
1558
|
-
if (db && typeof db.close === 'function') {
|
|
1559
|
-
db.close();
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
module.exports = {
|
|
1564
|
-
EXPECTED_VERSION,
|
|
1565
|
-
REQUIRED_TABLES,
|
|
1566
|
-
SCHEMA_FILE,
|
|
1567
|
-
openStateDb,
|
|
1568
|
-
applySchema,
|
|
1569
|
-
applyCompatibleUpgrades,
|
|
1570
|
-
applyPragmas,
|
|
1571
|
-
ensureSchemaVersion,
|
|
1572
|
-
initStateDb,
|
|
1573
|
-
closeStateDb,
|
|
1574
|
-
tableNames,
|
|
1575
|
-
runScript,
|
|
1576
|
-
MIGRATED_GAPS,
|
|
1577
|
-
ActiveSessionLeaseConflictError,
|
|
1578
|
-
activeSessionLeaseConflicts,
|
|
1579
|
-
assertNoDuplicateActiveSessionLeases,
|
|
1580
|
-
};
|