From 6bb5ca528e420a32e4cc844cc47972ac09ab50fb Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Thu, 12 Mar 2026 21:22:39 -0700 Subject: [PATCH] Tighten actor schema constraints --- .../backend/src/actors/history/db/schema.ts | 1 + .../project/db/drizzle/0004_slimy_proteus.sql | 36 ++ .../db/drizzle/meta/0004_snapshot.json | 265 ++++++++ .../project/db/drizzle/meta/_journal.json | 7 + .../src/actors/project/db/migrations.ts | 45 +- .../backend/src/actors/project/db/schema.ts | 8 +- .../db/drizzle/0002_common_betty_ross.sql | 20 + .../db/drizzle/meta/0002_snapshot.json | 180 ++++++ .../db/drizzle/meta/_journal.json | 7 + .../actors/sandbox-instance/db/migrations.ts | 27 + .../src/actors/sandbox-instance/db/schema.ts | 27 +- .../db/drizzle/0006_harsh_wind_dancer.sql | 49 ++ .../task/db/drizzle/meta/0006_snapshot.json | 334 ++++++++++ .../actors/task/db/drizzle/meta/_journal.json | 7 + .../backend/src/actors/task/db/migrations.ts | 44 +- .../backend/src/actors/task/db/schema.ts | 54 +- .../db/drizzle/0003_mushy_rictor.sql | 84 +++ .../db/drizzle/meta/0003_snapshot.json | 605 ++++++++++++++++++ .../workspace/db/drizzle/meta/_journal.json | 7 + .../src/actors/workspace/db/migrations.ts | 174 ++--- .../backend/src/actors/workspace/db/schema.ts | 2 + 21 files changed, 1832 insertions(+), 151 deletions(-) create mode 100644 foundry/packages/backend/src/actors/project/db/drizzle/0004_slimy_proteus.sql create mode 100644 foundry/packages/backend/src/actors/project/db/drizzle/meta/0004_snapshot.json create mode 100644 foundry/packages/backend/src/actors/sandbox-instance/db/drizzle/0002_common_betty_ross.sql create mode 100644 foundry/packages/backend/src/actors/sandbox-instance/db/drizzle/meta/0002_snapshot.json create mode 100644 foundry/packages/backend/src/actors/task/db/drizzle/0006_harsh_wind_dancer.sql create mode 100644 foundry/packages/backend/src/actors/task/db/drizzle/meta/0006_snapshot.json create mode 100644 foundry/packages/backend/src/actors/workspace/db/drizzle/0003_mushy_rictor.sql create mode 100644 foundry/packages/backend/src/actors/workspace/db/drizzle/meta/0003_snapshot.json diff --git a/foundry/packages/backend/src/actors/history/db/schema.ts b/foundry/packages/backend/src/actors/history/db/schema.ts index d015872..80eb7f4 100644 --- a/foundry/packages/backend/src/actors/history/db/schema.ts +++ b/foundry/packages/backend/src/actors/history/db/schema.ts @@ -5,6 +5,7 @@ export const events = sqliteTable("events", { taskId: text("task_id"), branchName: text("branch_name"), kind: text("kind").notNull(), + // Structured by the history event kind definitions in application code. payloadJson: text("payload_json").notNull(), createdAt: integer("created_at").notNull(), }); diff --git a/foundry/packages/backend/src/actors/project/db/drizzle/0004_slimy_proteus.sql b/foundry/packages/backend/src/actors/project/db/drizzle/0004_slimy_proteus.sql new file mode 100644 index 0000000..ab846d2 --- /dev/null +++ b/foundry/packages/backend/src/actors/project/db/drizzle/0004_slimy_proteus.sql @@ -0,0 +1,36 @@ +PRAGMA foreign_keys=OFF;--> statement-breakpoint +CREATE TABLE `__new_branches` ( + `branch_name` text PRIMARY KEY NOT NULL, + `commit_sha` text NOT NULL, + `parent_branch` text, + `tracked_in_stack` integer DEFAULT 0 NOT NULL, + `diff_stat` text, + `has_unpushed` integer DEFAULT 0 NOT NULL, + `conflicts_with_main` integer DEFAULT 0 NOT NULL, + `first_seen_at` integer, + `last_seen_at` integer, + `updated_at` integer NOT NULL +); +--> statement-breakpoint +INSERT INTO `__new_branches`("branch_name", "commit_sha", "parent_branch", "tracked_in_stack", "diff_stat", "has_unpushed", "conflicts_with_main", "first_seen_at", "last_seen_at", "updated_at") SELECT "branch_name", "commit_sha", "parent_branch", "tracked_in_stack", "diff_stat", "has_unpushed", "conflicts_with_main", "first_seen_at", "last_seen_at", "updated_at" FROM `branches`;--> statement-breakpoint +DROP TABLE `branches`;--> statement-breakpoint +ALTER TABLE `__new_branches` RENAME TO `branches`;--> statement-breakpoint +PRAGMA foreign_keys=ON;--> statement-breakpoint +CREATE TABLE `__new_pr_cache` ( + `branch_name` text PRIMARY KEY NOT NULL, + `pr_number` integer NOT NULL, + `state` text NOT NULL, + `title` text NOT NULL, + `pr_url` text, + `pr_author` text, + `is_draft` integer DEFAULT 0 NOT NULL, + `ci_status` text, + `review_status` text, + `reviewer` text, + `fetched_at` integer, + `updated_at` integer NOT NULL +); +--> statement-breakpoint +INSERT INTO `__new_pr_cache`("branch_name", "pr_number", "state", "title", "pr_url", "pr_author", "is_draft", "ci_status", "review_status", "reviewer", "fetched_at", "updated_at") SELECT "branch_name", "pr_number", "state", "title", "pr_url", "pr_author", "is_draft", "ci_status", "review_status", "reviewer", "fetched_at", "updated_at" FROM `pr_cache`;--> statement-breakpoint +DROP TABLE `pr_cache`;--> statement-breakpoint +ALTER TABLE `__new_pr_cache` RENAME TO `pr_cache`; \ No newline at end of file diff --git a/foundry/packages/backend/src/actors/project/db/drizzle/meta/0004_snapshot.json b/foundry/packages/backend/src/actors/project/db/drizzle/meta/0004_snapshot.json new file mode 100644 index 0000000..8bada08 --- /dev/null +++ b/foundry/packages/backend/src/actors/project/db/drizzle/meta/0004_snapshot.json @@ -0,0 +1,265 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "84e0ba9e-de6e-404e-8459-e01f44add7a1", + "prevId": "ac89870f-1630-4a16-9606-7b1225f6da8a", + "tables": { + "branches": { + "name": "branches", + "columns": { + "branch_name": { + "name": "branch_name", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "parent_branch": { + "name": "parent_branch", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tracked_in_stack": { + "name": "tracked_in_stack", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "diff_stat": { + "name": "diff_stat", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "has_unpushed": { + "name": "has_unpushed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "conflicts_with_main": { + "name": "conflicts_with_main", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "first_seen_at": { + "name": "first_seen_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "pr_cache": { + "name": "pr_cache", + "columns": { + "branch_name": { + "name": "branch_name", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "pr_number": { + "name": "pr_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "pr_url": { + "name": "pr_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pr_author": { + "name": "pr_author", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_draft": { + "name": "is_draft", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "ci_status": { + "name": "ci_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_status": { + "name": "review_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reviewer": { + "name": "reviewer", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "fetched_at": { + "name": "fetched_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repo_meta": { + "name": "repo_meta", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "remote_url": { + "name": "remote_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "task_index": { + "name": "task_index", + "columns": { + "task_id": { + "name": "task_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "branch_name": { + "name": "branch_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} diff --git a/foundry/packages/backend/src/actors/project/db/drizzle/meta/_journal.json b/foundry/packages/backend/src/actors/project/db/drizzle/meta/_journal.json index 1a5b47d..4490b46 100644 --- a/foundry/packages/backend/src/actors/project/db/drizzle/meta/_journal.json +++ b/foundry/packages/backend/src/actors/project/db/drizzle/meta/_journal.json @@ -29,6 +29,13 @@ "when": 1771369000000, "tag": "0003_busy_legacy", "breakpoints": true + }, + { + "idx": 4, + "version": "6", + "when": 1773375722282, + "tag": "0004_slimy_proteus", + "breakpoints": true } ] } diff --git a/foundry/packages/backend/src/actors/project/db/migrations.ts b/foundry/packages/backend/src/actors/project/db/migrations.ts index 795fd67..df62a58 100644 --- a/foundry/packages/backend/src/actors/project/db/migrations.ts +++ b/foundry/packages/backend/src/actors/project/db/migrations.ts @@ -28,6 +28,12 @@ const journal = { tag: "0003_busy_legacy", breakpoints: true, }, + { + idx: 4, + when: 1773375722282, + tag: "0004_slimy_proteus", + breakpoints: true, + }, ], } as const; @@ -76,6 +82,43 @@ ALTER TABLE \`branches\` DROP COLUMN \`worktree_path\`;`, \`updated_at\` integer NOT NULL ); `, - m0003: `ALTER TABLE \`branches\` ADD \`tracked_in_stack\` integer;`, + m0003: `ALTER TABLE \`branches\` ADD \`tracked_in_stack\` integer; +`, + m0004: `PRAGMA foreign_keys=OFF;--> statement-breakpoint +CREATE TABLE \`__new_branches\` ( + \`branch_name\` text PRIMARY KEY NOT NULL, + \`commit_sha\` text NOT NULL, + \`parent_branch\` text, + \`tracked_in_stack\` integer DEFAULT 0 NOT NULL, + \`diff_stat\` text, + \`has_unpushed\` integer DEFAULT 0 NOT NULL, + \`conflicts_with_main\` integer DEFAULT 0 NOT NULL, + \`first_seen_at\` integer, + \`last_seen_at\` integer, + \`updated_at\` integer NOT NULL +); +--> statement-breakpoint +INSERT INTO \`__new_branches\`("branch_name", "commit_sha", "parent_branch", "tracked_in_stack", "diff_stat", "has_unpushed", "conflicts_with_main", "first_seen_at", "last_seen_at", "updated_at") SELECT "branch_name", "commit_sha", "parent_branch", "tracked_in_stack", "diff_stat", "has_unpushed", "conflicts_with_main", "first_seen_at", "last_seen_at", "updated_at" FROM \`branches\`;--> statement-breakpoint +DROP TABLE \`branches\`;--> statement-breakpoint +ALTER TABLE \`__new_branches\` RENAME TO \`branches\`;--> statement-breakpoint +PRAGMA foreign_keys=ON;--> statement-breakpoint +CREATE TABLE \`__new_pr_cache\` ( + \`branch_name\` text PRIMARY KEY NOT NULL, + \`pr_number\` integer NOT NULL, + \`state\` text NOT NULL, + \`title\` text NOT NULL, + \`pr_url\` text, + \`pr_author\` text, + \`is_draft\` integer DEFAULT 0 NOT NULL, + \`ci_status\` text, + \`review_status\` text, + \`reviewer\` text, + \`fetched_at\` integer, + \`updated_at\` integer NOT NULL +); +--> statement-breakpoint +INSERT INTO \`__new_pr_cache\`("branch_name", "pr_number", "state", "title", "pr_url", "pr_author", "is_draft", "ci_status", "review_status", "reviewer", "fetched_at", "updated_at") SELECT "branch_name", "pr_number", "state", "title", "pr_url", "pr_author", "is_draft", "ci_status", "review_status", "reviewer", "fetched_at", "updated_at" FROM \`pr_cache\`;--> statement-breakpoint +DROP TABLE \`pr_cache\`;--> statement-breakpoint +ALTER TABLE \`__new_pr_cache\` RENAME TO \`pr_cache\`;`, } as const, }; diff --git a/foundry/packages/backend/src/actors/project/db/schema.ts b/foundry/packages/backend/src/actors/project/db/schema.ts index 2f7bfda..b72cbfc 100644 --- a/foundry/packages/backend/src/actors/project/db/schema.ts +++ b/foundry/packages/backend/src/actors/project/db/schema.ts @@ -6,10 +6,10 @@ export const branches = sqliteTable("branches", { branchName: text("branch_name").notNull().primaryKey(), commitSha: text("commit_sha").notNull(), parentBranch: text("parent_branch"), - trackedInStack: integer("tracked_in_stack"), + trackedInStack: integer("tracked_in_stack").notNull().default(0), diffStat: text("diff_stat"), - hasUnpushed: integer("has_unpushed"), - conflictsWithMain: integer("conflicts_with_main"), + hasUnpushed: integer("has_unpushed").notNull().default(0), + conflictsWithMain: integer("conflicts_with_main").notNull().default(0), firstSeenAt: integer("first_seen_at"), lastSeenAt: integer("last_seen_at"), updatedAt: integer("updated_at").notNull(), @@ -28,7 +28,7 @@ export const prCache = sqliteTable("pr_cache", { title: text("title").notNull(), prUrl: text("pr_url"), prAuthor: text("pr_author"), - isDraft: integer("is_draft"), + isDraft: integer("is_draft").notNull().default(0), ciStatus: text("ci_status"), reviewStatus: text("review_status"), reviewer: text("reviewer"), diff --git a/foundry/packages/backend/src/actors/sandbox-instance/db/drizzle/0002_common_betty_ross.sql b/foundry/packages/backend/src/actors/sandbox-instance/db/drizzle/0002_common_betty_ross.sql new file mode 100644 index 0000000..c0e8933 --- /dev/null +++ b/foundry/packages/backend/src/actors/sandbox-instance/db/drizzle/0002_common_betty_ross.sql @@ -0,0 +1,20 @@ +CREATE TABLE `sandbox_session_events` ( + `id` text PRIMARY KEY NOT NULL, + `session_id` text NOT NULL, + `event_index` integer NOT NULL, + `created_at` integer NOT NULL, + `connection_id` text NOT NULL, + `sender` text NOT NULL, + `payload_json` text NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `sandbox_session_events_session_id_event_index_unique` ON `sandbox_session_events` (`session_id`,`event_index`);--> statement-breakpoint +CREATE TABLE `sandbox_sessions` ( + `id` text PRIMARY KEY NOT NULL, + `agent` text NOT NULL, + `agent_session_id` text NOT NULL, + `last_connection_id` text NOT NULL, + `created_at` integer NOT NULL, + `destroyed_at` integer, + `session_init_json` text +); diff --git a/foundry/packages/backend/src/actors/sandbox-instance/db/drizzle/meta/0002_snapshot.json b/foundry/packages/backend/src/actors/sandbox-instance/db/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..df50425 --- /dev/null +++ b/foundry/packages/backend/src/actors/sandbox-instance/db/drizzle/meta/0002_snapshot.json @@ -0,0 +1,180 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "ff87c112-7c8f-4e99-b2a8-2867003e0e4e", + "prevId": "ef8a919c-64f0-46d9-b8ed-a15f039e6ba7", + "tables": { + "sandbox_instance": { + "name": "sandbox_instance", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "metadata_json": { + "name": "metadata_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sandbox_session_events": { + "name": "sandbox_session_events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_index": { + "name": "event_index", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sender": { + "name": "sender", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload_json": { + "name": "payload_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "sandbox_session_events_session_id_event_index_unique": { + "name": "sandbox_session_events_session_id_event_index_unique", + "columns": ["session_id", "event_index"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sandbox_sessions": { + "name": "sandbox_sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "agent": { + "name": "agent", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "agent_session_id": { + "name": "agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_connection_id": { + "name": "last_connection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "destroyed_at": { + "name": "destroyed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_init_json": { + "name": "session_init_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} diff --git a/foundry/packages/backend/src/actors/sandbox-instance/db/drizzle/meta/_journal.json b/foundry/packages/backend/src/actors/sandbox-instance/db/drizzle/meta/_journal.json index b3df430..99e3e16 100644 --- a/foundry/packages/backend/src/actors/sandbox-instance/db/drizzle/meta/_journal.json +++ b/foundry/packages/backend/src/actors/sandbox-instance/db/drizzle/meta/_journal.json @@ -15,6 +15,13 @@ "when": 1776482400000, "tag": "0001_sandbox_sessions", "breakpoints": true + }, + { + "idx": 2, + "version": "6", + "when": 1773375724623, + "tag": "0002_common_betty_ross", + "breakpoints": true } ] } diff --git a/foundry/packages/backend/src/actors/sandbox-instance/db/migrations.ts b/foundry/packages/backend/src/actors/sandbox-instance/db/migrations.ts index 81de338..6c16668 100644 --- a/foundry/packages/backend/src/actors/sandbox-instance/db/migrations.ts +++ b/foundry/packages/backend/src/actors/sandbox-instance/db/migrations.ts @@ -16,6 +16,12 @@ const journal = { tag: "0001_sandbox_sessions", breakpoints: true, }, + { + idx: 2, + when: 1773375724623, + tag: "0002_common_betty_ross", + breakpoints: true, + }, ], } as const; @@ -56,6 +62,27 @@ CREATE INDEX \`sandbox_sessions_created_at_idx\` ON \`sandbox_sessions\` (\`crea CREATE INDEX \`sandbox_session_events_session_id_event_index_idx\` ON \`sandbox_session_events\` (\`session_id\`,\`event_index\`); --> statement-breakpoint CREATE INDEX \`sandbox_session_events_session_id_created_at_idx\` ON \`sandbox_session_events\` (\`session_id\`,\`created_at\`); +`, + m0002: `CREATE TABLE \`sandbox_session_events\` ( + \`id\` text PRIMARY KEY NOT NULL, + \`session_id\` text NOT NULL, + \`event_index\` integer NOT NULL, + \`created_at\` integer NOT NULL, + \`connection_id\` text NOT NULL, + \`sender\` text NOT NULL, + \`payload_json\` text NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX \`sandbox_session_events_session_id_event_index_unique\` ON \`sandbox_session_events\` (\`session_id\`,\`event_index\`);--> statement-breakpoint +CREATE TABLE \`sandbox_sessions\` ( + \`id\` text PRIMARY KEY NOT NULL, + \`agent\` text NOT NULL, + \`agent_session_id\` text NOT NULL, + \`last_connection_id\` text NOT NULL, + \`created_at\` integer NOT NULL, + \`destroyed_at\` integer, + \`session_init_json\` text +); `, } as const, }; diff --git a/foundry/packages/backend/src/actors/sandbox-instance/db/schema.ts b/foundry/packages/backend/src/actors/sandbox-instance/db/schema.ts index b5dbf50..06ce05a 100644 --- a/foundry/packages/backend/src/actors/sandbox-instance/db/schema.ts +++ b/foundry/packages/backend/src/actors/sandbox-instance/db/schema.ts @@ -1,8 +1,9 @@ -import { integer, sqliteTable, text } from "rivetkit/db/drizzle"; +import { integer, sqliteTable, text, uniqueIndex } from "rivetkit/db/drizzle"; // SQLite is per sandbox-instance actor instance. export const sandboxInstance = sqliteTable("sandbox_instance", { id: integer("id").primaryKey(), + // Structured by the provider/runtime metadata serializer for this actor. metadataJson: text("metadata_json").notNull(), status: text("status").notNull(), updatedAt: integer("updated_at").notNull(), @@ -17,15 +18,21 @@ export const sandboxSessions = sqliteTable("sandbox_sessions", { lastConnectionId: text("last_connection_id").notNull(), createdAt: integer("created_at").notNull(), destroyedAt: integer("destroyed_at"), + // Structured by the sandbox-agent ACP session bootstrap payload. sessionInitJson: text("session_init_json"), }); -export const sandboxSessionEvents = sqliteTable("sandbox_session_events", { - id: text("id").notNull().primaryKey(), - sessionId: text("session_id").notNull(), - eventIndex: integer("event_index").notNull(), - createdAt: integer("created_at").notNull(), - connectionId: text("connection_id").notNull(), - sender: text("sender").notNull(), - payloadJson: text("payload_json").notNull(), -}); +export const sandboxSessionEvents = sqliteTable( + "sandbox_session_events", + { + id: text("id").notNull().primaryKey(), + sessionId: text("session_id").notNull(), + eventIndex: integer("event_index").notNull(), + createdAt: integer("created_at").notNull(), + connectionId: text("connection_id").notNull(), + sender: text("sender").notNull(), + // Structured by the sandbox-agent session event envelope. + payloadJson: text("payload_json").notNull(), + }, + (table) => [uniqueIndex("sandbox_session_events_session_id_event_index_unique").on(table.sessionId, table.eventIndex)], +); diff --git a/foundry/packages/backend/src/actors/task/db/drizzle/0006_harsh_wind_dancer.sql b/foundry/packages/backend/src/actors/task/db/drizzle/0006_harsh_wind_dancer.sql new file mode 100644 index 0000000..8fb59fb --- /dev/null +++ b/foundry/packages/backend/src/actors/task/db/drizzle/0006_harsh_wind_dancer.sql @@ -0,0 +1,49 @@ +CREATE TABLE `task_workbench_sessions` ( + `session_id` text PRIMARY KEY NOT NULL, + `session_name` text NOT NULL, + `model` text NOT NULL, + `unread` integer DEFAULT 0 NOT NULL, + `draft_text` text DEFAULT '' NOT NULL, + `draft_attachments_json` text DEFAULT '[]' NOT NULL, + `draft_updated_at` integer, + `created` integer DEFAULT 1 NOT NULL, + `closed` integer DEFAULT 0 NOT NULL, + `thinking_since_ms` integer, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL +); +--> statement-breakpoint +PRAGMA foreign_keys=OFF;--> statement-breakpoint +CREATE TABLE `__new_task` ( + `id` integer PRIMARY KEY NOT NULL, + `branch_name` text, + `title` text, + `task` text NOT NULL, + `provider_id` text NOT NULL, + `status` text NOT NULL, + `agent_type` text DEFAULT 'claude', + `pr_submitted` integer DEFAULT 0, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL, + CONSTRAINT "task_singleton_id_check" CHECK("__new_task"."id" = 1) +); +--> statement-breakpoint +INSERT INTO `__new_task`("id", "branch_name", "title", "task", "provider_id", "status", "agent_type", "pr_submitted", "created_at", "updated_at") SELECT "id", "branch_name", "title", "task", "provider_id", "status", "agent_type", "pr_submitted", "created_at", "updated_at" FROM `task`;--> statement-breakpoint +DROP TABLE `task`;--> statement-breakpoint +ALTER TABLE `__new_task` RENAME TO `task`;--> statement-breakpoint +PRAGMA foreign_keys=ON;--> statement-breakpoint +ALTER TABLE `task_sandboxes` ADD `sandbox_actor_id` text;--> statement-breakpoint +CREATE TABLE `__new_task_runtime` ( + `id` integer PRIMARY KEY NOT NULL, + `active_sandbox_id` text, + `active_session_id` text, + `active_switch_target` text, + `active_cwd` text, + `status_message` text, + `updated_at` integer NOT NULL, + CONSTRAINT "task_runtime_singleton_id_check" CHECK("__new_task_runtime"."id" = 1) +); +--> statement-breakpoint +INSERT INTO `__new_task_runtime`("id", "active_sandbox_id", "active_session_id", "active_switch_target", "active_cwd", "status_message", "updated_at") SELECT "id", "active_sandbox_id", "active_session_id", "active_switch_target", "active_cwd", "status_message", "updated_at" FROM `task_runtime`;--> statement-breakpoint +DROP TABLE `task_runtime`;--> statement-breakpoint +ALTER TABLE `__new_task_runtime` RENAME TO `task_runtime`; \ No newline at end of file diff --git a/foundry/packages/backend/src/actors/task/db/drizzle/meta/0006_snapshot.json b/foundry/packages/backend/src/actors/task/db/drizzle/meta/0006_snapshot.json new file mode 100644 index 0000000..f9fb589 --- /dev/null +++ b/foundry/packages/backend/src/actors/task/db/drizzle/meta/0006_snapshot.json @@ -0,0 +1,334 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "dd0e2bbf-2c39-405b-99c8-1bc7a3b9e17d", + "prevId": "72cef919-e545-48be-a7c0-7ac74cfcf9e6", + "tables": { + "task": { + "name": "task", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "branch_name": { + "name": "branch_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "task": { + "name": "task", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "agent_type": { + "name": "agent_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'claude'" + }, + "pr_submitted": { + "name": "pr_submitted", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "task_singleton_id_check": { + "name": "task_singleton_id_check", + "value": "\"task\".\"id\" = 1" + } + } + }, + "task_runtime": { + "name": "task_runtime", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "active_sandbox_id": { + "name": "active_sandbox_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "active_session_id": { + "name": "active_session_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "active_switch_target": { + "name": "active_switch_target", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "active_cwd": { + "name": "active_cwd", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status_message": { + "name": "status_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "task_runtime_singleton_id_check": { + "name": "task_runtime_singleton_id_check", + "value": "\"task_runtime\".\"id\" = 1" + } + } + }, + "task_sandboxes": { + "name": "task_sandboxes", + "columns": { + "sandbox_id": { + "name": "sandbox_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sandbox_actor_id": { + "name": "sandbox_actor_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "switch_target": { + "name": "switch_target", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cwd": { + "name": "cwd", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status_message": { + "name": "status_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "task_workbench_sessions": { + "name": "task_workbench_sessions", + "columns": { + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "session_name": { + "name": "session_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unread": { + "name": "unread", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "draft_text": { + "name": "draft_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "draft_attachments_json": { + "name": "draft_attachments_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "draft_updated_at": { + "name": "draft_updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created": { + "name": "created", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "closed": { + "name": "closed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "thinking_since_ms": { + "name": "thinking_since_ms", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} diff --git a/foundry/packages/backend/src/actors/task/db/drizzle/meta/_journal.json b/foundry/packages/backend/src/actors/task/db/drizzle/meta/_journal.json index 208f695..0af5946 100644 --- a/foundry/packages/backend/src/actors/task/db/drizzle/meta/_journal.json +++ b/foundry/packages/backend/src/actors/task/db/drizzle/meta/_journal.json @@ -43,6 +43,13 @@ "when": 1771370000000, "tag": "0005_sandbox_actor_id", "breakpoints": true + }, + { + "idx": 6, + "version": "6", + "when": 1773375723300, + "tag": "0006_harsh_wind_dancer", + "breakpoints": true } ] } diff --git a/foundry/packages/backend/src/actors/task/db/migrations.ts b/foundry/packages/backend/src/actors/task/db/migrations.ts index ee593bf..469e053 100644 --- a/foundry/packages/backend/src/actors/task/db/migrations.ts +++ b/foundry/packages/backend/src/actors/task/db/migrations.ts @@ -42,8 +42,8 @@ const journal = { }, { idx: 6, - when: 1773020000000, - tag: "0006_workbench_sessions", + when: 1773375723300, + tag: "0006_harsh_wind_dancer", breakpoints: true, }, ], @@ -226,7 +226,8 @@ ALTER TABLE \`task__new\` RENAME TO \`task\`; PRAGMA foreign_keys=on; `, - m0005: `ALTER TABLE \`task_sandboxes\` ADD \`sandbox_actor_id\` text;`, + m0005: `ALTER TABLE \`task_sandboxes\` ADD \`sandbox_actor_id\` text; +`, m0006: `CREATE TABLE \`task_workbench_sessions\` ( \`session_id\` text PRIMARY KEY NOT NULL, \`session_name\` text NOT NULL, @@ -240,6 +241,41 @@ PRAGMA foreign_keys=on; \`thinking_since_ms\` integer, \`created_at\` integer NOT NULL, \`updated_at\` integer NOT NULL -);`, +); +--> statement-breakpoint +PRAGMA foreign_keys=OFF;--> statement-breakpoint +CREATE TABLE \`__new_task\` ( + \`id\` integer PRIMARY KEY NOT NULL, + \`branch_name\` text, + \`title\` text, + \`task\` text NOT NULL, + \`provider_id\` text NOT NULL, + \`status\` text NOT NULL, + \`agent_type\` text DEFAULT 'claude', + \`pr_submitted\` integer DEFAULT 0, + \`created_at\` integer NOT NULL, + \`updated_at\` integer NOT NULL, + CONSTRAINT "task_singleton_id_check" CHECK("__new_task"."id" = 1) +); +--> statement-breakpoint +INSERT INTO \`__new_task\`("id", "branch_name", "title", "task", "provider_id", "status", "agent_type", "pr_submitted", "created_at", "updated_at") SELECT "id", "branch_name", "title", "task", "provider_id", "status", "agent_type", "pr_submitted", "created_at", "updated_at" FROM \`task\`;--> statement-breakpoint +DROP TABLE \`task\`;--> statement-breakpoint +ALTER TABLE \`__new_task\` RENAME TO \`task\`;--> statement-breakpoint +PRAGMA foreign_keys=ON;--> statement-breakpoint +ALTER TABLE \`task_sandboxes\` ADD \`sandbox_actor_id\` text;--> statement-breakpoint +CREATE TABLE \`__new_task_runtime\` ( + \`id\` integer PRIMARY KEY NOT NULL, + \`active_sandbox_id\` text, + \`active_session_id\` text, + \`active_switch_target\` text, + \`active_cwd\` text, + \`status_message\` text, + \`updated_at\` integer NOT NULL, + CONSTRAINT "task_runtime_singleton_id_check" CHECK("__new_task_runtime"."id" = 1) +); +--> statement-breakpoint +INSERT INTO \`__new_task_runtime\`("id", "active_sandbox_id", "active_session_id", "active_switch_target", "active_cwd", "status_message", "updated_at") SELECT "id", "active_sandbox_id", "active_session_id", "active_switch_target", "active_cwd", "status_message", "updated_at" FROM \`task_runtime\`;--> statement-breakpoint +DROP TABLE \`task_runtime\`;--> statement-breakpoint +ALTER TABLE \`__new_task_runtime\` RENAME TO \`task_runtime\`;`, } as const, }; diff --git a/foundry/packages/backend/src/actors/task/db/schema.ts b/foundry/packages/backend/src/actors/task/db/schema.ts index a9d8ad8..2684ebb 100644 --- a/foundry/packages/backend/src/actors/task/db/schema.ts +++ b/foundry/packages/backend/src/actors/task/db/schema.ts @@ -1,28 +1,37 @@ -import { integer, sqliteTable, text } from "rivetkit/db/drizzle"; +import { check, integer, sqliteTable, text } from "rivetkit/db/drizzle"; +import { sql } from "drizzle-orm"; // SQLite is per task actor instance, so these tables only ever store one row (id=1). -export const task = sqliteTable("task", { - id: integer("id").primaryKey(), - branchName: text("branch_name"), - title: text("title"), - task: text("task").notNull(), - providerId: text("provider_id").notNull(), - status: text("status").notNull(), - agentType: text("agent_type").default("claude"), - prSubmitted: integer("pr_submitted").default(0), - createdAt: integer("created_at").notNull(), - updatedAt: integer("updated_at").notNull(), -}); +export const task = sqliteTable( + "task", + { + id: integer("id").primaryKey(), + branchName: text("branch_name"), + title: text("title"), + task: text("task").notNull(), + providerId: text("provider_id").notNull(), + status: text("status").notNull(), + agentType: text("agent_type").default("claude"), + prSubmitted: integer("pr_submitted").default(0), + createdAt: integer("created_at").notNull(), + updatedAt: integer("updated_at").notNull(), + }, + (table) => [check("task_singleton_id_check", sql`${table.id} = 1`)], +); -export const taskRuntime = sqliteTable("task_runtime", { - id: integer("id").primaryKey(), - activeSandboxId: text("active_sandbox_id"), - activeSessionId: text("active_session_id"), - activeSwitchTarget: text("active_switch_target"), - activeCwd: text("active_cwd"), - statusMessage: text("status_message"), - updatedAt: integer("updated_at").notNull(), -}); +export const taskRuntime = sqliteTable( + "task_runtime", + { + id: integer("id").primaryKey(), + activeSandboxId: text("active_sandbox_id"), + activeSessionId: text("active_session_id"), + activeSwitchTarget: text("active_switch_target"), + activeCwd: text("active_cwd"), + statusMessage: text("status_message"), + updatedAt: integer("updated_at").notNull(), + }, + (table) => [check("task_runtime_singleton_id_check", sql`${table.id} = 1`)], +); export const taskSandboxes = sqliteTable("task_sandboxes", { sandboxId: text("sandbox_id").notNull().primaryKey(), @@ -41,6 +50,7 @@ export const taskWorkbenchSessions = sqliteTable("task_workbench_sessions", { model: text("model").notNull(), unread: integer("unread").notNull().default(0), draftText: text("draft_text").notNull().default(""), + // Structured by the workbench composer attachment payload format. draftAttachmentsJson: text("draft_attachments_json").notNull().default("[]"), draftUpdatedAt: integer("draft_updated_at"), created: integer("created").notNull().default(1), diff --git a/foundry/packages/backend/src/actors/workspace/db/drizzle/0003_mushy_rictor.sql b/foundry/packages/backend/src/actors/workspace/db/drizzle/0003_mushy_rictor.sql new file mode 100644 index 0000000..9905b26 --- /dev/null +++ b/foundry/packages/backend/src/actors/workspace/db/drizzle/0003_mushy_rictor.sql @@ -0,0 +1,84 @@ +CREATE TABLE `app_sessions` ( + `id` text PRIMARY KEY NOT NULL, + `current_user_id` text, + `current_user_name` text, + `current_user_email` text, + `current_user_github_login` text, + `current_user_role_label` text, + `eligible_organization_ids_json` text NOT NULL, + `active_organization_id` text, + `github_access_token` text, + `github_scope` text NOT NULL, + `starter_repo_status` text NOT NULL, + `starter_repo_starred_at` integer, + `starter_repo_skipped_at` integer, + `oauth_state` text, + `oauth_state_expires_at` integer, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL +); +--> statement-breakpoint +CREATE TABLE `invoices` ( + `id` text PRIMARY KEY NOT NULL, + `label` text NOT NULL, + `issued_at` text NOT NULL, + `amount_usd` integer NOT NULL, + `status` text NOT NULL, + `created_at` integer NOT NULL +); +--> statement-breakpoint +CREATE TABLE `organization_members` ( + `id` text PRIMARY KEY NOT NULL, + `name` text NOT NULL, + `email` text NOT NULL, + `role` text NOT NULL, + `state` text NOT NULL, + `updated_at` integer NOT NULL +); +--> statement-breakpoint +CREATE TABLE `organization_profile` ( + `id` text PRIMARY KEY NOT NULL, + `kind` text NOT NULL, + `github_account_id` text NOT NULL, + `github_login` text NOT NULL, + `github_account_type` text NOT NULL, + `display_name` text NOT NULL, + `slug` text NOT NULL, + `primary_domain` text NOT NULL, + `default_model` text NOT NULL, + `auto_import_repos` integer NOT NULL, + `repo_import_status` text NOT NULL, + `github_connected_account` text NOT NULL, + `github_installation_status` text NOT NULL, + `github_sync_status` text NOT NULL, + `github_installation_id` integer, + `github_last_sync_label` text NOT NULL, + `github_last_sync_at` integer, + `stripe_customer_id` text, + `stripe_subscription_id` text, + `stripe_price_id` text, + `billing_plan_id` text NOT NULL, + `billing_status` text NOT NULL, + `billing_seats_included` integer NOT NULL, + `billing_trial_ends_at` text, + `billing_renewal_at` text, + `billing_payment_method_label` text NOT NULL, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL +); +--> statement-breakpoint +CREATE TABLE `seat_assignments` ( + `email` text PRIMARY KEY NOT NULL, + `created_at` integer NOT NULL +); +--> statement-breakpoint +CREATE TABLE `stripe_lookup` ( + `lookup_key` text PRIMARY KEY NOT NULL, + `organization_id` text NOT NULL, + `updated_at` integer NOT NULL +); +--> statement-breakpoint +CREATE TABLE `task_lookup` ( + `task_id` text PRIMARY KEY NOT NULL, + `repo_id` text NOT NULL +); diff --git a/foundry/packages/backend/src/actors/workspace/db/drizzle/meta/0003_snapshot.json b/foundry/packages/backend/src/actors/workspace/db/drizzle/meta/0003_snapshot.json new file mode 100644 index 0000000..51ecd59 --- /dev/null +++ b/foundry/packages/backend/src/actors/workspace/db/drizzle/meta/0003_snapshot.json @@ -0,0 +1,605 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "90ecc616-3bbf-4077-9def-4a18f7979142", + "prevId": "450e2fdf-6349-482f-8a68-5bc0f0a9718a", + "tables": { + "app_sessions": { + "name": "app_sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "current_user_id": { + "name": "current_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "current_user_name": { + "name": "current_user_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "current_user_email": { + "name": "current_user_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "current_user_github_login": { + "name": "current_user_github_login", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "current_user_role_label": { + "name": "current_user_role_label", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "eligible_organization_ids_json": { + "name": "eligible_organization_ids_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "github_access_token": { + "name": "github_access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "github_scope": { + "name": "github_scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "starter_repo_status": { + "name": "starter_repo_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "starter_repo_starred_at": { + "name": "starter_repo_starred_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "starter_repo_skipped_at": { + "name": "starter_repo_skipped_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "oauth_state": { + "name": "oauth_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "oauth_state_expires_at": { + "name": "oauth_state_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "invoices": { + "name": "invoices", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "issued_at": { + "name": "issued_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "amount_usd": { + "name": "amount_usd", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organization_members": { + "name": "organization_members", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organization_profile": { + "name": "organization_profile", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "github_account_id": { + "name": "github_account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "github_login": { + "name": "github_login", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "github_account_type": { + "name": "github_account_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "primary_domain": { + "name": "primary_domain", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "default_model": { + "name": "default_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "auto_import_repos": { + "name": "auto_import_repos", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repo_import_status": { + "name": "repo_import_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "github_connected_account": { + "name": "github_connected_account", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "github_installation_status": { + "name": "github_installation_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "github_sync_status": { + "name": "github_sync_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "github_installation_id": { + "name": "github_installation_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "github_last_sync_label": { + "name": "github_last_sync_label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "github_last_sync_at": { + "name": "github_last_sync_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "stripe_price_id": { + "name": "stripe_price_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "billing_plan_id": { + "name": "billing_plan_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "billing_status": { + "name": "billing_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "billing_seats_included": { + "name": "billing_seats_included", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "billing_trial_ends_at": { + "name": "billing_trial_ends_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "billing_renewal_at": { + "name": "billing_renewal_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "billing_payment_method_label": { + "name": "billing_payment_method_label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "provider_profiles": { + "name": "provider_profiles", + "columns": { + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "profile_json": { + "name": "profile_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repos": { + "name": "repos", + "columns": { + "repo_id": { + "name": "repo_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "remote_url": { + "name": "remote_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "seat_assignments": { + "name": "seat_assignments", + "columns": { + "email": { + "name": "email", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "stripe_lookup": { + "name": "stripe_lookup", + "columns": { + "lookup_key": { + "name": "lookup_key", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "task_lookup": { + "name": "task_lookup", + "columns": { + "task_id": { + "name": "task_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "repo_id": { + "name": "repo_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} diff --git a/foundry/packages/backend/src/actors/workspace/db/drizzle/meta/_journal.json b/foundry/packages/backend/src/actors/workspace/db/drizzle/meta/_journal.json index 48662e9..a1dcc87 100644 --- a/foundry/packages/backend/src/actors/workspace/db/drizzle/meta/_journal.json +++ b/foundry/packages/backend/src/actors/workspace/db/drizzle/meta/_journal.json @@ -22,6 +22,13 @@ "when": 1772668800000, "tag": "0002_tiny_silver_surfer", "breakpoints": true + }, + { + "idx": 3, + "version": "6", + "when": 1773375721168, + "tag": "0003_mushy_rictor", + "breakpoints": true } ] } diff --git a/foundry/packages/backend/src/actors/workspace/db/migrations.ts b/foundry/packages/backend/src/actors/workspace/db/migrations.ts index 5aa7f6d..424a335 100644 --- a/foundry/packages/backend/src/actors/workspace/db/migrations.ts +++ b/foundry/packages/backend/src/actors/workspace/db/migrations.ts @@ -24,50 +24,8 @@ const journal = { }, { idx: 3, - when: 1773100800000, - tag: "0003_app_shell_organization_profile", - breakpoints: true, - }, - { - idx: 4, - when: 1773100800001, - tag: "0004_app_shell_organization_members", - breakpoints: true, - }, - { - idx: 5, - when: 1773100800002, - tag: "0005_app_shell_seat_assignments", - breakpoints: true, - }, - { - idx: 6, - when: 1773100800003, - tag: "0006_app_shell_invoices", - breakpoints: true, - }, - { - idx: 7, - when: 1773100800004, - tag: "0007_app_shell_sessions", - breakpoints: true, - }, - { - idx: 8, - when: 1773100800005, - tag: "0008_app_shell_stripe_lookup", - breakpoints: true, - }, - { - idx: 9, - when: 1773100800006, - tag: "0009_github_sync_status", - breakpoints: true, - }, - { - idx: 10, - when: 1772928000000, - tag: "0010_app_session_starter_repo", + when: 1773375721168, + tag: "0003_mushy_rictor", breakpoints: true, }, ], @@ -94,59 +52,7 @@ export default { \`repo_id\` text NOT NULL ); `, - m0003: `CREATE TABLE \`organization_profile\` ( - \`id\` text PRIMARY KEY NOT NULL, - \`kind\` text NOT NULL, - \`github_account_id\` text NOT NULL, - \`github_login\` text NOT NULL, - \`github_account_type\` text NOT NULL, - \`display_name\` text NOT NULL, - \`slug\` text NOT NULL, - \`primary_domain\` text NOT NULL, - \`default_model\` text NOT NULL, - \`auto_import_repos\` integer NOT NULL, - \`repo_import_status\` text NOT NULL, - \`github_connected_account\` text NOT NULL, - \`github_installation_status\` text NOT NULL, - \`github_installation_id\` integer, - \`github_last_sync_label\` text NOT NULL, - \`stripe_customer_id\` text, - \`stripe_subscription_id\` text, - \`stripe_price_id\` text, - \`billing_plan_id\` text NOT NULL, - \`billing_status\` text NOT NULL, - \`billing_seats_included\` integer NOT NULL, - \`billing_trial_ends_at\` text, - \`billing_renewal_at\` text, - \`billing_payment_method_label\` text NOT NULL, - \`created_at\` integer NOT NULL, - \`updated_at\` integer NOT NULL -); -`, - m0004: `CREATE TABLE \`organization_members\` ( - \`id\` text PRIMARY KEY NOT NULL, - \`name\` text NOT NULL, - \`email\` text NOT NULL, - \`role\` text NOT NULL, - \`state\` text NOT NULL, - \`updated_at\` integer NOT NULL -); -`, - m0005: `CREATE TABLE \`seat_assignments\` ( - \`email\` text PRIMARY KEY NOT NULL, - \`created_at\` integer NOT NULL -); -`, - m0006: `CREATE TABLE \`invoices\` ( - \`id\` text PRIMARY KEY NOT NULL, - \`label\` text NOT NULL, - \`issued_at\` text NOT NULL, - \`amount_usd\` integer NOT NULL, - \`status\` text NOT NULL, - \`created_at\` integer NOT NULL -); -`, - m0007: `CREATE TABLE \`app_sessions\` ( + m0003: `CREATE TABLE \`app_sessions\` ( \`id\` text PRIMARY KEY NOT NULL, \`current_user_id\` text, \`current_user_name\` text, @@ -165,23 +71,71 @@ export default { \`created_at\` integer NOT NULL, \`updated_at\` integer NOT NULL ); -`, - m0008: `CREATE TABLE \`stripe_lookup\` ( +--> statement-breakpoint +CREATE TABLE \`invoices\` ( + \`id\` text PRIMARY KEY NOT NULL, + \`label\` text NOT NULL, + \`issued_at\` text NOT NULL, + \`amount_usd\` integer NOT NULL, + \`status\` text NOT NULL, + \`created_at\` integer NOT NULL +); +--> statement-breakpoint +CREATE TABLE \`organization_members\` ( + \`id\` text PRIMARY KEY NOT NULL, + \`name\` text NOT NULL, + \`email\` text NOT NULL, + \`role\` text NOT NULL, + \`state\` text NOT NULL, + \`updated_at\` integer NOT NULL +); +--> statement-breakpoint +CREATE TABLE \`organization_profile\` ( + \`id\` text PRIMARY KEY NOT NULL, + \`kind\` text NOT NULL, + \`github_account_id\` text NOT NULL, + \`github_login\` text NOT NULL, + \`github_account_type\` text NOT NULL, + \`display_name\` text NOT NULL, + \`slug\` text NOT NULL, + \`primary_domain\` text NOT NULL, + \`default_model\` text NOT NULL, + \`auto_import_repos\` integer NOT NULL, + \`repo_import_status\` text NOT NULL, + \`github_connected_account\` text NOT NULL, + \`github_installation_status\` text NOT NULL, + \`github_sync_status\` text NOT NULL, + \`github_installation_id\` integer, + \`github_last_sync_label\` text NOT NULL, + \`github_last_sync_at\` integer, + \`stripe_customer_id\` text, + \`stripe_subscription_id\` text, + \`stripe_price_id\` text, + \`billing_plan_id\` text NOT NULL, + \`billing_status\` text NOT NULL, + \`billing_seats_included\` integer NOT NULL, + \`billing_trial_ends_at\` text, + \`billing_renewal_at\` text, + \`billing_payment_method_label\` text NOT NULL, + \`created_at\` integer NOT NULL, + \`updated_at\` integer NOT NULL +); +--> statement-breakpoint +CREATE TABLE \`seat_assignments\` ( + \`email\` text PRIMARY KEY NOT NULL, + \`created_at\` integer NOT NULL +); +--> statement-breakpoint +CREATE TABLE \`stripe_lookup\` ( \`lookup_key\` text PRIMARY KEY NOT NULL, \`organization_id\` text NOT NULL, \`updated_at\` integer NOT NULL ); -`, - m0009: `ALTER TABLE \`organization_profile\` ADD COLUMN \`github_sync_status\` text NOT NULL DEFAULT 'pending'; -ALTER TABLE \`organization_profile\` ADD COLUMN \`github_last_sync_at\` integer; -UPDATE \`organization_profile\` -SET \`github_sync_status\` = CASE - WHEN \`repo_import_status\` = 'ready' THEN 'synced' - WHEN \`repo_import_status\` = 'importing' THEN 'syncing' - ELSE 'pending' -END; -`, - m0010: `-- no-op: starter_repo_* columns are already present in m0007 app_sessions +--> statement-breakpoint +CREATE TABLE \`task_lookup\` ( + \`task_id\` text PRIMARY KEY NOT NULL, + \`repo_id\` text NOT NULL +); `, } as const, }; diff --git a/foundry/packages/backend/src/actors/workspace/db/schema.ts b/foundry/packages/backend/src/actors/workspace/db/schema.ts index 728103b..5f8cf66 100644 --- a/foundry/packages/backend/src/actors/workspace/db/schema.ts +++ b/foundry/packages/backend/src/actors/workspace/db/schema.ts @@ -3,6 +3,7 @@ import { integer, sqliteTable, text } from "rivetkit/db/drizzle"; // SQLite is per workspace actor instance, so no workspaceId column needed. export const providerProfiles = sqliteTable("provider_profiles", { providerId: text("provider_id").notNull().primaryKey(), + // Structured by the provider profile snapshot returned by provider integrations. profileJson: text("profile_json").notNull(), updatedAt: integer("updated_at").notNull(), }); @@ -80,6 +81,7 @@ export const appSessions = sqliteTable("app_sessions", { currentUserEmail: text("current_user_email"), currentUserGithubLogin: text("current_user_github_login"), currentUserRoleLabel: text("current_user_role_label"), + // Structured as a JSON array of eligible organization ids for the session. eligibleOrganizationIdsJson: text("eligible_organization_ids_json").notNull(), activeOrganizationId: text("active_organization_id"), githubAccessToken: text("github_access_token"),