返回 ppt-master
confirm_ui.md
根目录 / skills / ppt-master / scripts / docs / confirm_ui.md
1 # Confirm UI — Strategist and Template Confirmation Page
2
3 > The interactive surface for [`generate-pptx`](../../workflows/generate-pptx.md)
4 > Step 4. Stage 1 shows the template-independent communication recommendation
5 > and the template/free-design choice on one page and confirms both with one
6 > submission. That submission writes the Strategist contract to `result.json`
7 > and the selection sidecar to `template_selection.json`. The agent then installs
8 > any selected workspace and writes `template_handoff.json`; only afterward does
9 > final Stage 2 read installed template state and confirm the coordinated deck
10 > solution plus production mechanics. The chat path mirrors these boundaries
11 > without fabricating UI receipts.
12
13 ## Authority and Scope
14
15 | Concern | Owner |
16 |---|---|
17 | Stage-1 combined confirmation and post-confirmation installation order | [`generate-pptx.md`](../../workflows/generate-pptx.md) |
18 | Template option/selection schema and page transport | This document |
19 | Step 4 gate and pipeline order | [`generate-pptx.md`](../../workflows/generate-pptx.md) |
20 | Confirm UI schema | This document |
21 | Stage 1 / final Stage 2 field membership | This document |
22 | Server launch / wait / shutdown behavior | This document |
23 | Port and lock behavior | This document |
24 | Chat fallback equivalence | This document |
25 | Confirmed-value precedence | [`generate-pptx.md`](../../workflows/generate-pptx.md) plus this document's `result.json` contract |
26
27 **Hard rule**: Keep detailed Confirm UI behavior here. The Generate route may summarize orchestration, but it should not duplicate the full JSON schema, catalog behavior, or launcher lifecycle.
28
29 **Mandatory surface decision — before any UI command**: Resolve the most recent
30 explicit confirmation-surface instruction for this run before running
31 `--daemon` or `--wait-only`. Unrelated later messages do not reset the selected
32 branch. A new explicit selection may change it before launch; once confirmation
33 starts in chat or UI switches to chat, keep chat for the rest of this run.
34
35 | Most recent explicit surface instruction | Branch |
36 |---|---|
37 | The user explicitly delegates confirmation | Make the combined Stage-1 communication/template decision, install it, then present one complete final summary. Do not launch the page or fabricate UI receipts. |
38 | Otherwise, the user asks for or agrees to personally confirm in chat, or declines the confirmation page | Use chat for both Strategist stages; Stage 1 includes the template/free-design choice. Do not launch the page, run `--wait-only`, or require UI-authored receipts. |
39 | No explicit confirmation-surface instruction exists for this run | Use the page as the default. |
40
41 Interpret the instruction semantically: “confirm here”, “use the chat window”, or
42 “do not open the confirmation page” are sufficient; no literal `chat-only`
43 keyword is required. Invoking a chat-question tool by itself does not select the
44 chat branch—the user's instruction does. Both branches preserve the same
45 Stage-1 communication/template decision, installation handoff, and template-aware
46 final Stage 2.
47
48 **Chat/delegated Stage-1 listing**: Author the communication recommendation
49 before reading the four indexes, then present that recommendation together with
50 an explicit free-design/template-mode choice. Only template mode expands the
51 registered candidates and supplied exact roots, and it requires at least one
52 selection. Ordinary requests initialize free design; explicit template intent
53 or any supplied exact root initializes template mode. Exactly one supplied root
54 may also seed that candidate, while multiple roots remain unselected. Under
55 explicit delegation, make the same decision, install
56 it, and only then derive Stage 2. Do not launch the page or fabricate receipts.
57
58 **Fallback rule**: When no surface was selected before launch, the page is the
59 default. Use chat when the user answers either always-on handoff in chat, or
60 after launch failure/timeout and one re-check of `result.json` plus the
61 Stage-1-sidecar `template_selection.json` when Stage 1 is active. A chat-question
62 tool alone is not a launch failure. Preserve the combined Stage-1 decision and
63 keep communication prompts open-ended.
64
65 **In-run UI → chat switch — any phase or stage**: If the user explicitly selects chat
66 after the UI server has launched—while `--wait-only` is active, before that wait
67 starts, or after it times out while the server remains live:
68
69 1. If a wait is active, interrupt it and confirm that its process has exited.
70 Only the return code from this deliberate wait interruption is expected to
71 be non-zero.
72 2. Run `server.py <project_path> --shutdown` and require that cleanup to
73 succeed. The browser tab may remain open, but its stopped server makes it
74 inactive.
75 3. Re-check the active receipt once. During Stage 1, require both `result.json`
76 and `template_selection.json` from the same submission; during Stage 2, check
77 `result.json`. Retain only values persisted before shutdown; an unsubmitted
78 browser draft is not confirmed.
79 4. Continue the unresolved current phase/stage and everything remaining in chat. Do
80 not call `--wait-only` again, recover the server, or relaunch the page during
81 this run.
82
83 **Always-on Stage-1 chat handoff**: After writing `template_options.json` and
84 template-independent `recommendations.stage1.json`, launch the healthy daemon
85 without `--wait`. Immediately post its actual URL plus one compact localized
86 summary of the current communication recommendation and template choice state:
87 audience, communication intent, audience outcome, core message, delivery
88 context, artifact afterlife, `content_divergence`, canvas, and whether the
89 default is free design or template mode, including any sole preselected root. Explain
90 that template mode expands the registered-kind and supplied-root selectors.
91 Show a blank prose value as “not specified” without changing it. End with an
92 explicit localized line saying that, if the page did not open, the user may
93 confirm or revise the same communication and template choices in chat. Only
94 then run `--wait-only --wait-stage stage1`. A chat
95 reply to that handoff applies the in-run switch above without waiting for
96 timeout. The handoff is context, not confirmation, and silence confirms
97 nothing. After launch failure/timeout and the required result re-check, present
98 the same combined Stage-1 items as open chat questions and wait explicitly.
99
100 ## `confirm_ui/server.py`
101
102 The following launch and wait commands belong to the **UI branch only**:
103
104 ```bash
105 python3 scripts/confirm_ui/server.py <project_path> --daemon # launch combined Stage 1
106 python3 scripts/confirm_ui/server.py <project_path> --wait-only --wait-stage stage1 # communication + template selection
107 python3 scripts/confirm_ui/server.py <project_path> --complete-template-selection # after Stage-1 free-design closure / template install
108 python3 scripts/confirm_ui/server.py <project_path> --wait-only # current final Stage 2
109 python3 scripts/confirm_ui/server.py <project_path> --daemon --port 5051
110 python3 scripts/confirm_ui/server.py <project_path> --no-browser
111 python3 scripts/confirm_ui/server.py <project_path> --timeout 0 # disable idle auto-shutdown
112 python3 scripts/confirm_ui/server.py <project_path> --reset-template-selection # clear prior template sidecars before a fresh UI run
113 python3 scripts/confirm_ui/server.py <project_path> --shutdown # Step 4 cleanup (idempotent)
114 ```
115
116 - Without `--port`, binds the first free port from `127.0.0.1:5050`; the launch log prints the actual URL. `--port N` is exact and fails when unavailable. Auto-open is suppressed by `--no-browser`.
117 - In `--daemon` mode the launcher starts the child with browser opening suppressed, then accepts readiness only when `GET /api/health` identifies this confirm service, project, and child process. It opens the printed `http://127.0.0.1:<port>` URL only after that check.
118 - Confirm UI and live preview prefer the same memorable base port but keep separate processes and project-local locks (`.confirm_ui.lock` vs `live_preview/lock.json`). Normal Step 4 cleanup releases the confirm port before Step 6; concurrent projects may use different ports.
119 - `--daemon` starts the Flask process in the background and returns after the health check. Every Default UI run launches directly into combined Stage 1 and keeps the same process live through final Stage 2. The wait budget defaults to **590 s** (`--wait-timeout`); on timeout the detached server remains live, and the caller re-checks both Stage-1 receipts before chat fallback.
120 - `--wait-only` attaches to the page opened by `--daemon` and blocks until the requested receipt. If it is already persisted, the command returns before recovery, so a fast submit between launch, chat handoff, and wait is not lost. Otherwise, if the recorded server died, it restarts on the recorded/default port. Use `stage1` for the combined communication/template submission and the default/final wait for Stage 2.
121 - `--complete-template-selection` is agent-only. It validates the Stage-1 sidecar and writes the bound `template_handoff.json`; template mode additionally requires project-local `templates/design_spec.md`. Run it after installation/free-design closure and before writing Stage 2. `--reset-template-selection` removes exactly `template_options.json`, `template_selection.json`, and `template_handoff.json`; it does not alter Strategist files, installed template content, or `result.json`. The old `--*-template-phase` names are not aliases.
122 - `--shutdown` stops a confirm server left running for this project and exits — **idempotent** (a no-op when nothing is running). Tries a graceful `/api/shutdown`, falls back to killing the recorded pid, then clears the lock. Generate Step 4 runs this on every path so the selected port is released before live preview starts.
123 - Every fresh UI run starts with `--reset-template-selection`, then writes valid `<project_path>/confirm_ui/template_options.json` and a newer `recommendations.stage1.json`; `explicit_workspace_roots` is an empty array when no exact root was supplied. Stage 1 writes the bound selection and communication result together. Stage 2 is exposed only when the matching handoff is newer than that selection and its recommendation is newer than the handoff. `--shutdown` needs neither input.
124 - Per-project lock at `<project_path>/.confirm_ui.lock` — duplicate launches are refused; stale locks (dead pid) are overwritten.
125 - Idle auto-shutdown after 900 s by default; `/api/shutdown` exits gracefully and releases the lock.
126 - Stage-1 `GET /api/recommendations` embeds the server-built candidate catalog
127 as top-level `template_options`. Its `/api/confirm` submission validates
128 current candidate keys and writes `template_selection.json` beside the pure
129 Strategist `result.json`; there is no independent template-options endpoint,
130 template-confirm endpoint, or template wait stage. The same APIs later serve
131 Stage 2 and strip legacy `template_reuse_scope` / `template_adherence`
132 fields. The completed template handoff is authoritative: `free_design`
133 strips a stray `template_application`, while `templates` exposes that
134 editable natural-language field in Stage 2.
135
136 Dependency:
137
138 ```bash
139 pip install flask
140 ```
141
142 ## Stage-1 template-selection sidecar contract
143
144 Template selection shares the Stage-1 page and submit action but remains a
145 separate artifact from the Strategist contract. Its files live under
146 `<project_path>/confirm_ui/`; selection keys never enter `result.json`.
147
148 ### Input — `template_options.json` (created before launch)
149
150 ```json
151 {
152 "schema_version": 1,
153 "phase": "template",
154 "lang": "zh",
155 "default_mode": "free_design",
156 "explicit_workspace_roots": [
157 "/absolute/path/to/a/project-or-template-workspace"
158 ]
159 }
160 ```
161
162 - `schema_version` is exactly `1`; `phase` is exactly `template`.
163 - `lang` is optional, but when present it is a non-empty UI-language string.
164 - `default_mode` is required and is exactly `free_design` or `templates`.
165 Ordinary requests use `free_design`; explicit template intent or any supplied
166 exact root uses `templates`. It initializes the UI but never locks the user.
167 - `explicit_workspace_roots` is required even when empty. Every item is a
168 unique absolute path resolving to an existing directory with
169 `templates/design_spec.md` or compatible legacy `design_spec.md`.
170 - The array supplies candidates for the one specified-root dropdown; it does
171 not authorize selecting several explicit roots in one confirmation.
172 - Do not write library entries into this file. The server reads only
173 `templates/brands/brands_index.json`,
174 `templates/styles/styles_index.json`,
175 `templates/layouts/layouts_index.json`, and
176 `templates/decks/decks_index.json`, derives each direct-child workspace root,
177 and validates that it exists with `templates/design_spec.md`. It never scans
178 kind directories.
179
180 Stage-1 `GET /api/recommendations` embeds this browser catalog as top-level
181 `template_options`:
182
183 ```json
184 {
185 "schema_version": 1,
186 "phase": "template",
187 "lang": "zh",
188 "default_mode": "free_design",
189 "library": {
190 "brand": [],
191 "style": [],
192 "layout": [],
193 "deck": []
194 },
195 "explicit": [],
196 "preselected_keys": [],
197 "options_sha256": "<64 lowercase hex characters>"
198 }
199 ```
200
201 A library candidate has `key`, `source: "library"`, `kind`, `id`, `label`,
202 `summary`, and canonical absolute `workspace_root`. An unregistered explicit
203 candidate has `key`, `source: "explicit"`, parsed `kind`, `label`, and
204 canonical absolute `workspace_root`. If a supplied root exactly equals a registered canonical
205 root, the server reuses the library candidate/key instead of duplicating it as
206 explicit. Candidate keys are server-owned. The Stage-1 submit payload carries
207 the current `{ "mode": "free_design"|"templates", "selection_keys": [...] }`
208 beside the Strategist fields; the server validates that selection and writes it
209 to the sidecar rather than copying keys into `result.json`.
210
211 When the input supplies exactly one root, `preselected_keys` contains its
212 resolved candidate key as a convenience default, including when exact equality
213 reclassifies it as library. When several roots are supplied, all remain
214 candidates but none is preselected; one specified-root dropdown cannot encode
215 an instruction to use all of them.
216
217 **Page selection model**: Stage 1 first asks the user to choose `Free design` or
218 `Use templates`, initialized from `default_mode` but always switchable. Exactly
219 one supplied root may initialize its candidate as an editable convenience
220 default; multiple roots remain unselected. Only `Use templates` expands the
221 candidate controls: Brand, Style, Layout, and Deck each have one registered
222 single-select dropdown, and Specified has one explicit-root single-select
223 dropdown. Every dropdown includes `None`; template mode cannot submit until at
224 least one is non-empty. Free design clears all dropdowns. Registered kinds may
225 be combined, but each contributes at most one root and the specified channel at
226 most one. Because an explicit candidate carries its parsed kind, it may coexist
227 with one registered root of that kind and enter the two-workspace same-kind
228 conflict gate. Source provenance never grants priority.
229
230 ### Output — `template_selection.json` (written with Stage 1)
231
232 ```json
233 {
234 "schema_version": 1,
235 "phase": "template",
236 "status": "confirmed",
237 "mode": "templates",
238 "selections": [
239 {
240 "source": "library",
241 "kind": "style",
242 "id": "example_style",
243 "workspace_root": "/canonical/library/root/example_style"
244 },
245 {
246 "source": "explicit",
247 "kind": "deck",
248 "workspace_root": "/canonical/unregistered/workspace/root"
249 }
250 ],
251 "options_sha256": "<64 lowercase hex characters>",
252 "selection_sha256": "<64 lowercase hex characters>",
253 "confirmed_at": "2026-08-04T12:00:00"
254 }
255 ```
256
257 `mode: "free_design"` requires `selections: []`; `mode: "templates"` requires
258 at least one selection. Roots are unique canonical absolute paths. A library
259 selection contains exactly `source`, `kind`, `id`, and `workspace_root`; an
260 explicit selection contains exactly `source`, `kind`, and `workspace_root`.
261 There is at most one library selection per kind and at most one explicit
262 selection overall; cross-kind composition remains valid, and one explicit plus
263 one library selection may share a kind. The browser
264 cannot submit arbitrary paths because the server resolves posted keys against
265 the catalog it just built. `options_sha256` binds the receipt to the current
266 input, four index files, and resolved candidates. `selection_sha256` binds the
267 mode and canonical sorted selections to that option hash. Every receipt read
268 rebuilds the catalog and rejects option/index drift.
269
270 The Stage-1 submission writes this receipt and the Stage-1 `result.json`
271 together. Generate reads both exactly once after `--wait-only --wait-stage
272 stage1` returns. Free design skips installation. Template mode runs
273 `apply-template-workspace` against all selected roots and waits for complete
274 project-local installation/fusion. Only then does the agent complete the
275 handoff below. Installation resolves template-to-template ownership; Stage 2
276 later evaluates current-project fit from the installed copy. Strategist never
277 reads the source roots.
278
279 ### Agent handoff — `template_handoff.json`
280
281 After free design closes or template installation succeeds, run:
282
283 ```bash
284 python3 scripts/confirm_ui/server.py <project_path> --complete-template-selection
285 ```
286
287 The command writes, and agents must not hand-author:
288
289 ```json
290 {
291 "schema_version": 1,
292 "phase": "template",
293 "status": "ready",
294 "mode": "templates",
295 "selection_sha256": "<64 lowercase hex characters>",
296 "completed_at": "2026-08-04T12:01:00"
297 }
298 ```
299
300 The handoff must match the current valid selection. Template mode also requires
301 `<project_path>/templates/design_spec.md`; free design requires no installed
302 spec. Write `recommendations.stage2.json` only after this command succeeds, so
303 its file time is newer than the handoff.
304
305 ## Field shapes
306
307 The following fields belong to the Strategist stages, not to the
308 template-selection receipt.
309
310 - **Enumerable + custom** — canvas / icons retain blank manual inputs; mode / visual_style instead show a mandatory AI-authored proposal in full, initially unselected and editable after selection. Selected mode / style writes literal `custom` plus its behavior sibling.
311 - **Visual examples for hard-to-name choices** — the full-screen confirmation page loads real SVG page samples from `static/style_previews/` for `visual_style`, and renders real sample SVGs from `templates/icons` for `icons`. These thumbnails make style and icon-library choices visually comparable before the user locks them. Preview copy is fixed role text (big title / section title / body / points), not project content from recommendation files, so users compare visual treatment rather than copywriting. These previews are a confirmation aid only: they do not add fields to recommendation stage files or `result.json`, and they do not replace the later Step 6 live preview.
312 - **Image usage multi-select** — image sources are selected as one or more catalog ids: `ai` = AI-generated, `web` = Web-sourced, `provided` = User-provided, `placeholder` = Placeholder, `none` = No images. `none` is exclusive. A confirmed non-`none` set is the allowed acquisition-source boundary, not a requirement to use every selected source; only explicit `image_notes` wording can require a source, asset, or page role. Recommendation and result values may be a legacy single string, but new files should use an array. When several sources are recommended, write the source ids to `recommend.image_usage` and write the actual usage strategy to `image_notes`, not a custom prose value.
313 - **Closed enumerable** — PPT reading mode (`delivery_purpose` compatibility key), formula policy / generation mode / refine spec, plus AI source only when image usage includes `ai`. These have no Custom box; out-of-catalog values snap back to the recommended option.
314 - **Proactive execution booleans** — Final Stage 2 carries top-level `proactive_speaker_notes`, `proactive_custom_animations`, and `proactive_narration_audio` values. Defaults are `true`, `false`, and `false`, respectively. They control what the Agent does proactively only when the user has not explicitly instructed otherwise; the latest explicit user instruction always wins. These three values are raw confirmation evidence: the UI and server neither couple nor rewrite them, and every boolean combination is valid. When narration audio is enabled, Strategist later resolves the effective Speaker Notes outcome to enabled and records `Narration Audio dependency` as its Design Spec provenance. Disabling proactive custom animation does not suppress the Strategist's advisory motion recommendations.
315 - **Open prose** — `audience`, `communication_intent`, `audience_outcome`, `core_message`, `delivery_context`, `artifact_afterlife`, `content_divergence`, and `page_count`. `communication_intent` may carry several purposes plus priority / sequence; common paths appear only as help text. `delivery_context` states one primary presenter-led / reader-led / hybrid / recorded-self-running context plus optional secondary use; a hybrid recommendation names which context leads. `content_divergence` is the source-treatment axis. `page_count` may be a range here; Strategist resolves the exact §IX roster, leaving Executor no pagination latitude.
316 - **Coordinated generative directions** — `design_directions` carries ≥3 safe / shifted / bold candidates. Each candidate bundles visual style, color, typography, icon id, and conditional generated-image rendering. The page can still render legacy top-level `color`, `typography`, and `image_strategy` candidates, but new staged recommendations use the coordinated bundle.
317
318 AI-authored custom proposals apply only to mode, visual style, and conditional AI-image rendering; a selected proposal cannot be blank. Color / typography keep their existing manual Custom cards. Image usage uses source ids plus `image_notes`; closed sets have no Custom path.
319
320 **Stage-1 current-value contract.** Each editable prose box starts with the Strategist's recommendation, if one exists. The user may retain, revise, or clear it; no Stage-1 prose field has a non-empty validation gate. On confirmation, the browser submits the current strings and the server preserves them through every later stage and the final `result.json`, including `""`. Blank means no explicit user constraint and may cause downstream default judgment, but it never causes the initial recommendation to be restored. A profile-declared `locked: true` field is read-only and remains the sole exception.
321
322 `image_ai_path` is conditional: the page shows it and writes it to `result.json` only when `image_usage` includes `ai`. Web-sourced / User-provided / Placeholder / No images paths do not carry an AI backend choice.
323
324 ## Catalogs — `static/catalogs.json` (the finite option universe)
325
326 The front-end loads `/api/catalogs` (served by the confirm server) and falls back to the static `/static/catalogs.json` if that route is unavailable. `/api/catalogs` returns the static file **with the `canvas` list synced live from `config.py CANVAS_FORMATS`** — the set of formats and their `dim` come from config (single source of truth, zero drift), while trilingual labels / use text stay in catalogs.json (a plain fallback label is synthesized for any new id config adds). Keys: `canvas`, `modes`, `visual_styles` (grouped), `icons`, `image_usage`, `image_ai_path`, `formula_policy`, `generation_mode`, `delivery_purpose`. Each entry is `{ "id", "label", "label_zh", "label_en", "label_ja", ... }`; descriptions use `desc_zh` / `desc_en` / `desc_ja`, and `visual_styles` groups use `group_zh` / `group_en` / `group_ja`. The front-end falls back to legacy `label` / `desc` / `group`, so old catalogs still load, but new user-facing catalog text must cover all three languages (zh / en / ja). English labels should mirror canonical reference names (`pyramid`, `swiss-minimal`, `Path A`, `mixed`, etc.); Chinese and Japanese labels should be translated for users. Descriptions render inline after the option title, not as a separate selected-option line. `visual_styles` is `[{ "group", "group_zh", "group_en", "group_ja", "items": [...] }]`. For `canvas` you only need to maintain the trilingual labels in catalogs.json; the format set and dimensions are authoritative in `config.py CANVAS_FORMATS`.
327
328 ## Round-trip data contract
329
330 In the UI branch, round-trip and session files live under
331 `<project_path>/confirm_ui/`. `template_options.json` is prepared beside the
332 Stage-1 recommendation. The Stage-1 submit writes `result.json` and
333 `template_selection.json` together; after installation/free-design closure,
334 `template_handoff.json` unlocks Stage 2. The chat/delegated branch preserves the
335 same logical order without fabricating these UI receipts.
336
337 ### Current two-stage flow
338
339 The page runs a **two-stage Strategist wizard in one browser session**. Stage 1
340 contains the communication contract and template/free-design controls. Each stage
341 has its own Strategist-authored file and top-level `"stage"` selector. The active,
342 unconfirmed stage may be overwritten any number of times when the user asks for
343 a better recommendation; refresh the page to load the replacement. Once the
344 user confirms it, normal progression writes the next stage file rather than
345 repurposing the previous one. The server derives the active Strategist filename
346 from `result.json`; the bound template handoff is the prerequisite for Stage 2.
347
348 Confirm UI is a one-run surface, not a migration layer. It accepts only the
349 current Stage-1/Stage-2 files. If a project starts UI confirmation again, run
350 `--reset-template-selection`, write fresh `template_options.json`, then author a
351 fresh `recommendations.stage1.json`. Those two inputs start the new UI
352 lifecycle; neither a standalone newer Stage-1 file nor a standalone option file
353 does so.
354 The completed result cannot be reopened or overwritten, and a standalone newer
355 Stage-1 file cannot start its replacement. After Stage 1 writes both receipts,
356 the agent completes a newer bound handoff; `recommendations.stage2.json` must be
357 newer than that handoff and the Stage-1 result. A Stage-2 file left by an earlier
358 run remains inactive. An existing `result.json` outside the current `stage1` /
359 `final` contract also fails closed unless fresh paired inputs start a new run.
360
361 | Recommendation file | Declared stage | Page renders | Button | On submit |
362 |---|---|---|---|---|
363 | `recommendations.stage1.json` + `template_options.json` | `"stage1"` | communication contract — content language; audience; open `communication_intent`; audience outcome; core message / primary delivery context + optional secondary use / artifact afterlife / `content_divergence` (all prose fields may be blank); canvas; free-design/template mode and conditional candidate selectors | **Confirm contract & template choice** | writes Stage-1 `result.json` plus `template_selection.json` in one submission; the page stays open and polls while the agent installs/completes the handoff |
364 | `recommendations.stage2.json` | `"stage2"` | complete deck solution and production — conditional natural-language template application, reading mode, mode, page count, visual direction, color, icons, typography, image usage/rendering, conditional AI acquisition path, formula policy, proactive notes/custom-animation/narration-audio toggles, generation mode, and Design Spec review toggle | **Confirm final plan** | writes `result.json` `{ stage: "final", status: "confirmed", <all fields> }`, then shuts the page down |
365
366 In the UI branch, the AI authors Stage 1 without reading template candidates,
367 then launches the combined page. In chat/delegated confirmation it authors the
368 same communication recommendation before listing template candidates. After
369 the one Stage-1 confirmation, the AI installs any selection, completes the
370 handoff/equivalent state, then inspects only the project-local template and
371 authors the complete final Stage-2 solution plus production mechanics once from
372 the user's actual communication contract. An edit inside
373 the current stage never requests another recommendation. The page preserves
374 earlier answers across transitions. `GET /api/session` reports `phase:
375 "strategist"` with current Stage 1 from launch; after Stage-1 submission it
376 reports `waiting_agent` until the bound handoff and fresh Stage-2 file exist.
377 `GET /api/recommendations`
378 is `no-store`, and the server folds confirmed earlier-stage choices back into
379 the final Stage-2 payload so an in-run refresh preserves the confirmed Stage-1
380 communication contract. Unsubmitted Stage-2 edits are browser-local and a
381 completed final result is never reopened.
382
383 **Progression guard.** Stage 1 requires current `template_options.json` and a
384 fresh `recommendations.stage1.json`; it does not require a prior selection or
385 handoff. Its one submit must persist a valid Stage-1 result and selection bound
386 to those options. Stage 2 requires a newer `template_handoff.json` bound to that
387 selection and a fresh Stage-2 recommendation. This ordering prevents receipts
388 from an earlier one-run UI from satisfying a new run. Strategist confirms Stage
389 1 → installation/free-design handoff → final Stage 2.
390 `/api/confirm` accepts only the submit stage matching the
391 active filename and its required predecessor; the declared `stage` must also
392 match the filename. A confirmed templates-mode workspace does not exempt final
393 Stage 2: its recommendations must include `template_application.value`.
394
395 ### Input — `recommendations.stage1.json` (created beside template options)
396
397 Author this file before reading candidate index summaries in chat and without
398 reading any template spec, prototype, asset, fused segment owner, or template
399 canvas. `template_options.json` supplies display state only and never changes
400 the communication recommendation.
401
402 ```json
403 {
404 "stage": "stage1",
405 "lang": "zh",
406 "primary_language": "zh-CN",
407 "recommend": {
408 "canvas": "ppt169"
409 },
410 "audience": { "value": "公司管理层,包括财务与产品负责人" },
411 "communication_intent": {
412 "value": "先汇报进展并暴露交付风险,再推动管理层决定下一阶段投入"
413 },
414 "audience_outcome": {
415 "value": "管理层能比较三个选项、接受风险判断,并选定一条获得预算的路径"
416 },
417 "core_message": {
418 "value": "现在为方案 B 增加投入,能以可接受的成本守住发布时间"
419 },
420 "delivery_context": {
421 "value": "主要为有主讲的 20 分钟管理层现场评审;次要为会后独立阅读的审批材料"
422 },
423 "artifact_afterlife": {
424 "value": "作为审批记录、项目交接依据和季度审计材料"
425 },
426 "content_divergence": { "value": "" }
427 }
428 ```
429
430 All seven Stage-1 prose values may be blank. `primary_language` is required canonical BCP-47. The server normalizes legacy English / Chinese / Japanese / Korean aliases, rejects `und` and Chinese without script/region, and carries it forward; `lang` is UI-only. Prose submits verbatim, including `""`. A profile's `{ "locked": true }` value is read-only, persisted, and stripped of that marker in final `result.json`.
431
432 The common paths — inform / explain / persuade / decide / align / teach / report and account / mobilize / record and hand off — appear only as help text for `communication_intent`. They are not catalog ids and must not be emitted as a `primary_job` field.
433
434 After Stage 1 is confirmed, create `recommendations.stage2.json` with the complete solution; leave Stage 1 unchanged (the server folds confirmed communication fields back in when serving the page):
435
436 **Stage-2 production contract**: the server rejects the recommendation file
437 unless `recommend.formula_policy`, `recommend.generation_mode`, and boolean
438 `refine_spec.value` are present; `recommend.image_ai_path` is additionally
439 required when `image_usage` includes `ai`. Final submission must retain the
440 corresponding direct values (`formula_policy`, `generation_mode`, boolean
441 `refine_spec`, and conditional `image_ai_path`) or confirmation is rejected.
442
443 ```json
444 {
445 "stage": "stage2",
446 "lang": "zh",
447 "recommend": {
448 "delivery_purpose": "balanced",
449 "mode": "pyramid",
450 "visual_style": "swiss-minimal",
451 "image_usage": ["ai", "provided"],
452 "image_ai_path": "auto",
453 "formula_policy": "mixed",
454 "generation_mode": "continuous"
455 },
456 "page_count": { "value": "12-15" },
457 "image_notes": { "value": "封面和章节页用 AI 主视觉;产品页优先用户素材。" },
458 "proactive_speaker_notes": { "value": true },
459 "proactive_custom_animations": { "value": false },
460 "proactive_narration_audio": { "value": false },
461 "refine_spec": { "value": false },
462 "custom_candidates": {
463 "mode": {
464 "name_zh": "冲突到决策",
465 "behavior_zh": "先建立业务冲突,再用结论先行结构推动决策。"
466 },
467 "visual_style": {
468 "name_zh": "编辑批注风",
469 "behavior_zh": "严格栅格配合边注和证据强调。"
470 },
471 "image_strategy": {
472 "name_zh": "证据拼贴",
473 "rendering": "custom",
474 "visual_zh": "纸面证据拼贴",
475 "mood_zh": "审慎可信",
476 "behavior_zh": "裁切纸面配少量批注,保持平面深度并继承演示色板。"
477 }
478 },
479 "design_directions": {
480 "selected": 0,
481 "candidates": [
482 {
483 "name_zh": "稳妥专业",
484 "note_zh": "像成熟咨询简报",
485 "visual_style": "swiss-minimal",
486 "icons": "tabler-outline",
487 "color": { "name_zh": "冷静专业", "palette": {
488 "background": "#FFFFFF", "secondary_bg": "#F4F6F8",
489 "primary": "#1A3A6B", "accent": "#E8A317",
490 "secondary_accent": "#4A7BB5", "body_text": "#1D2430"
491 } },
492 "typography": {
493 "name_zh": "微软雅黑 + Arial",
494 "heading": { "primary": "Microsoft YaHei", "english": "Arial", "css": "sans-serif" },
495 "body": { "primary": "Microsoft YaHei", "english": "Arial", "css": "sans-serif" },
496 "body_size": 24
497 },
498 "image_strategy": {
499 "name_zh": "克制矢量",
500 "rendering": "vector-illustration",
501 "visual_zh": "扁平矢量、实色块、少阴影",
502 "mood_zh": "稳定、可信、克制"
503 }
504 }
505 ]
506 }
507 }
508 ```
509
510 The example abbreviates the required ≥3 directions. Custom mode/style candidates remain mandatory; only a recommendation containing AI requires the custom image candidate. Final Stage 2 rejects fewer than three bundles, incomplete six-role palettes, or incomplete heading/body stacks. Legacy grids remain readable only with three complete palettes and complete typography.
511
512 - `recommend.*` names each recommended id. New mode / style values use a catalog id or literal `custom`; arbitrary prose values are legacy-only. Use `recommend.image_strategy: "custom"` only when an explicit user-supplied image direction should start selected. Missing recommendations fall back to the normal preset. Legacy aliases remain accepted; new files write canonical ids.
513 - The three proactive-execution fields are top-level boolean `{ "value": ... }` objects, not catalog ids. Omitted fields use `true / false / false` for notes / custom animation / narration audio. These are absence-of-instruction defaults, not permission to override the user's latest explicit request. Preserve all three raw values independently through `result.json`; do not couple or rewrite them. Strategist derives effective Speaker Notes as enabled when audio is `true` and records `Narration Audio dependency` as provenance in the Design Spec. `proactive_custom_animations: false` leaves Strategist animation suggestions unchanged; it only prevents unrequested custom-animation execution.
514 - `custom_candidates` is recommendation-only. Mode / style carry localized `name` + `behavior`; conditional image strategy also carries `rendering: "custom"`, `visual`, and `mood`. When a proposal combines or borrows existing catalog entries, the visible behavior names every exact id and Strategist reads every corresponding file before authoring it; a genuinely novel proposal names none. The server rejects missing required candidates; the UI shows full copy, edits it only after selection, rejects a selected blank, and omits unselected candidates from `result.json`. Template-backed proposals obey inherited identity, prototype capacity, and `template_application`.
515 - Seed `audience`, `communication_intent`, `audience_outcome`, and `delivery_context` when evidence supports them; users need not supply them, and every Stage-1 prose field may end blank. The contract and `primary_language` stay in `result.json` and `design_spec.md`; `spec_lock.md communication` receives `primary_language`, compact `audience` / `objective` / `core_message`, and reading mode. `communication_intent` may preserve multiple purposes and priority/sequence; never add a `primary_job` enum.
516 - Do not write `recommend.template_reuse_scope` or `recommend.template_adherence`. Strategist records those internal exporter values later in `spec_lock.md` after inspecting the actual template and current content.
517 - For a confirmed templates-mode handoff, write one editable prose field as top-level `template_application.value`. It summarizes **how to use** the already selected project-local template: actual page/prototype use and preservation/reorganization decisions. It never chooses, changes, or reinstalls a workspace. Omit it for free design. The UI returns the current string through final Stage 2; Strategist then persists the final effective plan as `- **Template Application**: ...` in `design_spec.md §I`, which Executor reads from the retained Design Spec. Never replace it with internal reuse/adherence ids or a fixed option menu.
518
519 Template-mode-only Stage-2 fragment:
520
521 ```json
522 {
523 "template_application": {
524 "value": "选用封面、章节页和数据页原型;跳过示例内容页。品牌标识和页脚保留,正文可按当前材料重组。"
525 }
526 }
527 ```
528
529 - `recommend.image_usage` should be an array of source ids when more than one source applies, e.g. `["ai", "provided"]`. A single string is still accepted for backward compatibility. Do not write bare `"custom"` and do not encode a mixed-source plan as prose here; write the prose to top-level `image_notes.value`.
530 - `image_notes` is the initial strategy note shown under the image source chips. Use it for page-role guidance and constraints: which source applies where, what to avoid, which user assets are authoritative, how realistic / abstract the imagery should be, and what can remain as placeholders. It is intent guidance, not a separate finite option.
531 - Final Stage 2 shows and submits `recommend.image_ai_path` as one of `auto` / `api` / `host-native` / `manual` only while its current `image_usage` includes `ai`; changing sources refreshes that production control on the same page.
532 - **Color candidates carry the user-facing core `palette`**: `background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, and `body_text`. The page renders every role as a labelled swatch with its HEX value visible, and offers per-role override inputs for precise single-role edits, plus a **Custom color card with a free-text box** — the user can describe the palette in words or paste HEX values instead of filling each role; this writes `color: { "name": "custom", "custom": "<text>" }` to `result.json` for the AI to interpret. Legacy `text` is accepted as an alias for `body_text`, but new files should write `body_text`. Strategist derives secondary text, borders, state colors, and visual-style neutral tiers while writing `design_spec.md`, then projects the machine values to `spec_lock.md`; those are not user-facing confirmation choices.
533 - **Candidate display text may be multilingual**: color / typography candidates can provide `name_zh` / `name_en` / `name_ja` and `note_zh` / `note_en` / `note_ja`; the page falls back to legacy `name` / `note`. Labels resolve in the page language first, then fall back across the others (a `ja` page: ja → en → zh; zh/en pages keep their zh↔en fallback and try `_ja` last), so when `lang` is `ja` always include the `_ja` variants — otherwise the candidate labels render in English.
534 - **Typography candidates** use concrete heading/body `primary`; non-English decks also use `english`, while English-primary decks omit it. `cjk` / `latin` remain legacy aliases. Localized `name` labels the pair and `css` only previews. Bundles differ overall; font pairs may repeat without blocking. Fixed pairs require `fixed: true`. Catalog `fonts` supplies language-filtered dropdowns plus Other without limiting recommendations; edits mark Custom and refresh the preview. Include topic samples. PPT baselines are `text` 20 · `balanced` 24 · `presentation` 32 px; cards preserve sizes and submit px.
535 - **Per-role size override** (parallel to color's per-role HEX override): besides `body_size`, the page exposes editable inputs for `title` / `subtitle` / `annotation`. The browser applies one documented deterministic dependency chain: `reading mode → body baseline → unpinned role sizes` (role ramp: `body ×` the §g ratios). Changing reading mode updates the body and all unpinned roles locally; changing body updates unpinned roles locally. Editing body or a role pins that value, so later reading-mode changes do not overwrite it. Font / direction-card selection preserves all current sizes. This is a browser-only state update: it performs no fetch, asks the backend to author no new recommendations, and a re-render preserves exactly what the user sees. Each role input is labelled as px and shows an approximate pt equivalent (`1px = 0.75pt`) for orientation. The final values are written to `result.json` as `typography.sizes: { "title", "subtitle", "annotation" }` in **px** — every canvas, no pt and no `sizes_pt` provenance. These confirmed values are Strategist input anchors: the completed page plan may add recurring roles, and downstream execution owns bounded per-occurrence treatment. Candidate `sizes` remain accepted for compatibility, but the fresh Stage-2 baseline is normalized through the same local ramp before first render.
536 - **`delivery_purpose` compatibility key / Reading mode** (enumerable, PPT only) decides where meaning is carried, not merely how large type is: `text` makes pages self-contained with complete sentences, short prose, captions, tables, and necessary detail; `balanced` shares explanation between page and presenter; `presentation` uses one idea, concise claims, and visual evidence while speech / notes carry the detail. It therefore governs page grammar, granularity, density / rhythm, and note burden. Reading-mode cards intentionally show **no px value**; the typography section owns the separately visible body / role sizes and applies any local default. It is surfaced in Stage 2 beside the visual system, separate from communication intent. `recommend.delivery_purpose` pre-selects one; `result.json` retains the key, while `spec_lock.md` uses canonical `consumption_mode`. Non-PPT canvases omit it.
537 - **Combined style preview** — a compact live "overall impression" strip sits just above the color section and is **sticky**: it pins under the topbar so it stays visible while the user scrolls through the color / icon / typography sections, keeping the picking controls and their combined effect on screen together. It applies the currently selected color palette **and** typography (heading sample in `primary` over `background`, body sample in `body_text`, an `accent` bar, a `secondary_bg` chip) and repaints on every color / HEX-override / font / `body_size` change. It does not replace the per-candidate swatches or font samples (those stay for picking); it is deliberately an abstract style chip, **not** a slide-layout preview — page layout preview remains the live-preview server's job (Step 6). No schema field; it derives entirely from the existing color + typography selections.
538 - **Generated-image direction** appears only for `image_usage: ai`. One preset dropdown contains project recommendations when present plus the 20 system styles; Custom remains a separate card and is blank when AI was added manually. A preset submits its id; Custom submits `rendering: "custom"` + non-empty `behavior`; closing AI omits `image_strategy`. Catalog-based custom behavior names exact ids for optional `image_rendering_references`; a novel behavior has none. The left preview follows selection. No image palette is written; deck colors remain authoritative, and legacy `image_strategy.palette` is ignored.
539 - **`design_directions`** is the canonical Stage-2 spectrum: ≥3 meaningfully different safe / shifted / bold bundles with localized copy, style, icons, conditional image strategy, complete language-aware typography, and HEX `background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, `body_text`. Selection applies the bundle; component controls override it. `result.json` stores components, not a direction id.
540 - `recommend.generation_mode` and `refine_spec` mirror [`generate-pptx`](../../workflows/generate-pptx.md) Step 4. `split` / `true` are explicit opt-ins. Refinement adds no UI stage: after Gate 1 it stops before the lock for unrestricted chat revision until approval.
541 - `content_divergence` is a **free-text** Stage-1 source-treatment field. Blank means a balanced default; facts stay sourced at every level. Strategist consumes it while authoring §IX and records it in `design_spec.md §I`; it is not written to `spec_lock.md`. Beautify sends `{ "value": "keep source wording and page structure verbatim", "locked": true }`, so the UI displays it read-only and the server restores it on every staged submit. Template-fill does not use this confirmation flow and does not surface it.
542 - `lang` is the soft UI-language default (`zh` / `en` / `ja`); the persisted user choice wins. It never sets `primary_language`.
543
544 ### Output — `result.json` (written on submit, read by the AI)
545
546 ```json
547 {
548 "primary_language": "zh-CN",
549 "canvas": "ppt169",
550 "page_count": "12-15",
551 "audience": "...",
552 "communication_intent": "Report progress and expose risk first; then obtain an investment decision",
553 "audience_outcome": "The committee compares the options and chooses one funded path",
554 "core_message": "Fund option B now to protect the launch date at acceptable incremental cost",
555 "delivery_context": "Primary: presenter-led 20-minute leadership review; secondary: reader-led approval copy shared afterward",
556 "artifact_afterlife": "Approval record, hand-off reference, and audit trail",
557 "content_divergence": "freely restructure and expand within the source",
558 "mode": "pyramid",
559 "visual_style": "swiss-minimal",
560 "color": { "name": "...", "palette": { "background": "#...", "secondary_bg": "#...", "primary": "#...", "accent": "#...", "secondary_accent": "#...", "body_text": "#..." } },
561 "icons": "tabler-outline",
562 "typography": { "name": "...", "heading": { "primary": "...", "english": "...", "css": "..." }, "body": { "primary": "...", "english": "...", "css": "..." }, "body_size": 24, "body_size_unit": "px", "sizes": { "title": 42, "subtitle": 32, "annotation": 18 } },
563 "delivery_purpose": "balanced",
564 "formula_policy": "mixed",
565 "image_usage": ["ai", "provided"],
566 "image_notes": "封面和章节页用 AI 主视觉;产品页优先用户素材,缺口页可用占位符。",
567 "image_ai_path": "auto",
568 "image_strategy": { "name": "方案 A", "rendering": "vector-illustration", "visual": "...", "mood": "..." },
569 "proactive_speaker_notes": true,
570 "proactive_custom_animations": false,
571 "proactive_narration_audio": false,
572 "generation_mode": "continuous",
573 "refine_spec": false,
574 "stage": "final",
575 "status": "confirmed",
576 "confirmed_at": "2026-06-15T11:44:44"
577 }
578 ```
579
580 The shape above is final for Strategist confirmation. It intentionally contains
581 no template-selection field: `template_selection.json`, its agent handoff, and
582 the installed project-local state own the parallel Stage-1 decision. The proactive-execution values
583 are independent flat booleans in `result.json`; old recommendations and results
584 that omit them resolve to `true / false / false`. They remain raw evidence even
585 when `proactive_speaker_notes` is `false` and `proactive_narration_audio` is
586 `true`; Strategist owns the effective dependency resolution described above.
587 Selected custom values use `mode: custom` + `mode_behavior`, `visual_style:
588 custom` + `visual_style_behavior`, or `image_strategy.rendering: custom` +
589 `behavior`. During Design Spec and lock authoring, Strategist projects optional
590 `mode_references`, `visual_style_references`, or
591 `image_rendering_references` only when that confirmed behavior actually uses
592 named catalog sources; genuinely novel custom behavior has no reference list.
593 The Stage-1 intermediate write retains the communication contract for Stage 2.
594
595 **Final-result consumption contract.** A final result is the user-confirmed input contract for the Strategist's Design Spec, not another recommendation input. After the final wait, Generate Step 4 reads the complete final object exactly once and retains it while Strategist writes and audits `design_spec.md` against every explicitly present field. Normal lock authoring and downstream execution do not reopen `result.json`; the completed Design Spec is the durable authority. Only after that audit passes does Strategist author `spec_lock.md` from the Design Spec plus current execution context, selecting stable anchors and routing rather than copying every field or enumerating every legal color/font. Every value must be consumed at the semantic type owned by [`strategist.md`](../../references/strategist.md) §1 and its field owner: do not omit or substitute it, and do not silently strengthen or weaken its type. If a confirmed requirement cannot be honored, the owning workflow reports or pauses under failure recovery; it never deletes the requirement to keep the pipeline moving.
596
597 - Bespoke mode / style prose lives only in the required behavior sibling; image custom prose lives in `image_strategy.behavior`. Canvas / icons retain free-text edge cases, color / typography retain `name: "custom"`, and image usage remains a source-id array plus `image_notes`.
598 - `image_ai_path` and `image_strategy` appear only with `image_usage: ai` and remain confirmed downstream. The page is default; explicit/failure chat fallback keeps identical fields. `image_ai_path` selects the Step 5 path, and [`strategist-image.md`](../../references/strategist-image.md) §2 retains the selected rendering or custom behavior as the deck-level image identity anchor; individual prompts still adapt subject, composition, and atmosphere within it.
599 - Stage-1 **Confirm contract & template choice** writes the Stage-1 `result.json` and `template_selection.json` together, then keeps the page open while the agent installs the selection. The agent runs `--complete-template-selection`, writes fresh Stage 2 only after that handoff, and the page keeps polling. Stage-2 **Confirm final plan** saves the final `result.json` and shuts the server down (auto-close). The AI reads each receipt at its owning boundary; chat fallback mirrors the same decisions without UI artifacts. Either way, Step 4 ends with `--shutdown` so a never-confirmed page cannot retain its selected port ahead of Step 6 live preview.
600
601 ## Scope
602
603 - Confirmation surface only — Strategist authors every recommendation; the page never generates deck content.
604 - No SVG / layout preview here — that is the live preview server's job (`workflows/stages/live-preview.md`, Step 6).
605
605 lines MARKDOWN