Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions apps/daemon/src/core/db/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export function runMigrations(db: BazilionDb): void {

const sql = readFileSync(join(migrationsDir, file), 'utf8')
const tx = db.raw.transaction(() => {
if (version === '0013_harness_slot_layout') ensureHarnessSlotLayout(db)
db.raw.exec(sql)
db.raw.run('INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)', [
version,
Expand All @@ -39,3 +40,21 @@ export function runMigrations(db: BazilionDb): void {
tx()
}
}

function ensureHarnessSlotLayout(db: BazilionDb): void {
for (const table of ['harness_template_slots', 'harness_template_revision_slots']) {
const existing = new Set(
db.raw
.query<{ name: string }, []>(`PRAGMA table_info(${table})`)
.all()
.map((column) => column.name),
)
for (const [name, type] of [
['position_x', 'REAL'],
['position_y', 'REAL'],
['display_json', 'TEXT'],
] as const) {
if (!existing.has(name)) db.raw.exec(`ALTER TABLE ${table} ADD COLUMN ${name} ${type}`)
}
}
}
15 changes: 3 additions & 12 deletions apps/daemon/src/core/db/migrations/0009_canonical_harness.sql
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,7 @@ CREATE TABLE harness_template_slots (
agent_name TEXT NOT NULL,
model_override TEXT,
reasoning_level TEXT CHECK (reasoning_level IS NULL OR reasoning_level IN ('off','minimal','low','medium','high','xhigh')),
position_x REAL,
position_y REAL,
display_json TEXT,
tombstoned_at INTEGER,
CHECK ((position_x IS NULL) = (position_y IS NULL)),
PRIMARY KEY (template_id, slot_id)
);
CREATE UNIQUE INDEX harness_template_active_position
Expand Down Expand Up @@ -74,10 +70,6 @@ CREATE TABLE harness_template_revision_slots (
agent_name TEXT NOT NULL,
model_override TEXT,
reasoning_level TEXT CHECK (reasoning_level IS NULL OR reasoning_level IN ('off','minimal','low','medium','high','xhigh')),
position_x REAL,
position_y REAL,
display_json TEXT,
CHECK ((position_x IS NULL) = (position_y IS NULL)),
PRIMARY KEY (template_id, revision, slot_id),
UNIQUE (template_id, revision, position),
FOREIGN KEY (template_id, revision)
Expand Down Expand Up @@ -238,13 +230,13 @@ FROM profile_groups;

INSERT INTO harness_template_slots
(template_id, slot_id, position, profile_id, agent_name, model_override, reasoning_level,
position_x, position_y, display_json, tombstoned_at)
tombstoned_at)
SELECT profile_group_id,
lower(hex(randomblob(4))) || '-' || lower(hex(randomblob(2))) || '-4' ||
substr(lower(hex(randomblob(2))), 2) || '-' ||
substr('89ab', abs(random()) % 4 + 1, 1) || substr(lower(hex(randomblob(2))), 2) || '-' ||
lower(hex(randomblob(6))),
position, profile_id, agent_name, model_override, reasoning_level, NULL, NULL, NULL, NULL
position, profile_id, agent_name, model_override, reasoning_level, NULL
FROM profile_group_members;

-- Exact Open Team current policy: every distinct slot pair and four boundary edges per slot.
Expand All @@ -264,8 +256,7 @@ SELECT template_id, 'slot', slot_id, 'outside_group', '' FROM harness_template_s
INSERT INTO harness_template_revisions (template_id, revision, name, user_md, created_at)
SELECT id, 1, name, user_md, updated_at FROM harness_templates;
INSERT INTO harness_template_revision_slots
SELECT template_id, 1, slot_id, position, profile_id, agent_name, model_override, reasoning_level,
position_x, position_y, display_json
SELECT template_id, 1, slot_id, position, profile_id, agent_name, model_override, reasoning_level
FROM harness_template_slots WHERE tombstoned_at IS NULL;
INSERT INTO harness_template_revision_edges
SELECT template_id, 1, source_kind, source_id, target_kind, target_id
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- BAZ-012 added optional visual layout metadata to Team-template slots after
-- 0009 had shipped. The migration runner adds each column only when absent so
-- it can converge both older upgraded databases and databases created during
-- the interval in which 0009 incorrectly contained the later columns.
35 changes: 35 additions & 0 deletions apps/daemon/test/core/migrations.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { afterAll, beforeAll, expect, test } from 'vitest'
import { openInMemoryDb } from '../../src/core/db/client.ts'
import { runMigrations } from '../../src/core/db/migrate.ts'
import { makeTestEnv, type TestEnv } from './helpers.ts'

Expand Down Expand Up @@ -86,6 +87,40 @@ test('migrations are idempotent', () => {
expect(after[0]?.version).toBe('0001_init')
})

test('0013 repairs slot layout columns missing from an already-applied 0009', () => {
const db = openInMemoryDb()
try {
runMigrations(db)
for (const table of ['harness_template_slots', 'harness_template_revision_slots']) {
db.raw.exec(`ALTER TABLE ${table} DROP COLUMN display_json`)
db.raw.exec(`ALTER TABLE ${table} DROP COLUMN position_y`)
db.raw.exec(`ALTER TABLE ${table} DROP COLUMN position_x`)
}
db.raw.run("DELETE FROM schema_migrations WHERE version = '0013_harness_slot_layout'")

runMigrations(db)

for (const table of ['harness_template_slots', 'harness_template_revision_slots']) {
const columns = db.raw
.query<{ name: string }, []>(`PRAGMA table_info(${table})`)
.all()
.map((column) => column.name)
expect(columns).toEqual(
expect.arrayContaining(['position_x', 'position_y', 'display_json']),
)
}
expect(
db.raw
.query<{ count: number }, []>(
"SELECT COUNT(*) count FROM schema_migrations WHERE version = '0013_harness_slot_layout'",
)
.get()?.count,
).toBe(1)
} finally {
db.close()
}
})

test('nested transactions use savepoints without weakening outer rollback', () => {
env.db.raw.transaction(() => {
env.db.raw.run("INSERT INTO config (key, value, updated_at) VALUES ('outer', '1', 1)")
Expand Down
Loading