fix: separate target health from replay playback

This commit is contained in:
lda
2026-07-12 15:06:25 +07:00 Verified
parent 35ee86bf9d
commit 8926cb9aef
6 changed files with 54 additions and 25 deletions
@@ -282,10 +282,11 @@ describe("PresentationRoute", () => {
it("uses stored target for live presentation mode", async () => { it("uses stored target for live presentation mode", async () => {
window.sessionStorage.setItem("lda.workflowConsole.target", "http://127.0.0.1:8765/rpc"); window.sessionStorage.setItem("lda.workflowConsole.target", "http://127.0.0.1:8765/rpc");
window.location.hash = "#scene/run-from-deployment/operation";
const { PresentationRoute } = await import("./PresentationRoute.js"); const { PresentationRoute } = await import("./PresentationRoute.js");
render(<PresentationRoute />); render(<PresentationRoute />);
expect(await screen.findByRole("button", { name: /run prepared workflow/i })).toBeInTheDocument(); expect(await screen.findAllByRole("button", { name: /run prepared workflow/i })).toHaveLength(2);
}); });
it("exposes an explicit live launch on the Scene 10 operation beat", async () => { it("exposes an explicit live launch on the Scene 10 operation beat", async () => {
@@ -294,7 +295,9 @@ describe("PresentationRoute", () => {
const { PresentationRoute } = await import("./PresentationRoute.js"); const { PresentationRoute } = await import("./PresentationRoute.js");
render(<PresentationRoute />); render(<PresentationRoute />);
const launch = await screen.findByRole("button", { name: "Run prepared workflow" }); const launches = await screen.findAllByRole("button", { name: "Run prepared workflow" });
const launch = launches.at(-1);
if (!launch) throw new Error("Expected a live workflow launch control");
await userEvent.click(launch); await userEvent.click(launch);
await waitFor(() => { await waitFor(() => {
@@ -309,7 +312,7 @@ describe("PresentationRoute", () => {
const { PresentationRoute } = await import("./PresentationRoute.js"); const { PresentationRoute } = await import("./PresentationRoute.js");
render(<PresentationRoute />); render(<PresentationRoute />);
expect(await screen.findByText(/Replay evidence is active/i)).toBeInTheDocument(); expect(await screen.findByText(/Live target is ready/i)).toBeInTheDocument();
expect(await screen.findByText("Workflow input")).toBeInTheDocument(); expect(await screen.findByText("Workflow input")).toBeInTheDocument();
expect(await screen.findByRole("button", { name: "Submit" })).toBeInTheDocument(); expect(await screen.findByRole("button", { name: "Submit" })).toBeInTheDocument();
}); });
@@ -331,6 +334,7 @@ describe("PresentationRoute", () => {
it("updates the chat intro after the live health probe succeeds", async () => { it("updates the chat intro after the live health probe succeeds", async () => {
window.sessionStorage.setItem("lda.workflowConsole.target", "http://127.0.0.1:8765/rpc"); window.sessionStorage.setItem("lda.workflowConsole.target", "http://127.0.0.1:8765/rpc");
window.location.hash = "#scene/run-from-deployment/operation";
const { PresentationRoute } = await import("./PresentationRoute.js"); const { PresentationRoute } = await import("./PresentationRoute.js");
render(<PresentationRoute />); render(<PresentationRoute />);
@@ -347,10 +351,20 @@ describe("PresentationRoute", () => {
const { PresentationRoute } = await import("./PresentationRoute.js"); const { PresentationRoute } = await import("./PresentationRoute.js");
render(<PresentationRoute />); render(<PresentationRoute />);
expect(await screen.findByText(/Replay evidence is active/i)).toBeInTheDocument(); expect(await screen.findByText(/Live target is ready/i)).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Send" })).toBeEnabled(); expect(screen.getByRole("button", { name: "Send" })).toBeEnabled();
expect(screen.queryByRole("button", { name: "Run prepared workflow" })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Run prepared workflow" })).not.toBeInTheDocument();
expect(mockedCallOperation).not.toHaveBeenCalled(); expect(mockedCallOperation).toHaveBeenCalledWith("workflow.health", "http://127.0.0.1:8765/rpc", {});
});
it("does not probe a configured target on the title route", async () => {
window.sessionStorage.setItem("lda.workflowConsole.target", "http://127.0.0.1:8765/rpc");
mockedCallOperation.mockClear();
const { PresentationRoute } = await import("./PresentationRoute.js");
render(<PresentationRoute />);
expect(screen.getByRole("heading", { name: /Design and Implementation of lda\.chat/i })).toBeInTheDocument();
expect(mockedCallOperation).not.toHaveBeenCalledWith("workflow.health", expect.anything(), expect.anything());
}); });
it("opens Scene 10 approval from the canonical hash", async () => { it("opens Scene 10 approval from the canonical hash", async () => {
@@ -8,6 +8,7 @@ import { useDemoTimeline } from "../demo/useDemoTimeline.js";
import { PresentationCanvas } from "./PresentationCanvas.js"; import { PresentationCanvas } from "./PresentationCanvas.js";
import { PresentationStage } from "./PresentationStage.js"; import { PresentationStage } from "./PresentationStage.js";
import { requirementForDemoBeat } from "./demo-beat-requirements.js"; import { requirementForDemoBeat } from "./demo-beat-requirements.js";
import { isDemoChromeScene } from "./presentation-demo-chrome.js";
import { import {
createInitialPresentationState, createInitialPresentationState,
presentationReducer, presentationReducer,
@@ -53,8 +54,13 @@ export const PresentationRoute = () => {
const presentationTarget = useMemo(() => resolvePresentationTarget(), []); const presentationTarget = useMemo(() => resolvePresentationTarget(), []);
const demo = useDemoTimeline(presentationTarget.target, recordEvidence, recording); const demo = useDemoTimeline(presentationTarget.target, recordEvidence, recording);
const isScene8 = state.location.kind === "main" && state.location.sceneId === "agent-handoff"; const probeEnabled = state.location.kind === "main"
const targetStatusController = usePresentationTargetStatus(presentationTarget, demo.state, !isScene8); && isDemoChromeScene(state.location.sceneId);
const targetStatusController = usePresentationTargetStatus(
presentationTarget,
demo.state,
probeEnabled,
);
const targetStatus = targetStatusController.status; const targetStatus = targetStatusController.status;
const timelineAgent = useTimelineAgent(demo, { const timelineAgent = useTimelineAgent(demo, {
mode: presentationTarget.mode === "live" ? "live" : "replay", mode: presentationTarget.mode === "live" ? "live" : "replay",
@@ -18,22 +18,34 @@ describe("presentationTargetHealth", () => {
target: "http://127.0.0.1:8765/rpc", target: "http://127.0.0.1:8765/rpc",
probe: "ready", probe: "ready",
liveActive: false, liveActive: false,
replayActive: false,
})).toMatchObject({ })).toMatchObject({
kind: "ready", kind: "ready",
label: "Live target ready", label: "Live target ready",
detail: "127.0.0.1:8765",
}); });
}); });
it("labels a reviewed replay selected alongside a healthy target", () => { it("keeps a healthy target ready while replay is active", () => {
expect(presentationTargetHealth({ expect(presentationTargetHealth({
target: "http://127.0.0.1:8765/rpc", target: "http://127.0.0.1:8765/rpc",
probe: "ready", probe: "ready",
liveActive: false, liveActive: false,
replayActive: true, })).toMatchObject({
kind: "ready",
label: "Live target ready",
detail: "127.0.0.1:8765",
});
});
it("uses reviewed recording fallback when no target is configured", () => {
expect(presentationTargetHealth({
target: null,
probe: "none",
liveActive: false,
})).toMatchObject({ })).toMatchObject({
kind: "replay", kind: "replay",
label: "Replay evidence", label: "Replay evidence",
detail: "reviewed recording",
}); });
}); });
@@ -20,13 +20,11 @@ export const presentationTargetHealth = ({
target, target,
probe, probe,
liveActive, liveActive,
replayActive = false,
failureReason, failureReason,
}: { }: {
readonly target: string | null; readonly target: string | null;
readonly probe: TargetProbeState; readonly probe: TargetProbeState;
readonly liveActive: boolean; readonly liveActive: boolean;
readonly replayActive?: boolean;
readonly failureReason?: string | undefined; readonly failureReason?: string | undefined;
}): PresentationTargetHealth => { }): PresentationTargetHealth => {
if (!target) { if (!target) {
@@ -37,14 +35,6 @@ export const presentationTargetHealth = ({
}; };
} }
if (replayActive && probe === "ready") {
return {
kind: "replay",
label: "Replay evidence",
detail: "reviewed recording",
};
}
if (liveActive && probe === "ready") { if (liveActive && probe === "ready") {
return { return {
kind: "active", kind: "active",
@@ -95,7 +95,17 @@ describe("usePresentationTargetStatus", () => {
); );
await waitFor(() => expect(result.current.liveTargetReady).toBe(true)); await waitFor(() => expect(result.current.liveTargetReady).toBe(true));
expect(result.current.status.kind).toBe("replay"); expect(result.current.status.kind).toBe("ready");
expect(result.current.status.label).toBe("Live target ready");
});
it("does not probe when health probing is disabled", async () => {
const { result } = renderHook(() =>
usePresentationTargetStatus(target, replayState, false),
);
await waitFor(() => expect(result.current.status.kind).toBe("replay"));
expect(mockedCallOperation).not.toHaveBeenCalled();
}); });
it("retries health without changing replay playback", async () => { it("retries health without changing replay playback", async () => {
@@ -37,7 +37,7 @@ export const usePresentationTargetStatus = (
} }
if (!probeEnabled) { if (!probeEnabled) {
setProbe("none"); setProbe("none");
setFailureReason("deterministic Scene 8 replay"); setFailureReason(undefined);
return; return;
} }
@@ -70,14 +70,11 @@ export const usePresentationTargetStatus = (
target: null, target: null,
probe: "none", probe: "none",
liveActive: false, liveActive: false,
replayActive: true,
failureReason: "deterministic Scene 8 replay",
}) })
: presentationTargetHealth({ : presentationTargetHealth({
target: targetState.mode === "live" ? targetState.target : null, target: targetState.mode === "live" ? targetState.target : null,
probe, probe,
liveActive: liveActive(demoState), liveActive: liveActive(demoState),
replayActive: demoState.mode === "replay",
failureReason, failureReason,
}); });