feat: define defense storyboard catalog

This commit is contained in:
lda
2026-07-04 16:35:07 +07:00 Verified
parent 51491ed265
commit c6018c4a86
2 changed files with 374 additions and 0 deletions
@@ -0,0 +1,59 @@
import { describe, expect, it } from "vitest";
import {
defaultMainLocation,
discussionBranches,
findBeat,
findScene,
mainScenes,
} from "./storyboard.js";
describe("defense storyboard catalog", () => {
it("defines twelve ordered main scenes with unique scene and beat ids", () => {
expect(mainScenes).toHaveLength(12);
expect(mainScenes.map((scene) => scene.id)).toEqual([
"thesis",
"problem",
"positioning",
"planner-runtime",
"lifecycle",
"architecture",
"authoring",
"agent-handoff",
"workflow-demo",
"interrupt-evidence",
"evaluation",
"conclusion",
]);
for (const scene of mainScenes) {
expect(scene.beats.length).toBeGreaterThan(0);
expect(new Set(scene.beats.map((beat) => beat.id)).size).toBe(scene.beats.length);
expect(scene.claimClass.length).toBeGreaterThan(0);
expect(scene.evidencePointer.length).toBeGreaterThan(0);
}
});
it("uses act-level stage themes and independent chat composition", () => {
expect(mainScenes.slice(0, 3).every((scene) => scene.stageTheme === "paper")).toBe(true);
expect(mainScenes.slice(3, 10).every((scene) => scene.stageTheme === "night")).toBe(true);
expect(mainScenes.slice(10).every((scene) => scene.stageTheme === "paper")).toBe(true);
expect(findBeat("agent-handoff", "request")?.chatMode).toBe("full");
expect(findBeat("workflow-demo", "graph")?.chatMode).toBe("rail");
expect(findBeat("interrupt-evidence", "trace")?.chatMode).toBe("dock");
});
it("defines the five positioning discussion branches", () => {
expect(discussionBranches.map((branch) => branch.id)).toEqual([
"direct-orchestration",
"generated-scripts",
"hosted-automation",
"durable-agent-graphs",
"mcp-agent-scale",
]);
expect(discussionBranches.every((branch) => branch.parentSceneId === "positioning")).toBe(true);
});
it("exposes a valid default location", () => {
expect(defaultMainLocation).toEqual({ kind: "main", sceneId: "thesis", beatId: "title" });
expect(findScene(defaultMainLocation.sceneId)?.number).toBe(1);
});
});
@@ -0,0 +1,315 @@
export type ClaimClass = "motivation" | "implemented" | "evaluated" | "external-context" | "future-work";
export type StageTheme = "paper" | "night";
export type ChatTheme = "light" | "dark";
export type ChatMode = "hidden" | "full" | "rail" | "dock";
export type EvidenceMode = "hidden" | "peek" | "open";
export type SceneView =
| "narrative"
| "positioning"
| "boundary"
| "lifecycle"
| "architecture"
| "authoring"
| "agent"
| "demo"
| "evaluation"
| "conclusion";
export type SceneBeatDefinition = {
readonly id: string;
readonly title: string;
readonly caption: string;
readonly chatMode: ChatMode;
readonly chatTheme: ChatTheme;
readonly evidenceMode: EvidenceMode;
};
export type SceneDefinition = {
readonly id: string;
readonly number: number;
readonly title: string;
readonly claimClass: ClaimClass;
readonly evidencePointer: string;
readonly stageTheme: StageTheme;
readonly view: SceneView;
readonly beats: readonly SceneBeatDefinition[];
};
const defineScenes = <const Scenes extends readonly SceneDefinition[]>(scenes: Scenes): Scenes => scenes;
const sceneBeat = (
id: string,
title: string,
caption: string,
options: Partial<Pick<SceneBeatDefinition, "chatMode" | "chatTheme" | "evidenceMode">> = {},
): SceneBeatDefinition => ({
id,
title,
caption,
chatMode: options.chatMode ?? "hidden",
chatTheme: options.chatTheme ?? "dark",
evidenceMode: options.evidenceMode ?? "hidden",
});
export const mainScenes = defineScenes([
{
id: "thesis",
number: 1,
title: "Thesis",
claimClass: "implemented",
evidencePointer: "Thesis Abstract and Introduction",
stageTheme: "paper",
view: "narrative",
beats: [
sceneBeat("title", "Design and Implementation of lda.chat", "The project began as a pursuit of an AI agent for workspace automation."),
sceneBeat("substrate", "The substrate an agent needs", "The submitted contribution creates, validates, runs, and inspects reusable workflows."),
],
},
{
id: "problem",
number: 2,
title: "The Problem",
claimClass: "motivation",
evidencePointer: "Thesis Problem Statement and Requirements",
stageTheme: "paper",
view: "narrative",
beats: [
sceneBeat("direct-actions", "Direct actions are not reusable automation", "A tool loop can act without owning durable lifecycle records."),
sceneBeat("missing-contracts", "The missing contracts", "Reusable automation needs schemas, bindings, persistence, traces, and recovery boundaries."),
],
},
{
id: "positioning",
number: 3,
title: "Positioning and Related Systems",
claimClass: "motivation",
evidencePointer: "Thesis Positioning and Related Systems",
stageTheme: "paper",
view: "positioning",
beats: [
sceneBeat("landscape", "Different centers of gravity", "Tool loops, scripts, hosted automation, agent graphs, and MCP solve different parts of the problem."),
sceneBeat("lda-position", "lda.chat's position", "Typed lifecycle contracts and provider-neutral sources are exposed for external-agent operation."),
],
},
{
id: "planner-runtime",
number: 4,
title: "Planner and Runtime",
claimClass: "implemented",
evidencePointer: "Thesis Architecture Overview",
stageTheme: "night",
view: "boundary",
beats: [
sceneBeat("planner", "External planner", "The planner proposes and revises workflow structure."),
sceneBeat("runtime", "Deterministic runtime", "The runtime validates, executes, records, and resumes."),
sceneBeat("boundary", "Explicit operations", "Typed CLI and JSON-RPC operations cross the boundary."),
],
},
{
id: "lifecycle",
number: 5,
title: "Workflow Lifecycle",
claimClass: "implemented",
evidencePointer: "Thesis Workflow Lifecycle",
stageTheme: "night",
view: "lifecycle",
beats: [
sceneBeat("draft", "Draft", "Mutable iterative authoring state."),
sceneBeat("artifact", "Artifact", "Immutable saved workflow definition."),
sceneBeat("deployment", "Deployment", "Logical requirements bound to concrete sources."),
sceneBeat("run", "Run", "Persisted execution, output, status, and trace."),
],
},
{
id: "architecture",
number: 6,
title: "Architecture Zoom",
claimClass: "implemented",
evidencePointer: "Thesis System Architecture; docs/project_map.md; docs/source_architecture.md",
stageTheme: "night",
view: "architecture",
beats: [
sceneBeat("client", "Client operations", "Human and agent clients use the same public lifecycle surface."),
sceneBeat("api", "Transport and API", "JSON-RPC reaches WorkflowApi without owning domain behavior."),
sceneBeat("runtime", "Runtime and providers", "The runtime resolves provider-neutral capabilities and stores lifecycle records."),
sceneBeat("node-use", "NodeUse", "One callable node validates input, invokes a capability, and reduces output into state.", { evidenceMode: "peek" }),
],
},
{
id: "authoring",
number: 7,
title: "Author, Validate, Repair",
claimClass: "implemented",
evidencePointer: "CLI documentation; draft authoring API; challenge UX findings",
stageTheme: "night",
view: "authoring",
beats: [
sceneBeat("discover", "Discover", "Inspect capabilities and schemas before authoring."),
sceneBeat("author", "Author", "Focused operations build and connect the draft."),
sceneBeat("diagnose", "Diagnose", "Structured diagnostics identify invalid state.", { evidenceMode: "peek" }),
sceneBeat("repair", "Repair", "Repair hints lead to a valid compiled workflow."),
],
},
{
id: "agent-handoff",
number: 8,
title: "Agent Handoff",
claimClass: "implemented",
evidencePointer: "Constrained demo agent and prepared replay recipe",
stageTheme: "night",
view: "agent",
beats: [
sceneBeat("request", "Operator request", "A thin agent interface receives the report request.", { chatMode: "full", chatTheme: "light" }),
sceneBeat("handoff", "Prepared operation", "The interface delegates durable work to lda.chat.", { chatMode: "full", chatTheme: "light" }),
],
},
{
id: "workflow-demo",
number: 9,
title: "Workflow Takes the Stage",
claimClass: "implemented",
evidencePointer: "Prepared replay and examples/lda_report_workflow",
stageTheme: "night",
view: "demo",
beats: [
sceneBeat("operation", "Start operation", "Raw and interpreted operation evidence enters from chat.", { chatMode: "full", chatTheme: "light" }),
sceneBeat("graph", "Reusable graph", "The graph becomes primary while chat moves to a rail.", { chatMode: "rail", chatTheme: "light" }),
sceneBeat("interrupt", "Typed interrupt", "Execution reaches the issue-review boundary.", { chatMode: "rail", chatTheme: "light" }),
],
},
{
id: "interrupt-evidence",
number: 10,
title: "Interrupt, Resume, Evidence",
claimClass: "implemented",
evidencePointer: "Typed interrupt schemas, run inspection, and trace events",
stageTheme: "night",
view: "demo",
beats: [
sceneBeat("approval", "Approval", "The operator reviews a schema-backed resume request.", { chatMode: "rail", chatTheme: "light" }),
sceneBeat("resume", "Resume", "The approved payload resumes the same persisted run.", { chatMode: "rail", chatTheme: "light" }),
sceneBeat("output", "Output", "The workflow produces the report and issue-board changes.", { chatMode: "dock", chatTheme: "light" }),
sceneBeat("trace", "Evidence", "Trace frames and protocol evidence remain inspectable.", { chatMode: "dock", chatTheme: "light", evidenceMode: "open" }),
],
},
{
id: "evaluation",
number: 11,
title: "Evaluation",
claimClass: "evaluated",
evidencePointer: "Thesis Evaluation and Appendix C",
stageTheme: "paper",
view: "evaluation",
beats: [
sceneBeat("cohort", "36-trial cohort", "Two challenges, two hosted models, three profiles, and three waves."),
sceneBeat("validity", "Bounded validity", "Manual audit separates task completion from valid product-surface evidence."),
sceneBeat("findings", "Longitudinal findings", "Trials exposed concrete authoring and diagnostic UX gaps."),
],
},
{
id: "conclusion",
number: 12,
title: "Limits and Conclusion",
claimClass: "future-work",
evidencePointer: "Thesis Limitations, Future Work, and Conclusion",
stageTheme: "paper",
view: "conclusion",
beats: [
sceneBeat("limits", "Implemented boundary", "The prototype is not a production sandbox, scheduler, or broad agent benchmark."),
sceneBeat("future", "Surrounding layers", "A live LLM interface, scheduling, and broader evaluation remain future work."),
sceneBeat("conclusion", "Planner proposes; runtime executes", "The typed substrate makes reusable agent-operated automation inspectable.", { chatMode: "dock", chatTheme: "light" }),
],
},
]);
export type MainSceneId = (typeof mainScenes)[number]["id"];
export type MainLocation = {
readonly kind: "main";
readonly sceneId: MainSceneId;
readonly beatId: string;
};
export type DiscussionBranchDefinition = {
readonly id: string;
readonly parentSceneId: MainSceneId;
readonly title: string;
readonly claimClass: ClaimClass;
readonly evidencePointer: string;
readonly summary: string;
};
const defineDiscussionBranches = <const Branches extends readonly DiscussionBranchDefinition[]>(
branches: Branches,
): Branches => branches;
export const discussionBranches = defineDiscussionBranches([
{
id: "direct-orchestration",
parentSceneId: "positioning",
title: "Direct orchestration",
claimClass: "motivation",
evidencePointer: "Thesis: Direct LLM Tool Orchestration",
summary: "Dynamic adaptation versus durable reusable procedure.",
},
{
id: "generated-scripts",
parentSceneId: "positioning",
title: "Generated scripts",
claimClass: "motivation",
evidencePointer: "Thesis: Generated Scripts",
summary: "Simplicity and debuggability versus managed lifecycle records.",
},
{
id: "hosted-automation",
parentSceneId: "positioning",
title: "Hosted automation",
claimClass: "future-work",
evidencePointer: "Thesis: Workflow Automation Platforms and Future Work",
summary: "Hosted triggers and scheduling are mature elsewhere and remain future work here.",
},
{
id: "durable-agent-graphs",
parentSceneId: "positioning",
title: "Durable agent graphs",
claimClass: "motivation",
evidencePointer: "Thesis: Agent Graph Frameworks",
summary: "Shared durability concerns, with a different lifecycle and source-binding emphasis.",
},
{
id: "mcp-agent-scale",
parentSceneId: "positioning",
title: "MCP and agent-facing scale",
claimClass: "external-context",
evidencePointer: "Thesis: Model Context Protocol; Cloudflare Code Mode; Anthropic code execution with MCP",
summary: "MCP is a capability protocol; progressive discovery addresses large agent-facing surfaces.",
},
]);
export type DiscussionBranchId = (typeof discussionBranches)[number]["id"];
export type DiscussionLocation = {
readonly kind: "discussion";
readonly branchId: DiscussionBranchId;
};
export type PresentationLocation = MainLocation | DiscussionLocation;
export const findScene = (sceneId: string): SceneDefinition | undefined =>
mainScenes.find((scene) => scene.id === sceneId);
export const findBeat = (sceneId: string, beatId: string): SceneBeatDefinition | undefined =>
findScene(sceneId)?.beats.find((beat) => beat.id === beatId);
export const findDiscussionBranch = (branchId: string): DiscussionBranchDefinition | undefined =>
discussionBranches.find((branch) => branch.id === branchId);
export const defaultMainLocation: MainLocation = { kind: "main", sceneId: "thesis", beatId: "title" };
export const mainLocation = (sceneId: string, beatId: string): MainLocation | null => {
const scene = findScene(sceneId);
if (!scene) return null;
const beat = scene.beats.find((b) => b.id === beatId);
if (!beat) return null;
return { kind: "main", sceneId: scene.id as MainSceneId, beatId: beat.id };
};