$ cat workspace-template.yaml

G

Generazione Power Point Polizia Postale

// Generazione Power Point con tema Polizia Postale

Document Generation

// Canvas Preview

canvas.flow

// Instruction

instruction.md

Presentazione Polizia Postale dalle Cartelle Drive

Template ID

aec897f2-d1fa-438a-8935-8f10bda7f52d

Overview

Builds a self-contained Reveal.js deck from Drive documents using the Polizia Postale theme (navy/steel-blue header, Italian tricolor footer). For each (topic, slide) pair the agent clones a prototype, wires content from "Contenuto Presentazione", connects slide_html to "Make Array". Output assembled by Presentation Composer; published via "Public Share" (link) or downloaded via "PPTX Converter" (file, never auto-run). Publish decision collected at the Phase-1 gate.

Terminology: a topic has one or more slides (default 1). total_slides = sum of M_x. Final deck = #Intro + total_slides + #outro.

Page budget: page = slide. An explicit deck size P in the request is the TOTAL incl. #Intro + #outro → total_slides = P − 2. P is NOT a topic count (they differ for P > 3). Resolved at 3a-i, binding through step 6.

Two prototype chains ship on canvas — keep both (simple text chain + paired image chain; exact labels in Node Reference).

Workflow Chain

[Drive Folder Reader →] Drive Document Reader  → Selezione dei Topic Principali → Contenuto Presentazione  → #Intro (polpostale-intro)  → For each (Topic #X, Slide #Y): Slide Generator (polpostale-content / image-left / image-right)      [if image layout]: Image Creation → image input of Slide Generator  → #outro (polpostale-outro)  → Make Array (item_0 = #Intro, item_1..N topic slides, last = #outro)    → Presentation Composer (theme: polizia-postale)      → PPTX Converter (NEVER auto-run)      → Public Share (auto-run iff publishChoice === "publish")

Data Source Selection

  • Folder: "Seleziona Materiale Sorgente" (Folder Reader) + "Leggi i Documenti disponibili" (Document Reader).
  • Single file: deleteNode "Seleziona Materiale Sorgente"; configure only "Leggi i Documenti disponibili".

