Tier 1.3 Phase 2: Migrate existing data to spec tables

Implements automatic population of new spec tables from existing milestone/slice/task columns.

Migration function: populateSpecTablesFromExisting()
- Runs during schema v32 migration (first database open)
- Populates milestone_specs from existing milestone table spec columns
- Populates slice_specs from existing slice table spec columns
- Populates task_specs from existing task table spec columns
- Uses INSERT OR IGNORE to safely handle existing data
- Sets spec_version to 1 for all migrated specs
- Uses current timestamp for created_at if missing

Key properties:
- Non-destructive: existing runtime rows preserved
- Idempotent: safe to re-run (INSERT OR IGNORE)
- Evidence tables left empty: populated as tools create new evidence
- Evidence populated retroactively in future phase

This completes Phase 2 of Tier 1.3. Phases 3-5 (data layer updates, tool updates, tests) follow.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Mikael Hugo 2026-05-07 04:22:41 +02:00
parent 87aa04cf05
commit f3761d7f46

View file

@ -899,6 +899,60 @@ function columnExists(db, table, column) {
function ensureColumn(db, table, column, ddl) {
if (!columnExists(db, table, column)) db.exec(ddl);
}
function populateSpecTablesFromExisting(db) {
// Tier 1.3 Phase 2: Migrate existing spec data to new spec tables
// This populates milestone_specs, slice_specs, task_specs from existing columns
// Evidence tables are left empty; they populate as tools create new evidence.
const now = new Date().toISOString();
// Migrate milestone specs
db.prepare(`
INSERT OR IGNORE INTO milestone_specs (
id, vision, success_criteria, key_risks, proof_strategy,
verification_contract, verification_integration, verification_operational, verification_uat,
definition_of_done, requirement_coverage, boundary_map_markdown, vision_meeting_json,
spec_version, created_at
)
SELECT
id, vision, success_criteria, key_risks, proof_strategy,
verification_contract, verification_integration, verification_operational, verification_uat,
definition_of_done, requirement_coverage, boundary_map_markdown, vision_meeting_json,
1, COALESCE(created_at, ?)
FROM milestones
WHERE id NOT IN (SELECT id FROM milestone_specs)
`).run(now);
// Migrate slice specs
db.prepare(`
INSERT OR IGNORE INTO slice_specs (
milestone_id, slice_id, goal, success_criteria, proof_level,
integration_closure, observability_impact,
adversarial_partner, adversarial_combatant, adversarial_architect,
planning_meeting_json, spec_version, created_at
)
SELECT
milestone_id, id, goal, success_criteria, proof_level,
integration_closure, observability_impact,
adversarial_partner, adversarial_combatant, adversarial_architect,
planning_meeting_json, 1, COALESCE(created_at, ?)
FROM slices
WHERE (milestone_id, id) NOT IN (SELECT milestone_id, slice_id FROM slice_specs)
`).run(now);
// Migrate task specs
db.prepare(`
INSERT OR IGNORE INTO task_specs (
milestone_id, slice_id, task_id, verify, inputs, expected_output,
spec_version, created_at
)
SELECT
milestone_id, slice_id, id, verify, inputs, expected_output,
1, COALESCE(created_at, ?)
FROM tasks
WHERE (milestone_id, slice_id, id) NOT IN (SELECT milestone_id, slice_id, task_id FROM task_specs)
`).run(now);
}
function migrateSchema(db) {
const row = db.prepare("SELECT MAX(version) as v FROM schema_version").get();
const currentVersion = row ? row["v"] : 0;
@ -1828,6 +1882,8 @@ function migrateSchema(db) {
}
if (currentVersion < 32) {
ensureSpecSchemaTables(db);
// Populate spec tables from existing spec columns in runtime tables
populateSpecTablesFromExisting(db);
db.prepare(
"INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)",
).run({