Skip to content

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

  • liveenabled: 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's audited_at. Surfaces drift between docs.optimal.miami and the code those pages cite.
  • Schedule: 0 */6 * * * (every 6h)
  • Target: Pi (oracle) — audit JSONs live there
  • Output: Non-fatal ok:true step result with data.threshold_exceeded flag readable by the Loom dashboard. No external notification.

instance-gc

  • Purpose: Deletes rows from Supabase instances whose last_heartbeat is 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:false when SUPABASE_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 writes loom_runs + loom_step_runs rows with synthesized EXEC and (when applicable) DELIVER steps. Two-way enable/disable shells out to openclaw 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 IGNORE on sessionId. 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, writes score_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_summary payload. 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 in content_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 the loom_job_queue round-trip), pi-wrapup (local, depends on popos-ping). Proves Pi → pop-os → Pi dispatch works.
  • Schedule: null — manual
  • Target: Pi + pop-os (mixed; hostHint per step)
  • Output: loom_runs.status='success' with steps on the expected hosts and a loom_job_queue.claimed_by='pop-os' row. Disabled by default — flip on once pop-os has features.remoteWorker = true and 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, enable features.remoteWorker in ~/.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.service on pop-os. Requires one-time bootstrap: deploy key in ~/.ssh/, matching pubkey in pop-os authorized_keys, and a /etc/sudoers.d/optimalos-restart allowing 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: nulltriggerType: "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: nulltriggerType: "webhook" (kicked by upload)
  • Target: Pi (oracle); requires has-returnpro-creds capability
  • 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_raw vs income_statements_confirmed for the trailing month. To be migrated from optimal audit-financials.
  • Schedule: 0 0 * * * (midnight)
  • Target: Pi (oracle); requires has-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); requires has-snowflake-creds
  • Output (planned): Snowflake dw.snowflake-merge calls + validation step.

social-publisher

  • Purpose: Push approved Strapi drafts to X / Instagram / Facebook. Triggered by a Strapi lifecycle hook (afterUpdate with status=published).
  • Schedule: nulltriggerType: "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:

  1. Create optimalOS/workflows/<kebab-id>.ts exporting workflow: WorkflowDef.
  2. Validate locally — pnpm tsc --noEmit plus a manual POST /api/loom/strands/:id/run.
  3. Decide enabled carefully. Seed strands ship enabled: false. Cron strands flip to true only after a clean manual smoke run.
  4. If the strand cites code paths, add an audit JSON entry under ~/.optimalos/transfers/docs-audit/ so docs-drift-check can keep this page honest.

Built by Carlos Lenis in Miami