Skip to content

Commit c3bd802

Browse files
committed
ship: checkpoint before automate/finalize
1 parent 3a5fbe5 commit c3bd802

20 files changed

Lines changed: 392 additions & 81 deletions

apps/desktop/src/main/services/lanes/autoRebaseService.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -112,17 +112,17 @@ function blockedMessage(
112112
laneId: string | null,
113113
reason: "conflict" | "manual" | "lookup" | "failed" | "unavailable" | null,
114114
): string {
115-
if (!laneId) return "Pending: auto-rebase stopped at an earlier lane. Open the Rebase tab to continue.";
115+
if (!laneId) return "Pending: auto-rebase stopped at an earlier lane. Open the Rebase/Merge tab to continue.";
116116
if (reason === "manual") {
117-
return `Pending: ancestor lane '${laneId}' has a fixed PR base. Rebase that lane manually from the Rebase tab before descendants can continue.`;
117+
return `Pending: ancestor lane '${laneId}' has a fixed PR base. Rebase that lane manually from the Rebase/Merge tab before descendants can continue.`;
118118
}
119119
if (reason === "lookup" || reason === "unavailable") {
120-
return `Pending: ancestor lane '${laneId}' needs review before descendants can continue. Open the Rebase tab to inspect it.`;
120+
return `Pending: ancestor lane '${laneId}' needs review before descendants can continue. Open the Rebase/Merge tab to inspect it.`;
121121
}
122122
if (reason === "failed") {
123-
return `Pending: ancestor lane '${laneId}' failed automatic rebase. Open the Rebase tab to retry.`;
123+
return `Pending: ancestor lane '${laneId}' failed automatic rebase. Open the Rebase/Merge tab to retry.`;
124124
}
125-
return `Pending: ancestor lane '${laneId}' has unresolved rebase conflicts. Open the Rebase tab to continue.`;
125+
return `Pending: ancestor lane '${laneId}' has unresolved rebase conflicts. Open the Rebase/Merge tab to continue.`;
126126
}
127127

128128
function resolveAffectedChainLaneId(
@@ -459,7 +459,7 @@ export function createAutoRebaseService(args: {
459459
parentHeadSha: null,
460460
state: "rebasePending",
461461
conflictCount: 0,
462-
message: "Pending: parent lane is unavailable. Open the Rebase tab to review the lane."
462+
message: "Pending: parent lane is unavailable. Open the Rebase/Merge tab to review the lane."
463463
});
464464
blocked = true;
465465
blockedLaneId = lane.id;
@@ -522,7 +522,7 @@ export function createAutoRebaseService(args: {
522522
parentHeadSha,
523523
state: "rebaseConflict",
524524
conflictCount: Math.max(1, need.conflictingFiles.length),
525-
message: `Auto-rebase blocked: ${Math.max(1, need.conflictingFiles.length)} conflict(s) expected. Open the Rebase tab to resolve and publish.`
525+
message: `Auto-rebase blocked: ${Math.max(1, need.conflictingFiles.length)} conflict(s) expected. Open the Rebase/Merge tab to resolve and publish.`
526526
});
527527
continue;
528528
}
@@ -541,7 +541,7 @@ export function createAutoRebaseService(args: {
541541
parentHeadSha,
542542
state: "rebasePending",
543543
conflictCount: 0,
544-
message: "PR carries an immutable base — drift detected. Rebase manually from the Rebase tab when ready."
544+
message: "PR carries an immutable base — drift detected. Rebase manually from the Rebase/Merge tab when ready."
545545
});
546546
continue;
547547
}
@@ -572,8 +572,8 @@ export function createAutoRebaseService(args: {
572572
state: conflictHint ? "rebaseConflict" : "rebaseFailed",
573573
conflictCount: conflictHint ? 1 : 0,
574574
message: conflictHint
575-
? "Auto-rebase stopped due to conflicts. Open the Rebase tab to resolve, then publish."
576-
: `Auto-rebase failed: ${rebaseRun.run.error}. Open the Rebase tab to retry.`
575+
? "Auto-rebase stopped due to conflicts. Open the Rebase/Merge tab to resolve, then publish."
576+
: `Auto-rebase failed: ${rebaseRun.run.error}. Open the Rebase/Merge tab to retry.`
577577
});
578578
continue;
579579
}
@@ -625,8 +625,8 @@ export function createAutoRebaseService(args: {
625625
state: "rebaseFailed",
626626
conflictCount: 0,
627627
message: rollbackError
628-
? `Auto-push failed: ${pushError}. Automatic rollback also failed: ${rollbackError}. Open the Rebase tab to retry.`
629-
: `Auto-push failed: ${pushError}. The lane was restored to its pre-rebase state. Open the Rebase tab to retry.`
628+
? `Auto-push failed: ${pushError}. Automatic rollback also failed: ${rollbackError}. Open the Rebase/Merge tab to retry.`
629+
: `Auto-push failed: ${pushError}. The lane was restored to its pre-rebase state. Open the Rebase/Merge tab to retry.`
630630
});
631631
}
632632
}

