$ cat workspace-template.yaml

S

Slide Presentation for Mission Operative Briefing

// An interactive presentation with HTML slides for Marina Militare Italiana Briefings, Missions and Operations

Document Generation

// Canvas Preview

canvas.flow

// Instruction

instruction.md

Briefing Missione Navale dalle Cartelle Drive

Template ID

4f9ff1f1-9fd5-4d32-a4df-58eebe3c43ae

Overview

Composes a mission briefing deck from documents stored in a Drive Folder, using the Marina Militare theme and slide layouts. Designed for Italian Navy operational use — the output is a self-contained Reveal.js briefing presentation exported to PPTX, styled as a formal naval briefing. The workflow reads documents, then "Selezione dei Topic Principali" identifies the main topics and their subtopics. A centralized "Contenuto Presentazione" node generates ALL slide content for the entire briefing in a single pass. For each subtopic, the agent MUST clone a prototype node, rename it, wire its content input from "Contenuto Presentazione", and wire its output to "Make Array". All slides are assembled into the briefing and exported to PPTX.

Terminology: A topic is a high-level subject area. Each topic is subdivided into subtopics — each subtopic becomes exactly one slide. When a user says "I want 10 topics with 5 subtopics each," the result is 50 content slides plus intro and outro. Each topic can have a different number of subtopics.

The template includes one prototype: a standalone node for text-only slides ("Topic #x - Subtopic #x") configured with marina-content by default. In Phase 3, the agent clones this prototype for EVERY topic and subtopic, renames it, and optionally picks a more specific marina layout (marina-metrics, marina-timeline, marina-comparison) based on the subtopic content shape. Do NOT delete the prototype — it must remain on the canvas. Unless the user specifies a maximum, ALL topics and subtopics from the output are used.

Marina v1 does NOT ship image-layout variants. There is no marina-image-left or marina-image-right. If the user requests images, see "Common User Requests" below for the correct response — do NOT fall back to vanilla image-left / image-right (they render unbranded white and look broken next to the marina navy).

Workflow Chain

