···14141515### Commands
16161717-- `bunx tsc --emit`: Check project TypeScript types
1717+- `bunx run tsc`: Check project TypeScript types
1818- `bun run fmt`: Foramt with oxfmt
1919- `bun run test`: Run unit tests
2020- `bun run test:e2e`: Run end-to-end tests
···11+CREATE TABLE `kanban_task_comments` (
22+ `id` text PRIMARY KEY NOT NULL,
33+ `task_id` text NOT NULL,
44+ `author_did` text NOT NULL,
55+ `content` text NOT NULL,
66+ `pds_uri` text,
77+ `updated_at` text DEFAULT (datetime('now')) NOT NULL,
88+ `hidden_at` text,
99+ `moderated_by` text,
1010+ FOREIGN KEY (`task_id`) REFERENCES `kanban_tasks`(`id`) ON UPDATE no action ON DELETE no action
1111+);
1212+--> statement-breakpoint
1313+CREATE INDEX `idx_kanban_task_comments_task` ON `kanban_task_comments` (`task_id`);--> statement-breakpoint
1414+CREATE INDEX `idx_kanban_task_comments_author_task` ON `kanban_task_comments` (`author_did`,`task_id`);--> statement-breakpoint
1515+CREATE TABLE `kanban_task_status_changes` (
1616+ `id` text PRIMARY KEY NOT NULL,
1717+ `task_id` text NOT NULL,
1818+ `author_did` text NOT NULL,
1919+ `status` text NOT NULL,
2020+ `pds_uri` text,
2121+ FOREIGN KEY (`task_id`) REFERENCES `kanban_tasks`(`id`) ON UPDATE no action ON DELETE no action
2222+);
2323+--> statement-breakpoint
2424+CREATE INDEX `idx_kanban_task_status_changes_task` ON `kanban_task_status_changes` (`task_id`);--> statement-breakpoint
2525+CREATE TABLE `kanban_tasks` (
2626+ `id` text PRIMARY KEY NOT NULL,
2727+ `sphere_id` text NOT NULL,
2828+ `number` integer NOT NULL,
2929+ `author_did` text NOT NULL,
3030+ `title` text NOT NULL,
3131+ `description` text DEFAULT '' NOT NULL,
3232+ `status` text DEFAULT 'backlog' NOT NULL,
3333+ `position` integer DEFAULT 0 NOT NULL,
3434+ `assignee_did` text,
3535+ `pds_uri` text,
3636+ `hidden_at` text,
3737+ `moderated_by` text,
3838+ `updated_at` text DEFAULT (datetime('now')) NOT NULL,
3939+ FOREIGN KEY (`sphere_id`) REFERENCES `spheres`(`id`) ON UPDATE no action ON DELETE no action
4040+);
4141+--> statement-breakpoint
4242+CREATE UNIQUE INDEX `idx_kanban_tasks_sphere_number` ON `kanban_tasks` (`sphere_id`,`number`);--> statement-breakpoint
4343+CREATE INDEX `idx_kanban_tasks_sphere` ON `kanban_tasks` (`sphere_id`);--> statement-breakpoint
4444+CREATE INDEX `idx_kanban_tasks_status` ON `kanban_tasks` (`status`);--> statement-breakpoint
4545+CREATE INDEX `idx_kanban_tasks_sphere_status_position` ON `kanban_tasks` (`sphere_id`,`status`,`position`);
+12
drizzle/0002_far_deadpool.sql
···11+CREATE TABLE `kanban_columns` (
22+ `id` text PRIMARY KEY NOT NULL,
33+ `sphere_id` text NOT NULL,
44+ `slug` text NOT NULL,
55+ `label` text NOT NULL,
66+ `position` integer NOT NULL,
77+ `created_at` text DEFAULT (datetime('now')) NOT NULL,
88+ FOREIGN KEY (`sphere_id`) REFERENCES `spheres`(`id`) ON UPDATE no action ON DELETE no action
99+);
1010+--> statement-breakpoint
1111+CREATE UNIQUE INDEX `idx_kanban_columns_sphere_slug` ON `kanban_columns` (`sphere_id`,`slug`);--> statement-breakpoint
1212+CREATE INDEX `idx_kanban_columns_sphere_position` ON `kanban_columns` (`sphere_id`,`position`);
···88} from "@exosphere/core/permissions";
99// import { feedsModule } from "@exosphere/feeds";
1010import { featureRequestsModule } from "@exosphere/feature-requests";
1111+import { kanbanModule } from "@exosphere/kanban";
11121212-export const modules: ExosphereModule[] = [featureRequestsModule];
1313+export const modules: ExosphereModule[] = [featureRequestsModule, kanbanModule];
13141415// Register core sphere-level permissions.
1516// This runs as a side effect on import — the server (app/src/server.ts) imports from
···11+export type {
22+ KanbanColumn,
33+ KanbanTask,
44+ KanbanTaskComment,
55+ KanbanTaskStatusChange,
66+} from "./db/schema.ts";
77+88+import type { KanbanTask, KanbanTaskComment } from "./db/schema.ts";
99+1010+/** Column definition as returned by the API. */
1111+export type KanbanColumnDef = {
1212+ id: string;
1313+ slug: string;
1414+ label: string;
1515+ position: number;
1616+};
1717+1818+/** Shape returned by the GET /kanban list endpoint. */
1919+export type KanbanTaskListItem = KanbanTask & {
2020+ createdAt: string;
2121+ commentCount: number;
2222+ authorHandle?: string | null;
2323+ assigneeHandle?: string | null;
2424+};
2525+2626+/** Shape returned by the GET /kanban/:number detail endpoint. */
2727+export type KanbanTaskDetail = KanbanTaskListItem;
2828+2929+/** Shape returned by the GET comments endpoint. */
3030+export type KanbanTaskCommentListItem = KanbanTaskComment & {
3131+ createdAt: string;
3232+ authorHandle: string | null;
3333+};