slidemind 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,51 +5,115 @@ description: Author branded slide decks and individual slides against the active
5
5
 
6
6
  # slidemind-create-slides
7
7
 
8
- This skill is **thin workflow glue for the local harness**. Everything that determines how a deck is authored, styled, and rendered lives in the **slidemind PowerPoint add-in** and reaches you through a **materialized design bundle**:
9
-
10
- - The **brand** — tokens, `corporate-design.css`, the `design.md` brand rules, the closed layout palette, brand assets — is served per corporate design.
11
- - The **authoring workflow craft** — create, edit, composition, layouts, authoring contract, build/export — is the add-in's deployment-shipped `workflow/*.md`, served on the same bundle so the local harness reads **byte-identical** guidance to the in-app design agent.
8
+ This skill is **thin workflow glue for the local harness**. Everything that determines how a deck is
9
+ authored, styled, and rendered lives in the **slidemind PowerPoint add-in** and reaches you through
10
+ a **materialized design bundle**:
11
+
12
+ - The **brand** — tokens, `corporate-design.css`, the `design.md` brand rules, the closed layout
13
+ palette, brand assets — is served per corporate design.
14
+ - The **authoring workflow craft** — create, edit, composition, layouts, authoring contract,
15
+ build/export — is the add-in's deployment-shipped `workflow/*.md`, served on the same bundle so
16
+ the local harness reads **byte-identical** guidance to the in-app design agent.
12
17
  - The **`.pptx` render** happens server-side in the add-in. There is no local exporter.
13
18
 
14
- So this file owns only what is genuinely CLI-specific: the **first-step design-bundle gate**, a compact **intake/plan**, the **create flow**, and the **command mapping** that translates the workflow vocabulary — the harness-neutral verbs defined in the bundle's `workflow/flow.md` and spoken throughout `workflow/*.md` — into your `slidemind` CLI commands. Read the workflow + brand from the bundle; never restate or duplicate them here.
19
+ So this file owns only what is genuinely CLI-specific: the **first-step design-bundle gate**, a
20
+ compact **intake/plan**, the **create flow**, and the **command mapping** that translates the
21
+ workflow vocabulary — the harness-neutral verbs defined in the bundle's `workflow/flow.md` and
22
+ spoken throughout `workflow/*.md` — into your `slidemind` CLI commands. Read the workflow + brand
23
+ from the bundle; never restate or duplicate them here.
15
24
 
16
- **slidemind is a CLI you drive directly.** Run `slidemind <command>` in your shell — the same way you'd run `git` or `npm`. There is no separate tool layer to go through; every step below is a real command you type.
25
+ **slidemind is a CLI you drive directly.** Run `slidemind <command>` in your shell — the same way
26
+ you'd run `git` or `npm`. There is no separate tool layer to go through; every step below is a real
27
+ command you type.
17
28
 
18
29
  ## Create a todo list (before your first command)
19
30
 
20
- The moment this skill activates on an authoring task, **create a todo list of the workflow stages** in your harness's task tracker — before you run anything — and keep it current: mark each stage done as you complete it. This is `workflow/flow.md` → *track the stages*; the list is what keeps the later stages (above all the review gate) from being skipped. The stages, as they run over the CLI:
31
+ The moment this skill activates on an authoring task, **create a todo list of the workflow stages**
32
+ in your harness's task tracker — before you run anything — and keep it current: mark each stage done
33
+ as you complete it. This is `workflow/flow.md` → *track the stages*; the list is what keeps the
34
+ later stages (above all the review gate) from being skipped. The stages, as they run over the CLI:
21
35
 
22
36
  1. `slidemind ready` — session preflight (*First step* below)
23
- 2. Clarify with the user — the content **and the output target** (*Intake → plan* below; needs no host, so it can run while `ready` executes)
37
+ 2. Clarify with the user — the content **and the output target** (*Intake → plan* below; needs no
38
+ host, so it can run while `ready` executes)
24
39
  3. Plan sign-off — `slidemind plan <deck>` (decks; single slides stay in chat)
25
- 4. Concept mockups — `slidemind concepts <deck>`, then rebuild each toward its mockup (a deck authored from an approved plan; see *The create flow* below)
40
+ 4. Concept mockups — `slidemind concepts <deck>`, then immediately inspect every mockup to drive
41
+ image/content search (a deck authored from an approved plan; see *The create flow* below)
26
42
  5. Author the slides
27
43
  6. Draft check — `slidemind view-draft`
28
44
  7. Review gate — `slidemind review`, the moment the deck builds clean
29
45
  8. Deliver — `slidemind push` or `slidemind build-pptx`, whichever target was locked in stage 2
30
46
 
