diff --git a/src/resources/extensions/gsd/auto-dashboard.ts b/src/resources/extensions/gsd/auto-dashboard.ts index ddeab5256..85f06ca44 100644 --- a/src/resources/extensions/gsd/auto-dashboard.ts +++ b/src/resources/extensions/gsd/auto-dashboard.ts @@ -24,6 +24,18 @@ import { computeProgressScore } from "./progress-score.js"; import { getActiveWorktreeName } from "./worktree-command.js"; import { loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js"; +// ─── UAT Slice Extraction ───────────────────────────────────────────────────── + +/** + * Extract the target slice ID from a run-uat unit ID (e.g. "M001/S01" → "S01"). + * Returns null if the format doesn't match. + */ +export function extractUatSliceId(unitId: string): string | null { + const parts = unitId.split("/"); + if (parts.length >= 2 && parts[1]!.startsWith("S")) return parts[1]!; + return null; +} + // ─── Dashboard Data ─────────────────────────────────────────────────────────── /** Dashboard data for the overlay */ @@ -408,10 +420,17 @@ export function updateProgressWidget( const verb = unitVerb(unitType); const phaseLabel = unitPhaseLabel(unitType); const mid = state.activeMilestone; - const slice = state.activeSlice; - const task = state.activeTask; const isHook = unitType.startsWith("hook/"); + // When run-uat is executing for a just-completed slice (e.g. S01), + // deriveState() has already advanced activeSlice to the next one (S02). + // Override the displayed slice to match the UAT target from the unit ID. + const uatTargetSliceId = unitType === "run-uat" ? extractUatSliceId(unitId) : null; + const slice = uatTargetSliceId + ? { id: uatTargetSliceId, title: state.activeSlice?.title ?? "" } + : state.activeSlice; + const task = state.activeTask; + // Cache git branch at widget creation time (not per render) let cachedBranch: string | null = null; try { cachedBranch = getCurrentBranch(accessors.getBasePath()); } catch { /* not in git repo */ } diff --git a/src/resources/extensions/gsd/tests/auto-dashboard.test.ts b/src/resources/extensions/gsd/tests/auto-dashboard.test.ts index d514420a3..4ca0836f9 100644 --- a/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +++ b/src/resources/extensions/gsd/tests/auto-dashboard.test.ts @@ -8,6 +8,7 @@ import { formatAutoElapsed, formatWidgetTokens, estimateTimeRemaining, + extractUatSliceId, } from "../auto-dashboard.ts"; // ─── unitVerb ───────────────────────────────────────────────────────────── @@ -178,3 +179,17 @@ test("formatAutoElapsed returns empty string for negative autoStartTime", () => assert.equal(formatAutoElapsed(-1), ""); assert.equal(formatAutoElapsed(NaN), ""); }); + +// ─── extractUatSliceId ─────────────────────────────────────────────────── + +test("extractUatSliceId extracts slice ID from M001/S01 format", () => { + assert.equal(extractUatSliceId("M001/S01"), "S01"); + assert.equal(extractUatSliceId("M002/S03"), "S03"); + assert.equal(extractUatSliceId("M001/S12"), "S12"); +}); + +test("extractUatSliceId returns null for invalid formats", () => { + assert.equal(extractUatSliceId("M001"), null); + assert.equal(extractUatSliceId(""), null); + assert.equal(extractUatSliceId("M001/T01"), null); +});