From 69d5264360fb1e9b42ce7634603b88e83b1f155a Mon Sep 17 00:00:00 2001 From: lyon Date: Sat, 20 Jun 2026 20:24:48 +0800 Subject: [PATCH] fix: make workbench trace migration idempotent --- .../migrations/0001_cloud_core_skeleton.sql | 31 +++++++++++-------- internal/db/schema.test.ts | 4 +++ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/internal/db/migrations/0001_cloud_core_skeleton.sql b/internal/db/migrations/0001_cloud_core_skeleton.sql index 9f74543e..8dff62e7 100644 --- a/internal/db/migrations/0001_cloud_core_skeleton.sql +++ b/internal/db/migrations/0001_cloud_core_skeleton.sql @@ -391,23 +391,28 @@ INSERT INTO workbench_trace_events ( updated_at ) SELECT - id, - trace_id, - agent_session_id, - trace_id, + source_trace_events.id, + source_trace_events.trace_id, + source_trace_events.agent_session_id, + source_trace_events.trace_id, NULL, - 0, - id, - 0, + source_trace_events.durable_projection_seq, + source_trace_events.id, + source_trace_events.durable_projection_seq, 'agent_trace_event', false, false, - COALESCE(NULLIF(event_json, ''), '{}'), - occurred_at, - occurred_at -FROM agent_trace_events -WHERE id IS NOT NULL AND trace_id IS NOT NULL -ON CONFLICT (id) DO NOTHING; + COALESCE(NULLIF(source_trace_events.event_json, ''), '{}'), + source_trace_events.occurred_at, + source_trace_events.occurred_at +FROM ( + SELECT + agent_trace_events.*, + (ROW_NUMBER() OVER (PARTITION BY trace_id ORDER BY occurred_at, id))::integer AS durable_projection_seq + FROM agent_trace_events + WHERE id IS NOT NULL AND trace_id IS NOT NULL +) AS source_trace_events +ON CONFLICT DO NOTHING; INSERT INTO workbench_turns ( turn_id, diff --git a/internal/db/schema.test.ts b/internal/db/schema.test.ts index 9cdcefd9..528715f7 100644 --- a/internal/db/schema.test.ts +++ b/internal/db/schema.test.ts @@ -80,6 +80,10 @@ test("initial migration declares Workbench fact backfill sources", async () => { assert.match(sql, /ON CONFLICT \(session_id\) DO NOTHING/u); assert.match(sql, /ON CONFLICT \(trace_id\) DO NOTHING/u); + const traceBackfillMatch = sql.match(/INSERT INTO workbench_trace_events \([\s\S]*?ON CONFLICT DO NOTHING;/u); + assert.ok(traceBackfillMatch, "missing workbench_trace_events idempotent backfill"); + assert.match(traceBackfillMatch[0], /ROW_NUMBER\(\) OVER \(PARTITION BY trace_id ORDER BY occurred_at, id\)/u); + assert.match(traceBackfillMatch[0], /durable_projection_seq/u); }); test("protocol record guards catch schema drift before runtime writes", () => {