31
- **Deliver is the last todo — the task ends when the slides are delivered.** Don't add a "sanity pass" or "report back" step after it: the visual QA already happened at the draft check and the user already approved at the review gate, so on approval you push/build directly and stop. Each stage's full definition and the ordering invariants live in the bundle's `workflow/flow.md` — read it right after `ready` succeeds.
32
-
33
- **Concept mockups (stage 4) are a mandatory deck stage, not an optional aid.** After plan sign-off, for a deck authored from an approved plan, you **always** run `slidemind concepts <deck>` and rebuild each `concept-slide-NN.png` toward its target — this is the local-harness equivalent of the in-app manager's *unconditional* concept pass on every full-deck delegation (`generateConceptImages` fires for `approvedPlan && !targetSlideScope`). It degrades benignly: if it returns no mockups (exit 0 — the add-in can't generate them) you author from the storyboards alone, but the *attempt* is not optional. Skip it only where the add-in's `!targetSlideScope` gate would — a single slide, a tiny in-chat set, or a targeted rework of existing slides.
47
+ **Deliver is the last todo — the task ends when the slides are delivered.** Don't add a "sanity
48
+ pass" or "report back" step after it: the visual QA already happened at the draft check and the user
49
+ already approved at the review gate, so on approval you push/build directly and stop. Each stage's
50
+ full definition and the ordering invariants live in the bundle's `workflow/flow.md` — read it right
51
+ after `ready` succeeds.
52
+
53
+ **Concept mockups (stage 4) are a mandatory deck stage, not an optional aid.** After plan sign-off,
54
+ for a deck authored from an approved plan, you **always** run `slidemind concepts <deck>` and
55
+ **immediately Read every generated `concept-slide-NN.png` at full detail**. Use this first,
56
+ deck-wide viewing pass to inventory the imagery, pictograms, factual/content checks, and other
57
+ searches each slide needs; do those searches before authoring. This discovery read does **not**
58
+ replace the fresh per-batch concept read immediately before JSX creation in *The create flow*.
59
+ The durable visual anchors are the repo-relative files under
60
+ `decks/<slug>/concepts/concept-slide-NN.png`, never an earlier chat image or temporary attachment;
61
+ after any continuation, re-list that directory and reopen the files from those paths.
62
+ Rebuild each mockup toward its target — this is the local-harness equivalent of the in-app manager's
63
+ *unconditional* concept pass on every full-deck delegation (`generateConceptImages` fires for
64
+ `approvedPlan && !targetSlideScope`). It degrades benignly: if it returns no mockups (exit 0 — the
65
+ add-in can't generate them) you author from the storyboards alone, but the *attempt* is not optional.
66
+ Skip it only where the add-in's `!targetSlideScope` gate would — a single slide, a tiny in-chat set,
67
+ or a targeted rework of existing slides.
34
68
 
35
69
  Two gate invariants you must never break — one per gate, equally binding:
36
70
 
37
- - **Never jump from clarify to authoring for a deck** — the plan sign-off gate (`slidemind plan <deck>`, stage 3) always sits between. Writing `decks/<slug>/slides/*.jsx` before that gate has returned `DECISION: approved` is the single most common way this workflow goes wrong — it means the user never actually signed off on the story. The only exception is a single slide or a tiny set genuinely handled in-chat (*Intake → plan* below) — that's a real design choice, not a shortcut to take when a deck-sized request feels obvious.
38
- - **Never jump from authoring to deliver** — the draft check and an approved review gate always sit between.
71
+ - **Never jump from clarify to authoring for a deck** — the plan sign-off gate (`slidemind plan
72
+ <deck>`, stage 3) always sits between. Writing `decks/<slug>/slides/*.jsx` before that gate has
73
+ returned `DECISION: approved` is the single most common way this workflow goes wrong — it means
74
+ the user never actually signed off on the story. The only exception is a single slide or a tiny
75
+ set genuinely handled in-chat (*Intake → plan* below) — that's a real design choice, not a
76
+ shortcut to take when a deck-sized request feels obvious.
77
+ - **Never jump from authoring to deliver** — the draft check and an approved review gate always sit
78
+ between.
39
79
 
40
80
  ## Bootstrap (set yourself up — don't make the user do it)
41
81
 
42
- **The first step of every session is `slidemind ready`** (continuing an existing deck: `slidemind ready <deck>`). In one call it verifies authorization, connection, and CLI version; resolves the design the add-in has open; downloads or refreshes that design's bundle; and — given a deck arg — binds the deck to it.
43
-
44
- - **Exit 0** → you're fully set up. Read `workflow/flow.md` (the stage spine) and `design.md` from the reported `bundleDir` and start planning.
45
- - **Non-zero** → the CLI names exactly one blocker and one `nextAction`; do what it says. (The two blockers that ever need the user are handled in *First step* below.)
46
-
47
- `ready` drives the `slidemind` CLI, which reaches PowerPoint through the add-in's **cloud relay** — no local server. Patch a missing CLI yourself; **connecting** needs the user once (they hold the add-in's identity):
48
-
49
- - **CLI missing** (`slidemind` not on `PATH`): `npm install -g slidemind` — that alone is the fix. Only run `slidemind init --for <claude|codex|opencode|all>` ((re)deploys this skill) when this skill did **not** come from a plugin: if the `SKILL.md` you are reading sits under a harness plugin cache (`~/.claude/plugins/…`, `~/.codex/plugins/…`), the plugin already supplies it and `init` would plant a second copy that drifts on the next plugin update. `npx slidemind <command>` runs one-off without a global install; `slidemind --version` confirms PATH; `slidemind update` upgrades (`--ref <version>` to pin) — unless the CLI itself came from a plugin, in which case update it there.
50
- - **Not connected:** `ready` exits with `not_connected` — the CLI cannot start a connection by itself, because the claim that carries the deployment's address is minted in the add-in. Ask the user to open PowerPoint → slidemind → **Settings → General → Connect CLI**, press the **link button**, and copy the command shown (press **CLI already installed?** for the short `slidemind setup …` form). Have them paste it to you and run it **verbatim** — it connects, refreshes the skills, and runs `ready` itself. Add `--no-install --for none` only if their install must not be touched. `slidemind whoami` lists connected environments.
51
-
52
- The CLI is **multi-environment**: connect once per backend (local / dev / prod) and every command fans out across all of them, tags each open window with its `env`, and routes to the right backend automatically — no manual switching. (Scope first-use authorization with `--backend <url>`; scope later calls with `--backend <url|env>`.) A normal user connects to the baked-in production backend only.
82
+ **The first step of every session is `slidemind ready`** (continuing an existing deck: `slidemind
83
+ ready <deck>`). In one call it verifies authorization, connection, and CLI version; resolves the
84
+ design the add-in has open; downloads or refreshes that design's bundle; and — given a deck arg —
85
+ binds the deck to it.
86
+
87
+ - **Exit 0** → you're fully set up. Read `workflow/flow.md` (the stage spine) and `design.md` from
88
+ the reported `bundleDir` and start planning.
89
+ - **Non-zero** → the CLI names exactly one blocker and one `nextAction`; do what it says. (The two
90
+ blockers that ever need the user are handled in *First step* below.)
91
+
92
+ `ready` drives the `slidemind` CLI, which reaches PowerPoint through the add-in's **cloud relay** —
93
+ no local server. Patch a missing CLI yourself; **connecting** needs the user once (they hold the
94
+ add-in's identity):
95
+
96
+ - **CLI missing** (`slidemind` not on `PATH`): `npm install -g slidemind` — that alone is the fix.
97
+ Only run `slidemind init --for <claude|codex|opencode|all>` ((re)deploys this skill) when this
98
+ skill did **not** come from a plugin: if the `SKILL.md` you are reading sits under a harness
99
+ plugin cache (`~/.claude/plugins/…`, `~/.codex/plugins/…`), the plugin already supplies it and
100
+ `init` would plant a second copy that drifts on the next plugin update. `npx slidemind <command>`
101
+ runs one-off without a global install; `slidemind --version` confirms PATH; `slidemind update`
102
+ upgrades (`--ref <version>` to pin) — unless the CLI itself came from a plugin, in which case
103
+ update it there.
104
+ - **Not connected:** `ready` exits with `not_connected` — the CLI cannot start a connection by
105
+ itself, because the claim that carries the deployment's address is minted in the add-in. Ask the
106
+ user to open PowerPoint → slidemind → **Settings → General → Connect CLI**, press the **link
107
+ button**, and copy the command shown (press **CLI already installed?** for the short `slidemind
108
+ setup …` form). Have them paste it to you and run it **verbatim** — it connects, refreshes the
109
+ skills, and runs `ready` itself. Add `--no-install --for none` only if their install must not be
110
+ touched. `slidemind whoami` lists connected environments.
111
+
112
+ The CLI is **multi-environment**: connect once per backend (local / dev / prod) and every command
113
+ fans out across all of them, tags each open window with its `env`, and routes to the right backend
114
+ automatically — no manual switching. (Scope first-use authorization with `--backend <url>`; scope
115
+ later calls with `--backend <url|env>`.) A normal user connects to the baked-in production backend
116
+ only.
53
117
 
54
118
  ## Connecting the CLI (one-time; you guide the user)
55
119
 
@@ -58,63 +122,210 @@ PowerPoint — not here. Every organization runs its own slidemind backend, so a
58
122
  unconnected CLI has no address to reach and nothing to discover it from; the
59
123
  authenticated add-in knows both, and mints a one-time claim carrying its address.
60
124
 
61
- **1 — Ask for the command.** Tell the user to open the **slidemind add-in** task pane → **Settings** → **General** → **Connect CLI**, press the **link button**, and press **Copy command**. If they already have the CLI, **CLI already installed?** swaps it to the shorter `slidemind setup …` form; either works.
125
+ **1 — Ask for the command.** Tell the user to open the **slidemind add-in** task pane → **Settings**
126
+ → **General** → **Connect CLI**, press the **link button**, and press **Copy command**. If they
127
+ already have the CLI, **CLI already installed?** swaps it to the shorter `slidemind setup …` form;
128
+ either works.
62
129
 
63
- **2 — Run it verbatim.** Paste it exactly as given. It redeems the claim, saves the session, installs the CLI globally if needed, deploys the skills into every coding agent on the machine, and finishes by running `ready` — so no `init` and no add-in restart belong to this flow.
130
+ **2 — Run it verbatim.** Paste it exactly as given. It redeems the claim, saves the session,
131
+ installs the CLI globally if needed, deploys the skills into every coding agent on the machine, and
132
+ finishes by running `ready` — so no `init` and no add-in restart belong to this flow.
64
133
 
65
- **3 — If it fails.** A claim works once and expires in ~10 minutes; on *already used or expired*, ask for a fresh one. The pane flips to **Connected** by itself once the claim is spent.
134
+ **3 — If it fails.** A claim works once and expires in ~10 minutes; on *already used or expired*,
135
+ ask for a fresh one. The pane flips to **Connected** by itself once the claim is spent.
66
136
 
67
137
  ## First step — `slidemind ready` (before anything else, every time)
68
138
 
69
- Everything about how a deck is authored, styled, and rendered is shaped by the corporate design **the add-in has open**, and every brand rule you follow is read from that design's **materialized bundle**. `slidemind ready` gets you there in one call: it walks the connection ladder (a saved session → reachable backend → CLI version → an open render-ready window), resolves which open design to build against, downloads or refreshes that design's bundle (incremental — unchanged files cost nothing), and — when you pass a deck — binds the deck to it by writing `<deck>/.bundle-ref.json`.
70
-
71
- - **New work:** `slidemind ready` — resolves + materializes the open design's bundle so you can read its `design.md` while planning.
72
- - **Continuing a deck:** `slidemind ready <deck>` — same, and (re)binds the deck to whatever design is open.
73
-
74
- **The open add-in is the source of truth.** `ready` binds to whatever design is open *now*; a deck's `.bundle-ref.json` records the last-resolved design for the render-time etag contract and host-free bundle reads — it is **not** a pinned design preference. So you never "start a session with design A" from a deck ref; you start it from what the add-in has open.
75
-
76
- **On exit 0** the JSON reports `presId`, `presFP`, `designName`, `bundleDir`, `bundleEtag`, `bound`, `paneSuspended`, and `engines` (`{available, envs}` — whether engine search reaches the corpus without a window), with `nextAction: "Ready."`. Read `workflow/flow.md` and `design.md` from `bundleDir` (cached at `${XDG_CACHE_HOME:-~/.cache}/slidemind/bundles/<presFP>/`, alongside `corporate-design.css`, `layouts/*.jsx`, `layouts/layouts.css`, `assets/`, `samples/`, and the rest of the add-in-owned `workflow/*.md`) and start planning. A render-ready window whose pane is suspended still exits 0 with `paneSuspended: true` — the pane ops wait for a pane click on their own; **don't treat it as a blocker.**
77
-
78
- **A bound deck readies with NO window at all — that is the normal case, not a degraded one.** `slidemind ready <deck>` for a deck that was bound once before exits 0 with `windowless: true` even when PowerPoint is closed: the design identity lives in the deck's `.bundle-ref.json` and the bundle syncs over a pane-free route, so its ladder is *session → bundle freshness → engine reachability*, with **no pane precondition** — the pane is reported, never required. In that state authoring, `view-draft`, `build`, `review`, `build-pptx`, and (where `engines.available` is true) engine search and download all work end-to-end; only `push`'s insert, `insert-slide`, and `read <presId>` of an open presentation need a window — the `nextAction` says exactly that. Don't ask the user to open PowerPoint until one of those is actually next.
79
-
80
- **Microsoft 365 sign-in — automatic on first need; just let it happen.** The engine surface — corpus search, brand-imagery downloads, slide extraction — runs **as the user**. `ready` reports the state (JSON: `m365.signedIn`, `m365.degradation`) so you know it's coming, but you never initiate it: the first engine command opens the user's browser ITSELF, prints `Microsoft 365 sign-in needed — opening your browser…`, and **blocks until the human finishes**. When that line appears, tell the user to complete the sign-in in the browser (you cannot) and keep waiting — don't kill or re-run the command. One sign-in lasts ~90 days. Rendering, bundles, and already-materialized assets never need it, so a pure author→render session runs signed-out without ever seeing this.
81
-
82
- For a deck that was **never** bound and no window is open, `ready` asks the backend which corporate designs the user may build against: exactly one binds automatically, several stop with `ambiguous_brand` (below).
83
-
84
- **On non-zero** the CLI names exactly one blocker `{code, actor, message}` and one `nextAction`. **The CLI owns the remediation text — relay `nextAction`, don't restate it.** These are the ones that need the user:
85
-
86
- - **`ambiguous_design`** — several windows with **different** corporate designs are open, so `ready` can't choose. The CLI lists the candidates (each with its `env` and window `title`/`fileName`, plus a design fingerprint); if the deck was last built against one of them it's marked `suggested`. **Ask the user which design to build against, naming the windows by `title`/`fileName` (and `env`) — never by raw id** — and mention the suggested one if present ("this deck was last built against X — continue with it?"). Then re-run `slidemind ready --to <presId>` for the window they pick.
87
- - **`ambiguous_brand`** — the same question with no window to ask it from: the backend offers several designs (`brands[]`, each with a `name` and `env`). Ask the user by **name**, then re-run `slidemind ready [deck] --brand <presFP>`.
88
- - **`login_required`** — the automatic browser sign-in was refused or abandoned, so **corpus search and engine downloads** stop until the user runs `slidemind login` (browser step, whenever they're ready). It never blocks authoring, rendering, or the bundle — relay the line, keep working, and re-run the search after they've signed in. When a slidemind pane is already open the CLI says so and routes the search through that window instead: follow what it printed.
89
- - **Connection blockers** (`not_connected`, `all_unreachable`, `cli_update_required`, `no_window`, `not_render_ready`) — relay the CLI's `nextAction` verbatim (copy the setup command / open PowerPoint / click the pane / run `slidemind update`). Connecting is walked through above. (`no_window` only ever surfaces for a deck that is neither bound nor bindable from the backend's design list — a bound deck rides the pane-free rung above instead.)
90
-
91
- **The session spine** (= the stage todos you created up front): `slidemind ready` → read `workflow/flow.md` + `design.md` from `bundleDir` → intake/plan (below) → `slidemind new <slug>` (scaffolds **and** auto-binds the deck to the open design — zero extra download, because `ready` already materialized it) → `slidemind plan <slug>` (plan sign-off gate for decks; single slides stay in chat) → `slidemind concepts <slug>` (mandatory concept-mockup pass for a deck, then rebuild toward each) → author → `view-draft` → `review` (mandatory, the moment the deck builds clean) → deliver (`push` / `build-pptx`). `slidemind new` best-effort binds when the open design is unambiguous and the bundle is already materialized; if it can't (ambiguous, or no host), it scaffolds anyway and hints to run `slidemind ready <slug>`.
92
-
93
- **Backgrounded — or closed — PowerPoint is expected to work.** Do not pre-emptively ask the user to focus PowerPoint or the task pane, and never ask them to keep it foregrounded while something runs. **The whole authoring loop is host-free for a bound deck** — intake → plan → concepts → author → `view-draft` → `review` → `slidemind build-pptx <deck> [--out <file>]` — because the render captures the slides with a local headless browser and assembles the `.pptx` server-side over the pane-free brand route (streaming its own `rendering slide n/m…` progress), and engine discovery, search, downloads, and slide reuse run off the connection wherever the add-in serves them (`engines.available` in `ready`'s payload; older add-ins relay them through a pane instead). Exactly three things need a live PowerPoint: **`push`'s insert**, **`insert-slide`**, and **`read <presId>`** of an open presentation. Where a pane *is* involved, the CLI handles the wait itself: up to ~2 min, one *"click the slidemind pane"* line when user help is genuinely needed, then a safe retry. If you see that line, relay it once and keep waiting — do not rebuild, re-plan, or babysit. If a `push` can't deliver (pane never woke, or no window at all), the rendered `.pptx` was saved next to the deck (`paneSuspended` + `fallbackFile` in the result) — nothing is lost; re-run the push once a window exists. Reserve the full "open PowerPoint…" instruction for when a command actually reports it as the blocker.
94
-
95
- `slidemind build`, `review`, and `view-draft` run offline, but only against a **previously materialized** bundle — so `ready` (or a prior `ready`) must have succeeded at least once. `build-pptx` additionally needs the backend reachable (the OOXML assembly is server-side), but no PowerPoint window for a bound deck — so *"a `.pptx` file to send around"* is deliverable end-to-end with PowerPoint closed.
139
+ Everything about how a deck is authored, styled, and rendered is shaped by the corporate design
140
+ **the add-in has open**, and every brand rule you follow is read from that design's **materialized
141
+ bundle**. `slidemind ready` gets you there in one call: it walks the connection ladder (a saved
142
+ session → reachable backend → CLI version → an open render-ready window), resolves which open design
143
+ to build against, downloads or refreshes that design's bundle (incremental — unchanged files cost
144
+ nothing), and — when you pass a deck — binds the deck to it by writing `<deck>/.bundle-ref.json`.
145
+
146
+ - **New work:** `slidemind ready` — resolves + materializes the open design's bundle so you can read
147
+ its `design.md` while planning.
148
+ - **Continuing a deck:** `slidemind ready <deck>` — same, and (re)binds the deck to whatever design
149
+ is open.
150
+
151
+ **The open add-in is the source of truth.** `ready` binds to whatever design is open *now*; a deck's
152
+ `.bundle-ref.json` records the last-resolved design for the render-time etag contract and host-free
153
+ bundle reads — it is **not** a pinned design preference. So you never "start a session with design
154
+ A" from a deck ref; you start it from what the add-in has open.
155
+
156
+ **On exit 0** the JSON reports `presId`, `presFP`, `designName`, `bundleDir`, `bundleEtag`, `bound`,
157
+ `paneSuspended`, and `engines` (`{available, envs}` — whether engine search reaches the corpus
158
+ without a window), with `nextAction: "Ready."`. Read `workflow/flow.md` and `design.md` from
159
+ `bundleDir` (cached at `${XDG_CACHE_HOME:-~/.cache}/slidemind/bundles/<presFP>/`, alongside
160
+ `corporate-design.css`, `layouts/*.jsx`, `layouts/layouts.css`, `assets/`, `samples/`, and the rest
161
+ of the add-in-owned `workflow/*.md`) and start planning. A render-ready window whose pane is
162
+ suspended still exits 0 with `paneSuspended: true` — the pane ops wait for a pane click on their
163
+ own; **don't treat it as a blocker.**
164
+
165
+ **A bound deck readies with NO window at all — that is the normal case, not a degraded one.**
166
+ `slidemind ready <deck>` for a deck that was bound once before exits 0 with `windowless: true` even
167
+ when PowerPoint is closed: the design identity lives in the deck's `.bundle-ref.json` and the bundle
168
+ syncs over a pane-free route, so its ladder is *session → bundle freshness → engine reachability*,
169
+ with **no pane precondition** — the pane is reported, never required. In that state authoring,
170
+ `view-draft`, `build`, `review`, `build-pptx`, and (where `engines.available` is true) engine search
171
+ and download all work end-to-end; only `push`'s insert, `insert-slide`, and `read <presId>` of an
172
+ open presentation need a window — the `nextAction` says exactly that. Don't ask the user to open
173
+ PowerPoint until one of those is actually next.
174
+
175
+ **Microsoft 365 sign-in — automatic on first need; just let it happen.** The engine surface — corpus
176
+ search, brand-imagery downloads, slide extraction — runs **as the user**. `ready` reports the state
177
+ (JSON: `m365.signedIn`, `m365.degradation`) so you know it's coming, but you never initiate it: the
178
+ first engine command opens the user's browser ITSELF, prints `Microsoft 365 sign-in needed — opening
179
+ your browser…`, and **blocks until the human finishes**. When that line appears, tell the user to
180
+ complete the sign-in in the browser (you cannot) and keep waiting — don't kill or re-run the
181
+ command. One sign-in lasts ~90 days. Rendering, bundles, and already-materialized assets never need
182
+ it, so a pure author→render session runs signed-out without ever seeing this.
183
+
184
+ For a deck that was **never** bound and no window is open, `ready` asks the backend which corporate
185
+ designs the user may build against: exactly one binds automatically, several stop with
186
+ `ambiguous_brand` (below).
187
+
188
+ **On non-zero** the CLI names exactly one blocker `{code, actor, message}` and one `nextAction`.
189
+ **The CLI owns the remediation text — relay `nextAction`, don't restate it.** These are the ones
190
+ that need the user:
191
+
192
+ - **`ambiguous_design`** — several windows with **different** corporate designs are open, so `ready`
193
+ can't choose. The CLI lists the candidates (each with its `env` and window `title`/`fileName`,
194
+ plus a design fingerprint); if the deck was last built against one of them it's marked
195
+ `suggested`. **Ask the user which design to build against, naming the windows by
196
+ `title`/`fileName` (and `env`) — never by raw id** — and mention the suggested one if present
197
+ ("this deck was last built against X — continue with it?"). Then re-run `slidemind ready --to
198
+ <presId>` for the window they pick.
199
+ - **`ambiguous_brand`** — the same question with no window to ask it from: the backend offers
200
+ several designs (`brands[]`, each with a `name` and `env`). Ask the user by **name**, then re-run
201
+ `slidemind ready [deck] --brand <presFP>`.
202
+ - **`login_required`** — the automatic browser sign-in was refused or abandoned, so **corpus search
203
+ and engine downloads** stop until the user runs `slidemind login` (browser step, whenever they're
204
+ ready). It never blocks authoring, rendering, or the bundle — relay the line, keep working, and
205
+ re-run the search after they've signed in. When a slidemind pane is already open the CLI says so
206
+ and routes the search through that window instead: follow what it printed.
207
+ - **Connection blockers** (`not_connected`, `all_unreachable`, `cli_update_required`, `no_window`,
208
+ `not_render_ready`) — relay the CLI's `nextAction` verbatim (copy the setup command / open
209
+ PowerPoint / click the pane / run `slidemind update`). Connecting is walked through above.
210
+ (`no_window` only ever surfaces for a deck that is neither bound nor bindable from the backend's
211
+ design list — a bound deck rides the pane-free rung above instead.)
212
+
213
+ **The session spine** (= the stage todos you created up front): `slidemind ready` → read
214
+ `workflow/flow.md` + `design.md` from `bundleDir` → intake/plan (below) → `slidemind new <slug>`
215
+ (scaffolds **and** auto-binds the deck to the open design — zero extra download, because `ready`
216
+ already materialized it) → `slidemind plan <slug>` (plan sign-off gate for decks; single slides stay
217
+ in chat) → `slidemind concepts <slug>` (mandatory concept-mockup pass for a deck, then rebuild
218
+ toward each) → author → `view-draft` → `review` (mandatory, the moment the deck builds clean) →
219
+ deliver (`push` / `build-pptx`). `slidemind new` best-effort binds when the open design is
220
+ unambiguous and the bundle is already materialized; if it can't (ambiguous, or no host), it
221
+ scaffolds anyway and hints to run `slidemind ready <slug>`.
222
+
223
+ **Backgrounded — or closed — PowerPoint is expected to work.** Do not pre-emptively ask the user to
224
+ focus PowerPoint or the task pane, and never ask them to keep it foregrounded while something runs.
225
+ **The whole authoring loop is host-free for a bound deck** — intake → plan → concepts → author →
226
+ `view-draft` → `review` → `slidemind build-pptx <deck> [--out <file>]` — because the render captures
227
+ the slides with a local headless browser and assembles the `.pptx` server-side over the pane-free
228
+ brand route (streaming its own `rendering slide n/m…` progress), and engine discovery, search,
229
+ downloads, and slide reuse run off the connection wherever the add-in serves them
230
+ (`engines.available` in `ready`'s payload; older add-ins relay them through a pane instead). Exactly
231
+ three things need a live PowerPoint: **`push`'s insert**, **`insert-slide`**, and **`read
232
+ <presId>`** of an open presentation. Where a pane *is* involved, the CLI handles the wait itself: up
233
+ to ~2 min, one *"click the slidemind pane"* line when user help is genuinely needed, then a safe
234
+ retry. If you see that line, relay it once and keep waiting — do not rebuild, re-plan, or babysit.
235
+ If a `push` can't deliver (pane never woke, or no window at all), the rendered `.pptx` was saved
236
+ next to the deck (`paneSuspended` + `fallbackFile` in the result) — nothing is lost; re-run the push
237
+ once a window exists. Reserve the full "open PowerPoint…" instruction for when a command actually
238
+ reports it as the blocker.
239
+
240
+ `slidemind build`, `review`, and `view-draft` run offline, but only against a **previously
241
+ materialized** bundle — so `ready` (or a prior `ready`) must have succeeded at least once.
242
+ `build-pptx` additionally needs the backend reachable (the OOXML assembly is server-side), but no
243
+ PowerPoint window for a bound deck — so *"a `.pptx` file to send around"* is deliverable end-to-end
244
+ with PowerPoint closed.
96
245
 
97
246
  ## Two UX rules (gates + host)
98
247
 
99
- - **Announce every gate, with a clickable link.** The moment `slidemind plan` or `slidemind review` starts it prints `ready at <url>` and a `BROWSER:` status line. Immediately tell the user in chat whether a browser tab opened (new tab / reused tab / failed to open) and give the gate URL as a clickable link — every round, and again whenever you report that you're still waiting on their decision.
100
- - **Add-in unreachable → block and name the tab.** When a genuinely host-required command (`push`'s insert, `insert-slide`, `read <presId>`) can't reach the add-in (a connection blocker, a pane that never wakes, the *click the slidemind pane* line with no effect), **stop the workflow** — don't skip the step, work around it, or retry silently. Ask the user to go back into the PowerPoint tab of presentation **<title/fileName>** (name it from the command output or `slidemind presentations` — never a raw id), then re-run the failed command once they have. Everything before delivery keeps running meanwhile — never park the whole task on a window it doesn't need.
248
+ - **Announce every gate, with a clickable link.** The moment `slidemind plan` or `slidemind review`
249
+ starts it prints `ready at <url>` and a `BROWSER:` status line. Immediately tell the user in chat
250
+ whether a browser tab opened (new tab / reused tab / failed to open) and give the gate URL as a
251
+ clickable link — every round, and again whenever you report that you're still waiting on their
252
+ decision.
253
+ - **Add-in unreachable → block and name the tab.** When a genuinely host-required command (`push`'s
254
+ insert, `insert-slide`, `read <presId>`) can't reach the add-in (a connection blocker, a pane that
255
+ never wakes, the *click the slidemind pane* line with no effect), **stop the workflow** — don't
256
+ skip the step, work around it, or retry silently. Ask the user to go back into the PowerPoint tab
257
+ of presentation **<title/fileName>** (name it from the command output or `slidemind presentations`
258
+ — never a raw id), then re-run the failed command once they have. Everything before delivery keeps
259
+ running meanwhile — never park the whole task on a window it doesn't need.
101
260
 
102
261
  ## Intake → plan (you own this; do it in chat)
103
262
 
104
- This is your mapping of `workflow/flow.md` stages 1–2 (*clarify* + *plan sign-off*). In-app, a manager runs these; over the CLI the manager role collapses onto **you**, so you run the interview and the sign-off directly with the user. Don't write slide JSX until the plan is agreed.
105
-
106
- - **Read scope from the wording**, don't ask redundantly: single slide ("make a slide about X"), small set ("add three slides on Y"), or whole deck ("a deck for Friday"). Only ask "one slide, a small set, or a full deck?" when truly ambiguous.
107
- - **Interview just enough** to pin: audience/goal, the content and its sources (none / pasted / local files / existing deck / web research — a source that's already a local `.pptx` you read host-free with `slidemind read <file.pptx>`), and the slide outline (each slide's role + a layout *job* like cover/agenda/section/quote/closing/body — the concrete brand layout is chosen later against the bundle). If the content source resolves to "none", don't silently plan from generic knowledge — say so, and either interview the user for the substance (their claims, numbers, examples) or offer web research before writing storyboards.
108
- - **Lock the output target — it decides your deliver command.** Into the open PowerPoint (`slidemind push`), or a `.pptx` file (`slidemind build-pptx`) — or an HTML preview / a mix. Infer it when the wording is clear ("add a slide to this deck" → push; "make me a deck I can send around" → a file) and ask only when genuinely ambiguous — but **always confirm the concrete target before delivering** (which window, or which output path).
109
- - **Replay and get sign-off.** Single slides / tiny sets → a compact in-chat plan (no gate). Decks → persist a **story-focused plan with body storyboards** in `decks/<slug>/plan.md`, two top-level sections in order:
110
- - `## Red thread` — one presentation **move** per slide, as bullets or one arrow-separated line. The number of moves MUST equal the number of `### Slide N` sections: when the user names a slide count, write exactly that many moves and sections — never expand the narrative arc beyond the requested count. Name the *teaching function* of each slide — short verb+noun labels for what the audience is doing cognitively at that step — never its content: no claims, facts, metrics, client-specific terms, slide copy, generic section names, or wording copied from these instructions.
111
- - `## Per-slide storyboards` — one `### Slide N: <role>` subsection per slide, each a one-sentence `Core message:` followed by **exactly one** fenced `storyboard-canvas` JSON block.
112
-
113
- The `storyboard-canvas` is a **body-only spatial sketch**, not final slide design: it plans the body region only and never the template title/header/footer/logo/slide-number areas. Blocks live in a 1600 × 900 coordinate space, kept inside the body-safe area (roughly x 80–1520, y 110–780); position, size, and proximity must express the slide's meaning — hierarchy, sequence, causality, contrast, grouping, flow direction, or the main focus — not random placement. Each block shows only two visible text fields: `designElement` (the visual mechanism in up to 5 words — e.g. "hub-spoke visual", "loop diagram", "timeline", "matrix", "before/after contrast", "scorecard") and `content` (a 1–2 word label), plus `emphasis` ("primary"/"secondary"/"tertiary") for hierarchy. `keyMessage` is the slide's **story beat**: one plain sentence naming what this slide does to move the story forward, stated abstractly — NOT the headline or copy that will appear on the slide, and not the red-thread move restated. `contentNotes` is the **content draft** that delivers that beat — facts, claims, examples, source points, caveats, draft copy, proof points — and carries no design, layout, tone, or authoring-process instructions. It is the slide's full content budget: the create stage and the concept-mockup generator build from `contentNotes` alone and never add substance, so anything not in the notes will not be on the slide. Write them so the slide body could be authored from them without inventing anything — every claim spelled out concretely, with the numbers, named examples, or proof points that make it land. Two or three abstract topic bullets ("X is expensive", "Y improves") is an unfinished draft, not a storyboard.
114
-
115
- Judge whether two or more slides form a genuine visual series — for example a continuation, progressive reveal, repeated comparison, phased journey, or recurring framework. When they do, give those storyboard objects the same short, stable `visualFamily` identifier. Do not group slides merely because they share a topic, and do not ask the user to make this design judgment. A family tells concept generation to establish one recognizable visual system and adapt it to every member's own `keyMessage`, `contentNotes`, role, and structure. It never permits copying another slide's text, claims, data, or exact geometry, and it never permits dropping approved content to fit the shared concept. Omit `visualFamily` for independent slides.
116
-
117
- Choose the block count from the slide's meaning and **never default to three** (1 for a hero idea, 2 for a contrast or paired tension, 3 only for three genuinely distinct beats, 4–5 only for a true sequence or comparison set), and vary the spatial composition across slides — avoid repeating the same left/right/bottom alignment. Ban style micromanagement only — no colours, fonts, exact layout names, or final box/card counts: **the create stage owns realisation** of the approved body storyboard in the corporate design. Use this exact JSON shape:
263
+ This is your mapping of `workflow/flow.md` stages 1–2 (*clarify* + *plan sign-off*). In-app, a
264
+ manager runs these; over the CLI the manager role collapses onto **you**, so you run the interview
265
+ and the sign-off directly with the user. Don't write slide JSX until the plan is agreed.
266
+
267
+ - **Read scope from the wording**, don't ask redundantly: single slide ("make a slide about X"),
268
+ small set ("add three slides on Y"), or whole deck ("a deck for Friday"). Only ask "one slide, a
269
+ small set, or a full deck?" when truly ambiguous.
270
+ - **Interview just enough** to pin: audience/goal, the content and its sources (none / pasted /
271
+ local files / existing deck / web research — a source that's already a local `.pptx` you read
272
+ host-free with `slidemind read <file.pptx>`), and the slide outline (each slide's role + a layout
273
+ *job* like cover/agenda/section/quote/closing/body — the concrete brand layout is chosen later
274
+ against the bundle). If the content source resolves to "none", don't silently plan from generic
275
+ knowledge — say so, and either interview the user for the substance (their claims, numbers,
276
+ examples) or offer web research before writing storyboards.
277
+ - **Lock the output target — it decides your deliver command.** Into the open PowerPoint (`slidemind
278
+ push`), or a `.pptx` file (`slidemind build-pptx`) — or an HTML preview / a mix. Infer it when the
279
+ wording is clear ("add a slide to this deck" → push; "make me a deck I can send around" → a file)
280
+ and ask only when genuinely ambiguous — but **always confirm the concrete target before
281
+ delivering** (which window, or which output path).
282
+ - **Replay and get sign-off.** Single slides / tiny sets → a compact in-chat plan (no gate). Decks →
283
+ persist a **story-focused plan with body storyboards** in `decks/<slug>/plan.md`, two top-level
284
+ sections in order:
285
+ - `## Red thread` — one presentation **move** per slide, as bullets or one arrow-separated line.
286
+ The number of moves MUST equal the number of `### Slide N` sections: when the user names a slide
287
+ count, write exactly that many moves and sections — never expand the narrative arc beyond the
288
+ requested count. Name the *teaching function* of each slide — short verb+noun labels for what
289
+ the audience is doing cognitively at that step — never its content: no claims, facts, metrics,
290
+ client-specific terms, slide copy, generic section names, or wording copied from these
291
+ instructions.
292
+ - `## Per-slide storyboards` — one `### Slide N: <role>` subsection per slide, each a one-sentence
293
+ `Core message:` followed by **exactly one** fenced `storyboard-canvas` JSON block.
294
+
295
+ The `storyboard-canvas` is a **body-only spatial sketch**, not final slide design: it plans the
296
+ body region only and never the template title/header/footer/logo/slide-number areas. Blocks live
297
+ in a 1600 × 900 coordinate space, kept inside the body-safe area (roughly x 80–1520, y 110–780);
298
+ position, size, and proximity must express the slide's meaning — hierarchy, sequence, causality,
299
+ contrast, grouping, flow direction, or the main focus — not random placement. Each block shows
300
+ only two visible text fields: `designElement` (the visual mechanism in up to 5 words — e.g.
301
+ "hub-spoke visual", "loop diagram", "timeline", "matrix", "before/after contrast", "scorecard")
302
+ and `content` (a 1–2 word label), plus `emphasis` ("primary"/"secondary"/"tertiary") for
303
+ hierarchy. `keyMessage` is the slide's **story beat**: one plain sentence naming what this slide
304
+ does to move the story forward, stated abstractly — NOT the headline or copy that will appear on
305
+ the slide, and not the red-thread move restated. `contentNotes` is the **content draft** that
306
+ delivers that beat — facts, claims, examples, source points, caveats, draft copy, proof points —
307
+ and carries no design, layout, tone, or authoring-process instructions. It is the slide's full
308
+ content budget: the create stage and the concept-mockup generator build from `contentNotes` alone
309
+ and never add substance, so anything not in the notes will not be on the slide. Write them so the
310
+ slide body could be authored from them without inventing anything — every claim spelled out
311
+ concretely, with the numbers, named examples, or proof points that make it land. Two or three
312
+ abstract topic bullets ("X is expensive", "Y improves") is an unfinished draft, not a storyboard.
313
+
314
+ Judge whether two or more slides form a genuine visual series — for example a continuation,
315
+ progressive reveal, repeated comparison, phased journey, or recurring framework. When they do,
316
+ give those storyboard objects the same short, stable `visualFamily` identifier. Do not group
317
+ slides merely because they share a topic, and do not ask the user to make this design judgment. A
318
+ family tells concept generation to establish one recognizable visual system and adapt it to every
319
+ member's own `keyMessage`, `contentNotes`, role, and structure. It never permits copying another
320
+ slide's text, claims, data, or exact geometry, and it never permits dropping approved content to
321
+ fit the shared concept. Omit `visualFamily` for independent slides.
322
+
323
+ Choose the block count from the slide's meaning and **never default to three** (1 for a hero idea,
324
+ 2 for a contrast or paired tension, 3 only for three genuinely distinct beats, 4–5 only for a true
325
+ sequence or comparison set), and vary the spatial composition across slides — avoid repeating the
326
+ same left/right/bottom alignment. Ban style micromanagement only — no colours, fonts, exact layout
327
+ names, or final box/card counts: **the create stage owns realisation** of the approved body
328
+ storyboard in the corporate design. Use this exact JSON shape:
118
329
 
