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");
expect(loop).toHaveAttribute("data-readable-surface", "dark");
expect(loop).toHaveAttribute("data-active-stage", "diagnose");
const composition = screen.getByLabelText("agent authoring loop");
expect(composition).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("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(".scene-body__authoring-node"))
.toHaveAttribute("data-readable-surface", "dark");
expect(screen.getByText("Repair")).toBeInTheDocument();
expect(screen.getByText("Compile or save")).toBeInTheDocument();
expect(within(loop).getByText("Repair")).toBeInTheDocument();
expect(within(loop).getByText("Compile or save")).toBeInTheDocument();
});
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 { EvaluationEvidenceScene } from "./evaluation/EvaluationEvidenceScene.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 { ProblemLoopScene } from "./opening/ProblemLoopScene.js";
@@ -227,39 +230,65 @@ const authoringSteps = [
{ id: "compile", label: "Compile or save", detail: "artifact / deployment / run" },
] as const;
const AuthoringScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => (
<>
<StageCaption eyebrow="Act II · implemented" title={scene.title}>
<p>{beat.caption}</p>
</StageCaption>
<div
className="scene-body__authoring-loop"
aria-label="agent authoring loop"
data-active-stage={beat.id}
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>
<p className="scene-body__evidence">{scene.evidencePointer}</p>
</>
);
// Scene 7 uses speaker-friendly beat names; the prepared recording keeps the
// underlying product phases (`draft` and `validate`) as its canonical IDs.
const authoringPhaseForBeat = (beatId: string): AuthoringPhaseId => {
if (beatId === "author") return "draft";
if (beatId === "diagnose" || beatId === "repair") return "validate";
return beatId as AuthoringPhaseId;
};
const AuthoringScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => {
const projection = projectPreparedAuthoringPhase(authoringPhaseForBeat(beat.id));
const primaryCommand = projection.commands[0];
return (
<>
<StageCaption eyebrow="Act II · implemented" title={scene.title}>
<p>{beat.caption}</p>
</StageCaption>
<section
className="scene-body__authoring-composition"
aria-label="agent authoring loop"
data-active-stage={beat.id}
>
<article className="scene-body__authoring-evidence" aria-label={`${projection.label} product evidence`}>
<header>
<span>Public operation</span>
<strong>{primaryCommand.title}</strong>
<p>{projection.summary}</p>
</header>
<AuthoringPhaseVisual projection={projection} />
</article>
<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 => {
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 {
display: inline-flex;
align-items: center;