🗃️ chore: add userId to all task sub-tables and consolidate handoff into jsonb

- Add userId (NOT NULL, FK cascade) to taskDependencies, taskDocuments, taskComments
- Rename taskComments userId/agentId to authorUserId/authorAgentId for clarity
- Consolidate 4 handoff columns into single jsonb field on taskTopics
- Add updatedAt/accessedAt timestamps to taskTopics
- Merge migration 0096 into 0095 (feature branch, not yet released)
- Change default identifier prefix from TASK to T

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
arvinxx
2026-03-25 15:52:26 +08:00
parent 1b41fd8002
commit 63f0f72c81
6 changed files with 44 additions and 14166 deletions

View File

@@ -22,8 +22,9 @@ CREATE TABLE IF NOT EXISTS "briefs" (
CREATE TABLE IF NOT EXISTS "task_comments" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"task_id" text NOT NULL,
"user_id" text,
"agent_id" text,
"user_id" text NOT NULL,
"author_user_id" text,
"author_agent_id" text,
"content" text NOT NULL,
"editor_data" jsonb,
"brief_id" uuid,
@@ -37,6 +38,7 @@ CREATE TABLE IF NOT EXISTS "task_dependencies" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"task_id" text NOT NULL,
"depends_on_id" text NOT NULL,
"user_id" text NOT NULL,
"type" text DEFAULT 'blocks' NOT NULL,
"condition" jsonb,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
@@ -46,6 +48,7 @@ CREATE TABLE IF NOT EXISTS "task_documents" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"task_id" text NOT NULL,
"document_id" text NOT NULL,
"user_id" text NOT NULL,
"pinned_by" text DEFAULT 'agent' NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
@@ -54,15 +57,19 @@ CREATE TABLE IF NOT EXISTS "task_topics" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"task_id" text NOT NULL,
"topic_id" text NOT NULL,
"user_id" text NOT NULL,
"seq" integer NOT NULL,
"operation_id" text,
"status" text DEFAULT 'running' NOT NULL,
"handoff" jsonb,
"review_passed" integer,
"review_score" integer,
"review_scores" jsonb,
"review_iteration" integer,
"reviewed_at" timestamp with time zone,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
"accessed_at" timestamp with time zone DEFAULT now() NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "tasks" (
@@ -106,16 +113,24 @@ ALTER TABLE "briefs" DROP CONSTRAINT IF EXISTS "briefs_cron_job_id_agent_cron_jo
ALTER TABLE "briefs" ADD CONSTRAINT "briefs_cron_job_id_agent_cron_jobs_id_fk" FOREIGN KEY ("cron_job_id") REFERENCES "public"."agent_cron_jobs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "task_comments" DROP CONSTRAINT IF EXISTS "task_comments_task_id_tasks_id_fk";--> statement-breakpoint
ALTER TABLE "task_comments" ADD CONSTRAINT "task_comments_task_id_tasks_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "task_comments" DROP CONSTRAINT IF EXISTS "task_comments_user_id_users_id_fk";--> statement-breakpoint
ALTER TABLE "task_comments" ADD CONSTRAINT "task_comments_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "task_dependencies" DROP CONSTRAINT IF EXISTS "task_dependencies_task_id_tasks_id_fk";--> statement-breakpoint
ALTER TABLE "task_dependencies" ADD CONSTRAINT "task_dependencies_task_id_tasks_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "task_dependencies" DROP CONSTRAINT IF EXISTS "task_dependencies_depends_on_id_tasks_id_fk";--> statement-breakpoint
ALTER TABLE "task_dependencies" ADD CONSTRAINT "task_dependencies_depends_on_id_tasks_id_fk" FOREIGN KEY ("depends_on_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "task_dependencies" DROP CONSTRAINT IF EXISTS "task_dependencies_user_id_users_id_fk";--> statement-breakpoint
ALTER TABLE "task_dependencies" ADD CONSTRAINT "task_dependencies_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "task_documents" DROP CONSTRAINT IF EXISTS "task_documents_task_id_tasks_id_fk";--> statement-breakpoint
ALTER TABLE "task_documents" ADD CONSTRAINT "task_documents_task_id_tasks_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "task_documents" DROP CONSTRAINT IF EXISTS "task_documents_document_id_documents_id_fk";--> statement-breakpoint
ALTER TABLE "task_documents" ADD CONSTRAINT "task_documents_document_id_documents_id_fk" FOREIGN KEY ("document_id") REFERENCES "public"."documents"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "task_documents" DROP CONSTRAINT IF EXISTS "task_documents_user_id_users_id_fk";--> statement-breakpoint
ALTER TABLE "task_documents" ADD CONSTRAINT "task_documents_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "task_topics" DROP CONSTRAINT IF EXISTS "task_topics_task_id_tasks_id_fk";--> statement-breakpoint
ALTER TABLE "task_topics" ADD CONSTRAINT "task_topics_task_id_tasks_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "task_topics" DROP CONSTRAINT IF EXISTS "task_topics_user_id_users_id_fk";--> statement-breakpoint
ALTER TABLE "task_topics" ADD CONSTRAINT "task_topics_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tasks" DROP CONSTRAINT IF EXISTS "tasks_created_by_user_id_users_id_fk";--> statement-breakpoint
ALTER TABLE "tasks" ADD CONSTRAINT "tasks_created_by_user_id_users_id_fk" FOREIGN KEY ("created_by_user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "briefs_user_id_idx" ON "briefs" USING btree ("user_id");--> statement-breakpoint
@@ -127,18 +142,22 @@ CREATE INDEX IF NOT EXISTS "briefs_priority_idx" ON "briefs" USING btree ("prior
CREATE INDEX IF NOT EXISTS "briefs_unresolved_idx" ON "briefs" USING btree ("user_id","resolved_at");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_comments_task_id_idx" ON "task_comments" USING btree ("task_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_comments_user_id_idx" ON "task_comments" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_comments_agent_id_idx" ON "task_comments" USING btree ("agent_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_comments_author_user_id_idx" ON "task_comments" USING btree ("author_user_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_comments_agent_id_idx" ON "task_comments" USING btree ("author_agent_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_comments_brief_id_idx" ON "task_comments" USING btree ("brief_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_comments_topic_id_idx" ON "task_comments" USING btree ("topic_id");--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "task_deps_unique_idx" ON "task_dependencies" USING btree ("task_id","depends_on_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_deps_task_id_idx" ON "task_dependencies" USING btree ("task_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_deps_depends_on_id_idx" ON "task_dependencies" USING btree ("depends_on_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_deps_user_id_idx" ON "task_dependencies" USING btree ("user_id");--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "task_docs_unique_idx" ON "task_documents" USING btree ("task_id","document_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_docs_task_id_idx" ON "task_documents" USING btree ("task_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_docs_document_id_idx" ON "task_documents" USING btree ("document_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_docs_user_id_idx" ON "task_documents" USING btree ("user_id");--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "task_topics_unique_idx" ON "task_topics" USING btree ("task_id","topic_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_topics_task_id_idx" ON "task_topics" USING btree ("task_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_topics_topic_id_idx" ON "task_topics" USING btree ("topic_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_topics_user_id_idx" ON "task_topics" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "task_topics_status_idx" ON "task_topics" USING btree ("task_id","status");--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "tasks_identifier_idx" ON "tasks" USING btree ("identifier","created_by_user_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "tasks_created_by_user_id_idx" ON "tasks" USING btree ("created_by_user_id");--> statement-breakpoint

View File

@@ -1,7 +0,0 @@
ALTER TABLE "task_topics" ADD COLUMN "user_id" text NOT NULL;--> statement-breakpoint
ALTER TABLE "task_topics" ADD COLUMN "handoff_title" text;--> statement-breakpoint
ALTER TABLE "task_topics" ADD COLUMN "handoff_summary" text;--> statement-breakpoint
ALTER TABLE "task_topics" ADD COLUMN "handoff_key_findings" jsonb;--> statement-breakpoint
ALTER TABLE "task_topics" ADD COLUMN "handoff_next_action" text;--> statement-breakpoint
ALTER TABLE "task_topics" ADD CONSTRAINT "task_topics_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "task_topics_user_id_idx" ON "task_topics" USING btree ("user_id");

File diff suppressed because it is too large Load Diff

View File

@@ -672,13 +672,6 @@
"when": 1774180022586,
"tag": "0095_add_agent_task_system",
"breakpoints": true
},
{
"idx": 96,
"version": "7",
"when": 1774189914853,
"tag": "0096_lush_praxagora",
"breakpoints": true
}
],
"version": "6"

View File

@@ -311,7 +311,7 @@ export class TaskModel {
async addDependency(taskId: string, dependsOnId: string, type: string = 'blocks'): Promise<void> {
await this.db
.insert(taskDependencies)
.values({ dependsOnId, taskId, type })
.values({ dependsOnId, taskId, type, userId: this.userId })
.onConflictDoNothing();
}
@@ -397,7 +397,7 @@ export class TaskModel {
async pinDocument(taskId: string, documentId: string, pinnedBy: string = 'agent'): Promise<void> {
await this.db
.insert(taskDocuments)
.values({ documentId, pinnedBy, taskId })
.values({ documentId, pinnedBy, taskId, userId: this.userId })
.onConflictDoNothing();
}
@@ -516,6 +516,7 @@ export class TaskModel {
const result = await this.db
.delete(taskComments)
.where(and(eq(taskComments.id, id), eq(taskComments.userId, this.userId)))
.returning();
return result.length > 0;
}

View File

@@ -95,6 +95,9 @@ export const taskDependencies = pgTable(
dependsOnId: text('depends_on_id')
.references(() => tasks.id, { onDelete: 'cascade' })
.notNull(),
userId: text('user_id')
.references(() => users.id, { onDelete: 'cascade' })
.notNull(),
// 'blocks' | 'relates'
type: text('type').notNull().default('blocks'),
@@ -108,6 +111,7 @@ export const taskDependencies = pgTable(
uniqueIndex('task_deps_unique_idx').on(t.taskId, t.dependsOnId),
index('task_deps_task_id_idx').on(t.taskId),
index('task_deps_depends_on_id_idx').on(t.dependsOnId),
index('task_deps_user_id_idx').on(t.userId),
],
);
@@ -126,6 +130,9 @@ export const taskDocuments = pgTable(
documentId: text('document_id')
.references(() => documents.id, { onDelete: 'cascade' })
.notNull(),
userId: text('user_id')
.references(() => users.id, { onDelete: 'cascade' })
.notNull(),
// 'agent' | 'user' | 'system'
pinnedBy: text('pinned_by').notNull().default('agent'),
@@ -136,6 +143,7 @@ export const taskDocuments = pgTable(
uniqueIndex('task_docs_unique_idx').on(t.taskId, t.documentId),
index('task_docs_task_id_idx').on(t.taskId),
index('task_docs_document_id_idx').on(t.documentId),
index('task_docs_user_id_idx').on(t.userId),
],
);
@@ -162,10 +170,8 @@ export const taskTopics = pgTable(
status: text('status').notNull().default('running'),
// Handoff (populated after topic completes via LLM summarization)
handoffTitle: text('handoff_title'),
handoffSummary: text('handoff_summary'),
handoffKeyFindings: jsonb('handoff_key_findings'), // string[]
handoffNextAction: text('handoff_next_action'),
// { title, summary, keyFindings: string[], nextAction }
handoff: jsonb('handoff'),
// Review results (populated after topic completes + review runs)
reviewPassed: integer('review_passed'), // 1 = passed, 0 = failed, null = not reviewed
@@ -174,7 +180,7 @@ export const taskTopics = pgTable(
reviewIteration: integer('review_iteration'), // which iteration (1, 2, 3...)
reviewedAt: timestamptz('reviewed_at'),
createdAt: createdAt(),
...timestamps,
},
(t) => [
uniqueIndex('task_topics_unique_idx').on(t.taskId, t.topicId),
@@ -197,10 +203,13 @@ export const taskComments = pgTable(
taskId: text('task_id')
.references(() => tasks.id, { onDelete: 'cascade' })
.notNull(),
userId: text('user_id')
.references(() => users.id, { onDelete: 'cascade' })
.notNull(),
// Author (one of)
userId: text('user_id'),
agentId: text('agent_id'),
authorUserId: text('author_user_id'),
authorAgentId: text('author_agent_id'),
// Content
content: text('content').notNull(),
@@ -215,7 +224,8 @@ export const taskComments = pgTable(
(t) => [
index('task_comments_task_id_idx').on(t.taskId),
index('task_comments_user_id_idx').on(t.userId),
index('task_comments_agent_id_idx').on(t.agentId),
index('task_comments_author_user_id_idx').on(t.authorUserId),
index('task_comments_agent_id_idx').on(t.authorAgentId),
index('task_comments_brief_id_idx').on(t.briefId),
index('task_comments_topic_id_idx').on(t.topicId),
],