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
2 changes: 2 additions & 0 deletions apps/server/src/persistence/Migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import Migration0059 from "./Migrations/059_McpAppModelContext.ts";
import Migration0060 from "./Migrations/060_ThreadSnapshotWindowIndexes.ts";
import Migration0061 from "./Migrations/061_PullRequestReviewGuides.ts";
import Migration0062 from "./Migrations/062_ThreadFailureLookupIndex.ts";
import Migration0063 from "./Migrations/063_DropUnusedV1Projections.ts";

export const migrationEntries = [
[1, "OrchestrationEvents", Migration0001],
Expand Down Expand Up @@ -130,6 +131,7 @@ export const migrationEntries = [
[60, "ThreadSnapshotWindowIndexes", Migration0060],
[61, "PullRequestReviewGuides", Migration0061],
[62, "ThreadFailureLookupIndex", Migration0062],
[63, "DropUnusedV1Projections", Migration0063],
] as const;

export const migrationManifest = migrationEntries.map(([id, name]) => [id, name] as const);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ layer("055_OrchestrationV2", (it) => {
Effect.sync(() => {
assert.deepStrictEqual(
migrationEntries.map(([id]) => id),
Array.from({ length: 62 }, (_, index) => index + 1),
Array.from({ length: 63 }, (_, index) => index + 1),
);
}),
);
Expand All @@ -34,6 +34,7 @@ layer("055_OrchestrationV2", (it) => {
[60, "ThreadSnapshotWindowIndexes"],
[61, "PullRequestReviewGuides"],
[62, "ThreadFailureLookupIndex"],
[63, "DropUnusedV1Projections"],
]);
assert.deepStrictEqual(yield* runMigrations(), []);

Expand Down Expand Up @@ -62,6 +63,7 @@ layer("055_OrchestrationV2", (it) => {
{ migration_id: 60, name: "ThreadSnapshotWindowIndexes" },
{ migration_id: 61, name: "PullRequestReviewGuides" },
{ migration_id: 62, name: "ThreadFailureLookupIndex" },
{ migration_id: 63, name: "DropUnusedV1Projections" },
]);

const tables = yield* sql<{ readonly name: string }>`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { assert, it } from "@effect/vitest";
import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient";
import * as Effect from "effect/Effect";
import * as SqlClient from "effect/sql/SqlClient";

import { runMigrations } from "../Migrations.ts";

const layer = it.layer(NodeSqliteClient.layer({ filename: ":memory:" }));

layer("063_DropUnusedV1Projections", (it) => {
it.effect("drops V1 projections nothing reads and keeps the transcript import sources", () =>
Effect.gen(function* () {
const sql = yield* SqlClient.SqlClient;
yield* runMigrations({ toMigrationInclusive: 62 });
yield* sql`
INSERT INTO projection_thread_messages
(message_id, thread_id, role, text, is_streaming, created_at, updated_at)
VALUES ('message-1', 'thread-1', 'user', 'hello', 0, '2026-01-01', '2026-01-01')
`;

yield* runMigrations({ toMigrationInclusive: 63 });

const rows = yield* sql<{ readonly name: string }>`
SELECT name FROM sqlite_master WHERE type = 'table'
`;
const tables = new Set(rows.map((row) => row.name));
for (const dropped of [
"projection_thread_activities",
"projection_turns",
"projection_thread_proposed_plans",
"projection_pending_approvals",
"projection_state",
"checkpoint_diff_blobs",
]) {
assert.isFalse(tables.has(dropped), dropped);
}
for (const kept of [
"projection_threads",
"projection_thread_messages",
"projection_thread_pull_requests",
]) {
assert.isTrue(tables.has(kept), kept);
}
assert.deepEqual(yield* sql`SELECT message_id FROM projection_thread_messages`, [
{ message_id: "message-1" },
]);
}),
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as Effect from "effect/Effect";
import * as SqlClient from "effect/sql/SqlClient";

/**
* V2 databases start as a copy of the V1 database, and nothing reads these V1
* projections after the copy. The V1 transcript import still reads threads,
* messages and pull request links, so those stay. Builds before 0.1.0 read
* `state.sqlite`, never this file, so dropping them does not affect a rollback.
* The freed pages are reported as reclaimable space for `t3 doctor`.
*/
export default Effect.gen(function* () {
const sql = yield* SqlClient.SqlClient;

yield* sql`DROP TABLE IF EXISTS projection_thread_activities`;
yield* sql`DROP TABLE IF EXISTS projection_turns`;
yield* sql`DROP TABLE IF EXISTS projection_thread_proposed_plans`;
yield* sql`DROP TABLE IF EXISTS projection_pending_approvals`;
yield* sql`DROP TABLE IF EXISTS projection_state`;
yield* sql`DROP TABLE IF EXISTS checkpoint_diff_blobs`;
});
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ describe("V2 preview upgrade", () => {
[60, "ThreadSnapshotWindowIndexes"],
[61, "PullRequestReviewGuides"],
[62, "ThreadFailureLookupIndex"],
[63, "DropUnusedV1Projections"],
]);
assert.deepStrictEqual(yield* runMigrations(), []);
assert.deepStrictEqual(yield* sql`SELECT * FROM orchestration_v2_legacy_imports`, imports);
Expand Down Expand Up @@ -127,6 +128,7 @@ describe("V2 preview upgrade", () => {
[60, "ThreadSnapshotWindowIndexes"],
[61, "PullRequestReviewGuides"],
[62, "ThreadFailureLookupIndex"],
[63, "DropUnusedV1Projections"],
]);
}).pipe(Effect.provide(NodeSqliteClient.layer({ filename: ":memory:" }))),
);
Expand Down
90 changes: 0 additions & 90 deletions scripts/mobile-showcase-environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,6 @@ export const SHOWCASE_SCENES = [
] as const;
export type ShowcaseScene = (typeof SHOWCASE_SCENES)[number];

const PROJECTOR_NAMES = [
"projection.projects",
"projection.threads",
"projection.thread-messages",
"projection.thread-proposed-plans",
"projection.thread-activities",
"projection.thread-sessions",
"projection.thread-turns",
"projection.checkpoints",
"projection.pending-approvals",
] as const;

const MODEL_SELECTION = JSON.stringify({ instanceId: "codex", model: "gpt-5.4" });
const PROJECT_SCRIPTS = JSON.stringify([
{
Expand Down Expand Up @@ -392,23 +380,6 @@ function insertThread(
snoozedUntil,
snoozedAt,
);
database
.prepare(
`INSERT INTO projection_turns (
thread_id, turn_id, pending_message_id, assistant_message_id, state, requested_at,
started_at, completed_at, checkpoint_turn_count, checkpoint_ref, checkpoint_status,
checkpoint_files_json, source_proposed_plan_thread_id, source_proposed_plan_id
) VALUES (?, ?, NULL, ?, ?, ?, ?, ?, NULL, NULL, NULL, '[]', NULL, NULL)`,
)
.run(
input.id,
turnId,
isWorking ? null : `${input.id}-answer`,
isWorking ? "running" : "completed",
minutesBefore(now, input.minutesAgo + 2),
minutesBefore(now, input.minutesAgo + 2),
isWorking ? null : updatedAt,
);
database
.prepare(
`INSERT INTO projection_thread_sessions (
Expand All @@ -424,14 +395,9 @@ function insertThread(
// do not read the V1 thread rows; moving the seed to V2 is tracked in
// https://github.com/pingdotgg/t3code/issues/15013.
const SEEDED_V1_TABLES = [
"projection_pending_approvals",
"projection_thread_proposed_plans",
"projection_thread_activities",
"projection_thread_messages",
"projection_thread_sessions",
"projection_turns",
"projection_threads",
"projection_state",
] as const;

const SEEDED_PROJECTION_TABLES = [...SEEDED_V1_TABLES, "projection_projects"] as const;
Expand Down Expand Up @@ -558,62 +524,6 @@ function seedDatabase(
}
}

const turnId = `${SHOWCASE_THREAD_ID}-turn`;
const insertActivity = database.prepare(
`INSERT INTO projection_thread_activities (
activity_id, thread_id, turn_id, tone, kind, summary, payload_json, sequence, created_at
) VALUES (?, ?, ?, 'tool', 'tool.completed', ?, ?, ?, ?)`,
);
insertActivity.run(
"trace-remote-handoff",
SHOWCASE_THREAD_ID,
turnId,
"Traced the remote handoff path",
JSON.stringify({
itemType: "command_execution",
title: "Traced the remote handoff path",
detail: "Three environments, one continuous workspace",
status: "completed",
}),
1,
minutesBefore(now, 8),
);
insertActivity.run(
"sync-command-center",
SHOWCASE_THREAD_ID,
turnId,
"Synced the command center",
JSON.stringify({
itemType: "file_change",
title: "Synced the command center",
detail: "2 files changed · instant handoffs · calm reconnects",
status: "completed",
}),
2,
minutesBefore(now, 6),
);
insertActivity.run(
"run-changed-suite",
SHOWCASE_THREAD_ID,
turnId,
"Ran the changed workspace",
JSON.stringify({
itemType: "command_execution",
title: "Ran the changed workspace",
detail: "612 tests passed · 3 environments online",
status: "completed",
}),
3,
minutesBefore(now, 4),
);

for (const [index, projector] of PROJECTOR_NAMES.entries()) {
database
.prepare(
"INSERT INTO projection_state (projector, last_applied_sequence, updated_at) VALUES (?, ?, ?)",
)
.run(projector, index + 1, minutesBefore(now, 1));
}
database.exec("COMMIT");
} catch (error) {
// A failed BEGIN (or an error SQLite already auto-rolled back) leaves no
Expand Down
Loading