diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts
index bcaa5cf..3fadffb 100644
--- a/frontend/src/api/client.ts
+++ b/frontend/src/api/client.ts
@@ -25,6 +25,7 @@ export interface PipelineStageItem {
story_id: string;
name: string | null;
error: string | null;
+ merge_failure: string | null;
agent: AgentAssignment | null;
}
diff --git a/frontend/src/components/LozengeFlyContext.test.tsx b/frontend/src/components/LozengeFlyContext.test.tsx
index c0db6d2..6f6a06e 100644
--- a/frontend/src/components/LozengeFlyContext.test.tsx
+++ b/frontend/src/components/LozengeFlyContext.test.tsx
@@ -57,6 +57,7 @@ describe("AgentLozenge fixed intrinsic width", () => {
story_id: "74_width_test",
name: "Width Test",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: "sonnet", status: "running" },
},
];
@@ -108,6 +109,7 @@ describe("LozengeFlyProvider fly-in visibility", () => {
story_id: "74_hidden_test",
name: "Hidden Test",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: null, status: "running" },
},
],
@@ -143,6 +145,7 @@ describe("LozengeFlyProvider fly-in visibility", () => {
story_id: "74_no_roster",
name: "No Roster",
error: null,
+ merge_failure: null,
agent: {
agent_name: "unknown-agent",
model: null,
@@ -208,6 +211,7 @@ describe("LozengeFlyProvider fly-in clone", () => {
story_id: "74_portal_test",
name: "Portal Test",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: "sonnet", status: "running" },
},
],
@@ -248,6 +252,7 @@ describe("LozengeFlyProvider fly-in clone", () => {
story_id: "74_clone_remove",
name: "Clone Remove",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: null, status: "running" },
},
],
@@ -294,6 +299,7 @@ describe("LozengeFlyProvider fly-in clone", () => {
story_id: "74_reveal_test",
name: "Reveal Test",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: null, status: "running" },
},
],
@@ -362,6 +368,7 @@ describe("LozengeFlyProvider fly-out", () => {
story_id: "74_fly_out_test",
name: "Fly Out Test",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: "haiku", status: "completed" },
},
],
@@ -386,6 +393,7 @@ describe("LozengeFlyProvider fly-out", () => {
story_id: "74_fly_out_test",
name: "Fly Out Test",
error: null,
+ merge_failure: null,
agent: null,
},
],
@@ -417,6 +425,7 @@ describe("AgentLozenge idle vs active appearance", () => {
story_id: "74_running_color",
name: "Running",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: null, status: "running" },
},
];
@@ -440,6 +449,7 @@ describe("AgentLozenge idle vs active appearance", () => {
story_id: "74_pending_color",
name: "Pending",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: null, status: "pending" },
},
];
@@ -463,6 +473,7 @@ describe("AgentLozenge idle vs active appearance", () => {
story_id: "74_pulse_dot",
name: "Pulse",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: null, status: "running" },
},
];
@@ -513,6 +524,7 @@ describe("hiddenRosterAgents: assigned agents are absent from roster", () => {
story_id: "85_assign_test",
name: "Assign Test",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: null, status: "running" },
},
],
@@ -533,6 +545,7 @@ describe("hiddenRosterAgents: assigned agents are absent from roster", () => {
story_id: "85_no_agent",
name: "No Agent",
error: null,
+ merge_failure: null,
agent: null,
},
],
@@ -554,6 +567,7 @@ describe("hiddenRosterAgents: assigned agents are absent from roster", () => {
story_id: "85_transition_test",
name: "Transition",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: null, status: "running" },
},
],
@@ -613,6 +627,7 @@ describe("hiddenRosterAgents: fly-out keeps agent hidden until clone lands", ()
story_id: "85_flyout_hidden",
name: "Fly-out Hidden",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: null, status: "completed" },
},
],
@@ -623,6 +638,7 @@ describe("hiddenRosterAgents: fly-out keeps agent hidden until clone lands", ()
story_id: "85_flyout_hidden",
name: "Fly-out Hidden",
error: null,
+ merge_failure: null,
agent: null,
},
],
@@ -664,6 +680,7 @@ describe("hiddenRosterAgents: fly-out keeps agent hidden until clone lands", ()
story_id: "85_flyout_reveal",
name: "Fly-out Reveal",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: null, status: "completed" },
},
],
@@ -674,6 +691,7 @@ describe("hiddenRosterAgents: fly-out keeps agent hidden until clone lands", ()
story_id: "85_flyout_reveal",
name: "Fly-out Reveal",
error: null,
+ merge_failure: null,
agent: null,
},
],
@@ -746,6 +764,7 @@ describe("LozengeFlyProvider agent swap (name change)", () => {
story_id: "109_swap_test",
name: "Swap Test",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: "sonnet", status: "running" },
},
],
@@ -756,6 +775,7 @@ describe("LozengeFlyProvider agent swap (name change)", () => {
story_id: "109_swap_test",
name: "Swap Test",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-2", model: "haiku", status: "running" },
},
],
@@ -835,6 +855,7 @@ describe("LozengeFlyProvider fly-out without roster element", () => {
story_id: "109_no_roster_flyout",
name: "No Roster Flyout",
error: null,
+ merge_failure: null,
agent: {
agent_name: "orphan-agent",
model: null,
@@ -849,6 +870,7 @@ describe("LozengeFlyProvider fly-out without roster element", () => {
story_id: "109_no_roster_flyout",
name: "No Roster Flyout",
error: null,
+ merge_failure: null,
agent: null,
},
],
@@ -919,6 +941,7 @@ describe("FlyingLozengeClone initial non-flying render", () => {
story_id: "109_nontransition_test",
name: "Non-transition Test",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: null, status: "running" },
},
],
@@ -993,6 +1016,7 @@ describe("Bug 137: no animation actions lost during rapid pipeline updates", ()
story_id: "137_rapid_swap",
name: "Rapid Swap",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: "sonnet", status: "running" },
},
],
@@ -1003,6 +1027,7 @@ describe("Bug 137: no animation actions lost during rapid pipeline updates", ()
story_id: "137_rapid_swap",
name: "Rapid Swap",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-2", model: "haiku", status: "running" },
},
],
@@ -1068,6 +1093,7 @@ describe("Bug 137: no animation actions lost during rapid pipeline updates", ()
story_id: "137_reveal_last",
name: "Reveal Last",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-1", model: null, status: "running" },
},
],
@@ -1078,6 +1104,7 @@ describe("Bug 137: no animation actions lost during rapid pipeline updates", ()
story_id: "137_reveal_last",
name: "Reveal Last",
error: null,
+ merge_failure: null,
agent: { agent_name: "coder-2", model: null, status: "running" },
},
],
@@ -1162,6 +1189,7 @@ describe("Bug 137: animations remain functional through sustained agent activity
story_id: "137_sustained",
name: "Sustained",
error: null,
+ merge_failure: null,
agent: { agent_name: agentName, model: null, status: "running" },
},
],
diff --git a/frontend/src/components/StagePanel.test.tsx b/frontend/src/components/StagePanel.test.tsx
index 807fa82..3e637ae 100644
--- a/frontend/src/components/StagePanel.test.tsx
+++ b/frontend/src/components/StagePanel.test.tsx
@@ -15,6 +15,7 @@ describe("StagePanel", () => {
story_id: "42_story_no_agent",
name: "No Agent Story",
error: null,
+ merge_failure: null,
agent: null,
},
];
@@ -30,6 +31,7 @@ describe("StagePanel", () => {
story_id: "43_story_with_agent",
name: "Active Story",
error: null,
+ merge_failure: null,
agent: {
agent_name: "coder-1",
model: "sonnet",
@@ -48,6 +50,7 @@ describe("StagePanel", () => {
story_id: "44_story_no_model",
name: "No Model Story",
error: null,
+ merge_failure: null,
agent: {
agent_name: "coder-2",
model: null,
@@ -65,6 +68,7 @@ describe("StagePanel", () => {
story_id: "45_story_pending",
name: "Pending Story",
error: null,
+ merge_failure: null,
agent: {
agent_name: "coder-1",
model: "haiku",
@@ -82,6 +86,7 @@ describe("StagePanel", () => {
story_id: "59_story_current_work_panel",
name: "Current Work Panel",
error: null,
+ merge_failure: null,
agent: null,
},
];
@@ -95,6 +100,7 @@ describe("StagePanel", () => {
story_id: "1_story_bad",
name: null,
error: "Missing front matter",
+ merge_failure: null,
agent: null,
},
];
@@ -108,6 +114,7 @@ describe("StagePanel", () => {
story_id: "10_story_some_feature",
name: "Some Feature",
error: null,
+ merge_failure: null,
agent: null,
},
];
@@ -123,6 +130,7 @@ describe("StagePanel", () => {
story_id: "11_bug_broken_thing",
name: "Broken Thing",
error: null,
+ merge_failure: null,
agent: null,
},
];
@@ -138,6 +146,7 @@ describe("StagePanel", () => {
story_id: "12_spike_investigate_perf",
name: "Investigate Perf",
error: null,
+ merge_failure: null,
agent: null,
},
];
@@ -153,6 +162,7 @@ describe("StagePanel", () => {
story_id: "13_task_do_something",
name: "Do Something",
error: null,
+ merge_failure: null,
agent: null,
},
];
@@ -168,6 +178,7 @@ describe("StagePanel", () => {
story_id: "20_story_uniform_border",
name: "Uniform Border",
error: null,
+ merge_failure: null,
agent: null,
},
];
@@ -186,6 +197,7 @@ describe("StagePanel", () => {
story_id: "21_bug_uniform_border",
name: "Uniform Border Bug",
error: null,
+ merge_failure: null,
agent: null,
},
];
@@ -201,6 +213,7 @@ describe("StagePanel", () => {
story_id: "22_spike_uniform_border",
name: "Uniform Border Spike",
error: null,
+ merge_failure: null,
agent: null,
},
];
@@ -216,6 +229,7 @@ describe("StagePanel", () => {
story_id: "23_task_uniform_border",
name: "Uniform Border Task",
error: null,
+ merge_failure: null,
agent: null,
},
];
@@ -224,4 +238,42 @@ describe("StagePanel", () => {
expect(card.style.borderLeft).not.toContain("3px");
expect(card.style.borderLeft).toBe(card.style.borderTop);
});
+
+ it("shows merge failure icon and reason when merge_failure is set", () => {
+ const items: PipelineStageItem[] = [
+ {
+ story_id: "30_story_merge_failed",
+ name: "Failed Merge Story",
+ error: null,
+ merge_failure: "Squash merge failed: conflicts in Cargo.lock",
+ agent: null,
+ },
+ ];
+ render(