feat: define defense storyboard catalog
This commit is contained in:
@@ -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 };
|
||||
};
|
||||
Reference in New Issue
Block a user