feat: project demo lifecycle facts

This commit is contained in:
lda
2026-07-09 21:20:56 +07:00 Verified
parent aae14768de
commit 632d3a04eb
2 changed files with 139 additions and 0 deletions
@@ -0,0 +1,58 @@
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 = (): DemoTimelineController => {
const recording = loadCanonicalDemoRecording();
return {
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(),
};
};
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");
});
});
@@ -0,0 +1,81 @@
import type { DemoTimelineController } from "../demo/useDemoTimeline.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) =>
demo.state.events.find((event) => event.stage === "deployment_check");
const runStart = (demo: DemoTimelineController) =>
demo.state.events.find((event) => event.stage === "run_start");
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 = deployment?.interpreted as
| {
id?: string;
artifactId?: string;
artifactVersion?: number;
driftPolicy?: string;
bindings?: unknown;
}
| undefined;
const run = runStart(demo);
const runInterpreted = run?.interpreted as
| { runId?: string; status?: string }
| undefined;
return {
draft: {
label: "lda report workflow",
source: "examples/lda_report_workflow",
status: "prepared context",
},
artifact: {
id: deploymentInterpreted?.artifactId ?? "unavailable",
version: typeof deploymentInterpreted?.artifactVersion === "number"
? deploymentInterpreted.artifactVersion
: null,
},
deployment: {
id: deploymentInterpreted?.id ?? "unavailable",
driftPolicy: deploymentInterpreted?.driftPolicy ?? "unavailable",
bindings: readBindings(deploymentInterpreted?.bindings),
},
run: {
id: runInterpreted?.runId ?? run?.resultingIds.runId ?? null,
status: runInterpreted?.status ?? "not started",
},
};
};