$ cat workspace-template.yaml
Monthly Business Review
// Builds a board-ready business review deck for a course business from the organization ontology and live Odoo invoices: revenue by month and by course, enrollment and completion figures, and the actions they imply. Outputs an HTML deck and a downloadable PowerPoint. A second application on the same ontology — it defines no model and ingests nothing.
// Canvas Preview
// Instruction
Monthly Business Review
Overview
Builds a board-ready review deck for a course business: revenue by month and by course, enrollment and completion figures, and the actions they imply. Outputs a self-contained HTML deck and a downloadable .pptx.
This is the second application on the same ontology. It defines no object types, ingests nothing, and reuses the console's data-access node unchanged. That is the whole point of the artefact — once the model exists, another app is a rollup and four slide prompts.
Every figure is produced by "Build Review" and passed to the slides as text. The slide generators are instructed to reproduce numbers verbatim; they are laying out figures, not computing them. If a number on a slide looks wrong, the fault is in figures, which is emitted alongside for exactly that reason.
Requires: the ontology already loaded (run Odoo to Ontology Feeder first) and an Odoo connection on the workspace-token owner's account.
Workflow Chain
Node Reference
- "Run Date" (String) — the ISO date the reporting window ends on. The executor has no reliable clock. Configurable:
value. - "Read Ontology" (Custom Code) — the console's data-access node, reused unchanged. Configurable:
maxObjects. - "Ontology Census" (Ontology Aggregate) — exact object counts per type. Exact, not sampled: it is a census, not a retrieval ranking.
- "Odoo Invoices (live)" (Odoo RPC) — posted customer invoices.
invoice_dateis required inparams.fields; the monthly rollup buckets on it. Configurable:params.domain,params.limit. - "Build Review" (Custom Code) — computes every figure and renders the four slide inputs. Emits
figures(auditable JSON) alongside the text. Configurable:periodMonths(default 6),runDate. - "Slide — Cover / KPIs / Revenue / Actions" (Slide Generator) — layouts
emblema-cover,emblema-kpis,emblema-table,emblema-bullets. Configurable:instruction,template_name,language,llmModelId(leave EMPTY for the workspace default). - "Deck Slides" (Make Array) — order matters: cover, KPIs, revenue, actions on
item_0..item_3. - "Compose Deck" (Presentation Composer) → "Export PPTX" (PPTX Converter) → "Save Deck" (save-file, GATED,
destinationFolderships EMPTY on purpose).
Execution Strategy
You MUST complete all 4 phases in order: Phase 1 → Phase 2 → Phase 3 → Phase 4. Do NOT skip any phase.
There is exactly ONE requestUserDecision, in Phase 4, and only because saving to Drive is a side effect. Phases 1–3 are read-only.
Phase 1: Gather
applyCanvasDatato commit the template, thennodeFinderfor every id — preview ids are stale.- If the user named a date or a period,
upsertNode"Run Date"{ value: <date> }and "Build Review"{ runDate: <date>, periodMonths: <n> }. Otherwise keep the baked values; do NOT ask. nodeExecutor"Read Ontology", "Ontology Census" and "Odoo Invoices (live)" together — all three are independent.getNodeOutputeach. If "Read Ontology" reports no Enrollment objects, STOP and report that the Odoo to Ontology Feeder must run first. If "Odoo Invoices (live)" returnsrecord_count: 0, STOP — a revenue deck with no invoices would render every figure as zero and look like a collapsed business rather than a missing feed.
Phase 1 complete. Now proceed to Phase 2.
Phase 2: Compute
nodeExecutor"Build Review".getNodeOutputand readsummaryandfigures.- Report the headline figures to the user in prose before any slide is rendered: collected, outstanding, enrollments, completion rate, stalled count. Rendering four slides from numbers the user has not seen wastes a full LLM pass when a figure is wrong.
- If any enrollment carries
engagementSource: "synthetic", say that completion and progress figures are derived rather than measured, because no Moodle completion feed is wired. Never let a synthetic figure reach a board slide unlabelled.
Phase 2 complete. Now proceed to Phase 3.
Phase 3: Render
nodeExecutorthe four slide nodes together — they are independent of each other.getNodeOutputeach and confirmslide_htmlis non-empty. If one is empty, re-run that node alone; do not compose a deck with a hole in it.nodeExecutor"Deck Slides", then "Compose Deck", then "Export PPTX", in that order.getNodeOutput"Compose Deck" and spot-check that the figures in the HTML matchfiguresfrom step 5. The slide generators are instructed to copy numbers verbatim, and this is where that gets verified rather than assumed.
Phase 3 complete. Now proceed to Phase 4.
Phase 4: Deliver
- Call
requestUserDecisionONCE: "Save the deck to Drive?",allowFreeText: false, optionssave/skip. - If
save: callselectDriveItem(type=folder), thenupsertNode"Save Deck"{ destinationFolder: <picked id>, name: "business-review-<runDate>.pptx" }, thennodeExecutorit. Never invent a folder id and never reuse the empty default. Ifskiporcancelled: true, write nothing. - Call
verificationwith the headline figures and whether the deck was saved. ThenfollowUpoffering: (a) a monthly schedule, (b) re-run for a different period, (c) drill into one course in the console.
Configuration Tips
Error Handling
// Dependencies
1from input import String2from process import CustomCode3from process import OntologyAggregate4from process import OdooRPC5from process import SlideGenerator6from process import MakeArray7from output import PresentationComposer8from output import PPTXConverter9from output import SaveFile// Variables
1runDate:2 type: undefined3 label: "Run date"4 description: "ISO date the reporting window ends on."5 required: undefined67periodMonths:8 type: undefined9 label: "Period (months)"10 description: "How many months the review covers."11 required: undefined$ git log --oneline