Skip to content

Commit 3bd0e2d

Browse files
committed
fix(workboard): preserve created status from stale lifecycle
1 parent 440761c commit 3bd0e2d

4 files changed

Lines changed: 72 additions & 4 deletions

File tree

extensions/workboard/src/store.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,35 @@ describe("WorkboardStore", () => {
392392
);
393393
});
394394

395+
it("keeps creation status from stale lifecycle patches", async () => {
396+
vi.useFakeTimers();
397+
try {
398+
vi.setSystemTime(2000);
399+
const store = new WorkboardStore(createMemoryStore());
400+
const card = await store.create({
401+
title: "Initial running status",
402+
status: "running",
403+
});
404+
405+
const staleLifecycle = await store.update(card.id, {
406+
status: "review",
407+
metadata: { lifecycleStatusSourceUpdatedAt: 1000 },
408+
});
409+
expect(staleLifecycle).toEqual(card);
410+
expect(staleLifecycle.status).toBe("running");
411+
expect(staleLifecycle.metadata?.lifecycleStatusSourceUpdatedAt).toBeUndefined();
412+
413+
const freshLifecycle = await store.update(card.id, {
414+
status: "review",
415+
metadata: { lifecycleStatusSourceUpdatedAt: 3000 },
416+
});
417+
expect(freshLifecycle.status).toBe("review");
418+
expect(freshLifecycle.metadata?.lifecycleStatusSourceUpdatedAt).toBe(3000);
419+
} finally {
420+
vi.useRealTimers();
421+
}
422+
});
423+
395424
it("keeps non-status fields from stale lifecycle patches", async () => {
396425
const store = new WorkboardStore(createMemoryStore());
397426
const card = await store.create({

extensions/workboard/src/store.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,8 +1662,9 @@ function latestStatusTransitionAt(card: WorkboardCard): number | undefined {
16621662
for (let index = (card.events?.length ?? 0) - 1; index >= 0; index -= 1) {
16631663
const event = card.events?.[index];
16641664
if (
1665-
event?.kind === "moved" &&
1666-
event.fromStatus !== event.toStatus &&
1665+
(event?.kind === "moved" || event?.kind === "created") &&
1666+
((event.kind === "created" && card.status !== "todo") ||
1667+
(event.kind === "moved" && event.fromStatus !== event.toStatus)) &&
16671668
event.toStatus === card.status &&
16681669
typeof event.at === "number" &&
16691670
Number.isFinite(event.at)

ui/src/ui/controllers/workboard.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2141,6 +2141,43 @@ describe("workboard controller", () => {
21412141
expect(state.cards.find((card) => card.id === "card-review")?.status).toBe("review");
21422142
});
21432143

2144+
it("does not sync stale linked-session status over a card creation status", async () => {
2145+
const host = {};
2146+
const state = getWorkboardState(host);
2147+
state.loaded = true;
2148+
state.cards = [
2149+
{
2150+
...sampleCard,
2151+
status: "running",
2152+
sessionKey: sampleSession.key,
2153+
createdAt: 2000,
2154+
updatedAt: 2000,
2155+
events: [{ id: "event-created", kind: "created", at: 2000, toStatus: "running" }],
2156+
},
2157+
];
2158+
const client = createClient({
2159+
"workboard.cards.update": {
2160+
card: { ...sampleCard, status: "review", sessionKey: sampleSession.key },
2161+
},
2162+
});
2163+
2164+
await syncWorkboardLifecycle({
2165+
host,
2166+
client: client as never,
2167+
sessions: [
2168+
{
2169+
...sampleSession,
2170+
status: "done",
2171+
hasActiveRun: false,
2172+
updatedAt: 1000,
2173+
},
2174+
],
2175+
});
2176+
2177+
expect(client.request).not.toHaveBeenCalled();
2178+
expect(state.cards[0]?.status).toBe("running");
2179+
});
2180+
21442181
it("does not sync linked card status from sessions without lifecycle provenance", async () => {
21452182
const host = {};
21462183
const state = getWorkboardState(host);

ui/src/ui/controllers/workboard.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,8 +1454,9 @@ function latestStatusTransitionAt(card: WorkboardCard): number | undefined {
14541454
for (let index = (card.events?.length ?? 0) - 1; index >= 0; index -= 1) {
14551455
const event = card.events?.[index];
14561456
if (
1457-
event?.kind === "moved" &&
1458-
event.fromStatus !== event.toStatus &&
1457+
(event?.kind === "moved" || event?.kind === "created") &&
1458+
((event.kind === "created" && card.status !== "todo") ||
1459+
(event.kind === "moved" && event.fromStatus !== event.toStatus)) &&
14591460
event.toStatus === card.status &&
14601461
typeof event.at === "number" &&
14611462
Number.isFinite(event.at)

0 commit comments

Comments
 (0)