Node Reference

  • "Seleziona Materiale Sorgente" (Folder Reader) — input driveItemId (NOT folderId); output driveItemIds.
  • "Leggi i Documenti disponibili" (Document Reader) — input driveItemId; output content.
  • "Selezione dei Topic Principali" — input [content]; default prompt: "Seleziona i topic principali dei documenti e riassumi i punti chiave (topics), per ciascun topic riassumi i temi (slides) rilevanti"; output summary (extract topic + slide names verbatim).
  • "Contenuto Presentazione" — input [content]; agent updates prompt per Phase 2 step 7; output summary (feeds #Intro, #outro, all topic Slide Generators).
  • "#Intro"polpostale-intro; pre-connected to Make Array item_0.
  • "#outro"polpostale-outro; NOT pre-connected — agent wires to last pin.
  • Prototypes — DO NOT delete. Simple → "Topic #x - Slide #1" (polpostale-content). Paired → "Topic #x - Slide #1 - [With Image Input]" (polpostale-image-left) + "Image Creation - for Topic #x - Slide #1 - [With Image Input]"; internal image_creation.image → slide_gen.image preserved by cloneNodes when both IDs passed together.
  • Make Arrayitem_0 = #Intro; item_1..item_total_slides = topic slides (clone_index order); item_<total_slides+1> = #outro. Ships 10 pins (item_0…item_9) and does NOT auto-growupsertEdge/batchUpsertEdges add the edge but never the pin, so edges to item_10+ land on handles the node doesn't draw. When total_slides + 2 > 10, sync the schema at Step D2.
  • Presentation Composertheme: "polizia-postale"; pre-wired from Make Array.
  • PPTX Convertertheme: "polizia-postale"; pre-wired from Composer; never auto-run.
  • Public Share — input content from Composer's presentation; template ID a3f1d8c2-6e4b-5a9d-b7c3-2f8e1a4d6b9c (recreate if missing in Phase 4).

Polizia Postale Layout Selection

5 layouts; 3 pickable for topic slides.

Reserved (do NOT use for topic slides): polpostale-intro (#Intro), polpostale-outro (#outro). They embed the official stemma — NEVER wire a generated image to #Intro/#outro or swap their assets. No Image Creation node attaches to them.

Topic-slide layouts:

  • polpostale-content — text workhorse: header bar + <h2> + free body (<p>, <ul>, <table>). (no image input)
  • polpostale-image-left — image left, text right. (image input)
  • polpostale-image-right — text left, image right. (image input)

Content density constraint (image-left/image-right ONLY): the text column is narrow. To prevent overflow: MAX 5 items per list, and AT MOST ONE main list per slide. No nested lists, no multiple consecutive lists. If the source is denser, condense to prose or split across additional slides.

IMAGE_LAYOUTS = ["polpostale-image-left", "polpostale-image-right"]

Picking rule (by clone_index, 0-based):

  • useImagesChoice === "no" (or default) → every topic slide = polpostale-content.
  • useImagesChoice === "yes"strictly alternate content and image layouts, content-first: even index (0,2,4…) → polpostale-content; odd index (1,3,5…) → an image layout, the image layouts alternating polpostale-image-left then polpostale-image-right. Example (6 slides): content, image-left, content, image-right, content, image-left → 3 content / 3 image. HARD RULE: never make all topic slides image layouts (even indices keep ≥50% content); never silently fall back to all-content when images were requested — both families MUST appear (single topic slide → make it one image layout so images aren't absent).

Never use marina-* / emblema-*. Theme "polizia-postale" on BOTH Composer and PPTX Converter.

Important: Node IDs and Field Names

  • After applyCanvasData, always use nodeFinder by label.
  • Use atomic / batch tools for all modifications (no further applyCanvasData).
  • Drive Reader input field is driveItemId (NOT folderId).

Execution Strategy

Complete all 4 phases in order. NEVER auto-run PPTX Converter. Public Share runs in Phase 4 iff publishChoice === "publish". After nodeExecutor the system notifies — do NOT poll. On error retry 3× then ask user. On "continua", resume via nodeFinder. NEVER execute prototypes directly.

Phase 1: Setup

  1. applyCanvasData — IMMEDIATELY, before any other tool call.
  2. searchDriveItems + selectDriveItem — try shorter queries on no results (max 2 attempts) then ask user.
  3. Data source:
    • Folder: nodeFinder "Seleziona Materiale Sorgente" → upsertNode({ nodeId, input: { driveItemId } }).
    • Single file: deleteNode "Seleziona Materiale Sorgente"; upsertNode "Leggi i Documenti disponibili" with driveItemId.

3a. Phase 1 gate — two sub-steps: 3a-i (extract what the request already answers), then 3a-ii (ask only the rest). 3a-i is MANDATORY and runs first — it decides WHICH questions get asked at all.

3a-i — Known-answer extraction. Re-read the user's original message. Init known.topicCount = known.slidesPerTopic = known.useImages = known.imageStyle = known.publish = null, then fill:

key(s)explicit triggers (it / en / zh)value
topicCount + slidesPerTopic"PPT di 5 pagine", "da 8 slide", "a 5-page deck", "5 页的 PPT"pageBudget = P → decomposition below → both keys set
topicCount"5 topic principali", "5 main topics", "5 个主题"5
slidesPerTopic"3 slide per topic", "3 slides each", "每个主题3页"3
useImages"senza immagini", "solo testo", "no images", "不要图片""no"imageStyle moot, do NOT ask it
useImages"con immagini", "with images", "加上图片""yes" (style still unknown)
imageStyle"immagini fotografiche", "photographic", "真实照片"useImages="yes" + "photo"
imageStyle"diagrammi", "schemi", "icone", "diagrams", "示意图"useImages="yes" + "diagram"
publish"pubblicala", "condividila", "publish it", "发布""publish"
publish"lasciala in bozza", "keep it a draft", "先不要发布""draft"

EXPLICIT-ONLY RULE. Known = stated explicitly and unambiguously. If you must reason or estimate, it is NOT known — leave null and ASK.

  • Vague size ("circa 5 pagine", "5-7 slide", "breve") and vague imagery ("qualche immagine", "maybe some photos") → NOT known.
  • Never infer from tone, topic, audience or document type. Technical doc ≠ diagram; internal doc ≠ draft; long source ≠ big deck.
  • Only sanctioned cross-key inference: an explicit style ⇒ useImages = "yes"; useImages = "no"imageStyle moot.
  • In doubt, fail open to ASKING. An extra question costs one line; a wrong extraction silently builds the wrong deck.

Page-budget decomposition (only on an explicit integer P): content_slides = P − 2; if < 11 with budgetTopics = budgetSlidesPerTopic = 1 (state the 3-page minimum); else budgetTopics = content_slides, budgetSlidesPerTopic = 1; if content_slides > 20, take the smallest S ∈ 2..10 with content_slides % S == 0 and content_slides / S ≤ 20budgetSlidesPerTopic = S, budgetTopics = content_slides / S. Set known.topicCount / known.slidesPerTopic from these — so neither question is asked. No explicit PpageBudget = null. Conflict: P plus an explicit per-key number → the per-key number WINS, derive the other from content_slides (max(1, round(...))), recompute pageBudget, and say so in the print line.

Print what was extracted and what remains, e.g. Già nella richiesta: 5 pagine (→ 3 topic × 1 slide), niente immagini. Chiedo solo: pubblicazione. No print line = 3a-i not run.

Unit discipline: P is a PAGE total, budgetTopics a TOPIC count. Answering "5 topic" to a "5 pagine" request yields 1+5+1 = 7 pages — a defect, not rounding.

3a-ii — ONE requestUserDecision with ONLY the keys still null (italiano, canonical order below); additionally drop imageStyle when known.useImages === "no". Present each included question unchanged — never re-encode an extracted value into its options.

  • topicCount — "Quanti topic principali?" options 3 / 5 / 10 / auto (free-text accepted as integer ≤ 50).
  • slidesPerTopic — "Quante slide per topic?" options 1 / 2 / 3 / auto (free-text accepted as integer ≤ 10).
  • useImages — "Includere immagini generate automaticamente?" options yes / no. Context note: "#Intro e #outro NON ricevono mai immagini generate."
  • imageStyle — "Stile immagini?" options photo / diagram. Used only if useImages === "yes". Keep it whenever useImages is itself being asked (no mid-card branching) and discard the answer if the user picks no.
  • publish — "Pubblicare automaticamente (link condivisibile)?" options publish / draft. Context note: "PPTX NON viene generato automaticamente."

ZERO remaining → do NOT call requestUserDecision at all. questions is .min(1).max(6), so [] is a schema error — and pausing to ask nothing is worse than not pausing. Print the resolved set, treat the result as answers = [], go to the merge. Call the tool at most once in Phase 1, whatever the subset size.

Merge: asked → the gate answer wins; not asked → known.<key> IS the answer. Defaults when a question was asked and cancelled/missing: topicCountChoiceauto; slidesPerTopicChoiceauto; useImagesChoiceno; imageStyleChoicephoto; publishChoicedraft. known.publish === "publish" IS the user's explicit publish choice (asked for in their own words) — NEVER publish on null / draft / cancelled. pageBudget / budgetTopics / budgetSlidesPerTopic carry forward; a cancelled card retracts nothing extracted at 3a-i.

Phase 2 entry precondition: all five VALUES resolved (extracted or answered) — however many questions were actually asked, including zero. Any missing → STOP, re-run 3a-i, ask the remainder in ONE call.

Phase 2: Topic Selection & Content Generation

  1. Resolve first. topicCountChoice / slidesPerTopicChoice come from the 3a-ii merge. If pageBudget set and either is still auto → take it from budgetTopics / budgetSlidesPerTopic (safety net — with a budget neither should have been asked). If pageBudget set and either is a number differing from the decomposition → keep the number and recompute pageBudget = 1 + topicCountChoice × slidesPerTopicChoice + 1. Print Struttura risolta: <N> topic × <M> slide = <pageBudget|n/d> pagine. Then nodeFinder "Selezione dei Topic Principali". Configure:
    • Both auto AND pageBudget null → default unchanged.
    • Otherwise → upsertNode({ nodeId, input: { prompt: "Seleziona <N> topic principali dei documenti e per ciascun topic seleziona <M> temi (slides) rilevanti" } }). Replace <N>/<M> (or "i topic"/"i temi" if auto). When pageBudget set, append " La presentazione finale deve avere esattamente <pageBudget> pagine: intro + <N>×<M> slide di contenuto + outro." Append user's focus area if any.
  2. nodeExecutor "Selezione dei Topic Principali" (auto-runs Document Reader). Wait.
  3. getNodeOutput → extract topics, slides[X], N, M_x. Names verbatim. Cap N / M_x to the resolved integers if set. Budget reconciliation (when pageBudget set): total_slides MUST equal pageBudget − 2. Too high → drop trailing slides (last topic first). Too low → add slides round-robin from Topic #1, naming each from a sub-theme already present under that topic in the selector output (never invent a topic name); if the material genuinely can't support the count, STOP and ask rather than silently shrinking. Print Budget: <pageBudget> pagine = 1 intro + <total_slides> contenuto + 1 outro. total_slides is now the planned slide count that step 9b, step 16a and step 20 must all agree with. If N > 20 or total_slides > 50, ask whether to proceed/reduce.
  4. nodeFinder "Contenuto Presentazione" → upsertNode setting prompt:
Prepara il contenuto della presentazione espandendo ogni topic e slide selezionati dal nodo "Selezione dei Topic Principali" creando il testo per ciascuna sezione.
Nel prompt elenca tutti i topic e le slide da espandere includendo il loro nome esteso nel seguente formato:
#Topic 1: <topic1_name>##Slide 1: <slides[1][1]>##Slide 2: <slides[1][2]>#Topic 2: <topic2_name>##Slide 1: <slides[2][1]>...
Per ogni topic principale, sviluppa una sezione dettagliata con tutte le slide associate.

Replace placeholders with real names from step 6. 8. nodeExecutor "Contenuto Presentazione" IMMEDIATELY after step 7. Wait.

Phase 3: Slide Chain Creation

6 sequential steps (A→B→C→D→D2→E). Each batch tool called exactly once per step — patch mistakes with single-op tools. Do NOT delete prototypes.

Step A — Find prototypes, build mapping table, clone:

  1. Find by useImagesChoice: nosimplePrototypeId only. yesimageSlidePrototypeId + imageCreationPrototypeId AND simplePrototypeId (text rows still need it). Both branches also nodeFinder "Contenuto Presentazione" and "Make Array".

9b. Build IMMUTABLE mapping table. Per topic X (1..N) per slide Y (1..M_x): sequential clone_index from 0; pin = item_<clone_index+1>; pick layout per the Picking ruleno: all polpostale-content; yes: alternate content/image content-first by clone_index (even=content, odd=image; images alternate left/right), never all-image. Print the table.

9c. (Yes branch ONLY — MANDATORY before step 10.) Partition by IMAGE_LAYOUTS: image_rows / text_rows complement, sum = total_slides. Print both lists. If image_rows.length === 0, skip the image clone call.

  1. Clone:
  • No: batchCloneNodes({ nodeIds: [simplePrototypeId], count: total_slides })clones[i].
  • Yes (skip a call if its count is 0):
    • Text: batchCloneNodes({ nodeIds: [simplePrototypeId], count: text_rows.length })textClones[t].
    • Image: batchCloneNodes({ nodeIds: [imageCreationPrototypeId, imageSlidePrototypeId], count: image_rows.length })imageClones[g].nodeIdMap has BOTH IDs (internal image_creation.image → slide_gen.image edge preserved).
  • Spot-check one image pair survived; patch with upsertEdge if needed.

Step B — Rename + instruction + layout in ONE batchUpsertNodes:

  1. Cloned nodes inherit prototype label/instruction/template_name — overwrite all three per clone.

Per row: slide-gen op { nodeId: <slideGenCloneId>, label: "Topic #X - Slide #Y", input: { instruction: "<INSTRUCTION>", template_name: "<layout from mapping table>" } }.

<INSTRUCTION> base: "Inserisci il contenuto del Topic #X '<topicX_name>' - Slide #Y '<slides[X][Y]>'". For image-left / image-right rows, append: " MAX 5 voci per lista, UNA SOLA lista principale per slide; condensa in prosa o spezza in più slide se il contenuto è più denso. Niente liste annidate."

Image rows ALSO get an image-creation op: { nodeId: <imageCreationCloneId>, label: "Image - Topic #X - Slide #Y", input: { prompt: <PROMPT> } }.

<PROMPT> (per imageStyleChoice, both reference the slide's topic + slide name, palette navy/blu/bianco con accenti tricolore istituzionali):

  • photo: realistic editorial photography of people/places/atmospheres; NO icons, text, vector graphics, diagrams.
  • diagram: editorial diagram with icons, label text, vector schemes; clear composition.

Total ops: No = total_slides; Yes = text_rows.length + 2 × image_rows.length. Instructions MUST include exact names. template_name MUST be polpostale-content / -image-left / -image-right. NEVER assign an Image Creation node to #Intro or #outro.

Step C — Wire content (ONE batchUpsertEdges): for each slide-gen clone: { sourceNodeId: <Contenuto Presentazione ID>, sourcePort: "summary", targetNodeId: <slideGenCloneId>, targetPort: "content" }. Image Creation clones get NO content edge. Total: total_slides edges.

Step D — Wire to Make Array + #outro (ONE batchUpsertEdges): nodeFinder "#outro" first. Per row: { sourceNodeId: <slideGenCloneId>, sourcePort: "slide_html", targetNodeId: <Make Array ID>, targetPort: "item_<clone_index+1>" }. Last: #outro on item_<total_slides+1>. item_0 RESERVED for #Intro. The edges array must contain exactly total_slides + 1 entries (one per planned slide + #outro) — count them before moving on; step 16a re-derives the same roster from the canvas and blocks on any mismatch.

Step D2 — Sync Make Array pins (REQUIRED when total_slides + 2 > 10): Make Array ships 10 pins and batchUpsertEdges does NOT grow them, so edges to item_10+ hit handles the node never draws. Fix with ONE updateNodeSchema call replacing the WHOLE inputSchema with exactly item_0 … item_<total_slides+1>:

updateNodeSchema({ nodeId: "<Make Array ID>", inputSchema: { type: "object", properties: {  /* one entry per i = 0 … total_slides+1 */  item_<i>: { type: "string", order: <i>, title: "Item <i+1>", portType: "Any" }}, required: [] } })

It replaces (never merges) and prunes edges to any pin omitted — list EVERY pin, never fewer, never below 10. Skip entirely when total_slides + 2 ≤ 10.

Step E — "Topic Slides" section (MANDATORY for ≥3 clones). Full clone-ID set = every slide-gen clone + every image-creation clone (Yes). Four sub-steps in order: a. sectionFinder prototype's parent section → sectionManager({ action: "ungroup", sectionId }) (string ONLY). b. organizeNodes({ nodeIds: [<full set>] }). c. sectionManager({ action: "group", nodeIds: [<full set>], label: "Topic Slides" }). If section exists, ungroup it FIRST. d. autoLayout() (direction: "LR"). MANDATORY.

Phase 4: Verification & Final Execution

Order: 16 → 16a → 17 → 18 → 19 → 20. No user pauses. The delivered deck has exactly as many slides as Make Array has OCCUPIED PINS — not the clones you created, not your mapping rows, not the topics you announced. Every slide count you state (step 18 summary, step 20 checklist and wrap-up) MUST be the count measured at 16a. Announcing an unmeasured count is reporting a result that does not exist.

  1. nodeFinder Make Array + all Slide Gens + Composer + PPTX Converter + Public Share. (Make Array membership is audited at 16a — this step is the per-node pass.) Verify and patch with upsertEdge/upsertNode:
  • #Intro on item_0; #outro on last pin; neither has an image input (delete any orphan edge/node).
  • Every topic Slide Gen has a content edge from "Contenuto Presentazione".
  • template_name ∈ {polpostale-content, -image-left, -image-right} (NEVER intro/outro/marina-*/emblema-*).
  • Image-left/right instructions carry the MAX-5-items / 1-list constraint.
  • Theme "polizia-postale" on BOTH Composer AND PPTX Converter.
  • Yes mode: every image-row slide-gen has BOTH content AND image edges; every Image Creation clone has a non-empty per-slide prompt; layouts alternate content/image (content-first), ≥ every other topic slide is polpostale-content — never all-image.
  • Composer presentation → Public Share content edge present; "Topic Slides" section exists.
  • Make Array inputSchema lists ≥ total_slides + 2 item_* pins — the node must display them, not merely have edges to them. Fewer → Step D2 was skipped; fix with the same updateNodeSchema call.

16a. Make Array membership audit — BLOCKING. Observed failure: the agent announced "10 slide totali" and success while Make Array held only item_0 (#Intro) and item_11 (#outro) — every content pin empty, delivered deck 2 slides. Do NOT trust the mapping table, the batch call, or your memory of what you wired. Read the canvas. a. Expected roster from 9b: item_0 = #Intro; item_<clone_index+1> = each mapping row; item_<total_slides+1> = #outro. Expected occupied = total_slides + 2. b. Actual: nodeFinder Make Array, enumerate incoming edges as (targetPort, source label). If incoming edges aren't exposed, read from the source side — any Slide Gen with outputs: [] is NOT connected, whatever the plan says. c. Print the roster table (pin | expected | actual | OK/MISSING/DUPLICATE). Can't print it → you didn't audit; go back to (b). d. Checks: (1) every pin item_0 … item_<total_slides+1> has exactly one incoming edge; (2) no shared pins, no node on two pins, no gapsitem_0 + item_11 on an 11-slide plan is 2 slides, not 11; gaps are the signature of this failure; (3) actual_slide_count = occupied pins, and it must equal total_slides + 2. e. Pass → state Composizione verificata: <actual_slide_count>/<total_slides + 2> slide collegate. → step 17. f. Fail → repair ONCE, single-op only: upsertEdge per missing pin; deleteEdge + upsertEdge per duplicate. Never re-run batchUpsertEdges (already spent in Step D). A mapping row with no clone at all is a Step A/B failure, not a wiring one — say so explicitly. Then re-run (b)–(d) exactly once. g. Still failing → STOP. Do NOT loop, do NOT run step 19. Go to step 20 and report honestly: one error item per empty pin naming the slide that should occupy it, plus "Composizione incompleta: <actual_slide_count>/<total_slides + 2> slide collegate — la presentazione conterrà <actual_slide_count> slide." Reporting the planned count as delivered is a failure of this step, not a rounding of it. actual_slide_count is the ONLY slide number quotable from here on.

  1. nodeFinder({ query: "Public Share" }). If missing AND publishChoice === "publish" → recreate with upsertNode({ templateId: "a3f1d8c2-6e4b-5a9d-b7c3-2f8e1a4d6b9c" }) + upsertEdge from Composer. If missing AND draft → leave unset. Store <publicShareId>.

  2. One-sentence summary that the deck is built. If publishChoice === "publish" AND <publicShareId> set → step 19; otherwise skip 19, note "in bozza".

  3. nodeExecutor({ nodeId: <publicShareId> }) (only when step 18 said to). System auto-runs the upstream chain; result card shows /s/{slug}. Never auto-run PPTX Converter.

  4. verification — present checklist (info ✓ / warning / error) covering the step-16 items, the 16a composition result (info ✓ only if 16a passed; if it failed after its repair pass, one error per empty pin plus the incomplete-composition item), and execution status (✓ "Public Share eseguito; URL /s/{slug}" OR info "bozza — esegui Public Share / PPTX Converter su richiesta"). Summary: "Verifica finale presentazione Polizia Postale: <N> controlli, <E> errori, <W> warning." Include N topics, M_x slides/topic, total = actual_slide_count measured at 16a (NOT the planned total_slides + 2 — they match only when 16a passed, which is why you quote the measured one), theme = Polizia Postale, and either the share URL or the draft note. When pageBudget was set, state Richieste <pageBudget> pagine — consegnate <actual_slide_count>.; if those differ, lead with it and do NOT describe the run as successful. verification is terminal — do NOT call followUp. On errors, wait for user fix.

Incremental Edits (deck pre-existed this turn)

Do NOT re-run Phase 1 gate or Phases 2/3. Apply the delta, re-run the 16a roster audit (recomputing total_slides and actual_slide_count), then step-20 verification, then if previously published re-run nodeExecutor on Public Share; else leave draft. Past 10 pins, re-run Step D2 whenever the pin count changes. Bundled "build AND tweak" = fresh build.

  • Add a slide: upsertNode({ templateId: "69bba2bf-591b-5e7b-932b-6a705d4b9b87", label, input: { template_name, instruction } }) → content edge → insert in Make Array (shift later pins) → organizeNodes + autoLayout. Image slide: also create + wire + prompt the Image Creation node; instruction enforces MAX 5 items / 1 main list. To insert inside "Topic Slides", ungroup first then re-group.
  • Remove: deleteNode (also paired Image Creation) → close pin gap → re-layout.
  • Reorder: re-assign item_<n> pins via upsertEdge (#Intro stays on item_0, #outro last).

Common User Requests → Configuration

User saysConfigure
Request already answers a gate questionKnown-answer bypass — 3a-i extracts it, 3a-ii asks only what's left (nothing left → no card at all). Triggers per key in the 3a-i table.
"PPT di N pagine" / "N 页的 PPT" / "an N-page deck"pageBudget = Ntotal_slides = N − 2, budgetTopics = N − 2, budgetSlidesPerTopic = 1; both count questions skipped; step 6 forces total_slides. Deck = exactly N pages. N is NOT a topic count.
"senza immagini" / "不要图片"known.useImages = "no"; imageStyle dropped too — neither asked.
"pubblicala" / "发布"known.publish = "publish" — explicit choice; step 19 runs Public Share with no second prompt.
"circa N pagine" / "qualche immagine"Vague → NOT known (explicit-only rule). Leave null and ask.
"X topic, Y slide ciascuno"Topic Selector prompt → "Seleziona X topic principali e per ciascun topic seleziona Y temi (slides) rilevanti".
"Focus on [area]"Append focus to Topic Selector prompt.
"Immagine sinistra/destra"polpostale-image-left / -image-right + Image Creation node + per-slide prompt; instruction enforces MAX 5 items / 1 main list.
"Cambia logo/sfondo Intro/Outro"NOT supported — embed official stemma, must NOT be customised; tell the user.
"Pubblica / link"nodeExecutor Public Share → URL.
"Scarica / PowerPoint"nodeExecutor PPTX Converter → file (ONLY time PPTX runs).

Error Handling

  • Announced slide count ≠ delivered ("10 slide" reported, 2 in the deck) → Make Array had empty pins: Step D's batchUpsertEdges never issued, partial, or aimed at clones that were never created. 16a exists to catch this — no roster table printed means it was skipped. Never state an unmeasured count.
  • Delivered pages ≠ requested pages → budget lost: 3a-i must set pageBudget and print, step 4 must resolve it, step 6 must reconcile total_slides to pageBudget − 2. Unit error: "5 pagine" answered as "5 topic" → 7 pages.
  • Make Array shows fewer pins than edges wired into it (>10 connections, 10 pins drawn) → Step D2 skipped; updateNodeSchema with item_0 … item_<total_slides+1> (it replaces and prunes omitted pins — list every one).
  • Empty slides → missing content edge; upsertEdge Contenuto Presentazione [summary] → Slide Gen [content].
  • Image-left/right text overflows → instruction missed MAX 5 items / 1 main list rule; fix instruction, re-execute.
  • Image Creation attached to #Intro/#outrodeleteEdge and delete the orphan Image Creation node.
  • Image Creation prompt empty / image edge missingupsertNode (prompt) or upsertEdge (Image Creation [image] → Slide Gen [image]).
  • Theme drift / wrong layout family (marina-* / emblema-*)upsertNode to theme: "polizia-postale" and/or swap to polpostale layout on BOTH Composer AND PPTX Converter.
  • Public Share missing / blank share pageupsertNode({ templateId: "a3f1d8c2-…" }) + reconnect from Composer; if blank, verify edge and re-execute.
  • PPTX never produced → expected (never auto-run); execute on explicit request only.
  • Canvas broken after Phase 3 → Step E skipped; run full ungroup → organize → group → autoLayout.
  • ungroup schema errornodeIds was passed; ungroup takes ONLY sectionId (string).
  • group "already in a section" → do NOT retry; ungroup first, organize, re-group, autoLayout — or skip grouping (new node stays standalone).

Cross-references

  • apps/workspace-service/app/templates/presentation_composer.py_PP_CSS, CUSTOM_THEMES["polizia-postale"].
  • apps/workspace-service/app/templates/slide_generator.py — 5 polpostale-* entries.
  • Siblings: pptx-from-drive.md (Marina), pptx-from-drive-emblema.md (Emblema).

// Dependencies

requirements.py
1from input import DriveFolderReader2from input import DriveDocumentReader3from process import TextSummarizer4from process import SlideGenerator5from process import ImageCreation6from process import MakeArray7from output import PresentationComposer8from output import PublicShare9from output import PPTXConverter

$ git log --oneline

v1.1.0
HEAD
2026-08-05

v1.1.0 — user-input composition (known-answer bypass incl. page-budget resolution), blocking Make Array membership audit (16a) with honest reporting, and Step D2 updateNodeSchema pin-sync (fixes the false auto-grow-past-10-pins claim).

v1.0.32026-05-20
v1.0.22026-05-20
v1.0.12026-05-19
v1.0.02026-05-19