feat: model demo climax beats

This commit is contained in:
lda
2026-07-08 23:12:59 +07:00 Verified
parent 79a4081c8d
commit 66d4eed3f8
2 changed files with 122 additions and 0 deletions
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
import type { DemoEvent } from "../demo/timeline/models.js"; import type { DemoEvent } from "../demo/timeline/models.js";
import { loadCanonicalDemoRecording } from "../demo/timeline/replay.js"; import { loadCanonicalDemoRecording } from "../demo/timeline/replay.js";
import { import {
demoBeatLensForBeat,
graphExecutionForBeat, graphExecutionForBeat,
projectInterruptContract, projectInterruptContract,
projectOperationPresentation, projectOperationPresentation,
@@ -70,3 +71,48 @@ describe("demo workflow presentation model", () => {
}); });
}); });
}); });
describe("demo beat lens", () => {
it("defines a product-story phase for every Scene 9 and Scene 10 beat", () => {
const beatIds = ["operation", "graph", "interrupt", "approval", "resume", "output", "trace"];
expect(beatIds.map((beatId) => demoBeatLensForBeat(beatId).phase)).toEqual([
"agent",
"run",
"interrupt",
"interrupt",
"resume",
"evidence",
"evidence",
]);
});
it("uses stable audience-facing labels for the demo climax", () => {
expect(demoBeatLensForBeat("operation")).toMatchObject({
eyebrow: "Agent handoff",
headline: "Request becomes a workflow run",
proofLabel: "workflow.runs.start",
});
expect(demoBeatLensForBeat("approval")).toMatchObject({
eyebrow: "Typed human boundary",
headline: "The run waits for an explicit resume decision",
proofLabel: "issue_review",
});
expect(demoBeatLensForBeat("trace")).toMatchObject({
eyebrow: "Evidence remains",
headline: "The result can still be inspected after the demo",
proofLabel: "trace frames",
});
});
it("keeps graph execution continuity across the interrupt and resume beats", () => {
expect(graphExecutionForBeat("approval")).toEqual(graphExecutionForBeat("interrupt"));
expect(graphExecutionForBeat("resume")).toMatchObject({
completedNodeIds: ["read_docs", "build_report", "review_issues"],
currentNodeId: "create_issues",
});
expect(graphExecutionForBeat("trace")).toMatchObject({
currentNodeId: "end_completed",
});
});
});
@@ -40,6 +40,82 @@ export type GraphExecutionPresentation = {
readonly currentNodeId: string | null; readonly currentNodeId: string | null;
}; };
export type DemoClimaxPhase = "agent" | "run" | "interrupt" | "resume" | "evidence";
export type DemoBeatLens = {
readonly phase: DemoClimaxPhase;
readonly eyebrow: string;
readonly headline: string;
readonly proofLabel: string;
readonly speakerLine: string;
};
const demoBeatLensByBeat: Readonly<Record<string, DemoBeatLens>> = {
operation: {
phase: "agent",
eyebrow: "Agent handoff",
headline: "Request becomes a workflow run",
proofLabel: "workflow.runs.start",
speakerLine: "The AI-facing layer does not own execution; it asks lda.chat to start a durable workflow run.",
},
graph: {
phase: "run",
eyebrow: "Durable graph",
headline: "The product owns the reusable workflow shape",
proofLabel: "typed graph",
speakerLine: "The graph is the reusable automation boundary, not a one-off transcript of tool calls.",
},
interrupt: {
phase: "interrupt",
eyebrow: "Typed pause",
headline: "Execution stops at a declared human boundary",
proofLabel: "issue_review",
speakerLine: "The runtime exposes what decision is needed before any mutation continues.",
},
approval: {
phase: "interrupt",
eyebrow: "Typed human boundary",
headline: "The run waits for an explicit resume decision",
proofLabel: "issue_review",
speakerLine: "The operator answers a schema-backed request; the run remains persisted while waiting.",
},
resume: {
phase: "resume",
eyebrow: "Same run resumes",
headline: "The approved payload continues the persisted run",
proofLabel: "workflow.runs.resume",
speakerLine: "Resume is not a restart; it continues the stopped run with a validated payload.",
},
output: {
phase: "evidence",
eyebrow: "Workflow output",
headline: "The workflow produces artifacts outside the chat",
proofLabel: "report + issues",
speakerLine: "The result is inspectable product state: a report and issue-board changes.",
},
trace: {
phase: "evidence",
eyebrow: "Evidence remains",
headline: "The result can still be inspected after the demo",
proofLabel: "trace frames",
speakerLine: "Trace and protocol evidence keep the demo auditable after the live moment is over.",
},
};
/**
* Presentation-only copy for the demo climax. This intentionally stays separate
* from replay evidence parsing so changing speaker framing cannot mutate the
* canonical run recording.
*/
export const demoBeatLensForBeat = (beatId: string): DemoBeatLens =>
demoBeatLensByBeat[beatId] ?? {
phase: "run",
eyebrow: "Workflow",
headline: "Workflow state",
proofLabel: beatId,
speakerLine: "This beat is not part of the prepared demo climax.",
};
const decodeInterpretation = (event: DemoEvent) => { const decodeInterpretation = (event: DemoEvent) => {
const decoded = v.safeParse(RunInterpretationSchema, event.interpreted); const decoded = v.safeParse(RunInterpretationSchema, event.interpreted);
return decoded.success ? decoded.output : null; return decoded.success ? decoded.output : null;