Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,14 @@ export function TownSettingsPageClient({ townId, readOnly = false, organizationI
const [refineryGates, setRefineryGates] = useState<string[]>([]);
const [autoMerge, setAutoMerge] = useState(true);
const [refineryCodeReview, setRefineryCodeReview] = useState(true);
const [reviewMode, setReviewMode] = useState<'rework' | 'comments'>('rework');
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One day we'll migrate to https://react-hook-form.com/ and not have a bajillion useState's

const [autoResolvePrFeedback, setAutoResolvePrFeedback] = useState(false);
const [autoMergeDelayMinutes, setAutoMergeDelayMinutes] = useState<number | null>(null);
const [mergeStrategy, setMergeStrategy] = useState<'direct' | 'pr'>('direct');
const [stagedConvoysDefault, setStagedConvoysDefault] = useState(false);
const [convoyMergeMode, setConvoyMergeMode] = useState<'review-then-land' | 'review-and-merge'>(
'review-then-land'
);
const [githubCliPat, setGithubCliPat] = useState('');
const [gitAuthorName, setGitAuthorName] = useState('');
const [gitAuthorEmail, setGitAuthorEmail] = useState('');
Expand All @@ -296,10 +300,14 @@ export function TownSettingsPageClient({ townId, readOnly = false, organizationI
setRefineryGates(cfg.refinery?.gates ?? []);
setAutoMerge(cfg.refinery?.auto_merge ?? true);
setRefineryCodeReview(cfg.refinery?.code_review ?? true);
setReviewMode(cfg.refinery?.review_mode === 'comments' ? 'comments' : 'rework');
setAutoResolvePrFeedback(cfg.refinery?.auto_resolve_pr_feedback ?? false);
setAutoMergeDelayMinutes(cfg.refinery?.auto_merge_delay_minutes ?? null);
setMergeStrategy(cfg.merge_strategy === 'pr' ? 'pr' : 'direct');
setStagedConvoysDefault(cfg.staged_convoys_default ?? false);
setConvoyMergeMode(
cfg.convoy_merge_mode === 'review-and-merge' ? 'review-and-merge' : 'review-then-land'
);
setGithubCliPat(cfg.github_cli_pat ?? '');
savedGithubCliPatRef.current = cfg.github_cli_pat ?? '';
savedGithubTokenRef.current = cfg.git_auth?.github_token ?? '';
Expand Down Expand Up @@ -352,10 +360,12 @@ export function TownSettingsPageClient({ townId, readOnly = false, organizationI
gates: refineryGates.filter(g => g.trim()),
auto_merge: autoMerge,
code_review: refineryCodeReview,
review_mode: reviewMode,
require_clean_merge: true,
auto_resolve_pr_feedback: autoResolvePrFeedback,
auto_merge_delay_minutes: autoMergeDelayMinutes,
},
convoy_merge_mode: convoyMergeMode,
},
});
}
Expand Down Expand Up @@ -740,6 +750,41 @@ export function TownSettingsPageClient({ townId, readOnly = false, organizationI
onCheckedChange={setStagedConvoysDefault}
/>
</div>

<div className="mt-3">
<Label className="text-sm text-white/70">Default merge mode</Label>
<p className="mb-2 text-[11px] text-white/30">
Controls how convoy beads are merged.
</p>
<div className="flex gap-2">
<button
onClick={() => setConvoyMergeMode('review-then-land')}
className={`flex-1 rounded-lg border px-3 py-2 text-xs transition-colors ${
convoyMergeMode === 'review-then-land'
? 'border-indigo-500/40 bg-indigo-500/10 text-indigo-300'
: 'border-white/[0.06] bg-white/[0.02] text-white/40 hover:border-white/10'
}`}
>
<div className="font-medium">Review then land</div>
<div className="mt-0.5 text-[10px] opacity-60">
Beads merge into a feature branch. One landing PR at the end.
</div>
</button>
<button
onClick={() => setConvoyMergeMode('review-and-merge')}
className={`flex-1 rounded-lg border px-3 py-2 text-xs transition-colors ${
convoyMergeMode === 'review-and-merge'
? 'border-indigo-500/40 bg-indigo-500/10 text-indigo-300'
: 'border-white/[0.06] bg-white/[0.02] text-white/40 hover:border-white/10'
}`}
>
<div className="font-medium">Review and merge</div>
<div className="mt-0.5 text-[10px] opacity-60">
Each bead gets its own PR. Auto-merge applies per PR.
</div>
</button>
</div>
</div>
</SettingsSection>

{/* ── Merge Strategy ──────────────────────────────────── */}
Expand Down Expand Up @@ -825,13 +870,55 @@ export function TownSettingsPageClient({ townId, readOnly = false, organizationI
<div className="flex-1">
<Label className="text-sm text-white/70">Refinery code review</Label>
<p className="text-[11px] text-white/30">
The refinery agent reviews PRs and adds GitHub review comments. Disable if you
already use an external code-review bot.
The refinery agent reviews PRs — runs quality gates, checks the diff, and may
request rework. When disabled, PRs skip the refinery entirely.
</p>
</div>
<Switch checked={refineryCodeReview} onCheckedChange={setRefineryCodeReview} />
</div>

{refineryCodeReview && (
<div className="mt-3">
<Label className="text-sm text-white/70">Review mode</Label>
<p className="mb-2 text-[11px] text-white/30">
How the refinery communicates its findings.
</p>
<div className="flex gap-2">
<button
onClick={() => setReviewMode('rework')}
className={`flex-1 rounded-lg border px-3 py-2 text-xs transition-colors ${
reviewMode === 'rework'
? 'border-indigo-500/40 bg-indigo-500/10 text-indigo-300'
: 'border-white/[0.06] bg-white/[0.02] text-white/40 hover:border-white/10'
}`}
>
<div className="font-medium">Rework requests</div>
<div className="mt-0.5 text-[10px] opacity-60">
Creates internal rework beads for the polecat to fix.
</div>
</button>
<button
onClick={() => mergeStrategy === 'pr' && setReviewMode('comments')}
disabled={mergeStrategy !== 'pr'}
className={`flex-1 rounded-lg border px-3 py-2 text-xs transition-colors ${
reviewMode === 'comments'
? 'border-indigo-500/40 bg-indigo-500/10 text-indigo-300'
: mergeStrategy !== 'pr'
? 'cursor-not-allowed border-white/[0.03] bg-white/[0.01] text-white/20'
: 'border-white/[0.06] bg-white/[0.02] text-white/40 hover:border-white/10'
}`}
>
<div className="font-medium">PR comments</div>
<div className="mt-0.5 text-[10px] opacity-60">
{mergeStrategy !== 'pr'
? 'Requires Pull Request merge strategy.'
: 'Posts GitHub review comments on the PR.'}
</div>
</button>
</div>
</div>
)}

<div className="mt-3 flex items-center gap-3 rounded-lg border border-white/[0.06] bg-white/[0.02] px-4 py-3">
<div className="flex-1">
<Label className="text-sm text-white/70">Auto-resolve PR feedback</Label>
Expand Down Expand Up @@ -885,7 +972,7 @@ export function TownSettingsPageClient({ townId, readOnly = false, organizationI
const v = parseInt(e.target.value, 10);
setAutoMergeDelayMinutes(Number.isNaN(v) ? null : Math.max(0, v));
}}
className="w-20 border-white/[0.08] bg-white/[0.03] text-center font-mono text-xs text-white/85"
className="h-[26px] w-[60px] border-white/[0.08] bg-white/[0.03] text-center font-mono text-xs text-white/85"
/>
<span className="text-[11px] text-white/30">minutes</span>
</div>
Expand Down
12 changes: 12 additions & 0 deletions apps/web/src/lib/gastown/types/router.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ export declare const gastownRouter: import('@trpc/server').TRPCBuiltRouter<
auto_merge: boolean;
require_clean_merge: boolean;
code_review: boolean;
review_mode: 'comments' | 'rework';
auto_resolve_pr_feedback: boolean;
auto_merge_delay_minutes: number | null;
}
Expand All @@ -498,6 +499,7 @@ export declare const gastownRouter: import('@trpc/server').TRPCBuiltRouter<
}
| undefined;
staged_convoys_default: boolean;
convoy_merge_mode: 'review-and-merge' | 'review-then-land';
github_cli_pat?: string | undefined;
git_author_name?: string | undefined;
git_author_email?: string | undefined;
Expand Down Expand Up @@ -541,6 +543,7 @@ export declare const gastownRouter: import('@trpc/server').TRPCBuiltRouter<
auto_merge?: boolean | undefined;
require_clean_merge?: boolean | undefined;
code_review?: boolean | undefined;
review_mode?: 'comments' | 'rework' | undefined;
auto_resolve_pr_feedback?: boolean | undefined;
auto_merge_delay_minutes?: number | null | undefined;
}
Expand All @@ -553,6 +556,7 @@ export declare const gastownRouter: import('@trpc/server').TRPCBuiltRouter<
}
| undefined;
staged_convoys_default?: boolean | undefined;
convoy_merge_mode?: 'review-and-merge' | 'review-then-land' | undefined;
github_cli_pat?: string | undefined;
git_author_name?: string | undefined;
git_author_email?: string | undefined;
Expand Down Expand Up @@ -590,6 +594,7 @@ export declare const gastownRouter: import('@trpc/server').TRPCBuiltRouter<
auto_merge: boolean;
require_clean_merge: boolean;
code_review: boolean;
review_mode: 'comments' | 'rework';
auto_resolve_pr_feedback: boolean;
auto_merge_delay_minutes: number | null;
}
Expand All @@ -602,6 +607,7 @@ export declare const gastownRouter: import('@trpc/server').TRPCBuiltRouter<
}
| undefined;
staged_convoys_default: boolean;
convoy_merge_mode: 'review-and-merge' | 'review-then-land';
github_cli_pat?: string | undefined;
git_author_name?: string | undefined;
git_author_email?: string | undefined;
Expand Down Expand Up @@ -1791,6 +1797,7 @@ export declare const wrappedGastownRouter: import('@trpc/server').TRPCBuiltRoute
auto_merge: boolean;
require_clean_merge: boolean;
code_review: boolean;
review_mode: 'comments' | 'rework';
auto_resolve_pr_feedback: boolean;
auto_merge_delay_minutes: number | null;
}
Expand All @@ -1803,6 +1810,7 @@ export declare const wrappedGastownRouter: import('@trpc/server').TRPCBuiltRoute
}
| undefined;
staged_convoys_default: boolean;
convoy_merge_mode: 'review-and-merge' | 'review-then-land';
github_cli_pat?: string | undefined;
git_author_name?: string | undefined;
git_author_email?: string | undefined;
Expand Down Expand Up @@ -1846,6 +1854,7 @@ export declare const wrappedGastownRouter: import('@trpc/server').TRPCBuiltRoute
auto_merge?: boolean | undefined;
require_clean_merge?: boolean | undefined;
code_review?: boolean | undefined;
review_mode?: 'comments' | 'rework' | undefined;
auto_resolve_pr_feedback?: boolean | undefined;
auto_merge_delay_minutes?: number | null | undefined;
}
Expand All @@ -1858,6 +1867,7 @@ export declare const wrappedGastownRouter: import('@trpc/server').TRPCBuiltRoute
}
| undefined;
staged_convoys_default?: boolean | undefined;
convoy_merge_mode?: 'review-and-merge' | 'review-then-land' | undefined;
github_cli_pat?: string | undefined;
git_author_name?: string | undefined;
git_author_email?: string | undefined;
Expand Down Expand Up @@ -1895,6 +1905,7 @@ export declare const wrappedGastownRouter: import('@trpc/server').TRPCBuiltRoute
auto_merge: boolean;
require_clean_merge: boolean;
code_review: boolean;
review_mode: 'comments' | 'rework';
auto_resolve_pr_feedback: boolean;
auto_merge_delay_minutes: number | null;
}
Expand All @@ -1907,6 +1918,7 @@ export declare const wrappedGastownRouter: import('@trpc/server').TRPCBuiltRoute
}
| undefined;
staged_convoys_default: boolean;
convoy_merge_mode: 'review-and-merge' | 'review-then-land';
github_cli_pat?: string | undefined;
git_author_name?: string | undefined;
git_author_email?: string | undefined;
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/routers/admin/gastown-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ const TownConfigRecord = z.object({
auto_merge: z.boolean(),
require_clean_merge: z.boolean(),
code_review: z.boolean(),
review_mode: z.enum(['rework', 'comments']),
auto_resolve_pr_feedback: z.boolean(),
auto_merge_delay_minutes: z.number().nullable(),
})
Expand Down
Loading
Loading