Skip to content

Authoring a Strand

Loom does not have a drag-drop builder. Strands are TypeScript modules. The creation flow is agent-first: you describe what the workflow should do, click the launcher, and Claude Code (or your configured agent) writes the file.

The [+ NEW STRAND] button

In the Loom tab, the strand sidebar surfaces a [+ NEW STRAND] action that opens a modal. The modal asks for:

  • Strand id — kebab-case, becomes the filename (workflows/<id>.ts) and the workflow's id field.
  • Display name — human-readable.
  • Trigger — cron expression (with shortcut presets like "every 15m") or manual.
  • Required capabilities — capability tags.
  • Description — what should this thing do, in plain English.

On submit, the modal hits POST /api/loom/scaffold which writes a stub workflow file to ~/.optimalos/workflows/<id>.ts and returns the path. The Command Center launcher then spawns an agent session in a terminal tab with a scaffold prompt that includes:

  • The path of the stub.
  • The user's description.
  • Pointers to the operations registry and the WorkflowDef type.
  • Instructions to iterate locally until runWorkflow(<id>, "manual") returns success.

Reload after the agent edits

The scaffold endpoint triggers /api/loom/reload once the file is written. Subsequent agent edits during the session also trigger reload through file-watching.

The loader cache-busts the dynamic import() with a ?t=<timestamp> query so the second load actually picks up edits — without the nonce, Bun's ES module cache returns the original module. See optimalOS/src/loom/loader.ts:64-72 for the implementation.

Where the file lives

User-authored workflows go to ~/.optimalos/workflows/<id>.ts. Built-in workflows ship in optimalOS/workflows/ and are not touched by the scaffold flow.

The user-root wins on id collision, which makes it easy to override a shipped workflow temporarily.

Validation

validateWorkflowDef() (in optimalOS/src/loom/types.ts:169-233) checks:

  • id matches ^[a-z0-9][a-z0-9-]*$.
  • name is a non-empty string.
  • steps is non-empty.
  • Each step has a unique id, sets exactly one of fn or operationId, and provides a label if operationId is unset.
  • Every dependsOn entry references a real step id.

Operation cross-references are validated separately by the loader (optimalOS/src/loom/loader.ts:96-104). Unknown operationIds surface as workflow validationErrors rather than runtime crashes.

A workflow with validation errors stays in the registry so the Loom UI can show what went wrong instead of silently dropping the strand.

Debugging a failed step

When a step ends in failed status, the Loom detail panel surfaces a [DEBUG WITH AGENT] action.

Launcher integration

The agent-launcher integration that spawns a debug session with the failure context pre-loaded is described in optimalOS/docs/superpowers/specs/2026-04-23-launch-integration-design.md. Verify the current state in optimalOS/src/orchestration/launcher.ts before relying on it. The persistent-red ack endpoint (POST /api/loom/runs/:runId/steps/:stepId/ack) is shipped; the agent-spawn glue is still being wired through the launcher.

What gets reloaded vs restarted

ChangeAction
Edit a workflow filePOST /api/loom/reload — picks up edits without process restart.
Edit an operation fileSame reload re-imports operations first, then workflows (which cross-validate against ops).
Add a new workflow fileReload — new file is picked up by the directory scan.
Remove a workflow fileReload — registry is cleared and re-built from disk.
Edit WorkflowDef types or the runnerProcess restart — these are not hot-reloaded.

Built by Carlos Lenis in Miami