feat: connect prepared run and refine presenter notes
This commit is contained in:
@@ -152,7 +152,7 @@ Say:
|
|||||||
>
|
>
|
||||||
> Run is one recorded execution, including its status, output, and trace.
|
> Run is one recorded execution, including its status, output, and trace.
|
||||||
|
|
||||||
Keep Scene 5 conceptual. Scene 9 applies this vocabulary to the prepared example. Also note that raw plans can create artifacts without passing through Draft ([lines 629-660](../thesis/system-design-implementation.md#workflow-lifecycle)).
|
Keep Scene 5 conceptual. Scene 8 applies this vocabulary to the prepared example. Also note that raw plans can create artifacts without passing through Draft ([lines 629-660](../thesis/system-design-implementation.md#workflow-lifecycle)).
|
||||||
|
|
||||||
### Scene 6: Zoom through the implemented architecture
|
### Scene 6: Zoom through the implemented architecture
|
||||||
|
|
||||||
@@ -178,13 +178,13 @@ Keep Scene 5 conceptual. Scene 9 applies this vocabulary to the prepared example
|
|||||||
|
|
||||||
Say:
|
Say:
|
||||||
|
|
||||||
> This is how those concepts are organized in the implementation.
|
> Now I will show how those concepts map to the implementation.
|
||||||
>
|
>
|
||||||
> Humans and agents use the same public workflow operations.
|
> At the top, humans and agents use the same public workflow operations.
|
||||||
>
|
>
|
||||||
> The Workflow API is the front door. It exposes lifecycle operations without exposing runtime internals.
|
> Those operations enter through the Workflow API, the public boundary that keeps clients out of runtime internals.
|
||||||
>
|
>
|
||||||
> Behind it, the workflow server brings together stored records, available capabilities, and the execution core.
|
> Behind that boundary, the workflow server brings together stored records, available capabilities, and the execution core.
|
||||||
|
|
||||||
The optional NodeUse deep dive remains available through the architecture focus route and Q&A; it is not part of the timed forward sequence.
|
The optional NodeUse deep dive remains available through the architecture focus route and Q&A; it is not part of the timed forward sequence.
|
||||||
|
|
||||||
@@ -290,7 +290,7 @@ If live execution has not been completed during rehearsal, say: “The operation
|
|||||||
|
|
||||||
Say:
|
Say:
|
||||||
|
|
||||||
> Execution pauses at a typed issue_review interrupt exposing request data, allowed outcomes, request schema, and resume schema. The operator chooses submitted or revision-requested; this is a typed interrupt and resume contract, not a production approval gate, role system, or policy engine.
|
> Execution pauses at a typed issue_review interrupt exposing request data, allowed outcomes, request schema, and resume schema. The operator can Submit or Request revision. Both continue through declared workflow branches; this typed resume contract is not a production approval system.
|
||||||
|
|
||||||
Do not call the negative path “deny without resuming.” Both outcomes resume execution through different workflow branches. Do not imply that the prepared revision recording preserves the submitted branch’s run identity.
|
Do not call the negative path “deny without resuming.” Both outcomes resume execution through different workflow branches. Do not imply that the prepared revision recording preserves the submitted branch’s run identity.
|
||||||
|
|
||||||
@@ -314,9 +314,7 @@ Do not call the negative path “deny without resuming.” Both outcomes resume
|
|||||||
|
|
||||||
Say:
|
Say:
|
||||||
|
|
||||||
> On the submitted path, workflow.runs.resume continues the recorded interrupted Run. The workflow creates the report and issue-board changes, then records terminal output. Trace frames and protocol evidence remain inspectable; this is declared-boundary resumability, not arbitrary crash recovery or exactly-once execution. The revision replay is a separate prepared recording.
|
> On the submitted path, workflow.runs.resume continues the recorded interrupted Run. The workflow creates the report and issue-board changes, then records terminal output. Trace frames and protocol evidence remain inspectable; this is declared-boundary resumability, not arbitrary crash recovery or exactly-once execution.
|
||||||
|
|
||||||
For the submitted replay, the same run ID is demonstrated. The prepared revision replay currently uses `run_recorded_lda_report_revision`; describe it as a separate prepared branch recording.
|
|
||||||
|
|
||||||
### Scene 12: Explain what the evaluation proves
|
### Scene 12: Explain what the evaluation proves
|
||||||
|
|
||||||
@@ -334,13 +332,13 @@ For the submitted replay, the same run ID is demonstrated. The prepared revision
|
|||||||
|
|
||||||
**Beat:** `evaluation/findings`
|
**Beat:** `evaluation/findings`
|
||||||
**Goal:** State what the evaluation supports and what it cannot prove.
|
**Goal:** State what the evaluation supports and what it cannot prove.
|
||||||
**Anchor terms:** longitudinal evidence; not a benchmark
|
**Anchor terms:** product evolved; not a benchmark
|
||||||
|
|
||||||
Say:
|
Say:
|
||||||
|
|
||||||
> The evaluation combines conformance tests, deterministic case studies, and a manually audited external-agent campaign: 36 trials across two challenges, two hosted models, three instruction profiles, and three waves, with three attempts per cell. The author audit classified 27 trials as clean product-path passes, eight as invalid samples, and one as a failure. Invalid samples included contamination such as reading implementation files, prior artifacts, adjacent attempts, or evaluator state. Because prompts, product snapshots, and hosted conditions changed across waves, these results are longitudinal engineering evidence. They expose authoring and diagnostic gaps, not a benchmark of model success, token reduction, retry reduction, or superiority.
|
> The evaluation combines conformance tests, deterministic case studies, and a manually audited external-agent campaign. Each model, under each instruction profile, attempted each challenge three times, for 36 trials in total. The author audit then classified 27 trials as clean product-path passes, eight as invalid samples, and one as a failure. Invalid samples included contamination such as reading implementation files, prior artifacts, adjacent attempts, or evaluator state. But the prompts, product, and hosted conditions changed during the campaign. These results show what we learned while improving the product. They expose authoring and diagnostic gaps; they are not a benchmark of model success, token reduction, retry reduction, or superiority.
|
||||||
|
|
||||||
The product and prompts evolved across waves. Call this longitudinal engineering evidence, not a controlled benchmark ([lines 1287-1339](../thesis/system-design-implementation.md#formative-agent-trial-findings)).
|
The product and prompts evolved during the campaign. Say that the results show what was learned while improving the product; do not call them a controlled benchmark ([lines 1287-1339](../thesis/system-design-implementation.md#formative-agent-trial-findings)).
|
||||||
|
|
||||||
### Scene 13: Close on the bounded contribution
|
### Scene 13: Close on the bounded contribution
|
||||||
|
|
||||||
@@ -404,7 +402,7 @@ Start with the short answer. Expand only when the examiner continues.
|
|||||||
|
|
||||||
**Short answer:** They provide bounded engineering evidence about operability and failure modes, not a model benchmark.
|
**Short answer:** They provide bounded engineering evidence about operability and failure modes, not a model benchmark.
|
||||||
|
|
||||||
**Expanded answer:** The campaign used two challenges, two hosted models, three profiles, three waves, and three attempts per cell. The author manually audited product-path validity. Because prompts, product snapshots, and hosted conditions changed, the results support feasibility and longitudinal product learning, not stable model comparison or general success rates.
|
**Expanded answer:** The campaign used two challenges, two hosted models, and three profiles. Each model under each profile attempted each challenge three times. The author manually audited product-path validity. Because prompts, product snapshots, and hosted conditions changed, the results support feasibility and product learning, not stable model comparison or general success rates.
|
||||||
|
|
||||||
### 5. Is the prepared replay honest?
|
### 5. Is the prepared replay honest?
|
||||||
|
|
||||||
@@ -469,7 +467,7 @@ Start with the short answer. Expand only when the examiner continues.
|
|||||||
| Submitted replay resumes the same run | Supported | Valid for the submitted recording |
|
| Submitted replay resumes the same run | Supported | Valid for the submitted recording |
|
||||||
| Revision replay uses the same run | Do not claim | It is a separate prepared branch recording |
|
| Revision replay uses the same run | Do not claim | It is a separate prepared branch recording |
|
||||||
| Resume provides arbitrary crash recovery | Do not claim | Resume works at explicit stopped or interrupted boundaries |
|
| Resume provides arbitrary crash recovery | Do not claim | Resume works at explicit stopped or interrupted boundaries |
|
||||||
| The 36 trials estimate model success | Do not claim | They are bounded longitudinal engineering evidence |
|
| The 36 trials estimate model success | Do not claim | They are bounded evidence collected while the product evolved |
|
||||||
| The campaign measured token or retry reduction | Do not claim | These remain unmeasured design hypotheses |
|
| The campaign measured token or retry reduction | Do not claim | These remain unmeasured design hypotheses |
|
||||||
| The system is production-secure or scheduled | Do not claim | Security hardening and scheduling remain future work |
|
| The system is production-secure or scheduled | Do not claim | Security hardening and scheduling remain future work |
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,8 @@ execution goes next and recommends routing each missing outcome, using
|
|||||||
The reviewed repair is:
|
The reviewed repair is:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
wf draft set-route lda_report_workflow \
|
uv run wf --url http://127.0.0.1:8765/rpc \
|
||||||
|
draft set-route lda_report_workflow \
|
||||||
--revision 3 \
|
--revision 3 \
|
||||||
--step analyze \
|
--step analyze \
|
||||||
--outcome ok \
|
--outcome ok \
|
||||||
|
|||||||
@@ -80,15 +80,16 @@ export const GuidedProductMoment = ({
|
|||||||
const facts = projectDemoRunFacts(demo);
|
const facts = projectDemoRunFacts(demo);
|
||||||
const runResume = demo.state.events.find((event) => event.stage === "run_resume");
|
const runResume = demo.state.events.find((event) => event.stage === "run_resume");
|
||||||
const revisionRequested = approvalActions?.state === "revision_requested";
|
const revisionRequested = approvalActions?.state === "revision_requested";
|
||||||
|
const isReplay = demo.state.mode === "replay";
|
||||||
// Replay revision evidence has a separate recording identity; do not let
|
// Replay revision evidence has a separate recording identity; do not let
|
||||||
// the normal submitted-branch lens imply continuity for that branch.
|
// the normal submitted-branch lens imply continuity for that branch.
|
||||||
const eyebrow = revisionRequested
|
const eyebrow = revisionRequested
|
||||||
? demo.state.mode === "replay"
|
? isReplay
|
||||||
? "Prepared branch"
|
? "Prepared branch"
|
||||||
: "Live branch"
|
: "Live branch"
|
||||||
: lens.eyebrow;
|
: lens.eyebrow;
|
||||||
const headline = revisionRequested
|
const headline = revisionRequested
|
||||||
? demo.state.mode === "replay"
|
? isReplay
|
||||||
? "Separate prepared revision recording"
|
? "Separate prepared revision recording"
|
||||||
: "Live revision branch resumes the run"
|
: "Live revision branch resumes the run"
|
||||||
: lens.headline;
|
: lens.headline;
|
||||||
@@ -106,7 +107,7 @@ export const GuidedProductMoment = ({
|
|||||||
<header className="guided-product-moment__header">
|
<header className="guided-product-moment__header">
|
||||||
<span>{eyebrow}</span>
|
<span>{eyebrow}</span>
|
||||||
<strong>{headline}</strong>
|
<strong>{headline}</strong>
|
||||||
<p>{statusCopy(moment, approvalActions, demo.state.mode === "replay")}</p>
|
<p>{statusCopy(moment, approvalActions, isReplay)}</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className="guided-product-moment__primary">
|
<div className="guided-product-moment__primary">
|
||||||
|
|||||||
@@ -268,7 +268,8 @@ describe("PresentationRoute", () => {
|
|||||||
.toHaveAttribute("data-phase", "deployment");
|
.toHaveAttribute("data-phase", "deployment");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("records a Deployment run request locally without an RPC", async () => {
|
it("starts the shared prepared workflow action without advancing the Deployment beat", async () => {
|
||||||
|
window.sessionStorage.setItem("lda.workflowConsole.target", "http://127.0.0.1:8765/rpc");
|
||||||
window.location.hash = "#scene/prepared-lifecycle/deployment";
|
window.location.hash = "#scene/prepared-lifecycle/deployment";
|
||||||
const { PresentationRoute } = await import("./PresentationRoute.js");
|
const { PresentationRoute } = await import("./PresentationRoute.js");
|
||||||
render(<PresentationRoute />);
|
render(<PresentationRoute />);
|
||||||
@@ -277,11 +278,16 @@ describe("PresentationRoute", () => {
|
|||||||
|
|
||||||
await userEvent.click(screen.getByRole("button", { name: /send message/i }));
|
await userEvent.click(screen.getByRole("button", { name: /send message/i }));
|
||||||
|
|
||||||
expect(await screen.findByRole("status")).toHaveTextContent(
|
expect(await screen.findByText("Prepared run receipt added. Continue when you are ready."))
|
||||||
"Run request prepared for the next execution slice.",
|
.toBeInTheDocument();
|
||||||
);
|
expect(window.location.hash).toBe("#scene/prepared-lifecycle/deployment");
|
||||||
|
expect(screen.getByText(/three proposed issues are ready for review/i)).toBeInTheDocument();
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockedCallOperation.mock.calls.some(([operation]) => operation === "workflow.deployments.inspect"))
|
||||||
|
.toBe(true);
|
||||||
expect(mockedCallOperation.mock.calls.some(([operation]) => operation === "workflow.runs.start"))
|
expect(mockedCallOperation.mock.calls.some(([operation]) => operation === "workflow.runs.start"))
|
||||||
.toBe(false);
|
.toBe(true);
|
||||||
|
}, { timeout: 3000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps Diagnose submission on the current beat", async () => {
|
it("keeps Diagnose submission on the current beat", async () => {
|
||||||
|
|||||||
@@ -109,6 +109,11 @@ export const PresentationStage = ({
|
|||||||
motionDisabled={state.motionDisabled}
|
motionDisabled={state.motionDisabled}
|
||||||
approvalActions={approvalActions}
|
approvalActions={approvalActions}
|
||||||
onPreparedLifecycleAdvance={onPreparedLifecycleAdvance}
|
onPreparedLifecycleAdvance={onPreparedLifecycleAdvance}
|
||||||
|
onRunPreparedWorkflow={
|
||||||
|
demoRail.kind === "action" && timelineAgent
|
||||||
|
? () => timelineAgent.runPreparedWorkflow(demoRail.mode)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -30,15 +30,16 @@ describe("RunInputFileBrowser", () => {
|
|||||||
|
|
||||||
const files = within(browser).getByRole("list", { name: /included in prepared run/i });
|
const files = within(browser).getByRole("list", { name: /included in prepared run/i });
|
||||||
expect(within(files).getAllByRole("listitem")).toHaveLength(2);
|
expect(within(files).getAllByRole("listitem")).toHaveLength(2);
|
||||||
expect(within(files).getAllByText("selected")).toHaveLength(2);
|
expect(within(files).getAllByText("selected")).toHaveLength(1);
|
||||||
expect(within(files).getAllByRole("button")).toHaveLength(2);
|
expect(within(files).getAllByRole("button")).toHaveLength(2);
|
||||||
expect(within(files).queryAllByRole("link")).toHaveLength(0);
|
expect(within(files).queryAllByRole("link")).toHaveLength(0);
|
||||||
for (const path of ["docs/project-brief.md", "docs/architecture-notes.md"]) {
|
for (const path of ["docs/project-brief.md", "docs/architecture-notes.md"]) {
|
||||||
const row = within(files).getByText(path).closest("li");
|
const row = within(files).getByText(path).closest("li");
|
||||||
expect(row).not.toBeNull();
|
expect(row).not.toBeNull();
|
||||||
expect(row).toHaveAttribute("data-file-path", path);
|
expect(row).toHaveAttribute("data-file-path", path);
|
||||||
expect(row).toHaveTextContent(/selected/i);
|
|
||||||
}
|
}
|
||||||
|
expect(within(files).getByText("docs/project-brief.md").closest("li")).toHaveTextContent(/selected/i);
|
||||||
|
expect(within(files).getByText("docs/architecture-notes.md").closest("li")).not.toHaveTextContent(/selected/i);
|
||||||
|
|
||||||
const destination = within(browser).getByRole("group", { name: /workflow output/i });
|
const destination = within(browser).getByRole("group", { name: /workflow output/i });
|
||||||
expect(destination).toHaveTextContent("artifacts/issue-board.json");
|
expect(destination).toHaveTextContent("artifacts/issue-board.json");
|
||||||
@@ -57,6 +58,11 @@ describe("RunInputFileBrowser", () => {
|
|||||||
expect(preview).toHaveTextContent(/architecture-notes\.md/i);
|
expect(preview).toHaveTextContent(/architecture-notes\.md/i);
|
||||||
expect(preview).toHaveTextContent(/fixture preview/i);
|
expect(preview).toHaveTextContent(/fixture preview/i);
|
||||||
expect(preview).toHaveTextContent(/not execution evidence/i);
|
expect(preview).toHaveTextContent(/not execution evidence/i);
|
||||||
|
expect(screen.getByRole("button", { name: /architecture-notes\.md/i })).toHaveAttribute(
|
||||||
|
"aria-pressed",
|
||||||
|
"true",
|
||||||
|
);
|
||||||
|
expect(screen.getByRole("button", { name: /project-brief\.md/i })).not.toHaveTextContent(/selected/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows an honest empty preview for files absent from the fixture catalog", () => {
|
it("shows an honest empty preview for files absent from the fixture catalog", () => {
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ export const RunInputFileBrowser = ({
|
|||||||
>
|
>
|
||||||
<span className="run-input-file-browser__icon" aria-hidden="true">md</span>
|
<span className="run-input-file-browser__icon" aria-hidden="true">md</span>
|
||||||
<code>{path}</code>
|
<code>{path}</code>
|
||||||
|
{path === visibleSelectedPath ? (
|
||||||
<span className="run-input-file-browser__marker">selected</span>
|
<span className="run-input-file-browser__marker">selected</span>
|
||||||
|
) : null}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ type SceneBodyProps = {
|
|||||||
readonly motionDisabled: boolean;
|
readonly motionDisabled: boolean;
|
||||||
readonly approvalActions?: DemoApprovalActions | undefined;
|
readonly approvalActions?: DemoApprovalActions | undefined;
|
||||||
readonly onPreparedLifecycleAdvance?: (() => void) | undefined;
|
readonly onPreparedLifecycleAdvance?: (() => void) | undefined;
|
||||||
|
readonly onRunPreparedWorkflow?: (() => Promise<void>) | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const workflowDemoSceneIds = new Set([
|
const workflowDemoSceneIds = new Set([
|
||||||
@@ -290,7 +291,7 @@ const assertNever = (value: never): never => {
|
|||||||
throw new Error(`Unexpected view: ${value}`);
|
throw new Error(`Unexpected view: ${value}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SceneBody = ({ location, demo, selectedNodeId, selectNode, openEvidence, openDiscussion, onFocusPathChange, motionDisabled, approvalActions, onPreparedLifecycleAdvance }: SceneBodyProps) => {
|
export const SceneBody = ({ location, demo, selectedNodeId, selectNode, openEvidence, openDiscussion, onFocusPathChange, motionDisabled, approvalActions, onPreparedLifecycleAdvance, onRunPreparedWorkflow }: SceneBodyProps) => {
|
||||||
const sceneId = location.kind === "main" ? location.sceneId : "positioning";
|
const sceneId = location.kind === "main" ? location.sceneId : "positioning";
|
||||||
const beatId = location.kind === "main" ? location.beatId : "landscape";
|
const beatId = location.kind === "main" ? location.beatId : "landscape";
|
||||||
const scene = findScene(sceneId) ?? findScene("thesis")!;
|
const scene = findScene(sceneId) ?? findScene("thesis")!;
|
||||||
@@ -331,6 +332,7 @@ export const SceneBody = ({ location, demo, selectedNodeId, selectNode, openEvid
|
|||||||
scene={scene}
|
scene={scene}
|
||||||
beat={beat}
|
beat={beat}
|
||||||
onAdvance={onPreparedLifecycleAdvance}
|
onAdvance={onPreparedLifecycleAdvance}
|
||||||
|
onRunPreparedWorkflow={onRunPreparedWorkflow}
|
||||||
discussionRail={scene.id === "prepared-lifecycle" ? discussionLinks : undefined}
|
discussionRail={scene.id === "prepared-lifecycle" ? discussionLinks : undefined}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { AssistantOperatorThread } from "../chat/AssistantOperatorThread.js";
|
|||||||
import {
|
import {
|
||||||
authoringToolGroupId,
|
authoringToolGroupId,
|
||||||
projectPreparedAuthoringThread,
|
projectPreparedAuthoringThread,
|
||||||
|
projectPreparedRunRequestExchange,
|
||||||
type AuthoringPhaseId,
|
type AuthoringPhaseId,
|
||||||
} from "./authoring-recording.js";
|
} from "./authoring-recording.js";
|
||||||
import {
|
import {
|
||||||
@@ -17,6 +18,7 @@ type AuthoringConversationProps = {
|
|||||||
readonly requestOverride?: string | undefined;
|
readonly requestOverride?: string | undefined;
|
||||||
readonly requestOverrides?: PreparedLifecycleSubmittedOverrides | undefined;
|
readonly requestOverrides?: PreparedLifecycleSubmittedOverrides | undefined;
|
||||||
readonly scrollMode?: "active" | "start" | undefined;
|
readonly scrollMode?: "active" | "start" | undefined;
|
||||||
|
readonly runRequested?: string | null | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Renders the same prepared conversation at full-stage or compact-dock scale. */
|
/** Renders the same prepared conversation at full-stage or compact-dock scale. */
|
||||||
@@ -27,17 +29,25 @@ export const AuthoringConversation = ({
|
|||||||
requestOverride,
|
requestOverride,
|
||||||
requestOverrides,
|
requestOverrides,
|
||||||
scrollMode,
|
scrollMode,
|
||||||
}: AuthoringConversationProps) => (
|
runRequested,
|
||||||
<AssistantOperatorThread
|
}: AuthoringConversationProps) => {
|
||||||
mode={surface === "stage" ? "full" : "dock"}
|
const messages = [
|
||||||
surface={surface}
|
...projectPreparedAuthoringThread(
|
||||||
messages={projectPreparedAuthoringThread(
|
|
||||||
recordingPhaseForStep(throughPhase),
|
recordingPhaseForStep(throughPhase),
|
||||||
requestOverride,
|
requestOverride,
|
||||||
requestOverrides,
|
requestOverrides,
|
||||||
)}
|
),
|
||||||
|
...(runRequested ? projectPreparedRunRequestExchange(runRequested) : []),
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AssistantOperatorThread
|
||||||
|
mode={surface === "stage" ? "full" : "dock"}
|
||||||
|
surface={surface}
|
||||||
|
messages={messages}
|
||||||
activeToolGroupId={authoringToolGroupId(recordingPhaseForStep(activePhase))}
|
activeToolGroupId={authoringToolGroupId(recordingPhaseForStep(activePhase))}
|
||||||
scrollMode={scrollMode}
|
scrollMode={runRequested ? "end" : scrollMode}
|
||||||
ariaLabel="prepared authoring conversation"
|
ariaLabel="prepared authoring conversation"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|||||||
+22
-1
@@ -7,7 +7,12 @@ import { PreparedAuthoringLifecycleScene } from "./PreparedAuthoringLifecycleSce
|
|||||||
|
|
||||||
afterEach(() => cleanup());
|
afterEach(() => cleanup());
|
||||||
|
|
||||||
const renderBeat = (beatId: string, onAdvance?: () => void, discussionRail?: ReactNode) => {
|
const renderBeat = (
|
||||||
|
beatId: string,
|
||||||
|
onAdvance?: () => void,
|
||||||
|
discussionRail?: ReactNode,
|
||||||
|
onRunPreparedWorkflow?: () => Promise<void>,
|
||||||
|
) => {
|
||||||
const scene = findScene("prepared-lifecycle");
|
const scene = findScene("prepared-lifecycle");
|
||||||
const beat = findBeat("prepared-lifecycle", beatId);
|
const beat = findBeat("prepared-lifecycle", beatId);
|
||||||
if (!scene || !beat) throw new Error(`missing prepared-lifecycle/${beatId}`);
|
if (!scene || !beat) throw new Error(`missing prepared-lifecycle/${beatId}`);
|
||||||
@@ -16,6 +21,7 @@ const renderBeat = (beatId: string, onAdvance?: () => void, discussionRail?: Rea
|
|||||||
scene={scene}
|
scene={scene}
|
||||||
beat={beat}
|
beat={beat}
|
||||||
onAdvance={onAdvance}
|
onAdvance={onAdvance}
|
||||||
|
onRunPreparedWorkflow={onRunPreparedWorkflow}
|
||||||
discussionRail={discussionRail}
|
discussionRail={discussionRail}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
@@ -173,6 +179,21 @@ describe("PreparedAuthoringLifecycleScene", () => {
|
|||||||
expect(onAdvance).toHaveBeenCalledTimes(advances ? 1 : 0);
|
expect(onAdvance).toHaveBeenCalledTimes(advances ? 1 : 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("answers the terminal run request without advancing the presentation", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
const onAdvance = vi.fn();
|
||||||
|
const onRunPreparedWorkflow = vi.fn().mockResolvedValue(undefined);
|
||||||
|
renderBeat("deployment", onAdvance, undefined, onRunPreparedWorkflow);
|
||||||
|
|
||||||
|
await user.click(screen.getByRole("button", { name: /send message/i }));
|
||||||
|
|
||||||
|
expect(screen.getByRole("textbox", { name: /message to authoring assistant/i })).toHaveValue("");
|
||||||
|
expect(screen.getByRole("button", { name: /run.*1 tool call/i })).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/three proposed issues are ready for review/i)).toBeInTheDocument();
|
||||||
|
expect(onRunPreparedWorkflow).toHaveBeenCalledOnce();
|
||||||
|
expect(onAdvance).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
["discover", "Discover"],
|
["discover", "Discover"],
|
||||||
["draft", "Draft"],
|
["draft", "Draft"],
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type PreparedAuthoringLifecycleSceneProps = {
|
|||||||
readonly scene: SceneDefinition;
|
readonly scene: SceneDefinition;
|
||||||
readonly beat: SceneBeatDefinition;
|
readonly beat: SceneBeatDefinition;
|
||||||
readonly onAdvance?: (() => void) | undefined;
|
readonly onAdvance?: (() => void) | undefined;
|
||||||
|
readonly onRunPreparedWorkflow?: (() => Promise<void>) | undefined;
|
||||||
readonly discussionRail?: ReactNode;
|
readonly discussionRail?: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ const steps = [
|
|||||||
* Each beat shows a persistent prepared assistant beside one dominant phase
|
* Each beat shows a persistent prepared assistant beside one dominant phase
|
||||||
* projection sourced from the prepared authoring recording.
|
* projection sourced from the prepared authoring recording.
|
||||||
*/
|
*/
|
||||||
export const PreparedAuthoringLifecycleScene = ({ scene, beat, onAdvance, discussionRail }: PreparedAuthoringLifecycleSceneProps) => {
|
export const PreparedAuthoringLifecycleScene = ({ scene, beat, onAdvance, onRunPreparedWorkflow, discussionRail }: PreparedAuthoringLifecycleSceneProps) => {
|
||||||
const [messageState, dispatch] = useReducer(
|
const [messageState, dispatch] = useReducer(
|
||||||
preparedLifecycleMessageReducer,
|
preparedLifecycleMessageReducer,
|
||||||
initialPreparedLifecycleMessageState,
|
initialPreparedLifecycleMessageState,
|
||||||
@@ -81,7 +82,12 @@ export const PreparedAuthoringLifecycleScene = ({ scene, beat, onAdvance, discus
|
|||||||
dispatch({ type: "artifact_submitted" });
|
dispatch({ type: "artifact_submitted" });
|
||||||
onAdvance?.();
|
onAdvance?.();
|
||||||
}
|
}
|
||||||
if (step === "deployment") dispatch({ type: "run_requested" });
|
if (step === "deployment") {
|
||||||
|
dispatch({ type: "run_requested" });
|
||||||
|
// This is the same mode-aware action as the footer control; the
|
||||||
|
// presentation remains on this beat so the presenter advances it.
|
||||||
|
void onRunPreparedWorkflow?.();
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="prepared-lifecycle-scene__presentation">
|
<div className="prepared-lifecycle-scene__presentation">
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ describe("PresentationAssistantPane", () => {
|
|||||||
);
|
);
|
||||||
expect(screen.getByRole("heading", { name: /authoring assistant/i })).toBeInTheDocument();
|
expect(screen.getByRole("heading", { name: /authoring assistant/i })).toBeInTheDocument();
|
||||||
expect(screen.getByText(/current phase: validate/i)).toBeInTheDocument();
|
expect(screen.getByText(/current phase: validate/i)).toBeInTheDocument();
|
||||||
expect(screen.getByText(/prepared replay only/i)).toBeInTheDocument();
|
expect(screen.getByText(/prepared assistant transcript/i)).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/final run request uses the configured demo target/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("exposes an explicit support role when composed in the lifecycle scene", () => {
|
it("exposes an explicit support role when composed in the lifecycle scene", () => {
|
||||||
@@ -129,6 +130,15 @@ describe("PresentationAssistantPane", () => {
|
|||||||
cleanup();
|
cleanup();
|
||||||
renderPane("deployment", { runRequested: "Run this deployment" });
|
renderPane("deployment", { runRequested: "Run this deployment" });
|
||||||
expect(screen.getByRole("button", { name: /send message/i })).toBeDisabled();
|
expect(screen.getByRole("button", { name: /send message/i })).toBeDisabled();
|
||||||
|
expect(screen.getByText("Run this deployment")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/starting lda_report_case_study\.default with the prepared input/i))
|
||||||
|
.toBeInTheDocument();
|
||||||
|
const runTools = screen.getByRole("button", { name: /run.*1 tool call/i });
|
||||||
|
expect(runTools).toHaveAttribute("aria-expanded", "false");
|
||||||
|
await user.click(runTools);
|
||||||
|
await user.click(screen.getByRole("button", { name: /startRun/i }));
|
||||||
|
expect(screen.getByText(/run_recorded_lda_report/i)).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/paused at the typed issue-review boundary/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders submitted overrides while keeping the replay tools canonical", () => {
|
it("renders submitted overrides while keeping the replay tools canonical", () => {
|
||||||
|
|||||||
@@ -58,7 +58,10 @@ export const PresentationAssistantPane = ({
|
|||||||
};
|
};
|
||||||
const { submit, handleKeyDown } = usePreparedComposerSubmit(
|
const { submit, handleKeyDown } = usePreparedComposerSubmit(
|
||||||
canSubmit,
|
canSubmit,
|
||||||
() => onSubmit(draft),
|
() => {
|
||||||
|
onSubmit(draft);
|
||||||
|
setDraft("");
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -74,7 +77,7 @@ export const PresentationAssistantPane = ({
|
|||||||
<h2>Authoring assistant</h2>
|
<h2>Authoring assistant</h2>
|
||||||
<p>Current phase: {phaseLabels[phase]}</p>
|
<p>Current phase: {phaseLabels[phase]}</p>
|
||||||
<p className="presentation-assistant-pane__disclosure">
|
<p className="presentation-assistant-pane__disclosure">
|
||||||
Prepared replay only. No live actions or RPC calls.
|
Prepared assistant transcript. The final run request uses the configured demo target.
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
<div className="presentation-assistant-pane__conversation">
|
<div className="presentation-assistant-pane__conversation">
|
||||||
@@ -83,6 +86,7 @@ export const PresentationAssistantPane = ({
|
|||||||
activePhase={phase}
|
activePhase={phase}
|
||||||
surface="stage"
|
surface="stage"
|
||||||
requestOverrides={submittedOverrides}
|
requestOverrides={submittedOverrides}
|
||||||
|
runRequested={runRequested}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<form className="presentation-assistant-pane__composer" onSubmit={submit}>
|
<form className="presentation-assistant-pane__composer" onSubmit={submit}>
|
||||||
@@ -106,7 +110,7 @@ export const PresentationAssistantPane = ({
|
|||||||
</p>
|
</p>
|
||||||
{runRequested !== null ? (
|
{runRequested !== null ? (
|
||||||
<p role="status" className="presentation-assistant-pane__run-status">
|
<p role="status" className="presentation-assistant-pane__run-status">
|
||||||
Run request prepared for the next execution slice.
|
Prepared run receipt added. Continue when you are ready.
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
authoringToolGroupId,
|
authoringToolGroupId,
|
||||||
projectPreparedAuthoring,
|
projectPreparedAuthoring,
|
||||||
projectPreparedAuthoringThread,
|
projectPreparedAuthoringThread,
|
||||||
|
projectPreparedRunRequestExchange,
|
||||||
type AuthoringPhaseId,
|
type AuthoringPhaseId,
|
||||||
} from "./authoring-recording.js";
|
} from "./authoring-recording.js";
|
||||||
|
|
||||||
@@ -225,3 +226,27 @@ describe("projectPreparedAuthoringThread", () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("projectPreparedRunRequestExchange", () => {
|
||||||
|
it("projects the prepared start receipt and typed interrupt without navigation actions", () => {
|
||||||
|
const messages = projectPreparedRunRequestExchange("Run the saved deployment.");
|
||||||
|
const parts = messages.flatMap((message) => message.parts);
|
||||||
|
|
||||||
|
expect(messages[0]).toMatchObject({ role: "user", parts: [{ text: "Run the saved deployment." }] });
|
||||||
|
expect(parts).toContainEqual(expect.objectContaining({
|
||||||
|
type: "tool-call",
|
||||||
|
call: expect.objectContaining({ name: "startRun" }),
|
||||||
|
}));
|
||||||
|
expect(parts).toContainEqual(expect.objectContaining({
|
||||||
|
type: "tool-result",
|
||||||
|
result: expect.objectContaining({
|
||||||
|
output: expect.objectContaining({
|
||||||
|
run_id: "run_recorded_lda_report",
|
||||||
|
status: "interrupted",
|
||||||
|
resume_readiness: "ready",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
expect(parts.some((part) => part.type === "presentation-action")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -354,5 +354,62 @@ export const projectPreparedAuthoringThread = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Projects the terminal prepared request into the same chat vocabulary as a
|
||||||
|
* live run, while retaining the stable replay run identity used by later scenes.
|
||||||
|
*/
|
||||||
|
export const projectPreparedRunRequestExchange = (
|
||||||
|
request: string,
|
||||||
|
): readonly AgentMessage[] => {
|
||||||
|
const callId = "authoring-run-request-call";
|
||||||
|
return [
|
||||||
|
agentTextMessage("authoring-run-request-user", "user", request),
|
||||||
|
agentTextMessage(
|
||||||
|
"authoring-run-request-starting",
|
||||||
|
"assistant",
|
||||||
|
"Starting lda_report_case_study.default with the prepared input.",
|
||||||
|
),
|
||||||
|
{
|
||||||
|
// The authoring prefix reuses the compact phase tool-group treatment;
|
||||||
|
// presenters can expand the real-shaped receipt only when useful.
|
||||||
|
id: "authoring-run-tools",
|
||||||
|
role: "assistant",
|
||||||
|
parts: [
|
||||||
|
agentToolCallPart(callId, "startRun", {
|
||||||
|
operation: "workflow.runs.start",
|
||||||
|
deployment_id: "lda_report_case_study.default",
|
||||||
|
input_file: "examples/lda_report_workflow/run-input.json",
|
||||||
|
equivalent_cli:
|
||||||
|
"uv run wf --url http://127.0.0.1:8765/rpc run start lda_report_case_study.default --input-file examples/lda_report_workflow/run-input.json",
|
||||||
|
}),
|
||||||
|
agentToolResultPart(callId, "startRun", "success", {
|
||||||
|
artifact_id: "lda_report_case_study",
|
||||||
|
artifact_version: 1,
|
||||||
|
deployment_id: "lda_report_case_study.default",
|
||||||
|
diagnostics: [],
|
||||||
|
interrupt: {
|
||||||
|
id: "interrupt:review_issues",
|
||||||
|
kind: "issue_review",
|
||||||
|
node_id: "review_issues",
|
||||||
|
outcomes: ["submitted", "cancelled"],
|
||||||
|
proposed_issue_count: 3,
|
||||||
|
resumable: true,
|
||||||
|
typed: true,
|
||||||
|
},
|
||||||
|
resume_readiness: "ready",
|
||||||
|
run_id: "run_recorded_lda_report",
|
||||||
|
status: "interrupted",
|
||||||
|
trace_count: 6,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
agentTextMessage(
|
||||||
|
"authoring-run-request-paused",
|
||||||
|
"assistant",
|
||||||
|
"The run started and paused at the typed issue-review boundary. Three proposed issues are ready for review.",
|
||||||
|
),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
/** Returns the full prepared authoring recording. */
|
/** Returns the full prepared authoring recording. */
|
||||||
export const projectPreparedAuthoring = (): readonly PreparedAuthoringPhase[] => recording;
|
export const projectPreparedAuthoring = (): readonly PreparedAuthoringPhase[] => recording;
|
||||||
|
|||||||
+5
-4
@@ -43,14 +43,14 @@ describe("prepared lifecycle staged message state", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("preserves edited draft text exactly when submitting draft", () => {
|
it("preserves submitted text exactly and clears the composer draft", () => {
|
||||||
const edited = preparedLifecycleMessageReducer(initialPreparedLifecycleMessageState, {
|
const edited = preparedLifecycleMessageReducer(initialPreparedLifecycleMessageState, {
|
||||||
type: "draft_edited",
|
type: "draft_edited",
|
||||||
draft: " Check only the report binding. ",
|
draft: " Check only the report binding. ",
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(preparedLifecycleMessageReducer(edited, { type: "draft_submitted" })).toEqual({
|
expect(preparedLifecycleMessageReducer(edited, { type: "draft_submitted" })).toEqual({
|
||||||
draft: edited.draft,
|
draft: "",
|
||||||
submittedOverrides: { validate: edited.draft },
|
submittedOverrides: { validate: edited.draft },
|
||||||
runRequested: null,
|
runRequested: null,
|
||||||
});
|
});
|
||||||
@@ -63,7 +63,7 @@ describe("prepared lifecycle staged message state", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(preparedLifecycleMessageReducer(state, { type: "artifact_submitted" })).toEqual({
|
expect(preparedLifecycleMessageReducer(state, { type: "artifact_submitted" })).toEqual({
|
||||||
draft: state.draft,
|
draft: "",
|
||||||
submittedOverrides: { deployment: state.draft },
|
submittedOverrides: { deployment: state.draft },
|
||||||
runRequested: null,
|
runRequested: null,
|
||||||
});
|
});
|
||||||
@@ -76,7 +76,7 @@ describe("prepared lifecycle staged message state", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(preparedLifecycleMessageReducer(state, { type: "discover_submitted" })).toEqual({
|
expect(preparedLifecycleMessageReducer(state, { type: "discover_submitted" })).toEqual({
|
||||||
draft: state.draft,
|
draft: "",
|
||||||
submittedOverrides: { discover: state.draft },
|
submittedOverrides: { discover: state.draft },
|
||||||
runRequested: null,
|
runRequested: null,
|
||||||
});
|
});
|
||||||
@@ -106,6 +106,7 @@ describe("prepared lifecycle staged message state", () => {
|
|||||||
const requested = preparedLifecycleMessageReducer(state, { type: "run_requested" });
|
const requested = preparedLifecycleMessageReducer(state, { type: "run_requested" });
|
||||||
|
|
||||||
expect(requested.runRequested).toBe("Run this deployment");
|
expect(requested.runRequested).toBe("Run this deployment");
|
||||||
|
expect(requested.draft).toBe("");
|
||||||
expect(requested.submittedOverrides).toEqual({});
|
expect(requested.submittedOverrides).toEqual({});
|
||||||
expect(preparedLifecycleMessageReducer(requested, { type: "run_requested" })).toBe(requested);
|
expect(preparedLifecycleMessageReducer(requested, { type: "run_requested" })).toBe(requested);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ const submitOverride = (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
// The submitted text is preserved in the transcript override; leaving it
|
||||||
|
// in the composer would leak the previous phase's request into the next.
|
||||||
|
draft: "",
|
||||||
submittedOverrides: {
|
submittedOverrides: {
|
||||||
...state.submittedOverrides,
|
...state.submittedOverrides,
|
||||||
[destination]: state.draft,
|
[destination]: state.draft,
|
||||||
@@ -79,7 +82,7 @@ export const preparedLifecycleMessageReducer = (
|
|||||||
return submitOverride(state, "deployment");
|
return submitOverride(state, "deployment");
|
||||||
case "run_requested":
|
case "run_requested":
|
||||||
return state.runRequested === null && hasText(state.draft)
|
return state.runRequested === null && hasText(state.draft)
|
||||||
? { ...state, runRequested: state.draft }
|
? { ...state, draft: "", runRequested: state.draft }
|
||||||
: state;
|
: state;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ type PreparedComposerSubmitHandlers = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const PREPARED_COMPOSER_HELP =
|
export const PREPARED_COMPOSER_HELP =
|
||||||
"Shift+Enter adds a new line. This is a deterministic prepared replay, not a live model request.";
|
"Shift+Enter adds a new line. Responses are prepared; the final run request may use the configured workflow target.";
|
||||||
|
|
||||||
/** Shares the prepared-replay composer keyboard and form submission contract. */
|
/** Shares the prepared-replay composer keyboard and form submission contract. */
|
||||||
export const usePreparedComposerSubmit = (
|
export const usePreparedComposerSubmit = (
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ describe("AssistantOperatorThread", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can show the latest response in a static comparison transcript", async () => {
|
it("can show the latest response even when an authoring group is synchronized", async () => {
|
||||||
const setScrollTop = vi.fn();
|
const setScrollTop = vi.fn();
|
||||||
const descriptors = {
|
const descriptors = {
|
||||||
scrollTop: Object.getOwnPropertyDescriptor(HTMLDivElement.prototype, "scrollTop"),
|
scrollTop: Object.getOwnPropertyDescriptor(HTMLDivElement.prototype, "scrollTop"),
|
||||||
@@ -246,7 +246,14 @@ describe("AssistantOperatorThread", () => {
|
|||||||
scrollHeight: { configurable: true, get: () => 240 },
|
scrollHeight: { configurable: true, get: () => 240 },
|
||||||
clientHeight: { configurable: true, get: () => 80 },
|
clientHeight: { configurable: true, get: () => 80 },
|
||||||
});
|
});
|
||||||
render(<AssistantOperatorThread mode="dock" messages={[]} scrollMode="end" />);
|
render(
|
||||||
|
<AssistantOperatorThread
|
||||||
|
mode="dock"
|
||||||
|
messages={[]}
|
||||||
|
activeToolGroupId="authoring-deployment"
|
||||||
|
scrollMode="end"
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
await waitFor(() => expect(setScrollTop).toHaveBeenCalledWith(160));
|
await waitFor(() => expect(setScrollTop).toHaveBeenCalledWith(160));
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -276,9 +276,9 @@ export const AssistantOperatorThread = ({
|
|||||||
const viewport = viewportRef.current;
|
const viewport = viewportRef.current;
|
||||||
if (!viewport) return;
|
if (!viewport) return;
|
||||||
|
|
||||||
// Static comparison transcripts should show their final answer; live
|
// A terminal exchange must override phase anchoring so its result and
|
||||||
// presentation docks instead keep the beat-owned tool group in view.
|
// final assistant response remain visible after the composer submits.
|
||||||
if (!activeToolGroupId && scrollMode === "end") {
|
if (scrollMode === "end") {
|
||||||
viewport.scrollTop = Math.max(0, viewport.scrollHeight - viewport.clientHeight);
|
viewport.scrollTop = Math.max(0, viewport.scrollHeight - viewport.clientHeight);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ describe("PresenterNote", () => {
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByRole("region", { name: "Beat goal" })).toHaveTextContent(note.goal);
|
const goal = screen.getByRole("region", { name: "Beat goal" });
|
||||||
|
expect(goal).toHaveTextContent(note.goal.replaceAll("**", ""));
|
||||||
|
expect(within(goal).getByText("one public operation").tagName).toBe("STRONG");
|
||||||
|
|
||||||
const anchors = screen.getByRole("region", { name: "Anchor terms" });
|
const anchors = screen.getByRole("region", { name: "Anchor terms" });
|
||||||
const anchorList = within(anchors).getByRole("list");
|
const anchorList = within(anchors).getByRole("list");
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const PresenterNote = ({ note, cumulativeSeconds, next, covered, onCovere
|
|||||||
|
|
||||||
<section className="presenter-note__goal" aria-labelledby="presenter-goal">
|
<section className="presenter-note__goal" aria-labelledby="presenter-goal">
|
||||||
<span id="presenter-goal">Beat goal</span>
|
<span id="presenter-goal">Beat goal</span>
|
||||||
<p>{note.goal}</p>
|
<div className="presenter-note__goal-copy"><ReactMarkdown>{note.goal}</ReactMarkdown></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="presenter-note__anchors" aria-labelledby="presenter-anchors">
|
<section className="presenter-note__anchors" aria-labelledby="presenter-anchors">
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export const presenterNotes = [
|
|||||||
"thesis",
|
"thesis",
|
||||||
"title",
|
"title",
|
||||||
15,
|
15,
|
||||||
"Separate the AI-agent ambition from the implemented contribution.",
|
"Separate the **AI-agent ambition** from the implemented contribution.",
|
||||||
["AI-agent goal", "platform underneath"],
|
["AI-agent goal", "platform underneath"],
|
||||||
"The title describes the original goal: an AI agent for workspace automation. My contribution is the platform underneath that agent.",
|
"The title describes the original goal: an AI agent for workspace automation. My contribution is the platform underneath that agent.",
|
||||||
["Thesis Abstract and Introduction"],
|
["Thesis Abstract and Introduction"],
|
||||||
@@ -72,7 +72,7 @@ export const presenterNotes = [
|
|||||||
"thesis",
|
"thesis",
|
||||||
"substrate",
|
"substrate",
|
||||||
15,
|
15,
|
||||||
"State what the platform lets its users do.",
|
"State **what the platform lets its users do**.",
|
||||||
["agents and humans", "build, run, inspect"],
|
["agents and humans", "build, run, inspect"],
|
||||||
"It lets agents and humans build workflows, run them, and inspect what happened.",
|
"It lets agents and humans build workflows, run them, and inspect what happened.",
|
||||||
["Thesis Abstract and Introduction", "Thesis Contributions"],
|
["Thesis Abstract and Introduction", "Thesis Contributions"],
|
||||||
@@ -82,7 +82,7 @@ export const presenterNotes = [
|
|||||||
"problem",
|
"problem",
|
||||||
"direct-actions",
|
"direct-actions",
|
||||||
15,
|
15,
|
||||||
"Show why one successful chat is not yet automation.",
|
"Show why **one successful chat is not yet automation**.",
|
||||||
["tool calls", "not reusable"],
|
["tool calls", "not reusable"],
|
||||||
"Like the chat example, an agent can call tools and finish one task. But that conversation is not yet a reusable workflow.",
|
"Like the chat example, an agent can call tools and finish one task. But that conversation is not yet a reusable workflow.",
|
||||||
["Thesis Problem Statement and Requirements"],
|
["Thesis Problem Statement and Requirements"],
|
||||||
@@ -92,7 +92,7 @@ export const presenterNotes = [
|
|||||||
"problem",
|
"problem",
|
||||||
"missing-contracts",
|
"missing-contracts",
|
||||||
15,
|
15,
|
||||||
"Name the minimum durable properties reusable automation needs.",
|
"Name the **minimum durable properties** reusable automation needs.",
|
||||||
["saved definition", "validation", "execution records"],
|
["saved definition", "validation", "execution records"],
|
||||||
"Reusable automation needs a saved definition, validation, execution records, and a clear way to pause and continue.",
|
"Reusable automation needs a saved definition, validation, execution records, and a clear way to pause and continue.",
|
||||||
["Thesis Problem Statement and Requirements"],
|
["Thesis Problem Statement and Requirements"],
|
||||||
@@ -102,7 +102,7 @@ export const presenterNotes = [
|
|||||||
"positioning",
|
"positioning",
|
||||||
"landscape",
|
"landscape",
|
||||||
18,
|
18,
|
||||||
"Place the work beside familiar adjacent systems.",
|
"Place the work beside **familiar adjacent systems**.",
|
||||||
["Python / n8n / Zapier", "LangGraph", "MCP"],
|
["Python / n8n / Zapier", "LangGraph", "MCP"],
|
||||||
"Existing systems solve different parts of this problem: Python scripts, n8n, Zapier, LangGraph, and MCP.",
|
"Existing systems solve different parts of this problem: Python scripts, n8n, Zapier, LangGraph, and MCP.",
|
||||||
["Thesis Positioning and Related Systems"],
|
["Thesis Positioning and Related Systems"],
|
||||||
@@ -112,7 +112,7 @@ export const presenterNotes = [
|
|||||||
"positioning",
|
"positioning",
|
||||||
"lda-position",
|
"lda-position",
|
||||||
17,
|
17,
|
||||||
"State the platform's narrow position without a superiority claim.",
|
"State the platform's **narrow position**, without a superiority claim.",
|
||||||
["provider-neutral", "workflow layer", "not a replacement"],
|
["provider-neutral", "workflow layer", "not a replacement"],
|
||||||
"My platform does not replace them. It provides a provider-neutral workflow layer that agents and humans can operate.",
|
"My platform does not replace them. It provides a provider-neutral workflow layer that agents and humans can operate.",
|
||||||
["Thesis Positioning and Related Systems", "Thesis Source Model"],
|
["Thesis Positioning and Related Systems", "Thesis Source Model"],
|
||||||
@@ -122,7 +122,7 @@ export const presenterNotes = [
|
|||||||
"planner-runtime",
|
"planner-runtime",
|
||||||
"planner",
|
"planner",
|
||||||
12,
|
12,
|
||||||
"Assign workflow decisions to an external planner.",
|
"Assign workflow decisions to an **external planner**.",
|
||||||
["human or AI planner"],
|
["human or AI planner"],
|
||||||
"A human or AI planner decides what workflow to build.",
|
"A human or AI planner decides what workflow to build.",
|
||||||
["Thesis Architecture Overview"],
|
["Thesis Architecture Overview"],
|
||||||
@@ -132,7 +132,7 @@ export const presenterNotes = [
|
|||||||
"planner-runtime",
|
"planner-runtime",
|
||||||
"runtime",
|
"runtime",
|
||||||
16,
|
16,
|
||||||
"Assign execution and recording to the runtime.",
|
"Assign **execution and recording** to the runtime.",
|
||||||
["validation", "step-by-step execution", "state and traces"],
|
["validation", "step-by-step execution", "state and traces"],
|
||||||
"The runtime validates the graph, executes it step by step, records state and traces, and pauses at declared boundaries.",
|
"The runtime validates the graph, executes it step by step, records state and traces, and pauses at declared boundaries.",
|
||||||
["Thesis Workflow Core", "Thesis Architecture Overview"],
|
["Thesis Workflow Core", "Thesis Architecture Overview"],
|
||||||
@@ -146,7 +146,7 @@ export const presenterNotes = [
|
|||||||
"planner-runtime",
|
"planner-runtime",
|
||||||
"boundary",
|
"boundary",
|
||||||
12,
|
12,
|
||||||
"Introduce the public seam between clients and runtime.",
|
"Introduce the **public seam** between clients and runtime.",
|
||||||
["Workflow API", "CLI", "JSON-RPC"],
|
["Workflow API", "CLI", "JSON-RPC"],
|
||||||
"Both sides communicate through the Workflow API. Today, clients reach it through the CLI or JSON-RPC without accessing runtime internals directly.",
|
"Both sides communicate through the Workflow API. Today, clients reach it through the CLI or JSON-RPC without accessing runtime internals directly.",
|
||||||
["Thesis Architecture Overview", "docs/source_architecture.md"],
|
["Thesis Architecture Overview", "docs/source_architecture.md"],
|
||||||
@@ -156,7 +156,7 @@ export const presenterNotes = [
|
|||||||
"lifecycle",
|
"lifecycle",
|
||||||
"draft",
|
"draft",
|
||||||
9,
|
9,
|
||||||
"Introduce the editable lifecycle state.",
|
"Introduce the **editable lifecycle state**.",
|
||||||
["Draft", "being built"],
|
["Draft", "being built"],
|
||||||
"A workflow moves through four lifecycle stages. Draft means the workflow is still being built.",
|
"A workflow moves through four lifecycle stages. Draft means the workflow is still being built.",
|
||||||
["Thesis Workflow Lifecycle"],
|
["Thesis Workflow Lifecycle"],
|
||||||
@@ -166,7 +166,7 @@ export const presenterNotes = [
|
|||||||
"lifecycle",
|
"lifecycle",
|
||||||
"artifact",
|
"artifact",
|
||||||
9,
|
9,
|
||||||
"Introduce the immutable saved definition.",
|
"Introduce the **immutable saved definition**.",
|
||||||
["Artifact", "immutable version"],
|
["Artifact", "immutable version"],
|
||||||
"Artifact is a saved, immutable version.",
|
"Artifact is a saved, immutable version.",
|
||||||
["Thesis Workflow Lifecycle"],
|
["Thesis Workflow Lifecycle"],
|
||||||
@@ -175,7 +175,7 @@ export const presenterNotes = [
|
|||||||
"lifecycle",
|
"lifecycle",
|
||||||
"deployment",
|
"deployment",
|
||||||
9,
|
9,
|
||||||
"Connect a saved definition to a runnable environment.",
|
"Connect a saved definition to a **runnable environment**.",
|
||||||
["Deployment", "sources", "ready"],
|
["Deployment", "sources", "ready"],
|
||||||
"Deployment connects that version to the sources it needs and checks whether it is ready.",
|
"Deployment connects that version to the sources it needs and checks whether it is ready.",
|
||||||
["Thesis Workflow Lifecycle"],
|
["Thesis Workflow Lifecycle"],
|
||||||
@@ -184,7 +184,7 @@ export const presenterNotes = [
|
|||||||
"lifecycle",
|
"lifecycle",
|
||||||
"run",
|
"run",
|
||||||
9,
|
9,
|
||||||
"Introduce one persisted execution record.",
|
"Introduce one **persisted execution record**.",
|
||||||
["Run", "status", "output and trace"],
|
["Run", "status", "output and trace"],
|
||||||
"Run is one recorded execution, including its status, output, and trace.",
|
"Run is one recorded execution, including its status, output, and trace.",
|
||||||
["Thesis Workflow Lifecycle"],
|
["Thesis Workflow Lifecycle"],
|
||||||
@@ -194,27 +194,27 @@ export const presenterNotes = [
|
|||||||
"architecture",
|
"architecture",
|
||||||
"overview",
|
"overview",
|
||||||
6,
|
6,
|
||||||
"Show how the implementation realizes the earlier concepts.",
|
"Show how the **implementation realizes the earlier concepts**.",
|
||||||
["architecture spine"],
|
["architecture spine"],
|
||||||
"This is how those concepts are organized in the implementation.",
|
"Now I will show how those concepts map to the implementation.",
|
||||||
["Thesis System Architecture", "docs/project_map.md"],
|
["Thesis System Architecture", "docs/project_map.md"],
|
||||||
),
|
),
|
||||||
beatNote(
|
beatNote(
|
||||||
"architecture",
|
"architecture",
|
||||||
"client",
|
"client",
|
||||||
8,
|
8,
|
||||||
"Show that humans and agents share one public surface.",
|
"Show that humans and agents share **one public surface**.",
|
||||||
["shared operations"],
|
["shared operations"],
|
||||||
"Humans and agents use the same public workflow operations.",
|
"At the top, humans and agents use the same public workflow operations.",
|
||||||
["Thesis System Architecture", "docs/project_map.md"],
|
["Thesis System Architecture", "docs/project_map.md"],
|
||||||
),
|
),
|
||||||
beatNote(
|
beatNote(
|
||||||
"architecture",
|
"architecture",
|
||||||
"api",
|
"api",
|
||||||
9,
|
9,
|
||||||
"Identify the system's public front door.",
|
"Identify the system's **public front door**.",
|
||||||
["Workflow API", "public boundary"],
|
["Workflow API", "public boundary"],
|
||||||
"The Workflow API is the front door. It exposes lifecycle operations without exposing runtime internals.",
|
"Those operations enter through the Workflow API, the public boundary that keeps clients out of runtime internals.",
|
||||||
["Thesis System Architecture", "docs/source_architecture.md"],
|
["Thesis System Architecture", "docs/source_architecture.md"],
|
||||||
{ qnaBranchIds: ["not-just-cli"] },
|
{ qnaBranchIds: ["not-just-cli"] },
|
||||||
),
|
),
|
||||||
@@ -222,9 +222,9 @@ export const presenterNotes = [
|
|||||||
"architecture",
|
"architecture",
|
||||||
"runtime",
|
"runtime",
|
||||||
9,
|
9,
|
||||||
"Explain what the server composes behind the API.",
|
"Explain what the server composes **behind the API**.",
|
||||||
["WorkflowServer", "records and capabilities", "execution core"],
|
["WorkflowServer", "records and capabilities", "execution core"],
|
||||||
"Behind it, the workflow server brings together stored records, available capabilities, and the execution core.",
|
"Behind that boundary, the workflow server brings together stored records, available capabilities, and the execution core.",
|
||||||
["Thesis System Architecture", "docs/source_architecture.md"],
|
["Thesis System Architecture", "docs/source_architecture.md"],
|
||||||
{ qnaBranchIds: ["provider-security"] },
|
{ qnaBranchIds: ["provider-security"] },
|
||||||
),
|
),
|
||||||
@@ -232,7 +232,7 @@ export const presenterNotes = [
|
|||||||
"agent-handoff",
|
"agent-handoff",
|
||||||
"request",
|
"request",
|
||||||
12,
|
12,
|
||||||
"Disclose the prepared demonstration before it begins.",
|
"Disclose the **prepared demonstration** before it begins.",
|
||||||
["prepared example", "not an autonomous planner"],
|
["prepared example", "not an autonomous planner"],
|
||||||
"This is a prepared example, not a live autonomous AI agent. It shows how an agent could use the platform to build and run a workflow.",
|
"This is a prepared example, not a live autonomous AI agent. It shows how an agent could use the platform to build and run a workflow.",
|
||||||
["Constrained demo agent and prepared replay recipe"],
|
["Constrained demo agent and prepared replay recipe"],
|
||||||
@@ -245,7 +245,7 @@ export const presenterNotes = [
|
|||||||
"prepared-lifecycle",
|
"prepared-lifecycle",
|
||||||
"discover",
|
"discover",
|
||||||
7,
|
7,
|
||||||
"Show that authoring starts with interface discovery.",
|
"Show that authoring starts with **interface discovery**.",
|
||||||
["sources", "capabilities"],
|
["sources", "capabilities"],
|
||||||
"First, the agent checks which sources and operations are available.",
|
"First, the agent checks which sources and operations are available.",
|
||||||
["examples/lda_report_workflow", "deployment inspect replay evidence"],
|
["examples/lda_report_workflow", "deployment inspect replay evidence"],
|
||||||
@@ -255,7 +255,7 @@ export const presenterNotes = [
|
|||||||
"prepared-lifecycle",
|
"prepared-lifecycle",
|
||||||
"draft",
|
"draft",
|
||||||
7,
|
7,
|
||||||
"Show mutable workflow authoring.",
|
"Show **mutable workflow authoring**.",
|
||||||
["Draft", "editable workflow"],
|
["Draft", "editable workflow"],
|
||||||
"Then it builds an editable workflow draft.",
|
"Then it builds an editable workflow draft.",
|
||||||
["examples/lda_report_workflow", "deployment inspect replay evidence", "CLI documentation", "Draft authoring API"],
|
["examples/lda_report_workflow", "deployment inspect replay evidence", "CLI documentation", "Draft authoring API"],
|
||||||
@@ -265,7 +265,7 @@ export const presenterNotes = [
|
|||||||
"prepared-lifecycle",
|
"prepared-lifecycle",
|
||||||
"diagnose",
|
"diagnose",
|
||||||
7,
|
7,
|
||||||
"Show a concrete structured validation failure.",
|
"Show a **concrete structured validation failure**.",
|
||||||
["validation", "missing_outcome_edge"],
|
["validation", "missing_outcome_edge"],
|
||||||
"Validation finds that the analyze step has no route for its ok outcome.",
|
"Validation finds that the analyze step has no route for its ok outcome.",
|
||||||
["examples/lda_report_workflow", "deployment inspect replay evidence"],
|
["examples/lda_report_workflow", "deployment inspect replay evidence"],
|
||||||
@@ -275,7 +275,7 @@ export const presenterNotes = [
|
|||||||
"prepared-lifecycle",
|
"prepared-lifecycle",
|
||||||
"repair",
|
"repair",
|
||||||
7,
|
7,
|
||||||
"Show the exact focused correction and revalidation.",
|
"Show the **focused correction and revalidation**.",
|
||||||
["set-route", "validation passes"],
|
["set-route", "validation passes"],
|
||||||
"The agent adds that route, and validation passes.",
|
"The agent adds that route, and validation passes.",
|
||||||
["Validation and diagnostics", "Challenge UX findings"],
|
["Validation and diagnostics", "Challenge UX findings"],
|
||||||
@@ -285,7 +285,7 @@ export const presenterNotes = [
|
|||||||
"prepared-lifecycle",
|
"prepared-lifecycle",
|
||||||
"artifact",
|
"artifact",
|
||||||
7,
|
7,
|
||||||
"Show the transition to an immutable saved version.",
|
"Show the transition to an **immutable saved version**.",
|
||||||
["Artifact", "immutable"],
|
["Artifact", "immutable"],
|
||||||
"The valid workflow is saved as an immutable artifact.",
|
"The valid workflow is saved as an immutable artifact.",
|
||||||
["examples/lda_report_workflow", "deployment inspect replay evidence"],
|
["examples/lda_report_workflow", "deployment inspect replay evidence"],
|
||||||
@@ -294,7 +294,7 @@ export const presenterNotes = [
|
|||||||
"prepared-lifecycle",
|
"prepared-lifecycle",
|
||||||
"deployment",
|
"deployment",
|
||||||
7,
|
7,
|
||||||
"Show source binding and readiness before execution.",
|
"Show **source binding and readiness** before execution.",
|
||||||
["Deployment", "three local sources"],
|
["Deployment", "three local sources"],
|
||||||
"Finally, a deployment connects it to the three local sources it needs.",
|
"Finally, a deployment connects it to the three local sources it needs.",
|
||||||
["examples/lda_report_workflow", "deployment inspect replay evidence", "Thesis deterministic report case study"],
|
["examples/lda_report_workflow", "deployment inspect replay evidence", "Thesis deterministic report case study"],
|
||||||
@@ -304,7 +304,7 @@ export const presenterNotes = [
|
|||||||
"run-from-deployment",
|
"run-from-deployment",
|
||||||
"input",
|
"input",
|
||||||
11,
|
11,
|
||||||
"Show the concrete inputs supplied before execution.",
|
"Show the **concrete inputs** supplied before execution.",
|
||||||
["run input", "selected documents"],
|
["run input", "selected documents"],
|
||||||
"The deployment receives selected local documents and an issue-board path.",
|
"The deployment receives selected local documents and an issue-board path.",
|
||||||
["workflow.runs.start replay evidence"],
|
["workflow.runs.start replay evidence"],
|
||||||
@@ -313,7 +313,7 @@ export const presenterNotes = [
|
|||||||
"run-from-deployment",
|
"run-from-deployment",
|
||||||
"operation",
|
"operation",
|
||||||
12,
|
12,
|
||||||
"Show that one public operation creates a persisted execution.",
|
"Show that **one public operation** creates a persisted execution.",
|
||||||
["workflow.runs.start", "persisted Run"],
|
["workflow.runs.start", "persisted Run"],
|
||||||
"The public **workflow.runs.start** operation validates the deployment and input, creates a **persisted Run**, and begins the reusable graph.",
|
"The public **workflow.runs.start** operation validates the deployment and input, creates a **persisted Run**, and begins the reusable graph.",
|
||||||
["workflow.runs.start replay evidence"],
|
["workflow.runs.start replay evidence"],
|
||||||
@@ -323,7 +323,7 @@ export const presenterNotes = [
|
|||||||
"run-from-deployment",
|
"run-from-deployment",
|
||||||
"graph",
|
"graph",
|
||||||
12,
|
12,
|
||||||
"Show the reusable workflow executing beyond the chat conversation.",
|
"Show the **reusable workflow** executing beyond the chat conversation.",
|
||||||
["workflow graph", "declared interrupt"],
|
["workflow graph", "declared interrupt"],
|
||||||
"The graph reads documents, analyzes them, builds a report, drafts proposed issues, and pauses at a declared review interrupt before issue-board changes.",
|
"The graph reads documents, analyzes them, builds a report, drafts proposed issues, and pauses at a declared review interrupt before issue-board changes.",
|
||||||
["workflow.runs.start replay evidence", "examples/lda_report_workflow"],
|
["workflow.runs.start replay evidence", "examples/lda_report_workflow"],
|
||||||
@@ -333,7 +333,7 @@ export const presenterNotes = [
|
|||||||
"typed-human-boundary",
|
"typed-human-boundary",
|
||||||
"interrupt",
|
"interrupt",
|
||||||
15,
|
15,
|
||||||
"Show what the paused workflow asks from the operator.",
|
"Show what the **paused workflow asks** from the operator.",
|
||||||
["issue_review", "interrupt payload", "resume schema"],
|
["issue_review", "interrupt payload", "resume schema"],
|
||||||
"Execution pauses at a **typed issue_review interrupt** exposing request data, allowed outcomes, request schema, and resume schema.",
|
"Execution pauses at a **typed issue_review interrupt** exposing request data, allowed outcomes, request schema, and resume schema.",
|
||||||
["Typed interrupt payload and resume contract"],
|
["Typed interrupt payload and resume contract"],
|
||||||
@@ -343,9 +343,9 @@ export const presenterNotes = [
|
|||||||
"typed-human-boundary",
|
"typed-human-boundary",
|
||||||
"approval",
|
"approval",
|
||||||
15,
|
15,
|
||||||
"Show that the operator chooses a declared continuation.",
|
"Show that the operator chooses a **declared continuation**.",
|
||||||
["submitted", "revision-requested", "typed resume"],
|
["submitted", "revision-requested", "typed resume"],
|
||||||
"The operator chooses submitted or revision-requested; this is a typed interrupt and resume contract, not a production approval gate, role system, or policy engine.",
|
"The operator can Submit or Request revision. Both continue through declared workflow branches; this typed resume contract is not a production approval system.",
|
||||||
["Typed interrupt payload and resume contract"],
|
["Typed interrupt payload and resume contract"],
|
||||||
{ warning: "Both outcomes resume through declared workflow branches; this is not production approval governance.", qnaBranchIds: ["typed-interrupts", "security-production-boundary"] },
|
{ warning: "Both outcomes resume through declared workflow branches; this is not production approval governance.", qnaBranchIds: ["typed-interrupts", "security-production-boundary"] },
|
||||||
),
|
),
|
||||||
@@ -353,7 +353,7 @@ export const presenterNotes = [
|
|||||||
"resume-output-evidence",
|
"resume-output-evidence",
|
||||||
"resume",
|
"resume",
|
||||||
16,
|
16,
|
||||||
"Show continuation of the same recorded run.",
|
"Show continuation of the **same recorded run**.",
|
||||||
["workflow.runs.resume", "same Run"],
|
["workflow.runs.resume", "same Run"],
|
||||||
"On the submitted path, **workflow.runs.resume continues the recorded interrupted Run**.",
|
"On the submitted path, **workflow.runs.resume continues the recorded interrupted Run**.",
|
||||||
["workflow.runs.resume replay evidence", "Revision replay identity"],
|
["workflow.runs.resume replay evidence", "Revision replay identity"],
|
||||||
@@ -363,7 +363,7 @@ export const presenterNotes = [
|
|||||||
"resume-output-evidence",
|
"resume-output-evidence",
|
||||||
"output",
|
"output",
|
||||||
16,
|
16,
|
||||||
"Show the persisted terminal results of the submitted path.",
|
"Show the **persisted terminal results** of the submitted path.",
|
||||||
["report output", "issue-board changes"],
|
["report output", "issue-board changes"],
|
||||||
"The workflow creates the report and issue-board changes, then records terminal output.",
|
"The workflow creates the report and issue-board changes, then records terminal output.",
|
||||||
["workflow.runs.resume replay evidence", "examples/lda_report_workflow"],
|
["workflow.runs.resume replay evidence", "examples/lda_report_workflow"],
|
||||||
@@ -373,19 +373,19 @@ export const presenterNotes = [
|
|||||||
"resume-output-evidence",
|
"resume-output-evidence",
|
||||||
"trace",
|
"trace",
|
||||||
18,
|
18,
|
||||||
"Show that execution evidence remains inspectable after completion.",
|
"Show that **execution evidence remains inspectable** after completion.",
|
||||||
["trace frames", "protocol evidence"],
|
["trace frames", "protocol evidence"],
|
||||||
"Trace frames and protocol evidence remain inspectable; this is declared-boundary resumability, not arbitrary crash recovery or exactly-once execution. The revision replay is a separate prepared recording.",
|
"Trace frames and protocol evidence remain inspectable; this is declared-boundary resumability, not arbitrary crash recovery or exactly-once execution.",
|
||||||
["workflow.runs.resume replay evidence", "Revision replay identity"],
|
["workflow.runs.resume replay evidence"],
|
||||||
{ warning: "Never claim run-ID continuity for the prepared revision recording.", qnaBranchIds: ["replay-provenance", "demo-reliability"] },
|
{ qnaBranchIds: ["replay-provenance", "demo-reliability"] },
|
||||||
),
|
),
|
||||||
beatNote(
|
beatNote(
|
||||||
"evaluation",
|
"evaluation",
|
||||||
"cohort",
|
"cohort",
|
||||||
40,
|
40,
|
||||||
"Describe the external-agent evaluation design.",
|
"Describe the **external-agent evaluation design**.",
|
||||||
["36 trials", "two challenges", "three profiles"],
|
["36 trials", "two challenges", "three profiles"],
|
||||||
"The evaluation combines conformance tests, deterministic case studies, and a **manually audited external-agent campaign**: 36 trials across two challenges, two hosted models, three instruction profiles, and three waves, with three attempts per cell.",
|
"The evaluation combines conformance tests, deterministic case studies, and a **manually audited external-agent campaign**. Each model, under each instruction profile, attempted each challenge three times, for 36 trials in total.",
|
||||||
["Thesis Evaluation and Appendix C"],
|
["Thesis Evaluation and Appendix C"],
|
||||||
{ qnaBranchIds: ["evaluation-validity"] },
|
{ qnaBranchIds: ["evaluation-validity"] },
|
||||||
),
|
),
|
||||||
@@ -393,9 +393,9 @@ export const presenterNotes = [
|
|||||||
"evaluation",
|
"evaluation",
|
||||||
"validity",
|
"validity",
|
||||||
40,
|
40,
|
||||||
"Separate audited valid evidence from contaminated samples.",
|
"Separate **audited valid evidence** from contaminated samples.",
|
||||||
["27 pass", "8 invalid", "1 fail"],
|
["27 pass", "8 invalid", "1 fail"],
|
||||||
"The author audit classified 27 trials as clean product-path passes, eight as invalid samples, and one as a failure. Invalid samples included contamination such as reading implementation files, prior artifacts, adjacent attempts, or evaluator state.",
|
"The author audit then classified 27 trials as clean product-path passes, eight as invalid samples, and one as a failure. Invalid samples included contamination such as reading implementation files, prior artifacts, adjacent attempts, or evaluator state.",
|
||||||
["Thesis Evaluation and Appendix C", "Author audit"],
|
["Thesis Evaluation and Appendix C", "Author audit"],
|
||||||
{ qnaBranchIds: ["evaluation-validity"] },
|
{ qnaBranchIds: ["evaluation-validity"] },
|
||||||
),
|
),
|
||||||
@@ -403,9 +403,9 @@ export const presenterNotes = [
|
|||||||
"evaluation",
|
"evaluation",
|
||||||
"findings",
|
"findings",
|
||||||
40,
|
40,
|
||||||
"State what the evaluation supports and what it cannot prove.",
|
"State **what the evaluation supports** and what it cannot prove.",
|
||||||
["longitudinal evidence", "not a benchmark"],
|
["product evolved", "not a benchmark"],
|
||||||
"Because prompts, product snapshots, and hosted conditions changed across waves, these results are **longitudinal engineering evidence**. They expose authoring and diagnostic gaps, **not a benchmark** of model success, token reduction, retry reduction, or superiority.",
|
"But the prompts, product, and hosted conditions changed during the campaign. These results show what we learned while improving the product. They expose authoring and diagnostic gaps; they are **not a benchmark** of model success, token reduction, retry reduction, or superiority.",
|
||||||
["Thesis Evaluation and Appendix C", "Thesis Threats to Validity"],
|
["Thesis Evaluation and Appendix C", "Thesis Threats to Validity"],
|
||||||
{ warning: "Use non-benchmark wording; do not report the counts as general model performance.", qnaBranchIds: ["evaluation-validity"] },
|
{ warning: "Use non-benchmark wording; do not report the counts as general model performance.", qnaBranchIds: ["evaluation-validity"] },
|
||||||
),
|
),
|
||||||
@@ -413,7 +413,7 @@ export const presenterNotes = [
|
|||||||
"conclusion",
|
"conclusion",
|
||||||
"limits",
|
"limits",
|
||||||
18,
|
18,
|
||||||
"Bound the prototype claims before the final contribution statement.",
|
"Bound the **prototype claims** before the final contribution statement.",
|
||||||
["prototype", "not production security"],
|
["prototype", "not production security"],
|
||||||
"The prototype uses trusted in-process Python and file-backed stores; it does not provide production authentication, RBAC, sandboxing, scheduling, arbitrary crash recovery, or a bundled autonomous planner.",
|
"The prototype uses trusted in-process Python and file-backed stores; it does not provide production authentication, RBAC, sandboxing, scheduling, arbitrary crash recovery, or a bundled autonomous planner.",
|
||||||
["Thesis Limitations"],
|
["Thesis Limitations"],
|
||||||
@@ -423,7 +423,7 @@ export const presenterNotes = [
|
|||||||
"conclusion",
|
"conclusion",
|
||||||
"future",
|
"future",
|
||||||
18,
|
18,
|
||||||
"Name the surrounding layers left as future work.",
|
"Name the surrounding layers left as **future work**.",
|
||||||
["live agent", "scheduling", "controlled evaluation"],
|
["live agent", "scheduling", "controlled evaluation"],
|
||||||
"A live agent interface, transactional storage, richer debugging, security hardening, scheduling, and controlled comparative evaluation remain future work.",
|
"A live agent interface, transactional storage, richer debugging, security hardening, scheduling, and controlled comparative evaluation remain future work.",
|
||||||
["Thesis Future Work"],
|
["Thesis Future Work"],
|
||||||
@@ -433,7 +433,7 @@ export const presenterNotes = [
|
|||||||
"conclusion",
|
"conclusion",
|
||||||
"conclusion",
|
"conclusion",
|
||||||
20,
|
20,
|
||||||
"Restate the implemented contribution and planner-runtime boundary.",
|
"Restate the **implemented contribution** and planner-runtime boundary.",
|
||||||
["planner proposes", "platform executes"],
|
["planner proposes", "platform executes"],
|
||||||
"The contribution is **architectural and implemented**: external planners can propose workflows while a typed platform **validates, binds, executes, persists, interrupts, resumes, and inspects** them through public operations.",
|
"The contribution is **architectural and implemented**: external planners can propose workflows while a typed platform **validates, binds, executes, persists, interrupts, resumes, and inspects** them through public operations.",
|
||||||
["Thesis Contributions", "Thesis Conclusion"],
|
["Thesis Contributions", "Thesis Conclusion"],
|
||||||
@@ -443,7 +443,7 @@ export const presenterNotes = [
|
|||||||
"conclusion",
|
"conclusion",
|
||||||
"questions",
|
"questions",
|
||||||
19,
|
19,
|
||||||
"Open structured examiner discussion without introducing new claims.",
|
"Open **structured examiner discussion** without introducing new claims.",
|
||||||
["defense questions", "evidence"],
|
["defense questions", "evidence"],
|
||||||
"That boundary is the claim I will defend: reusable agent-operated automation is inspectable because planning and execution have explicit contracts. I welcome questions.",
|
"That boundary is the claim I will defend: reusable agent-operated automation is inspectable because planning and execution have explicit contracts. I welcome questions.",
|
||||||
["Thesis Conclusion", "Defense Q&A index"],
|
["Thesis Conclusion", "Defense Q&A index"],
|
||||||
|
|||||||
@@ -114,7 +114,11 @@ describe("defense storyboard catalog", () => {
|
|||||||
|
|
||||||
it("makes the evaluation beat carry the audited counts and validity boundary", () => {
|
it("makes the evaluation beat carry the audited counts and validity boundary", () => {
|
||||||
expect(findBeat("evaluation", "cohort")?.caption).toMatch(/36|two challenges|two hosted models|three waves/i);
|
expect(findBeat("evaluation", "cohort")?.caption).toMatch(/36|two challenges|two hosted models|three waves/i);
|
||||||
expect(findBeat("evaluation", "validity")?.caption).toMatch(/27|8|1|audit/i);
|
const validityCaption = findBeat("evaluation", "validity")?.caption ?? "";
|
||||||
|
expect(validityCaption).toMatch(/\b27\b/);
|
||||||
|
expect(validityCaption).toMatch(/\b8\b/);
|
||||||
|
expect(validityCaption).toMatch(/\b1\b/);
|
||||||
|
expect(validityCaption).toMatch(/audit/i);
|
||||||
expect(findBeat("evaluation", "findings")?.caption).toMatch(/changing|longitudinal|benchmark/i);
|
expect(findBeat("evaluation", "findings")?.caption).toMatch(/changing|longitudinal|benchmark/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -37,12 +37,23 @@ describe("presentationWorkflowPlan", () => {
|
|||||||
expect(node.capability, node.id).toBeTruthy();
|
expect(node.capability, node.id).toBeTruthy();
|
||||||
expect(node.inputSummary, node.id).toBeTruthy();
|
expect(node.inputSummary, node.id).toBeTruthy();
|
||||||
expect(node.outputSummary, node.id).toBeTruthy();
|
expect(node.outputSummary, node.id).toBeTruthy();
|
||||||
expect(node.outcomes, node.id).not.toHaveLength(0);
|
|
||||||
expect(node.evidencePointer, node.id).toBeTruthy();
|
expect(node.evidencePointer, node.id).toBeTruthy();
|
||||||
|
|
||||||
|
if (node.type !== "end") {
|
||||||
|
expect(node.outcomes, node.id).toEqual(
|
||||||
|
presentationWorkflowPlan.edges
|
||||||
|
.filter((edge) => edge.from === node.id)
|
||||||
|
.map((edge) => edge.outcome),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const interrupt = presentationWorkflowPlan.nodes.find((node) => node.id === "review_issues");
|
const interrupt = presentationWorkflowPlan.nodes.find((node) => node.id === "review_issues");
|
||||||
expect(interrupt?.schemaSummary).toMatch(/request: issue proposals/i);
|
expect(interrupt?.schemaSummary).toMatch(/request: issue proposals/i);
|
||||||
expect(interrupt?.outcomes).toEqual(["submitted", "cancelled"]);
|
expect(interrupt?.outcomes).toEqual(["submitted", "cancelled"]);
|
||||||
|
expect(presentationWorkflowPlan.nodes.find((node) => node.id === "finalise")?.outcomes).toEqual([
|
||||||
|
"completed",
|
||||||
|
]);
|
||||||
|
expect(presentationWorkflowPlan.nodes.find((node) => node.id === "revision_requested")?.outcomes).toEqual([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export const presentationWorkflowPlan = {
|
|||||||
capability: "State output",
|
capability: "State output",
|
||||||
inputSummary: "Markdown report and issue-board state",
|
inputSummary: "Markdown report and issue-board state",
|
||||||
outputSummary: "Completed run output",
|
outputSummary: "Completed run output",
|
||||||
outcomes: ["ok"],
|
outcomes: ["completed"],
|
||||||
evidencePointer: "local.lda_report.finalise_report",
|
evidencePointer: "local.lda_report.finalise_report",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -111,7 +111,7 @@ export const presentationWorkflowPlan = {
|
|||||||
capability: "Revision record",
|
capability: "Revision record",
|
||||||
inputSummary: "Issue-review decision",
|
inputSummary: "Issue-review decision",
|
||||||
outputSummary: "Recorded revision request",
|
outputSummary: "Recorded revision request",
|
||||||
outcomes: ["cancelled"],
|
outcomes: [],
|
||||||
evidencePointer: "local.lda_report.record_revision_request",
|
evidencePointer: "local.lda_report.record_revision_request",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user