Workflow Library
Loom workflows ("strands") are TypeScript modules under optimalOS/workflows/*.ts that each export a workflow: WorkflowDef. The loader (src/loom/loader.ts) scans this directory plus ~/.optimalos/workflows/ at startup and on every POST /api/loom/reload. Each strand becomes visible in the Loom UI, runnable manually via POST /api/loom/strands/:id/run, and — if it has a schedule and enabled: true — fires automatically via the in-process scheduler on the hub node.
This page catalogues every strand currently shipped with OptimalOS. The shape of a WorkflowDef, step DAG semantics, runner, scheduler, and HTTP/WS API surface live at /loom/authoring — read that first if you're authoring a new strand.
Status legend
- live —
enabled: true, wired to real code, fires on its trigger - seed — file exists with a schema-only DAG, every step throws
not yet wired; reserved slot in the sculpture for future migration
The seed strands are intentional: they load into the registry so the Loom UI's strand topology stays stable while the underlying logic is being migrated from n8n / optimal-cli / Strapi lifecycle hooks.
Operational strands (live)
These run today and produce real side effects.
docs-drift-check
- Purpose: Walks the audit JSONs at
~/.optimalos/transfers/docs-audit/*.json, stats every cited evidence path, and counts files modified since each JSON'saudited_at. Surfaces drift betweendocs.optimal.miamiand the code those pages cite. - Schedule:
0 */6 * * *(every 6h) - Target: Pi (
oracle) — audit JSONs live there - Output: Non-fatal
ok:truestep result withdata.threshold_exceededflag readable by the Loom dashboard. No external notification.
instance-gc
- Purpose: Deletes rows from Supabase
instanceswhoselast_heartbeatis older than 24h. Keeps the cross-node presence tracker from accumulating dead hosts. - Schedule:
0 */6 * * *(every 6h) - Target: Pi (
oracle) - Output: Soft-fails to
ok:falsewhenSUPABASE_URL/ service-role key are missing instead of crashing the runner. Reference:docs/superpowers/specs/2026-04-23-launch-integration-design.md.
openclaw-cron-mirror
- Purpose: Mirrors OpenClaw cron jobs into the Loom registry as synthetic strands (id prefix
openclaw-cron-). Each openclaw firing writesloom_runs+loom_step_runsrows with synthesized EXEC and (when applicable) DELIVER steps. Two-way enable/disable shells out toopenclaw cron enable|disable. - Schedule:
*/5 * * * *(every 5 min) - Target: Any (
defaultHost: null) - Output: Writes synthetic strands + run rows to the local SQLite. Idempotent via
INSERT OR IGNOREonsessionId. Full design at /loom/openclaw-cron-mirror.
priority-triage
- Purpose: One reprioritization pass over open kanban tasks. Snapshots tasks + briefs + memories + overrides, builds a prompt, calls Ollama (
/v1/chat/completions), enforces lane caps + arithmetic, writesscore_history+ denormalized task columns in a transaction, marks briefs consumed, applies override acks, emits a run summary. - Schedule:
null— manual trigger from the Loom UI or HTTP API - Target: Pi (
oracle) - Output: Updated kanban scores + lanes in OptimalOS Supabase, plus a
run_summary/distribution_warning/clash_summarypayload. Full charter at /loom/priority-triage.
research-pipeline
- Purpose: End-to-end research-and-publish flow. Each step does an authenticated localhost POST to
/api/plugins/research/*so the existing route handlers do the real work:scrape(fetch active sources, dedupe, store incontent_scraped_items), classify/dedupe/store (no-ops today, embedded in the scrape handler),generate(call LLM, write Strapi drafts),review(no-op gate, Strapi Publish is the human authorization),publish(/drafts/publish-all). - Schedule:
0 */2 * * *(every 2h) — enabled 2026-04-23 after the 8/8-step smoke run - Target: Pi (
oracle) - Output: New Strapi drafts (rate-limited to 5 per cycle). Carlos approves manually in the Strapi admin; downstream lifecycle hooks then publish to socials.
Operational strands (manual / disabled by default)
cross-node-smoke
- Purpose: Phase 9 validation. Three steps:
pi-ping(local),popos-ping(hostHint: pop-os, exercises theloom_job_queueround-trip),pi-wrapup(local, depends on popos-ping). Proves Pi → pop-os → Pi dispatch works. - Schedule:
null— manual - Target: Pi + pop-os (mixed;
hostHintper step) - Output:
loom_runs.status='success'with steps on the expected hosts and aloom_job_queue.claimed_by='pop-os'row. Disabled by default — flip on once pop-os hasfeatures.remoteWorker = trueand both nodes share a Supabase service-role key.
popos-deploy
- Purpose: SSH-driven deploy to the pop-os worker:
git pull+pnpm install+pnpm build, enablefeatures.remoteWorkerin~/.optimalos/config.json,sudo systemctl restart optimalos.service(NOPASSWD required). - Schedule:
null— manual - Target: pop-os (via SSH from Pi using
~/.ssh/id_popos-deploy) - Output: Restarted
optimalos.serviceon pop-os. Requires one-time bootstrap: deploy key in~/.ssh/, matching pubkey in pop-osauthorized_keys, and a/etc/sudoers.d/optimalos-restartallowing NOPASSWD restart.
Seed strands (schema-only)
Every step in these workflows throws not yet wired (or similar). They reserve the slot in the Loom sculpture so the strand topology is stable while the underlying logic migrates over. Listed alphabetically.
backup-runner
- Purpose: Nightly SQLite + config snapshots to external storage (rclone → Google Drive).
- Schedule:
0 3 * * *daily at 03:00 (when enabled) - Target: Pi (
oracle) - Output (planned): Snapshot → compress →
storage.upload-file→ verify → prune old. No notification step yet.
discord-watcher
- Purpose: Long-running listener on the Optimal Discord guild — routes events to agents.
- Schedule:
null—triggerType: "manual"; the Discord service starts it at boot - Target: Pi (
oracle) - Output (planned): connect → watch → route. Not yet wired.
fpa-validator
- Purpose: Validate FY27 budget scenario uploads for completeness, variance against actuals, and sign conventions.
- Schedule:
null—triggerType: "webhook"(kicked by upload) - Target: Pi (
oracle); requireshas-returnpro-credscapability - Output (planned): Discord webhook report on completeness / variance / sign issues.
kanban-sync
- Purpose: Two-way sync between local SQLite tasks and the Supabase board, then broadcast events to subscribers.
- Schedule:
*/5 * * * *(every 5 min, when enabled) - Target: Pi (
oracle) - Output (planned): Supabase reads + writes, then broadcast step.
newsletter-digest
- Purpose: Weekly digest assembled from
research_items, rendered to HTML, queued for distribution. To be migrated from n8n. - Schedule:
0 14 * * 1(Mondays 14:00) - Target: Pi (
oracle) - Output (planned): Strapi draft + Discord notification.
returnpro-audit
- Purpose: Daily reconciliation of
stg_financials_rawvsincome_statements_confirmedfor the trailing month. To be migrated fromoptimal audit-financials. - Schedule:
0 0 * * *(midnight) - Target: Pi (
oracle); requireshas-returnpro-creds - Output (planned): Discord webhook with per-month accuracy table.
scrape-monitor
- Purpose: Watchdog for research scrapers — heartbeat, alert on stalled sources, record per-source success rates.
- Schedule:
*/15 * * * *(every 15 min, when enabled) - Target: Pi (
oracle) - Output (planned): Discord alert when a source goes stale.
snowflake-ingest
- Purpose: Source-to-warehouse ETL into
RETURNPRO_DW.ANALYTICS. Extracts from Supabase + ReturnPro APIs, stages, transforms, loads dim/fact tables. - Schedule:
0 */6 * * *(every 6h, when enabled) - Target: Pi (
oracle); requireshas-snowflake-creds - Output (planned): Snowflake
dw.snowflake-mergecalls + validation step.
social-publisher
- Purpose: Push approved Strapi drafts to X / Instagram / Facebook. Triggered by a Strapi lifecycle hook (
afterUpdatewithstatus=published). - Schedule:
null—triggerType: "webhook" - Target: Pi (
oracle) - Output (planned): Per-platform render + post steps. The Strapi lifecycle path currently bypasses Loom and posts directly via OpenRouter; this strand reserves the slot for when that logic moves into the Loom runner.
Authoring a new strand
See /loom/authoring for the full WorkflowDef field reference, the StepContext API (logger, secrets, previous, signal), the loader's two-root precedence (~/.optimalos/workflows/ wins on id collision), and how the scheduler / runner / WebSocket event hub fit together. A minimal example lives there as well.
Quick checklist for adding a new strand:
- Create
optimalOS/workflows/<kebab-id>.tsexportingworkflow: WorkflowDef. - Validate locally —
pnpm tsc --noEmitplus a manualPOST /api/loom/strands/:id/run. - Decide
enabledcarefully. Seed strands shipenabled: false. Cron strands flip totrueonly after a clean manual smoke run. - If the strand cites code paths, add an audit JSON entry under
~/.optimalos/transfers/docs-audit/sodocs-drift-checkcan keep this page honest.