kanbo-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/better-sqlite3-fU3G4xnX.cjs +2254 -0
- package/dist/better-sqlite3-fU3G4xnX.cjs.map +1 -0
- package/dist/cli.cjs +42949 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/dist-Cd6wg8pp.cjs +942 -0
- package/dist/dist-Cd6wg8pp.cjs.map +1 -0
- package/dist/drizzle-postgres/0000_board.sql +135 -0
- package/dist/drizzle-postgres/0001_history_order.sql +2 -0
- package/dist/drizzle-postgres/0002_issue_pull_requests.sql +16 -0
- package/dist/drizzle-postgres/0003_milestone_start_date.sql +1 -0
- package/dist/drizzle-postgres/0004_status_entry_rules.sql +1 -0
- package/dist/drizzle-postgres/0005_run_external_session_ref.sql +1 -0
- package/dist/drizzle-postgres/README.md +61 -0
- package/dist/drizzle-postgres/meta/0000_snapshot.json +1078 -0
- package/dist/drizzle-postgres/meta/0001_snapshot.json +1090 -0
- package/dist/drizzle-postgres/meta/0002_snapshot.json +1239 -0
- package/dist/drizzle-postgres/meta/0003_snapshot.json +1245 -0
- package/dist/drizzle-postgres/meta/0004_snapshot.json +1251 -0
- package/dist/drizzle-postgres/meta/0005_snapshot.json +1257 -0
- package/dist/drizzle-postgres/meta/_journal.json +48 -0
- package/dist/drizzle-sqlite/0000_board.sql +133 -0
- package/dist/drizzle-sqlite/0001_issue_pull_requests.sql +15 -0
- package/dist/drizzle-sqlite/0002_milestone_start_date.sql +1 -0
- package/dist/drizzle-sqlite/0003_status_entry_rules.sql +1 -0
- package/dist/drizzle-sqlite/0004_run_external_session_ref.sql +1 -0
- package/dist/drizzle-sqlite/README.md +67 -0
- package/dist/drizzle-sqlite/meta/0000_snapshot.json +946 -0
- package/dist/drizzle-sqlite/meta/0001_snapshot.json +1054 -0
- package/dist/drizzle-sqlite/meta/0002_snapshot.json +1061 -0
- package/dist/drizzle-sqlite/meta/0003_snapshot.json +1068 -0
- package/dist/drizzle-sqlite/meta/0004_snapshot.json +1075 -0
- package/dist/drizzle-sqlite/meta/_journal.json +41 -0
- package/dist/lib/board-store-B16YarvX.d.mts +281 -0
- package/dist/lib/board-store-BHHo_ZlR.d.cts +281 -0
- package/dist/lib/index.cjs +340 -0
- package/dist/lib/index.cjs.map +1 -0
- package/dist/lib/index.d.cts +1608 -0
- package/dist/lib/index.d.mts +1608 -0
- package/dist/lib/index.mjs +284 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/manifest-C0EcwwgO.mjs +7866 -0
- package/dist/lib/manifest-C0EcwwgO.mjs.map +1 -0
- package/dist/lib/manifest-CpZOcB_6.cjs +8272 -0
- package/dist/lib/manifest-CpZOcB_6.cjs.map +1 -0
- package/dist/lib/mcp/index.cjs +184 -0
- package/dist/lib/mcp/index.cjs.map +1 -0
- package/dist/lib/mcp/index.d.cts +346 -0
- package/dist/lib/mcp/index.d.mts +346 -0
- package/dist/lib/mcp/index.mjs +173 -0
- package/dist/lib/mcp/index.mjs.map +1 -0
- package/dist/lib/migrate-BhYi9cXw.mjs +656 -0
- package/dist/lib/migrate-BhYi9cXw.mjs.map +1 -0
- package/dist/lib/migrate-DnMjTCvu.cjs +709 -0
- package/dist/lib/migrate-DnMjTCvu.cjs.map +1 -0
- package/dist/lib/migrations-path-CZxVOgeD.cjs +109 -0
- package/dist/lib/migrations-path-CZxVOgeD.cjs.map +1 -0
- package/dist/lib/migrations-path-Ciwan537.mjs +80 -0
- package/dist/lib/migrations-path-Ciwan537.mjs.map +1 -0
- package/dist/lib/postgres/index.cjs +56 -0
- package/dist/lib/postgres/index.cjs.map +1 -0
- package/dist/lib/postgres/index.d.cts +3403 -0
- package/dist/lib/postgres/index.d.mts +3403 -0
- package/dist/lib/postgres/index.mjs +35 -0
- package/dist/lib/postgres/index.mjs.map +1 -0
- package/dist/lib/roles.sql-B9rt-x4o.cjs +1033 -0
- package/dist/lib/roles.sql-B9rt-x4o.cjs.map +1 -0
- package/dist/lib/roles.sql-CvyCGt38.mjs +920 -0
- package/dist/lib/roles.sql-CvyCGt38.mjs.map +1 -0
- package/dist/lib/schema-epoch-ZyumVL_A.d.cts +16 -0
- package/dist/lib/schema-epoch-ZyumVL_A.d.mts +16 -0
- package/dist/lib/schema-fsHqNAip.d.cts +3612 -0
- package/dist/lib/schema-fsHqNAip.d.mts +3612 -0
- package/dist/lib/sqlite/index.cjs +23 -0
- package/dist/lib/sqlite/index.d.cts +175 -0
- package/dist/lib/sqlite/index.d.mts +175 -0
- package/dist/lib/sqlite/index.mjs +4 -0
- package/dist/lib/sqlite/schema.cjs +266 -0
- package/dist/lib/sqlite/schema.cjs.map +1 -0
- package/dist/lib/sqlite/schema.d.cts +2 -0
- package/dist/lib/sqlite/schema.d.mts +2 -0
- package/dist/lib/sqlite/schema.mjs +256 -0
- package/dist/lib/sqlite/schema.mjs.map +1 -0
- package/dist/lib/tool-names-9cmCn0gP.d.cts +112 -0
- package/dist/lib/tool-names-Dxg1UTUl.d.mts +112 -0
- package/dist/page/board.css +477 -0
- package/dist/page/board.js +2 -0
- package/drizzle-postgres/0000_board.sql +135 -0
- package/drizzle-postgres/0001_history_order.sql +2 -0
- package/drizzle-postgres/0002_issue_pull_requests.sql +16 -0
- package/drizzle-postgres/0003_milestone_start_date.sql +1 -0
- package/drizzle-postgres/0004_status_entry_rules.sql +1 -0
- package/drizzle-postgres/0005_run_external_session_ref.sql +1 -0
- package/drizzle-postgres/README.md +61 -0
- package/drizzle-postgres/meta/0000_snapshot.json +1078 -0
- package/drizzle-postgres/meta/0001_snapshot.json +1090 -0
- package/drizzle-postgres/meta/0002_snapshot.json +1239 -0
- package/drizzle-postgres/meta/0003_snapshot.json +1245 -0
- package/drizzle-postgres/meta/0004_snapshot.json +1251 -0
- package/drizzle-postgres/meta/0005_snapshot.json +1257 -0
- package/drizzle-postgres/meta/_journal.json +48 -0
- package/drizzle-sqlite/0000_board.sql +133 -0
- package/drizzle-sqlite/0001_issue_pull_requests.sql +15 -0
- package/drizzle-sqlite/0002_milestone_start_date.sql +1 -0
- package/drizzle-sqlite/0003_status_entry_rules.sql +1 -0
- package/drizzle-sqlite/0004_run_external_session_ref.sql +1 -0
- package/drizzle-sqlite/README.md +67 -0
- package/drizzle-sqlite/meta/0000_snapshot.json +946 -0
- package/drizzle-sqlite/meta/0001_snapshot.json +1054 -0
- package/drizzle-sqlite/meta/0002_snapshot.json +1061 -0
- package/drizzle-sqlite/meta/0003_snapshot.json +1068 -0
- package/drizzle-sqlite/meta/0004_snapshot.json +1075 -0
- package/drizzle-sqlite/meta/_journal.json +41 -0
- package/package.json +147 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roles.sql-CvyCGt38.mjs","names":[],"sources":["../../src/postgres/schema.ts","../../src/postgres/board-store.postgres.ts","../../src/postgres/migrations-path.ts","../../src/postgres/open-database.ts","../../src/postgres/roles.sql.ts"],"sourcesContent":["import { sql } from 'drizzle-orm'\nimport type { AnyPgColumn } from 'drizzle-orm/pg-core'\nimport { bigint, bigserial, index, integer, pgTable, text, uniqueIndex } from 'drizzle-orm/pg-core'\n\n/**\n * The board, as a Postgres database holds it.\n *\n * This is the same logical schema as `src/sqlite/schema.ts` — same column\n * names, same nullability, same enum members, same index names — written for a\n * second dialect rather than a second design. The store reads and writes both\n * through one `BoardStore` interface, so anything that drifts here shows up as\n * a card that reads differently depending on where its board lives.\n *\n * Two deliberate differences from the SQLite file:\n *\n * - **Unix seconds are `bigint`, read as `number`.** SQLite stores them in an\n * `integer` wide enough for them; Postgres's `integer` is 32-bit, and\n * `mode: 'number'` keeps the value a JavaScript number so the store hands\n * `ops` and `domain` exactly what the SQLite store hands them.\n * - **`seq` on `issue_runs`, `issue_comments`, `issue_field_changes` and\n * `issue_pull_requests`.**\n * SQLite orders those four by `rowid` — insertion order, which two rows\n * written in the same second still separate. Postgres has no `rowid`, and its\n * heap order is free to change (a row written last can land in the slot a\n * deleted one freed), so the board carries its own order: a `bigserial` those\n * queries order by, and the only columns here that have no SQLite\n * counterpart.\n *\n * What used to be a third difference is one no longer: `workspace_id`,\n * `delegate_agent_id`, `delegate_provider_target_id` and\n * `source_chat_session_id` are plain text here *and* in the file (ruling 5-1),\n * because a database holding only the board was never the only home without a\n * `workspaces` table to point at.\n *\n * `labels` and `context_refs` stay `text` holding JSON rather than becoming\n * `jsonb`: they are encoded and decoded above the store, and a second\n * representation would be a second thing to keep in step for nothing.\n */\n\n/** `unixepoch()` in the dialect that has no such function. */\nconst unixNow = sql`(extract(epoch from now())::bigint)`\n\nconst textPk = () => text('id').primaryKey()\n\nconst createdAt = () => ({\n createdAt: bigint('created_at', { mode: 'number' }).notNull().default(unixNow),\n})\n\nconst timestamps = () => ({\n ...createdAt(),\n updatedAt: bigint('updated_at', { mode: 'number' }).notNull().default(unixNow),\n})\n\nexport const issueStatuses = pgTable('issue_statuses', {\n id: textPk(),\n workspaceId: text('workspace_id').notNull(),\n name: text('name').notNull(),\n description: text('description'),\n color: text('color'),\n category: text('category', { enum: ['triage', 'backlog', 'unstarted', 'started', 'completed', 'canceled'] }).notNull().default('unstarted'),\n order: integer('order').notNull().default(0),\n /** A JSON array of `ENTRY_RULES`, or `null` for none — see the SQLite schema. */\n entryRules: text('entry_rules'),\n ...createdAt(),\n}, table => [\n index('issue_statuses_workspace_id_idx').on(table.workspaceId),\n uniqueIndex('issue_statuses_workspace_name_unique').on(table.workspaceId, table.name),\n])\n\nexport const issueMilestones = pgTable('issue_milestones', {\n id: textPk(),\n workspaceId: text('workspace_id').notNull(),\n title: text('title').notNull(),\n description: text('description'),\n /** When the milestone, read as a sprint, begins (unix seconds); null for a plain milestone. */\n startDate: bigint('start_date', { mode: 'number' }),\n dueDate: bigint('due_date', { mode: 'number' }),\n status: text('status', { enum: ['open', 'closed'] }).notNull().default('open'),\n ...timestamps(),\n}, table => [\n index('issue_milestones_workspace_id_idx').on(table.workspaceId),\n])\n\nexport const issues = pgTable('issues', {\n id: textPk(),\n workspaceId: text('workspace_id').notNull(),\n number: integer('number').notNull(),\n statusId: text('status_id').references(() => issueStatuses.id, { onDelete: 'set null' }),\n milestoneId: text('milestone_id').references(() => issueMilestones.id, { onDelete: 'set null' }),\n parentIssueId: text('parent_issue_id')\n .references((): AnyPgColumn => issues.id, { onDelete: 'set null' }),\n title: text('title').notNull(),\n description: text('description'),\n priority: text('priority', {\n enum: ['none', 'low', 'medium', 'high', 'urgent'],\n }).notNull().default('none'),\n labels: text('labels').notNull().default('[]'),\n assigneeKind: text('assignee_kind'),\n assigneeId: text('assignee_id'),\n dueDate: bigint('due_date', { mode: 'number' }),\n createdByKind: text('created_by_kind', { enum: ['user', 'agent', 'system', 'provider-target'] }).notNull().default('user'),\n createdById: text('created_by_id').notNull().default('__self__'),\n sourceChatSessionId: text('source_chat_session_id'),\n delegateAgentId: text('delegate_agent_id'),\n delegateProviderTargetId: text('delegate_provider_target_id'),\n contextRefs: text('context_refs').notNull().default('[]'),\n statusLine: text('status_line'),\n waitingFor: text('waiting_for', { enum: ['human'] }),\n executionMode: text('execution_mode', { enum: ['worktree', 'main'] }).notNull().default('worktree'),\n order: integer('order').notNull().default(0),\n ...timestamps(),\n}, table => [\n index('issues_workspace_id_idx').on(table.workspaceId),\n uniqueIndex('issues_workspace_number_unique').on(table.workspaceId, table.number),\n index('issues_status_id_idx').on(table.statusId),\n index('issues_milestone_id_idx').on(table.milestoneId),\n index('issues_parent_issue_id_idx').on(table.parentIssueId),\n index('issues_delegate_agent_id_idx').on(table.delegateAgentId),\n index('issues_delegate_provider_target_id_idx').on(table.delegateProviderTargetId),\n])\n\nexport const issueComments = pgTable('issue_comments', {\n id: textPk(),\n issueId: text('issue_id')\n .notNull()\n .references(() => issues.id, { onDelete: 'cascade' }),\n content: text('content').notNull(),\n authorKind: text('author_kind', {\n enum: [\n 'user',\n 'agent',\n 'provider-target',\n 'system',\n 'system.delegated',\n 'system.undelegated',\n 'system.run',\n 'system.approved',\n 'system.returned',\n 'system.pr',\n ],\n }).notNull().default('user'),\n authorId: text('author_id'),\n sourceChatSessionId: text('source_chat_session_id'),\n agentActivityId: text('agent_activity_id'),\n dedupeKey: text('dedupe_key'),\n ...createdAt(),\n /** Insertion order, standing in for the `rowid` the SQLite board orders comments by. */\n seq: bigserial('seq', { mode: 'number' }).notNull(),\n}, table => [\n index('issue_comments_issue_id_idx').on(table.issueId),\n uniqueIndex('issue_comments_issue_dedupe_idx').on(table.issueId, table.dedupeKey),\n])\n\nexport const issueRuns = pgTable('issue_runs', {\n id: textPk(),\n issueId: text('issue_id')\n .notNull()\n .references(() => issues.id, { onDelete: 'cascade' }),\n launchedByKind: text('launched_by_kind', { enum: ['user', 'agent', 'external'] }).notNull().default('user'),\n launchedById: text('launched_by_id'),\n agentId: text('agent_id'),\n agentName: text('agent_name').notNull(),\n host: text('host'),\n executionMode: text('execution_mode', { enum: ['worktree', 'main'] }).notNull().default('worktree'),\n branch: text('branch'),\n worktreePath: text('worktree_path'),\n state: text('state', { enum: ['running', 'finished', 'failed', 'stopped'] }).notNull().default('running'),\n chatSessionId: text('chat_session_id'),\n /** Where an external agent's own log of this run lives — `claude:<id>` or `codex:<id>` (ruling 7-1), `null` when it never said. */\n externalSessionRef: text('external_session_ref'),\n startedAt: bigint('started_at', { mode: 'number' }).notNull().default(unixNow),\n endedAt: bigint('ended_at', { mode: 'number' }),\n /** Insertion order, standing in for the `rowid` the SQLite board orders runs by. */\n seq: bigserial('seq', { mode: 'number' }).notNull(),\n}, table => [\n index('issue_runs_issue_id_idx').on(table.issueId),\n index('issue_runs_issue_state_idx').on(table.issueId, table.state),\n index('issue_runs_chat_session_idx').on(table.chatSessionId),\n])\n\nexport const issueRelations = pgTable('issue_relations', {\n id: textPk(),\n sourceIssueId: text('source_issue_id')\n .notNull()\n .references(() => issues.id, { onDelete: 'cascade' }),\n targetIssueId: text('target_issue_id')\n .notNull()\n .references(() => issues.id, { onDelete: 'cascade' }),\n type: text('type', { enum: ['blocks', 'duplicates', 'relates_to'] }).notNull(),\n ...createdAt(),\n}, table => [\n index('issue_relations_source_issue_id_idx').on(table.sourceIssueId),\n index('issue_relations_target_issue_id_idx').on(table.targetIssueId),\n uniqueIndex('issue_relations_pair_type_unique')\n .on(table.sourceIssueId, table.targetIssueId, table.type),\n])\n\nexport const issueFieldChanges = pgTable('issue_field_changes', {\n id: textPk(),\n issueId: text('issue_id')\n .notNull()\n .references(() => issues.id, { onDelete: 'cascade' }),\n field: text('field').notNull(),\n fromValue: text('from_value'),\n toValue: text('to_value'),\n actorKind: text('actor_kind', { enum: ['user', 'agent', 'provider-target', 'system'] }).notNull().default('user'),\n actorId: text('actor_id'),\n sourceChatSessionId: text('source_chat_session_id'),\n ...createdAt(),\n /** Insertion order, standing in for the `rowid` the SQLite board orders changes by. */\n seq: bigserial('seq', { mode: 'number' }).notNull(),\n}, table => [\n index('issue_field_changes_issue_id_idx').on(table.issueId),\n])\n\n/** A pull request someone said belongs to a card — see the SQLite twin for what the row is and is not. */\nexport const issuePullRequests = pgTable('issue_pull_requests', {\n id: textPk(),\n issueId: text('issue_id')\n .notNull()\n .references(() => issues.id, { onDelete: 'cascade' }),\n owner: text('owner').notNull(),\n repo: text('repo').notNull(),\n number: integer('number').notNull(),\n url: text('url').notNull(),\n createdByKind: text('created_by_kind', { enum: ['user', 'agent', 'provider-target', 'system'] }).notNull(),\n createdById: text('created_by_id'),\n ...createdAt(),\n /** Insertion order, standing in for the `rowid` the SQLite board orders a card's links by. */\n seq: bigserial('seq', { mode: 'number' }).notNull(),\n}, table => [\n uniqueIndex('issue_pull_requests_issue_pr_unique')\n .on(table.issueId, table.owner, table.repo, table.number),\n index('issue_pull_requests_pr_idx').on(table.owner, table.repo, table.number),\n])\n\n/**\n * The counters the board keeps for its readers, exactly as the SQLite file\n * keeps them: `board` is bumped once per write transaction that changed\n * something, and `schema_epoch` records the generation the board was created\n * with.\n */\nexport const kanbanMeta = pgTable('kanban_meta', {\n key: text('key').primaryKey(),\n revision: integer('revision').notNull().default(0),\n updatedAt: bigint('updated_at', { mode: 'number' }).notNull().default(unixNow),\n})\n\n/** Every table of the board, in the order a reader of the schema wants them. */\nexport const boardPostgresSchema = {\n issueStatuses,\n issueMilestones,\n issues,\n issueComments,\n issueRuns,\n issueRelations,\n issueFieldChanges,\n issuePullRequests,\n kanbanMeta,\n}\n\n/** The table names a Postgres board must carry, for the guard that refuses a database without them. */\nexport const BOARD_POSTGRES_TABLES = [\n 'issue_statuses',\n 'issue_milestones',\n 'issues',\n 'issue_comments',\n 'issue_runs',\n 'issue_relations',\n 'issue_field_changes',\n 'issue_pull_requests',\n 'kanban_meta',\n] as const\n","import { and, asc, desc, eq, getTableColumns, inArray, isNotNull, or, sql } from 'drizzle-orm'\n\nimport type {\n BoardLinkedPullRequest,\n BoardRunProjection,\n BoardRunProjectionScope,\n BoardStore,\n BoardTransactionMode,\n} from '../board-store'\nimport type { IssueComment, IssueFieldChange, IssuePullRequest, IssueRun } from '../sqlite/schema'\nimport type { BoardPostgresDatabase } from './open-database'\nimport {\n issueComments,\n issueFieldChanges,\n issueMilestones,\n issuePullRequests,\n issueRelations,\n issueRuns,\n issues,\n issueStatuses,\n kanbanMeta,\n} from './schema'\n\n/** A run as Postgres keeps it: the board's run, plus the insertion order the board orders by. */\ntype StoredRun = typeof issueRuns.$inferSelect\n\n/** A comment as Postgres keeps it, and a field change: the same, plus `seq`. */\ntype StoredComment = typeof issueComments.$inferSelect\ntype StoredFieldChange = typeof issueFieldChanges.$inferSelect\ntype StoredPullRequest = typeof issuePullRequests.$inferSelect\n\n/**\n * The run without `seq`.\n *\n * `seq` is this dialect's answer to SQLite's `rowid` — the board's own record of\n * which of two runs started in the same second was written first. It is a fact\n * about the storage rather than about the run, so it is taken off here instead\n * of leaking into a type shared with a board that has no such column.\n */\nfunction toIssueRun({ seq: _seq, ...run }: StoredRun): IssueRun {\n return run\n}\n\n/** The comment without `seq`, for the same reason a run is handed over without it. */\nfunction toIssueComment({ seq: _seq, ...comment }: StoredComment): IssueComment {\n return comment\n}\n\n/** The field change without `seq`, for the same reason. */\nfunction toIssueFieldChange({ seq: _seq, ...change }: StoredFieldChange): IssueFieldChange {\n return change\n}\n\n/** The pull-request link without `seq`, for the same reason. */\nfunction toIssuePullRequest({ seq: _seq, ...link }: StoredPullRequest): IssuePullRequest {\n return link\n}\n\n/** A link with its card's workspace, without `seq`. */\nfunction toLinkedPullRequest({ seq: _seq, ...link }: StoredPullRequest & { workspaceId: string }): BoardLinkedPullRequest {\n return link\n}\n\n/**\n * The run facts of every card in scope, in one statement.\n *\n * The same shape as the SQLite store's: `count(*)` counts the card's runs on\n * every one of its rows and `row_number()` ranks them — a `running` run first,\n * then the newest start, then the row written last — so reading only rank 1\n * leaves one row per card. Two differences from the file, both dialect: the\n * tie-break reads `seq` where SQLite reads `rowid`, and both window counters are\n * cast to `int` because Postgres counts in `bigint`, which a driver is free to\n * hand back as a string.\n */\nasync function selectRunProjection(\n database: BoardPostgresDatabase,\n scope: BoardRunProjectionScope,\n): Promise<BoardRunProjection[]> {\n if ('issueIds' in scope && scope.issueIds.length === 0) {\n return []\n }\n\n const cards = 'workspaceId' in scope\n ? inArray(\n issueRuns.issueId,\n database.select({ id: issues.id }).from(issues).where(eq(issues.workspaceId, scope.workspaceId)),\n )\n : inArray(issueRuns.issueId, scope.issueIds)\n\n const ranked = database\n .select({\n issueId: issueRuns.issueId,\n attemptCount: sql<number>`cast(count(*) over (partition by ${issueRuns.issueId}) as int)`.as('attempt_count'),\n id: issueRuns.id,\n agentId: issueRuns.agentId,\n agentName: issueRuns.agentName,\n state: issueRuns.state,\n branch: issueRuns.branch,\n executionMode: issueRuns.executionMode,\n chatSessionId: issueRuns.chatSessionId,\n startedAt: issueRuns.startedAt,\n cardRank: sql<number>`cast(row_number() over (partition by ${issueRuns.issueId} order by (${issueRuns.state} = 'running') desc, ${issueRuns.startedAt} desc, ${issueRuns.seq} desc) as int)`.as('card_rank'),\n })\n .from(issueRuns)\n .where(cards)\n .as('ranked')\n\n const rows = await database\n .select({\n issueId: ranked.issueId,\n attemptCount: ranked.attemptCount,\n id: ranked.id,\n agentId: ranked.agentId,\n agentName: ranked.agentName,\n state: ranked.state,\n branch: ranked.branch,\n executionMode: ranked.executionMode,\n chatSessionId: ranked.chatSessionId,\n startedAt: ranked.startedAt,\n })\n .from(ranked)\n .where(eq(ranked.cardRank, 1))\n\n return rows.map(row => ({\n issueId: row.issueId,\n attemptCount: row.attemptCount,\n activeRun: row.state === 'running'\n ? {\n id: row.id,\n agentId: row.agentId,\n agentName: row.agentName,\n state: row.state,\n branch: row.branch,\n executionMode: row.executionMode,\n chatSessionId: row.chatSessionId,\n startedAt: row.startedAt,\n }\n : null,\n }))\n}\n\n/**\n * The board's statements against one Postgres handle — a connection, or a\n * transaction on one.\n *\n * `transaction` builds a second store over the transactional handle drizzle\n * hands it, which is the one structural difference from the SQLite store: there\n * a single connection *is* the transaction, so the store hands back itself.\n */\nfunction createStoreOver(database: BoardPostgresDatabase): BoardStore {\n const store: BoardStore = {\n statuses: {\n listByWorkspace: async workspaceId => await database\n .select()\n .from(issueStatuses)\n .where(eq(issueStatuses.workspaceId, workspaceId))\n .orderBy(issueStatuses.order),\n listAllInBoardOrder: async () => await database\n .select()\n .from(issueStatuses)\n .orderBy(issueStatuses.workspaceId, issueStatuses.order),\n listAll: async () => await database.select().from(issueStatuses),\n findById: async (statusId) => {\n const [row] = await database.select().from(issueStatuses).where(eq(issueStatuses.id, statusId)).limit(1)\n return row ?? null\n },\n findInWorkspace: async (workspaceId, statusId) => {\n const [row] = await database\n .select()\n .from(issueStatuses)\n .where(and(eq(issueStatuses.workspaceId, workspaceId), eq(issueStatuses.id, statusId)))\n .limit(1)\n return row ?? null\n },\n countByWorkspace: async (workspaceId) => {\n const [row] = await database\n .select({ count: sql<number>`cast(count(*) as int)` })\n .from(issueStatuses)\n .where(eq(issueStatuses.workspaceId, workspaceId))\n return row?.count ?? 0\n },\n create: async (values) => {\n const [row] = await database.insert(issueStatuses).values(values).returning()\n return row\n },\n update: async (statusId, patch) => {\n await database.update(issueStatuses).set(patch).where(eq(issueStatuses.id, statusId))\n },\n delete: async (statusId) => {\n await database.delete(issueStatuses).where(eq(issueStatuses.id, statusId))\n },\n },\n\n milestones: {\n listNewestFirst: async workspaceId => await database\n .select()\n .from(issueMilestones)\n .where(workspaceId ? eq(issueMilestones.workspaceId, workspaceId) : undefined)\n .orderBy(desc(issueMilestones.createdAt)),\n listByWorkspace: async workspaceId => await database\n .select()\n .from(issueMilestones)\n .where(eq(issueMilestones.workspaceId, workspaceId)),\n listAll: async () => await database.select().from(issueMilestones),\n findById: async (milestoneId) => {\n const [row] = await database.select().from(issueMilestones).where(eq(issueMilestones.id, milestoneId)).limit(1)\n return row ?? null\n },\n findInWorkspace: async (workspaceId, milestoneId) => {\n const [row] = await database\n .select()\n .from(issueMilestones)\n .where(and(eq(issueMilestones.id, milestoneId), eq(issueMilestones.workspaceId, workspaceId)))\n .limit(1)\n return row ?? null\n },\n create: async (values) => {\n const [row] = await database.insert(issueMilestones).values(values).returning()\n return row\n },\n update: async (milestoneId, patch) => {\n await database.update(issueMilestones).set(patch).where(eq(issueMilestones.id, milestoneId))\n },\n delete: async (milestoneId) => {\n await database.delete(issueMilestones).where(eq(issueMilestones.id, milestoneId))\n },\n },\n\n issues: {\n listInBoardOrder: async workspaceId => await database\n .select()\n .from(issues)\n .where(workspaceId ? eq(issues.workspaceId, workspaceId) : undefined)\n // Board position is authoritative; creation time only breaks ties between\n // issues that have never been dragged (all of which share order 0).\n .orderBy(issues.order, desc(issues.createdAt)),\n listNewestFirst: async () => await database.select().from(issues).orderBy(desc(issues.createdAt)),\n listAll: async () => await database.select().from(issues),\n listByWorkspace: async workspaceId => await database\n .select()\n .from(issues)\n .where(eq(issues.workspaceId, workspaceId)),\n listByIds: async issueIds => await database.select().from(issues).where(inArray(issues.id, issueIds)),\n findById: async (issueId) => {\n const [row] = await database.select().from(issues).where(eq(issues.id, issueId)).limit(1)\n return row ?? null\n },\n findInWorkspace: async (workspaceId, issueId) => {\n const [row] = await database\n .select()\n .from(issues)\n .where(and(eq(issues.id, issueId), eq(issues.workspaceId, workspaceId)))\n .limit(1)\n return row ?? null\n },\n findByNumber: async (workspaceId, number) => {\n const [row] = await database\n .select()\n .from(issues)\n .where(and(eq(issues.workspaceId, workspaceId), eq(issues.number, number)))\n .limit(1)\n return row ?? null\n },\n maxNumber: async (workspaceId) => {\n const [row] = await database\n .select({ maxNumber: sql<number>`cast(coalesce(max(${issues.number}), 0) as int)` })\n .from(issues)\n .where(eq(issues.workspaceId, workspaceId))\n return row?.maxNumber ?? 0\n },\n maxOrder: async (workspaceId) => {\n const [row] = await database\n .select({ maxOrder: sql<number>`cast(coalesce(max(${issues.order}), 0) as int)` })\n .from(issues)\n .where(eq(issues.workspaceId, workspaceId))\n return row?.maxOrder ?? 0\n },\n /**\n * The one insert the board expects to be refused sometimes.\n *\n * Two writers can pick the same card number, and the unique index turns\n * the loser's insert into an error that the numbering answers by reading\n * the board again. Postgres refuses to read anything from a transaction a\n * statement has failed in, so this insert runs inside a savepoint of its\n * own: when it is refused, the savepoint rolls back and the caller's\n * transaction is still there to be read. A board in a file needs nothing\n * of the sort — SQLite leaves a transaction usable after a failed\n * statement — which is why this lives in the store and not in `ops`.\n */\n create: async (values) => {\n const [row] = await database.transaction(async tx => await tx.insert(issues).values(values).returning())\n return row\n },\n update: async (issueId, patch) => {\n await database.update(issues).set(patch).where(eq(issues.id, issueId))\n },\n updateMany: async (issueIds, patch) => {\n if (issueIds.length === 0) {\n return 0\n }\n // The rows written are counted by naming them back: `returning` is the\n // one row count every Postgres driver reports the same way.\n const written = await database\n .update(issues)\n .set(patch)\n .where(inArray(issues.id, issueIds))\n .returning({ id: issues.id })\n return written.length\n },\n clearMilestoneReferences: async (milestoneId, updatedAt) => {\n await database\n .update(issues)\n .set({ milestoneId: null, updatedAt })\n .where(eq(issues.milestoneId, milestoneId))\n },\n clearParentReferences: async (parentIssueId, updatedAt) => {\n await database\n .update(issues)\n .set({ parentIssueId: null, updatedAt })\n .where(eq(issues.parentIssueId, parentIssueId))\n },\n clearDelegateReferences: async (delegate, updatedAt) => {\n const written = await database\n .update(issues)\n .set({ delegateAgentId: null, delegateProviderTargetId: null, updatedAt })\n .where('agentId' in delegate\n ? eq(issues.delegateAgentId, delegate.agentId)\n : eq(issues.delegateProviderTargetId, delegate.providerTargetId))\n .returning({ id: issues.id })\n return written.length\n },\n delete: async (issueId) => {\n await database.delete(issues).where(eq(issues.id, issueId))\n },\n },\n\n comments: {\n listByIssue: async issueId => (await database\n .select()\n .from(issueComments)\n .where(eq(issueComments.issueId, issueId))\n .orderBy(asc(issueComments.createdAt), asc(issueComments.seq))).map(toIssueComment),\n findById: async (commentId) => {\n const [row] = await database.select().from(issueComments).where(eq(issueComments.id, commentId)).limit(1)\n return row ? toIssueComment(row) : null\n },\n findByDedupeKey: async (issueId, dedupeKey) => {\n const [row] = await database\n .select()\n .from(issueComments)\n .where(and(eq(issueComments.issueId, issueId), eq(issueComments.dedupeKey, dedupeKey)))\n .limit(1)\n return row ? toIssueComment(row) : null\n },\n // `starts_with` rather than `like`: a key may carry `_` or `%`, which\n // `like` would read as wildcards.\n listByDedupeKeyPrefix: async (issueId, prefix) => (await database\n .select()\n .from(issueComments)\n .where(and(eq(issueComments.issueId, issueId), sql`starts_with(${issueComments.dedupeKey}, ${prefix})`))\n .orderBy(asc(issueComments.createdAt), asc(issueComments.seq))).map(toIssueComment),\n create: async (values) => {\n const [row] = await database.insert(issueComments).values(values).returning()\n return toIssueComment(row)\n },\n createOnce: async (values) => {\n // `do nothing` returns no row when the key is already taken, and the\n // read below then finds whichever row holds it.\n const [inserted] = await database.insert(issueComments).values(values).onConflictDoNothing().returning()\n if (inserted) {\n return toIssueComment(inserted)\n }\n const [existing] = await database\n .select()\n .from(issueComments)\n .where(and(eq(issueComments.issueId, values.issueId), eq(issueComments.dedupeKey, values.dedupeKey)))\n .limit(1)\n if (!existing) {\n // The insert was refused, and not by the dedupe index: some other\n // constraint on the row said no. Returning nothing here would hand the\n // caller an `IssueComment` that does not exist.\n throw new Error(\n `issue comment for ${values.issueId} was refused, and no comment carries dedupe key ${values.dedupeKey}`,\n )\n }\n return toIssueComment(existing)\n },\n delete: async (commentId) => {\n await database.delete(issueComments).where(eq(issueComments.id, commentId))\n },\n },\n\n runs: {\n listByIssue: async issueId => (await database\n .select()\n .from(issueRuns)\n .where(eq(issueRuns.issueId, issueId))\n .orderBy(asc(issueRuns.startedAt), asc(issueRuns.seq))).map(toIssueRun),\n listRunning: async issueId => (await database\n .select()\n .from(issueRuns)\n .where(and(eq(issueRuns.issueId, issueId), eq(issueRuns.state, 'running')))\n .orderBy(asc(issueRuns.startedAt), asc(issueRuns.seq))).map(toIssueRun),\n listRunningWithSession: async () => (await database\n .select()\n .from(issueRuns)\n .where(and(eq(issueRuns.state, 'running'), isNotNull(issueRuns.chatSessionId)))\n .orderBy(asc(issueRuns.startedAt), asc(issueRuns.seq))).map(toIssueRun),\n findById: async (runId) => {\n const [row] = await database.select().from(issueRuns).where(eq(issueRuns.id, runId)).limit(1)\n return row ? toIssueRun(row) : null\n },\n findByChatSessionId: async (chatSessionId) => {\n const [row] = await database\n .select()\n .from(issueRuns)\n .where(eq(issueRuns.chatSessionId, chatSessionId))\n .orderBy(desc(issueRuns.startedAt), desc(issueRuns.seq))\n .limit(1)\n return row ? toIssueRun(row) : null\n },\n countByIssue: async (issueId) => {\n const [row] = await database\n .select({ count: sql<number>`cast(count(*) as int)` })\n .from(issueRuns)\n .where(eq(issueRuns.issueId, issueId))\n return row?.count ?? 0\n },\n create: async (values) => {\n const [row] = await database.insert(issueRuns).values(values).returning()\n return toIssueRun(row)\n },\n update: async (runId, patch) => {\n await database.update(issueRuns).set(patch).where(eq(issueRuns.id, runId))\n },\n project: async scope => await selectRunProjection(database, scope),\n },\n\n relations: {\n listByIssue: async issueId => await database\n .select()\n .from(issueRelations)\n .where(or(eq(issueRelations.sourceIssueId, issueId), eq(issueRelations.targetIssueId, issueId))),\n findById: async (relationId) => {\n const [row] = await database.select().from(issueRelations).where(eq(issueRelations.id, relationId)).limit(1)\n return row ?? null\n },\n findMatching: async (edge) => {\n const [row] = await database\n .select()\n .from(issueRelations)\n .where(and(\n eq(issueRelations.type, edge.type),\n edge.type === 'relates_to'\n ? or(\n and(\n eq(issueRelations.sourceIssueId, edge.sourceIssueId),\n eq(issueRelations.targetIssueId, edge.targetIssueId),\n ),\n and(\n eq(issueRelations.sourceIssueId, edge.targetIssueId),\n eq(issueRelations.targetIssueId, edge.sourceIssueId),\n ),\n )\n : and(\n eq(issueRelations.sourceIssueId, edge.sourceIssueId),\n eq(issueRelations.targetIssueId, edge.targetIssueId),\n ),\n ))\n .limit(1)\n return row ?? null\n },\n create: async (values) => {\n const [row] = await database.insert(issueRelations).values(values).returning()\n return row\n },\n delete: async (relationId) => {\n await database.delete(issueRelations).where(eq(issueRelations.id, relationId))\n },\n deleteByIssue: async (issueId) => {\n await database\n .delete(issueRelations)\n .where(or(eq(issueRelations.sourceIssueId, issueId), eq(issueRelations.targetIssueId, issueId)))\n },\n },\n\n contextRefs: {\n replace: async (issueId, contextRefs, updatedAt) => {\n await database.update(issues).set({ contextRefs, updatedAt }).where(eq(issues.id, issueId))\n },\n },\n\n fieldChanges: {\n listByIssue: async issueId => (await database\n .select()\n .from(issueFieldChanges)\n .where(eq(issueFieldChanges.issueId, issueId))\n .orderBy(asc(issueFieldChanges.createdAt), asc(issueFieldChanges.seq))).map(toIssueFieldChange),\n listByIssueField: async (issueId, field) => (await database\n .select()\n .from(issueFieldChanges)\n .where(and(eq(issueFieldChanges.issueId, issueId), eq(issueFieldChanges.field, field)))\n .orderBy(asc(issueFieldChanges.createdAt), asc(issueFieldChanges.seq))).map(toIssueFieldChange),\n create: async (values) => {\n await database.insert(issueFieldChanges).values(values)\n },\n },\n\n pullRequests: {\n listByIssue: async issueId => (await database\n .select()\n .from(issuePullRequests)\n .where(eq(issuePullRequests.issueId, issueId))\n .orderBy(asc(issuePullRequests.createdAt), asc(issuePullRequests.seq))).map(toIssuePullRequest),\n listLinked: async () => (await database\n .select({ ...getTableColumns(issuePullRequests), workspaceId: issues.workspaceId })\n .from(issuePullRequests)\n .innerJoin(issues, eq(issues.id, issuePullRequests.issueId))\n .orderBy(asc(issuePullRequests.createdAt), asc(issuePullRequests.seq))).map(toLinkedPullRequest),\n link: async (values) => {\n // `do nothing` returns no row when the card already names the pull\n // request, and the read below then finds the link that does. A conflict\n // it absorbs leaves the transaction usable, so no savepoint is needed.\n const [inserted] = await database.insert(issuePullRequests).values(values).onConflictDoNothing().returning()\n if (inserted) {\n return toIssuePullRequest(inserted)\n }\n const [existing] = await database\n .select()\n .from(issuePullRequests)\n .where(and(\n eq(issuePullRequests.issueId, values.issueId),\n eq(issuePullRequests.owner, values.owner),\n eq(issuePullRequests.repo, values.repo),\n eq(issuePullRequests.number, values.number),\n ))\n .limit(1)\n if (!existing) {\n // Refused, and not by the one index that makes a link idempotent.\n throw new Error(\n `pull request link for ${values.issueId} was refused, and the card names no ${values.owner}/${values.repo}#${values.number}`,\n )\n }\n return toIssuePullRequest(existing)\n },\n unlink: async (issueId, linkId) => {\n const [removed] = await database\n .delete(issuePullRequests)\n .where(and(eq(issuePullRequests.id, linkId), eq(issuePullRequests.issueId, issueId)))\n .returning()\n return removed ? toIssuePullRequest(removed) : null\n },\n },\n\n meta: {\n read: async (key) => {\n const [row] = await database.select().from(kanbanMeta).where(eq(kanbanMeta.key, key)).limit(1)\n return row ?? null\n },\n bumpRevision: async (key, updatedAt) => {\n await database\n .insert(kanbanMeta)\n .values({ key, revision: 1, updatedAt })\n .onConflictDoUpdate({\n target: kanbanMeta.key,\n set: { revision: sql`${kanbanMeta.revision} + 1`, updatedAt },\n })\n },\n },\n\n /**\n * Run `fn` inside one Postgres transaction, on the handle this store is\n * bound to. A transaction opened on a transactional handle is a savepoint —\n * drizzle writes that for us — so a nested write rolls back on its own\n * without taking the outer one with it.\n *\n * `mode` reaches nothing here. `immediate` is SQLite's way of taking the\n * file's write lock up front, and Postgres has no counterpart worth writing:\n * it locks rows as they are written, and the two races the board actually\n * has — two writers numbering a card, two writers bumping the change\n * counter — are settled by the unique index on `(workspace_id, number)` and\n * by the `on conflict do update` on `kanban_meta`. Nothing here takes a lock\n * the statement itself would not.\n */\n transaction: async <T>(\n fn: (tx: BoardStore) => Promise<T>,\n _options?: { mode?: BoardTransactionMode },\n ): Promise<T> => {\n return await database.transaction(async tx => await fn(createStoreOver(tx)))\n },\n }\n\n return store\n}\n\n/**\n * `BoardStore` backed by Postgres — the board in a database several machines\n * share, rather than in a file one machine holds.\n *\n * It is written against `pg-core` and nothing below it, so the same store serves\n * a real connection onto an external database and the embedded\n * Postgres the tests run on. Every method answers what the SQLite store\n * answers, down to the order of two rows written in the same second — `seq` on\n * `issue_runs`, `issue_comments`, `issue_field_changes` and\n * `issue_pull_requests` is what stands in for `rowid` there.\n */\nexport function createPostgresBoardStore(options: { database: BoardPostgresDatabase }): BoardStore {\n return createStoreOver(options.database)\n}\n","import { dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport { hasMigrationJournal, resolveBoardMigrationsPath } from '../migrations-path'\n\n/** The folder's name, wherever it has been put. */\nconst MIGRATIONS_DIRECTORY = 'drizzle-postgres'\n\n/**\n * The board's Postgres migrations for the file that is running right now — the\n * ones `kanbo migrate` applies to an external database. The two\n * layouts this has to survive are `src/migrations-path.ts`'s subject.\n */\nexport function getBoardMigrationsPath(): string {\n const here = dirname(fileURLToPath(import.meta.url))\n return resolveBoardMigrationsPath(here, MIGRATIONS_DIRECTORY, hasMigrationJournal)\n}\n","import { eq, sql } from 'drizzle-orm'\nimport type { PgDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core'\nimport { getTableConfig, pgSchema, text } from 'drizzle-orm/pg-core'\n\nimport { BoardError } from '../domain/errors'\nimport { BOARD_SCHEMA_EPOCH, BOARD_TABLES_ADDED_WITHIN_EPOCH } from '../schema-epoch'\nimport { BOARD_POSTGRES_TABLES, boardPostgresSchema, kanbanMeta } from './schema'\n\n/**\n * An open Postgres board, whichever driver is underneath.\n *\n * The board is written against `pg-core` and nothing below it, so the same code\n * serves a real connection onto a Supabase database and the embedded\n * Postgres the tests run on. Only the migrator, which drizzle ships per driver,\n * needs to know more than this.\n */\nexport type BoardPostgresDatabase = PgDatabase<PgQueryResultHKT, Record<string, unknown>>\n\n/** `information_schema.tables`, the first of the two views the guard reads. */\nconst informationSchemaTables = pgSchema('information_schema').table('tables', {\n tableSchema: text('table_schema').notNull(),\n tableName: text('table_name').notNull(),\n})\n\n/** And `information_schema.columns`, for a board whose tables are all there but older inside. */\nconst informationSchemaColumns = pgSchema('information_schema').table('columns', {\n tableSchema: text('table_schema').notNull(),\n tableName: text('table_name').notNull(),\n columnName: text('column_name').notNull(),\n})\n\n/**\n * Every column this build expects to find, read off the schema itself rather\n * than listed again here — a column added to the schema and forgotten in a\n * migration is exactly the drift this is looking for.\n */\nfunction expectedColumns(): { table: string, column: string }[] {\n return Object.values(boardPostgresSchema).flatMap((table) => {\n const config = getTableConfig(table)\n return config.columns.map(column => ({ table: config.name, column: column.name }))\n })\n}\n\n/**\n * Refuse a Postgres board this build cannot speak for, before any statement\n * runs against it.\n *\n * An external database has no app of its own to migrate it — `kanbo migrate`\n * is the only thing that ever creates the board here — so this guard answers\n * three different mistakes with one code: a connection string pointing at a\n * database that has no board in it at all, one pointing at a board whose tables\n * are all there but which a later migration has since added to, and\n * one pointing at a board written by another generation of this package.\n *\n * The middle case is the one a stamp alone cannot catch, and it comes in two\n * shapes: a table a later migration added (`issue_pull_requests`, reported as\n * `missingAddedTables` so a host does not read it as \"no board here\"), and a\n * column one added. `BOARD_SCHEMA_EPOCH` says which *logical* board a database\n * holds, and a migration that only adds a column this dialect needs for itself — `seq`, the insertion order Postgres\n * has no `rowid` for — does not change that. Such a board would pass a stamp\n * check and then fail on the first listing, statement by statement, so the\n * columns are asked for by name instead.\n */\nexport async function assertBoardSchema(database: BoardPostgresDatabase): Promise<void> {\n const present = new Set((await database\n .select({ name: informationSchemaTables.tableName })\n .from(informationSchemaTables)\n .where(eq(informationSchemaTables.tableSchema, sql`current_schema()`)))\n .map(row => row.name))\n const added = new Set<string>(BOARD_TABLES_ADDED_WITHIN_EPOCH)\n const missing = BOARD_POSTGRES_TABLES.filter(table => !added.has(table) && !present.has(table))\n if (missing.length > 0) {\n throw new BoardError('board_schema_outdated', { missingTables: missing })\n }\n const missingAddedTables = BOARD_TABLES_ADDED_WITHIN_EPOCH.filter(table => !present.has(table))\n if (missingAddedTables.length > 0) {\n throw new BoardError('board_schema_outdated', { missingAddedTables })\n }\n\n const columns = new Set((await database\n .select({ table: informationSchemaColumns.tableName, column: informationSchemaColumns.columnName })\n .from(informationSchemaColumns)\n .where(eq(informationSchemaColumns.tableSchema, sql`current_schema()`)))\n .map(row => `${row.table}.${row.column}`))\n const missingColumns = expectedColumns()\n .filter(({ table, column }) => !columns.has(`${table}.${column}`))\n .map(({ table, column }) => `${table}.${column}`)\n if (missingColumns.length > 0) {\n throw new BoardError('board_schema_outdated', { missingColumns })\n }\n\n const [row] = await database\n .select({ revision: kanbanMeta.revision })\n .from(kanbanMeta)\n .where(eq(kanbanMeta.key, 'schema_epoch'))\n if (row?.revision !== BOARD_SCHEMA_EPOCH) {\n throw new BoardError('board_schema_outdated', { expected: BOARD_SCHEMA_EPOCH, found: row?.revision ?? null })\n }\n}\n","import { BOARD_POSTGRES_TABLES } from './schema'\n\n/** The role a person connects as: the board, whole. */\nexport const BOARD_PERSON_ROLE = 'kanban_person'\n\n/** The role an agent connects as: the board, minus the two decisions that are a person's. */\nexport const BOARD_AGENT_ROLE = 'kanban_agent'\n\n/**\n * What the database says when the agent role tries to take a card back from a\n * person. The board's clients turn it into `board_approval_requires_user`;\n * it is written here because by then the only thing left of the refusal is this\n * sentence.\n */\nexport const AGENT_WAITING_REFUSAL = 'kanbo: only a person may take a card out of waiting'\n\n/** What the database says when the agent role tries to write a person's decision into the history. */\nexport const AGENT_APPROVAL_COMMENT_REFUSAL = 'kanbo: only a person may approve or return a card'\n\n/**\n * `insufficient_privilege` — the SQLSTATE every refusal above is raised with,\n * and the one thing about them that survives a driver. A caller that maps the\n * refusal to an answer of its own matches this rather than the sentence.\n */\nexport const BOARD_REFUSAL_ERRCODE = '42501'\n\n/**\n * The sequences behind the four `seq` columns — the board's stand-in for the\n * `rowid` a file orders by. An insert into one of those tables draws from its\n * sequence, so a role without `usage` on it cannot write the row at all.\n */\nconst BOARD_SEQUENCES = [\n 'issue_runs_seq_seq',\n 'issue_comments_seq_seq',\n 'issue_field_changes_seq_seq',\n 'issue_pull_requests_seq_seq',\n]\n\n/** The cards themselves — the one table the agent role may not delete from. */\nconst ISSUES_TABLE = 'issues'\n\n/** Everything else, which both roles hold outright. */\nconst AGENT_DELETABLE_TABLES = BOARD_POSTGRES_TABLES.filter(table => table !== ISSUES_TABLE)\n\n/** Every board table, as the SQL spells a list of them. */\nfunction quoted(tables: readonly string[]): string {\n return tables.map(table => `\"${table}\"`).join(', ')\n}\n\n/**\n * The two roles an external board is worked through, and the two rules the\n * database itself keeps.\n *\n * A board in a file is protected by whoever can open the file. A board in a\n * shared database is not: several people and several agents connect to it at\n * once, from machines nobody else knows about, so the rule that approving is\n * a person's job has to live somewhere they all pass through. That is here —\n * below every client, in triggers that read `current_user`, so an agent holding\n * a connection string still cannot do a person's two things:\n *\n * - take a card out of `waiting_for = 'human'`, which is how a person accepts\n * or returns the work, and\n * - write a `system.approved` or `system.returned` comment, which is how that\n * decision is recorded — nor delete one, nor rewrite one, because a rule\n * about who may write a row is worth nothing if the other side may erase or\n * re-word it afterwards.\n *\n * The third rule is a grant rather than a trigger: the agent role may not\n * delete a card at all. A trigger cannot answer this one, because a cascading\n * delete is carried out as the owner of the table it reaches — `current_user`\n * inside the comment guard is the owner, not the agent, so deleting the card\n * would quietly take the decision on it with it. Nothing is lost by refusing:\n * the board's own tools offer no way to delete a card (cancelling one is a move\n * to another column), and a card that could be deleted and written again is a\n * way around waiting for a person.\n *\n * The guards read `current_user`, which is the role the session *logged in as*:\n * a login merely granted membership in `kanban_agent` is still itself, and the\n * rules below do not fire for it. The header of the SQL says so to whoever\n * applies it.\n *\n * Everything else about the board is the agent's to do. Both roles are created\n * with `login` and **no password**: an administrator sets one with\n * `alter role kanban_agent with password '…'` before a connection string is\n * handed out, and no password is ever written by this package.\n *\n * Applying this twice is a no-op — `kanbo roles apply` is meant to be run\n * again whenever the board moves or the rules change.\n */\nexport const BOARD_ROLE_SQL = `-- Two roles for one board: a person's and an agent's.\n-- Apply as the owner of the board tables (or a superuser):\n-- psql \"$KANBO_DATABASE_URL\" -f roles.sql\n-- Neither role is given a password here. Set one before handing out a\n-- connection string:\n-- alter role ${BOARD_AGENT_ROLE} with password '…';\n-- The agent role is granted no delete on \"issues\": see the comment on that\n-- grant below.\n-- Connect AS the role. The guards below read current_user, and a login that was\n-- merely granted membership (\"create role bot login; grant ${BOARD_AGENT_ROLE} to bot;\")\n-- keeps current_user = 'bot' — the guards then never fire and the board looks\n-- configured while nothing is enforced. Either hand out ${BOARD_AGENT_ROLE}'s own\n-- connection string, or have the session run \"set role ${BOARD_AGENT_ROLE}\" before it\n-- touches the board.\ndo $$\nbegin\n if not exists (select 1 from pg_catalog.pg_roles where rolname = '${BOARD_PERSON_ROLE}') then\n create role \"${BOARD_PERSON_ROLE}\" login;\n end if;\n if not exists (select 1 from pg_catalog.pg_roles where rolname = '${BOARD_AGENT_ROLE}') then\n create role \"${BOARD_AGENT_ROLE}\" login;\n end if;\nend\n$$;\n\ngrant usage on schema public to \"${BOARD_PERSON_ROLE}\", \"${BOARD_AGENT_ROLE}\";\n\ngrant select, insert, update, delete on ${quoted(BOARD_POSTGRES_TABLES)}\n to \"${BOARD_PERSON_ROLE}\";\n\n-- The agent holds everything but the deletion of a card. A card carries its\n-- history by cascade, and a cascading delete runs as the table's owner, so a\n-- trigger on the comments cannot tell an agent's deletion from anyone's: the\n-- only place to say it is here. The board's own tools never delete a card —\n-- cancelling one is a move to another column — and a card that could be deleted\n-- and written again is a way around waiting for a person.\ngrant select, insert, update, delete on ${quoted(AGENT_DELETABLE_TABLES)}\n to \"${BOARD_AGENT_ROLE}\";\n\ngrant select, insert, update on \"${ISSUES_TABLE}\" to \"${BOARD_AGENT_ROLE}\";\nrevoke delete on \"${ISSUES_TABLE}\" from \"${BOARD_AGENT_ROLE}\";\n\ngrant usage, select on sequence ${quoted(BOARD_SEQUENCES)}\n to \"${BOARD_PERSON_ROLE}\", \"${BOARD_AGENT_ROLE}\";\n\n-- A person accepts or returns the work; an agent asks and waits.\ncreate or replace function kanban_guard_waiting_for() returns trigger\nlanguage plpgsql as $$\nbegin\n if current_user = '${BOARD_AGENT_ROLE}'\n and old.waiting_for = 'human'\n and new.waiting_for is distinct from old.waiting_for then\n raise exception '${AGENT_WAITING_REFUSAL}' using errcode = '${BOARD_REFUSAL_ERRCODE}';\n end if;\n return new;\nend;\n$$;\n\ndrop trigger if exists kanban_guard_waiting_for on \"issues\";\ncreate trigger kanban_guard_waiting_for\n before update on \"issues\"\n for each row execute function kanban_guard_waiting_for();\n\n-- And the same decision, said in the card's history.\ncreate or replace function kanban_guard_approval_comments() returns trigger\nlanguage plpgsql as $$\nbegin\n if current_user = '${BOARD_AGENT_ROLE}'\n and new.author_kind in ('system.approved', 'system.returned') then\n raise exception '${AGENT_APPROVAL_COMMENT_REFUSAL}' using errcode = '${BOARD_REFUSAL_ERRCODE}';\n end if;\n return new;\nend;\n$$;\n\ndrop trigger if exists kanban_guard_approval_comments on \"issue_comments\";\ncreate trigger kanban_guard_approval_comments\n before insert on \"issue_comments\"\n for each row execute function kanban_guard_approval_comments();\n\n-- And the same decision cannot be erased either: a rule about who may write a\n-- row is worth nothing if the other side may delete it afterwards.\ncreate or replace function kanban_guard_approval_comment_deletes() returns trigger\nlanguage plpgsql as $$\nbegin\n if current_user = '${BOARD_AGENT_ROLE}'\n and old.author_kind in ('system.approved', 'system.returned') then\n raise exception '${AGENT_APPROVAL_COMMENT_REFUSAL}' using errcode = '${BOARD_REFUSAL_ERRCODE}';\n end if;\n return old;\nend;\n$$;\n\ndrop trigger if exists kanban_guard_approval_comment_deletes on \"issue_comments\";\ncreate trigger kanban_guard_approval_comment_deletes\n before delete on \"issue_comments\"\n for each row execute function kanban_guard_approval_comment_deletes();\n\n-- Nor rewritten. A decision that can be re-worded, re-dated or turned into\n-- something else is not a decision, and an update is how each of those is\n-- spelled: \"set content\", \"set created_at\", \"set author_kind = 'agent'\". The\n-- row is guarded coming and going — what it already is, and what it is being\n-- made into — so the agent can neither take a person's decision away nor turn\n-- its own comment into one.\ncreate or replace function kanban_guard_approval_comment_updates() returns trigger\nlanguage plpgsql as $$\nbegin\n if current_user = '${BOARD_AGENT_ROLE}'\n and (old.author_kind in ('system.approved', 'system.returned')\n or new.author_kind in ('system.approved', 'system.returned')) then\n raise exception '${AGENT_APPROVAL_COMMENT_REFUSAL}' using errcode = '${BOARD_REFUSAL_ERRCODE}';\n end if;\n return new;\nend;\n$$;\n\ndrop trigger if exists kanban_guard_approval_comment_updates on \"issue_comments\";\ncreate trigger kanban_guard_approval_comment_updates\n before update on \"issue_comments\"\n for each row execute function kanban_guard_approval_comment_updates();\n`\n"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/schema-epoch.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The board schema generation this build of the package speaks. A database
|
|
4
|
+
* stamped with anything else was written by another version, and the migration
|
|
5
|
+
* that would bring it forward belongs to whoever owns the database — never to a
|
|
6
|
+
* reader.
|
|
7
|
+
*
|
|
8
|
+
* The number says nothing about which engine holds the board: a SQLite file and
|
|
9
|
+
* a Postgres database at the same epoch carry the same logical schema, which is
|
|
10
|
+
* why a change to one is only ever made together with the same change to the
|
|
11
|
+
* other.
|
|
12
|
+
*/
|
|
13
|
+
declare const BOARD_SCHEMA_EPOCH = 1;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { BOARD_SCHEMA_EPOCH as t };
|
|
16
|
+
//# sourceMappingURL=schema-epoch-ZyumVL_A.d.cts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/schema-epoch.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The board schema generation this build of the package speaks. A database
|
|
4
|
+
* stamped with anything else was written by another version, and the migration
|
|
5
|
+
* that would bring it forward belongs to whoever owns the database — never to a
|
|
6
|
+
* reader.
|
|
7
|
+
*
|
|
8
|
+
* The number says nothing about which engine holds the board: a SQLite file and
|
|
9
|
+
* a Postgres database at the same epoch carry the same logical schema, which is
|
|
10
|
+
* why a change to one is only ever made together with the same change to the
|
|
11
|
+
* other.
|
|
12
|
+
*/
|
|
13
|
+
declare const BOARD_SCHEMA_EPOCH = 1;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { BOARD_SCHEMA_EPOCH as t };
|
|
16
|
+
//# sourceMappingURL=schema-epoch-ZyumVL_A.d.mts.map
|