feat: finish continuous authoring workspace

This commit is contained in:
lda
2026-07-11 08:35:29 +07:00 Verified
parent c76be621d9
commit ce90b6b8a9
13 changed files with 168 additions and 77 deletions
@@ -36,7 +36,7 @@ describe("AgentHandoffScene", () => {
it("interleaves prepared workflow tool groups with the handoff conversation", () => {
renderBeat("handoff");
expect(screen.getAllByText(/runWorkflowCommand/i).length).toBeGreaterThan(0);
expect(screen.getAllByText(/workflow\.deployments\.save/i).length).toBeGreaterThan(0);
expect(screen.getByRole("button", { name: /deployment.*2 tool calls/i }))
.toHaveAttribute("aria-expanded", "true");
});
@@ -16,7 +16,7 @@ describe("AuthoringConversation", () => {
expect(screen.getByRole("log", { name: "prepared authoring conversation" }))
.toHaveAttribute("data-surface", "stage");
expect(screen.getAllByText(/runWorkflowCommand/i).length).toBeGreaterThan(0);
expect(screen.getAllByText(/workflow\.deployments\.save/i).length).toBeGreaterThan(0);
expect(screen.getAllByText(/deployment/i).length).toBeGreaterThan(0);
});
@@ -15,7 +15,7 @@ type AuthoringPhaseVisualProps = {
};
const InventoryVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "inventory" }> }) => (
<section className="authoring-visual authoring-visual--inventory" role="region" aria-label="discovery evidence">
<section className="authoring-visual authoring-visual--inventory" aria-label="discovery evidence">
<div className="authoring-inventory__sources">
{visual.sources.map((source) => (
<div key={source}><Database aria-hidden="true" /><code>{source}</code></div>
@@ -31,7 +31,7 @@ const InventoryVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection[
);
const GraphVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "graph" }> }) => (
<section className="authoring-visual authoring-visual--graph" role="region" aria-label="draft graph evidence">
<section className="authoring-visual authoring-visual--graph" aria-label="draft graph evidence">
<div className="authoring-graph__node"><Database aria-hidden="true" /><strong>{visual.nodes[0]}</strong></div>
<div className="authoring-graph__edge"><span>{visual.inputBinding}</span><ArrowRight aria-hidden="true" /></div>
<div className="authoring-graph__node"><Workflow aria-hidden="true" /><strong>{visual.nodes[1]}</strong></div>
@@ -41,7 +41,7 @@ const GraphVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["vis
);
const RepairVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "repair" }> }) => (
<section className="authoring-visual authoring-visual--repair" role="region" aria-label="validation repair evidence">
<section className="authoring-visual authoring-visual--repair" aria-label="validation repair evidence">
<div className="authoring-repair__diagnostic"><AlertTriangle aria-hidden="true" /><span>Diagnostic</span><strong>{visual.diagnostic}</strong></div>
<ArrowRight aria-hidden="true" />
<div className="authoring-repair__correction"><Link2 aria-hidden="true" /><span>Repair</span><code>{visual.correction}</code></div>
@@ -50,7 +50,7 @@ const RepairVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["vi
);
const ArtifactVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "artifact" }> }) => (
<section className="authoring-visual authoring-visual--artifact" role="region" aria-label="artifact evidence">
<section className="authoring-visual authoring-visual--artifact" aria-label="artifact evidence">
<LockKeyhole aria-hidden="true" />
<div><span>Immutable workflow artifact</span><strong>{visual.artifactId}</strong></div>
<dl><div><dt>Version</dt><dd>Version {visual.version}</dd></div><div><dt>Requirements</dt><dd>{visual.requiredSources} local sources</dd></div></dl>
@@ -58,7 +58,7 @@ const ArtifactVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["
);
const BindingsVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "bindings" }> }) => (
<section className="authoring-visual authoring-visual--bindings" role="region" aria-label="deployment binding evidence">
<section className="authoring-visual authoring-visual--bindings" aria-label="deployment binding evidence">
<header><Link2 aria-hidden="true" /><div><span>Deployment</span><strong>{visual.deploymentId}</strong></div><b><CheckCircle2 aria-hidden="true" />{visual.status}</b></header>
<div className="authoring-bindings__rows">
{visual.bindings.map((binding) => (
@@ -67,6 +67,29 @@ describe("projectPreparedAuthoring", () => {
}
});
it("labels each prepared command with its actual operation name", () => {
const titles = projectPreparedAuthoring()
.flatMap((phase) => phase.commands)
.map((command) => command.title);
expect(titles).toEqual([
"workflow.sources.list",
"workflow.capabilities.list",
"workflow.capabilities.inspect",
"wf schema",
"workflow.draft_workspaces.create_from_capability",
"workflow.draft_workspaces.add_step_from_capability",
"workflow.draft_workspaces.get",
"workflow.draft_workspaces.validate",
"workflow.draft_workspaces.set_step_output_map",
"workflow.draft_workspaces.compile",
"workflow.draft_workspaces.create_artifact",
"workflow.artifacts.inspect",
"workflow.deployments.save",
"workflow.deployments.validate",
]);
});
it("starts every command with wf or uv run wf", () => {
const phases = projectPreparedAuthoring();
for (const phase of phases) {
@@ -118,7 +141,12 @@ describe("projectPreparedAuthoringThread", () => {
expect(calls).toHaveLength(projectPreparedAuthoring()[0]!.commands.length);
expect(results).toHaveLength(calls.length);
expect(calls.every((part) => part.call.name === "runWorkflowCommand")).toBe(true);
expect(calls.map((part) => part.call.name)).toEqual([
"workflow.sources.list",
"workflow.capabilities.list",
"workflow.capabilities.inspect",
"wf schema",
]);
});
it("uses a stable phase tool-group id", () => {
@@ -13,6 +13,7 @@ import {
agentToolResultPart,
type AgentMessage,
} from "../../demo/agent/events.js";
import type { PreparedAuthoringToolName } from "../../demo/agent/tools.js";
export type AuthoringPhaseId =
| "discover"
@@ -24,6 +25,8 @@ export type AuthoringPhaseId =
export type CommandResult = "success" | "diagnostic";
export type PreparedAuthoringCommand = {
/** JSON-RPC method, or the explicit local CLI label for non-RPC commands. */
readonly title: PreparedAuthoringToolName;
readonly command: string;
readonly summary: string;
readonly result: CommandResult;
@@ -70,24 +73,28 @@ const recording: readonly PreparedAuthoringPhase[] = [
label: "Discover",
commands: [
{
title: "workflow.sources.list",
command: "wf source list",
summary: "List available capability sources",
result: "success",
detail: "6 sources: local.lda_docs, local.lda_report, local.issue_board, and platform helpers.",
},
{
title: "workflow.capabilities.list",
command: "wf cap list --source local.lda_report --format ids",
summary: "List report workflow capabilities",
result: "success",
detail: "5 capabilities: analyze_documents, build_report, create_issue_drafts, finalise_report, record_revision_request.",
},
{
title: "workflow.capabilities.inspect",
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.",
},
{
title: "wf schema",
command: "wf schema",
summary: "List registered workflow schemas",
result: "success",
@@ -116,17 +123,20 @@ const recording: readonly PreparedAuthoringPhase[] = [
label: "Draft",
commands: [
{
title: "workflow.draft_workspaces.create_from_capability",
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",
},
{
title: "workflow.draft_workspaces.add_step_from_capability",
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",
},
{
title: "workflow.draft_workspaces.get",
command: "wf draft inspect lda_report_workflow --include-draft",
summary: "Inspect the prepared draft graph",
result: "success",
@@ -151,12 +161,14 @@ const recording: readonly PreparedAuthoringPhase[] = [
label: "Validate",
commands: [
{
title: "workflow.draft_workspaces.validate",
command: "wf draft validate lda_report_workflow",
summary: "Validate the workflow draft",
result: "diagnostic",
detail: "Diagnostic: analyze output 'analysis' has no state projection.",
},
{
title: "workflow.draft_workspaces.set_step_output_map",
command: "wf draft set-output lda_report_workflow --revision 2 --step analyze --map analysis=state.analysis",
summary: "Repair the missing output binding",
result: "success",
@@ -185,18 +197,21 @@ const recording: readonly PreparedAuthoringPhase[] = [
label: "Artifact",
commands: [
{
title: "workflow.draft_workspaces.compile",
command: "wf draft compile lda_report_workflow",
summary: "Compile the validated draft without mutating it",
result: "success",
detail: "Compiled raw plan requires local.lda_docs, local.lda_report, and local.issue_board.",
},
{
title: "workflow.draft_workspaces.create_artifact",
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.",
},
{
title: "workflow.artifacts.inspect",
command: "wf artifact inspect lda_report_case_study --version 1",
summary: "Inspect the compiled artifact",
result: "success",
@@ -221,12 +236,14 @@ const recording: readonly PreparedAuthoringPhase[] = [
label: "Deployment",
commands: [
{
title: "workflow.deployments.save",
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 lda_report_case_study.default saved with all three local source bindings.",
},
{
title: "workflow.deployments.validate",
command: "wf deploy validate lda_report_case_study.default",
summary: "Validate deployment bindings",
result: "success",
@@ -275,12 +292,12 @@ export const projectPreparedAuthoringThread = (
const toolParts = phase.commands.flatMap((command, index) => {
const callId = `${groupId}-command-${index}`;
return [
agentToolCallPart(callId, "runWorkflowCommand", {
agentToolCallPart(callId, command.title, {
phase: phase.phase,
command: command.command,
summary: command.summary,
}),
agentToolResultPart(callId, "runWorkflowCommand", "success", {
agentToolResultPart(callId, command.title, "success", {
status: command.result,
detail: command.detail ?? null,
}),
@@ -298,27 +315,5 @@ export const projectPreparedAuthoringThread = (
});
};
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;