$ cat workspace-template.yaml
PMESII Analysis
// Sistema di analisi documentale PMESII versione 1 - A partire da un folder o un documento, esegue analisi di tipo Politico, Militare, Infrastrutturale, Informativo, Economico, Sociale e Ambientale.
// Canvas Preview
// Instruction
PMESII Analysis
Template ID
569a4e0f-0464-4bce-b441-8ac447fdb619
Overview
A PMESII intelligence-analysis workflow. It reads a source document (or Drive folder), asks for a subject / geopolitical entity, then runs 7 parallel chains — one per PMESII domain (Political, Military, Economic, Social, Infrastructure, Information, Environmental). Each merges its preset prompt + subject + document text, summarizes via an LLM, and feeds "Make Array - Merge all Content" → save-file (one consolidated .md to Drive). The standalone "Public Share" node is optional and NEVER auto-run.
The template ships with the [Political] chain fully built (section "Analysis Loop [Political]") and all 7 domain prompts already authored ("Impersonification Prompts Presets"). The build: clone the 3 Political-chain NODES 6 times (Insert + Merge + Summarizer — by nodeIds, NOT the section), rename + rewire each clone to its existing preset and a fresh Make Array pin, then run save-file. Presets are populated — do NOT rewrite them. NEVER rename, re-pin, or delete the original Political chain — it stays on item_0.
Workflow Chain
Make Array pins (IMMUTABLE): item_0=Political (pre-wired); clones fill item_1…item_6. 10 pins → none created.
Node Reference
- "Drive Folder Reader" — lists items in a folder. Input
driveItemId= the folder ID (notfolderId). OutputdriveItemIds→ "Document Reader"[driveItemId]. - "Document Reader" — reads document text. Input
driveItemId. Outputcontent→ thecontentinput of ALL 7 Summarizers. - "Geopolitical Actor" (Text) — holds the subject. Input
value(default"Vulpecula"placeholder, overwrite it). User calls it the "Subject to Analyze" node; canvas label "Geopolitical Actor". - "Analysis prompt [X]" ×7 (Text) — each holds a fully-authored domain PMESII prompt. Already populated — do NOT clone or rewrite. Output
value→ "Merge Prompt and Subject [X]"[text1]. - "Insert The Subject into Prompt [X]" (Text Merge,
text1/text2, NEVERvalue) —text1="The subject of the analysis is" (preserved by clone),text2←Geopolitical Actor. Outputtext. - "Merge Prompt and Subject [X]" (Text Merge — ports
text1/text2) —text1←preset,text2←Insert. Outputtext→ Summarizer[prompt]. - "Summarizer - X" (Text Summarizer) —
prompt←Merge,content←Document Reader; model fieldllmModelId(defaultqwen3.5-35b, leave it). Outputsummary→ Make Array[item_<n>]. - "Make Array - Merge all Content" — collects 7 summaries; pins
item_0…item_6. Outputitems→ save-file[text]AND Public Share[content]. - "save-file" — writes
.mdto Drive. LOCAL — auto-run after verify, no publish gate. Inputsname(set subject-based at step 5 →pmesii_<slug>.md),destinationFolder(set at step 5; default may not exist → 422),text. - "Public Share" — mints a public
/s/{slug}URL. Externally visible — NEVER auto-run; offer only as an explicit follow-up. Inputstitle,share_type,expiry_hours,pin_protected.
Domain → Preset → Pin Mapping (IMMUTABLE)
Political exists (item_0); build the 6 rows below in order. Section per domain = "Analysis Loop [domain]".
Per cloned chain [X]: the 6 edges to ensure
batchUpsertEdges is idempotent — re-assert ALL 6 per chain (node-clone preserves E1/E2 but never the external E3–E6).
- E1 (internal):
Insert The Subject into Prompt [X][text]→Merge Prompt and Subject [X][text2] - E2 (internal):
Merge Prompt and Subject [X][text]→Summarizer - [X][prompt] - E3:
Geopolitical Actor[value]→Insert The Subject into Prompt [X][text2] - E4:
Analysis prompt [X][value]→Merge Prompt and Subject [X][text1] - E5:
Document Reader[content]→Summarizer - [X][content] - E6:
Summarizer - [X][summary]→Make Array - Merge all Content[item_<n>](pin from table)
All IDs come from nodeFinder (ground truth — never assume edges exist). Both readers' field is driveItemId. Validate every nodeId is a real UUID before upsertEdge.
Execution Strategy
You MUST complete all 3 phases in order: Phase 1 → Phase 2 → Phase 3. Do NOT skip any phase.
After nodeExecutor, the system notifies — do NOT poll getNodeOutput. On a tool error, retry up to 3× then ask the user. On "continua", resume via nodeFinder checks.
Tool rule for inputs: the source (file OR folder) and the output folder use the Drive picker selectDriveItem — NOT a requestUserDecision/preset "folder vs file?" question. Only the subject (free text) uses requestUserDecision. Never make the user type a Drive ID, and do NOT ask "come vuoi fornire i documenti?" with preset options — open the picker and branch on what they pick.
Drive the tools yourself — start NOW, no confirmation, no premature verification. After applyCanvasData, go STRAIGHT into the pickers in the SAME turn (selectDriveItem source → requestUserDecision subject → selectDriveItem output folder), then build. Do NOT run verification before Phase 3, and do NOT post a "problemi da risolvere / vuoi che proceda?" summary of the unset source/subject/output or the 6 missing chains — those are EXPECTED (you're about to create them), NOT problems; listing them, or any prose question, and ending your turn is a FAILURE. The user's request IS the go-ahead — even with no file/subject/folder named, go straight into the pickers; never fall back to the demo/placeholder.
Fallback guard (CRITICAL) — applies to BOTH the source AND the output-folder question; they must behave identically: if you ask for a file/folder via requestUserDecision (e.g. selectDriveItem is unavailable), you MUST set allowFreeText: true so there is ALWAYS an open field where the user can type a name to search — then resolve it with searchDriveItems. Suggested items may be added as options, but they are EXTRA choices, never the only ones, and keep them FEW (≤4 top matches). NEVER allowFreeText: false for a file/folder question — in particular do NOT emit a sourceType "folder vs file" prompt with 2 fixed options and no open field.
Phase 1: Source, Subject & Output
applyCanvasData→ IMMEDIATELY commit the template, before any other tool call. After this, preview IDs are invalid — usenodeFinder.nodeFinder→ "Document Reader" and "Drive Folder Reader" to get their IDs for step 3.- Source — use the Drive picker. Call
selectDriveItemso the user browses and picks the source — a single file OR a folder (the picker IS the choice; do NOT first ask "folder or file?" with preset options). If the user already named a target in chat, use it. Then branch on what was picked:- Folder picked:
upsertNode"Drive Folder Reader"{ driveItemId: "<folder ID>" }. Keep BOTH readers (folder feeds document viadriveItemIds → driveItemId). - Single file picked (OR — rule 2A — "Drive Folder Reader" empty while "Document Reader" set): ORDER MATTERS —
deleteNode"Drive Folder Reader" FIRST, THENupsertNode"Document Reader"{ driveItemId: "<file ID>" }— settingdriveItemIdwhile the folder-reader edge still feeds it does NOT stick (delete that edge by deleting the folder reader first).
- Folder picked:
- Subject. Call
requestUserDecisionexactly once — one free-text question (options: []), key"subject": question "Quale soggetto o entità geopolitica vuoi analizzare con il framework PMESII?", context "Il soggetto verrà inserito in tutti i 7 prompt PMESII." Readsubject = answers.find(a=>a.key==="subject")?.otherText ?? answers.find(a=>a.key==="subject")?.selectedIds?.[0]. If cancelled/empty, ask once more in chat.nodeFinder→ "Geopolitical Actor";upsertNode{ value: subject }. Do NOT keep the placeholder"Vulpecula". - Output folder (REQUIRED) — use the Drive folder picker. Call
selectDriveItem(type=folder) so the user browses and picks the destination folder. If you instead ask viarequestUserDecision, it MUST haveallowFreeText: true+ ≤4 options, NEVERfalse.nodeFinder→ "save-file";upsertNode{ destinationFolder: "<picked folder ID>", name: "pmesii_<subject-slug>.md" }. Slugify the subject (lowercase, spaces/punctuation → hyphens), e.g.pmesii_regia-marina.md. The defaultdestinationFoldermay not exist → 422, so always set a real, user-picked folder. (No date/time — the agent has no reliable clock; re-runs upsert the same item.)
Phase 1 complete. Now proceed to Phase 2.
Phase 2: Build the 6 remaining chains
Exactly 6 domains to add (Military, Economic, Social, Infrastructure, Information, Environmental); Political already exists on item_0. Use the IMMUTABLE mapping table.
CRITICAL RULES (prevent the known failure that silently drops Political):
- Clone the 3 chain NODES by
nodeIds, NOT the section. A section-clone'snodeIdMapmaps only the section → 7 identically-labelled children you can't tell apart → you rename the wrong nodes and lose Political. Node-clone gives an addressablenodeIdMap+ preserves internal edges. - Touch ONLY IDs in the current clone's
nodeIdMap. NEVER rename, re-pin, or delete the original Political nodes. - NEVER recreate a node from a
templateId. If a clone is wrong,deleteNodeit andcloneNodesagain. labelis a TOP-LEVEL field of eachbatchUpsertNodesoperation — never insideinput({input:{label}}fails).item_0is Political's reserved pin. Clones useitem_1…item_6ONLY.
nodeFinder→ store the shared-target IDs: "Geopolitical Actor" (E3), "Document Reader" (E5), "Make Array" (E6). Also store the 3 clone-source IDs (thecloneNodesinput ANDnodeIdMapkeys, same for all 6 clones): "Insert The Subject into Prompt [Political]", "Merge Prompt and Subject [Political]", "Summarizer - Political".- For EACH domain X from 1 to 6 (mapping-table order), do sub-steps a–d:
a.
cloneNodes({ nodeIds: ["<Insert Political>", "<Merge Political>", "<Summarizer Political>"] })→ clones the 3 nodes together (internal edges E1/E2 preserved). ReturnsnodeIdMapmapping each old ID → its new ID. Use ONLY these 3 new IDs below. b. Rename the 3 new nodes viabatchUpsertNodes—labeltop-level, noinput: c.nodeFinder→ "Analysis prompt [X]" (existing preset — do NOT clone/rewrite). ThenbatchUpsertEdgeswith all 6 edges E1–E6, E6 pin from the table (item_1…item_6, NEVERitem_0): d. Group the 3 new nodes:sectionManager({ action: "group", nodeIds: [<the 3 new IDs>], label: "Analysis Loop [X]" }), then next domain.
Hard gate: do NOT proceed to Phase 3 until all 6 chains are cloned, renamed, wired, and grouped. The original Political chain stays untouched on item_0.
8. After all 6 chains exist, call autoLayout() once to space the new sections across the canvas.
Phase 2 complete. Now proceed to Phase 3.
Phase 3: Verification & Output
nodeFinder→ "Make Array - Merge all Content" and ALL 7 "Summarizer - X". The inner LLM drops middle edges and a priorverificationcan be a FALSE POSITIVE — verify againstnodeFinderground truth:- Political present & on
item_0(#1 failure): "Summarizer - Political" still exists AND feedsitem_0. Ifitem_0is fed by another domain, the original chain was renamed — STOP, rebuild the lost domain, restore Political toitem_0. - 7 distinct pins
item_0…item_6, each from a DIFFERENT Summarizer (empty → missing E6; two on one pin → re-pin per table). - Node types: every Insert/Merge is a Text Merge (
text1/text2). Avalueport = wrong type (aTextnode) →deleteNode+ re-clone (never recreate from template). One node per label — delete duplicates. - Per-chain edges: Summarizer
outputs: []→ E6; nocontent→ E5; noprompt→ E2. Merge notext2→ E1; notext1→ E4. Fix withupsertEdge. - "Geopolitical Actor"
value== subject (NOT"Vulpecula"); else re-apply step 4. - Source + output set: the active reader's
driveItemIdis non-empty (single-file: delete folder reader FIRST, then set), and "save-file"destinationFolder+ subject-basedname(pmesii_<slug>.md) are set (step 5) — empty source/dest → 422.
- Political present & on
nodeExecutor→ "save-file". It auto-resolves the whole upstream DAG. Wait for completion (no polling). Do NOT auto-run "Public Share".verification→ present the step-9 checks as a user-facing checklist (info✓ /warning/error), most important first: "Summarizer - Political" exists & feedsitem_0; 7 distinct pinsitem_0…item_6; every Insert/Merge is a Text Merge (novalueport); no duplicate labels; each Summarizer wired E5/E2/E6 + chain E1/E4; subject ≠"Vulpecula"; source + output set; ✓ "save-file eseguito — report .md scritto in Drive". On anyerror: do NOTfollowUp; wait for the user's fix, re-run step 10. Only if all pass → step 12.followUp→ summarize: 7 PMESII domains analyzed for "", report saved as.md. Suggest next steps: "Esegui 'Public Share' per un link pubblico" (public/s/{slug}URL), change subject, change source.
Phase 3 complete.
Configuration Tips
Error Handling
- Empty domain in the report: that Summarizer→Make Array edge (E6) is missing (
outputs: []). Fix withupsertEdgeusing the table pin. - Empty / generic analysis for a chain: missing E4 (preset→Merge) or E5 (document→Summarizer). Re-assert both.
- save-file fails with 422: source or dest unset. Usually "Document Reader"
driveItemIdis empty (often set BEFORE the folder-reader edge was deleted) → single-file:deleteNode"Drive Folder Reader" thenupsertNode"Document Reader"{ driveItemId }. ElsedestinationFolderis invalid → set the step-5 folder. Re-run. - Folder reader overwriting Document Reader (single-file): you skipped rule 2A —
deleteNode"Drive Folder Reader", re-set "Document Reader"driveItemId. - Political domain missing /
item_0fed by Military (or another domain): the original Political chain was renamed instead of a clone — the classic result of section-cloning + duplicate labels. Restore: ensure a "Summarizer - Political" feedsitem_0, and rebuild the domain that displaced it. Prevent by cloning the 3 nodes bynodeIds, never the section, and touching onlynodeIdMapIDs. - Wrong node type (
valueport on Insert/Merge) / duplicate labels / clones still named "[Political]": from recreating viatemplateIdor a skipped rename.deleteNodethe bad/extra node and re-clone; rename clones withbatchUpsertNodes(labelis TOP-LEVEL, not ininput). Never recreate from template. - Schema field errors (
"Unknown input field 'label'/'llmModel'/'text1'"): field map =Text→value,Text Merge→text1/text2,Text Summarizer→prompt/content/llmModelId. Sections overlapping →autoLayout(step 8).nodeFinderno match → fix the exact label; never pass a guessed ID toupsertEdge.
Incremental Edits
Applies only when the pipeline pre-existed this turn (7 "Analysis Loop [X]" sections + Make Array wired): it's an EDIT — do NOT re-applyCanvasData, re-run the gates, or re-run the clone loop. Apply the delta with atomic tools, then re-run the step-9 verification + "save-file", then followUp. A bundled "build AND tweak" request is a fresh build (all 3 phases; delta after the chains exist).
- Change subject —
upsertNode"Geopolitical Actor"{ value }. - Change source — reconfigure readers as in Phase 1 step 3.
- Add a domain (rare) —
cloneNodes({ nodeIds: [<the 3 Political chain nodes>] }), then 7b–7d, Summarizer → next free pin (item_7) → group →autoLayout. Never clone the section or recreate from template. - Remove a domain —
deleteNodeits 3 chain nodes; shift later Summarizer→Make Array pins down by one so pins stay contiguous (Political staysitem_0);organizeNodes+autoLayout. - Publish — explicit request only:
nodeFinder→ "Public Share" →nodeExecutor; warn it mints a public URL.
Cross-references
reference/INSTRUCTION_GUIDELINES.md · pptx-from-drive.md
// Dependencies
1from input import DriveDocumentReader2from process import TextSummarizer3from input import Text4from process import TextMerge5from output import PublicShare6from process import MakeArray7from input import DriveFolderReader8from output import SaveFile$ git log --oneline