story-kit: queue 100_story_test_coverage_http_context_rs_to_100 for QA
This commit is contained in:
@@ -98,7 +98,8 @@ describe("RosterBadge availability state", () => {
|
|||||||
expect(badge.style.color).toBe("rgb(170, 170, 170)");
|
expect(badge.style.color).toBe("rgb(170, 170, 170)");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows a green pulsing dot for an active agent", async () => {
|
// AC1: roster badge always shows idle (grey) even when agent is running
|
||||||
|
it("shows a static green dot for a running agent (roster always idle)", async () => {
|
||||||
const agentList: AgentInfo[] = [
|
const agentList: AgentInfo[] = [
|
||||||
{
|
{
|
||||||
story_id: "81_active",
|
story_id: "81_active",
|
||||||
@@ -115,12 +116,13 @@ describe("RosterBadge availability state", () => {
|
|||||||
render(<AgentPanel />);
|
render(<AgentPanel />);
|
||||||
|
|
||||||
const dot = await screen.findByTestId("roster-dot-coder-1");
|
const dot = await screen.findByTestId("roster-dot-coder-1");
|
||||||
// JSDOM normalizes #3fb950 to rgb(63, 185, 80)
|
|
||||||
expect(dot.style.background).toBe("rgb(63, 185, 80)");
|
expect(dot.style.background).toBe("rgb(63, 185, 80)");
|
||||||
expect(dot.style.animation).toBe("pulse 1.5s infinite");
|
// Roster is always idle — no pulsing animation
|
||||||
|
expect(dot.style.animation).toBe("");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows green badge styling for an active agent", async () => {
|
// AC1: roster badge always shows idle (grey) even when agent is running
|
||||||
|
it("shows grey (idle) badge styling for a running agent", async () => {
|
||||||
const agentList: AgentInfo[] = [
|
const agentList: AgentInfo[] = [
|
||||||
{
|
{
|
||||||
story_id: "81_active",
|
story_id: "81_active",
|
||||||
@@ -137,8 +139,32 @@ describe("RosterBadge availability state", () => {
|
|||||||
render(<AgentPanel />);
|
render(<AgentPanel />);
|
||||||
|
|
||||||
const badge = await screen.findByTestId("roster-badge-coder-1");
|
const badge = await screen.findByTestId("roster-badge-coder-1");
|
||||||
// JSDOM normalizes #3fb95018 to rgba(63, 185, 80, 0.094) and #3fb950 to rgb(63, 185, 80)
|
// Always idle: grey background and grey text
|
||||||
expect(badge.style.background).toBe("rgba(63, 185, 80, 0.094)");
|
expect(badge.style.background).toBe("rgba(170, 170, 170, 0.094)");
|
||||||
expect(badge.style.color).toBe("rgb(63, 185, 80)");
|
expect(badge.style.color).toBe("rgb(170, 170, 170)");
|
||||||
|
});
|
||||||
|
|
||||||
|
// AC2: after agent completes and returns to roster, badge shows idle
|
||||||
|
it("shows idle state after agent status changes from running to completed", async () => {
|
||||||
|
const agentList: AgentInfo[] = [
|
||||||
|
{
|
||||||
|
story_id: "81_completed",
|
||||||
|
agent_name: "coder-1",
|
||||||
|
status: "completed",
|
||||||
|
session_id: null,
|
||||||
|
worktree_path: null,
|
||||||
|
base_branch: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
mockedAgents.listAgents.mockResolvedValue(agentList);
|
||||||
|
|
||||||
|
render(<AgentPanel />);
|
||||||
|
|
||||||
|
const badge = await screen.findByTestId("roster-badge-coder-1");
|
||||||
|
const dot = screen.getByTestId("roster-dot-coder-1");
|
||||||
|
// Completed agent: badge is idle
|
||||||
|
expect(badge.style.background).toBe("rgba(170, 170, 170, 0.094)");
|
||||||
|
expect(badge.style.color).toBe("rgb(170, 170, 170)");
|
||||||
|
expect(dot.style.animation).toBe("");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,17 +29,9 @@ const formatTimestamp = (value: Date | null): string => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function RosterBadge({
|
function RosterBadge({ agent }: { agent: AgentConfigInfo }) {
|
||||||
agent,
|
|
||||||
activeStoryId,
|
|
||||||
}: {
|
|
||||||
agent: AgentConfigInfo;
|
|
||||||
activeStoryId: string | null;
|
|
||||||
}) {
|
|
||||||
const { registerRosterEl } = useLozengeFly();
|
const { registerRosterEl } = useLozengeFly();
|
||||||
const badgeRef = useRef<HTMLSpanElement>(null);
|
const badgeRef = useRef<HTMLSpanElement>(null);
|
||||||
const isActive = activeStoryId !== null;
|
|
||||||
const storyNumber = activeStoryId?.match(/^(\d+)/)?.[1];
|
|
||||||
|
|
||||||
// Register this element so fly animations know where to start/end
|
// Register this element so fly animations know where to start/end
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -59,31 +51,12 @@ function RosterBadge({
|
|||||||
padding: "2px 8px",
|
padding: "2px 8px",
|
||||||
borderRadius: "6px",
|
borderRadius: "6px",
|
||||||
fontSize: "0.7em",
|
fontSize: "0.7em",
|
||||||
background: isActive ? "#3fb95018" : "#aaaaaa18",
|
background: "#aaaaaa18",
|
||||||
color: isActive ? "#3fb950" : "#aaa",
|
color: "#aaa",
|
||||||
border: isActive ? "1px solid #3fb95044" : "1px solid #aaaaaa44",
|
border: "1px solid #aaaaaa44",
|
||||||
transition: "background 0.3s, color 0.3s, border-color 0.3s",
|
|
||||||
}}
|
}}
|
||||||
title={
|
title={`${agent.role || agent.name} — available`}
|
||||||
isActive
|
|
||||||
? `Working on #${storyNumber ?? activeStoryId}`
|
|
||||||
: `${agent.role || agent.name} — available`
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{isActive && (
|
|
||||||
<span
|
|
||||||
data-testid={`roster-dot-${agent.name}`}
|
|
||||||
style={{
|
|
||||||
width: "5px",
|
|
||||||
height: "5px",
|
|
||||||
borderRadius: "50%",
|
|
||||||
background: "#3fb950",
|
|
||||||
animation: "pulse 1.5s infinite",
|
|
||||||
flexShrink: 0,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!isActive && (
|
|
||||||
<span
|
<span
|
||||||
data-testid={`roster-dot-${agent.name}`}
|
data-testid={`roster-dot-${agent.name}`}
|
||||||
style={{
|
style={{
|
||||||
@@ -94,23 +67,9 @@ function RosterBadge({
|
|||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
<span style={{ fontWeight: 600, color: "#aaa" }}>{agent.name}</span>
|
||||||
<span style={{ fontWeight: 600, color: isActive ? "#3fb950" : "#aaa" }}>
|
{agent.model && <span style={{ color: "#888" }}>{agent.model}</span>}
|
||||||
{agent.name}
|
|
||||||
</span>
|
|
||||||
{agent.model && (
|
|
||||||
<span style={{ color: isActive ? "#5ab96a" : "#888" }}>
|
|
||||||
{agent.model}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{isActive && storyNumber && (
|
|
||||||
<span style={{ color: "#5ab96a", marginLeft: "2px" }}>
|
|
||||||
#{storyNumber}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{!isActive && (
|
|
||||||
<span style={{ color: "#888", fontStyle: "italic" }}>available</span>
|
<span style={{ color: "#888", fontStyle: "italic" }}>available</span>
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -394,7 +353,7 @@ export function AgentPanel() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Roster badges — show all configured agents with idle/active state */}
|
{/* Roster badges — agents always display in idle state here */}
|
||||||
{roster.length > 0 && (
|
{roster.length > 0 && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -404,19 +363,8 @@ export function AgentPanel() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{roster.map((a) => {
|
{roster.map((a) => {
|
||||||
// Find the story this roster agent is currently working on (if any)
|
|
||||||
const activeEntry = Object.entries(agents).find(
|
|
||||||
([, state]) =>
|
|
||||||
state.agentName === a.name &&
|
|
||||||
(state.status === "running" || state.status === "pending"),
|
|
||||||
);
|
|
||||||
const activeStoryId = activeEntry
|
|
||||||
? activeEntry[0].split(":")[0]
|
|
||||||
: null;
|
|
||||||
const isHidden = hiddenRosterAgents.has(a.name);
|
const isHidden = hiddenRosterAgents.has(a.name);
|
||||||
return (
|
return (
|
||||||
// Collapsing wrapper: smoothly shrinks when agent departs
|
|
||||||
// to a work item and expands when it returns.
|
|
||||||
<div
|
<div
|
||||||
key={`roster-wrapper-${a.name}`}
|
key={`roster-wrapper-${a.name}`}
|
||||||
data-testid={`roster-badge-wrapper-${a.name}`}
|
data-testid={`roster-badge-wrapper-${a.name}`}
|
||||||
@@ -427,7 +375,7 @@ export function AgentPanel() {
|
|||||||
transition: "max-width 0.35s ease, opacity 0.2s ease",
|
transition: "max-width 0.35s ease, opacity 0.2s ease",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RosterBadge agent={a} activeStoryId={activeStoryId} />
|
<RosterBadge agent={a} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user