feat: render Scene 8 product evidence
This commit is contained in:
@@ -1,88 +1,102 @@
|
|||||||
import { cleanup, render, screen } from "@testing-library/react";
|
import { cleanup, render, screen, within } from "@testing-library/react";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import { projectPreparedAuthoringPhase } from "./authoring-projection.js";
|
import {
|
||||||
|
projectPreparedLifecycleStep,
|
||||||
|
type PreparedLifecycleStepId,
|
||||||
|
} from "./authoring-projection.js";
|
||||||
import { AuthoringPhaseVisual } from "./AuthoringPhaseVisual.js";
|
import { AuthoringPhaseVisual } from "./AuthoringPhaseVisual.js";
|
||||||
|
|
||||||
afterEach(cleanup);
|
afterEach(cleanup);
|
||||||
|
|
||||||
|
const renderStep = (step: PreparedLifecycleStepId) =>
|
||||||
|
render(<AuthoringPhaseVisual projection={projectPreparedLifecycleStep(step)} />);
|
||||||
|
|
||||||
describe("AuthoringPhaseVisual", () => {
|
describe("AuthoringPhaseVisual", () => {
|
||||||
it.each(["discover", "draft", "validate", "artifact", "deployment"] as const)(
|
it.each([
|
||||||
"marks the %s visual as editorial",
|
["discover", "inventory", "source inventory result"],
|
||||||
(phase) => {
|
["draft", "draft", "draft structure result"],
|
||||||
render(<AuthoringPhaseVisual projection={projectPreparedAuthoringPhase(phase)} />);
|
["diagnose", "diagnostic", "draft validation diagnostic"],
|
||||||
expect(screen.getByRole("region", { name: /evidence/i })).toHaveAttribute(
|
["repair", "repair", "route repair result"],
|
||||||
"data-presentation-surface",
|
["artifact", "artifact", "immutable artifact result"],
|
||||||
"editorial",
|
["deployment", "deployment", "runnable deployment result"],
|
||||||
);
|
] as const)("renders %s as a factual %s result", (step, kind, label) => {
|
||||||
},
|
renderStep(step);
|
||||||
);
|
expect(screen.getByRole("region", { name: label })).toHaveAttribute(
|
||||||
|
"data-authoring-result",
|
||||||
it("shows source inventory and the inspected contract", () => {
|
kind,
|
||||||
render(<AuthoringPhaseVisual projection={projectPreparedAuthoringPhase("discover")} />);
|
|
||||||
expect(screen.getByRole("region", { name: /discovery evidence/i })).toHaveAttribute(
|
|
||||||
"data-presentation-surface",
|
|
||||||
"editorial",
|
|
||||||
);
|
|
||||||
expect(screen.getByText("local.lda_docs")).toBeInTheDocument();
|
|
||||||
expect(screen.getByText("local.lda_report")).toBeInTheDocument();
|
|
||||||
expect(screen.getByText(/documents.*analysis/i)).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("shows the declared draft graph and outcome route", () => {
|
|
||||||
render(<AuthoringPhaseVisual projection={projectPreparedAuthoringPhase("draft")} />);
|
|
||||||
expect(screen.getByRole("region", { name: /draft graph evidence/i })).toBeInTheDocument();
|
|
||||||
expect(screen.getByText("read_documents")).toBeInTheDocument();
|
|
||||||
expect(screen.getByText("analyze")).toBeInTheDocument();
|
|
||||||
expect(screen.getByText("ok → end")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("shows the validation diagnostic and repaired projection", () => {
|
|
||||||
render(<AuthoringPhaseVisual projection={projectPreparedAuthoringPhase("validate")} />);
|
|
||||||
expect(screen.getByText(/no state projection/i)).toBeInTheDocument();
|
|
||||||
expect(screen.getByText("analysis → state.analysis")).toBeInTheDocument();
|
|
||||||
expect(screen.getByText(/valid draft/i)).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it.each(["diagnose", "repair"] as const)("marks the %s authoring focus", (focus) => {
|
|
||||||
render(<AuthoringPhaseVisual projection={projectPreparedAuthoringPhase("validate")} focus={focus} />);
|
|
||||||
expect(screen.getByRole("region", { name: /validation repair evidence/i })).toHaveAttribute(
|
|
||||||
"data-authoring-focus",
|
|
||||||
focus,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps diagnosis and repair attached to the recorded validate phase", () => {
|
it("renders total inventory separately from configured local sources", () => {
|
||||||
for (const focus of ["diagnose", "repair"] as const) {
|
renderStep("discover");
|
||||||
render(<AuthoringPhaseVisual projection={projectPreparedAuthoringPhase("validate")} focus={focus} />);
|
const result = screen.getByRole("region", { name: /source inventory result/i });
|
||||||
|
|
||||||
expect(screen.getByRole("region", { name: /validation repair evidence/i })).toHaveAttribute(
|
expect(within(result).getByText("6 total inventory sources")).toBeInTheDocument();
|
||||||
"data-authoring-recording-phase",
|
expect(within(result).getByRole("heading", { name: "Configured local sources (3)" })).toBeInTheDocument();
|
||||||
"validate",
|
expect(within(result).getByText("local.lda_docs")).toBeInTheDocument();
|
||||||
);
|
expect(within(result).getByText("local.lda_report")).toBeInTheDocument();
|
||||||
cleanup();
|
expect(within(result).getByText("local.issue_board")).toBeInTheDocument();
|
||||||
}
|
expect(within(result).queryByText(/^6 configured local sources$/)).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each(["diagnose", "repair"] as const)("marks %s evidence as the primary visual", (focus) => {
|
it("renders the draft revision, steps, and routes", () => {
|
||||||
render(<AuthoringPhaseVisual projection={projectPreparedAuthoringPhase("validate")} focus={focus} />);
|
renderStep("draft");
|
||||||
expect(screen.getByRole("region", { name: /validation repair evidence/i })).toHaveAttribute(
|
const result = screen.getByRole("region", { name: /draft structure result/i });
|
||||||
"data-visual-role",
|
|
||||||
"primary",
|
expect(within(result).getByText("Revision 2")).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText("read_documents")).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText("analyze")).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText("read_documents.ok -> analyze")).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText("analyze.ok -> __end__")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders the reviewed draft validation diagnostic", () => {
|
||||||
|
renderStep("diagnose");
|
||||||
|
const result = screen.getByRole("region", { name: /draft validation diagnostic/i });
|
||||||
|
|
||||||
|
expect(result).toHaveAttribute("data-authoring-result", "diagnostic");
|
||||||
|
expect(within(result).getByText("missing_outcome_edge")).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText("nodes[analyze]")).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText(/missing edges for outcomes.*ok/i)).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText(/cannot prove where execution goes next/i)).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText("Revision 3")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders route repair as a valid revision with compact prior context", () => {
|
||||||
|
renderStep("repair");
|
||||||
|
const result = screen.getByRole("region", { name: /route repair result/i });
|
||||||
|
const prior = within(result).getByRole("note", { name: /prior validation diagnostic/i });
|
||||||
|
|
||||||
|
expect(within(result).getByText("wf draft set-route lda_report_workflow --revision 3 --step analyze --outcome ok --to __end__")).toBeInTheDocument();
|
||||||
|
expect(within(result).getAllByText("Valid")).toHaveLength(2);
|
||||||
|
expect(within(result).getByText("Revision 4")).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText("0 diagnostics")).toBeInTheDocument();
|
||||||
|
expect(within(prior).getByText(/reachable node is missing edges for outcomes.*ok/i)).toBeInTheDocument();
|
||||||
|
expect(result.querySelector("[data-result-primary='true']")).toContainElement(
|
||||||
|
within(result).getByText(/set-route/),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows immutable artifact identity and version", () => {
|
it("renders immutable artifact identity and required sources", () => {
|
||||||
render(<AuthoringPhaseVisual projection={projectPreparedAuthoringPhase("artifact")} />);
|
renderStep("artifact");
|
||||||
expect(screen.getByText("lda_report_case_study")).toBeInTheDocument();
|
const result = screen.getByRole("region", { name: /immutable artifact result/i });
|
||||||
expect(screen.getByText("Version 1")).toBeInTheDocument();
|
|
||||||
expect(screen.getByText(/immutable/i)).toBeInTheDocument();
|
expect(within(result).getByText("lda_report_case_study")).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText("Version 1")).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText("Immutable")).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText("local.lda_docs")).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText("local.lda_report")).toBeInTheDocument();
|
||||||
|
expect(within(result).getByText("local.issue_board")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows all concrete deployment bindings and validation", () => {
|
it("renders runnable deployment identity and bindings", () => {
|
||||||
render(<AuthoringPhaseVisual projection={projectPreparedAuthoringPhase("deployment")} />);
|
renderStep("deployment");
|
||||||
expect(screen.getAllByText("local.lda_docs")).toHaveLength(2);
|
const result = screen.getByRole("region", { name: /runnable deployment result/i });
|
||||||
expect(screen.getAllByText("local.lda_report")).toHaveLength(2);
|
|
||||||
expect(screen.getAllByText("local.issue_board")).toHaveLength(2);
|
expect(within(result).getByText("lda_report_case_study.default")).toBeInTheDocument();
|
||||||
expect(screen.getByText(/deployment valid/i)).toBeInTheDocument();
|
expect(within(result).getByText("Runnable")).toBeInTheDocument();
|
||||||
|
expect(within(result).getAllByText("local.lda_docs")).toHaveLength(2);
|
||||||
|
expect(within(result).getAllByText("local.lda_report")).toHaveLength(2);
|
||||||
|
expect(within(result).getAllByText("local.issue_board")).toHaveLength(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,98 +1,256 @@
|
|||||||
import {
|
import {
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
ArrowRight,
|
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Database,
|
Database,
|
||||||
FileJson,
|
|
||||||
Link2,
|
Link2,
|
||||||
LockKeyhole,
|
LockKeyhole,
|
||||||
|
Route,
|
||||||
Workflow,
|
Workflow,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import type { AuthoringPhaseProjection } from "./authoring-projection.js";
|
import type { LucideIcon } from "lucide-react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import type { PreparedLifecycleStepProjection } from "./authoring-projection.js";
|
||||||
|
import { reviewedAuthoringEvidenceFor } from "./reviewed-authoring-evidence.js";
|
||||||
|
|
||||||
type AuthoringPhaseVisualProps = {
|
type Evidence = PreparedLifecycleStepProjection["evidence"];
|
||||||
readonly projection: AuthoringPhaseProjection;
|
type EvidenceOf<Kind extends Evidence["kind"]> = Extract<Evidence, { readonly kind: Kind }>;
|
||||||
readonly focus?: "full" | "diagnose" | "repair";
|
|
||||||
|
type ResultHeaderProps = {
|
||||||
|
readonly icon: LucideIcon;
|
||||||
|
readonly label: string;
|
||||||
|
readonly status: string;
|
||||||
|
readonly revision?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const InventoryVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "inventory" }> }) => (
|
const ResultHeader = ({ icon: Icon, label, status, revision }: ResultHeaderProps) => (
|
||||||
<section className="authoring-visual authoring-visual--inventory" aria-label="discovery evidence" data-presentation-surface="editorial" data-visual-role="primary">
|
<header className="authoring-result__header">
|
||||||
<div className="authoring-inventory__sources">
|
<div className="authoring-result__heading">
|
||||||
{visual.sources.map((source) => (
|
<Icon aria-hidden="true" />
|
||||||
<div key={source}><Database aria-hidden="true" /><code>{source}</code></div>
|
<div>
|
||||||
))}
|
<span>{label}</span>
|
||||||
|
{revision !== undefined && <code>Revision {revision}</code>}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ArrowRight className="authoring-visual__arrow" aria-hidden="true" />
|
<strong>{status}</strong>
|
||||||
<div className="authoring-inventory__contract">
|
</header>
|
||||||
<strong><Workflow aria-hidden="true" /> Inspected capability</strong>
|
|
||||||
<code>{visual.capability}</code>
|
|
||||||
<span><FileJson aria-hidden="true" />{visual.contract}</span>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const GraphVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "graph" }> }) => (
|
const EvidenceRows = ({
|
||||||
<section className="authoring-visual authoring-visual--graph" aria-label="draft graph evidence" data-presentation-surface="editorial" data-visual-role="primary">
|
rows,
|
||||||
<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>
|
|
||||||
<div className="authoring-graph__route"><span>{visual.route}</span><ArrowRight aria-hidden="true" /></div>
|
|
||||||
<div className="authoring-graph__end">END</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
|
|
||||||
const RepairVisual = ({
|
|
||||||
visual,
|
|
||||||
focus,
|
|
||||||
recordingPhase,
|
|
||||||
}: {
|
}: {
|
||||||
readonly visual: Extract<AuthoringPhaseProjection["visual"], { kind: "repair" }>;
|
readonly rows: readonly { readonly label: string; readonly value: ReactNode }[];
|
||||||
readonly focus: "full" | "diagnose" | "repair";
|
}) => (
|
||||||
readonly recordingPhase: AuthoringPhaseProjection["phase"];
|
<dl className="authoring-result__rows">
|
||||||
|
{rows.map(({ label, value }) => (
|
||||||
|
<div key={label}>
|
||||||
|
<dt>{label}</dt>
|
||||||
|
<dd>{value}</dd>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</dl>
|
||||||
|
);
|
||||||
|
|
||||||
|
const StringList = ({ label, items }: { readonly label: string; readonly items: readonly string[] }) => (
|
||||||
|
<section className="authoring-result__list-section">
|
||||||
|
<h3>{label}</h3>
|
||||||
|
<ul>
|
||||||
|
{items.map((item) => (
|
||||||
|
<li key={item}><code>{item}</code></li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
|
||||||
|
const ResultRoot = ({
|
||||||
|
kind,
|
||||||
|
label,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
readonly kind: Evidence["kind"];
|
||||||
|
readonly label: string;
|
||||||
|
readonly children: ReactNode;
|
||||||
}) => (
|
}) => (
|
||||||
<section
|
<section
|
||||||
className="authoring-visual authoring-visual--repair"
|
className={`authoring-visual authoring-result authoring-result--${kind}`}
|
||||||
aria-label="validation repair evidence"
|
aria-label={label}
|
||||||
|
data-authoring-result={kind}
|
||||||
data-presentation-surface="editorial"
|
data-presentation-surface="editorial"
|
||||||
data-visual-role="primary"
|
data-visual-role="primary"
|
||||||
data-authoring-focus={focus}
|
|
||||||
data-authoring-recording-phase={recordingPhase}
|
|
||||||
>
|
>
|
||||||
<div className="authoring-repair__diagnostic"><AlertTriangle aria-hidden="true" /><span>Diagnostic</span><strong>{visual.diagnostic}</strong></div>
|
{children}
|
||||||
<ArrowRight className="authoring-repair__connector" aria-hidden="true" />
|
|
||||||
<div className="authoring-repair__correction"><Link2 aria-hidden="true" /><span>Repair</span><code>{visual.correction}</code></div>
|
|
||||||
<div className="authoring-repair__status"><CheckCircle2 aria-hidden="true" /><strong>{visual.status}</strong></div>
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|
||||||
const ArtifactVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "artifact" }> }) => (
|
const InventoryResult = ({ evidence }: { readonly evidence: EvidenceOf<"inventory"> }) => (
|
||||||
<section className="authoring-visual authoring-visual--artifact" aria-label="artifact evidence" data-presentation-surface="editorial" data-visual-role="primary">
|
<ResultRoot kind={evidence.kind} label="source inventory result">
|
||||||
<LockKeyhole aria-hidden="true" />
|
<ResultHeader
|
||||||
<div><span>Immutable workflow artifact</span><strong>{visual.artifactId}</strong></div>
|
icon={Database}
|
||||||
<dl><div><dt>Version</dt><dd>Version {visual.version}</dd></div><div><dt>Requirements</dt><dd>{visual.requiredSources} local sources</dd></div></dl>
|
label="SOURCE INVENTORY"
|
||||||
</section>
|
status={`${evidence.sourceCount} TOTAL SOURCES`}
|
||||||
);
|
/>
|
||||||
|
<div className="authoring-result__body">
|
||||||
const BindingsVisual = ({ visual }: { visual: Extract<AuthoringPhaseProjection["visual"], { kind: "bindings" }> }) => (
|
<EvidenceRows
|
||||||
<section className="authoring-visual authoring-visual--bindings" aria-label="deployment binding evidence" data-presentation-surface="editorial" data-visual-role="primary">
|
rows={[
|
||||||
<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">
|
label: "Inventory",
|
||||||
{visual.bindings.map((binding) => (
|
value: <strong>{evidence.sourceCount} total inventory sources</strong>,
|
||||||
<div key={binding.requirement}><code>{binding.requirement}</code><ArrowRight aria-hidden="true" /><code>{binding.source}</code></div>
|
},
|
||||||
))}
|
{
|
||||||
|
label: "Configured local sources",
|
||||||
|
value: <strong>{evidence.sources.length} configured local source IDs</strong>,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<StringList label={`Configured local sources (${evidence.sources.length})`} items={evidence.sources} />
|
||||||
|
<section className="authoring-result__contract">
|
||||||
|
<h3>Capability contract</h3>
|
||||||
|
<EvidenceRows
|
||||||
|
rows={[
|
||||||
|
{ label: "Capability", value: <code>{evidence.capability.name}</code> },
|
||||||
|
{ label: "Inputs", value: <code>{evidence.capability.inputs.join(", ")}</code> },
|
||||||
|
{ label: "Outputs", value: <code>{evidence.capability.outputs.join(", ")}</code> },
|
||||||
|
{ label: "Outcomes", value: <code>{evidence.capability.outcomes.join(", ")}</code> },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</ResultRoot>
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Renders the factual product artifact appropriate to one authoring phase. */
|
const DraftResult = ({ evidence }: { readonly evidence: EvidenceOf<"draft"> }) => (
|
||||||
export const AuthoringPhaseVisual = ({ projection, focus = "full" }: AuthoringPhaseVisualProps) => {
|
<ResultRoot kind={evidence.kind} label="draft structure result">
|
||||||
switch (projection.visual.kind) {
|
<ResultHeader icon={Workflow} label="VALID DRAFT" status="Valid" revision={evidence.revision} />
|
||||||
case "inventory": return <InventoryVisual visual={projection.visual} />;
|
<div className="authoring-result__body">
|
||||||
case "graph": return <GraphVisual visual={projection.visual} />;
|
<EvidenceRows
|
||||||
// Diagnose and repair deliberately share this factual validate visual while
|
rows={[
|
||||||
// the lifecycle scene supplies the distinct recorded command and focus.
|
{ label: "Workspace", value: <code>{evidence.workspaceId}</code> },
|
||||||
case "repair": return <RepairVisual visual={projection.visual} focus={focus} recordingPhase={projection.phase} />;
|
{ label: "Steps", value: evidence.stepCount },
|
||||||
case "artifact": return <ArtifactVisual visual={projection.visual} />;
|
{ label: "Routes", value: evidence.routeCount },
|
||||||
case "bindings": return <BindingsVisual visual={projection.visual} />;
|
]}
|
||||||
|
/>
|
||||||
|
<StringList label="Steps" items={evidence.steps} />
|
||||||
|
<StringList label="Routes" items={evidence.routes} />
|
||||||
|
</div>
|
||||||
|
</ResultRoot>
|
||||||
|
);
|
||||||
|
|
||||||
|
const DiagnosticResult = ({ evidence }: { readonly evidence: EvidenceOf<"diagnostic"> }) => (
|
||||||
|
<ResultRoot kind={evidence.kind} label="draft validation diagnostic">
|
||||||
|
<ResultHeader
|
||||||
|
icon={AlertTriangle}
|
||||||
|
label="INVALID DRAFT"
|
||||||
|
status={evidence.status === "invalid" ? "Invalid" : evidence.status}
|
||||||
|
revision={evidence.revision}
|
||||||
|
/>
|
||||||
|
<div className="authoring-result__body" data-result-primary="true">
|
||||||
|
<EvidenceRows
|
||||||
|
rows={[
|
||||||
|
{ label: "Diagnostic", value: <code>{evidence.diagnostic.code}</code> },
|
||||||
|
{ label: "Path", value: <code>{evidence.diagnostic.path}</code> },
|
||||||
|
{ label: "Message", value: evidence.diagnostic.message },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<p className="authoring-result__explanation">{evidence.diagnostic.explanation}</p>
|
||||||
|
</div>
|
||||||
|
</ResultRoot>
|
||||||
|
);
|
||||||
|
|
||||||
|
const RepairResult = ({ evidence }: { readonly evidence: EvidenceOf<"repair"> }) => {
|
||||||
|
// The repair record keeps the successful result compact; prior invalid context
|
||||||
|
// comes from the same reviewed catalog and is never presented as a new result.
|
||||||
|
const priorEvidence = reviewedAuthoringEvidenceFor("diagnose");
|
||||||
|
if (priorEvidence.kind !== "diagnostic") {
|
||||||
|
throw new Error("reviewed repair context has an unexpected shape");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ResultRoot kind={evidence.kind} label="route repair result">
|
||||||
|
<ResultHeader
|
||||||
|
icon={Route}
|
||||||
|
label="ROUTE REPAIR"
|
||||||
|
status={evidence.status === "valid" ? "Valid" : evidence.status}
|
||||||
|
revision={evidence.toRevision}
|
||||||
|
/>
|
||||||
|
<aside className="authoring-result__prior" role="note" aria-label="prior validation diagnostic">
|
||||||
|
<span>Prior validation</span>
|
||||||
|
<code>{priorEvidence.diagnostic.code} · {priorEvidence.diagnostic.path}</code>
|
||||||
|
<p>{priorEvidence.diagnostic.message}</p>
|
||||||
|
</aside>
|
||||||
|
<div className="authoring-result__body" data-result-primary="true">
|
||||||
|
<EvidenceRows
|
||||||
|
rows={[
|
||||||
|
{ label: "Command", value: <code>{evidence.command}</code> },
|
||||||
|
{ label: "Result", value: <strong>Valid</strong> },
|
||||||
|
{ label: "Diagnostics", value: <strong>0 diagnostics</strong> },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ResultRoot>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ArtifactResult = ({ evidence }: { readonly evidence: EvidenceOf<"artifact"> }) => (
|
||||||
|
<ResultRoot kind={evidence.kind} label="immutable artifact result">
|
||||||
|
<ResultHeader icon={LockKeyhole} label="IMMUTABLE ARTIFACT" status="Immutable" />
|
||||||
|
<div className="authoring-result__body">
|
||||||
|
<EvidenceRows
|
||||||
|
rows={[
|
||||||
|
{ label: "Artifact", value: <code>{evidence.artifactId}</code> },
|
||||||
|
{ label: "Version", value: <strong>Version {evidence.version}</strong> },
|
||||||
|
{ label: "Required sources", value: `${evidence.requiredSources.length} configured local sources` },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<StringList label="Required local sources" items={evidence.requiredSources} />
|
||||||
|
</div>
|
||||||
|
</ResultRoot>
|
||||||
|
);
|
||||||
|
|
||||||
|
const DeploymentResult = ({ evidence }: { readonly evidence: EvidenceOf<"deployment"> }) => (
|
||||||
|
<ResultRoot kind={evidence.kind} label="runnable deployment result">
|
||||||
|
<ResultHeader
|
||||||
|
icon={Link2}
|
||||||
|
label="RUNNABLE DEPLOYMENT"
|
||||||
|
status={evidence.status === "runnable" ? "Runnable" : evidence.status}
|
||||||
|
/>
|
||||||
|
<div className="authoring-result__body">
|
||||||
|
<EvidenceRows
|
||||||
|
rows={[{ label: "Deployment", value: <code>{evidence.deploymentId}</code> }]}
|
||||||
|
/>
|
||||||
|
<section className="authoring-result__list-section">
|
||||||
|
<h3>Bindings</h3>
|
||||||
|
<ul>
|
||||||
|
{evidence.bindings.map((binding) => (
|
||||||
|
<li key={binding.requirement}>
|
||||||
|
<code>{binding.requirement}</code>
|
||||||
|
<span aria-hidden="true">-></span>
|
||||||
|
<code>{binding.source}</code>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<p className="authoring-result__status"><CheckCircle2 aria-hidden="true" /> Ready for a persisted run</p>
|
||||||
|
</div>
|
||||||
|
</ResultRoot>
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Renders one audience-facing product result from the reviewed evidence union. */
|
||||||
|
export const AuthoringPhaseVisual = ({
|
||||||
|
projection,
|
||||||
|
}: {
|
||||||
|
readonly projection: PreparedLifecycleStepProjection;
|
||||||
|
}) => {
|
||||||
|
switch (projection.evidence.kind) {
|
||||||
|
case "inventory":
|
||||||
|
return <InventoryResult evidence={projection.evidence} />;
|
||||||
|
case "draft":
|
||||||
|
return <DraftResult evidence={projection.evidence} />;
|
||||||
|
case "diagnostic":
|
||||||
|
return <DiagnosticResult evidence={projection.evidence} />;
|
||||||
|
case "repair":
|
||||||
|
return <RepairResult evidence={projection.evidence} />;
|
||||||
|
case "artifact":
|
||||||
|
return <ArtifactResult evidence={projection.evidence} />;
|
||||||
|
case "deployment":
|
||||||
|
return <DeploymentResult evidence={projection.evidence} />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+25
-20
@@ -49,16 +49,18 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
const frame = screen.getByRole("region", { name: /active authoring operation/i });
|
const frame = screen.getByRole("region", { name: /active authoring operation/i });
|
||||||
expect(frame).toHaveTextContent("workflow.draft_workspaces.validate");
|
expect(frame).toHaveTextContent("workflow.draft_workspaces.validate");
|
||||||
expect(frame).toHaveTextContent("wf draft validate lda_report_workflow");
|
expect(frame).toHaveTextContent("wf draft validate lda_report_workflow");
|
||||||
expect(frame).toHaveTextContent(/structured missing-output diagnostic/i);
|
expect(frame).toHaveTextContent("missing_outcome_edge");
|
||||||
|
expect(frame).toHaveTextContent("nodes[analyze]");
|
||||||
|
expect(frame).toHaveTextContent(/missing edges for outcomes.*ok/i);
|
||||||
expect(frame).toHaveAttribute("data-authoring-step", "diagnose");
|
expect(frame).toHaveAttribute("data-authoring-step", "diagnose");
|
||||||
expect(frame.querySelector('[data-authoring-focus="diagnose"]')).toBeInTheDocument();
|
expect(frame).toHaveAttribute("data-recording-phase", "validate");
|
||||||
expect(screen.getByRole("region", { name: "validation repair evidence" })).toHaveAttribute(
|
expect(screen.getByRole("region", { name: "draft validation diagnostic" })).toHaveAttribute(
|
||||||
"data-authoring-focus",
|
"data-authoring-result",
|
||||||
"diagnose",
|
"diagnostic",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("repair shows the output-map operation over the same validation visual", () => {
|
it("repair shows the route repair operation and valid result", () => {
|
||||||
const scene = findScene("prepared-lifecycle");
|
const scene = findScene("prepared-lifecycle");
|
||||||
const repairBeat = findBeat("prepared-lifecycle", "repair");
|
const repairBeat = findBeat("prepared-lifecycle", "repair");
|
||||||
if (!scene || !repairBeat) throw new Error("missing prepared-lifecycle/repair");
|
if (!scene || !repairBeat) throw new Error("missing prepared-lifecycle/repair");
|
||||||
@@ -66,13 +68,16 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
const { rerender } = renderBeat("diagnose");
|
const { rerender } = renderBeat("diagnose");
|
||||||
rerender(<PreparedAuthoringLifecycleScene scene={scene} beat={repairBeat} />);
|
rerender(<PreparedAuthoringLifecycleScene scene={scene} beat={repairBeat} />);
|
||||||
const frame = screen.getByRole("region", { name: /active authoring operation/i });
|
const frame = screen.getByRole("region", { name: /active authoring operation/i });
|
||||||
expect(frame).toHaveTextContent("workflow.draft_workspaces.set_step_output_map");
|
expect(frame).toHaveTextContent("workflow.draft_workspaces.set_route");
|
||||||
expect(frame).toHaveTextContent(/wf draft set-output lda_report_workflow/i);
|
expect(frame).toHaveTextContent(/wf draft set-route lda_report_workflow --revision 3 --step analyze --outcome ok --to __end__/i);
|
||||||
|
expect(frame).toHaveTextContent("Valid");
|
||||||
|
expect(frame).toHaveTextContent("Revision 4");
|
||||||
|
expect(frame).toHaveTextContent("0 diagnostics");
|
||||||
expect(frame).toHaveTextContent(/prepared workflow lifecycle/i);
|
expect(frame).toHaveTextContent(/prepared workflow lifecycle/i);
|
||||||
expect(frame).toHaveAttribute("data-authoring-step", "repair");
|
expect(frame).toHaveAttribute("data-authoring-step", "repair");
|
||||||
expect(frame.querySelector('[data-authoring-focus="repair"]')).toBeInTheDocument();
|
expect(frame).toHaveAttribute("data-recording-phase", "validate");
|
||||||
expect(screen.getByRole("region", { name: "validation repair evidence" })).toHaveAttribute(
|
expect(screen.getByRole("region", { name: "route repair result" })).toHaveAttribute(
|
||||||
"data-authoring-focus",
|
"data-authoring-result",
|
||||||
"repair",
|
"repair",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -91,12 +96,12 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
["discover", "discovery evidence"],
|
["discover", "source inventory result"],
|
||||||
["draft", "draft graph evidence"],
|
["draft", "draft structure result"],
|
||||||
["diagnose", "validation repair evidence"],
|
["diagnose", "draft validation diagnostic"],
|
||||||
["repair", "validation repair evidence"],
|
["repair", "route repair result"],
|
||||||
["artifact", "artifact evidence"],
|
["artifact", "immutable artifact result"],
|
||||||
["deployment", "deployment binding evidence"],
|
["deployment", "runnable deployment result"],
|
||||||
] as const)("renders %s as the primary phase visual", (beatId, label) => {
|
] as const)("renders %s as the primary phase visual", (beatId, label) => {
|
||||||
renderBeat(beatId);
|
renderBeat(beatId);
|
||||||
expect(screen.getByRole("region", { name: label })).toBeInTheDocument();
|
expect(screen.getByRole("region", { name: label })).toBeInTheDocument();
|
||||||
@@ -108,7 +113,7 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
const workspace = screen.getByRole("region", { name: "prepared workflow authoring lifecycle" });
|
const workspace = screen.getByRole("region", { name: "prepared workflow authoring lifecycle" });
|
||||||
const assistant = screen.getByRole("complementary", { name: /prepared authoring assistant/i });
|
const assistant = screen.getByRole("complementary", { name: /prepared authoring assistant/i });
|
||||||
const frame = screen.getByRole("region", { name: "active authoring operation" });
|
const frame = screen.getByRole("region", { name: "active authoring operation" });
|
||||||
const visual = screen.getByRole("region", { name: "draft graph evidence" });
|
const visual = screen.getByRole("region", { name: "draft structure result" });
|
||||||
|
|
||||||
expect(workspace).toContainElement(assistant);
|
expect(workspace).toContainElement(assistant);
|
||||||
expect(workspace).toContainElement(frame);
|
expect(workspace).toContainElement(frame);
|
||||||
@@ -191,7 +196,7 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
.toHaveTextContent("Diagnose");
|
.toHaveTextContent("Diagnose");
|
||||||
const chat = screen.getByRole("log", { name: "prepared authoring conversation" });
|
const chat = screen.getByRole("log", { name: "prepared authoring conversation" });
|
||||||
expect(chat).toHaveAttribute("data-surface", "stage");
|
expect(chat).toHaveAttribute("data-surface", "stage");
|
||||||
expect(screen.getByRole("region", { name: "validation repair evidence" })).toHaveAttribute(
|
expect(screen.getByRole("region", { name: "draft validation diagnostic" })).toHaveAttribute(
|
||||||
"data-presentation-surface",
|
"data-presentation-surface",
|
||||||
"editorial",
|
"editorial",
|
||||||
);
|
);
|
||||||
@@ -246,7 +251,7 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
|
|
||||||
render(<PreparedAuthoringLifecycleScene scene={scene} beat={unexpectedBeat} />);
|
render(<PreparedAuthoringLifecycleScene scene={scene} beat={unexpectedBeat} />);
|
||||||
|
|
||||||
expect(screen.getByRole("region", { name: "discovery evidence" })).toBeInTheDocument();
|
expect(screen.getByRole("region", { name: "source inventory result" })).toBeInTheDocument();
|
||||||
expect(screen.getByRole("list", { name: /prepared authoring lifecycle/i }).querySelector("[data-active='true']"))
|
expect(screen.getByRole("list", { name: /prepared authoring lifecycle/i }).querySelector("[data-active='true']"))
|
||||||
.toHaveTextContent("Discover");
|
.toHaveTextContent("Discover");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export const PreparedAuthoringLifecycleScene = ({ scene, beat, onAdvance, discus
|
|||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</header>
|
</header>
|
||||||
<AuthoringPhaseVisual projection={projection} focus={projection.focus} />
|
<AuthoringPhaseVisual projection={projection} />
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
{discussionRail && (
|
{discussionRail && (
|
||||||
|
|||||||
Reference in New Issue
Block a user