limn-review 0.1.126 → 0.1.127
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/dist/config.js +3 -0
- package/dist/db/erase-account.js +12 -0
- package/dist/db/migrations/0084_auto_fix_off_by_default.sql +7 -0
- package/dist/db/migrations/0085_auto_review_daily_cap.sql +6 -0
- package/dist/db/migrations/0086_ticket_review_pr_cards.sql +24 -0
- package/dist/db/migrations/0087_auto_post.sql +21 -0
- package/dist/db/migrations/meta/_journal.json +28 -0
- package/dist/db/migrations-pg/0071_auto_fix_off_by_default.sql +5 -0
- package/dist/db/migrations-pg/0072_auto_review_daily_cap.sql +3 -0
- package/dist/db/migrations-pg/0073_ticket_review_pr_cards.sql +17 -0
- package/dist/db/migrations-pg/0074_auto_post.sql +14 -0
- package/dist/db/migrations-pg/meta/_journal.json +28 -0
- package/dist/db/queries.js +13 -2
- package/dist/db/schema.pg.js +39 -1
- package/dist/db/schema.sqlite.js +66 -1
- package/dist/db/ticket-review-prune.js +4 -1
- package/dist/review/claude-review/auto-post.js +405 -0
- package/dist/review/claude-review/auto-settings.js +175 -11
- package/dist/review/claude-review/auto.js +3 -3
- package/dist/review/claude-review/manager.js +5 -0
- package/dist/review/claude-review/persist.js +29 -5
- package/dist/review/claude-review/routes.js +9 -0
- package/dist/review/clone-manager.js +48 -0
- package/dist/review/path-guard.js +1 -1
- package/dist/review/post-review.js +6 -2
- package/dist/review/post-seam.js +22 -13
- package/dist/review/ticket-review/auto-post.js +291 -0
- package/dist/review/ticket-review/cards.js +271 -0
- package/dist/review/ticket-review/fingerprint.js +2 -0
- package/dist/review/ticket-review/manager.js +167 -26
- package/dist/review/ticket-review/persist.js +44 -2
- package/dist/review/ticket-review/post-item.js +187 -0
- package/dist/review/ticket-review/prepare.js +18 -7
- package/dist/review/ticket-review/prepass.js +259 -0
- package/dist/review/ticket-review/prompts.js +72 -11
- package/dist/review/ticket-review/routes.js +30 -146
- package/dist/review/ticket-review/schema.js +43 -0
- package/dist/shared/types.js +29 -2
- package/package.json +1 -1
- package/public/assets/{AiFixTab-CzugrcNK.js → AiFixTab-D-Xph0Bw.js} +2 -2
- package/public/assets/ClaudeReviewTab-BwIpVwEa.js +16 -0
- package/public/assets/{PrBotBehaviourTab-Cc1F4O6z.js → PrBotBehaviourTab-DYNOt_5y.js} +1 -1
- package/public/assets/index-gwxOLty-.css +1 -0
- package/public/assets/index-rdFHnTfI.js +77 -0
- package/public/index.html +2 -2
- package/public/assets/ClaudeReviewTab-OGu_QicR.js +0 -16
- package/public/assets/index-1ieo_9fr.js +0 -77
- package/public/assets/index-C9m8ITkB.css +0 -1
package/dist/config.js
CHANGED
|
@@ -536,6 +536,9 @@ export const config = {
|
|
|
536
536
|
// every member's worktree.
|
|
537
537
|
ticketReviewBudgetUsd: floatFromEnv('TICKET_REVIEW_BUDGET_USD', 4),
|
|
538
538
|
ticketReviewMaxTurns: intFromEnv('TICKET_REVIEW_MAX_TURNS', 40),
|
|
539
|
+
// The per-card spend cap of the ticket review's CARD PRE-PASS (Sonnet 5, diff-only, one tool —
|
|
540
|
+
// review/ticket-review/prepass.ts). Its spend is added to the ticket run's cost. Env only.
|
|
541
|
+
ticketCardBudgetUsd: floatFromEnv('TICKET_CARD_BUDGET_USD', 0.4),
|
|
539
542
|
// AUTOMATIC ticket reviews (trigger 'auto' / 'cascade') per workspace per UTC day. Not shared with
|
|
540
543
|
// the PR review's own auto cap. Manual runs never count. Default = shared TICKET_REVIEW_DAILY_CAP.
|
|
541
544
|
ticketReviewDailyCap: intFromEnv('TICKET_REVIEW_DAILY_CAP', 20),
|
package/dist/db/erase-account.js
CHANGED
|
@@ -128,6 +128,12 @@ export async function eraseAccountData(accountId) {
|
|
|
128
128
|
.delete(schema.ticketReviews)
|
|
129
129
|
.where(eq(schema.ticketReviews.accountId, accountId))
|
|
130
130
|
.execute();
|
|
131
|
+
// Ticket review contribution cards (migration 0086 / pg 0073): model text describing the user's
|
|
132
|
+
// PRs. The repo loop has normally taken them with their PRs; explicit, like every agentic table.
|
|
133
|
+
await tx
|
|
134
|
+
.delete(schema.ticketReviewPrCards)
|
|
135
|
+
.where(eq(schema.ticketReviewPrCards.accountId, accountId))
|
|
136
|
+
.execute();
|
|
131
137
|
// CI reviews (migration 0082 / pg 0069), items first. The repo loop has normally taken them with
|
|
132
138
|
// their PRs; explicit, like every agentic table.
|
|
133
139
|
await tx
|
|
@@ -218,6 +224,12 @@ export function accountScopedTables() {
|
|
|
218
224
|
col: schema.ticketReviewItems.accountId,
|
|
219
225
|
table: schema.ticketReviewItems,
|
|
220
226
|
},
|
|
227
|
+
// Contribution cards (migration 0086 / pg 0073) — one per (PR, head); erased explicitly above.
|
|
228
|
+
{
|
|
229
|
+
name: 'ticketReviewPrCards',
|
|
230
|
+
col: schema.ticketReviewPrCards.accountId,
|
|
231
|
+
table: schema.ticketReviewPrCards,
|
|
232
|
+
},
|
|
221
233
|
// CI reviews (migration 0082 / pg 0069) — the runs and their per-check items. Both carry their
|
|
222
234
|
// own accountId; both are erased explicitly above.
|
|
223
235
|
{ name: 'ciReviews', col: schema.ciReviews.accountId, table: schema.ciReviews },
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
-- AUTO AI FIX IS OFF BY DEFAULT. 0083 added `workspaces.auto_fix_enabled` DEFAULT 1 so nothing
|
|
2
|
+
-- changed on upgrade; the product decision since is OFF until someone switches it on. Every
|
|
3
|
+
-- existing workspace is switched off once here (the switch shipped one release earlier, so no one
|
|
4
|
+
-- has had time to choose it). SQLite cannot change a column default in place, so the 0083 DDL
|
|
5
|
+
-- default stays 1; both workspace inserts in db/queries.ts write `autoFixEnabled: false`
|
|
6
|
+
-- explicitly. The Postgres twin is migrations-pg/0071_auto_fix_off_by_default.sql.
|
|
7
|
+
UPDATE `workspaces` SET `auto_fix_enabled` = 0;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
-- AUTO REVIEW DAILY CAP, PER WORKSPACE. How many auto Claude reviews a workspace may start per UTC
|
|
2
|
+
-- day. OVERRIDES ONLY: NULL = the product default (20, `AUTO_REVIEW_DAILY_CAP`), resolved through
|
|
3
|
+
-- `resolveAutoReviewDailyCap` (review/claude-review/auto-settings.ts); the PUT route bounds it
|
|
4
|
+
-- 1..500. Nullable with no default, so no workspace insert has to name it. The Postgres twin is
|
|
5
|
+
-- migrations-pg/0072_auto_review_daily_cap.sql.
|
|
6
|
+
ALTER TABLE `workspaces` ADD `auto_review_daily_cap` integer;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- TICKET REVIEW CONTRIBUTION CARDS. One row per (account, PR, head commit): a model-written,
|
|
2
|
+
-- verdict-free description of what that PR's head does (summary, interfaces, the story criteria it
|
|
3
|
+
-- moves forward, loose ends) plus the PR's changed files at that head (server-written). A ticket
|
|
4
|
+
-- review reads a member whose CURRENT head has a card as the card, not as its diff; the run writes a
|
|
5
|
+
-- card for every member it read as a diff (source 'story_check') and a cheap per-PR pre-pass writes
|
|
6
|
+
-- the overflow (source 'prepass', with its own cost). Re-writing the same head replaces the card.
|
|
7
|
+
-- Tenancy: a composite FK against pull_requests(id, account_id). Both delete paths (deleteRepo,
|
|
8
|
+
-- retention's deletePrSubtree, via db/ticket-review-prune.ts) and eraseAccountData delete these rows
|
|
9
|
+
-- explicitly. The Postgres twin is migrations-pg/0073_ticket_review_pr_cards.sql.
|
|
10
|
+
CREATE TABLE IF NOT EXISTS `ticket_review_pr_cards` (
|
|
11
|
+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
12
|
+
`account_id` integer NOT NULL,
|
|
13
|
+
`pr_id` integer NOT NULL,
|
|
14
|
+
`head_sha` text NOT NULL,
|
|
15
|
+
`card` text NOT NULL,
|
|
16
|
+
`source` text NOT NULL,
|
|
17
|
+
`model` text NOT NULL,
|
|
18
|
+
`cost_usd` real,
|
|
19
|
+
`created_at` integer DEFAULT (unixepoch()) NOT NULL,
|
|
20
|
+
FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`) ON UPDATE no action ON DELETE cascade,
|
|
21
|
+
CONSTRAINT `trpc_pr_account_fk` FOREIGN KEY (`pr_id`,`account_id`) REFERENCES `pull_requests`(`id`,`account_id`) ON UPDATE no action ON DELETE cascade
|
|
22
|
+
);
|
|
23
|
+
--> statement-breakpoint
|
|
24
|
+
CREATE UNIQUE INDEX IF NOT EXISTS `trpc_account_pr_head_ux` ON `ticket_review_pr_cards` (`account_id`,`pr_id`,`head_sha`);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- AUTO-POSTING OF CLAUDE REVIEW OUTPUT (review/claude-review/auto-post.ts, review/ticket-review/
|
|
2
|
+
-- auto-post.ts). A per-workspace master switch, OFF for every workspace existing and new, plus the
|
|
3
|
+
-- per-run records of what was posted and the per-item "posted automatically" flag.
|
|
4
|
+
-- workspaces.auto_post_enabled NULL/false = off. Nullable, no default: no insert names it.
|
|
5
|
+
-- workspaces.auto_post_settings JSON, OVERRIDES ONLY ({ scope?, kinds? }); NULL = defaults.
|
|
6
|
+
-- claude_reviews.auto_post JSON ClaudeAutoPostRecord; NULL = never looked at.
|
|
7
|
+
-- claude_review_findings.posted_auto true = posted by auto-posting.
|
|
8
|
+
-- ticket_reviews.auto_post JSON TicketAutoPostRecord; NULL = never looked at.
|
|
9
|
+
-- ticket_review_items.posted_auto true = posted by auto-posting (carried with the posting).
|
|
10
|
+
-- The Postgres twin is migrations-pg/0074_auto_post.sql.
|
|
11
|
+
ALTER TABLE `workspaces` ADD `auto_post_enabled` integer;
|
|
12
|
+
--> statement-breakpoint
|
|
13
|
+
ALTER TABLE `workspaces` ADD `auto_post_settings` text;
|
|
14
|
+
--> statement-breakpoint
|
|
15
|
+
ALTER TABLE `claude_reviews` ADD `auto_post` text;
|
|
16
|
+
--> statement-breakpoint
|
|
17
|
+
ALTER TABLE `claude_review_findings` ADD `posted_auto` integer;
|
|
18
|
+
--> statement-breakpoint
|
|
19
|
+
ALTER TABLE `ticket_reviews` ADD `auto_post` text;
|
|
20
|
+
--> statement-breakpoint
|
|
21
|
+
ALTER TABLE `ticket_review_items` ADD `posted_auto` integer;
|
|
@@ -589,6 +589,34 @@
|
|
|
589
589
|
"when": 1790665200000,
|
|
590
590
|
"tag": "0083_workspace_auto_fix",
|
|
591
591
|
"breakpoints": true
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
"idx": 84,
|
|
595
|
+
"version": "6",
|
|
596
|
+
"when": 1790751600000,
|
|
597
|
+
"tag": "0084_auto_fix_off_by_default",
|
|
598
|
+
"breakpoints": true
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
"idx": 85,
|
|
602
|
+
"version": "6",
|
|
603
|
+
"when": 1790838000000,
|
|
604
|
+
"tag": "0085_auto_review_daily_cap",
|
|
605
|
+
"breakpoints": true
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
"idx": 86,
|
|
609
|
+
"version": "6",
|
|
610
|
+
"when": 1790841600000,
|
|
611
|
+
"tag": "0086_ticket_review_pr_cards",
|
|
612
|
+
"breakpoints": true
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
"idx": 87,
|
|
616
|
+
"version": "6",
|
|
617
|
+
"when": 1790928000000,
|
|
618
|
+
"tag": "0087_auto_post",
|
|
619
|
+
"breakpoints": true
|
|
592
620
|
}
|
|
593
621
|
]
|
|
594
622
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- Postgres twin of migrations/0084_auto_fix_off_by_default.sql: auto AI Fix is OFF by default,
|
|
2
|
+
-- and every existing workspace is switched off once.
|
|
3
|
+
ALTER TABLE "workspaces" ALTER COLUMN "auto_fix_enabled" SET DEFAULT false;
|
|
4
|
+
--> statement-breakpoint
|
|
5
|
+
UPDATE "workspaces" SET "auto_fix_enabled" = false;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- Postgres twin of migrations/0086_ticket_review_pr_cards.sql: ticket review contribution cards,
|
|
2
|
+
-- one row per (account, PR, head commit). The contract lives in schema.sqlite.ts.
|
|
3
|
+
CREATE TABLE IF NOT EXISTS "ticket_review_pr_cards" (
|
|
4
|
+
"id" serial PRIMARY KEY NOT NULL,
|
|
5
|
+
"account_id" integer NOT NULL,
|
|
6
|
+
"pr_id" integer NOT NULL,
|
|
7
|
+
"head_sha" text NOT NULL,
|
|
8
|
+
"card" jsonb NOT NULL,
|
|
9
|
+
"source" text NOT NULL,
|
|
10
|
+
"model" text NOT NULL,
|
|
11
|
+
"cost_usd" double precision,
|
|
12
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
13
|
+
CONSTRAINT "ticket_review_pr_cards_account_id_accounts_id_fk" FOREIGN KEY ("account_id") REFERENCES "accounts"("id") ON DELETE cascade,
|
|
14
|
+
CONSTRAINT "trpc_pr_account_fk" FOREIGN KEY ("pr_id","account_id") REFERENCES "pull_requests"("id","account_id") ON DELETE cascade
|
|
15
|
+
);
|
|
16
|
+
--> statement-breakpoint
|
|
17
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "trpc_account_pr_head_ux" ON "ticket_review_pr_cards" USING btree ("account_id","pr_id","head_sha");
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- Postgres twin of migrations/0087_auto_post.sql: auto-posting of Claude review output. A
|
|
2
|
+
-- per-workspace switch (NULL/false = off) + overrides-only settings, the per-run records and the
|
|
3
|
+
-- per-item "posted automatically" flags. The contract lives in schema.sqlite.ts.
|
|
4
|
+
ALTER TABLE "workspaces" ADD COLUMN IF NOT EXISTS "auto_post_enabled" boolean;
|
|
5
|
+
--> statement-breakpoint
|
|
6
|
+
ALTER TABLE "workspaces" ADD COLUMN IF NOT EXISTS "auto_post_settings" jsonb;
|
|
7
|
+
--> statement-breakpoint
|
|
8
|
+
ALTER TABLE "claude_reviews" ADD COLUMN IF NOT EXISTS "auto_post" jsonb;
|
|
9
|
+
--> statement-breakpoint
|
|
10
|
+
ALTER TABLE "claude_review_findings" ADD COLUMN IF NOT EXISTS "posted_auto" boolean;
|
|
11
|
+
--> statement-breakpoint
|
|
12
|
+
ALTER TABLE "ticket_reviews" ADD COLUMN IF NOT EXISTS "auto_post" jsonb;
|
|
13
|
+
--> statement-breakpoint
|
|
14
|
+
ALTER TABLE "ticket_review_items" ADD COLUMN IF NOT EXISTS "posted_auto" boolean;
|
|
@@ -498,6 +498,34 @@
|
|
|
498
498
|
"when": 1790665200000,
|
|
499
499
|
"tag": "0070_workspace_auto_fix",
|
|
500
500
|
"breakpoints": true
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
"idx": 71,
|
|
504
|
+
"version": "7",
|
|
505
|
+
"when": 1790751600000,
|
|
506
|
+
"tag": "0071_auto_fix_off_by_default",
|
|
507
|
+
"breakpoints": true
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
"idx": 72,
|
|
511
|
+
"version": "7",
|
|
512
|
+
"when": 1790838000000,
|
|
513
|
+
"tag": "0072_auto_review_daily_cap",
|
|
514
|
+
"breakpoints": true
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
"idx": 73,
|
|
518
|
+
"version": "7",
|
|
519
|
+
"when": 1790841600000,
|
|
520
|
+
"tag": "0073_ticket_review_pr_cards",
|
|
521
|
+
"breakpoints": true
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
"idx": 74,
|
|
525
|
+
"version": "7",
|
|
526
|
+
"when": 1790928000000,
|
|
527
|
+
"tag": "0074_auto_post",
|
|
528
|
+
"breakpoints": true
|
|
501
529
|
}
|
|
502
530
|
]
|
|
503
531
|
}
|
package/dist/db/queries.js
CHANGED
|
@@ -253,7 +253,7 @@ export async function ensureDefaultWorkspace(accountId) {
|
|
|
253
253
|
: `Default (workspace ${accountId})`;
|
|
254
254
|
await db
|
|
255
255
|
.insert(workspaces)
|
|
256
|
-
.values({ accountId, name, isDefault: true })
|
|
256
|
+
.values({ accountId, name, isDefault: true, autoFixEnabled: false })
|
|
257
257
|
.onConflictDoNothing()
|
|
258
258
|
.execute();
|
|
259
259
|
const after = await readDefault();
|
|
@@ -336,7 +336,7 @@ export async function listWorkspaces(accountId) {
|
|
|
336
336
|
export async function createWorkspace(accountId, name) {
|
|
337
337
|
const [row] = await db
|
|
338
338
|
.insert(workspaces)
|
|
339
|
-
.values({ accountId, name, isDefault: false })
|
|
339
|
+
.values({ accountId, name, isDefault: false, autoFixEnabled: false })
|
|
340
340
|
.returning()
|
|
341
341
|
.execute();
|
|
342
342
|
// No repos yet, so no repo-grained mutes to intersect — and `pending_muted` defaults to false.
|
|
@@ -14792,4 +14792,15 @@ export async function updateAutoMergeState(id, opts) {
|
|
|
14792
14792
|
.where(eq(autoMergeRequests.id, id))
|
|
14793
14793
|
.execute();
|
|
14794
14794
|
}
|
|
14795
|
+
/**
|
|
14796
|
+
* Is this user automation IN THIS WORKSPACE — the same union `hiddenBotUserIds` hides (a manual
|
|
14797
|
+
* "this is a human" wins both directions)? Read by auto-posting (review/claude-review/auto-post.ts),
|
|
14798
|
+
* which never posts on a bot-authored PR. A null user (an unmapped author) answers TRUE: a PR whose
|
|
14799
|
+
* author we cannot place is not one we write on.
|
|
14800
|
+
*/
|
|
14801
|
+
export async function isWorkspaceAutomationUser(accountId, workspaceId, userId) {
|
|
14802
|
+
if (userId == null)
|
|
14803
|
+
return true;
|
|
14804
|
+
return (await hiddenBotUserIds(accountId, workspaceId)).includes(userId);
|
|
14805
|
+
}
|
|
14795
14806
|
//# sourceMappingURL=queries.js.map
|
package/dist/db/schema.pg.js
CHANGED
|
@@ -726,6 +726,8 @@ export const claudeReviews = pgTable('claude_reviews', {
|
|
|
726
726
|
commentsThrough: timestamp('comments_through', { withTimezone: true, mode: 'date' }),
|
|
727
727
|
// Failed CI on the reviewed head, diagnosed. Twin of schema.sqlite.ts. Migration pg 0064 (sqlite 0077).
|
|
728
728
|
ciFailures: jsonb('ci_failures').$type(),
|
|
729
|
+
// What auto-posting did with this run (migration 0087 / pg 0074). Twin of schema.sqlite.ts.
|
|
730
|
+
autoPost: jsonb('auto_post').$type(),
|
|
729
731
|
}, (t) => ({
|
|
730
732
|
prIdx: index('cr_pr_idx').on(t.prId),
|
|
731
733
|
prShaIdx: index('cr_pr_sha_idx').on(t.prId, t.headSha),
|
|
@@ -779,6 +781,8 @@ export const claudeReviewFindings = pgTable('claude_review_findings', {
|
|
|
779
781
|
// 0066 (sqlite 0079).
|
|
780
782
|
storyIndex: integer('story_index'),
|
|
781
783
|
storyRef: text('story_ref'),
|
|
784
|
+
// Posted by auto-posting (migration 0087 / pg 0074). Twin of schema.sqlite.ts.
|
|
785
|
+
postedAuto: boolean('posted_auto'),
|
|
782
786
|
}, (t) => ({ reviewIdx: index('crf_review_idx').on(t.reviewId) }));
|
|
783
787
|
// Claude Review chat messages. Twin of schema.sqlite.ts claudeReviewChatMessages, where the
|
|
784
788
|
// contract lives. Migration pg 0060 (sqlite 0073).
|
|
@@ -855,6 +859,8 @@ export const ticketReviews = pgTable('ticket_reviews', {
|
|
|
855
859
|
summary: text('summary'),
|
|
856
860
|
// The server-validated TicketAssessment. Null unless the run succeeded.
|
|
857
861
|
assessment: jsonb('assessment').$type(),
|
|
862
|
+
// What auto-posting did with this run (migration 0087 / pg 0074). Twin of schema.sqlite.ts.
|
|
863
|
+
autoPost: jsonb('auto_post').$type(),
|
|
858
864
|
}, (t) => ({
|
|
859
865
|
accountIdentIdx: index('tr_account_ident_created_idx').on(t.accountId, t.ticketIdent, t.createdAt),
|
|
860
866
|
// The per-workspace daily cap on automatic runs.
|
|
@@ -908,6 +914,8 @@ export const ticketReviewItems = pgTable('ticket_review_items', {
|
|
|
908
914
|
postedPrId: integer('posted_pr_id'),
|
|
909
915
|
postedCommentId: text('posted_comment_id'),
|
|
910
916
|
postedAt: timestamp('posted_at', { withTimezone: true, mode: 'date' }),
|
|
917
|
+
// Posted by auto-posting (migration 0087 / pg 0074). Twin of schema.sqlite.ts.
|
|
918
|
+
postedAuto: boolean('posted_auto'),
|
|
911
919
|
priorItemId: integer('prior_item_id'),
|
|
912
920
|
createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' })
|
|
913
921
|
.notNull()
|
|
@@ -922,6 +930,30 @@ export const ticketReviewItems = pgTable('ticket_review_items', {
|
|
|
922
930
|
foreignColumns: [ticketReviews.id, ticketReviews.accountId],
|
|
923
931
|
}).onDelete('cascade'),
|
|
924
932
|
}));
|
|
933
|
+
// ---- Ticket review contribution cards (CORE; pg 0073 / sqlite 0086) ----
|
|
934
|
+
// Twin of schema.sqlite.ts ticketReviewPrCards, where the contract lives.
|
|
935
|
+
export const ticketReviewPrCards = pgTable('ticket_review_pr_cards', {
|
|
936
|
+
id: serial('id').primaryKey(),
|
|
937
|
+
accountId: integer('account_id')
|
|
938
|
+
.notNull()
|
|
939
|
+
.references(() => accounts.id, { onDelete: 'cascade' }),
|
|
940
|
+
prId: integer('pr_id').notNull(),
|
|
941
|
+
headSha: text('head_sha').notNull(),
|
|
942
|
+
card: jsonb('card').$type().notNull(),
|
|
943
|
+
source: text('source', { enum: ['story_check', 'prepass'] }).notNull(),
|
|
944
|
+
model: text('model').notNull(),
|
|
945
|
+
costUsd: doublePrecision('cost_usd'),
|
|
946
|
+
createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' })
|
|
947
|
+
.notNull()
|
|
948
|
+
.defaultNow(),
|
|
949
|
+
}, (t) => ({
|
|
950
|
+
accountPrHeadUx: uniqueIndex('trpc_account_pr_head_ux').on(t.accountId, t.prId, t.headSha),
|
|
951
|
+
prAccountFk: foreignKey({
|
|
952
|
+
name: 'trpc_pr_account_fk',
|
|
953
|
+
columns: [t.prId, t.accountId],
|
|
954
|
+
foreignColumns: [pullRequests.id, pullRequests.accountId],
|
|
955
|
+
}).onDelete('cascade'),
|
|
956
|
+
}));
|
|
925
957
|
// ---- CI review (CORE; pg 0069 / sqlite 0082) ----
|
|
926
958
|
// Twin of schema.sqlite.ts ciReviews / ciReviewItems, where the contract lives.
|
|
927
959
|
export const ciReviews = pgTable('ci_reviews', {
|
|
@@ -1081,7 +1113,13 @@ export const workspaces = pgTable('workspaces', {
|
|
|
1081
1113
|
autoReviewEnabled: boolean('auto_review_enabled'),
|
|
1082
1114
|
autoReviewEnabledAt: timestamp('auto_review_enabled_at', { withTimezone: true, mode: 'date' }),
|
|
1083
1115
|
// Auto AI Fix (migration 0083 / pg 0070) — the pg twin. DEFAULT true. Rationale in the sqlite twin.
|
|
1084
|
-
autoFixEnabled: boolean('auto_fix_enabled').notNull().default(
|
|
1116
|
+
autoFixEnabled: boolean('auto_fix_enabled').notNull().default(false),
|
|
1117
|
+
// Auto review daily cap (migration 0085 / pg 0072) — the pg twin. NULL = the default 20.
|
|
1118
|
+
autoReviewDailyCap: integer('auto_review_daily_cap'),
|
|
1119
|
+
// Auto-posting (migration 0087 / pg 0074) — the pg twin. NULL/false = off; settings are
|
|
1120
|
+
// overrides only. Rationale in the sqlite twin.
|
|
1121
|
+
autoPostEnabled: boolean('auto_post_enabled'),
|
|
1122
|
+
autoPostSettings: jsonb('auto_post_settings').$type(),
|
|
1085
1123
|
createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' })
|
|
1086
1124
|
.notNull()
|
|
1087
1125
|
.defaultNow(),
|
package/dist/db/schema.sqlite.js
CHANGED
|
@@ -1007,6 +1007,11 @@ export const claudeReviews = sqliteTable('claude_reviews', {
|
|
|
1007
1007
|
// the field, a skip, a run that did not succeed, or one whose checks could not be read.
|
|
1008
1008
|
// Migration 0077 (pg 0064).
|
|
1009
1009
|
ciFailures: text('ci_failures', { mode: 'json' }).$type(),
|
|
1010
|
+
// WHAT AUTO-POSTING DID WITH THIS RUN (migration 0087 / pg 0074): written once, by the claim in
|
|
1011
|
+
// review/claude-review/auto-post.ts (`status: 'posting'` BEFORE any GitHub call, so a crash
|
|
1012
|
+
// mid-post can never lead to a retry), then settled to posted / partial / failed / skipped.
|
|
1013
|
+
// NULL = auto-posting never looked at this run (a manual run, or the switch was off).
|
|
1014
|
+
autoPost: text('auto_post', { mode: 'json' }).$type(),
|
|
1010
1015
|
}, (t) => ({
|
|
1011
1016
|
prIdx: index('cr_pr_idx').on(t.prId),
|
|
1012
1017
|
prShaIdx: index('cr_pr_sha_idx').on(t.prId, t.headSha),
|
|
@@ -1077,6 +1082,9 @@ export const claudeReviewFindings = sqliteTable('claude_review_findings', {
|
|
|
1077
1082
|
// together: a row has both or neither.
|
|
1078
1083
|
storyIndex: integer('story_index'),
|
|
1079
1084
|
storyRef: text('story_ref'),
|
|
1085
|
+
// true = auto-posting put this finding on GitHub (migration 0087 / pg 0074); NULL/false = a
|
|
1086
|
+
// person did, or it is not posted. Display only ("Posted automatically").
|
|
1087
|
+
postedAuto: integer('posted_auto', { mode: 'boolean' }),
|
|
1080
1088
|
}, (t) => ({ reviewIdx: index('crf_review_idx').on(t.reviewId) }));
|
|
1081
1089
|
// ---- Claude Review chat (CORE table, written by the Pro plugin) ----
|
|
1082
1090
|
// One row per chat MESSAGE about one succeeded review: a general thread (`finding_id` NULL) and
|
|
@@ -1185,6 +1193,9 @@ export const ticketReviews = sqliteTable('ticket_reviews', {
|
|
|
1185
1193
|
summary: text('summary'),
|
|
1186
1194
|
// The server-validated TicketAssessment. Null unless the run succeeded.
|
|
1187
1195
|
assessment: text('assessment', { mode: 'json' }).$type(),
|
|
1196
|
+
// What auto-posting did with this run (migration 0087 / pg 0074) — review/ticket-review/
|
|
1197
|
+
// auto-post.ts. Also holds the posted "Not asked for" entries, which have no item row.
|
|
1198
|
+
autoPost: text('auto_post', { mode: 'json' }).$type(),
|
|
1188
1199
|
}, (t) => ({
|
|
1189
1200
|
accountIdentIdx: index('tr_account_ident_created_idx').on(t.accountId, t.ticketIdent, t.createdAt),
|
|
1190
1201
|
// The per-workspace daily cap on automatic runs.
|
|
@@ -1238,6 +1249,8 @@ export const ticketReviewItems = sqliteTable('ticket_review_items', {
|
|
|
1238
1249
|
postedPrId: integer('posted_pr_id'),
|
|
1239
1250
|
postedCommentId: text('posted_comment_id'),
|
|
1240
1251
|
postedAt: integer('posted_at', { mode: 'timestamp' }),
|
|
1252
|
+
// true = auto-posting posted it (migration 0087 / pg 0074); carried with the posting.
|
|
1253
|
+
postedAuto: integer('posted_auto', { mode: 'boolean' }),
|
|
1241
1254
|
priorItemId: integer('prior_item_id'),
|
|
1242
1255
|
createdAt: integer('created_at', { mode: 'timestamp' })
|
|
1243
1256
|
.notNull()
|
|
@@ -1252,6 +1265,45 @@ export const ticketReviewItems = sqliteTable('ticket_review_items', {
|
|
|
1252
1265
|
foreignColumns: [ticketReviews.id, ticketReviews.accountId],
|
|
1253
1266
|
}).onDelete('cascade'),
|
|
1254
1267
|
}));
|
|
1268
|
+
// ---- Ticket review CONTRIBUTION CARDS (migration 0086, pg 0073) ----
|
|
1269
|
+
// One row per (account, PR, head commit): a model-written, verdict-free description of what that
|
|
1270
|
+
// PR's head does (shared `TicketPrCard`: summary, interfaces, criteria it moves forward, loose
|
|
1271
|
+
// ends) plus the changed files at that head (server-written). Written by a ticket review — its own
|
|
1272
|
+
// run for every member it read as a diff (`source = 'story_check'`), or the cheap per-PR pre-pass
|
|
1273
|
+
// for the overflow beyond TICKET_REVIEW_MAX_DIFFS (`'prepass'`, with that card's own cost). A later
|
|
1274
|
+
// run reads a member whose current head has a card AS THE CARD. Currency is `head_sha` equality
|
|
1275
|
+
// with the PR's synced head: a merged PR's head never moves, so its card holds for good.
|
|
1276
|
+
// ⚠ Card availability NEVER enters the ticket fingerprint (review/ticket-review/fingerprint.ts).
|
|
1277
|
+
// Re-writing the same head replaces the card (upsert on the unique below).
|
|
1278
|
+
// Tenancy: a composite FK against pull_requests(id, account_id). ⚠ BOTH DELETE PATHS clear rows for
|
|
1279
|
+
// the deleted PRs (db/ticket-review-prune.ts), plus eraseAccountData + accountScopedTables().
|
|
1280
|
+
// Twin: schema.pg.ts.
|
|
1281
|
+
export const ticketReviewPrCards = sqliteTable('ticket_review_pr_cards', {
|
|
1282
|
+
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
1283
|
+
accountId: integer('account_id')
|
|
1284
|
+
.notNull()
|
|
1285
|
+
.references(() => accounts.id, { onDelete: 'cascade' }),
|
|
1286
|
+
// Composite FK below (no single-column one).
|
|
1287
|
+
prId: integer('pr_id').notNull(),
|
|
1288
|
+
headSha: text('head_sha').notNull(),
|
|
1289
|
+
card: text('card', { mode: 'json' }).$type().notNull(),
|
|
1290
|
+
source: text('source', { enum: ['story_check', 'prepass'] }).notNull(),
|
|
1291
|
+
// Stored model id (no drizzle `enum:`, like ticket_reviews.model).
|
|
1292
|
+
model: text('model').notNull(),
|
|
1293
|
+
// A pre-pass card's own spend; null for a card written inside a ticket review's run (that run's
|
|
1294
|
+
// cost holds it).
|
|
1295
|
+
costUsd: real('cost_usd'),
|
|
1296
|
+
createdAt: integer('created_at', { mode: 'timestamp' })
|
|
1297
|
+
.notNull()
|
|
1298
|
+
.default(sql `(unixepoch())`),
|
|
1299
|
+
}, (t) => ({
|
|
1300
|
+
accountPrHeadUx: uniqueIndex('trpc_account_pr_head_ux').on(t.accountId, t.prId, t.headSha),
|
|
1301
|
+
prAccountFk: foreignKey({
|
|
1302
|
+
name: 'trpc_pr_account_fk',
|
|
1303
|
+
columns: [t.prId, t.accountId],
|
|
1304
|
+
foreignColumns: [pullRequests.id, pullRequests.accountId],
|
|
1305
|
+
}).onDelete('cascade'),
|
|
1306
|
+
}));
|
|
1255
1307
|
// ---- CI review (CORE, local-only like the rest of Claude Review) ----
|
|
1256
1308
|
// A separate Claude run that explains why checks FAILED on a PR's head — split out of the PR review's
|
|
1257
1309
|
// own run (`claude_reviews.ci_failures`, now LEGACY and read-only). Two tables, migration 0082
|
|
@@ -1474,7 +1526,20 @@ export const workspaces = sqliteTable('workspaces', {
|
|
|
1474
1526
|
// column, so existing workspaces keep that until switched off. Read by `maybeStartAutoFix`
|
|
1475
1527
|
// (coding/ai-fix/auto-fix.ts); the ONE writer is `setWorkspaceAutoReview`. It only matters
|
|
1476
1528
|
// while auto review is on.
|
|
1477
|
-
autoFixEnabled: integer('auto_fix_enabled', { mode: 'boolean' }).notNull().default(
|
|
1529
|
+
autoFixEnabled: integer('auto_fix_enabled', { mode: 'boolean' }).notNull().default(false),
|
|
1530
|
+
// AUTO REVIEW DAILY CAP (migration 0085 / pg 0072): auto runs this workspace may start per UTC
|
|
1531
|
+
// day. OVERRIDES ONLY — NULL = the default `AUTO_REVIEW_DAILY_CAP` (20), resolved through
|
|
1532
|
+
// `resolveAutoReviewDailyCap`; the route bounds it 1..500. The ONE writer is
|
|
1533
|
+
// `setWorkspaceAutoReview`, the reader the sweeper's roster (`listAutoReviewWorkspaces`).
|
|
1534
|
+
autoReviewDailyCap: integer('auto_review_daily_cap'),
|
|
1535
|
+
// AUTO-POSTING (migration 0087 / pg 0074): may a SUCCEEDED AUTO review (and an auto ticket
|
|
1536
|
+
// review) post its output to GitHub without a click? NULL/false = OFF — the default for every
|
|
1537
|
+
// workspace, existing and new; nullable with no default, so no workspace insert has to name it.
|
|
1538
|
+
// `autoPostSettings` is OVERRIDES ONLY ({ scope?, kinds? }) — NULL = the product defaults,
|
|
1539
|
+
// resolved through `resolveAutoPostSettings` (review/claude-review/auto-settings.ts). The ONE
|
|
1540
|
+
// writer is `setWorkspaceAutoReview`; the reader is review/claude-review/auto-post.ts.
|
|
1541
|
+
autoPostEnabled: integer('auto_post_enabled', { mode: 'boolean' }),
|
|
1542
|
+
autoPostSettings: text('auto_post_settings', { mode: 'json' }).$type(),
|
|
1478
1543
|
createdAt: integer('created_at', { mode: 'timestamp' })
|
|
1479
1544
|
.notNull()
|
|
1480
1545
|
.default(sql `(unixepoch())`),
|
|
@@ -6,7 +6,9 @@ import { schema } from './client.js';
|
|
|
6
6
|
// • a run left with NO members is deleted with its items — it describes nothing any more. That
|
|
7
7
|
// includes a run refused before it had members, which only its starting PR anchors;
|
|
8
8
|
// • an item whose owner was a deleted PR keeps its history but loses the owner (`owner_pr_id`
|
|
9
|
-
// null), so a Post can never target a PR that is gone
|
|
9
|
+
// null), so a Post can never target a PR that is gone;
|
|
10
|
+
// • the deleted PRs' contribution cards (`ticket_review_pr_cards`, migration 0086 / pg 0073) go
|
|
11
|
+
// with them — a card describes one PR's head and nothing else.
|
|
10
12
|
// Children before parents, explicitly — the composite FKs cascade, but these paths run parent-last
|
|
11
13
|
// by hand on both dialects. Runs inside the caller's transaction.
|
|
12
14
|
export async function pruneTicketReviewsForPrs(tx, prIds) {
|
|
@@ -29,6 +31,7 @@ export async function pruneTicketReviewsForPrs(tx, prIds) {
|
|
|
29
31
|
.where(inArray(tr.originPrId, ids))
|
|
30
32
|
.execute());
|
|
31
33
|
await tx.delete(m).where(inArray(m.prId, ids)).execute();
|
|
34
|
+
await tx.delete(schema.ticketReviewPrCards).where(inArray(schema.ticketReviewPrCards.prId, ids)).execute();
|
|
32
35
|
await tx.update(items).set({ ownerPrId: null }).where(inArray(items.ownerPrId, ids)).execute();
|
|
33
36
|
const runIds = [...new Set([...touched, ...started].map((r) => r.id))];
|
|
34
37
|
if (runIds.length === 0)
|