diff --git a/app/(dashboard)/actions.ts b/app/(dashboard)/actions.ts
index 156dd5b..b0addfc 100644
--- a/app/(dashboard)/actions.ts
+++ b/app/(dashboard)/actions.ts
@@ -243,7 +243,6 @@ export async function createCodePlanAction(formData: FormData) {
type,
tags,
targetAssetIds: [],
- assigneeIds: [],
deadline,
specUrl,
},
diff --git a/app/(dashboard)/tasks/tasks-client.tsx b/app/(dashboard)/tasks/tasks-client.tsx
index 8d21556..cae8ee7 100644
--- a/app/(dashboard)/tasks/tasks-client.tsx
+++ b/app/(dashboard)/tasks/tasks-client.tsx
@@ -218,15 +218,9 @@ export function TasksClient({
return (
<>
-
-
-
Tasks
-
Track and manage tasks across all code plans
-
-
+
+
Tasks
+
Track and manage tasks across all code plans
+
{ setStatusFilter(v as TaskStatus | 'all' | 'open'); setPage(0) }} className="w-full sm:w-auto">
Open
diff --git a/app/api/mcp/[transport]/route.ts b/app/api/mcp/[transport]/route.ts
index 0dd859e..9430834 100644
--- a/app/api/mcp/[transport]/route.ts
+++ b/app/api/mcp/[transport]/route.ts
@@ -432,7 +432,7 @@ const handler = createMcpHandler(
async ({ workItemIds, ownerEmail, ...data }, extra) => {
requireWrite(extra)
const ownerId = ownerEmail ? await resolveAssigneeEmail(uid(extra), ownerEmail) : undefined
- const plan = await createCodePlan({ ...data, ownerId, assigneeIds: [] }, uid(extra))
+ const plan = await createCodePlan({ ...data, ownerId }, uid(extra))
for (const workItemId of workItemIds) await linkWorkItemToPlan(workItemId, plan.id)
return json(plan)
},
diff --git a/lib/db/migrations/postgres/0010_drop_plan_assignees.sql b/lib/db/migrations/postgres/0010_drop_plan_assignees.sql
new file mode 100644
index 0000000..e68212a
--- /dev/null
+++ b/lib/db/migrations/postgres/0010_drop_plan_assignees.sql
@@ -0,0 +1 @@
+DROP TABLE "code_plan_assignees";
diff --git a/lib/db/migrations/postgres/meta/_journal.json b/lib/db/migrations/postgres/meta/_journal.json
index c615466..f56ca84 100644
--- a/lib/db/migrations/postgres/meta/_journal.json
+++ b/lib/db/migrations/postgres/meta/_journal.json
@@ -71,6 +71,13 @@
"when": 1783480000000,
"tag": "0009_task_percent",
"breakpoints": true
+ },
+ {
+ "idx": 10,
+ "version": "7",
+ "when": 1784058835573,
+ "tag": "0010_drop_plan_assignees",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/lib/db/migrations/sqlite/0010_drop_plan_assignees.sql b/lib/db/migrations/sqlite/0010_drop_plan_assignees.sql
new file mode 100644
index 0000000..21ceaf0
--- /dev/null
+++ b/lib/db/migrations/sqlite/0010_drop_plan_assignees.sql
@@ -0,0 +1 @@
+DROP TABLE `code_plan_assignees`;
\ No newline at end of file
diff --git a/lib/db/migrations/sqlite/meta/0010_snapshot.json b/lib/db/migrations/sqlite/meta/0010_snapshot.json
new file mode 100644
index 0000000..d1cbc33
--- /dev/null
+++ b/lib/db/migrations/sqlite/meta/0010_snapshot.json
@@ -0,0 +1,1960 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "1289b0db-bfaa-41e4-8db3-cc14ec419502",
+ "prevId": "c0c08b3f-8fc6-4df5-ae3b-6c4b47dba93a",
+ "tables": {
+ "api_keys": {
+ "name": "api_keys",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_hash": {
+ "name": "key_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "key_prefix": {
+ "name": "key_prefix",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'read'"
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "api_keys_key_hash_unique": {
+ "name": "api_keys_key_hash_unique",
+ "columns": [
+ "key_hash"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "api_keys_user_id_users_id_fk": {
+ "name": "api_keys_user_id_users_id_fk",
+ "tableFrom": "api_keys",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "asset_dependencies": {
+ "name": "asset_dependencies",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "source_asset_id": {
+ "name": "source_asset_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "target_asset_id": {
+ "name": "target_asset_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dependency_type": {
+ "name": "dependency_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "asset_dependencies_source_asset_id_assets_id_fk": {
+ "name": "asset_dependencies_source_asset_id_assets_id_fk",
+ "tableFrom": "asset_dependencies",
+ "tableTo": "assets",
+ "columnsFrom": [
+ "source_asset_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "asset_dependencies_target_asset_id_assets_id_fk": {
+ "name": "asset_dependencies_target_asset_id_assets_id_fk",
+ "tableFrom": "asset_dependencies",
+ "tableTo": "assets",
+ "columnsFrom": [
+ "target_asset_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "assets": {
+ "name": "assets",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "product_id": {
+ "name": "product_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "''"
+ },
+ "tags": {
+ "name": "tags",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "health": {
+ "name": "health",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'healthy'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'active'"
+ },
+ "tech_debt_score": {
+ "name": "tech_debt_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "repository_url": {
+ "name": "repository_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "repo_path": {
+ "name": "repo_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "documentation_url": {
+ "name": "documentation_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'{}'"
+ },
+ "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": {
+ "assets_product_id_products_id_fk": {
+ "name": "assets_product_id_products_id_fk",
+ "tableFrom": "assets",
+ "tableTo": "products",
+ "columnsFrom": [
+ "product_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "code_plan_assets": {
+ "name": "code_plan_assets",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "code_plan_id": {
+ "name": "code_plan_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "asset_id": {
+ "name": "asset_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "pr_status": {
+ "name": "pr_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'none'"
+ },
+ "notes": {
+ "name": "notes",
+ "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": {
+ "code_plan_assets_plan_asset_idx": {
+ "name": "code_plan_assets_plan_asset_idx",
+ "columns": [
+ "code_plan_id",
+ "asset_id"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "code_plan_assets_code_plan_id_code_plans_id_fk": {
+ "name": "code_plan_assets_code_plan_id_code_plans_id_fk",
+ "tableFrom": "code_plan_assets",
+ "tableTo": "code_plans",
+ "columnsFrom": [
+ "code_plan_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "code_plan_assets_asset_id_assets_id_fk": {
+ "name": "code_plan_assets_asset_id_assets_id_fk",
+ "tableFrom": "code_plan_assets",
+ "tableTo": "assets",
+ "columnsFrom": [
+ "asset_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "code_plans": {
+ "name": "code_plans",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "''"
+ },
+ "product_id": {
+ "name": "product_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'draft'"
+ },
+ "tags": {
+ "name": "tags",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "start_date": {
+ "name": "start_date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "end_date": {
+ "name": "end_date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "deadline": {
+ "name": "deadline",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "spec_url": {
+ "name": "spec_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'native'"
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_key": {
+ "name": "external_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_url": {
+ "name": "external_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_data": {
+ "name": "external_data",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'{}'"
+ },
+ "external_deleted": {
+ "name": "external_deleted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "synced_at": {
+ "name": "synced_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": {
+ "code_plans_connection_external_idx": {
+ "name": "code_plans_connection_external_idx",
+ "columns": [
+ "connection_id",
+ "external_id"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "code_plans_product_id_products_id_fk": {
+ "name": "code_plans_product_id_products_id_fk",
+ "tableFrom": "code_plans",
+ "tableTo": "products",
+ "columnsFrom": [
+ "product_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "code_plans_creator_id_users_id_fk": {
+ "name": "code_plans_creator_id_users_id_fk",
+ "tableFrom": "code_plans",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "code_plans_owner_id_users_id_fk": {
+ "name": "code_plans_owner_id_users_id_fk",
+ "tableFrom": "code_plans",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "code_plans_connection_id_integrations_id_fk": {
+ "name": "code_plans_connection_id_integrations_id_fk",
+ "tableFrom": "code_plans",
+ "tableTo": "integrations",
+ "columnsFrom": [
+ "connection_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "email_verification_tokens": {
+ "name": "email_verification_tokens",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "new_email": {
+ "name": "new_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "email_verification_tokens_token_unique": {
+ "name": "email_verification_tokens_token_unique",
+ "columns": [
+ "token"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "email_verification_tokens_user_id_users_id_fk": {
+ "name": "email_verification_tokens_user_id_users_id_fk",
+ "tableFrom": "email_verification_tokens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "integrations": {
+ "name": "integrations",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "auth_ref": {
+ "name": "auth_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "token_encrypted": {
+ "name": "token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "config": {
+ "name": "config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'{}'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'active'"
+ },
+ "last_sync_at": {
+ "name": "last_sync_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "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": {
+ "integrations_organization_id_organizations_id_fk": {
+ "name": "integrations_organization_id_organizations_id_fk",
+ "tableFrom": "integrations",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "organization_members": {
+ "name": "organization_members",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'viewer'"
+ },
+ "invited_by": {
+ "name": "invited_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "joined_at": {
+ "name": "joined_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "organization_members_organization_id_organizations_id_fk": {
+ "name": "organization_members_organization_id_organizations_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_members_user_id_users_id_fk": {
+ "name": "organization_members_user_id_users_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_members_invited_by_users_id_fk": {
+ "name": "organization_members_invited_by_users_id_fk",
+ "tableFrom": "organization_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "invited_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "organizations": {
+ "name": "organizations",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "billing_tier": {
+ "name": "billing_tier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'free'"
+ },
+ "product_limit": {
+ "name": "product_limit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 1
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "organizations_slug_unique": {
+ "name": "organizations_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "organizations_owner_id_users_id_fk": {
+ "name": "organizations_owner_id_users_id_fk",
+ "tableFrom": "organizations",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "products": {
+ "name": "products",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "''"
+ },
+ "tags": {
+ "name": "tags",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "products_slug_unique": {
+ "name": "products_slug_unique",
+ "columns": [
+ "slug"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "products_organization_id_organizations_id_fk": {
+ "name": "products_organization_id_organizations_id_fk",
+ "tableFrom": "products",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "products_creator_id_users_id_fk": {
+ "name": "products_creator_id_users_id_fk",
+ "tableFrom": "products",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "sync_log": {
+ "name": "sync_log",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "entity_id": {
+ "name": "entity_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "event": {
+ "name": "event",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'{}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "sync_log_org_created_idx": {
+ "name": "sync_log_org_created_idx",
+ "columns": [
+ "organization_id",
+ "created_at"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "sync_log_organization_id_organizations_id_fk": {
+ "name": "sync_log_organization_id_organizations_id_fk",
+ "tableFrom": "sync_log",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "sync_log_connection_id_integrations_id_fk": {
+ "name": "sync_log_connection_id_integrations_id_fk",
+ "tableFrom": "sync_log",
+ "tableTo": "integrations",
+ "columnsFrom": [
+ "connection_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "sync_log_actor_id_users_id_fk": {
+ "name": "sync_log_actor_id_users_id_fk",
+ "tableFrom": "sync_log",
+ "tableTo": "users",
+ "columnsFrom": [
+ "actor_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "tasks": {
+ "name": "tasks",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "code_plan_id": {
+ "name": "code_plan_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "asset_id": {
+ "name": "asset_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "''"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'not_started'"
+ },
+ "priority": {
+ "name": "priority",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'medium'"
+ },
+ "tags": {
+ "name": "tags",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "assignee_id": {
+ "name": "assignee_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "percent_complete": {
+ "name": "percent_complete",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "start_date": {
+ "name": "start_date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "end_date": {
+ "name": "end_date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "estimated_effort": {
+ "name": "estimated_effort",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "actual_effort": {
+ "name": "actual_effort",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'native'"
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_key": {
+ "name": "external_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_url": {
+ "name": "external_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_data": {
+ "name": "external_data",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'{}'"
+ },
+ "external_deleted": {
+ "name": "external_deleted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "synced_at": {
+ "name": "synced_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": {
+ "tasks_connection_external_idx": {
+ "name": "tasks_connection_external_idx",
+ "columns": [
+ "connection_id",
+ "external_id"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "tasks_code_plan_id_code_plans_id_fk": {
+ "name": "tasks_code_plan_id_code_plans_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "code_plans",
+ "columnsFrom": [
+ "code_plan_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tasks_asset_id_assets_id_fk": {
+ "name": "tasks_asset_id_assets_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "assets",
+ "columnsFrom": [
+ "asset_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tasks_assignee_id_users_id_fk": {
+ "name": "tasks_assignee_id_users_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "assignee_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tasks_connection_id_integrations_id_fk": {
+ "name": "tasks_connection_id_integrations_id_fk",
+ "tableFrom": "tasks",
+ "tableTo": "integrations",
+ "columnsFrom": [
+ "connection_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users": {
+ "name": "users",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "billing_tier": {
+ "name": "billing_tier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'free'"
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'viewer'"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "feature_flags": {
+ "name": "feature_flags",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'{}'"
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "work_item_code_plans": {
+ "name": "work_item_code_plans",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "work_item_id": {
+ "name": "work_item_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "code_plan_id": {
+ "name": "code_plan_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "work_item_code_plans_item_plan_idx": {
+ "name": "work_item_code_plans_item_plan_idx",
+ "columns": [
+ "work_item_id",
+ "code_plan_id"
+ ],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "work_item_code_plans_work_item_id_work_items_id_fk": {
+ "name": "work_item_code_plans_work_item_id_work_items_id_fk",
+ "tableFrom": "work_item_code_plans",
+ "tableTo": "work_items",
+ "columnsFrom": [
+ "work_item_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "work_item_code_plans_code_plan_id_code_plans_id_fk": {
+ "name": "work_item_code_plans_code_plan_id_code_plans_id_fk",
+ "tableFrom": "work_item_code_plans",
+ "tableTo": "code_plans",
+ "columnsFrom": [
+ "code_plan_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "work_items": {
+ "name": "work_items",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "product_id": {
+ "name": "product_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "asset_id": {
+ "name": "asset_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "area": {
+ "name": "area",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "parent_id": {
+ "name": "parent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "''"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'open'"
+ },
+ "severity": {
+ "name": "severity",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'medium'"
+ },
+ "tags": {
+ "name": "tags",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "reporter_id": {
+ "name": "reporter_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "spec_url": {
+ "name": "spec_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'native'"
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_key": {
+ "name": "external_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_url": {
+ "name": "external_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "external_data": {
+ "name": "external_data",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'{}'"
+ },
+ "external_deleted": {
+ "name": "external_deleted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "synced_at": {
+ "name": "synced_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": {
+ "work_items_connection_external_idx": {
+ "name": "work_items_connection_external_idx",
+ "columns": [
+ "connection_id",
+ "external_id"
+ ],
+ "isUnique": true
+ },
+ "work_items_product_idx": {
+ "name": "work_items_product_idx",
+ "columns": [
+ "product_id"
+ ],
+ "isUnique": false
+ },
+ "work_items_asset_idx": {
+ "name": "work_items_asset_idx",
+ "columns": [
+ "asset_id"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "work_items_product_id_products_id_fk": {
+ "name": "work_items_product_id_products_id_fk",
+ "tableFrom": "work_items",
+ "tableTo": "products",
+ "columnsFrom": [
+ "product_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "work_items_asset_id_assets_id_fk": {
+ "name": "work_items_asset_id_assets_id_fk",
+ "tableFrom": "work_items",
+ "tableTo": "assets",
+ "columnsFrom": [
+ "asset_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "work_items_parent_id_work_items_id_fk": {
+ "name": "work_items_parent_id_work_items_id_fk",
+ "tableFrom": "work_items",
+ "tableTo": "work_items",
+ "columnsFrom": [
+ "parent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "work_items_reporter_id_users_id_fk": {
+ "name": "work_items_reporter_id_users_id_fk",
+ "tableFrom": "work_items",
+ "tableTo": "users",
+ "columnsFrom": [
+ "reporter_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "work_items_owner_id_users_id_fk": {
+ "name": "work_items_owner_id_users_id_fk",
+ "tableFrom": "work_items",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "work_items_connection_id_integrations_id_fk": {
+ "name": "work_items_connection_id_integrations_id_fk",
+ "tableFrom": "work_items",
+ "tableTo": "integrations",
+ "columnsFrom": [
+ "connection_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
\ No newline at end of file
diff --git a/lib/db/migrations/sqlite/meta/_journal.json b/lib/db/migrations/sqlite/meta/_journal.json
index dc752aa..5a661bd 100644
--- a/lib/db/migrations/sqlite/meta/_journal.json
+++ b/lib/db/migrations/sqlite/meta/_journal.json
@@ -71,6 +71,13 @@
"when": 1783565221947,
"tag": "0009_daffy_the_executioner",
"breakpoints": true
+ },
+ {
+ "idx": 10,
+ "version": "6",
+ "when": 1784058835573,
+ "tag": "0010_drop_plan_assignees",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/lib/db/mutations.ts b/lib/db/mutations.ts
index 988b012..55256f0 100644
--- a/lib/db/mutations.ts
+++ b/lib/db/mutations.ts
@@ -6,7 +6,6 @@ import {
integrations,
codePlans,
codePlanAssets,
- codePlanAssignees,
workItems,
workItemCodePlans,
tasks,
@@ -120,11 +119,11 @@ type CreateCodePlanData = {
deadline?: string
specUrl?: string
ownerId?: string | null
- assigneeIds: string[]
}
-// Join tables are the source of truth for plan↔asset and plan↔assignee links
-// (the deprecated array columns were dropped in v0.3.0).
+// codePlanAssets is the source of truth for plan↔asset links (the deprecated
+// array column was dropped in v0.3.0). Plan assignees aren't a stored link at
+// all — see getCodePlan/getCodePlans, which derive them from task.assigneeId.
async function syncPlanAssets(planId: string, assetIds: string[]) {
const existing = await db
@@ -147,15 +146,8 @@ async function syncPlanAssets(planId: string, assetIds: string[]) {
}
}
-async function syncPlanAssignees(planId: string, userIds: string[]) {
- await db.delete(codePlanAssignees).where(eq(codePlanAssignees.codePlanId, planId))
- if (userIds.length > 0) {
- await db.insert(codePlanAssignees).values(userIds.map((userId) => ({ codePlanId: planId, userId })))
- }
-}
-
export async function createCodePlan(data: CreateCodePlanData, userId: string) {
- const { targetAssetIds, assigneeIds, ...columns } = data
+ const { targetAssetIds, ...columns } = data
const [plan] = await db
.insert(codePlans)
.values({
@@ -165,7 +157,6 @@ export async function createCodePlan(data: CreateCodePlanData, userId: string) {
})
.returning()
await syncPlanAssets(plan.id, targetAssetIds)
- await syncPlanAssignees(plan.id, assigneeIds)
return plan
}
@@ -176,7 +167,7 @@ type UpdateCodePlanData = Partial<
>
export async function updateCodePlan(id: string, data: UpdateCodePlanData) {
- const { targetAssetIds, assigneeIds, ...columns } = data
+ const { targetAssetIds, ...columns } = data
const [plan] = await db
.update(codePlans)
.set({ ...columns, updatedAt: new Date() })
@@ -184,7 +175,6 @@ export async function updateCodePlan(id: string, data: UpdateCodePlanData) {
.returning()
if (!plan) return null
if (targetAssetIds !== undefined) await syncPlanAssets(id, targetAssetIds)
- if (assigneeIds !== undefined) await syncPlanAssignees(id, assigneeIds)
return plan
}
diff --git a/lib/db/queries.ts b/lib/db/queries.ts
index 7c05f0e..573b883 100644
--- a/lib/db/queries.ts
+++ b/lib/db/queries.ts
@@ -5,7 +5,6 @@ import {
assetDependencies,
codePlans,
codePlanAssets,
- codePlanAssignees,
workItems,
workItemCodePlans,
tasks,
@@ -316,16 +315,18 @@ export async function getCodePlans(userId: string, filters: PlanFilters = {}): P
.orderBy(desc(codePlans.updatedAt))
const planIds = rows.map((r) => r.id)
- const [assetLinks, assigneeLinks] = planIds.length
+ // Assignees are derived from who's actually assigned to the plan's tasks —
+ // not a separately-maintained list, so they can never drift from reality.
+ const [assetLinks, taskAssigneeLinks] = planIds.length
? await Promise.all([
db
.select({ codePlanId: codePlanAssets.codePlanId, assetId: codePlanAssets.assetId })
.from(codePlanAssets)
.where(inArray(codePlanAssets.codePlanId, planIds)),
db
- .select({ codePlanId: codePlanAssignees.codePlanId, userId: codePlanAssignees.userId })
- .from(codePlanAssignees)
- .where(inArray(codePlanAssignees.codePlanId, planIds)),
+ .select({ codePlanId: tasks.codePlanId, assigneeId: tasks.assigneeId })
+ .from(tasks)
+ .where(and(inArray(tasks.codePlanId, planIds), isNotNull(tasks.assigneeId))),
])
: [[], []]
@@ -333,9 +334,12 @@ export async function getCodePlans(userId: string, filters: PlanFilters = {}): P
for (const l of assetLinks) {
assetsByPlan.set(l.codePlanId, [...(assetsByPlan.get(l.codePlanId) ?? []), l.assetId])
}
- const assigneesByPlan = new Map()
- for (const l of assigneeLinks) {
- assigneesByPlan.set(l.codePlanId, [...(assigneesByPlan.get(l.codePlanId) ?? []), l.userId])
+ const assigneesByPlan = new Map>()
+ for (const l of taskAssigneeLinks) {
+ if (!l.assigneeId) continue
+ const set = assigneesByPlan.get(l.codePlanId) ?? new Set()
+ set.add(l.assigneeId)
+ assigneesByPlan.set(l.codePlanId, set)
}
return rows.map((r) => ({
@@ -343,7 +347,7 @@ export async function getCodePlans(userId: string, filters: PlanFilters = {}): P
ownerId: r.ownerId ?? undefined,
specUrl: r.specUrl ?? undefined,
targetAssetIds: assetsByPlan.get(r.id) ?? [],
- assigneeIds: assigneesByPlan.get(r.id) ?? [],
+ assigneeIds: [...(assigneesByPlan.get(r.id) ?? [])],
startDate: r.startDate ?? undefined,
endDate: r.endDate ?? undefined,
deadline: r.deadline ?? undefined,
@@ -375,7 +379,7 @@ export async function getCodePlan(id: string, userId: string): Promise ({ id: r.assetId, name: r.assetName }))
+ // Assignees are derived from who's actually assigned to the plan's tasks —
+ // not a separately-maintained list, so they can never drift from reality.
+ const assigneeIds = [...new Set(planTasks.map((t) => t.assigneeId).filter((x): x is string => !!x))]
+ const resolvedAssignees = assigneeIds.length
+ ? await db.select({ id: users.id, name: users.name }).from(users).where(inArray(users.id, assigneeIds))
+ : []
+
const taskCount = planTasks.length
const completedTaskCount = planTasks.filter((t) => t.status === 'done').length
diff --git a/lib/db/schema.pg.ts b/lib/db/schema.pg.ts
index 1910251..1cdc39a 100644
--- a/lib/db/schema.pg.ts
+++ b/lib/db/schema.pg.ts
@@ -175,14 +175,6 @@ export const codePlanAssets = pgTable('code_plan_assets', {
uniqueIndex('code_plan_assets_plan_asset_idx').on(t.codePlanId, t.assetId),
])
-export const codePlanAssignees = pgTable('code_plan_assignees', {
- codePlanId: uuid('code_plan_id').notNull().references(() => codePlans.id, { onDelete: 'cascade' }),
- userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
- createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
-}, (t) => [
- primaryKey({ columns: [t.codePlanId, t.userId] }),
-])
-
export const workItems = pgTable('work_items', {
id: uuid('id').primaryKey().defaultRandom(),
productId: uuid('product_id').notNull().references(() => products.id, { onDelete: 'cascade' }),
diff --git a/lib/db/schema.sqlite.ts b/lib/db/schema.sqlite.ts
index c207c8c..c1c34e5 100644
--- a/lib/db/schema.sqlite.ts
+++ b/lib/db/schema.sqlite.ts
@@ -169,14 +169,6 @@ export const codePlanAssets = sqliteTable('code_plan_assets', {
uniqueIndex('code_plan_assets_plan_asset_idx').on(t.codePlanId, t.assetId),
])
-export const codePlanAssignees = sqliteTable('code_plan_assignees', {
- codePlanId: text('code_plan_id').notNull().references(() => codePlans.id, { onDelete: 'cascade' }),
- userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
- createdAt: integer('created_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
-}, (t) => [
- primaryKey({ columns: [t.codePlanId, t.userId] }),
-])
-
export const workItems = sqliteTable('work_items', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
productId: text('product_id').notNull().references(() => products.id, { onDelete: 'cascade' }),
diff --git a/lib/db/schema.ts b/lib/db/schema.ts
index d2232f9..10cffe8 100644
--- a/lib/db/schema.ts
+++ b/lib/db/schema.ts
@@ -20,7 +20,6 @@ export const {
assetDependencies,
codePlans,
codePlanAssets,
- codePlanAssignees,
workItems,
workItemCodePlans,
tasks,
diff --git a/lib/db/seed-demo.ts b/lib/db/seed-demo.ts
index 3b66027..1ea7858 100644
--- a/lib/db/seed-demo.ts
+++ b/lib/db/seed-demo.ts
@@ -18,7 +18,6 @@ import {
assets,
codePlans,
codePlanAssets,
- codePlanAssignees,
tasks,
} from './schema'
import { eq, inArray } from 'drizzle-orm'
@@ -282,7 +281,7 @@ async function seed() {
async function findOrCreatePlan(
title: string,
- values: typeof codePlans.$inferInsert & { targetAssetIds?: string[]; assigneeIds?: string[] },
+ values: typeof codePlans.$inferInsert & { targetAssetIds?: string[] },
) {
const existing = await db.query.codePlans.findFirst({
where: (p, { eq }) => eq(p.title, title),
@@ -291,16 +290,12 @@ async function seed() {
console.log(` plan exists: ${title}`)
return existing.id
}
- const { targetAssetIds: _t, assigneeIds: _a, ...columns } = values
+ const { targetAssetIds: _t, ...columns } = values
const [p] = await db.insert(codePlans).values(columns).returning()
const assetIds = (values.targetAssetIds ?? []) as string[]
if (assetIds.length > 0) {
await db.insert(codePlanAssets).values(assetIds.map((assetId) => ({ codePlanId: p.id, assetId })))
}
- const assigneeIds = (values.assigneeIds ?? []) as string[]
- if (assigneeIds.length > 0) {
- await db.insert(codePlanAssignees).values(assigneeIds.map((userId) => ({ codePlanId: p.id, userId })))
- }
console.log(` created plan: ${title}`)
return p.id
}
@@ -312,7 +307,7 @@ async function seed() {
tags: ['ai', 'llm', 'q2'],
targetAssetIds: [webAppId, planEngineId],
startDate: '2026-04-01', endDate: '2026-05-20', deadline: '2026-06-01',
- creatorId: alexId, assigneeIds: [alexId, mikeId],
+ creatorId: alexId,
})
const collabPlanId = await findOrCreatePlan('Real-time Collaboration', {
@@ -322,7 +317,7 @@ async function seed() {
tags: ['realtime', 'websockets', 'collab'],
targetAssetIds: [webAppId, planEngineId, redisId],
startDate: '2026-04-15', endDate: '2026-06-01', deadline: '2026-06-15',
- creatorId: sarahId, assigneeIds: [sarahId, lisaId],
+ creatorId: sarahId,
})
const uiLibPlanId = await findOrCreatePlan('Component Library v2', {
@@ -332,7 +327,7 @@ async function seed() {
tags: ['frontend', 'a11y', 'design-system'],
targetAssetIds: [uiLibId],
startDate: '2026-03-01', endDate: '2026-04-15', deadline: '2026-04-20',
- creatorId: mikeId, assigneeIds: [mikeId],
+ creatorId: mikeId,
})
const apiV2PlanId = await findOrCreatePlan('API Gateway v2', {
@@ -342,7 +337,7 @@ async function seed() {
tags: ['api', 'backend', 'performance'],
targetAssetIds: [apiGatewayId, workerSvcId],
startDate: '2026-03-20', endDate: '2026-05-10', deadline: '2026-05-20',
- creatorId: sarahId, assigneeIds: [sarahId, lisaId],
+ creatorId: sarahId,
})
const analyticsPlanId = await findOrCreatePlan('Analytics Pipeline Overhaul', {
@@ -352,7 +347,7 @@ async function seed() {
tags: ['analytics', 'performance', 'urgent'],
targetAssetIds: [analyticsDbId],
startDate: '2026-05-01', endDate: '2026-05-31', deadline: '2026-06-07',
- creatorId: alexId, assigneeIds: [alexId, lisaId],
+ creatorId: alexId,
})
const pushPlanId = await findOrCreatePlan('Push Notifications', {
@@ -362,7 +357,7 @@ async function seed() {
tags: ['mobile', 'notifications', 'feature'],
targetAssetIds: [iosId, androidId, bffId],
startDate: '2026-04-10', endDate: '2026-05-15', deadline: '2026-05-20',
- creatorId: sarahId, assigneeIds: [sarahId],
+ creatorId: sarahId,
})
const androidPlanId = await findOrCreatePlan('Android Performance Optimization', {
@@ -372,7 +367,7 @@ async function seed() {
tags: ['mobile', 'android', 'performance'],
targetAssetIds: [androidId],
startDate: '2026-05-10', endDate: '2026-06-05', deadline: '2026-06-10',
- creatorId: lisaId, assigneeIds: [lisaId],
+ creatorId: lisaId,
})
await findOrCreatePlan('SSO & OAuth Integration', {
@@ -382,7 +377,7 @@ async function seed() {
tags: ['auth', 'sso', 'oauth'],
targetAssetIds: [authSvcId],
startDate: '2026-01-20', endDate: '2026-03-10', deadline: '2026-03-15',
- creatorId: alexId, assigneeIds: [alexId, sarahId],
+ creatorId: alexId,
})
await findOrCreatePlan('Database Schema v2', {
@@ -391,7 +386,7 @@ async function seed() {
productId: apiId, type: 'refactor', status: 'draft',
tags: ['database', 'maintenance'],
targetAssetIds: [postgresId],
- creatorId: alexId, assigneeIds: [],
+ creatorId: alexId,
})
// ── Tasks ──────────────────────────────────────────────────────────────────
diff --git a/package.json b/package.json
index 7ddb45a..2fcdf5d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codeplans",
- "version": "0.3.21",
+ "version": "0.3.22",
"description": "Manage and track coordinated changes across your software architecture.",
"author": "Sai Prakash ",
"homepage": "https://codeplans.ai",
diff --git a/tests/helpers/db.ts b/tests/helpers/db.ts
index a4ecc7e..43c4371 100644
--- a/tests/helpers/db.ts
+++ b/tests/helpers/db.ts
@@ -11,7 +11,6 @@ import {
assets,
codePlans,
codePlanAssets,
- codePlanAssignees,
workItems,
workItemCodePlans,
tasks,
@@ -33,7 +32,6 @@ export async function clearTables() {
await d.delete(workItemCodePlans)
await d.delete(workItems)
await d.delete(codePlanAssets)
- await d.delete(codePlanAssignees)
await d.delete(tasks)
await d.delete(codePlans)
await d.delete(assets)
@@ -224,14 +222,12 @@ export async function seedFixtures() {
},
])
- // 7b. Plan↔asset and plan↔assignee join rows (source of truth; arrays kept in step 7
- // to mirror the one-release rollback window)
+ // 7b. Plan↔asset join rows (source of truth for targeted assets). Plan
+ // assignees aren't a stored link — they're derived from task.assigneeId
+ // (task1 below, assigned to bob, is what makes planActive show bob).
await d.insert(codePlanAssets).values([
{ codePlanId: F.planActive, assetId: F.assetApi },
])
- await d.insert(codePlanAssignees).values([
- { codePlanId: F.planActive, userId: F.bob },
- ])
// 8. Tasks
await d.insert(tasks).values([
diff --git a/tests/lib/db/mutations.test.ts b/tests/lib/db/mutations.test.ts
index b7bd1c2..066a2e7 100644
--- a/tests/lib/db/mutations.test.ts
+++ b/tests/lib/db/mutations.test.ts
@@ -178,7 +178,6 @@ describe('createCodePlan', () => {
type: 'improvement',
tags: ['perf'],
targetAssetIds: [F.assetApi],
- assigneeIds: [F.bob],
},
F.alice,
)
@@ -187,10 +186,11 @@ describe('createCodePlan', () => {
expect(plan.status).toBe('draft')
expect(plan.creatorId).toBe(F.alice)
- // Links live in the join tables (array columns dropped in v0.3.0)
+ // Target assets live in the join table (array column dropped in v0.3.0).
+ // Assignees aren't a stored link at all — a fresh plan with no tasks has none.
const detail = await getCodePlan(plan.id, F.alice)
expect(detail!.targetAssetIds).toEqual([F.assetApi])
- expect(detail!.assigneeIds).toEqual([F.bob])
+ expect(detail!.assignees).toEqual([])
})
})
@@ -205,12 +205,9 @@ describe('updateCodePlan', () => {
const updated = await updateCodePlan(F.planActive, {
title: 'Renamed Plan',
tags: ['new-tag'],
- assigneeIds: [],
})
expect(updated!.title).toBe('Renamed Plan')
expect(updated!.tags).toEqual(['new-tag'])
- const detail = await getCodePlan(F.planActive, F.alice)
- expect(detail!.assigneeIds).toEqual([])
})
it('returns null for non-existent plan', async () => {
@@ -219,6 +216,29 @@ describe('updateCodePlan', () => {
})
})
+describe('plan assignees (derived from task assignment)', () => {
+ it('reflects the unique set of assignees across the plan\'s tasks', async () => {
+ // Fixture: planActive already has task1 assigned to bob.
+ let detail = await getCodePlan(F.planActive, F.alice)
+ expect(detail!.assignees.map((a) => a.id)).toEqual([F.bob])
+
+ // Assigning task2 to carol adds her to the derived set.
+ await updateTask(F.task2, { assigneeId: F.carol })
+ detail = await getCodePlan(F.planActive, F.alice)
+ expect(detail!.assignees.map((a) => a.id).sort()).toEqual([F.bob, F.carol].sort())
+
+ // Unassigning task1 removes bob once no task in the plan points to him.
+ await updateTask(F.task1, { assigneeId: null })
+ detail = await getCodePlan(F.planActive, F.alice)
+ expect(detail!.assignees.map((a) => a.id)).toEqual([F.carol])
+ })
+
+ it('a plan with no tasks has no assignees', async () => {
+ const detail = await getCodePlan(F.planDraft, F.alice)
+ expect(detail!.assignees).toEqual([])
+ })
+})
+
describe('deleteCodePlan', () => {
it('deletes plan when userId is creator', async () => {
const result = await deleteCodePlan(F.planDraft, F.alice)