| ... | @@ -20,8 +20,10 @@ describe("hasUnseenCompletion", () => { | ... | @@ -20,8 +20,10 @@ describe("hasUnseenCompletion", () => { |
| 20 | it("returns true when a thread completed after its last visit", () => { | 20 | it("returns true when a thread completed after its last visit", () => { |
| 21 | expect( | 21 | expect( |
| 22 | hasUnseenCompletion({ | 22 | hasUnseenCompletion({ |
| | 23 | interactionMode: "default", |
| 23 | latestTurn: makeLatestTurn(), | 24 | latestTurn: makeLatestTurn(), |
| 24 | lastVisitedAt: "2026-03-09T10:04:00.000Z", | 25 | lastVisitedAt: "2026-03-09T10:04:00.000Z", |
| | 26 | proposedPlans: [], |
| 25 | session: null, | 27 | session: null, |
| 26 | }), | 28 | }), |
| 27 | ).toBe(true); | 29 | ).toBe(true); |
| ... | @@ -30,8 +32,10 @@ describe("hasUnseenCompletion", () => { | ... | @@ -30,8 +32,10 @@ describe("hasUnseenCompletion", () => { |
| 30 | | 32 | |
| 31 | describe("resolveThreadStatusPill", () => { | 33 | describe("resolveThreadStatusPill", () => { |
| 32 | const baseThread = { | 34 | const baseThread = { |
| | 35 | interactionMode: "plan" as const, |
| 33 | latestTurn: null, | 36 | latestTurn: null, |
| 34 | lastVisitedAt: undefined, | 37 | lastVisitedAt: undefined, |
| | 38 | proposedPlans: [], |
| 35 | session: { | 39 | session: { |
| 36 | provider: "codex" as const, | 40 | provider: "codex" as const, |
| 37 | status: "running" as const, | 41 | status: "running" as const, |
| ... | @@ -71,11 +75,39 @@ describe("resolveThreadStatusPill", () => { | ... | @@ -71,11 +75,39 @@ describe("resolveThreadStatusPill", () => { |
| 71 | ).toMatchObject({ label: "Working", pulse: true }); | 75 | ).toMatchObject({ label: "Working", pulse: true }); |
| 72 | }); | 76 | }); |
| 73 | | 77 | |
| | 78 | it("shows plan ready when a settled plan turn has a proposed plan ready for follow-up", () => { |
| | 79 | expect( |
| | 80 | resolveThreadStatusPill({ |
| | 81 | thread: { |
| | 82 | ...baseThread, |
| | 83 | latestTurn: makeLatestTurn(), |
| | 84 | proposedPlans: [ |
| | 85 | { |
| | 86 | id: "plan-1" as never, |
| | 87 | turnId: "turn-1" as never, |
| | 88 | createdAt: "2026-03-09T10:00:00.000Z", |
| | 89 | updatedAt: "2026-03-09T10:05:00.000Z", |
| | 90 | planMarkdown: "# Plan", |
| | 91 | }, |
| | 92 | ], |
| | 93 | session: { |
| | 94 | ...baseThread.session, |
| | 95 | status: "ready", |
| | 96 | orchestrationStatus: "ready", |
| | 97 | }, |
| | 98 | }, |
| | 99 | hasPendingApprovals: false, |
| | 100 | hasPendingUserInput: false, |
| | 101 | }), |
| | 102 | ).toMatchObject({ label: "Plan Ready", pulse: false }); |
| | 103 | }); |
| | 104 | |
| 74 | it("shows completed when there is an unseen completion and no active blocker", () => { | 105 | it("shows completed when there is an unseen completion and no active blocker", () => { |
| 75 | expect( | 106 | expect( |
| 76 | resolveThreadStatusPill({ | 107 | resolveThreadStatusPill({ |
| 77 | thread: { | 108 | thread: { |
| 78 | ...baseThread, | 109 | ...baseThread, |
| | 110 | interactionMode: "default", |
| 79 | latestTurn: makeLatestTurn(), | 111 | latestTurn: makeLatestTurn(), |
| 80 | lastVisitedAt: "2026-03-09T10:04:00.000Z", | 112 | lastVisitedAt: "2026-03-09T10:04:00.000Z", |
| 81 | session: { | 113 | session: { |