fix: use factual prepared validation evidence

This commit is contained in:
lda
2026-07-13 21:43:37 +07:00 Verified
parent 47161eade7
commit 6a7838e2fa
7 changed files with 49 additions and 26 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ export type PreparedAuthoringToolName =
| "workflow.draft_workspaces.add_step_from_capability" | "workflow.draft_workspaces.add_step_from_capability"
| "workflow.draft_workspaces.get" | "workflow.draft_workspaces.get"
| "workflow.draft_workspaces.validate" | "workflow.draft_workspaces.validate"
| "workflow.draft_workspaces.set_step_output_map" | "workflow.draft_workspaces.set_route"
| "workflow.draft_workspaces.compile" | "workflow.draft_workspaces.compile"
| "workflow.draft_workspaces.create_artifact" | "workflow.draft_workspaces.create_artifact"
| "workflow.artifacts.inspect" | "workflow.artifacts.inspect"
@@ -51,9 +51,9 @@ describe("projectPreparedAuthoringPhase", () => {
it("keeps the validation diagnostic and repair command distinct", () => { it("keeps the validation diagnostic and repair command distinct", () => {
const phase = projectPreparedAuthoringPhase("validate"); const phase = projectPreparedAuthoringPhase("validate");
const diagnosticCmd = phase.commands.find( const diagnosticCmd = phase.commands.find(
(cmd) => cmd.result === "diagnostic" && cmd.detail?.includes("no state projection"), (cmd) => cmd.result === "diagnostic" && cmd.detail?.includes("missing_outcome_edge"),
); );
const repairCmd = phase.commands.find((cmd) => cmd.command.includes("draft set-output")); const repairCmd = phase.commands.find((cmd) => cmd.command.includes("draft set-route"));
expect(diagnosticCmd).toBeDefined(); expect(diagnosticCmd).toBeDefined();
expect(repairCmd).toBeDefined(); expect(repairCmd).toBeDefined();
expect(diagnosticCmd).not.toBe(repairCmd); expect(diagnosticCmd).not.toBe(repairCmd);
@@ -78,7 +78,11 @@ describe("projectPreparedAuthoringPhase", () => {
expect(diagnose.focus).toBe("diagnose"); expect(diagnose.focus).toBe("diagnose");
expect(repair.focus).toBe("repair"); expect(repair.focus).toBe("repair");
expect(diagnose.primaryCommand.title).toBe("workflow.draft_workspaces.validate"); expect(diagnose.primaryCommand.title).toBe("workflow.draft_workspaces.validate");
expect(repair.primaryCommand.title).toBe("workflow.draft_workspaces.set_step_output_map"); expect(diagnose.primaryCommand.detail).toContain("missing_outcome_edge");
expect(repair.primaryCommand.title).toBe("workflow.draft_workspaces.set_route");
expect(repair.primaryCommand.command).toContain("--revision 3 --step analyze --outcome ok --to __end__");
expect(diagnose.evidence.kind).toBe("diagnostic");
expect(repair.evidence.kind).toBe("repair");
}); });
it("projects six presentation steps from five recorded phases", () => { it("projects six presentation steps from five recorded phases", () => {
@@ -1,4 +1,8 @@
import { projectPreparedAuthoring, type AuthoringPhaseId, type PreparedAuthoringCommand } from "./authoring-recording.js"; import { projectPreparedAuthoring, type AuthoringPhaseId, type PreparedAuthoringCommand } from "./authoring-recording.js";
import {
reviewedAuthoringEvidenceFor,
type ReviewedAuthoringEvidence,
} from "./reviewed-authoring-evidence.js";
export type PreparedLifecycleStepId = export type PreparedLifecycleStepId =
| "discover" | "discover"
@@ -23,6 +27,7 @@ export type PreparedLifecycleStepProjection = AuthoringPhaseProjection & {
readonly recordingPhase: AuthoringPhaseId; readonly recordingPhase: AuthoringPhaseId;
readonly focus: "full" | "diagnose" | "repair"; readonly focus: "full" | "diagnose" | "repair";
readonly primaryCommand: PreparedAuthoringCommand; readonly primaryCommand: PreparedAuthoringCommand;
readonly evidence: ReviewedAuthoringEvidence;
}; };
export type AuthoringPhaseVisualModel = export type AuthoringPhaseVisualModel =
@@ -74,12 +79,19 @@ const visualForPhase = (phase: AuthoringPhaseId): AuthoringPhaseVisualModel => {
inputBinding: "state.documents → documents", inputBinding: "state.documents → documents",
}; };
case "validate": case "validate":
return { {
kind: "repair", const diagnostic = reviewedAuthoringEvidenceFor("diagnose");
diagnostic: "analysis has no state projection", const repair = reviewedAuthoringEvidenceFor("repair");
correction: "analysis → state.analysis", if (diagnostic.kind !== "diagnostic" || repair.kind !== "repair") {
status: "Valid draft", throw new Error("reviewed validation evidence has an unexpected shape");
}; }
return {
kind: "repair",
diagnostic: `${diagnostic.diagnostic.code} at ${diagnostic.diagnostic.path}: ${diagnostic.diagnostic.message}`,
correction: repair.command,
status: `Revision ${repair.toRevision}: ${repair.status}`,
};
}
case "artifact": case "artifact":
return { return {
kind: "artifact", kind: "artifact",
@@ -139,6 +151,7 @@ export const projectPreparedLifecycleStep = (
): PreparedLifecycleStepProjection => { ): PreparedLifecycleStepProjection => {
const recordingPhase = recordingPhaseForStep(step); const recordingPhase = recordingPhaseForStep(step);
const phase = projectPreparedAuthoringPhase(recordingPhase); const phase = projectPreparedAuthoringPhase(recordingPhase);
const evidence = reviewedAuthoringEvidenceFor(step);
// Diagnose and repair are presentation choreography over one factual // Diagnose and repair are presentation choreography over one factual
// recording phase; they select distinct evidence without duplicating it. // recording phase; they select distinct evidence without duplicating it.
const commandIndex = step === "repair" ? 1 : 0; const commandIndex = step === "repair" ? 1 : 0;
@@ -155,5 +168,6 @@ export const projectPreparedLifecycleStep = (
recordingPhase, recordingPhase,
focus: step === "diagnose" || step === "repair" ? step : "full", focus: step === "diagnose" || step === "repair" ? step : "full",
primaryCommand, primaryCommand,
evidence,
}; };
}; };
@@ -81,7 +81,7 @@ describe("projectPreparedAuthoring", () => {
"workflow.draft_workspaces.add_step_from_capability", "workflow.draft_workspaces.add_step_from_capability",
"workflow.draft_workspaces.get", "workflow.draft_workspaces.get",
"workflow.draft_workspaces.validate", "workflow.draft_workspaces.validate",
"workflow.draft_workspaces.set_step_output_map", "workflow.draft_workspaces.set_route",
"workflow.draft_workspaces.compile", "workflow.draft_workspaces.compile",
"workflow.draft_workspaces.create_artifact", "workflow.draft_workspaces.create_artifact",
"workflow.artifacts.inspect", "workflow.artifacts.inspect",
@@ -110,6 +110,7 @@ describe("projectPreparedAuthoring", () => {
expect(commands.some((command) => command === "wf schema")).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.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 validate lda_report_workflow")).toBe(true);
expect(commands.some((command) => command === "wf draft set-route lda_report_workflow --revision 3 --step analyze --outcome ok --to __end__")).toBe(true);
expect(commands.some((command) => command === "wf draft compile 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 === "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.startsWith("wf deploy save lda_report_case_study.default"))).toBe(true);
@@ -77,7 +77,7 @@ const recording: readonly PreparedAuthoringPhase[] = [
command: "wf source list", command: "wf source list",
summary: "List available capability sources", summary: "List available capability sources",
result: "success", result: "success",
detail: "6 sources: local.lda_docs, local.lda_report, local.issue_board, and platform helpers.", detail: "Inventory: 6 total sources; configured local sources: local.lda_docs, local.lda_report, local.issue_board; platform helpers are also available.",
}, },
{ {
title: "workflow.capabilities.list", title: "workflow.capabilities.list",
@@ -165,14 +165,14 @@ const recording: readonly PreparedAuthoringPhase[] = [
command: "wf draft validate lda_report_workflow", command: "wf draft validate lda_report_workflow",
summary: "Validate the workflow draft", summary: "Validate the workflow draft",
result: "diagnostic", result: "diagnostic",
detail: "Diagnostic: analyze output 'analysis' has no state projection.", detail: "missing_outcome_edge at nodes[analyze]: reachable node is missing edges for outcomes ['ok']",
}, },
{ {
title: "workflow.draft_workspaces.set_step_output_map", title: "workflow.draft_workspaces.set_route",
command: "wf draft set-output lda_report_workflow --revision 2 --step analyze --map analysis=state.analysis", command: "wf draft set-route lda_report_workflow --revision 3 --step analyze --outcome ok --to __end__",
summary: "Repair the missing output binding", summary: "Restore the missing terminal route",
result: "success", result: "success",
detail: "Added analysis -> state.analysis. A follow-up draft validate reports a valid draft.", detail: "Revision 4 validates with status valid and diagnostics [].",
}, },
], ],
conversation: [ conversation: [
@@ -186,9 +186,9 @@ const recording: readonly PreparedAuthoringPhase[] = [
}, },
], ],
proof: [ proof: [
"analysis → state.analysis", "missing_outcome_edge",
"diagnostic resolved", "analyze.ok -> __end__",
"validated draft", "revision 4: valid",
], ],
}, },
{ {
@@ -91,8 +91,12 @@ describe("defense storyboard catalog", () => {
it("defines the lifecycle story beats before run evidence", () => { it("defines the lifecycle story beats before run evidence", () => {
expect(findBeat("prepared-lifecycle", "discover")?.caption).toMatch(/sources|capabilities|schemas/i); expect(findBeat("prepared-lifecycle", "discover")?.caption).toMatch(/sources|capabilities|schemas/i);
expect(findBeat("prepared-lifecycle", "draft")?.caption).toMatch(/draft/i); expect(findBeat("prepared-lifecycle", "draft")?.caption).toMatch(/draft/i);
expect(findBeat("prepared-lifecycle", "diagnose")?.caption).toMatch(/diagnostic|missing-output/i); expect(findBeat("prepared-lifecycle", "diagnose")?.caption).toBe(
expect(findBeat("prepared-lifecycle", "repair")?.caption).toMatch(/repair|valid Draft/i); "Validation returns a structured diagnostic because analyze has no route for its ok outcome.",
);
expect(findBeat("prepared-lifecycle", "repair")?.caption).toBe(
"One route edit sends analyze.ok to __end__; the follow-up validation is valid.",
);
expect(findBeat("prepared-lifecycle", "artifact")?.caption).toMatch(/compile|artifact/i); expect(findBeat("prepared-lifecycle", "artifact")?.caption).toMatch(/compile|artifact/i);
expect(findBeat("prepared-lifecycle", "deployment")?.caption).toMatch(/deploy|bindings/i); expect(findBeat("prepared-lifecycle", "deployment")?.caption).toMatch(/deploy|bindings/i);
}); });
@@ -102,8 +106,8 @@ describe("defense storyboard catalog", () => {
expect(findBeat("lifecycle", "artifact")?.caption).toMatch(/^Artifact is an immutable workflow definition\.$/); 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", "deployment")?.caption).toMatch(/^Deployment binds an artifact version/);
expect(findBeat("lifecycle", "run")?.caption).toMatch(/^Run records one execution/); expect(findBeat("lifecycle", "run")?.caption).toMatch(/^Run records one execution/);
expect(findBeat("prepared-lifecycle", "diagnose")?.caption).toMatch(/diagnostic|missing-output/i); expect(findBeat("prepared-lifecycle", "diagnose")?.caption).toContain("no route for its ok outcome");
expect(findBeat("prepared-lifecycle", "repair")?.caption).toMatch(/repair|valid Draft/i); expect(findBeat("prepared-lifecycle", "repair")?.caption).toContain("analyze.ok to __end__");
expect(findBeat("prepared-lifecycle", "deployment")?.caption).toMatch(/ready|does not run|Scene 9/i); expect(findBeat("prepared-lifecycle", "deployment")?.caption).toMatch(/ready|does not run|Scene 9/i);
expect(findBeat("prepared-lifecycle", "deployment")?.caption).toMatch(/three-node|implementation extension/i); expect(findBeat("prepared-lifecycle", "deployment")?.caption).toMatch(/three-node|implementation extension/i);
}); });
@@ -162,13 +162,13 @@ export const mainScenes = defineScenes([
sceneBeat( sceneBeat(
"diagnose", "diagnose",
"Diagnose invalid draft", "Diagnose invalid draft",
"Validation returns a structured missing-output diagnostic before artifact creation.", "Validation returns a structured diagnostic because analyze has no route for its ok outcome.",
{ chatMode: "hidden", chatTheme: "light" }, { chatMode: "hidden", chatTheme: "light" },
), ),
sceneBeat( sceneBeat(
"repair", "repair",
"Apply targeted repair", "Apply targeted repair",
"A focused output-map edit resolves the diagnostic and produces a valid Draft.", "One route edit sends analyze.ok to __end__; the follow-up validation is valid.",
{ chatMode: "hidden", chatTheme: "light" }, { chatMode: "hidden", chatTheme: "light" },
), ),
sceneBeat("artifact", "Compile artifact", "Save the validated plan as an immutable artifact.", { chatMode: "hidden", chatTheme: "light" }), sceneBeat("artifact", "Compile artifact", "Save the validated plan as an immutable artifact.", { chatMode: "hidden", chatTheme: "light" }),