119
330
  ```storyboard-canvas
120
331
  {
@@ -141,77 +352,284 @@ This is your mapping of `workflow/flow.md` stages 1–2 (*clarify* + *plan sign-
141
352
  }
142
353
  ```
143
354
 
144
- Make the connection to the current deck explicit in the red thread, core messages, `keyMessage`, or `contentNotes` whenever the work extends the open presentation, and fold in any content findings from slides/files you inspected. Then run `slidemind plan <deck>`: it opens the plan as **editable storyboards** — the user reorders/renames/inserts/removes slides on the red-thread strip, drags/resizes/edits blocks and the content draft directly on each slide's canvas, and/or leaves comments anchored to a slide or to the whole story — and it **blocks until the user decides** — announce it per *Two UX rules* (browser opened? + the clickable gate URL) and give the shell call a **generous timeout** (the gate waits up to 30 min; if your harness kills the call early, just re-run it — the user's open tab reconnects). Outcomes: `DECISION: approved` → proceed to author; `DECISION: comments` → apply the comments to `plan.md` — each is **slide-anchored** (`- [Slide 3: "<beat>"] …`) or whole-story, and the batch may include a **generated story-iteration instruction** (when the user restructured the red thread by adding or renaming slides, the gate asks you to build storyboards and content for exactly those moves while keeping the other slides intact) — (the round is persisted verbatim in `<deck>/plan-review.json` if your capture of the output was truncated). Change only what the comments require and keep unaffected slides verbatim (that is what makes the reviewer's per-slide "updated" badges meaningful), then **re-run the gate in the same turn**. **Expect several consecutive `DECISION: comments` rounds — that is the normal shape of a review, not an error.** The reviewer's tab stays live while you revise, so they keep reading and keep queueing comments; those queued comments are delivered by your next run, which then returns *immediately* with them (a drain round) instead of waiting. So never end your turn on a comments round, never claim to be "polling", and never send the user back to the browser between rounds — nothing reaches them until the gate runs again. Only `DECISION: approved` or `DECISION: pending` ends the loop; `DECISION: pending` → the gate timed out with no answer — not approval, not rejection: don't author; wrap up telling the user the plan still awaits their sign-off and re-run `slidemind plan <deck>` on their go-ahead. Loop until approved. A `plan.md` **approved in a prior gate round and unchanged since** counts as approved (skip straight to authoring); any new or revised plan must pass the gate. When the gate reports the user edited `plan.md` directly, re-read it — the edited storyboards **are** the approved plan; author from them.
145
-
146
- Keep planning about intent and content. The bundle is already materialized by `slidemind ready`, so `design.md` is there if you need to sanity-check feasibility — but **don't dive into brand/layout/composition detail until create/edit.**
355
+ Make the connection to the current deck explicit in the red thread, core messages, `keyMessage`,
356
+ or `contentNotes` whenever the work extends the open presentation, and fold in any content
357
+ findings from slides/files you inspected. Then run `slidemind plan <deck>`: it opens the plan as
358
+ **editable storyboards** — the user reorders/renames/inserts/removes slides on the red-thread
359
+ strip, drags/resizes/edits blocks and the content draft directly on each slide's canvas, and/or
360
+ leaves comments anchored to a slide or to the whole story — and it **blocks until the user
361
+ decides** — announce it per *Two UX rules* (browser opened? + the clickable gate URL) and give the
362
+ shell call a **generous timeout** (the gate waits up to 30 min; if your harness kills the call
363
+ early, just re-run it — the user's open tab reconnects). Outcomes: `DECISION: approved` → proceed
364
+ to author; `DECISION: comments` → apply the comments to `plan.md` — each is **slide-anchored** (`-
365
+ [Slide 3: "<beat>"] …`) or whole-story, and the batch may include a **generated story-iteration
366
+ instruction** (when the user restructured the red thread by adding or renaming slides, the gate
367
+ asks you to build storyboards and content for exactly those moves while keeping the other slides
368
+ intact) — (the round is persisted verbatim in `<deck>/plan-review.json` if your capture of the
369
+ output was truncated). Change only what the comments require and keep unaffected slides verbatim
370
+ (that is what makes the reviewer's per-slide "updated" badges meaningful), then **re-run the gate
371
+ in the same turn**. **Expect several consecutive `DECISION: comments` rounds — that is the normal
372
+ shape of a review, not an error.** The reviewer's tab stays live while you revise, so they keep
373
+ reading and keep queueing comments; those queued comments are delivered by your next run, which
374
+ then returns *immediately* with them (a drain round) instead of waiting. So never end your turn on
375
+ a comments round, never claim to be "polling", and never send the user back to the browser between
376
+ rounds — nothing reaches them until the gate runs again. Only `DECISION: approved` or `DECISION:
377
+ pending` ends the loop; `DECISION: pending` → the gate timed out with no answer — not approval,
378
+ not rejection: don't author; wrap up telling the user the plan still awaits their sign-off and
379
+ re-run `slidemind plan <deck>` on their go-ahead. Loop until approved. A `plan.md` **approved in a
380
+ prior gate round and unchanged since** counts as approved (skip straight to authoring); any new or
381
+ revised plan must pass the gate. When the gate reports the user edited `plan.md` directly, re-read
382
+ it — the edited storyboards **are** the approved plan; author from them.
383
+
384
+ Keep planning about intent and content. The bundle is already materialized by `slidemind ready`, so
385
+ `design.md` is there if you need to sanity-check feasibility — but **don't dive into
386
+ brand/layout/composition detail until create/edit.**
147
387
 
148
388
  ## The create flow
149
389
 
150
- `slidemind ready` already resolved the design source and materialized its bundle; the deck was bound to it by `slidemind ready <deck>` (continuing) or by `slidemind new <slug>`'s auto-bind (new work). From here:
151
-
152
- 1. **Study brand samples (before composing).** The materialized bundle's `samples/` dir holds the brand's own reference slides. If it's non-empty, pick **3 at random** and **Read** each (the Read tool renders images) *before* writing any JSX — this is the local-harness stand-in for the in-app persona's reference thumbnails, and it is what keeps a deck looking on-brand rather than merely compliant. Study them for whitespace rhythm, shape density, hierarchy, accent use, and layering, then compose in that spirit (never copy a sample 1:1). This is `composition.md`'s samples discipline; skip only when `samples/` is empty.
153
- 2. **Read, then author.** Read the create-stage references together — `workflow/create.md` (the procedure), `workflow/composition.md` (the composition baseline), `workflow/layouts.md` (palette + working area), and `workflow/authoring.md` (the substrate rules) — plus the deck's `design.md` and the specific `layouts/*.jsx` the plan uses. (That is the read set `workflow/create.md` → Context loading prescribes; follow it as written — `workflow/flow.md` you already read after `ready`.) Then write `decks/<slug>/slides/NN-<topic>.jsx` — zero-padded numeric prefix from the first file on (play order is filename order here; see the slide-file row in the command mapping), one default export per slide, composing one bundle layout (Mode A) or a blank-content layout with freeform children (Mode B), exactly as those docs prescribe. The approved `plan.md` is a **content contract, not a rendering spec** — you drew its storyboards yourself, but approval covered the story and content, not the sketch's geometry; how to interpret the `storyboard-canvas` blocks at author time (design hypothesis: reinterpret freely, never transcribe coordinates, block geometry, or block labels) is owned by `workflow/create.md` → Preconditions. **Generate concept mockups first — mandatory for a deck (stage 4).** Before authoring a deck from an approved plan, run `slidemind concepts <deck>` — the local-harness equivalent of the in-app manager's *unconditional* concept pass (`generateConceptImages` fires for every `approvedPlan && !targetSlideScope` delegation). Then **Read** each `decks/<slug>/concepts/concept-slide-NN.png` as the per-slide design target and rebuild it under your constraints — the rebuild rule is owned by `workflow/create.md` → Preconditions (reproduce the mockup's composition, hierarchy, graphic mechanism, AND granularity — its organism count, badges, satellites, and annotation layers are part of the target, and restraint is a colour budget, never an element budget; texts follow the story). It never hard-fails: if it produces no mockups (exit 0 — the add-in can't generate them) author from the storyboards alone. Skip it only where the add-in's `!targetSlideScope` gate would — a single slide, a tiny in-chat set, or a targeted rework of existing slides.
154
- When storyboard objects share a `visualFamily`, rebuild those mockups as one recognizable visual series. The lowest-numbered member is the anchor. Preserve its shape vocabulary, framing, hierarchy, annotation style, illustration treatment, and spatial rhythm across the family, but adapt every member's element count, sizing, arrangement, labels, emphasis, and connections to its own complete `keyMessage`, `contentNotes`, role, and structure. Never copy another member's text, claims, or data, never force its exact geometry, and never omit approved content because it does not fit the anchor composition.
155
- 3. **Imagery from the engine.** Discover the available image engines by listing them — `slidemind ls engines --subtype image` (the local-harness equivalent of the in-app design agent's `list_engines` tool; no PowerPoint window needed where the add-in serves the pane-free engine routes) — judge which engine fits from its label and description, then search it and place results. (`slidemind search` without `--engine` picks an image engine the same way.) The engine search/reuse mechanics live in **`slidemind-search`** — use it to find and fetch imagery; the create-only detail — sizing a fetched asset's box and recolouring an icon — is in *Placing engine imagery* below. To pull in an **existing whole slide** (not just an image), use `slidemind-search`'s `add-slide` — it folds the slide into `decks/<slug>/slides/` as a native `.pptx` that coexists with your JSX in play order and survives re-push. The slide can come from an engine search **or** from a local `.pptx` on disk (`add-slide --from <file.pptx> --pick <n> --deck <slug>`, host-free); both fold in identically — see `slidemind-search`.
156
- 4. **Look at your own draft first (you, the agent).** This is the **draft check** in `workflow/build-and-export.md` — your pre-check, *before* any human sees the deck. Run `slidemind view-draft <deck>`: it renders each authored `slides/*.jsx` to a per-slide PNG and hands the images back to you — **nothing is pushed, no `.pptx` is written, no review gate opens**. Read every image the way the audience will, fix anything visibly wrong **in the JSX** (re-express, never delete), and view again until each slide is sound. One round usually suffices. It runs locally against the materialized bundle, so it needs no awake pane and no open host once the bundle has been downloaded once. Two faithful limits to know: a slide on a **purpose-built (Mode A) layout** (cover/divider/agenda) and a **reused native `.pptx`** slide have no freeform region to capture, so they come back as a status line, not an image — judge those at the full-deck `review`/`build` step below. Do this self-check before the human review gate; don't push a draft you haven't looked at.
157
- 5. **The review gate — the moment the deck builds clean.** This is the **review gate** in `workflow/build-and-export.md`; the CLI's gate command is `slidemind review <deck>`. Run it the instant the authored set builds clean — before any deliver, without asking whether the user wants a review. It builds the HTML preview first (the same lint the render enforces, so a gate that *opens* is a clean build; while lint errors remain it exits non-zero — fix and re-run), then opens the browser with a spatial comment overlay and **blocks until the user decides** (host-free — no open PowerPoint needed for the gate). Announce it per *Two UX rules*: tell the user the browser opened and give the clickable gate URL. **Blocking is the point: give the shell call a generous timeout (the gate waits up to 30 min) and let it sit — a long quiet wait means the user is reviewing, not that something hung. Never cancel it; if your harness kills the call early, just re-run it (the user's open tab reconnects).** The verdict prints to stdout:
158
- - `DECISION: approved` → proceed to the deliver the user chose at intake: `slidemind push <deck>` (into PowerPoint) or `slidemind build-pptx <deck>` (to a file).
159
- - `DECISION: comments` → the user left spatially-anchored notes, each citing a `slides/<file>.jsx:line:col`, the source line, the click position, and a **round tag** (`[2, round 1] …`) — plus a readonly-layout note where the click landed on a layout slot. A pin carrying an *older* round was dropped on the previous render, so read the file before you edit it rather than trusting the cited line blindly. **Edit the cited files in place at the given line and re-run `slidemind review <deck>` in the same turn.** **Expect several consecutive `DECISION: comments` rounds — that is the normal shape of a review, not an error.** The user's tab stays live while you fix: they keep browsing the preview and keep dropping pins, and those queued pins are delivered by your next run, which then returns *immediately* with them (a drain round) instead of waiting. So never end your turn on a comments round, never claim to be "polling", and never send the user back to the browser between rounds — nothing reaches them until the gate runs again. Addressed pins come back as read-only ghost pins so the user can confirm each note was worked in. Only `DECISION: approved` or `DECISION: pending` ends the loop. Nothing is exported on a comments round. **Every round is persisted verbatim in `<deck>/review.json`** — if your shell capture of the gate's output was truncated or lost (backgrounded command, clipped scrollback), read that file for the complete comment list; never guess at missing comments or ask the user to re-type them.
160
- - `DECISION: pending` → the gate timed out with no answer (the user stepped away). Not approval, not rejection — **do not deliver**. Wrap up your turn: tell the user the preview is still waiting for their review and that on their go-ahead you'll re-run `slidemind review <deck>` (their tab reconnects).
161
-
162
- `slidemind review` is the only step that pauses; `push`/`build-pptx` never pause — they render pane-free (local capture + server-side assembly, with live per-slide progress). **CLI exports happen out of the user's sight, so the flow invariant — *the user sees every change before the task is done* — makes re-review mandatory here: every deliver is preceded by an approved review of exactly what it delivers. After any later slide change, the deck re-enters `slidemind review` before it can be pushed or exported again — never deliver an unreviewed change.** If several presentations are open, confirm **which one to push into** first (it can differ from the design source `slidemind ready` locked in). After approval, run the push without asking; it works while PowerPoint is backgrounded, and the CLI itself waits + retries if the pane is needed (see *First step* above). If a push still times out, the rendered `.pptx` is saved next to the deck — re-run `push` after the user clicks the pane, without rebuilding.
163
-
164
- **On a clean approve + deliver, the task is done.** Report what landed (slide count + target) and stop — no sanity pass, no re-verification. The visual QA already happened at the draft check (step 4) and the user signed off at the review gate.
390
+ `slidemind ready` already resolved the design source and materialized its bundle; the deck was bound
391
+ to it by `slidemind ready <deck>` (continuing) or by `slidemind new <slug>`'s auto-bind (new work).
392
+ From here:
393
+
394
+ 1. **Study brand samples (before composing).** The materialized bundle's `samples/` dir holds the
395
+ brand's own reference slides. If it's non-empty, pick **3 at random** and **Read** each (the Read
396
+ tool renders images) *before* writing any JSX — this is the local-harness stand-in for the in-app
397
+ persona's reference thumbnails, and it is what keeps a deck looking on-brand rather than merely
398
+ compliant. Study them for whitespace rhythm, shape density, hierarchy, accent use, and layering,
399
+ then compose in that spirit (never copy a sample 1:1). This is `composition.md`'s samples
400
+ discipline; skip only when `samples/` is empty.
401
+ 2. **Load create context, then inspect the concepts.** Read the create-stage references together —
402
+ `workflow/create.md` (the procedure), `workflow/composition.md` (the composition baseline),
403
+ `workflow/layouts.md` (palette + working area), and `workflow/authoring.md` (the substrate rules)
404
+ — plus the deck's `design.md` and the specific `layouts/*.jsx` the plan uses. (That is the read
405
+ set `workflow/create.md` → Context loading prescribes; follow it as written — `workflow/flow.md`
406
+ you already read after `ready`.) The approved `plan.md` is a **content contract, not a rendering
407
+ spec** — you drew its storyboards yourself, but approval covered the story and content, not the
408
+ sketch's geometry; how to interpret the `storyboard-canvas` blocks at author time (design
409
+ hypothesis: reinterpret freely, never transcribe coordinates, block geometry, or block labels)
410
+ is owned by `workflow/create.md` → Preconditions. **Generate concept mockups first — mandatory
411
+ for a deck (stage 4).** Before
412
+ authoring a deck from an approved plan, run `slidemind concepts <deck>` — the local-harness
413
+ equivalent of the in-app manager's *unconditional* concept pass (`generateConceptImages` fires
414
+ for every `approvedPlan && !targetSlideScope` delegation). **Immediately after generation, Read
415
+ every `decks/<slug>/concepts/concept-slide-NN.png` at full detail, before any engine or content
416
+ search.** First list the directory and keep the literal repo-relative mapping in the task context:
417
+ concept `NN` maps to `decks/<slug>/concepts/concept-slide-NN.png` and later to the `NN-*.jsx`
418
+ slide. These workspace files are the durable source of truth across turns and compaction — never
419
+ rely on an earlier rendered attachment. Inventory what each mockup requires — imagery, distinct
420
+ pictograms, factual/content verification, annotations, and other visible organisms — and use
421
+ that inventory to drive step 3. This first pass is deliberately deck-wide and search-oriented;
422
+ it is not the fresh authoring read required in step 4. Rebuild each mockup under your constraints
423
+ — the rebuild rule is owned by
424
+ `workflow/create.md` → Preconditions (reproduce the mockup's composition, hierarchy, graphic
425
+ mechanism, AND granularity — its organism count, badges, satellites, and annotation layers are
426
+ part of the target, and restraint is a colour budget, never an element budget; texts follow the
427
+ story). It never hard-fails: if it produces no mockups (exit 0 — the add-in can't generate them)
428
+ author from the storyboards alone. Skip it only where the add-in's `!targetSlideScope` gate would
429
+ — a single slide, a tiny in-chat set, or a targeted rework of existing slides.
430
+ When storyboard objects share a `visualFamily`, rebuild those mockups as one recognizable visual
431
+ series. The lowest-numbered member is the anchor. Preserve its shape vocabulary, framing,
432
+ hierarchy, annotation style, illustration treatment, and spatial rhythm across the family, but
433
+ adapt every member's element count, sizing, arrangement, labels, emphasis, and connections to its
434
+ own complete `keyMessage`, `contentNotes`, role, and structure. Never copy another member's text,
435
+ claims, or data, never force its exact geometry, and never omit approved content because it does
436
+ not fit the anchor composition.
437
+ 3. **Search and fetch from the concept inventory.** Resolve the image and content needs identified
438
+ in the immediate post-generation concept pass. Discover the available image engines by listing
439
+ them — `slidemind ls engines --subtype image` (the local-harness equivalent of the in-app design
440
+ agent's
441
+ `list_engines` tool; no PowerPoint window needed where the add-in serves the pane-free engine
442
+ routes) — judge which engine fits from its label and description, then search it and place
443
+ results. (`slidemind search` without `--engine` picks an image engine the same way.) The engine
444
+ search/reuse mechanics live in **`slidemind-search`** — use it to find and fetch imagery; the
445
+ create-only detail — sizing a fetched asset's box and recolouring an icon — is in *Placing engine
446
+ imagery* below. To pull in an **existing whole slide** (not just an image), use
447
+ `slidemind-search`'s `add-slide` — it folds the slide into `decks/<slug>/slides/` as a native
448
+ `.pptx` that coexists with your JSX in play order and survives re-push. The slide can come from
449
+ an engine search **or** from a local `.pptx` on disk (`add-slide --from <file.pptx> --pick <n>
450
+ --deck <slug>`, host-free); both fold in identically — see `slidemind-search`.
451
+ 4. **Reopen the concepts, then author in small batches.** Once the searches and asset downloads are
452
+ complete, split a concept-seeded deck into batches of at most **3 slides**. For each batch, first
453
+ Read its matching anchored `decks/<slug>/concepts/concept-slide-NN.png` files together at full
454
+ detail; as the **very next tool action**, create those slides'
455
+ `decks/<slug>/slides/NN-<topic>.jsx` files. Put no search, command, reference read, user exchange,
456
+ or unrelated work between the concept images and the JSX write.
457
+ The deck-wide discovery read in step 2 never counts as this authoring read. A user interruption,
458
+ resumed turn, compaction, long wait, or unrelated tool work invalidates an earlier authoring read:
459
+ re-list `decks/<slug>/concepts/` if needed and reopen the affected anchored files before writing.
460
+ This batching is a local-harness context discipline, not a second create pass: the whole deck
461
+ remains one create pass and every slide file is created exactly once. Use a zero-padded numeric
462
+ prefix from the first file on (play order is filename order here; see the slide-file row in the
463
+ command mapping), one default export per slide, and one bundle layout (Mode A) or blank-content
464
+ layout with freeform children (Mode B), exactly as the bundle docs prescribe.
465
+ 5. **Look at your own draft first (you, the agent).** This is the **draft check** in
466
+ `workflow/build-and-export.md` — your pre-check, *before* any human sees the deck. Run `slidemind
467
+ view-draft <deck>`: it renders each authored `slides/*.jsx` to a per-slide PNG and hands the
468
+ images back to you — **nothing is pushed, no `.pptx` is written, no review gate opens**. For
469
+ every concept-seeded slide, Read its anchored
470
+ `decks/<slug>/concepts/concept-slide-NN.png` and matching
471
+ `decks/<slug>/.draft-preview/<slide-basename>.png` **together in the same visual inspection**, in
472
+ batches of at most 3 pairs. Diff each pair organism by organism; a draft-only inspection does not
473
+ satisfy this check. Fix anything visibly wrong **in the JSX** (re-express, never delete), render
474
+ again, and reopen the affected anchored concept/draft pairs until each slide is sound. One round
475
+ usually suffices. It runs
476
+ locally against the materialized bundle, so it needs no awake pane and no open host once the
477
+ bundle has been downloaded once. Two faithful limits to know: a slide on a **purpose-built (Mode
478
+ A) layout** (cover/divider/agenda) and a **reused native `.pptx`** slide have no freeform region
479
+ to capture, so they come back as a status line, not an image — judge those at the full-deck
480
+ `review`/`build` step below. Do this self-check before the human review gate; don't push a draft
481
+ you haven't looked at.
482
+ 6. **The review gate — the moment the deck builds clean.** This is the **review gate** in
483
+ `workflow/build-and-export.md`; the CLI's gate command is `slidemind review <deck>`. Run it the
484
+ instant the authored set builds clean — before any deliver, without asking whether the user wants
485
+ a review. It builds the HTML preview first (the same lint the render enforces, so a gate that
486
+ *opens* is a clean build; while lint errors remain it exits non-zero — fix and re-run), then
487
+ opens the browser with a spatial comment overlay and **blocks until the user decides** (host-free
488
+ — no open PowerPoint needed for the gate). Announce it per *Two UX rules*: tell the user the
489
+ browser opened and give the clickable gate URL. **Blocking is the point: give the shell call a
490
+ generous timeout (the gate waits up to 30 min) and let it sit — a long quiet wait means the user
491
+ is reviewing, not that something hung. Never cancel it; if your harness kills the call early,
492
+ just re-run it (the user's open tab reconnects).** The verdict prints to stdout:
493
+ - `DECISION: approved` → proceed to the deliver the user chose at intake: `slidemind push <deck>`
494
+ (into PowerPoint) or `slidemind build-pptx <deck>` (to a file).
495
+ - `DECISION: comments` → the user left spatially-anchored notes, each citing a
496
+ `slides/<file>.jsx:line:col`, the source line, the click position, and a **round tag** (`[2,
497
+ round 1] …`) — plus a readonly-layout note where the click landed on a layout slot. A pin
498
+ carrying an *older* round was dropped on the previous render, so read the file before you edit
499
+ it rather than trusting the cited line blindly. **Edit the cited files in place at the given
500
+ line and re-run `slidemind review <deck>` in the same turn.** **Expect several consecutive
501
+ `DECISION: comments` rounds — that is the normal shape of a review, not an error.** The user's
502
+ tab stays live while you fix: they keep browsing the preview and keep dropping pins, and those
503
+ queued pins are delivered by your next run, which then returns *immediately* with them (a drain
504
+ round) instead of waiting. So never end your turn on a comments round, never claim to be
505
+ "polling", and never send the user back to the browser between rounds — nothing reaches them
506
+ until the gate runs again. Addressed pins come back as read-only ghost pins so the user can
507
+ confirm each note was worked in. Only `DECISION: approved` or `DECISION: pending` ends the
508
+ loop. Nothing is exported on a comments round. **Every round is persisted verbatim in
509
+ `<deck>/review.json`** — if your shell capture of the gate's output was truncated or lost
510
+ (backgrounded command, clipped scrollback), read that file for the complete comment list; never
511
+ guess at missing comments or ask the user to re-type them.
512
+ - `DECISION: pending` → the gate timed out with no answer (the user stepped away). Not approval,
513
+ not rejection — **do not deliver**. Wrap up your turn: tell the user the preview is still
514
+ waiting for their review and that on their go-ahead you'll re-run `slidemind review <deck>`
515
+ (their tab reconnects).
516
+
517
+ `slidemind review` is the only step that pauses; `push`/`build-pptx` never pause — they render
518
+ pane-free (local capture + server-side assembly, with live per-slide progress). **CLI exports
519
+ happen out of the user's sight, so the flow invariant — *the user sees every change before the
520
+ task is done* — makes re-review mandatory here: every deliver is preceded by an approved review
521
+ of exactly what it delivers. After any later slide change, the deck re-enters `slidemind review`
522
+ before it can be pushed or exported again — never deliver an unreviewed change.** If several
523
+ presentations are open, confirm **which one to push into** first (it can differ from the design
524
+ source `slidemind ready` locked in). After approval, run the push without asking; it works while
525
+ PowerPoint is backgrounded, and the CLI itself waits + retries if the pane is needed (see *First
526
+ step* above). If a push still times out, the rendered `.pptx` is saved next to the deck — re-run
527
+ `push` after the user clicks the pane, without rebuilding.
528
+
529
+ **On a clean approve + deliver, the task is done.** Report what landed (slide count + target) and
530
+ stop — no sanity pass, no re-verification. The visual QA already happened at the draft check
531
+ (step 4) and the user signed off at the review gate.
165
532
 
166
533
  ## Manual PowerPoint edits (the reconcile loop)
167
534
 
168
- The user may have edited the deck directly in PowerPoint since your last deliver — moved a shape, retyped a line, recoloured a box. Those edits live only in that file or that window; the deck's jsx knows nothing about them. `slidemind build-pptx` and `slidemind push --replace` therefore check before they write, and play the changes back to you:
535
+ The user may have edited the deck directly in PowerPoint since your last deliver — moved a shape,
536
+ retyped a line, recoloured a box. Those edits live only in that file or that window; the deck's jsx
537
+ knows nothing about them. `slidemind build-pptx` and `slidemind push --replace` therefore check
538
+ before they write, and play the changes back to you:
169
539
 
170
- - **Plain text edits are already folded into your jsx** — the command prints `note: folded N manual text edit(s) … into slides/<file>` and the fold is now the source of truth on disk. **Re-read that file before your next edit** or you will write over it from a stale copy.
171
- - **Everything else refuses the deliver** with a `RECONCILE REQUIRED` report per slide (geometry in EMU, style changes, added/removed shapes) and exit code non-zero. Nothing was written or pushed.
540
+ - **Plain text edits are already folded into your jsx** — the command prints `note: folded N manual
541
+ text edit(s) … into slides/<file>` and the fold is now the source of truth on disk. **Re-read that
542
+ file before your next edit** or you will write over it from a stale copy.
543
+ - **Everything else refuses the deliver** with a `RECONCILE REQUIRED` report per slide (geometry in
544
+ EMU, style changes, added/removed shapes) and exit code non-zero. Nothing was written or pushed.
172
545
 
173
546
  On a `RECONCILE REQUIRED`:
174
547
 
175
- 1. **Fold every listed change into the slide's jsx** — the user made them on purpose; losing them is unacceptable. Translate geometry deltas into **layout intent** (spacing, order, relative sizing, which region a thing sits in) — never literal offsets, absolute coordinates, or transform/translate hacks bolted onto a working composition. Recreate added shapes as jsx elements; drop deleted ones. If a folded position genuinely cannot fit the content area, re-express its intent (reflow, reorder, resize) rather than discarding it.
176
- 2. Lines marked `[preserved]` are foreign add-in content (e.g. an embedded chart) — do **not** recreate them; they are spliced back into the rendered slide automatically.
177
- 3. **Re-run the same command.** The re-run proceeds: the report was acknowledged (`decks/<slug>/.reconcile-ack.json`), and only a NEW manual edit re-arms the check. The deck changed, so the flow invariant applies — re-run `slidemind review <deck>` before delivering again.
178
- 4. **Do this silently.** Reconcile is bookkeeping, not a deliverable: never explain the report, the baseline, or your folding steps, and never ask the user about it. Fold and carry on with their actual request in the same turn. At most one short clause in your final answer ("kept your manual layout change").
179
-
180
- `--force` on either command **discards the user's manual edits** and writes anyway. It is a last resort, only on the user's explicit say-so — never reach for it to get past a report you could fold.
548
+ 1. **Fold every listed change into the slide's jsx** — the user made them on purpose; losing them is
549
+ unacceptable. Translate geometry deltas into **layout intent** (spacing, order, relative sizing,
550
+ which region a thing sits in) — never literal offsets, absolute coordinates, or
551
+ transform/translate hacks bolted onto a working composition. Recreate added shapes as jsx
552
+ elements; drop deleted ones. If a folded position genuinely cannot fit the content area,
553
+ re-express its intent (reflow, reorder, resize) rather than discarding it.
554
+ 2. Lines marked `[preserved]` are foreign add-in content (e.g. an embedded chart) — do **not**
555
+ recreate them; they are spliced back into the rendered slide automatically.
556
+ 3. **Re-run the same command.** The re-run proceeds: the report was acknowledged
557
+ (`decks/<slug>/.reconcile-ack.json`), and only a NEW manual edit re-arms the check. The deck
558
+ changed, so the flow invariant applies — re-run `slidemind review <deck>` before delivering
559
+ again.
560
+ 4. **Do this silently.** Reconcile is bookkeeping, not a deliverable: never explain the report, the
561
+ baseline, or your folding steps, and never ask the user about it. Fold and carry on with their
562
+ actual request in the same turn. At most one short clause in your final answer ("kept your manual
563
+ layout change").
564
+
565
+ `--force` on either command **discards the user's manual edits** and writes anyway. It is a last
566
+ resort, only on the user's explicit say-so — never reach for it to get past a report you could fold.
181
567
 
182
568
  Three refusals are not foldable and need the user, not a retry:
183
569
 
184
- - `the live deck contains N slide(s) not in this deck` — a whole-deck replace would delete slides someone added by hand or with another tool. Push **without** `--replace` to insert instead, or ask the user what should happen to them.
185
- - `<file> was edited in PowerPoint …` naming slides with no jsx behind them (a reused native slide, or one pushed by another deck) — build to a different target with `--out <other file>` so their edits survive, or ask.
186
- - `backend does not support manual-edit reconcile` / `the live deck could not be inspected` — the add-in is older than this contract, or its PowerPoint build cannot export slides. Tell the user to update the slidemind add-in. Do not paper over it with `--force`.
570
+ - `the live deck contains N slide(s) not in this deck` — a whole-deck replace would delete slides
571
+ someone added by hand or with another tool. Push **without** `--replace` to insert instead, or ask
572
+ the user what should happen to them.
573
+ - `<file> was edited in PowerPoint …` naming slides with no jsx behind them (a reused native slide,
574
+ or one pushed by another deck) — build to a different target with `--out <other file>` so their
575
+ edits survive, or ask.
576
+ - `backend does not support manual-edit reconcile` / `the live deck could not be inspected` — the
577
+ add-in is older than this contract, or its PowerPoint build cannot export slides. Tell the user to
578
+ update the slidemind add-in. Do not paper over it with `--force`.
187
579
 
188
580
  ## Placing engine imagery (create-authoring specifics)
189
581
 
190
- Finding and fetching engine imagery is `slidemind-search`'s job (`slidemind search` → `slidemind download-result`). What follows is **create-only**: how to size and colour a fetched asset so it reads on-brand in your JSX, honoured in both the `slidemind build` preview and the render.
191
-
192
- Engine results land as files referenced with `<img src="./assets/<file>">`. These facts are specific to the local harness (the bundle's `workflow/*.md` stay harness-neutral):
193
-
194
- - **Size the box from the asset's own dimensions.** Both `slidemind search` (from the engine index, before any fetch) and `slidemind download-result` (measured from the delivered bytes) print each image's intrinsic `width×height`. **Use it.** A plain `<img>` is stretched to whatever box you write, so a box whose aspect ratio disagrees with the asset ships a distorted picture. Derive one side from your layout and compute the other from the printed ratio. `object-fit: contain`/`cover` is honoured in the render as well as the preview, but it is a *fallback*, not a substitute: it converts a wrong ratio into letterboxing or a crop, both of which you chose blind. When the size prints as `(size unknown)`, the format carried no readable header — give the asset a square-ish box and check it at the draft check.
195
- - **Never open the SVG to find out whether it recolours.** `slidemind download-result` prints a `recolor` descriptor for each asset — `{ recolorable, mode, applied, note }`, where `mode` is `currentColor`, `mono`, `multicolor`, or `raster`. Read it; that *is* the answer.
582
+ Finding and fetching engine imagery is `slidemind-search`'s job (`slidemind search` → `slidemind
583
+ download-result`). What follows is **create-only**: how to size and colour a fetched asset so it
584
+ reads on-brand in your JSX, honoured in both the `slidemind build` preview and the render.
585
+
586
+ Engine results land as files referenced with `<img src="./assets/<file>">`. These facts are specific
587
+ to the local harness (the bundle's `workflow/*.md` stay harness-neutral):
588
+
589
+ - **Size the box from the asset's own dimensions.** Both `slidemind search` (from the engine index,
590
+ before any fetch) and `slidemind download-result` (measured from the delivered bytes) print each
591
+ image's intrinsic `width×height`. **Use it.** A plain `<img>` is stretched to whatever box you
592
+ write, so a box whose aspect ratio disagrees with the asset ships a distorted picture. Derive one
593
+ side from your layout and compute the other from the printed ratio. `object-fit: contain`/`cover`
594
+ is honoured in the render as well as the preview, but it is a *fallback*, not a substitute: it
595
+ converts a wrong ratio into letterboxing or a crop, both of which you chose blind. When the size
596
+ prints as `(size unknown)`, the format carried no readable header — give the asset a square-ish
597
+ box and check it at the draft check.
598
+ - **Never open the SVG to find out whether it recolours.** `slidemind download-result` prints a
599
+ `recolor` descriptor for each asset — `{ recolorable, mode, applied, note }`, where `mode` is
600
+ `currentColor`, `mono`, `multicolor`, or `raster`. Read it; that *is* the answer.
196
601
  - **Two ways to colour an icon, both honoured in the `slidemind build` preview *and* the render:**
197
- - **Bake it in (prefer this):** pass `--icon-color <hex>` — a hex you resolve from a token in the bundle's `corporate-design.css` — to `slidemind download-result`. Works for any `recolorable` glyph and the colour is in the saved file, so preview and render always agree. The tint is applied by the add-in, so on the window-free path it can be **refused**: the asset is then saved in its original colours and the `recolor` note says so. Believe the note over your request — to actually bake the colour in, re-run the search and the download with `--to <presId>` against an open PowerPoint window.
198
- - **Tint via CSS:** set `color` to a token on the `<img>`. This works only for `mode: "currentColor"` icons (the preview inlines the SVG so `color` resolves, and the render substitutes `currentColor`). A `mono` icon will not respond to CSS colour — bake it instead.
199
- - **Batch the fetch.** Pull every icon a slide needs in one call — `slidemind download-result --search <id> --pick <n,n,…> --deck <deck> [--icon-color <hex,hex,…>]` (one colour applies to all picks, or give one per pick; add `--to <presId>` only on an add-in that still relays the fetch through a pane, which the `search` output names) — the downloads run concurrently. Don't run `download-result` once per icon.
602
+ - **Bake it in (prefer this):** pass `--icon-color <hex>` — a hex you resolve from a token in the
603
+ bundle's `corporate-design.css` — to `slidemind download-result`. Works for any `recolorable`
604
+ glyph and the colour is in the saved file, so preview and render always agree. The tint is
605
+ applied by the add-in, so on the window-free path it can be **refused**: the asset is then saved
606
+ in its original colours and the `recolor` note says so. Believe the note over your request — to
607
+ actually bake the colour in, re-run the search and the download with `--to <presId>` against an
608
+ open PowerPoint window.
609
+ - **Tint via CSS:** set `color` to a token on the `<img>`. This works only for `mode:
610
+ "currentColor"` icons (the preview inlines the SVG so `color` resolves, and the render
611
+ substitutes `currentColor`). A `mono` icon will not respond to CSS colour — bake it instead.
612
+ - **Batch the fetch.** Pull every icon a slide needs in one call — `slidemind download-result
613
+ --search <id> --pick <n,n,…> --deck <deck> [--icon-color <hex,hex,…>]` (one colour applies to all
614
+ picks, or give one per pick; add `--to <presId>` only on an add-in that still relays the fetch
615
+ through a pane, which the `search` output names) — the downloads run concurrently. Don't run
616
+ `download-result` once per icon.
200
617
 
201
618
  ## Command mapping (the workflow vocabulary ⇄ your CLI)
202
619
 
203
- The bundle's `workflow/*.md` speak the harness-neutral verbs defined in `workflow/flow.md` → Vocabulary. You are the local harness — this is your mapping:
620
+ The bundle's `workflow/*.md` speak the harness-neutral verbs defined in `workflow/flow.md` →
621
+ Vocabulary. You are the local harness — this is your mapping:
204
622
 
205
623
  | Verb (`workflow/flow.md`) | Your equivalent (local harness) |
206
624
  |---|---|
207
625
  | (session setup — a CLI-only stage before the spine) | `slidemind ready [deck]` — the one-shot session start: connection ladder + design resolution (open window, the deck's own binding, or `--brand <presFP>`) + bundle materialize + deck bind. **Run it first, every session** (see *First step* above). `slidemind download-bundle` is the lower-level materialize-only command `ready` now covers — you rarely call it directly |
208
626
  | **track the stages** | your harness's task tracker — explicitly create the stage todo list before your first command and keep it current (see *Create a todo list* above) |
209
627
  | **clarify** / **plan sign-off** | intake stays in chat with the user (above); for decks, persist `decks/<slug>/plan.md` as `## Red thread` + `## Per-slide storyboards` (one `storyboard-canvas` JSON block per slide), then run `slidemind plan <deck>` — the same blocking `DECISION:` gate contract as `slidemind review`, on the plan (renders editable storyboards — red-thread strip + spatial canvases — plus slide-anchored comments; expect several comment rounds, some returning instantly with comments queued while you revised; loop until approved) |
210
- | concept mockups (mandatory deck stage, after plan sign-off) | `slidemind concepts <deck>` — renders one image mockup per approved-plan slide into `decks/<slug>/concepts/concept-slide-NN.png`; the local-harness stand-in for the in-app manager's *unconditional* concept pass on every full-deck delegation (`approvedPlan && !targetSlideScope`). **Read each and rebuild it** toward its target under your constraints (rule owned by `workflow/create.md` → Preconditions — don't restate it). It never hard-fails: on an empty result (exit 0 — the add-in can't generate them) author from the storyboards alone, but for a deck you always run it. Skip only where the add-in's `!targetSlideScope` gate would — a single slide, a tiny in-chat set, or a targeted rework of existing slides |
628
+ | concept mockups (mandatory deck stage, after plan sign-off) | `slidemind concepts <deck>` — renders one image mockup per approved-plan slide into the durable repo-relative path `decks/<slug>/concepts/concept-slide-NN.png`; immediately Read all of them for image/content search, then after searches reopen them in batches of at most 3 immediately before writing the matching `NN-*.jsx`. Re-list and reopen those paths after any interruption or continuation; never rely on an earlier chat image. The rebuild rule is owned by `workflow/create.md` → Preconditions. It never hard-fails: on an empty result (exit 0 — the add-in can't generate them) author from the storyboards alone, but for a deck you always run it. Skip only where the add-in's `!targetSlideScope` gate would — a single slide, a tiny in-chat set, or a targeted rework of existing slides |
211
629
  | **read** / **list** the workspace | read the files from the materialized bundle dir (`<bundle>/…`) and your `decks/<slug>/` |
212
- | **create / update a slide file** | write `decks/<slug>/slides/NN-<topic>.jsx` on disk with a **zero-padded numeric prefix** (`01-cover.jsx`, `02-agenda.jsx`, …) — over the CLI, "the ordered set you hand to build & push" is expressed as **numeric-aware filename order**; there is no ordered-list channel, so reordering = renaming files |
630
+ | **create / update a slide file** | write `decks/<slug>/slides/NN-<topic>.jsx` on disk with a **zero-padded numeric prefix** (`01-cover.jsx`, `02-agenda.jsx`, …) — over the CLI, "the ordered set you hand to build & push" is expressed as **numeric-aware filename order**; there is no ordered-list channel, so reordering = renaming files. For a concept-seeded create or structural rewrite, the immediately preceding tool action must Read the matching anchored concept path(s) at full detail |
213
631
  | brand reference samples (studied before composing) | **Read** 3 random images from the materialized bundle's `samples/` dir — the Read tool renders them (see *The create flow* step 1) |
214
- | **draft check** | `slidemind view-draft <deck>` — renders each `slides/*.jsx` to a per-slide PNG and returns the images to **you**; **no push, no `.pptx`, no review gate**. Look, fix the JSX, view again. Host-free once the bundle is materialized. Freeform slides only — purpose-built (Mode A) and reused native `.pptx` slides report as a status line (judge those at `review`/`build`). This is your self-check; `slidemind review` is the *human* gate |
632
+ | **draft check** | `slidemind view-draft <deck>` — renders each `slides/*.jsx` to `decks/<slug>/.draft-preview/<slide-basename>.png` and returns the images to **you**; **no push, no `.pptx`, no review gate**. Read each concept/draft path pair together, fix the JSX, render, and pair again. A draft-only inspection does not count. Host-free once the bundle is materialized. Freeform slides only — purpose-built (Mode A) and reused native `.pptx` slides report as a status line (judge those at `review`/`build`). This is your self-check; `slidemind review` is the *human* gate |
215
633
  | **build & push** | `slidemind push <deck>` (into the open PowerPoint — the insert needs a window) · `slidemind build-pptx <deck> [--out <file>]` (to a `.pptx` file — **host-free** for a bound deck: no PowerPoint window at all) — deliver whichever target was locked at intake; both render server-side via the bridge and for a bound deck the render itself is pane-free |
216
634
  | **the review gate** | `slidemind review <deck>` — builds the HTML preview (lint-clean or it doesn't open), serves it with a spatial comment overlay and **blocks until the user decides**, then prints `DECISION: approved` or `DECISION: comments` (each comment cited to `slides/<file>.jsx:line:col` + the round it was dropped on). On approve → deliver (`slidemind push` / `build-pptx`); on comments → edit the cited files and re-run `slidemind review` in the same turn (addressed pins show as ghost pins); expect several comment rounds, some returning instantly with pins the user queued while you were fixing. CLI delivers happen out of the user's sight, so **every deliver follows an approved review of what it delivers — re-run `review` after any slide change**. `push`/`build-pptx` never pause; only `review` does. |
217
635
  | **inspect the deck** | `slidemind read [presId\|file.pptx] [--mode summary\|raw]` — one verb, two sources: pull the current content of an open presentation (a presId; **host-required** — it reads the live, possibly unsaved file) **or** read a local `.pptx` on disk (a file path; host-free — no PowerPoint, no pairing). Prefer the file path when the deck is saved. `summary` = per-slide outline; `raw` = each slide's plain text — to inspect what's there before editing. Plain text, not authoring markup |
@@ -219,15 +637,26 @@ The bundle's `workflow/*.md` speak the harness-neutral verbs defined in `workflo
219
637
  | **engine search / fetch** | **`slidemind-search`** owns the engine search + reuse mechanics (`slidemind search` → `slidemind download-result`, plus whole-slide reuse via `add-slide`). For placing a fetched asset in your JSX, size its box from the printed `width×height`, pass `--icon-color` / batch with `--pick <n,n,…>` and colour per *Placing engine imagery* below |
220
638
  | **see** a live slide | no CLI analog — once delivered, the user is looking at the result; your visual authority runs at the draft check and the review gate instead |
221
639
 
222
- Everything else in `workflow/*.md` — the composition craft, the substrate/authoring rules, the layout discovery and slot/working-area rules, the visual quality bar (applied at the draft check) — applies to you unchanged.
640
+ Everything else in `workflow/*.md` — the composition craft, the substrate/authoring rules, the
641
+ layout discovery and slot/working-area rules, the visual quality bar (applied at the draft check) —
642
+ applies to you unchanged.
223
643
 
224
644
  ## The brand and the workflow both live in the bundle, not here
225
645
 
226
- Do not restate brand values (colour, type scale, spacing, layout names, accent discipline) or re-derive the authoring/composition craft in this file or in slide files — they are in the bundle's `design.md` and `workflow/*.md`. Read them and follow them. Don't load external UI/design-taste plugins during slide work; compose against the bundle's layouts and tokens. If the corporate design moves mid-task (`bundle_etag_stale`), the CLI self-heals: it refreshes the bundle and retries the render on its own — afterwards re-read the bundle's `design.md` before authoring anything new, since the rules may have changed.
646
+ Do not restate brand values (colour, type scale, spacing, layout names, accent discipline) or
647
+ re-derive the authoring/composition craft in this file or in slide files — they are in the bundle's
648
+ `design.md` and `workflow/*.md`. Read them and follow them. Don't load external UI/design-taste
649
+ plugins during slide work; compose against the bundle's layouts and tokens. If the corporate design
650
+ moves mid-task (`bundle_etag_stale`), the CLI self-heals: it refreshes the bundle and retries the
651
+ render on its own — afterwards re-read the bundle's `design.md` before authoring anything new, since
652
+ the rules may have changed.
227
653
 
228
654
  ## Failure modes the render lint can't catch
229
655
 
230
- Render-time lint rejects contract violations (unsupported CSS, bad imports, remote `<img>` URLs — the add-in's capability-table engine; `slidemind build` runs the same engine locally from the bundle). It can't catch content-shaped failures; check for these **at the draft check** (step 4, before the review gate) — not after delivery:
656
+ Render-time lint rejects contract violations (unsupported CSS, bad imports, remote `<img>` URLs —
657
+ the add-in's capability-table engine; `slidemind build` runs the same engine locally from the
658
+ bundle). It can't catch content-shaped failures; check for these **at the draft check** (step 4,
659
+ before the review gate) — not after delivery:
231
660
 
232
661
  - Layout-default / placeholder text leaking through an unfilled prop.
233
662
  - A bullet list standing in for a structured idea (process, comparison, timeline).
@@ -239,4 +668,7 @@ Render-time lint rejects contract violations (unsupported CSS, bad imports, remo
239
668
 
240
669
  ## Where the deck lives
241
670
 
242
- `decks/<slug>/` is created in the **current working directory** (the shell's pwd when the skill was invoked). Every `slidemind` command and `mkdir` resolves `decks/` against that cwd — don't `cd` into the skill's install directory. The materialized bundle lives in the cache dir above, not under the deck.
671
+ `decks/<slug>/` is created in the **current working directory** (the shell's pwd when the skill was
672
+ invoked). Every `slidemind` command and `mkdir` resolves `decks/` against that cwd — don't `cd` into
673
+ the skill's install directory. The materialized bundle lives in the cache dir above, not under the
674
+ deck.