feat: show factual authoring evidence in scene seven

This commit is contained in:
lda
2026-07-11 09:23:12 +07:00 Verified
parent 833553c1d4
commit 4951d2487c
3 changed files with 143 additions and 38 deletions
@@ -342,16 +342,19 @@ describe("SceneBody", () => {
/>, />,
); );
const loop = screen.getByLabelText("agent authoring loop"); const composition = screen.getByLabelText("agent authoring loop");
expect(loop).toHaveAttribute("data-readable-surface", "dark"); expect(composition).toHaveAttribute("data-active-stage", "diagnose");
expect(loop).toHaveAttribute("data-active-stage", "diagnose"); expect(screen.getByRole("article", { name: /validate product evidence/i })).toBeInTheDocument();
expect(screen.getByText("workflow.draft_workspaces.validate")).toBeInTheDocument();
const loop = screen.getByLabelText("authoring phase loop");
expect(loop.querySelector("[data-readable-surface='dark']")).toBeInTheDocument();
expect(screen.getByText("Discover capability")).toBeInTheDocument(); expect(screen.getByText("Discover capability")).toBeInTheDocument();
expect(screen.getByText("Author draft")).toBeInTheDocument(); expect(screen.getByText("Author draft")).toBeInTheDocument();
expect(screen.getByText("Validate and diagnose").closest("[data-authoring-active]")).toHaveAttribute("data-authoring-active", "true"); expect(screen.getByText("Validate and diagnose").closest("[data-authoring-active]")).toHaveAttribute("data-authoring-active", "true");
expect(screen.getByText("Validate and diagnose").closest(".scene-body__authoring-node")) expect(screen.getByText("Validate and diagnose").closest(".scene-body__authoring-node"))
.toHaveAttribute("data-readable-surface", "dark"); .toHaveAttribute("data-readable-surface", "dark");
expect(screen.getByText("Repair")).toBeInTheDocument(); expect(within(loop).getByText("Repair")).toBeInTheDocument();
expect(screen.getByText("Compile or save")).toBeInTheDocument(); expect(within(loop).getByText("Compile or save")).toBeInTheDocument();
}); });
it("renders discussion branches as a labelled presenter rail", () => { it("renders discussion branches as a labelled presenter rail", () => {
+62 -33
View File
@@ -16,6 +16,9 @@ import { ConclusionScene } from "./conclusion/ConclusionScene.js";
import { DefenseDiscussionIndex } from "./discussion/DefenseDiscussionIndex.js"; import { DefenseDiscussionIndex } from "./discussion/DefenseDiscussionIndex.js";
import { EvaluationEvidenceScene } from "./evaluation/EvaluationEvidenceScene.js"; import { EvaluationEvidenceScene } from "./evaluation/EvaluationEvidenceScene.js";
import { ArchitectureScene } from "./scenes/ArchitectureScene.js"; import { ArchitectureScene } from "./scenes/ArchitectureScene.js";
import { AuthoringPhaseVisual } from "./authoring/AuthoringPhaseVisual.js";
import { projectPreparedAuthoringPhase } from "./authoring/authoring-projection.js";
import type { AuthoringPhaseId } from "./authoring/authoring-recording.js";
import { OpeningThesisScene } from "./opening/OpeningThesisScene.js"; import { OpeningThesisScene } from "./opening/OpeningThesisScene.js";
import { ProblemLoopScene } from "./opening/ProblemLoopScene.js"; import { ProblemLoopScene } from "./opening/ProblemLoopScene.js";
@@ -227,39 +230,65 @@ const authoringSteps = [
{ id: "compile", label: "Compile or save", detail: "artifact / deployment / run" }, { id: "compile", label: "Compile or save", detail: "artifact / deployment / run" },
] as const; ] as const;
const AuthoringScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => ( // Scene 7 uses speaker-friendly beat names; the prepared recording keeps the
<> // underlying product phases (`draft` and `validate`) as its canonical IDs.
<StageCaption eyebrow="Act II · implemented" title={scene.title}> const authoringPhaseForBeat = (beatId: string): AuthoringPhaseId => {
<p>{beat.caption}</p> if (beatId === "author") return "draft";
</StageCaption> if (beatId === "diagnose" || beatId === "repair") return "validate";
<div return beatId as AuthoringPhaseId;
className="scene-body__authoring-loop" };
aria-label="agent authoring loop"
data-active-stage={beat.id} const AuthoringScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => {
data-readable-surface="dark" const projection = projectPreparedAuthoringPhase(authoringPhaseForBeat(beat.id));
> const primaryCommand = projection.commands[0];
<div className="scene-body__authoring-loop-rail" aria-hidden="true" />
{authoringSteps.map((step, i) => { return (
const isActive = beat.id === step.id; <>
const isPast = authoringSteps.findIndex((candidate) => candidate.id === beat.id) > i; <StageCaption eyebrow="Act II · implemented" title={scene.title}>
return ( <p>{beat.caption}</p>
<div </StageCaption>
key={step.id} <section
className="scene-body__authoring-node" className="scene-body__authoring-composition"
data-authoring-active={isActive} aria-label="agent authoring loop"
data-authoring-past={isPast} data-active-stage={beat.id}
data-readable-surface="dark" >
> <article className="scene-body__authoring-evidence" aria-label={`${projection.label} product evidence`}>
<span className="scene-body__authoring-number">{i + 1}</span> <header>
<strong>{step.label}</strong> <span>Public operation</span>
<small>{step.detail}</small> <strong>{primaryCommand.title}</strong>
</div> <p>{projection.summary}</p>
); </header>
})} <AuthoringPhaseVisual projection={projection} />
</div> </article>
<p className="scene-body__evidence">{scene.evidencePointer}</p> <div
</> className="scene-body__authoring-loop"
); aria-label="authoring phase loop"
data-readable-surface="dark"
>
<div className="scene-body__authoring-loop-rail" aria-hidden="true" />
{authoringSteps.map((step, i) => {
const isActive = beat.id === step.id;
const isPast = authoringSteps.findIndex((candidate) => candidate.id === beat.id) > i;
return (
<div
key={step.id}
className="scene-body__authoring-node"
data-authoring-active={isActive}
data-authoring-past={isPast}
data-readable-surface="dark"
>
<span className="scene-body__authoring-number">{i + 1}</span>
<strong>{step.label}</strong>
<small>{step.detail}</small>
</div>
);
})}
</div>
</section>
<p className="scene-body__evidence">{scene.evidencePointer}</p>
</>
);
};
const assertNever = (value: never): never => { const assertNever = (value: never): never => {
throw new Error(`Unexpected view: ${value}`); throw new Error(`Unexpected view: ${value}`);
@@ -1426,6 +1426,79 @@
} }
} }
.scene-body__authoring-composition {
display: grid;
grid-template-rows: minmax(0, 1fr) auto;
gap: 0.7rem;
flex: 1 1 auto;
min-height: 0;
}
.scene-body__authoring-evidence {
display: grid;
grid-template-rows: auto minmax(0, 1fr);
min-height: 0;
overflow: hidden;
border: 1px solid color-mix(in oklch, var(--accent-cyan) 42%, var(--stage-line));
border-radius: 0.75rem;
padding: 0.8rem 1rem;
background: color-mix(in oklch, var(--stage-surface) 82%, var(--stage-inset));
}
.scene-body__authoring-evidence > header {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 0.2rem 0.65rem;
align-items: baseline;
}
.scene-body__authoring-evidence > header span {
color: var(--accent-cyan);
font: 700 0.65rem/1 var(--font-evidence);
letter-spacing: 0.05em;
text-transform: uppercase;
}
.scene-body__authoring-evidence > header strong {
min-width: 0;
overflow: hidden;
color: var(--text-primary);
font: 700 0.82rem/1.2 var(--font-evidence);
text-overflow: ellipsis;
white-space: nowrap;
}
.scene-body__authoring-evidence > header p {
grid-column: 1 / -1;
margin: 0;
color: var(--text-secondary);
font: 0.82rem/1.3 var(--font-interface);
}
.scene-body__authoring-evidence .authoring-visual {
min-height: 11rem;
}
.scene-body__authoring-composition .scene-body__authoring-loop {
grid-template-columns: repeat(5, minmax(0, 1fr));
min-height: 6.4rem;
margin-top: 0;
padding-top: 0.55rem;
}
.scene-body__authoring-composition .scene-body__authoring-node {
min-height: 5.8rem;
padding: 0.55rem;
}
@media (max-width: 1050px) {
.scene-body__authoring-composition .scene-body__authoring-loop {
grid-template-columns: repeat(5, minmax(9.5rem, 1fr));
overflow-x: auto;
padding-bottom: 0.35rem;
}
}
.scene-body__lifecycle-number { .scene-body__lifecycle-number {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;