apps/desktop/src/main/services/prs/prRebaseResolver.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,10 @@ describe("launchRebaseResolutionChat", () => {
141141
expect(sendMessage).toHaveBeenCalledWith(
142142
expect.objectContaining({
143143
sessionId: "session-rebase-1",
144-
displayText: "Rebase feature/rebase-target onto main",
145144
reasoningEffort: "high",
146145
}),
147146
);
147+
expect(sendMessage.mock.calls[0]?.[0]).not.toHaveProperty("displayText");
148148
expect(result).toEqual({
149149
sessionId: "session-rebase-1",
150150
laneId: lane.id,

apps/desktop/src/main/services/prs/prRebaseResolver.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,6 @@ export async function launchRebaseResolutionChat(
158158
await deps.agentChatService.sendMessage({
159159
sessionId: session.id,
160160
text: prompt,
161-
displayText: title,
162161
...(reasoningEffort ? { reasoningEffort } : {}),
163162
});
164163

apps/desktop/src/main/services/prs/prService.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ export function createPrService({
842842
parentHeadSha: null,
843843
state: "rebaseFailed",
844844
conflictCount: 0,
845-
message: `Auto-rebase failed after '${args.landedLaneName}' merged because ADE could not find a new parent lane. Open the Rebase tab to recover this lane.`,
845+
message: `Auto-rebase failed after '${args.landedLaneName}' merged because ADE could not find a new parent lane. Open the Rebase/Merge tab to recover this lane.`,
846846
}, child.id);
847847
}
848848
return {
@@ -950,8 +950,8 @@ export function createPrService({
950950
state: "rebaseFailed",
951951
conflictCount: 0,
952952
message: rollbackError
953-
? `Auto-rebase failed after '${args.landedLaneName}' merged: ${childError}. Automatic rollback also failed: ${rollbackError}. Open the Rebase tab to recover this lane.`
954-
: `Auto-rebase failed after '${args.landedLaneName}' merged: ${childError}. The lane was restored to its pre-rebase state. Open the Rebase tab to recover this lane.`,
953+
? `Auto-rebase failed after '${args.landedLaneName}' merged: ${childError}. Automatic rollback also failed: ${rollbackError}. Open the Rebase/Merge tab to recover this lane.`
954+
: `Auto-rebase failed after '${args.landedLaneName}' merged: ${childError}. The lane was restored to its pre-rebase state. Open the Rebase/Merge tab to recover this lane.`,
955955
}, child.id);
956956
failedLaneIds.push(child.id);
957957
}

apps/desktop/src/renderer/components/lanes/LaneGitActionsPane.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ describe("LaneGitActionsPane rescue action", () => {
328328
expect(syncButton.getAttribute("title")).toMatch(/before rebasing and pushing/i);
329329
});
330330

331-
it("treats auto-rebase conflicts as failures and links to the Rebase tab", async () => {
331+
it("treats auto-rebase conflicts as failures and links to the Rebase/Merge tab", async () => {
332332
const user = userEvent.setup();
333333
const resolveRebaseConflict = vi.fn();
334334
mockAutoRebaseStatuses = [
@@ -345,7 +345,7 @@ describe("LaneGitActionsPane rescue action", () => {
345345

346346
renderPane({ onResolveRebaseConflict: resolveRebaseConflict });
347347

348-
const rebaseTabButton = await screen.findByRole("button", { name: /open rebase tab/i });
348+
const rebaseTabButton = await screen.findByRole("button", { name: /open rebase\/merge tab/i });
349349
screen.getByText("AUTO-REBASE FAILED");
350350
screen.getByText(/auto-rebase failed\. files need follow-up before this lane can be pushed\./i);
351351

apps/desktop/src/renderer/components/lanes/LaneGitActionsPane.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,7 +1546,7 @@ export function LaneGitActionsPane({
15461546
onResolveRebaseConflict(laneId, rebaseConflictParentLaneId);
15471547
return;
15481548
}
1549-
const search = new URLSearchParams({ tab: "rebase", laneId });
1549+
const search = new URLSearchParams({ tab: "workflows", workflow: "rebase", laneId });
15501550
if (rebaseConflictParentLaneId) search.set("parentLaneId", rebaseConflictParentLaneId);
15511551
navigate(`/prs?${search.toString()}`);
15521552
};
@@ -1571,8 +1571,8 @@ export function LaneGitActionsPane({
15711571
{autoRebaseStatus.state !== "autoRebased" ? (
15721572
isAutoRebaseFailure ? (
15731573
<SmartTooltip content={{
1574-
label: "Open Rebase Tab",
1575-
description: "View detailed rebase conflict information and resolve issues.",
1574+
label: "Open Rebase/Merge tab",
1575+
description: "View detailed rebase information and resolve issues.",
15761576
effect: "Navigate to the rebase details view",
15771577
}}>
15781578
<button
@@ -1581,7 +1581,7 @@ export function LaneGitActionsPane({
15811581
disabled={!laneId || busyAction != null}
15821582
onClick={openRebaseTab}
15831583
>
1584-
OPEN REBASE TAB
1584+
OPEN REBASE/MERGE TAB
15851585
</button>
15861586
</SmartTooltip>
15871587
) : (

apps/desktop/src/renderer/components/lanes/LaneRebaseBanner.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ export function LaneRebaseBanner({
5050
</div>
5151
</div>
5252
<div className="flex shrink-0 flex-wrap items-center gap-1.5">
53-
<SmartTooltip content={{ label: "View in Rebase tab", description: "Open the Rebase tab for this lane." }}>
53+
<SmartTooltip content={{ label: "View in Rebase/Merge tab", description: "Open the Rebase/Merge tab for this lane." }}>
5454
<button
5555
type="button"
5656
style={primaryButton({ height: 24, padding: "0 8px", fontSize: 10 })}
5757
onClick={() => onViewRebaseDetails(s.laneId)}
5858
>
59-
View in Rebase tab
59+
View in Rebase/Merge tab
6060
</button>
6161
</SmartTooltip>
6262
<SmartTooltip content={{ label: "Dismiss", description: "Remove this rebase suggestion permanently until new parent commits arrive." }}>
@@ -120,13 +120,13 @@ export function LaneRebaseBanner({
120120
</div>
121121
</div>
122122
<div className="shrink-0 flex items-center gap-1.5">
123-
<SmartTooltip content={{ label: "View in Rebase tab", description: "Open the Rebase tab for this lane." }}>
123+
<SmartTooltip content={{ label: "View in Rebase/Merge tab", description: "Open the Rebase/Merge tab for this lane." }}>
124124
<button
125125
type="button"
126126
style={primaryButton({ height: 24, padding: "0 8px", fontSize: 10 })}
127127
onClick={() => onViewRebaseDetails(status.laneId)}
128128
>
129-
View in Rebase tab
129+
View in Rebase/Merge tab
130130
</button>
131131
</SmartTooltip>
132132
<SmartTooltip content={{ label: "Dismiss", description: "Hide this alert until the parent or base changes again." }}>

apps/desktop/src/renderer/components/lanes/LanesPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,7 +1065,7 @@ export function LanesPage() {
10651065
const failedLane = start.run.failedLaneId ? lanesById.get(start.run.failedLaneId)?.name ?? start.run.failedLaneId : null;
10661066
const detail = start.run.error ?? "Rebase failed.";
10671067
setRebaseSuggestionError(`Rebase needs attention${failedLane ? ` for ${failedLane}` : ""}. ${detail}`);
1068-
navigate("/prs?tab=rebase");
1068+
navigate("/prs?tab=workflows&workflow=rebase");
10691069
return;
10701070
}
10711071

@@ -1085,7 +1085,7 @@ export function LanesPage() {
10851085
} catch (err) {
10861086
const message = err instanceof Error ? err.message : String(err);
10871087
setRebaseSuggestionError(message);
1088-
navigate("/prs?tab=rebase");
1088+
navigate("/prs?tab=workflows&workflow=rebase");
10891089
}
10901090
}, [lanesById, navigate, refreshLanes, requestPushSelection, requestRebaseScope]);
10911091

apps/desktop/src/renderer/components/prs/CreatePrModal.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from "react";
2+
import { useNavigate } from "react-router-dom";
23
import * as Dialog from "@radix-ui/react-dialog";
34
import { GitPullRequest, GitMerge, Stack as Layers, CheckCircle, Warning, CircleNotch, X, GitBranch, Sparkle, ArrowRight, ArrowLeft, Check, DotsSixVertical, Trash, ArrowUp, ArrowDown } from "@phosphor-icons/react";
45
import { useAppStore } from "../../state/appStore";
@@ -446,7 +447,7 @@ function LaneWarningPanel({
446447
cursor: "pointer",
447448
}}
448449
>
449-
Open Rebase Tab
450+
Open Rebase/Merge Tab
450451
</button>
451452
<span style={{ fontSize: 10, color: C.textMuted, fontFamily: MONO_FONT }}>
452453
Review rebase status before PR creation{rebaseLaneIds.length > 1 ? ` (${rebaseLaneIds.length} lanes)` : ""}.
@@ -470,6 +471,7 @@ export function CreatePrModal({
470471
onOpenChange: (open: boolean) => void;
471472
onCreated?: (created: PrSummary[]) => void | Promise<void>;
472473
}) {
474+
const navigate = useNavigate();
473475
const lanes = useAppStore((s) => s.lanes);
474476
const primaryLane = React.useMemo(() => lanes.find((l) => l.laneType === "primary") ?? null, [lanes]);
475477

@@ -595,8 +597,9 @@ export function CreatePrModal({
595597

596598
const openRebaseTab = React.useCallback((laneId: string) => {
597599
onOpenChange(false);
598-
window.location.hash = `#/prs?tab=rebase&laneId=${encodeURIComponent(laneId)}`;
599-
}, [onOpenChange]);
600+
const search = new URLSearchParams({ tab: "workflows", workflow: "rebase", laneId });
601+
navigate({ pathname: "/prs", search: `?${search.toString()}` });
602+
}, [navigate, onOpenChange]);
600603

601604
// Reset on close
602605
React.useEffect(() => {

apps/desktop/src/renderer/components/prs/PRsPage.tsx

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { GitHubTab } from "./tabs/GitHubTab";
1111
import { WorkflowsTab, type WorkflowCategory } from "./tabs/WorkflowsTab";
1212
import { SANS_FONT } from "../lanes/laneDesignTokens";
1313
import { isMissionLaneHiddenByDefault } from "../lanes/laneUtils";
14-
import { buildPrsRouteSearch, parsePrsRouteState } from "./prsRouteState";
14+
import { buildPrsRouteSearch, parsePrsRouteState, resolvePrsActiveTab } from "./prsRouteState";
1515
import { resolveRouteRebaseSelection } from "./shared/rebaseNeedUtils";
1616
import type { PrSummary } from "../../../shared/types";
1717

@@ -84,31 +84,21 @@ function PRsPageInner() {
8484
search: location.search,
8585
hash: window.location.hash,
8686
});
87-
const tab = routeState.tab;
88-
const workflowTab = routeState.workflowTab;
87+
const resolved = resolvePrsActiveTab(routeState);
8988
const routeRebaseItemId = resolveRouteRebaseSelection({
9089
rebaseNeeds,
9190
routeItemId: routeState.laneId,
9291
});
9392

94-
if (tab === "github" || tab === "normal") {
95-
setActiveTab("normal");
96-
} else if (tab === "workflows") {
97-
const nextWorkflowTab = workflowTab === "queue" || workflowTab === "integration" || workflowTab === "rebase"
98-
? workflowTab
99-
: "integration";
100-
setActiveTab(nextWorkflowTab);
101-
} else if (tab === "queue" || tab === "integration" || tab === "rebase") {
102-
setActiveTab(tab);
103-
}
93+
setActiveTab(resolved.activeTab);
10494

105-
if (tab === "normal" || tab === "github") {
95+
if (!resolved.isWorkflowRoute && (routeState.tab === "normal" || routeState.tab === "github")) {
10696
setSelectedPrId(routeState.prId ?? null);
10797
}
108-
if (tab === "queue" || workflowTab === "queue") {
98+
if (resolved.effectiveWorkflow === "queue") {
10999
setSelectedQueueGroupId(routeState.queueGroupId ?? null);
110100
}
111-
if (tab === "rebase" || workflowTab === "rebase") {
101+
if (resolved.effectiveWorkflow === "rebase") {
112102
setSelectedRebaseItemId(routeRebaseItemId);
113103
}
114104
} catch {

0 commit comments

Comments
 (0)