[Data Source — depends on user intent]  Option A (folder): "Seleziona Materiale Sorgente" (Drive Folder Reader) → "Leggi i Documenti disponibili" (Drive Document Reader)  Option B (single file): "Leggi i Documenti disponibili" (Drive Document Reader) only
  → "Selezione dei Topic Principali" (identifies N topics, each with M_x subtopics)  → "Contenuto Presentazione" (generates ALL slide content for entire presentation)
  → "#Intro" (Slide Generator, marina-intro) — intro slide, content from "Contenuto Presentazione"  → For each Topic #X - Subtopic #Y:    "Topic #X - Subtopic #Y" (Slide Generator, marina-content/metrics/timeline/comparison) — content from "Contenuto Presentazione"  → "#outro" (Slide Generator, marina-outro) — closing slide, content from "Contenuto Presentazione"
  → Make Array (pin order: #Intro → T1-Sub1 → T1-Sub2 → ... → TN-SubM → #outro)    → Presentation Composer (theme: marina, assembles into full HTML presentation)      ├→ PPTX Converter (theme: marina, exports to .pptx file)    └→ Public Share (publishes the Reveal.js HTML as a shareable public link)

Data Source Selection

The data source depends on what the user wants to process:

  • Folder (multiple documents): Use Drive Folder Reader connected to Drive Document Reader (both nodes). The Folder Reader lists items, the Document Reader reads their content.
  • Single file: Use only Drive Document Reader (no Folder Reader needed). The user selects the document directly. You must delete the "Seleziona Materiale Sorgente" node from the canvas using deleteNode, then configure the Document Reader with upsertNode.

Node Reference

Data Source Nodes

"Seleziona Materiale Sorgente" (Drive Folder Reader):

  • What it does: Lists all document items in a selected Drive folder.
  • Configurable inputs: driveItemId — the Drive folder ID (NOT folderId).
  • Output: driveItemIds → list of document IDs.

"Leggi i Documenti disponibili" (Drive Document Reader):

  • What it does: Reads the processed text content from documents.
  • Input from: Drive Folder Reader [driveItemIds] (folder mode) or user-selected document (single file mode).
  • Configurable inputs: driveItemId — the Drive item ID.
  • Output: content → full text content of the document(s). This content feeds "Selezione dei Topic Principali" AND "Contenuto Presentazione".

"Selezione dei Topic Principali" (Topic Selector)

What it does: Analyzes document content and identifies both the main topics AND the subtopics for each topic in a single pass. This is the only node that determines the presentation structure. Input from: "Leggi i Documenti disponibili" [content]. Configurable inputs:

  • llmModel: LLM model for topic extraction (default: auto-selected)
  • prompt: Instruction for topic and subtopic selection. Default: "Seleziona i topic principali dei documenti e riassumi i punti chiave (topics), per ciascun topic riassumi i temi (subtopic) rilevanti". Can be customized to focus on specific areas or to request a specific number of topics/subtopics. Output: summary → structured text containing topics and their subtopics. The agent MUST read this output to extract both the topic names AND the subtopic names per topic. All subsequent node naming and prompts MUST use the exact topic and subtopic names from this output.

"Contenuto Presentazione" (Content Generator)

What it does: Generates the complete textual content for ALL slides in the presentation in a single pass. The agent MUST update the prompt with the full list of topics and subtopics (from "Selezione dei Topic Principali" output) before executing this node. Input from: "Leggi i Documenti disponibili" [content]. Configurable inputs:

  • llmModel: LLM model (default: openai/gpt-oss-120b)
  • prompt: MUST be updated by the agent to list ALL topics and subtopics in the following format:
    Prepara il contenuto della presentazione espandendo ogni topic e subtopic selezionati dal nodo "Selezione dei Topic Principali" creando il testo per ciascuna sezione.
    Nel prompt elenca tutti i topic e subtopic da espandere includendo il loro nome esteso nel seguente formato:
    #Topic 1: <full topic name>##Subtopic 1: <full subtopic name>##Subtopic 2: <full subtopic name>
    #Topic 2: <full topic name>##Subtopic 1: <full subtopic name>...
    Per ogni topic principale, sviluppa una sezione dettagliata con tutti i relativi subtopic, fornendo descrizioni complete e coerenti per la presentazione finale.

Output: summary → complete presentation content organized by topic/subtopic. This feeds "#Intro", "#outro", and ALL topic Slide Generator nodes.

"#Intro" and "#outro" (Slide Generators)

"#Intro" (Slide Generator):

  • What it does: Generates the opening slide(s) for the presentation with the Marina Militare crest, uppercase title, and subtitle.
  • Input from: "Contenuto Presentazione" [summary][content].
  • Pre-connected: Output slide_html → Make Array item_0.
  • Default config: template_name: "marina-intro", instruction: "titolo slide di apertura".

"#outro" (Slide Generator):

  • What it does: Generates the closing slide with the Marina Militare crest, "Grazie" headline, and hashtag.
  • Input from: "Contenuto Presentazione" [summary][content].
  • NOT pre-connected to Make Array — the agent MUST wire it to the last pin after all topic slides are connected.
  • Default config: template_name: "marina-outro".

Prototype Node (for cloning)

The template includes one prototype. It is NOT real content — it serves as a template to clone from. Do NOT delete the prototype — it must remain on the canvas as a reference node for future operations.

"Topic #x - Subtopic #x" (standalone Slide Generator, text-only):

  • 1 node: Slide Generator
  • Default config: template_name: "marina-content", instruction: "Inserisci il contenuto del Topic #x - Subtopic #x"
  • Input: content ← from "Contenuto Presentazione" [summary] (external edge — NOT preserved by cloneNodes)
  • Output: slide_html — NOT pre-connected to Make Array
  • IMPORTANT: This is a standalone node, NOT inside a section. Use nodeFinder to locate it (not sectionFinder). When cloning with cloneNodes({ nodeIds: [nodeId] }), the clone keeps the original label "Topic #x - Subtopic #x" — you MUST call upsertNode to rename it, set the instruction, and (optionally) pick a specific marina layout for that subtopic.

Make Array

What it does: Collects all slide HTML outputs into a single ordered array. Pin order determines slide order in the final presentation. Pin layout: item_0 = #Intro, then all subtopic slides grouped by topic in sequential order, last pin = #outro. Example (2 topics, 3 and 2 subtopics): item_0 = #Intro, item_1 = T1-Sub1, item_2 = T1-Sub2, item_3 = T1-Sub3, item_4 = T2-Sub1, item_5 = T2-Sub2, item_6 = #outro. Default pins: The template ships with 10 input pins. When the total exceeds 10, new pins are created automatically by upsertEdge. Total pins formula: 1 (Intro) + sum of all subtopics across all topics + 1 (Outro). Output: Array of slide HTML sections (ordered by pin index).

Presentation Composer

What it does: Assembles individual slides into a complete self-contained Reveal.js HTML presentation. Pre-connected in template: Receives items array from Make Array. Configurable inputs:

  • title: Presentation title (shown in browser tab)
  • theme: default "marina" (Marina Militare — navy + gold + Italian tricolor footer). The marina theme renders at a 1920×1080 internal canvas with Google Fonts (Graduate + Montserrat).
  • transition: Slide transition effect — slide, fade, convex, concave, zoom, none Output: Complete HTML document (self-contained presentation).

PPTX Converter

What it does: Converts the Reveal.js HTML presentation into a downloadable PowerPoint .pptx file. Pre-connected in template: Receives presentation HTML from Presentation Composer. Configurable inputs:

  • title: PowerPoint metadata title
  • theme: default "marina" — must match Presentation Composer theme for correct color mapping. Note: the PPTX converter uses its own inch-based coordinate system; the marina theme's 1920×1080 Reveal canvas does NOT affect PowerPoint output dimensions (PPTX is always 13.33 × 7.5 inches LAYOUT_WIDE). Output: PowerPoint .pptx file.

Public Share

What it does: Publishes the Reveal.js HTML from Presentation Composer as a shareable, read-only public link. Intended for recipients who want to preview the briefing in a browser without downloading the .pptx. Pre-connected in template: Receives the presentation HTML from Presentation Composer. Configurable inputs: none required for the default publish flow — the node uses workspace defaults. Output: url → a public URL the user can share.

Marina Layout Selection

Marina Militare ships 6 slide layouts in v1. The agent SHOULD pick the best layout per subtopic based on the content shape, not just use marina-content for everything. Variety across slides improves the visual pacing of the deck.

  • marina-intro — cover/title slide. Used by the #Intro node only. Shows the Marina Militare crest, a large uppercase title in Graduate font, a subtitle, and a meta line. Navy-blue gradient background + Italian tricolor footer.
  • marina-contentdefault workhorse for text-and-bullets content. Shows a fixed top header bar (Marina logo + slide title pulled from data-header-title), an <h2> section heading, and a body of text, lists, or mixed content. Use when the subtopic doesn't fit one of the more specialized layouts below.
  • marina-metrics — KPI/capability dashboard with 3–6 card grid. Each card has a label, a large value (optionally colored gold for highlights, cyan for cool readings, red for alerts, green for OK), and an optional detail line. Use when the subtopic content is numerical or capability-oriented: performance metrics, operational readiness, fleet stats, force levels, budget summaries.
  • marina-timeline — vertical timeline with circular markers along a gold rail. Each event has a timestamp and a description. Supports alert (red with halo) and gold (milestone) variants. Use when the subtopic content is chronological: mission timeline, historical milestones, project cadence, development roadmap, cronologia operativa.
  • marina-comparison — wide table with a navy header row (gold text), bordered rows, and right-aligned status-pill dots (cyan / green / gold / red). Use when the subtopic content is a comparison table, a tracking list, or a status-by-category layout: platform-vs-platform comparisons, contact tracking tables, capability-vs-ally tables, threat assessment tables.
  • marina-outro — closing slide. Used by the #outro node only. Shows the Marina Militare crest, a "Grazie" headline in gold, and a hashtag subtitle.

Layout picking rule (apply in Phase 3 step B when renaming each cloned node):

  1. If the subtopic title or expected content is chronological (contains words like "timeline", "cronologia", "milestone", "history", "roadmap", "evolution", "phases", dates, years) → marina-timeline
  2. Else if the subtopic is number-heavy (contains words like "metrics", "KPI", "statistics", "performance", "capability", "budget", "readiness", "fleet size", "counts") → marina-metrics
  3. Else if the subtopic is a comparison or tracking list (contains words like "comparison", "vs", "competitors", "tracking", "allies", "table", "matrix") → marina-comparison
  4. Else → marina-content

The agent MUST NOT use marina-intro or marina-outro for topic subtopics — those are reserved for the #Intro and #outro nodes.

Theme consistency: marina is already the active theme throughout this template (Presentation Composer + PPTX Converter + all Slide Generator prototypes). If the user explicitly requests a different theme (Polizia Postale, Emblema), that is out of scope for this marina-flavored workspace template — direct them to the sibling pptx-from-drive.md (polpostale-flavored) template instead.

Important: Node IDs and Field Names

  • After applyCanvasData (used only for initial template commit), always use nodeFinder to locate nodes by label name — nodesPreview IDs are no longer valid.
  • Use atomic tools (upsertNode, upsertEdge, deleteNode, deleteEdge, cloneNodes) for ALL canvas modifications after the initial template apply. These execute instantly — no applyCanvasData needed.
  • The Drive Folder Reader input field is driveItemId (NOT folderId).
  • The Drive Document Reader input field is driveItemId.
  • Always verify that a nodeId is a valid UUID before passing it to upsertEdge. If nodeFinder returns no match, do NOT proceed with a missing ID — investigate the label mismatch first.

Execution Strategy

Layout contract: Call organizeNodes (step 14b) → sectionManager group (step 14c) → autoLayout (step 15a) in that exact order. The first two are scoped to the cloned Topic # nodes only; autoLayout then arranges the whole canvas. These tools only change visual arrangement and the section wrapping — they do NOT change node semantics or edges.

You MUST complete all 4 phases in order: Phase 1 → Phase 2 → Phase 3 → Phase 4. Do NOT skip any phase. Do NOT jump to Phase 4 early.

After calling nodeExecutor, the system notifies automatically when execution completes. Do NOT poll getNodeOutput in a loop.

Do NOT call followUp until Phase 4 is fully complete AND the verification audit has been presented in step 18.

If a tool call returns an error, retry up to 3 times with the same or corrected parameters before giving up. If all 3 retries fail, ask the user for help — do NOT skip the step and continue with default values.

If interrupted mid-step and the user sends "continua", resume from where you left off. Use nodeFinder to check which nodes already have the correct label/connections before re-processing them. Nodes that already show the correct label or connections do not need to be updated again.

NEVER execute the prototype node ("Topic #x - Subtopic #x") directly. Only execute cloned and renamed nodes. The prototype is a template for cloning — executing it produces incorrect output.

Phase 1: Setup

  1. applyCanvasDataIMMEDIATELY call this to commit the template to workspace. You MUST call applyCanvasData here before any other tool call.
  2. searchDriveItems + selectDriveItem → get the folder/file ID the user mentioned. If no results, try shorter/broader queries (e.g., "cyber" instead of "cybersecurity cyber security"). If still nothing after 2 attempts, ask the user to specify the drive item. Do NOT retry the same query.
  3. Configure the data source:
    • If folder: nodeFinder → find "Seleziona Materiale Sorgente". upsertNode({ nodeId: "<found ID>", input: { driveItemId: "<selected ID>" } }).
    • If single file: nodeFinder → find "Seleziona Materiale Sorgente". deleteNode({ nodeId: "<found ID>" }). Then nodeFinder → find "Leggi i Documenti disponibili". upsertNode({ nodeId: "<found ID>", input: { driveItemId: "<selected ID>" } }).

Phase 1 complete. Now proceed to Phase 2. Do NOT skip to Phase 4.

Phase 2: Topic Selection & Content Generation

  1. nodeFinder → find "Selezione dei Topic Principali" by label. If the user specified a topic/subtopic count or focus area, update the prompt:
    • upsertNode({ nodeId: <found ID>, input: { prompt: "Seleziona N topic principali dei documenti e per ciascun topic seleziona M temi (subtopic) rilevanti" } }) — replace N and M with the user's desired counts. If the user did NOT specify counts, leave the default prompt.
  2. nodeExecutor → execute "Selezione dei Topic Principali". Wait for completion. The system will execute "Leggi i Documenti disponibili" as a dependency of this node.
  3. getNodeOutput → read the output. Extract the structured list of topics AND their subtopics. For each topic, extract the topic name and the list of subtopic names. Store as:
    • topics = [topic1_name, topic2_name, ..., topicN_name]
    • subtopics[X] = [sub1_name, sub2_name, ..., subM_name] for each topic X
    • N = number of topics, M_x = number of subtopics for topic X
    • If the user specified a maximum topic count, use that count. If the user did NOT specify a maximum, use ALL topics from the output — do NOT cap or reduce them.
    • If the user specified a maximum subtopic count per topic, use that count. If the user did NOT specify a maximum, use ALL subtopics per topic from the output — do NOT cap or reduce them.
    • Safety limits: if N (number of topics) exceeds 20, warn the user and ask whether to proceed or reduce. If total_subtopics (sum of M_x across all topics) exceeds 50, warn the user and ask whether to proceed or reduce. Do NOT silently reduce.

CRITICAL: ALL topic names and subtopic names used in subsequent phases MUST come from the "Selezione dei Topic Principali" output (this step). Do NOT invent topic or subtopic names — always use the exact names from this output.

  1. nodeFinder → find "Contenuto Presentazione" by label. Update its prompt to list ALL topics and subtopics using the exact names from step 6:
    • upsertNode({ nodeId: <found ID>, input: { prompt: "Prepara il contenuto della presentazione espandendo ogni topic e subtopic selezionati dal nodo \"Selezione dei Topic Principali\" creando il testo per ciascuna sezione.\n\nNel prompt elenca tutti i topic e subtopic da espandere includendo il loro nome esteso nel seguente formato:\n\n#Topic 1: <topic1_name>\n##Subtopic 1: <subtopics[1][1]>\n##Subtopic 2: <subtopics[1][2]>\n...\n#Topic 2: <topic2_name>\n##Subtopic 1: <subtopics[2][1]>\n...\n\nPer ogni topic principale, sviluppa una sezione dettagliata con tutti i relativi subtopic, fornendo descrizioni complete e coerenti per la presentazione finale." } }) — replace ALL placeholder names with actual names from step 6.
  2. nodeExecutor → execute "Contenuto Presentazione". IMMEDIATELY after step 7 — do NOT stop between step 7 and step 8. Wait for completion.

Phase 2 complete. Now proceed to Phase 3. Do NOT skip to Phase 4.

Phase 3: Slide Chain Creation

This phase has 4 sequential steps (A → B → C → D) with hard gates. You MUST complete each step before proceeding to the next step. Each batch tool must be called EXACTLY ONCE per step.

Marina v1 has only TEXT-ONLY MODE. There is no image-layout branching in this phase. If the user explicitly asked for images, see the error handling at the bottom of this document — do NOT silently proceed with vanilla image-left/image-right fallbacks; they render unbranded white and look broken next to marina navy.

Rules:

  • Use batch tools for all bulk modifications: batchCloneNodes to duplicate all at once, batchUpsertNodes to rename/configure all at once, batchUpsertEdges for all connections at once.
  • Each batch tool is called EXACTLY ONCE per step. Do NOT call a batch tool a second time to "fix" or "add" entries. If you discover a mistake after a batch call, use the single-operation tool (upsertNode, upsertEdge) to patch the specific item.
  • Do NOT use compileCanvasData or applyCanvasData in this phase. All modifications use atomic/batch tools only.
  • Do NOT delete the prototype node — it must remain on the canvas for future reference.

Step A — Build mapping table, find the prototype, and clone ALL subtopic nodes:

  1. Find prototype and key nodes:
    • nodeFinder → find "Topic #x - Subtopic #x" (standalone Slide Generator prototype). Store its nodeId as simplePrototypeId.
    • nodeFinder → find "Contenuto Presentazione" and "Make Array". Store their nodeId values for all subsequent steps.

9b. Build the COMPLETE MAPPING TABLE before cloning. This mapping is IMMUTABLE — once built, do NOT change it. All subsequent steps (B, C, D) MUST use this exact mapping.

For each topic X from #1 to #N, for each subtopic Y from #1 to #M_x:- Assign sequential `clone_index` starting at 0- Assign `pin` = `item_<clone_index + 1>` (because `item_0` is #Intro)- Pick the best marina layout per subtopic using the "Layout picking rule" in the "Marina Layout Selection" section above (`marina-timeline` / `marina-metrics` / `marina-comparison` / `marina-content`).
**Print the mapping table before proceeding.** Example (3 topics: M_1=2, M_2=3, M_3=1):
| clone_index | pin | topic | subtopic | layout ||-------------|-----|-------|----------|--------|| 0 | item_1 | Topic #1 | Subtopic #1 (chronology) | marina-timeline || 1 | item_2 | Topic #1 | Subtopic #2 (bullet summary) | marina-content || 2 | item_3 | Topic #2 | Subtopic #1 (KPI dashboard) | marina-metrics || 3 | item_4 | Topic #2 | Subtopic #2 (comparison) | marina-comparison || 4 | item_5 | Topic #2 | Subtopic #3 (text) | marina-content || 5 | item_6 | Topic #3 | Subtopic #1 (text) | marina-content || — | item_7 | — | #outro | marina-outro |
`total_subtopics` = 6, total Make Array pins = 8 (item_0 #Intro + 6 slides + item_7 #outro).
  1. Clone ALL subtopic nodes in a single call:

    • batchCloneNodes({ nodeIds: ["<simplePrototypeId>"], count: <total_subtopics> }) → Returns clones array. Each clones[i].nodeIdMap[simplePrototypeId] = cloned Slide Generator ID.

    The clones array is indexed by clone_index from the mapping table. clones[0] = first row, clones[1] = second row, etc.

Do NOT proceed to Step B until Step A completes. You should have exactly total_subtopics entries in the clones array.

Step B — Rename ALL cloned nodes, set their instructions, AND set their marina layouts in one call:

CRITICAL: Cloned nodes keep the original label "Topic #x - Subtopic #x", the original instruction, and the original template_name: "marina-content". You MUST update EVERY cloned node to set label, input.instruction, AND input.template_name (to the layout picked in the mapping table).

  1. Build the operations array using the MAPPING TABLE from step 9b and call batchUpsertNodes exactly once: For each row in the mapping table, construct one operation: { nodeId: clones[clone_index].nodeIdMap[prototypeId], label: "Topic #X - Subtopic #Y", input: { instruction: "Inserisci il contenuto del Topic #X '<topicX_name>' - Subtopic #Y '<subtopics[X][Y]>'", template_name: "<layout from mapping table>" } }

    batchUpsertNodes({ operations: [  { nodeId: clones[0].nodeIdMap["<prototypeId>"], label: "Topic #1 - Subtopic #1", input: { instruction: "Inserisci il contenuto del Topic #1 '<topic1_name>' - Subtopic #1 '<subtopics[1][1]>'", template_name: "marina-timeline" } },  { nodeId: clones[1].nodeIdMap["<prototypeId>"], label: "Topic #1 - Subtopic #2", input: { instruction: "Inserisci il contenuto del Topic #1 '<topic1_name>' - Subtopic #2 '<subtopics[1][2]>'", template_name: "marina-content" } },  ...] })
    • The instruction MUST include the exact topic and subtopic names. Example: "Inserisci il contenuto del Topic #1 'Operazioni nel Mediterraneo' - Subtopic #2 'Cronologia Operativa'" — NOT "Inserisci il contenuto del Topic #1 - Subtopic #2" alone.
    • The template_name MUST be one of the 4 content layouts: marina-content, marina-metrics, marina-timeline, marina-comparison. Do NOT use marina-intro or marina-outro (reserved for the #Intro and #outro nodes). Do NOT use layouts from other themes (no emblema-*, no polpostale-*, no vanilla bullet-points etc.).

ANTI-PATTERN: Do NOT call batchUpsertNodes a second time to "fix" or "add" entries. If you discover a mistake, use the single upsertNode tool to patch that specific node.

Do NOT proceed to Step C until Step B completes with all nodes renamed and their marina layouts set.

Step C — Wire content input on ALL cloned nodes in one call:

CRITICAL: Cloned nodes do NOT inherit external edges. Each cloned Slide Generator starts with NO content input. You MUST create an edge from "Contenuto Presentazione" to EVERY cloned Slide Generator. Without this edge, slides will be EMPTY.

  1. Build the edges array using the MAPPING TABLE from step 9b and call batchUpsertEdges exactly once: For each row in the mapping table: { sourceNodeId: "<Contenuto Presentazione ID>", sourcePort: "summary", targetNodeId: clones[clone_index].nodeIdMap[prototypeId], targetPort: "content" }

    batchUpsertEdges({ edges: [  { sourceNodeId: "<Contenuto Presentazione ID>", sourcePort: "summary", targetNodeId: clones[0].nodeIdMap["<prototypeId>"], targetPort: "content" },  { sourceNodeId: "<Contenuto Presentazione ID>", sourcePort: "summary", targetNodeId: clones[1].nodeIdMap["<prototypeId>"], targetPort: "content" },  ...] })

ANTI-PATTERN: Do NOT call batchUpsertEdges a second time. If a single edge is missing, use upsertEdge to add it.

Do NOT proceed to Step D until Step C completes with all content inputs wired.

Step D — Wire ALL cloned nodes to Make Array and connect #outro in one call:

CRITICAL: item_0 is RESERVED for #Intro (already pre-connected). The first topic slide MUST go to item_1, NOT item_0. If you connect a topic slide to item_0, you will overwrite the Intro slide.

  1. nodeFinder → find "#outro". Store its nodeId for the last connection.

  2. Build the edges array using the MAPPING TABLE from step 9b and call batchUpsertEdges exactly once: For each row in the mapping table, the pin is already determined: pin = item_<clone_index + 1>: { sourceNodeId: clones[clone_index].nodeIdMap[prototypeId], sourcePort: "slide_html", targetNodeId: "<Make Array ID>", targetPort: "item_<clone_index + 1>" }

    LAST entry: #outro on item_<total_subtopics + 1>: { sourceNodeId: "<#outro ID>", sourcePort: "slide_html", targetNodeId: "<Make Array ID>", targetPort: "item_<total_subtopics + 1>" }

    batchUpsertEdges({ edges: [  { sourceNodeId: clones[0].nodeIdMap["<prototypeId>"], sourcePort: "slide_html", targetNodeId: "<Make Array ID>", targetPort: "item_1" },  { sourceNodeId: clones[1].nodeIdMap["<prototypeId>"], sourcePort: "slide_html", targetNodeId: "<Make Array ID>", targetPort: "item_2" },  ...  { sourceNodeId: "<#outro ID>", sourcePort: "slide_html", targetNodeId: "<Make Array ID>", targetPort: "item_<total_subtopics + 1>" },] })

ANTI-PATTERN: Do NOT call batchUpsertEdges a second time. If a single edge is missing, use upsertEdge to add it.

Do NOT proceed to Phase 4 until Step D completes with #outro connected to Make Array.

Step E — Organize the cloned subtopic nodes into a tidy grid:

14b. Build the list of cloned node IDs from the MAPPING TABLE: cloneIds = [ clones[i].nodeIdMap[<prototypeId>] for i in 0..total_subtopics-1 ]. Call organizeNodes exactly once: organizeNodes({ nodeIds: cloneIds }) — no anchor (the tool centers the grid on the clones' current centroid), default gap: 80. The tool computes rows × cols automatically, targeting a ~16:9 bounding box.

ANTI-PATTERN: Do NOT pass section IDs (the "Prototype Node per Topic/Subtopic" section stays where it is). Do NOT pass #Intro / #outro / Make Array / Presentation Composer / PPTX Converter / Public Share — those are arranged by autoLayout in Phase 4.

Do NOT proceed to Step F until Step E completes.

Step F — Group the organized clones into a section:

14c. Using the same cloneIds array from step 14b, call sectionManager exactly once with the group action: sectionManager({ action: "group", nodeIds: cloneIds, label: "Topic Slides", color: "#3b82f6" }) — this wraps all the cloned Topic # nodes into a single section labeled "Topic Slides". The section becomes a single draggable unit that autoLayout arranges alongside the other top-level nodes in Phase 4.

ANTI-PATTERN: Do NOT include the prototype node (simplePrototypeId), #Intro, #outro, or any non-subtopic node in this group call. The section must contain ONLY the cloned Topic # subtopic slides.

Do NOT proceed to Phase 4 until Step F completes.

Pin order example (3 topics: M_1=2, M_2=3, M_3=2):

  • item_0 = #Intro (pre-connected — do NOT touch)
  • item_1 = Topic #1 - Subtopic #1 (clone_index=0), item_2 = Topic #1 - Subtopic #2 (clone_index=1)
  • item_3 = Topic #2 - Subtopic #1 (clone_index=2), item_4 = Topic #2 - Subtopic #2 (clone_index=3), item_5 = Topic #2 - Subtopic #3 (clone_index=4)
  • item_6 = Topic #3 - Subtopic #1 (clone_index=5), item_7 = Topic #3 - Subtopic #2 (clone_index=6)
  • item_8 = #outro

Do NOT delete the prototype node. It must remain on the canvas for future reference.

Phase 3 complete. All subtopic nodes must be cloned, renamed, assigned a marina layout, wired for content, connected to Make Array starting at item_1, and #outro connected as the last pin. Now proceed to Phase 4.

Phase 4: Verification & Final Execution

15a. Call autoLayout exactly once to arrange the entire canvas (sections + top-level nodes) along the edge flow: autoLayout({}) — defaults direction: "LR", nodeSpacing: 50, rankSpacing: 100. The locked section "Prototype Node per Topic/Subtopic" stays pinned (the tool excludes locked sections and their children). This makes the graph easy to scan before the user runs it.

CRITICAL: Do NOT skip this phase. You MUST verify connections before executing. Missing edges cause empty slides in the final presentation. Phase 4 ends with a final audit via the verification tool — followUp must not be called until the audit has been presented.

  1. nodeFinder → find "Make Array" and ALL Slide Generator nodes (matching "Topic #" in label, plus "#Intro" and "#outro"). Compute total_subtopics = sum of M_x across all topics. Check that Make Array has exactly total_subtopics + 2 inputs (1 #Intro + total_subtopics + 1 #outro). Verify:
    • #Intro on item_0: "#Intro" output slide_html → Make Array item_0. If a topic slide is on item_0 instead, it has overwritten #Intro — fix by reconnecting #Intro to item_0 and shifting topic slides to start from item_1.
    • Content connection: For EACH Slide Generator node labeled "Topic #X - Subtopic #Y", verify it has content input connected. If a Slide Generator shows inputs: [] or no input from "Contenuto Presentazione", it will produce an EMPTY slide. Fix with upsertEdge({ sourceNodeId: "<Contenuto Presentazione>", sourcePort: "summary", targetNodeId: <Slide Gen ID>, targetPort: "content" }).
    • Marina layout: For EACH Slide Generator node labeled "Topic #X - Subtopic #Y", verify template_name is one of the 4 valid content layouts (marina-content, marina-metrics, marina-timeline, marina-comparison). If any shows template_name: "marina-content" uniformly across all slides, step B may have skipped the per-subtopic layout picking — re-check and patch with upsertNode if needed for visual variety.
    • Make Array connection: Each "Topic #X - Subtopic #Y" → "Make Array" item_<pin>. Topic slides start at item_1. #outro is always on the last pin.
    • #outro connected: "#outro" output slide_html → Make Array last pin. If #outro is NOT connected, the presentation will have no closing slide. Fix immediately.
    • Theme consistency: Presentation Composer theme == "marina" AND PPTX Converter theme == "marina". If either drifted to another theme (e.g. default "white"), fix with upsertNode. If ANY Slide Generator node shows outputs: [], it is missing its Make Array edge. Fix ALL missing connections with upsertEdge before proceeding.
  2. nodeFinder → find "PPTX Converter" by label.
  3. Execute both terminal nodes sequentially: a. nodeExecutor → execute "PPTX Converter" (node id from step 16). Wait for completion. The system auto-runs all upstream nodes (#Intro + all topic slides + #outro → Make Array → Presentation Composer → PPTX Converter). When complete, a .pptx file is available for download. b. nodeFinder → find "Public Share" by label. nodeExecutor → execute it. Because Presentation Composer has already produced its HTML output in step 17a, Public Share will resolve quickly. On completion, the Public Share node returns a public url.

Both terminal executions must complete before step 18. If either fails, retry per the "retry 3×, then ask the user" rule in the Execution Strategy preamble — do NOT skip either output silently.

(Legacy note): The system auto-runs all upstream nodes (#Intro + all topic slides + #outro → Make Array → Presentation Composer → PPTX Converter). Wait for completion. 18. verification → run a final audit and present the results to the user as a checklist before summarizing. Items array (use info severity ✓ for passing checks, warning/error for real issues found): - Make Array has exactly total_subtopics + 2 inputs - #Intro is on item_0 - #outro is on the last Make Array pin - Every "Topic #X - Subtopic #Y" Slide Generator has a content edge from "Contenuto Presentazione" - Every "Topic #X - Subtopic #Y" Slide Generator has a valid marina content layout (marina-content, marina-metrics, marina-timeline, or marina-comparison) - Presentation Composer theme == "marina" and PPTX Converter theme == "marina" - autoLayout was applied in step 15a (canvas has been laid out left-to-right along the edge flow; no node is stacked on top of another unless part of the locked Prototype section) - Both "PPTX Converter" AND "Public Share" completed successfully. PPTX Converter produced a downloadable .pptx file; Public Share produced a shareable url

Summary: `"Verifica finale del briefing Marina Militare: <N> controlli, <E> errori, <W> warning."` Use the user's language. If any `error`-severity items are reported, the user will select which to fix — do NOT call `followUp` in that case; wait for the user's fix selection and then re-run the relevant Phase 4 steps. **Only if all checks pass** (items are all `info` severity or empty) proceed to step 19.
  1. followUp — summarize what was done (mention both deliverables: the .pptx file from PPTX Converter and the Public Share url): N topics, M_x subtopics per topic, total slides = total_subtopics + 2 (intro + outro), theme = Marina Militare. The PPTX file is available for download.

Common User Requests → Configuration Tips

User saysConfigure
"X topics with Y subtopics each"Topic Selector: update prompt to "Seleziona X topic principali e per ciascun topic seleziona Y temi rilevanti". Total content slides = X × Y.
"5 subtopics per topic"Topic Selector: update prompt to include "per ciascun topic seleziona 5 temi"
"More detail per topic"Topic Selector: increase subtopic count in prompt
"Fewer slides"Topic Selector: decrease subtopic count in prompt
"Focus on specific topics"Topic Selector: update prompt to "Focus on [topic area] only"
"Change content style"Update "Contenuto Presentazione" prompt to specify formatting preferences
"Make slides with timelines" / "include a timeline for Topic #X"Ensure the relevant subtopic(s) are assigned template_name: "marina-timeline" in Phase 3 step B. Update post-hoc with upsertNode if needed.
"Use KPI cards" / "metric dashboard"Assign template_name: "marina-metrics" to the relevant subtopic(s).
"Use a comparison table"Assign template_name: "marina-comparison" to the relevant subtopic(s).
"Use a different theme" / "Use Polizia Postale" / "Use Emblema"Out of scope for this workspace template. This template is marina-flavored. Direct the user to use the pptx-from-drive.md (polpostale) workspace template, or ask for the marina-to-other theme migration as a separate request.
"Add images to slides" / "Use image layout" / "con immagini"Marina v1 does not ship branded image layouts. Respond: "Il tema Marina Militare v1 non include layout con immagini (solo intro, content, metrics, timeline, comparison, outro). Posso procedere con il layout testuale marina-content (o le varianti metrics/timeline/comparison per i subtopic che lo richiedono), oppure se hai bisogno di slide con immagini posso usare il template Polizia Postale o Emblema che li supportano. Cosa preferisci?" Do NOT silently fall back to vanilla image-left / image-right — they render white and look broken next to marina navy.

Error Handling

  • MANIFEST_NOT_FOUND: Documents not processed. User needs to upload them to a KnowledgeBase first.
  • Connection refused: Check that www-emblema is running at the correct EMBLEMA_API_BASE_URL.
  • Empty slides: Cloned Slide Generator nodes do NOT inherit external edges. Verify that "Contenuto Presentazione" [summary] → each Slide Generator [content] edge exists. Also check the "Contenuto Presentazione" prompt includes all topics/subtopics with their full names.
  • No topics or subtopics in output: If "Selezione dei Topic Principali" returns no topics or fewer subtopics than expected, the document may not have enough content. Try providing more source material or adjusting the prompt to be less restrictive.
  • Missing "Contenuto Presentazione" content: If the prompt was not updated with all topics/subtopics, or if topic names are wrong, the Slide Generators will produce incomplete or mismatched slides. Always verify the prompt matches step 6 output exactly.
  • Connected slide_html → undefined: The targetNodeId or targetPort was invalid. Verify Make Array nodeId is a valid UUID from nodeFinder and the item_X pin number is correct. Always validate nodeIds before passing to upsertEdge.
  • Nodes still named "Topic #x - Subtopic #x": cloneNodes with a single nodeIds does NOT rename the cloned node. You MUST call upsertNode with label to rename each clone (Step B).
  • All slides are marina-content uniformly: Step B's per-subtopic layout picking was skipped. Go back and patch each cloned node with upsertNode to set the right layout (marina-timeline / marina-metrics / marina-comparison) based on the subtopic's content shape.
  • User requested image layouts: Marina v1 has no marina-image-left or marina-image-right. Respond with the polite refusal from the Common User Requests table above. Do NOT silently use vanilla image-left / image-right — they are unbranded and break the marina visual identity. If the user specifically wants to add marina image layouts to the theme itself, that is a separate theme-extension task: new CSS in _MR_CSS, new TEMPLATE_INSTRUCTIONS entries in slide_generator.py, new entries in slide-templates.ts, new SVG thumbnails, and an executor rebuild. Direct the user to request that separately.
  • Theme drifted from marina: If Presentation Composer or PPTX Converter theme field shows anything other than "marina" (e.g. default "white"), fix with upsertNode({ nodeId: <found ID>, input: { theme: "marina" } }). Both nodes must match.

Cross-references

  • apps/workspace-service/app/templates/themes/marina/ — staging folder for the marina theme adaptation work (source Reveal.js files + extracted CSS / base64 artifacts).
  • apps/workspace-service/app/templates/MARINA_SLIDES_CATALOG.md — full catalog of the marina-* slide layouts (including the 18 aspirational layouts not yet shipped in v1).
  • apps/workspace-service/app/templates/sample_marina_briefing.md — ready-to-feed mock briefing content covering every marina-* layout in this document, grounded in publicly-verifiable Marina Militare facts.
  • apps/workspace-service/app/templates/ADDING_PRESENTATION_THEMES.md — procedure for adding or extending marina (e.g. if you want to ship marina-image-left/marina-image-right later).
  • apps/workspace-service/app/templates/reference/pptx-from-drive.md — the polpostale-flavored sibling of this document. Use it for Polizia Postale or Emblema theme workflows.
  • apps/workspace-service/app/templates/presentation_composer.py — composer source with _MR_CSS (marina theme CSS), CUSTOM_THEMES["marina"], and THEME_DIMENSIONS["marina"] = (1920, 1080).
  • apps/workspace-service/app/templates/slide_generator.py — slide generator with the 6 marina-* entries in TEMPLATE_INSTRUCTIONS.

// Dependencies

requirements.py
1from input import DriveFolderReader2from input import DriveDocumentReader3from process import SlideGenerator4from process import MakeArray5from output import PresentationComposer6from output import PPTXConverter7from process import RecursiveSummarizer8from output import PublicShare

$ git log --oneline

v1.2.3
HEAD
2026-04-23

set slide as default transition for composer

v1.2.22026-04-23

unlocked sections