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'sidfield. - 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
WorkflowDeftype. - 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:
idmatches^[a-z0-9][a-z0-9-]*$.nameis a non-empty string.stepsis non-empty.- Each step has a unique id, sets exactly one of
fnoroperationId, and provides alabelifoperationIdis unset. - Every
dependsOnentry 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
| Change | Action |
|---|---|
| Edit a workflow file | POST /api/loom/reload — picks up edits without process restart. |
| Edit an operation file | Same reload re-imports operations first, then workflows (which cross-validate against ops). |
| Add a new workflow file | Reload — new file is picked up by the directory scan. |
| Remove a workflow file | Reload — registry is cleared and re-built from disk. |
Edit WorkflowDef types or the runner | Process restart — these are not hot-reloaded. |