feat: reweight defense story and presenter notes

This commit is contained in:
lda
2026-07-13 07:55:34 +07:00 Verified
parent ab943deed5
commit 18c930449a
15 changed files with 703 additions and 172 deletions
@@ -418,6 +418,7 @@ describe("useDemoTimeline", () => {
expect(result.current.state.phase).toBe("paused");
expect(result.current.state.events[result.current.state.appliedCount - 1]?.stage).toBe("run_resume");
expect(result.current.recordingId).toBe("lda-report-revision-v1");
expect(result.current.output?.approved).toBe(false);
expect(result.current.output?.created_issues).toHaveLength(0);
expect(result.current.trace).toBeNull();
+5 -3
View File
@@ -48,8 +48,10 @@ export type DemoTimelineController = {
readonly primeReplayToStage: (stage: DemoEvent["stage"] | null) => void;
};
const deriveRecordingId = (state: DemoTimelineState): string | null =>
state.mode === "replay" ? "lda-report-success-v1" : null;
const deriveRecordingId = (
mode: DemoMode,
activeRecording: DemoRecording | null,
): string | null => mode === "replay" ? activeRecording?.recordingId ?? null : null;
const deriveMissingMessage = (mode: DemoMode, target: string | null): string | null => {
if (mode !== "live" || target !== null) return null;
@@ -349,7 +351,7 @@ export const useDemoTimeline = (
output,
trace,
missingDeploymentMessage: deriveMissingMessage(state.mode, target),
recordingId: deriveRecordingId(state),
recordingId: deriveRecordingId(state.mode, activeRecording.current),
canStart: state.mode === "replay" || target !== null,
setMode,
start,
@@ -122,6 +122,28 @@ describe("GuidedProductMoment", () => {
expect(screen.getByLabelText("workflow.runs.resume operation")).toBeInTheDocument();
});
it("labels a replay revision as a separate prepared recording", () => {
render(
<GuidedProductMoment
beat={findBeat("resume-output-evidence", "resume")!}
demo={demoWithAppliedCount(6)}
contract={contract}
operation={resumeOperation}
approvalActions={{
state: "revision_requested",
canSubmit: false,
canRequestRevision: false,
submit: vi.fn(async () => {}),
requestRevision: vi.fn(async () => {}),
}}
openEvidence={vi.fn()}
/>,
);
expect(screen.getByText(/separate prepared recording/i)).toBeInTheDocument();
expect(screen.queryByText(/same run/i)).not.toBeInTheDocument();
});
it("keeps approval focused on input and decision without pre-resume output", () => {
render(
<GuidedProductMoment
@@ -38,13 +38,16 @@ const momentForBeat = (beatId: string): GuidedProductMoment => {
const statusCopy = (
moment: ReturnType<typeof momentForBeat>,
approvalActions?: DemoApprovalActions,
isReplay = false,
): string => {
if (moment === "interrupt") return "Run paused at the typed interrupt; inspect the context before deciding.";
if (approvalActions?.state === "revision_requested") {
return isReplay
? "Revision requested. Separate prepared recording; no run-ID continuity is claimed."
: "Revision requested. The live run resumes through its negative outcome branch.";
}
if (moment !== "approval") return "Same persisted run; inspect the proof below.";
if (approvalActions?.state === "submitted") return "Submitted. Same run resumed.";
if (approvalActions?.state === "revision_requested") {
return "Revision requested. The same run resumed through its negative outcome branch.";
}
return "Run is paused. Submit resumes this same run.";
};
@@ -76,8 +79,18 @@ export const GuidedProductMoment = ({
const lens = demoBeatLensForBeat(beat.id);
const facts = projectDemoRunFacts(demo);
const runResume = demo.state.events.find((event) => event.stage === "run_resume");
const headline = moment === "resume" && approvalActions?.state === "revision_requested"
? "The revision request continues the persisted run"
const revisionRequested = approvalActions?.state === "revision_requested";
// Replay revision evidence has a separate recording identity; do not let
// the normal submitted-branch lens imply continuity for that branch.
const eyebrow = revisionRequested
? demo.state.mode === "replay"
? "Prepared branch"
: "Live branch"
: lens.eyebrow;
const headline = revisionRequested
? demo.state.mode === "replay"
? "Separate prepared revision recording"
: "Live revision branch resumes the run"
: lens.headline;
return (
@@ -91,9 +104,9 @@ export const GuidedProductMoment = ({
data-continuation-focus={moment === "resume" ? "output" : moment === "trace" ? "trace" : undefined}
>
<header className="guided-product-moment__header">
<span>{lens.eyebrow}</span>
<span>{eyebrow}</span>
<strong>{headline}</strong>
<p>{statusCopy(moment, approvalActions)}</p>
<p>{statusCopy(moment, approvalActions, demo.state.mode === "replay")}</p>
</header>
<div className="guided-product-moment__primary">
@@ -615,7 +615,7 @@ describe("PresentationRoute", () => {
expect(window.location.hash).toBe("#scene/resume-output-evidence/resume");
expect(screen.getByLabelText("workflow.runs.resume operation")).toBeInTheDocument();
expect(screen.getByText(/Revision Requested/i)).toBeInTheDocument();
expect(screen.getByText(/separate prepared recording; no run-ID continuity is claimed/i)).toBeInTheDocument();
window.location.hash = "#scene/resume-output-evidence/trace";
window.dispatchEvent(new HashChangeEvent("hashchange"));
@@ -0,0 +1,81 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { mainScenes } from "../storyboard.js";
import {
completeDeckTargetSeconds,
discussionBranchForId,
mainSpeechWordCount,
presenterBeatNoteFor,
presenterNotes,
presenterSceneNotes,
} from "./presenter-notes.js";
describe("presenter note catalog", () => {
it("has exactly one note for every current storyboard beat", () => {
const noteKeys = presenterNotes.map((note) => `${note.sceneId}/${note.beatId}`);
expect(new Set(noteKeys).size).toBe(noteKeys.length);
for (const scene of mainScenes) {
for (const beat of scene.beats) {
const note = presenterBeatNoteFor(scene.id, beat.id);
expect(note).toBeDefined();
expect(note?.mustSay.trim().length).toBeGreaterThan(0);
}
}
expect(noteKeys).toHaveLength(mainScenes.flatMap((scene) => scene.beats).length);
});
it("keeps the planned scene timing and complete-deck cap", () => {
expect(mainScenes.map((scene) => presenterSceneNotes(scene.id).reduce((sum, note) => sum + note.targetSeconds, 0))).toEqual([
45,
45,
45,
55,
45,
55,
40,
20,
45,
35,
30,
50,
120,
75,
]);
expect(completeDeckTargetSeconds()).toBe(780);
expect(completeDeckTargetSeconds()).toBeLessThanOrEqual(780);
});
it("keeps the must-say speech within the defense word budget", () => {
expect(mainSpeechWordCount()).toBeGreaterThanOrEqual(750);
expect(mainSpeechWordCount()).toBeLessThanOrEqual(850);
});
it("keeps the readable speech runbook synchronized with every must-say note", () => {
const speech = readFileSync(
join(import.meta.dirname, "../../../../../../docs/runbooks/defense-speech-and-claim-audit.md"),
"utf8",
);
for (const note of presenterNotes) {
expect(speech, `${note.sceneId}/${note.beatId}`).toContain(note.mustSay);
}
});
it("requires evidence and resolvable Q&A links without empty placeholders", () => {
for (const note of presenterNotes) {
expect(note.evidencePointers.length).toBeGreaterThan(0);
for (const pointer of note.evidencePointers) {
expect(pointer.trim().length).toBeGreaterThan(0);
}
for (const branchId of note.qnaBranchIds) {
expect(discussionBranchForId(branchId)).toBeDefined();
}
for (const optionalField of [note.optionalDetail, note.warning, note.fallback]) {
expect(optionalField === null || optionalField.trim().length > 0).toBe(true);
}
}
});
});
@@ -0,0 +1,418 @@
import {
findDiscussionBranch,
mainScenes,
type DiscussionBranchDefinition,
type DiscussionBranchId,
type MainSceneId,
} from "../storyboard.js";
export type PresenterBeatNote = {
readonly sceneId: MainSceneId;
readonly beatId: string;
readonly targetSeconds: number;
readonly mustSay: string;
readonly optionalDetail: string | null;
readonly warning: string | null;
readonly fallback: string | null;
readonly evidencePointers: readonly [string, ...string[]];
readonly qnaBranchIds: readonly DiscussionBranchId[];
};
type PresenterBeatNoteOptions = {
readonly optionalDetail?: string | null;
readonly warning?: string | null;
readonly fallback?: string | null;
readonly qnaBranchIds?: readonly DiscussionBranchId[];
};
const beatNote = <const EvidencePointers extends readonly [string, ...string[]]>(
sceneId: MainSceneId,
beatId: string,
targetSeconds: number,
mustSay: string,
evidencePointers: EvidencePointers,
options: PresenterBeatNoteOptions = {},
): PresenterBeatNote => ({
sceneId,
beatId,
targetSeconds,
mustSay,
optionalDetail: options.optionalDetail ?? null,
warning: options.warning ?? null,
fallback: options.fallback ?? null,
evidencePointers,
qnaBranchIds: options.qnaBranchIds ?? [],
});
/**
* Presenter-only speech and evidence source. The audience route remains in
* storyboard.ts; this catalog is deliberately not rendered by scene content.
*/
export const presenterNotes = [
beatNote(
"thesis",
"title",
22,
"This project began with the goal in the title: an AI agent for creating and automating workspace workflows. The difficult engineering problem became the system underneath the chat.",
["Thesis Abstract and Introduction"],
{
warning: "Do not present the submitted system as a bundled autonomous planner.",
qnaBranchIds: ["where-is-ai-agent", "title-ai-agent-wording"],
},
),
beatNote(
"thesis",
"substrate",
23,
"The submitted contribution is a typed workflow substrate: an external planner can propose work while the platform owns definitions, validation, bindings, execution records, traces, and explicit resume boundaries.",
["Thesis Abstract and Introduction", "Thesis Contributions"],
{ qnaBranchIds: ["where-is-ai-agent", "not-just-cli"] },
),
beatNote(
"problem",
"direct-actions",
22,
"A model can call tools and complete one task, but a tool transcript is not reusable automation.",
["Thesis Problem Statement and Requirements"],
{ qnaBranchIds: ["direct-orchestration", "not-just-scripts"] },
),
beatNote(
"problem",
"missing-contracts",
23,
"Reuse needs schemas, source bindings, persistence, traces, and declared recovery boundaries, with planning kept separate from execution.",
["Thesis Problem Statement and Requirements"],
{ qnaBranchIds: ["why-schemas", "run-persistence"] },
),
beatNote(
"positioning",
"landscape",
22,
"Related systems have different centers of gravity: tool loops act now, scripts package code, hosted platforms operate workflows, agent graphs organize planners, and MCP exposes capabilities.",
["Thesis Positioning and Related Systems"],
{ qnaBranchIds: ["direct-orchestration", "generated-scripts", "hosted-automation", "durable-agent-graphs", "mcp-agent-scale"] },
),
beatNote(
"positioning",
"lda-position",
23,
"lda.chat takes a narrower position: a typed, provider-neutral lifecycle for workflows authored or operated by external agents, not a replacement or superiority claim.",
["Thesis Positioning and Related Systems", "Thesis Source Model"],
{ warning: "Provider neutrality is demonstrated for the implemented source families, not arbitrary future providers.", qnaBranchIds: ["not-just-scripts"] },
),
beatNote(
"planner-runtime",
"planner",
18,
"An external model or human proposes and revises workflow structure; this keeps planning outside the runtime.",
["Thesis Architecture Overview"],
{ qnaBranchIds: ["where-is-ai-agent", "not-just-cli"] },
),
beatNote(
"planner-runtime",
"runtime",
18,
"For fixed definitions and handler results, the runtime validates the graph, resolves sources, executes steps, records state and traces, and resumes only at declared boundaries.",
["Thesis Workflow Core", "Thesis Architecture Overview"],
{ warning: "Qualify determinism; provider code, resource reads, and external side effects can vary.", qnaBranchIds: ["run-persistence", "typed-interrupts"] },
),
beatNote(
"planner-runtime",
"boundary",
19,
"Typed CLI and JSON-RPC operations reach the same Workflow API, making schemas, diagnostics, and lifecycle state machine-readable without importing runtime internals.",
["Thesis Architecture Overview", "docs/source_architecture.md"],
{ qnaBranchIds: ["not-just-cli"] },
),
beatNote(
"lifecycle",
"draft",
11,
"Draft is mutable authoring state.",
["Thesis Workflow Lifecycle"],
{ optionalDetail: "Raw plans can also create artifacts without passing through a Draft." },
),
beatNote(
"lifecycle",
"artifact",
11,
"Artifact is an immutable workflow definition.",
["Thesis Workflow Lifecycle"],
),
beatNote(
"lifecycle",
"deployment",
11,
"Deployment binds an artifact version to concrete sources and runtime context.",
["Thesis Workflow Lifecycle"],
),
beatNote(
"lifecycle",
"run",
12,
"Run records one execution, including status, diagnostics, output, trace, and an explicit stopped or interrupted state.",
["Thesis Workflow Lifecycle"],
{ qnaBranchIds: ["lifecycle-states", "run-persistence"] },
),
beatNote(
"architecture",
"client",
13,
"Human and agent clients use the same public lifecycle operations.",
["Thesis System Architecture", "docs/project_map.md"],
),
beatNote(
"architecture",
"api",
13,
"JSON-RPC handles transport concerns and delegates to WorkflowApi rather than owning domain behavior.",
["Thesis System Architecture", "docs/source_architecture.md"],
{ qnaBranchIds: ["not-just-cli"] },
),
beatNote(
"architecture",
"runtime",
14,
"Server composition supplies stores, provider projections, and the runtime while the core remains independent of MCP and Python behavior.",
["Thesis System Architecture", "docs/source_architecture.md"],
{ qnaBranchIds: ["provider-security"] },
),
beatNote(
"architecture",
"node-use",
15,
"A NodeUse validates input, invokes a projected capability, checks its declared outcome, reduces output into state, appends a trace frame, and routes to the next edge.",
["Thesis Workflow Core Model"],
),
beatNote(
"authoring",
"discover",
10,
"Before authoring, a client can discover sources, capabilities, and schemas instead of guessing at hidden interfaces.",
["CLI documentation", "Draft authoring API", "Challenge UX findings"],
{ qnaBranchIds: ["why-schemas", "validation-diagnostics"] },
),
beatNote(
"authoring",
"author",
10,
"Focused operations let an external agent change a mutable Draft while preserving a clear lifecycle boundary.",
["CLI documentation", "Draft authoring API"],
{ qnaBranchIds: ["raw-plan-import"] },
),
beatNote(
"authoring",
"diagnose",
10,
"Validation returns structured diagnostics, affected paths, repair hints, and suggested next actions.",
["Validation and diagnostics", "Challenge UX findings"],
{ qnaBranchIds: ["validation-diagnostics"] },
),
beatNote(
"authoring",
"repair",
10,
"These surfaces make invalid intermediate drafts repairable; they support a loop, but no hint guarantees success.",
["Validation and diagnostics", "Challenge UX findings"],
{ warning: "Do not promise that diagnostics automatically repair every workflow.", qnaBranchIds: ["validation-diagnostics"] },
),
beatNote(
"agent-handoff",
"request",
20,
"I will now show a prepared demonstration built on this platform. The chat is a presentation interface, not the autonomous planner evaluated by the thesis. The chat translates a report request into the same public lifecycle operations an external agent could call. This prepared path demonstrates product behavior and recorded evidence, not a fresh model-performance result.",
["Constrained demo agent and prepared replay recipe"],
{
fallback: "This is the reviewed recording, not a live model planning this workflow.",
qnaBranchIds: ["prepared-replay-boundary", "demo-reliability"],
},
),
beatNote(
"prepared-lifecycle",
"discover",
9,
"The later issue-review example first inspects configured local.lda_docs, report, and issue-board capabilities.",
["examples/lda_report_workflow", "deployment inspect replay evidence"],
{ qnaBranchIds: ["prepared-replay-boundary"] },
),
beatNote(
"prepared-lifecycle",
"draft",
9,
"It creates and edits a Draft for report generation, making the proposal visible before execution.",
["examples/lda_report_workflow", "deployment inspect replay evidence"],
),
beatNote(
"prepared-lifecycle",
"validate",
9,
"It validates incomplete state, exposes a missing output binding, and applies a targeted repair.",
["examples/lda_report_workflow", "deployment inspect replay evidence"],
{ qnaBranchIds: ["validation-diagnostics"] },
),
beatNote(
"prepared-lifecycle",
"artifact",
9,
"It saves the validated plan as immutable artifact lda_report_case_study version 1.",
["examples/lda_report_workflow", "deployment inspect replay evidence"],
),
beatNote(
"prepared-lifecycle",
"deployment",
9,
"This later issue-review example is richer than the thesis three-node deterministic report case study: it is an implementation extension built on the same platform. Deployment binds and validates a ready configuration but does not run the workflow.",
["examples/lda_report_workflow", "deployment inspect replay evidence", "Thesis deterministic report case study"],
{ warning: "Do not present issue-board output from this later example as the thesis case study.", qnaBranchIds: ["prepared-replay-boundary"] },
),
beatNote(
"run-from-deployment",
"input",
11,
"The deployment receives selected local documents and an issue-board path.",
["workflow.runs.start replay evidence"],
),
beatNote(
"run-from-deployment",
"operation",
12,
"The public workflow.runs.start operation validates the deployment and input, creates a persisted Run, and begins the reusable graph.",
["workflow.runs.start replay evidence"],
{ qnaBranchIds: ["run-persistence"] },
),
beatNote(
"run-from-deployment",
"graph",
12,
"The graph reads documents, analyzes them, builds a report, drafts proposed issues, and pauses at a declared review interrupt before issue-board changes.",
["workflow.runs.start replay evidence", "examples/lda_report_workflow"],
{ fallback: "The operation view is replay-backed evidence of the prepared path, not a newly completed live run." },
),
beatNote(
"typed-human-boundary",
"interrupt",
15,
"Execution pauses at a typed issue_review interrupt exposing request data, allowed outcomes, request schema, and resume schema.",
["Typed interrupt payload and resume contract"],
{ qnaBranchIds: ["typed-interrupts", "why-schemas"] },
),
beatNote(
"typed-human-boundary",
"approval",
15,
"The operator chooses submitted or revision-requested; this is a typed interrupt and resume contract, not a production approval gate, role system, or policy engine.",
["Typed interrupt payload and resume contract"],
{ warning: "Both outcomes resume through declared workflow branches; this is not production approval governance.", qnaBranchIds: ["typed-interrupts", "security-production-boundary"] },
),
beatNote(
"resume-output-evidence",
"resume",
16,
"On the submitted path, workflow.runs.resume continues the recorded interrupted Run.",
["workflow.runs.resume replay evidence", "Revision replay identity"],
{ fallback: "The submitted replay demonstrates same-run continuation; the revision branch is recorded separately.", qnaBranchIds: ["replay-provenance", "prepared-replay-boundary"] },
),
beatNote(
"resume-output-evidence",
"output",
16,
"The workflow creates the report and issue-board changes, then records terminal output.",
["workflow.runs.resume replay evidence", "examples/lda_report_workflow"],
{ warning: "Identify these issue-board changes as later example evidence, not output from the thesis three-node case study." },
),
beatNote(
"resume-output-evidence",
"trace",
18,
"Trace frames and protocol evidence remain inspectable; this is declared-boundary resumability, not arbitrary crash recovery or exactly-once execution. The revision replay is a separate prepared recording.",
["workflow.runs.resume replay evidence", "Revision replay identity"],
{ warning: "Never claim run-ID continuity for the prepared revision recording.", qnaBranchIds: ["replay-provenance", "demo-reliability"] },
),
beatNote(
"evaluation",
"cohort",
40,
"The evaluation combines conformance tests, deterministic case studies, and a manually audited external-agent campaign: 36 trials across two challenges, two hosted models, three instruction profiles, and three waves, with three attempts per cell.",
["Thesis Evaluation and Appendix C"],
{ qnaBranchIds: ["evaluation-validity"] },
),
beatNote(
"evaluation",
"validity",
40,
"The author audit classified 27 trials as clean product-path passes, eight as invalid samples, and one as a failure. Invalid samples included contamination such as reading implementation files, prior artifacts, adjacent attempts, or evaluator state.",
["Thesis Evaluation and Appendix C", "Author audit"],
{ qnaBranchIds: ["evaluation-validity"] },
),
beatNote(
"evaluation",
"findings",
40,
"Because prompts, product snapshots, and hosted conditions changed across waves, these results are longitudinal engineering evidence. They expose authoring and diagnostic gaps, not a benchmark of model success, token reduction, retry reduction, or superiority.",
["Thesis Evaluation and Appendix C", "Thesis Threats to Validity"],
{ warning: "Use non-benchmark wording; do not report the counts as general model performance.", qnaBranchIds: ["evaluation-validity"] },
),
beatNote(
"conclusion",
"limits",
18,
"The prototype uses trusted in-process Python and file-backed stores; it does not provide production authentication, RBAC, sandboxing, scheduling, arbitrary crash recovery, or a bundled autonomous planner.",
["Thesis Limitations"],
{ qnaBranchIds: ["security-production-boundary", "production-readiness"] },
),
beatNote(
"conclusion",
"future",
18,
"A live agent interface, transactional storage, richer debugging, security hardening, scheduling, and controlled comparative evaluation remain future work.",
["Thesis Future Work"],
{ qnaBranchIds: ["production-readiness"] },
),
beatNote(
"conclusion",
"conclusion",
20,
"The contribution is architectural and implemented: external planners can propose workflows while a typed platform validates, binds, executes, persists, interrupts, resumes, and inspects them through public operations.",
["Thesis Contributions", "Thesis Conclusion"],
{ qnaBranchIds: ["where-is-ai-agent", "not-just-cli"] },
),
beatNote(
"conclusion",
"questions",
19,
"That boundary is the claim I will defend: reusable agent-operated automation is inspectable because planning and execution have explicit contracts. I welcome questions.",
["Thesis Conclusion", "Defense Q&A index"],
{ qnaBranchIds: ["where-is-ai-agent", "evaluation-validity", "production-readiness"] },
),
] satisfies readonly PresenterBeatNote[];
type PresenterRouteKey = `${MainSceneId}/${string}`;
const presenterNotesByRoute = new Map<PresenterRouteKey, PresenterBeatNote>(
presenterNotes.map((note) => [`${note.sceneId}/${note.beatId}` as PresenterRouteKey, note]),
);
/** Resolve a presenter note without giving audience components access to the catalog internals. */
export const presenterBeatNoteFor = (sceneId: MainSceneId, beatId: string): PresenterBeatNote | undefined =>
presenterNotesByRoute.get(`${sceneId}/${beatId}`);
export const presenterSceneNotes = (sceneId: MainSceneId): readonly PresenterBeatNote[] =>
presenterNotes.filter((note) => note.sceneId === sceneId);
/** Keep the Q&A lookup in one place so note links cannot silently drift from storyboard branches. */
export const discussionBranchForId = (branchId: DiscussionBranchId): DiscussionBranchDefinition | undefined =>
findDiscussionBranch(branchId);
const NAVIGATION_BUFFER_SECONDS = 75;
export const completeDeckTargetSeconds = (): number =>
presenterNotes.reduce((total, note) => total + note.targetSeconds, 0) + NAVIGATION_BUFFER_SECONDS;
/**
* Counts only must-say text. Inline punctuation and identifiers are split into
* readable word units, while hyphenated terms and contractions stay together.
*/
export const mainSpeechWordCount = (): number => {
const wordPattern = /\b[\p{L}\p{N}]+(?:['-][\p{L}\p{N}]+)*\b/gu;
return presenterNotes.reduce((total, note) => total + (note.mustSay.match(wordPattern)?.length ?? 0), 0);
};
@@ -79,6 +79,22 @@ describe("defense storyboard catalog", () => {
expect(findBeat("prepared-lifecycle", "deployment")?.caption).toMatch(/deploy|bindings/i);
});
it("keeps Scene 5 as vocabulary and moves applied lifecycle evidence to Scene 9", () => {
expect(findBeat("lifecycle", "draft")?.caption).toMatch(/^Draft is mutable authoring state\.$/);
expect(findBeat("lifecycle", "artifact")?.caption).toMatch(/^Artifact is an immutable workflow definition\.$/);
expect(findBeat("lifecycle", "deployment")?.caption).toMatch(/^Deployment binds an artifact version/);
expect(findBeat("lifecycle", "run")?.caption).toMatch(/^Run records one execution/);
expect(findBeat("prepared-lifecycle", "validate")?.caption).toMatch(/incomplete|repair/i);
expect(findBeat("prepared-lifecycle", "deployment")?.caption).toMatch(/ready|does not run|Scene 10/i);
expect(findBeat("prepared-lifecycle", "deployment")?.caption).toMatch(/three-node|implementation extension/i);
});
it("makes the evaluation beat carry the audited counts and validity boundary", () => {
expect(findBeat("evaluation", "cohort")?.caption).toMatch(/36|two challenges|two hosted models|three waves/i);
expect(findBeat("evaluation", "validity")?.caption).toMatch(/27|8|1|audit/i);
expect(findBeat("evaluation", "findings")?.caption).toMatch(/changing|longitudinal|benchmark/i);
});
it("defines focused run, interrupt, and evidence beats", () => {
expect(findBeat("run-from-deployment", "input")).toBeDefined();
expect(findBeat("run-from-deployment", "operation")).toBeDefined();
+11 -11
View File
@@ -118,10 +118,10 @@ export const mainScenes = defineScenes([
evidencePointer: "Thesis Workflow Lifecycle",
view: "lifecycle",
beats: [
sceneBeat("draft", "Draft", "Draft, Artifact, Deployment, and Run are the durable vocabulary behind reusable automation."),
sceneBeat("artifact", "Artifact", "An immutable artifact carries the durable workflow definition forward."),
sceneBeat("deployment", "Deployment", "A deployment binds the durable workflow to concrete sources."),
sceneBeat("run", "Run", "A run records durable execution, output, status, and trace."),
sceneBeat("draft", "Draft", "Draft is mutable authoring state."),
sceneBeat("artifact", "Artifact", "Artifact is an immutable workflow definition."),
sceneBeat("deployment", "Deployment", "Deployment binds an artifact version to concrete sources."),
sceneBeat("run", "Run", "Run records one execution, including output, status, and trace."),
],
},
{
@@ -172,11 +172,11 @@ export const mainScenes = defineScenes([
evidencePointer: "examples/lda_report_workflow; deployment inspect replay evidence",
view: "demo-lifecycle",
beats: [
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" }),
sceneBeat("discover", "Discover capabilities", "The later issue-review example inspects configured sources, capabilities, and schemas.", { chatMode: "hidden", chatTheme: "light" }),
sceneBeat("draft", "Author draft", "Create and edit a Draft for report generation before execution.", { chatMode: "hidden", chatTheme: "light" }),
sceneBeat("validate", "Validate and repair", "Validate incomplete state, diagnose a missing binding, and apply a targeted repair.", { chatMode: "hidden", chatTheme: "light" }),
sceneBeat("artifact", "Compile artifact", "Save the validated plan as an immutable artifact.", { chatMode: "hidden", chatTheme: "light" }),
sceneBeat("deployment", "Deploy and validate", "Bind and validate a ready Deployment; the run begins in Scene 10. This implementation extension is richer than the thesis three-node case study.", { chatMode: "hidden", chatTheme: "light" }),
],
},
{
@@ -231,8 +231,8 @@ export const mainScenes = defineScenes([
view: "evaluation",
beats: [
sceneBeat("cohort", "36-trial cohort", "Two challenges, two hosted models, three profiles, and three waves.", { chatMode: "hidden" }),
sceneBeat("validity", "Bounded validity", "Manual audit separates task completion from valid product-surface evidence.", { chatMode: "hidden" }),
sceneBeat("findings", "Longitudinal findings", "Trials exposed concrete authoring and diagnostic UX gaps.", { chatMode: "hidden" }),
sceneBeat("validity", "Bounded validity", "Author audit: 27 pass, 8 invalid, and 1 fail; task completion is separate from valid product-surface evidence.", { chatMode: "hidden" }),
sceneBeat("findings", "Longitudinal findings", "Changing prompts and product snapshots make this engineering evidence, not a model benchmark.", { chatMode: "hidden" }),
],
},
{