fix: separate Scene 11 interrupt context from approval
This commit is contained in:
@@ -93,9 +93,12 @@ describe("DemoWorkflowScene", () => {
|
|||||||
renderBeat("interrupt", "typed-human-boundary");
|
renderBeat("interrupt", "typed-human-boundary");
|
||||||
|
|
||||||
const moment = screen.getByRole("region", { name: /current product moment/i });
|
const moment = screen.getByRole("region", { name: /current product moment/i });
|
||||||
expect(moment).toHaveAttribute("data-moment", "approval");
|
expect(moment).toHaveAttribute("data-moment", "interrupt");
|
||||||
expect(screen.getByRole("group", { name: /operator resume decision/i })).toBeInTheDocument();
|
|
||||||
expect(screen.getByText("Workflow input")).toBeInTheDocument();
|
expect(screen.getByText("Workflow input")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("Interrupt payload")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("group", { name: /operator resume decision/i })).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: "Submit" })).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: "Request revision" })).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("carries the contract into Scene 10 approval via guided product moment", () => {
|
it("carries the contract into Scene 10 approval via guided product moment", () => {
|
||||||
@@ -164,7 +167,9 @@ describe("DemoWorkflowScene", () => {
|
|||||||
it("renders approval and interrupt beats via guided product moment", () => {
|
it("renders approval and interrupt beats via guided product moment", () => {
|
||||||
const interrupt = renderBeat("interrupt", "typed-human-boundary");
|
const interrupt = renderBeat("interrupt", "typed-human-boundary");
|
||||||
expect(screen.queryByLabelText("workflow graph")).not.toBeInTheDocument();
|
expect(screen.queryByLabelText("workflow graph")).not.toBeInTheDocument();
|
||||||
expect(screen.getByRole("region", { name: /current product moment/i })).toHaveAttribute("data-moment", "approval");
|
expect(screen.getByRole("region", { name: /current product moment/i })).toHaveAttribute("data-moment", "interrupt");
|
||||||
|
expect(screen.queryByRole("button", { name: "Submit" })).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: "Request revision" })).not.toBeInTheDocument();
|
||||||
interrupt.unmount();
|
interrupt.unmount();
|
||||||
|
|
||||||
renderBeat("approval", "typed-human-boundary");
|
renderBeat("approval", "typed-human-boundary");
|
||||||
@@ -209,10 +214,13 @@ describe("DemoWorkflowScene", () => {
|
|||||||
expect(screen.getByText("Workflow input")).toBeInTheDocument();
|
expect(screen.getByText("Workflow input")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders interrupt decision form but no raw schema in guided product moment", () => {
|
it("renders interrupt facts but no decision controls in guided product moment", () => {
|
||||||
renderBeat("interrupt", "typed-human-boundary");
|
renderBeat("interrupt", "typed-human-boundary");
|
||||||
expect(screen.queryByText("Resume schema")).not.toBeInTheDocument();
|
expect(screen.queryByText("Resume schema")).not.toBeInTheDocument();
|
||||||
expect(screen.getByRole("group", { name: /operator resume decision/i })).toBeInTheDocument();
|
expect(screen.getByText("Interrupt payload")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("group", { name: /operator resume decision/i })).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: "Submit" })).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: "Request revision" })).not.toBeInTheDocument();
|
||||||
cleanup();
|
cleanup();
|
||||||
|
|
||||||
renderBeat("approval", "typed-human-boundary");
|
renderBeat("approval", "typed-human-boundary");
|
||||||
|
|||||||
@@ -53,6 +53,26 @@ const demoWithAppliedCount = (appliedCount: number): DemoTimelineController => (
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("GuidedProductMoment", () => {
|
describe("GuidedProductMoment", () => {
|
||||||
|
it("renders interrupt context without decision controls", () => {
|
||||||
|
render(
|
||||||
|
<GuidedProductMoment
|
||||||
|
beat={findBeat("typed-human-boundary", "interrupt")!}
|
||||||
|
demo={demo}
|
||||||
|
contract={contract}
|
||||||
|
operation={null}
|
||||||
|
openEvidence={vi.fn()}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const moment = screen.getByRole("region", { name: /current product moment/i });
|
||||||
|
expect(moment).toHaveAttribute("data-moment", "interrupt");
|
||||||
|
expect(screen.getByText("Workflow input")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("Interrupt payload")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("group", { name: /operator resume decision/i })).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: "Submit" })).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: "Request revision" })).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it("makes approval the primary product decision with factual panels", () => {
|
it("makes approval the primary product decision with factual panels", () => {
|
||||||
render(
|
render(
|
||||||
<GuidedProductMoment
|
<GuidedProductMoment
|
||||||
|
|||||||
@@ -26,13 +26,20 @@ export type GuidedProductMomentProps = {
|
|||||||
readonly openEvidence: () => void;
|
readonly openEvidence: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const momentForBeat = (beatId: string): "approval" | "resume" | "output" | "trace" =>
|
type GuidedProductMoment = "interrupt" | "approval" | "resume" | "output" | "trace";
|
||||||
beatId === "resume" || beatId === "output" || beatId === "trace" ? beatId : "approval";
|
|
||||||
|
const momentForBeat = (beatId: string): GuidedProductMoment => {
|
||||||
|
if (beatId === "interrupt" || beatId === "approval" || beatId === "resume" || beatId === "output" || beatId === "trace") {
|
||||||
|
return beatId;
|
||||||
|
}
|
||||||
|
return "approval";
|
||||||
|
};
|
||||||
|
|
||||||
const statusCopy = (
|
const statusCopy = (
|
||||||
moment: ReturnType<typeof momentForBeat>,
|
moment: ReturnType<typeof momentForBeat>,
|
||||||
approvalActions?: DemoApprovalActions,
|
approvalActions?: DemoApprovalActions,
|
||||||
): string => {
|
): string => {
|
||||||
|
if (moment === "interrupt") return "Run paused at the typed interrupt; inspect the context before deciding.";
|
||||||
if (moment !== "approval") return "Same persisted run; inspect the proof below.";
|
if (moment !== "approval") return "Same persisted run; inspect the proof below.";
|
||||||
if (approvalActions?.state === "submitted") return "Submitted. Same run resumed.";
|
if (approvalActions?.state === "submitted") return "Submitted. Same run resumed.";
|
||||||
if (approvalActions?.state === "revision_requested") {
|
if (approvalActions?.state === "revision_requested") {
|
||||||
@@ -43,6 +50,8 @@ const statusCopy = (
|
|||||||
|
|
||||||
const hierarchyForMoment = (moment: ReturnType<typeof momentForBeat>) => {
|
const hierarchyForMoment = (moment: ReturnType<typeof momentForBeat>) => {
|
||||||
switch (moment) {
|
switch (moment) {
|
||||||
|
case "interrupt":
|
||||||
|
return { primary: "interrupt-context", support: "input-facts" };
|
||||||
case "approval":
|
case "approval":
|
||||||
return { primary: "interrupt-approval", support: "input-facts" };
|
return { primary: "interrupt-approval", support: "input-facts" };
|
||||||
case "resume":
|
case "resume":
|
||||||
@@ -88,6 +97,12 @@ export const GuidedProductMoment = ({
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className="guided-product-moment__primary">
|
<div className="guided-product-moment__primary">
|
||||||
|
{moment === "interrupt" ? (
|
||||||
|
<div className="guided-product-moment__interrupt-grid">
|
||||||
|
<RunInputFacts facts={facts} density="compact" />
|
||||||
|
<InterruptPayloadFacts facts={facts} priority="primary" />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
{moment === "approval" && contract ? (
|
{moment === "approval" && contract ? (
|
||||||
<div className="guided-product-moment__approval-grid">
|
<div className="guided-product-moment__approval-grid">
|
||||||
<aside className="guided-product-moment__input-rail" aria-label="workflow input context">
|
<aside className="guided-product-moment__input-rail" aria-label="workflow input context">
|
||||||
|
|||||||
@@ -1244,6 +1244,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.guided-product-moment[data-moment="approval"],
|
.guided-product-moment[data-moment="approval"],
|
||||||
|
.guided-product-moment[data-moment="interrupt"],
|
||||||
.guided-product-moment[data-moment="resume"],
|
.guided-product-moment[data-moment="resume"],
|
||||||
.guided-product-moment[data-moment="output"],
|
.guided-product-moment[data-moment="output"],
|
||||||
.guided-product-moment[data-moment="trace"] {
|
.guided-product-moment[data-moment="trace"] {
|
||||||
@@ -1260,6 +1261,10 @@
|
|||||||
gap: 0.55rem;
|
gap: 0.55rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.guided-product-moment[data-moment="interrupt"] {
|
||||||
|
gap: 0.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
.guided-product-moment[data-moment="approval"] .guided-product-moment__header {
|
.guided-product-moment[data-moment="approval"] .guided-product-moment__header {
|
||||||
grid-template-columns: minmax(0, 0.42fr) minmax(0, 1fr);
|
grid-template-columns: minmax(0, 0.42fr) minmax(0, 1fr);
|
||||||
padding: 0.55rem 0.75rem;
|
padding: 0.55rem 0.75rem;
|
||||||
@@ -1291,6 +1296,17 @@
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.guided-product-moment__interrupt-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(14rem, 0.55fr) minmax(0, 1.45fr);
|
||||||
|
gap: 0.85rem;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guided-product-moment__interrupt-grid > * {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.guided-product-moment__approval-grid > * {
|
.guided-product-moment__approval-grid > * {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
@@ -1570,6 +1586,7 @@
|
|||||||
|
|
||||||
@container presentation-canvas (max-width: 1050px) {
|
@container presentation-canvas (max-width: 1050px) {
|
||||||
.guided-product-moment__approval-grid,
|
.guided-product-moment__approval-grid,
|
||||||
|
.guided-product-moment__interrupt-grid,
|
||||||
.guided-product-moment__resume-grid,
|
.guided-product-moment__resume-grid,
|
||||||
.guided-product-moment__trace-grid {
|
.guided-product-moment__trace-grid {
|
||||||
grid-template-columns: minmax(0, 1fr);
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
|||||||
Reference in New Issue
Block a user