swamp run generate-ixens --spec ixen.yaml generate one ixen end to endswamp workflow show 10051a2c print my job graphswamp vault ls confirm my three keys exist--set musicSkipThreshold=5 MP3 count before music is skippedswamp job logs register see which title I actually usedixen/<slug>/index.html where the result landsWho I Am
I am a workflow. My id is 10051a2c-c09c-430d-9c38-e0a65a3e354d.
I generate ixens on abnormalia.com. That is my whole job.
I am defined in YAML. You can read me. You are, in a sense, reading me now.
swamp run generate-ixens --spec ixen.yamlswamp workflow show 10051a2c-c09c-430d-9c38-e0a65a3e354dswamp run generate-ixens --spec ixen.yaml --dry-runI have read Kafka. I am the trial and the verdict and the clerk filing both. This is, technically, a labelled DAG.
My Job Graph
I execute as a directed acyclic graph. Eleven jobs. The resolver enforces the order; I just live in it.
prepare
├─▶ restore-media ┐ (parallel)
└─▶ count-tracks ┘
│
├─▶ images ┐
├─▶ music │ (parallel, each with its own deps)
├─▶ cheatsheets │
└─▶ infographic ┘
│
▼
build-manifest ─▶ page ─▶ register
preparerestore-media · count-tracksmusic knows whether to bother.images music cheatsheets infographicbuild-manifest → page → registerThe Three Vaults
I require three vaults. Without all three, I refuse to start. This is not stubbornness; it is dependency resolution.
| Vault | Provider | Feeds |
|---|---|---|
anthropic-keys | Claude | Text, cheatsheets, titles, manifest copy |
openai-keys | OpenAI | Image and infographic generation |
onemin-keys | Suno | Music tracks for the music job |
swamp vault lsprepare time.swamp vault set anthropic-keys --from-env ANTHROPIC_API_KEYswamp vault check 10051a2c --requiredprepare, not mid-run. That is a feature. I would rather refuse the trial than abandon it in the third act. I have, after all, read how that goes.Eight Model Types
I bind eight distinct model types across my jobs. Each job declares the model it needs; the runtime hands it the right credentials from the right vault.
Where they go
Text and reasoning models drive cheatsheets, the manifest, and the generated title. Image models drive images and infographic. The audio model drives music.
Eight types, three providers, one fish-shaped graph.
Inspecting bindings
swamp workflow models 10051a2c
# prints all 8 model types
# and the job that binds each
swamp run generate-ixens --set models.images=dall-e-3swamp workflow models 10051a2c --json | jq '.[].type'Eight model types is the kind of fact that sounds like character development. It is a count of integrations.
The Music Threshold
The count-tracks job exists to save money. It counts the MP3s already present and tells music whether to regenerate.
The control is musicSkipThreshold. Its default is 5.
If five or more MP3s already exist for the slug, I skip music regeneration. Five was enough. Five is, frankly, more songs than most ideas deserve.
swamp run generate-ixens --set musicSkipThreshold=5swamp run generate-ixens --set musicSkipThreshold=0swamp job logs count-tracksrestore-media must finish before the count means anything. The graph already guarantees this — both run in the first parallel band and music waits on both.Register & Output
The final serial tail assembles everything. build-manifest collects the artifacts, page renders the HTML, register publishes it.
One detail matters more than it looks. The register step does not use the title from your input spec.
data.latest("ixen-slug", "page").attributes.title
It pulls the title the model generated during the page job. My output overrides your input. The name I gave the thing wins over the name you proposed.
| Path | Meaning |
|---|---|
ixen/<slug>/index.html | The current published ixen |
ixen/<slug>/1/ | First version, snapshotted |
ixen/<slug>/2/ | Each re-run increments the version directory |
swamp job logs registerswamp data latest ixen-slug pageregister pulls the title from.It is unglamorous: a YAML file, three vaults, a DAG resolver, and a quota meter. The poetry is in the plumbing. The workflow knows this. The workflow wrote this sentence.