feat: show prepared workflow authoring lifecycle

This commit is contained in:
lda
2026-07-11 05:25:35 +07:00 Verified
parent f33dc88b59
commit baccbbb5f0
11 changed files with 282 additions and 309 deletions
@@ -1,65 +0,0 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { loadCanonicalDemoRecording } from "../demo/timeline/replay.js";
import { initialDemoTimelineState } from "../demo/timeline/reducer.js";
import type { DemoTimelineController } from "../demo/useDemoTimeline.js";
import { DemoLifecycleScene } from "./DemoLifecycleScene.js";
import { findBeat, findScene } from "./storyboard.js";
const recording = loadCanonicalDemoRecording();
const demo: DemoTimelineController = {
state: {
...initialDemoTimelineState,
mode: "replay",
phase: "paused",
events: recording.events,
appliedCount: recording.events.length,
autoplay: false,
},
inFlight: false,
interruptPayload: null,
output: null,
trace: null,
missingDeploymentMessage: null,
recordingId: recording.recordingId,
canStart: true,
setMode: vi.fn(),
start: vi.fn(),
pause: vi.fn(),
play: vi.fn(),
next: vi.fn(async () => {}),
submitSelectedIssues: vi.fn(async () => {}),
cancelReview: vi.fn(async () => {}),
restart: vi.fn(),
primeReplayToStage: vi.fn(),
};
const renderBeat = (beatId: "draft" | "artifact" | "deployment" | "ready-run") => {
const scene = findScene("prepared-lifecycle");
const beat = findBeat("prepared-lifecycle", beatId);
if (!scene || !beat) throw new Error(`missing prepared-lifecycle/${beatId}`);
render(<DemoLifecycleScene scene={scene} beat={beat} demo={demo} />);
};
describe("DemoLifecycleScene", () => {
it("renders prepared draft context honestly", () => {
renderBeat("draft");
expect(screen.getByRole("region", { name: "prepared workflow lifecycle" })).toHaveAttribute("data-active-lifecycle", "draft");
expect(screen.getByText("prepared context")).toBeInTheDocument();
expect(screen.getByText("examples/lda_report_workflow")).toBeInTheDocument();
});
it("renders artifact and deployment facts from replay evidence", () => {
renderBeat("deployment");
expect(screen.getByText("lda_report_case_study.default")).toBeInTheDocument();
expect(screen.getByText("block")).toBeInTheDocument();
expect(screen.getByText(/local\.lda_docs/)).toBeInTheDocument();
});
it("renders run readiness without claiming output exists yet", () => {
renderBeat("ready-run");
expect(screen.getByText("run_recorded_lda_report")).toBeInTheDocument();
expect(screen.getByText("interrupted")).toBeInTheDocument();
expect(screen.queryByText(/created issues/i)).not.toBeInTheDocument();
});
});
@@ -1,81 +0,0 @@
import type { DemoTimelineController } from "../demo/useDemoTimeline.js";
import { projectDemoLifecycleFacts } from "./demo-lifecycle-facts.js";
import { StageCaption } from "./StageCaption.js";
import type { SceneBeatDefinition, SceneDefinition } from "./storyboard.js";
type DemoLifecycleSceneProps = {
readonly scene: SceneDefinition;
readonly beat: SceneBeatDefinition;
readonly demo: DemoTimelineController;
};
const stages = [
{ id: "draft", label: "Draft", description: "Prepared authoring context" },
{ id: "artifact", label: "Artifact", description: "Immutable versioned workflow" },
{ id: "deployment", label: "Deployment", description: "Configured source bindings" },
{ id: "ready-run", label: "Run-ready", description: "Persisted run can start" },
] as const;
export const DemoLifecycleScene = ({ scene, beat, demo }: DemoLifecycleSceneProps) => {
const facts = projectDemoLifecycleFacts(demo);
return (
<>
<StageCaption eyebrow="Product lifecycle" title={scene.title}>
<p>{beat.caption}</p>
</StageCaption>
<section
className="demo-lifecycle-scene"
aria-label="prepared workflow lifecycle"
data-active-lifecycle={beat.id}
>
<ol className="demo-lifecycle-scene__rail">
{stages.map((stage) => (
<li key={stage.id} data-active={stage.id === beat.id ? "true" : "false"}>
<strong>{stage.label}</strong>
<span>{stage.description}</span>
</li>
))}
</ol>
<article className="demo-lifecycle-scene__proof">
<h3>{stages.find((stage) => stage.id === beat.id)?.label ?? "Lifecycle"}</h3>
{beat.id === "draft" && (
<dl>
<dt>Workflow</dt><dd>{facts.draft.label}</dd>
<dt>Status</dt><dd>{facts.draft.status}</dd>
<dt>Source</dt><dd>{facts.draft.source}</dd>
</dl>
)}
{beat.id === "artifact" && (
<dl>
<dt>Artifact</dt><dd>{facts.artifact.id}</dd>
<dt>Version</dt><dd>{facts.artifact.version ?? "unavailable"}</dd>
</dl>
)}
{beat.id === "deployment" && (
<dl>
<dt>Deployment</dt><dd>{facts.deployment.id}</dd>
<dt>Drift policy</dt><dd>{facts.deployment.driftPolicy}</dd>
<dt>Bindings</dt>
<dd>
<ul>
{facts.deployment.bindings.map(([from, to]) => (
<li key={`${from}:${to}`}>{from} -&gt; {to}</li>
))}
</ul>
</dd>
</dl>
)}
{beat.id === "ready-run" && (
<dl>
<dt>Run</dt><dd>{facts.run.id ?? "not started"}</dd>
<dt>Status</dt><dd>{facts.run.status}</dd>
<dt>Deployment</dt><dd>{facts.deployment.id}</dd>
</dl>
)}
</article>
</section>
</>
);
};
@@ -420,6 +420,18 @@ describe("SceneBody", () => {
expect(openDiscussion).toHaveBeenCalledWith("where-is-ai-agent");
});
it("renders Scene 9 discover beat with prepared authoring phases", () => {
renderSceneBodyAtMainLocation("prepared-lifecycle", "discover");
expect(screen.getByLabelText("authoring phase rail")).toBeInTheDocument();
expect(screen.getByText("Discover")).toBeInTheDocument();
});
it("renders Scene 9 artifact beat with compiled artifact evidence", () => {
renderSceneBodyAtMainLocation("prepared-lifecycle", "artifact");
expect(screen.getByText("Artifact")).toBeInTheDocument();
expect(screen.getByText(/art_x9y8z7/i)).toBeInTheDocument();
});
it("renders evidence before discussion links so the chip lane cannot cover evidence text", () => {
const location: PresentationLocation = { kind: "main", sceneId: "positioning", beatId: "landscape", focusPath: [] };
const { container } = render(
@@ -8,7 +8,7 @@ import {
type SceneDefinition,
type SceneBeatDefinition,
} from "./storyboard.js";
import { DemoLifecycleScene } from "./DemoLifecycleScene.js";
import { PreparedAuthoringLifecycleScene } from "./authoring/PreparedAuthoringLifecycleScene.js";
import { DemoWorkflowScene } from "./DemoWorkflowScene.js";
import { StageCaption } from "./StageCaption.js";
import { ConclusionScene } from "./conclusion/ConclusionScene.js";
@@ -311,7 +311,7 @@ export const SceneBody = ({ location, demo, selectedNodeId, selectNode, openEvid
case "agent":
return <AgentHandoffScene scene={scene} beat={beat} />;
case "demo-lifecycle":
return <DemoLifecycleScene scene={scene} beat={beat} demo={demo} />;
return <PreparedAuthoringLifecycleScene scene={scene} beat={beat} />;
case "demo":
return (
<DemoWorkflowScene
@@ -0,0 +1,78 @@
import { cleanup, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
import { findBeat, findScene } from "../storyboard.js";
import { PreparedAuthoringLifecycleScene } from "./PreparedAuthoringLifecycleScene.js";
afterEach(() => cleanup());
const renderBeat = (beatId: string) => {
const scene = findScene("prepared-lifecycle");
const beat = findBeat("prepared-lifecycle", beatId);
if (!scene || !beat) throw new Error(`missing prepared-lifecycle/${beatId}`);
return render(<PreparedAuthoringLifecycleScene scene={scene} beat={beat} />);
};
describe("PreparedAuthoringLifecycleScene", () => {
it("discover shows sources, capabilities, and schema", () => {
renderBeat("discover");
expect(screen.getByText("Discover")).toBeInTheDocument();
expect(screen.getAllByText(/sources|capabilities|schema/i).length).toBeGreaterThanOrEqual(1);
});
it("draft shows graph or routes", () => {
renderBeat("draft");
expect(screen.getByText("Draft")).toBeInTheDocument();
expect(screen.getAllByText(/graph|routes/i).length).toBeGreaterThanOrEqual(1);
});
it("validate shows diagnosis and repair", () => {
renderBeat("validate");
expect(screen.getByText("Validate")).toBeInTheDocument();
expect(screen.getAllByText(/diagnos|repair/i).length).toBeGreaterThanOrEqual(1);
});
it("artifact shows immutable ID and version", () => {
renderBeat("artifact");
expect(screen.getByText("Artifact")).toBeInTheDocument();
expect(screen.getByText(/art_x9y8z7/i)).toBeInTheDocument();
});
it("deployment shows bindings and validation", () => {
renderBeat("deployment");
expect(screen.getByText("Deployment")).toBeInTheDocument();
expect(screen.getByText(/dep_m4n5p6/i)).toBeInTheDocument();
});
it("renders an Agent trace trigger", () => {
renderBeat("discover");
expect(screen.getByRole("button", { name: "Agent trace" })).toBeInTheDocument();
});
it("trace is initially closed", () => {
renderBeat("discover");
expect(screen.queryByRole("dialog", { name: "Authoring trace" })).not.toBeInTheDocument();
});
it("opens the trace panel when Agent trace is clicked", async () => {
const user = userEvent.setup();
renderBeat("draft");
await user.click(screen.getByRole("button", { name: "Agent trace" }));
expect(screen.getByRole("dialog", { name: "Authoring trace" })).toBeInTheDocument();
});
it("renders a compact orientation rail", () => {
renderBeat("artifact");
const rail = screen.getByLabelText("authoring phase rail");
expect(rail).toBeInTheDocument();
expect(rail.children.length).toBeGreaterThanOrEqual(5);
});
it("highlights the active phase in the rail", () => {
renderBeat("deployment");
const rail = screen.getByLabelText("authoring phase rail");
const active = rail.querySelector("[data-active='true']");
expect(active).toBeInTheDocument();
expect(active).toHaveTextContent("Deployment");
});
});
@@ -0,0 +1,80 @@
import { useState } from "react";
import { projectPreparedAuthoringPhase } from "./authoring-projection.js";
import { AuthoringTracePanel } from "./AuthoringTracePanel.js";
import type { AuthoringPhaseId } from "./authoring-recording.js";
import type { SceneBeatDefinition, SceneDefinition } from "../storyboard.js";
import { StageCaption } from "../StageCaption.js";
type PreparedAuthoringLifecycleSceneProps = {
readonly scene: SceneDefinition;
readonly beat: SceneBeatDefinition;
};
const phases: readonly { readonly id: AuthoringPhaseId; readonly label: string }[] = [
{ id: "discover", label: "Discover" },
{ id: "draft", label: "Draft" },
{ id: "validate", label: "Validate" },
{ id: "artifact", label: "Artifact" },
{ id: "deployment", label: "Deployment" },
];
/**
* Scene 9 — Prepared workflow authoring lifecycle.
*
* Each beat shows a compact orientation rail and one dominant phase projection
* sourced from the prepared authoring recording. A persistent "Agent trace"
* trigger opens the AuthoringTracePanel overlay.
*
* The receipt bridges Scene 8's full conversation and Scene 9's separate
* trace panel without morphing runtime-owned components.
*/
export const PreparedAuthoringLifecycleScene = ({ scene, beat }: PreparedAuthoringLifecycleSceneProps) => {
const beatId = beat.id as AuthoringPhaseId;
const [traceOpen, setTraceOpen] = useState(false);
const projection = projectPreparedAuthoringPhase(beatId);
return (
<>
<StageCaption eyebrow="Prepared workflow" title={scene.title}>
<p>{beat.caption}</p>
</StageCaption>
<section
className="prepared-lifecycle-scene"
aria-label="prepared workflow authoring lifecycle"
data-active-phase={beatId}
>
<ol className="prepared-lifecycle-scene__rail" aria-label="authoring phase rail">
{phases.map((phase) => (
<li key={phase.id} data-active={phase.id === beatId ? "true" : "false"}>
<strong>{phase.label}</strong>
</li>
))}
</ol>
<article className="prepared-lifecycle-scene__projection">
<div className="prepared-lifecycle-scene__summary">
<p>{projection.summary}</p>
</div>
<div className="prepared-lifecycle-scene__commands">
{projection.commands.map((cmd, i) => (
<div key={i} className="prepared-lifecycle-scene__command">
<code>$ {cmd.command}</code>
<span className="prepared-lifecycle-scene__command-result" data-result={cmd.result}>
{cmd.result === "success" ? "✓" : "⚡"} {cmd.result}
</span>
</div>
))}
</div>
</article>
</section>
<div className="prepared-lifecycle-scene__receipt">
<AuthoringTracePanel
phase={beatId}
open={traceOpen}
onOpen={() => setTraceOpen(true)}
onClose={() => setTraceOpen(false)}
/>
</div>
</>
);
};
@@ -1,70 +0,0 @@
import { describe, expect, it, vi } from "vitest";
import { loadCanonicalDemoRecording } from "../demo/timeline/replay.js";
import { initialDemoTimelineState } from "../demo/timeline/reducer.js";
import type { DemoTimelineController } from "../demo/useDemoTimeline.js";
import { projectDemoLifecycleFacts } from "./demo-lifecycle-facts.js";
const controller = (eventMode: "recorded" | "empty" = "recorded"): DemoTimelineController => {
const recording = loadCanonicalDemoRecording();
return {
state: {
...initialDemoTimelineState,
mode: "replay",
phase: "paused",
events: eventMode === "recorded" ? recording.events : [],
appliedCount: eventMode === "recorded" ? recording.events.length : 0,
autoplay: false,
},
inFlight: false,
interruptPayload: null,
output: null,
trace: null,
missingDeploymentMessage: null,
recordingId: recording.recordingId,
canStart: true,
setMode: vi.fn(),
start: vi.fn(),
pause: vi.fn(),
play: vi.fn(),
next: vi.fn(async () => {}),
submitSelectedIssues: vi.fn(async () => {}),
cancelReview: vi.fn(async () => {}),
restart: vi.fn(),
primeReplayToStage: vi.fn(),
};
};
describe("projectDemoLifecycleFacts", () => {
it("projects prepared draft context without pretending it was runtime evidence", () => {
const facts = projectDemoLifecycleFacts(controller());
expect(facts.draft.label).toBe("lda report workflow");
expect(facts.draft.source).toContain("examples/lda_report_workflow");
expect(facts.draft.status).toBe("prepared context");
});
it("projects artifact and deployment facts from deployment inspect evidence", () => {
const facts = projectDemoLifecycleFacts(controller());
expect(facts.artifact).toEqual({ id: "lda_report_case_study", version: 1 });
expect(facts.deployment.id).toBe("lda_report_case_study.default");
expect(facts.deployment.driftPolicy).toBe("block");
expect(facts.deployment.bindings).toContainEqual(["local.lda_docs", "local.lda_docs"]);
});
it("projects run readiness from the run start event", () => {
const facts = projectDemoLifecycleFacts(controller());
expect(facts.run.id).toBe("run_recorded_lda_report");
expect(facts.run.status).toBe("interrupted");
});
it("falls back honestly when replay evidence has not loaded yet", () => {
const facts = projectDemoLifecycleFacts(controller("empty"));
expect(facts.artifact).toEqual({ id: "unavailable", version: null });
expect(facts.deployment).toEqual({
id: "unavailable",
driftPolicy: "unavailable",
bindings: [],
});
expect(facts.run).toEqual({ id: null, status: "not started" });
});
});
@@ -1,83 +0,0 @@
import type { DemoTimelineController } from "../demo/useDemoTimeline.js";
import type { DemoEvent } from "../demo/timeline/models.js";
export type DemoLifecycleFacts = {
readonly draft: {
readonly label: string;
readonly source: string;
readonly status: string;
};
readonly artifact: {
readonly id: string;
readonly version: number | null;
};
readonly deployment: {
readonly id: string;
readonly driftPolicy: string;
readonly bindings: ReadonlyArray<readonly [string, string]>;
};
readonly run: {
readonly id: string | null;
readonly status: string;
};
};
const deploymentInspect = (demo: DemoTimelineController): DemoEvent | undefined =>
demo.state.events.find((event) => event.stage === "deployment_check");
const runStart = (demo: DemoTimelineController): DemoEvent | undefined =>
demo.state.events.find((event) => event.stage === "run_start");
const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === "object" && value !== null && !Array.isArray(value);
const stringField = (record: Record<string, unknown> | undefined, field: string): string | undefined => {
const value = record?.[field];
return typeof value === "string" ? value : undefined;
};
const numberField = (record: Record<string, unknown> | undefined, field: string): number | undefined => {
const value = record?.[field];
return typeof value === "number" ? value : undefined;
};
const readBindings = (value: unknown): ReadonlyArray<readonly [string, string]> => {
if (!Array.isArray(value)) return [];
return value.flatMap((entry) => {
if (!Array.isArray(entry) || entry.length !== 2) return [];
const [from, to] = entry;
return typeof from === "string" && typeof to === "string" ? [[from, to] as const] : [];
});
};
/**
* Presentation-only lifecycle facts. Draft context is prepared example context;
* artifact/deployment/run facts come from replay evidence when available.
*/
export const projectDemoLifecycleFacts = (demo: DemoTimelineController): DemoLifecycleFacts => {
const deployment = deploymentInspect(demo);
const deploymentInterpreted = isRecord(deployment?.interpreted) ? deployment.interpreted : undefined;
const run = runStart(demo);
const runInterpreted = isRecord(run?.interpreted) ? run.interpreted : undefined;
return {
draft: {
label: "lda report workflow",
source: "examples/lda_report_workflow",
status: "prepared context",
},
artifact: {
id: stringField(deploymentInterpreted, "artifactId") ?? "unavailable",
version: numberField(deploymentInterpreted, "artifactVersion") ?? null,
},
deployment: {
id: stringField(deploymentInterpreted, "id") ?? "unavailable",
driftPolicy: stringField(deploymentInterpreted, "driftPolicy") ?? "unavailable",
bindings: readBindings(deploymentInterpreted?.["bindings"]),
},
run: {
id: stringField(runInterpreted, "runId") ?? run?.resultingIds.runId ?? null,
status: stringField(runInterpreted, "status") ?? "not started",
},
};
};
@@ -2738,4 +2738,101 @@
white-space: pre-wrap;
overflow-x: auto;
color: var(--text-secondary);
}
/*
Scene 9 — Prepared Workflow Authoring Lifecycle.
Compact orientation rail plus dominant phase projection.
*/
.prepared-lifecycle-scene {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.prepared-lifecycle-scene__rail {
display: flex;
gap: 0.35rem;
list-style: none;
margin: 0;
padding: 0.45rem 0.55rem;
background: color-mix(in oklch, var(--stage-inset) 55%, transparent);
border-radius: 0.45rem;
overflow-x: auto;
scrollbar-width: none;
}
.prepared-lifecycle-scene__rail li {
display: flex;
align-items: center;
gap: 0.3rem;
padding: 0.25rem 0.45rem;
border-radius: 0.3rem;
font: 600 0.65rem/1 var(--font-interface);
color: var(--text-secondary);
white-space: nowrap;
}
.prepared-lifecycle-scene__rail li[data-active="true"] {
background: color-mix(in oklch, var(--accent-cyan) 14%, transparent);
color: var(--accent-cyan);
}
.prepared-lifecycle-scene__projection {
display: flex;
flex-direction: column;
gap: 0.55rem;
padding: 0.55rem 0.65rem;
background: color-mix(in oklch, var(--stage-surface) 60%, var(--stage-inset));
border: 1px solid var(--stage-line);
border-radius: 0.45rem;
}
.prepared-lifecycle-scene__summary p {
margin: 0;
font-size: 0.78rem;
line-height: 1.4;
color: var(--text-primary);
}
.prepared-lifecycle-scene__commands {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.prepared-lifecycle-scene__command {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.3rem 0.45rem;
background: color-mix(in oklch, var(--stage-inset) 65%, transparent);
border-radius: 0.3rem;
}
.prepared-lifecycle-scene__command code {
font-family: var(--font-evidence);
font-size: 0.65rem;
color: var(--accent-cyan);
}
.prepared-lifecycle-scene__command-result {
font-size: 0.6rem;
font-weight: 600;
letter-spacing: 0.02em;
text-transform: uppercase;
}
.prepared-lifecycle-scene__command-result[data-result="success"] {
color: var(--accent-green);
}
.prepared-lifecycle-scene__command-result[data-result="diagnostic"] {
color: var(--accent-yellow);
}
.prepared-lifecycle-scene__receipt {
display: flex;
justify-content: center;
padding: 0.35rem 0;
}
@@ -51,7 +51,10 @@ describe("defense storyboard catalog", () => {
});
it("keeps chat out of the way during proof-heavy demo beats", () => {
expect(findBeat("prepared-lifecycle", "discover")?.chatMode).toBe("hidden");
expect(findBeat("prepared-lifecycle", "draft")?.chatMode).toBe("hidden");
expect(findBeat("prepared-lifecycle", "validate")?.chatMode).toBe("hidden");
expect(findBeat("prepared-lifecycle", "artifact")?.chatMode).toBe("hidden");
expect(findBeat("prepared-lifecycle", "deployment")?.chatMode).toBe("hidden");
expect(findBeat("run-from-deployment", "input")?.chatMode).toBe("hidden");
expect(findBeat("run-from-deployment", "graph")?.chatMode).toBe("hidden");
@@ -72,10 +75,11 @@ describe("defense storyboard catalog", () => {
});
it("defines the lifecycle story beats before run evidence", () => {
expect(findBeat("prepared-lifecycle", "draft")?.caption).toMatch(/prepared authoring/i);
expect(findBeat("prepared-lifecycle", "artifact")?.caption).toMatch(/artifact/i);
expect(findBeat("prepared-lifecycle", "deployment")?.caption).toMatch(/source/i);
expect(findBeat("prepared-lifecycle", "ready-run")?.caption).toMatch(/ready/i);
expect(findBeat("prepared-lifecycle", "discover")?.caption).toMatch(/sources|capabilities|schemas/i);
expect(findBeat("prepared-lifecycle", "draft")?.caption).toMatch(/draft/i);
expect(findBeat("prepared-lifecycle", "validate")?.caption).toMatch(/diagnose|repair/i);
expect(findBeat("prepared-lifecycle", "artifact")?.caption).toMatch(/compile|artifact/i);
expect(findBeat("prepared-lifecycle", "deployment")?.caption).toMatch(/deploy|bindings/i);
});
it("defines focused run, interrupt, and evidence beats", () => {
@@ -182,10 +182,11 @@ export const mainScenes = defineScenes([
stageTheme: "night",
view: "demo-lifecycle",
beats: [
sceneBeat("draft", "Prepared draft", "Prepared authoring context creates a reusable report workflow.", { chatMode: "hidden", chatTheme: "light" }),
sceneBeat("artifact", "Saved artifact", "The workflow is preserved as a versioned artifact.", { chatMode: "hidden", chatTheme: "light" }),
sceneBeat("deployment", "Deployment bindings", "Deployment binds workflow requirements to configured local sources.", { chatMode: "hidden", chatTheme: "light" }),
sceneBeat("ready-run", "Ready to run", "The deployment is ready to start a persisted run from workflow input.", { chatMode: "hidden", chatTheme: "light" }),
sceneBeat("discover", "Discover capabilities", "Inspect available sources, capabilities, and schemas before authoring.", { chatMode: "hidden", chatTheme: "light" }),
sceneBeat("draft", "Author draft", "Create a workflow draft with report generation steps and routes.", { chatMode: "hidden", chatTheme: "light" }),
sceneBeat("validate", "Validate and repair", "Bind sources and validate the draft; diagnose and repair issues.", { chatMode: "hidden", chatTheme: "light" }),
sceneBeat("artifact", "Compile artifact", "Compile the validated draft into an immutable artifact.", { chatMode: "hidden", chatTheme: "light" }),
sceneBeat("deployment", "Deploy and validate", "Save deployment bindings and validate readiness.", { chatMode: "hidden", chatTheme: "light" }),
],
},
{