feat: project prepared authoring conversation

This commit is contained in:
lda
2026-07-11 06:41:26 +07:00 Verified
parent 4f41f84005
commit 01c52ea9bc
3 changed files with 198 additions and 32 deletions
+7 -1
View File
@@ -2,7 +2,8 @@ export type WorkflowToolName =
| "inspectDeployment"
| "startRun"
| "resumeIssueReview"
| "readRunTrace";
| "readRunTrace"
| "runWorkflowCommand";
export type PresentationToolName =
| "selectWorkflowNode"
@@ -41,6 +42,11 @@ export const AGENT_TOOLS = {
kind: "workflow",
description: "Read trace frames for the completed report run.",
},
runWorkflowCommand: {
name: "runWorkflowCommand",
kind: "workflow",
description: "Run one public wf CLI operation in the prepared authoring trace.",
},
selectWorkflowNode: {
name: "selectWorkflowNode",
kind: "presentation",
@@ -1,7 +1,9 @@
import { describe, expect, it } from "vitest";
import {
authoringPhaseForBeat,
authoringToolGroupId,
projectPreparedAuthoring,
projectPreparedAuthoringThread,
type AuthoringPhaseId,
} from "./authoring-recording.js";
@@ -69,14 +71,57 @@ describe("projectPreparedAuthoring", () => {
const phases = projectPreparedAuthoring();
for (const phase of phases) {
for (const cmd of phase.commands) {
expect(cmd.command.startsWith("wf") || cmd.command.startsWith("uv run wf")).toBe(true);
expect(cmd.command).toMatch(/^(uv run )?wf(\s|$)/);
}
}
});
it("uses the public CLI command families for each authoring phase", () => {
const commands = projectPreparedAuthoring()
.flatMap((phase) => phase.commands)
.map((command) => command.command);
expect(commands.some((command) => command === "wf source list")).toBe(true);
expect(commands.some((command) => command === "wf cap list --source local.lda_report --format ids")).toBe(true);
expect(commands.some((command) => command === "wf cap inspect local.lda_report.analyze_documents")).toBe(true);
expect(commands.some((command) => command === "wf schema")).toBe(true);
expect(commands.some((command) => command.startsWith("wf draft add-step lda_report_workflow"))).toBe(true);
expect(commands.some((command) => command === "wf draft validate lda_report_workflow")).toBe(true);
expect(commands.some((command) => command === "wf draft compile lda_report_workflow")).toBe(true);
expect(commands.some((command) => command === "wf artifact inspect lda_report_case_study --version 1")).toBe(true);
expect(commands.some((command) => command.startsWith("wf deploy save lda_report_case_study.default"))).toBe(true);
expect(commands.some((command) => command === "wf deploy validate lda_report_case_study.default")).toBe(true);
});
it("has unique beat IDs for each phase", () => {
const phases = projectPreparedAuthoring();
const ids = phases.map((p) => p.beatId);
expect(new Set(ids).size).toBe(ids.length);
});
});
describe("projectPreparedAuthoringThread", () => {
it("keeps stable message ids while revealing later phases", () => {
const draft = projectPreparedAuthoringThread("draft");
const deployment = projectPreparedAuthoringThread("deployment");
expect(draft.map(({ id }) => id)).toEqual(
deployment.slice(0, draft.length).map(({ id }) => id),
);
});
it("projects literal commands as paired workflow tool calls and results", () => {
const messages = projectPreparedAuthoringThread("discover");
const parts = messages.flatMap(({ parts }) => parts);
const calls = parts.filter((part) => part.type === "tool-call");
const results = parts.filter((part) => part.type === "tool-result");
expect(calls).toHaveLength(projectPreparedAuthoring()[0]!.commands.length);
expect(results).toHaveLength(calls.length);
expect(calls.every((part) => part.call.name === "runWorkflowCommand")).toBe(true);
});
it("uses a stable phase tool-group id", () => {
expect(authoringToolGroupId("validate")).toBe("authoring-validate");
});
});
@@ -7,6 +7,13 @@
* presentation evidence, not live RPC responses.
*/
import {
agentTextMessage,
agentToolCallPart,
agentToolResultPart,
type AgentMessage,
} from "../../demo/agent/events.js";
export type AuthoringPhaseId =
| "discover"
| "draft"
@@ -34,6 +41,8 @@ export type PreparedAuthoringPhase = {
readonly label: string;
readonly commands: readonly PreparedAuthoringCommand[];
readonly conversation: readonly AuthoringConversationTurn[];
/** Compact factual projection for Scene 9; command detail stays in the trace panel. */
readonly proof: readonly string[];
};
const unknownPhase = (phase: never): never => {
@@ -64,19 +73,25 @@ const recording: readonly PreparedAuthoringPhase[] = [
command: "wf source list",
summary: "List available capability sources",
result: "success",
detail: "2 sources: local (file:///.providers), docs (file:///docs)",
detail: "6 sources: local.lda_docs, local.lda_report, local.issue_board, and platform helpers.",
},
{
command: "wf cap inspect",
summary: "Inspect declared capabilities",
command: "wf cap list --source local.lda_report --format ids",
summary: "List report workflow capabilities",
result: "success",
detail: "6 capabilities: read_source, create_draft, add_step, bind_source, validate_graph, compile_artifact",
detail: "5 capabilities: analyze_documents, build_report, create_issue_drafts, finalise_report, record_revision_request.",
},
{
command: "wf schema list",
command: "wf cap inspect local.lda_report.analyze_documents",
summary: "Inspect the report-analysis contract",
result: "success",
detail: "Input: documents. Output: analysis. Declared outcome: ok.",
},
{
command: "wf schema",
summary: "List registered workflow schemas",
result: "success",
detail: "3 schemas: [email protected], [email protected], [email protected]",
detail: "WorkflowDraft, RawWorkflowPlan, and NodeUse are available public shapes.",
},
],
conversation: [
@@ -89,6 +104,11 @@ const recording: readonly PreparedAuthoringPhase[] = [
text: "Let me inspect the available sources, capabilities, and schemas to understand what we can work with.",
},
],
proof: [
"local.lda_docs",
"local.lda_report",
"documents input → analysis output",
],
},
{
phase: "draft",
@@ -96,21 +116,21 @@ const recording: readonly PreparedAuthoringPhase[] = [
label: "Draft",
commands: [
{
command: "wf draft create lda_report_workflow",
command: "wf draft create lda_report_workflow --capability local.lda_docs.read_documents",
summary: "Create a new workflow draft",
result: "success",
detail: "Draft 'lda_report_workflow' created with ID draft_a1b2c3",
},
{
command: "wf add-step --id report --action generate_report --source lda_docs",
summary: "Add a report generation step",
command: "wf draft add-step lda_report_workflow --revision 1 --step analyze --capability local.lda_report.analyze_documents --from-step read_documents --from-outcome ok --route ok=__end__ --input state.documents=documents",
summary: "Add the report-analysis step with a declared route",
result: "success",
},
{
command: "wf route list",
summary: "List routes in the draft graph",
command: "wf draft inspect lda_report_workflow --include-draft",
summary: "Inspect the prepared draft graph",
result: "success",
detail: "2 routes: start->report, report->end",
detail: "Draft contains read_documents followed by analyze with declared ok routing.",
},
],
conversation: [
@@ -119,6 +139,11 @@ const recording: readonly PreparedAuthoringPhase[] = [
text: "I found the available capabilities. Now I'll create a workflow draft with the report generation step and configure its routes.",
},
],
proof: [
"read_documents → analyze",
"analyze.ok → __end__",
"state.documents → documents",
],
},
{
phase: "validate",
@@ -126,16 +151,16 @@ const recording: readonly PreparedAuthoringPhase[] = [
label: "Validate",
commands: [
{
command: "wf bind --source lda_docs --capability read_source",
summary: "Bind source to capability",
result: "success",
detail: "Binding established: lda_docs->read_source",
},
{
command: "wf validate draft_a1b2c3",
command: "wf draft validate lda_report_workflow",
summary: "Validate the workflow draft",
result: "diagnostic",
detail: "Diagnostic: Unbound step 'report' — missing output destination. Repair applied: connected report output to end. Status: repaired.",
detail: "Diagnostic: analyze output 'analysis' has no state projection.",
},
{
command: "wf draft set-output lda_report_workflow --revision 2 --step analyze --map analysis=state.analysis",
summary: "Repair the missing output binding",
result: "success",
detail: "Added analysis -> state.analysis. A follow-up draft validate reports a valid draft.",
},
],
conversation: [
@@ -148,6 +173,11 @@ const recording: readonly PreparedAuthoringPhase[] = [
text: "Running diagnosis now. I'll bind the source and repair any issues found in the draft graph.",
},
],
proof: [
"analysis → state.analysis",
"diagnostic resolved",
"validated draft",
],
},
{
phase: "artifact",
@@ -155,24 +185,35 @@ const recording: readonly PreparedAuthoringPhase[] = [
label: "Artifact",
commands: [
{
command: "wf compile draft_a1b2c3",
summary: "Compile draft into an immutable artifact",
command: "wf draft compile lda_report_workflow",
summary: "Compile the validated draft without mutating it",
result: "success",
detail: "Compiled artifact art_x9y8z7 (version 1). 3 nodes, 2 edges. Schema: [email protected]",
detail: "Compiled raw plan requires local.lda_docs, local.lda_report, and local.issue_board.",
},
{
command: "wf artifact inspect art_x9y8z7",
command: "wf draft save lda_report_workflow --artifact lda_report_case_study --version 1 --title \"lda.chat Report Case Study\" --outcome completed --outcome cancelled --binding local.lda_docs=local.lda_docs --binding local.lda_report=local.lda_report --binding local.issue_board=local.issue_board",
summary: "Save immutable artifact version 1",
result: "success",
detail: "Artifact lda_report_case_study v1 saved with report-workflow source requirements.",
},
{
command: "wf artifact inspect lda_report_case_study --version 1",
summary: "Inspect the compiled artifact",
result: "success",
detail: "Artifact art_x9y8z7 v1 — immutable. Created: 2026-07-11. Nodes: 3. Routes: 2.",
detail: "Artifact lda_report_case_study v1 — immutable workflow definition.",
},
],
conversation: [
{
role: "assistant",
text: "The draft is valid and repaired. Let me compile it into an immutable artifact and inspect the result.",
text: "The draft is valid and repaired. I compiled and saved immutable artifact lda_report_case_study version 1.",
},
],
proof: [
"lda_report_case_study v1",
"immutable artifact",
"3 required local sources",
],
},
{
phase: "deployment",
@@ -180,16 +221,16 @@ const recording: readonly PreparedAuthoringPhase[] = [
label: "Deployment",
commands: [
{
command: "wf deployment save --artifact art_x9y8z7 --bind lda_docs:local.lda_docs",
command: "wf deploy save lda_report_case_study.default --artifact lda_report_case_study --version 1 --binding local.lda_docs=local.lda_docs --binding local.lda_report=local.lda_report --binding local.issue_board=local.issue_board",
summary: "Save deployment with source bindings",
result: "success",
detail: "Deployment dep_m4n5p6 saved. Binding: lda_docs -> local.lda_docs",
detail: "Deployment lda_report_case_study.default saved with all three local source bindings.",
},
{
command: "wf deployment validate dep_m4n5p6",
command: "wf deploy validate lda_report_case_study.default",
summary: "Validate deployment bindings",
result: "success",
detail: "Deployment dep_m4n5p6 valid — all 1 bindings resolved. Ready for run.",
detail: "Deployment lda_report_case_study.default valid — ready for a persisted run.",
},
],
conversation: [
@@ -202,8 +243,82 @@ const recording: readonly PreparedAuthoringPhase[] = [
text: "Saving the deployment with the source bindings and validating that everything is correctly configured.",
},
],
proof: [
"lda_report_case_study.default",
"local.lda_docs=local.lda_docs",
"local.lda_report=local.lda_report",
"local.issue_board=local.issue_board",
],
},
];
export const authoringToolGroupId = (phase: AuthoringPhaseId): string =>
`authoring-${phase}`;
/**
* Projects the prepared recording into one stable assistant transcript.
*
* IDs depend only on phase and command position so the full Scene 8 thread and
* compact Scene 9 dock retain DOM/message identity while later phases appear.
*/
export const projectPreparedAuthoringThread = (
throughPhase: AuthoringPhaseId = "deployment",
): readonly AgentMessage[] => {
const finalPhaseIndex = recording.findIndex(({ phase }) => phase === throughPhase);
if (finalPhaseIndex < 0) throw new Error(`unknown phase: ${throughPhase}`);
return recording.slice(0, finalPhaseIndex + 1).flatMap((phase) => {
const conversation = phase.conversation.map((turn, index) =>
agentTextMessage(`authoring-${phase.phase}-message-${index}`, turn.role, turn.text),
);
const groupId = authoringToolGroupId(phase.phase);
const toolParts = phase.commands.flatMap((command, index) => {
const callId = `${groupId}-command-${index}`;
return [
agentToolCallPart(callId, "runWorkflowCommand", {
phase: phase.phase,
command: command.command,
summary: command.summary,
}),
agentToolResultPart(callId, "runWorkflowCommand", "success", {
status: command.result,
detail: command.detail ?? null,
}),
];
});
return [
...conversation,
{
id: `${groupId}-tools`,
role: "assistant" as const,
parts: toolParts,
},
];
});
};
const handoffConversation: readonly AuthoringConversationTurn[] = [
{
role: "user",
text: "We need to prepare a report workflow for the lda_report scenario. What sources and capabilities are available?",
},
{
role: "assistant",
text: "I found the local document, report, and issue-board capabilities. I can author the reusable workflow through the public wf CLI.",
},
{
role: "user",
text: "Keep the workflow inspectable and validate the source bindings before it runs.",
},
{
role: "assistant",
text: "The prepared workflow is ready for deployment: I will create the draft, repair validation diagnostics, save artifact lda_report_case_study v1, then validate its deployment bindings.",
},
];
/** Returns the full prepared authoring recording. */
export const projectPreparedAuthoring = (): readonly PreparedAuthoringPhase[] => recording;
/** Returns the compact Scene 8 handoff derived from the prepared authoring evidence. */
export const projectPreparedAuthoringHandoff = (): readonly AuthoringConversationTurn[] => handoffConversation;