absurd

Installation
SKILL.md

Absurd

Use this skill when the project uses Absurd, the Postgres-native durable workflow engine, or when the user mentions absurdctl, queues, durable tasks, runs, retries, sleeping tasks, or events.

Tiny mental model

  • A queue is a namespace of Absurd tables (t_, r_, c_, e_, w_).
  • A task is the durable workflow instance.
  • A run is one execution attempt of a task.
  • A step is a checkpoint. Completed step results are stored as JSON.
  • Sleeping tasks are usually waiting for time or an event.
  • Events wake waiting tasks. Event payloads are cached; first emit wins.

Important distinction:

  • task_id = the whole workflow across all attempts
  • run_id = one specific execution attempt

First: make sure absurdctl works

If absurdctl is not on PATH, check whether you are inside a repo checkout and, if so, use:

export PATH="$PWD:$PATH"

Absurd connection precedence is:

--database > ABSURD_DATABASE_URL > PGDATABASE > postgresql://localhost/absurd

For non-URI connections, PGHOST, PGPORT, PGUSER, and PGPASSWORD are also honored.

Default debugging workflow

Prefer absurdctl state inspection before source inspection. Usually you do not need to read application code first.

If the user explicitly asks you to use absurdctl to inspect or fix a workflow, do that first instead of starting with rg / source browsing.

When the user wants to debug a task, start with these commands in order:

1) Discover queues

absurdctl list-queues

2) Inspect recent activity in the likely queue

absurdctl list-tasks --queue=default --limit=20

Notes:

  • list-tasks defaults to 50 rows if --limit is omitted.
  • Useful statuses: pending, running, sleeping, completed, failed, cancelled.

3) Focus on failures or sleepers

absurdctl list-tasks --queue=default --status=failed --limit=20
absurdctl list-tasks --queue=default --status=sleeping --limit=20

4) Inspect one workflow or one attempt in detail

absurdctl dump-task --task-id=<task-id>
absurdctl dump-task --run-id=<run-id>

dump-task is the most important inspection command. It shows things like:

  • task name, params, and headers
  • retry settings and attempts
  • checkpointed step state
  • waits / events / sleep state
  • final result or failure

How to reason about common states

If a task is failed

  1. dump-task --task-id=<task-id>
  2. Read the failure and the last successful checkpoints.
  3. If needed, inspect the most recent attempt with --run-id.
  4. Search the code for the task implementation by task name.
  5. Only then decide whether to retry.

If a task is sleeping

  1. dump-task --task-id=<task-id>
  2. Look for the wait reason:
    • sleeping until a timestamp
    • waiting for an event name
  3. If it is waiting for an event and the user wants it resumed, emit that event.

If a task is running

  1. dump-task --task-id=<task-id>
  2. Look at existing checkpoints to see how far it got.
  3. If the user suspects a stuck worker, inspect the worker process / application logs too.

About workers

Do not assume you need to start or modify a worker.

  • If a spawned task moves from pending to sleeping, running, or completed, a worker is already active.
  • If tasks remain pending, then investigate whether a worker for that queue is actually running.
  • Only inspect Python / TypeScript runtime details when the task state suggests a worker problem or the user asks for code changes.

If you need the implementation

After you know the task name, search the codebase for its registration.

TypeScript / JavaScript:

rg -n "registerTask\(|name:\s*['\"]<task-name>['\"]" .

Python:

rg -n "register_task\(|@.*register_task|['\"]<task-name>['\"]" .

If the task is waiting for an event, also search for the event name.

Common actions

Spawn work

Use -P key=value for strings and -P key:=json for typed JSON values.

absurdctl spawn-task my-task -q default -P foo=bar
absurdctl spawn-task my-task -q default -P count:=42 -P enabled:=true
absurdctl spawn-task my-task -q default -P user.name=Alice -P user.age:=30

Use --params when the user already has a JSON object:

absurdctl spawn-task my-task -q default --params '{"foo":"bar","count":42}'

Retry failed work

absurdctl retry-task <task-id>
absurdctl retry-task <task-id> --max-attempts 5
absurdctl retry-task -q default <task-id> --spawn-new

Guidance:

  • plain retry-task retries the existing task
  • --spawn-new creates a brand-new task with the original inputs
  • prefer understanding the failure before retrying

Cancel work

absurdctl cancel-task <task-id>
absurdctl cancel-task -q default <task-id>

Wake waiting tasks by emitting an event

absurdctl emit-event order.completed -q default -P orderId=123
absurdctl emit-event approval.granted:42 -q default -P approved:=true

If the event payload should be structured JSON:

absurdctl emit-event shipment.packed:42 -q default --payload '{"trackingNumber":"XYZ"}'

Schema setup / migrations

Use these on a blank or controlled database, or when the user explicitly asks:

absurdctl init
absurdctl schema-version
absurdctl migrate
absurdctl create-queue default

Safe operating rules

Be careful with state-changing commands. Unless the user clearly wants them, avoid running these blindly on a shared or production database:

  • init
  • migrate
  • create-queue
  • drop-queue
  • cleanup
  • cancel-task
  • retry-task
  • emit-event
  • spawn-task

If the environment is ambiguous, ask which database / queue is safe to operate on.

Good copy-paste playbooks

Debug the latest failures in default

absurdctl list-queues
absurdctl list-tasks --queue=default --status=failed --limit=20
absurdctl dump-task --task-id=<task-id>

Find sleepers and wake one

absurdctl list-tasks --queue=default --status=sleeping --limit=20
absurdctl dump-task --task-id=<task-id>
absurdctl emit-event <event-name> -q default -P key=value

Reproduce by spawning a task, then inspect it

absurdctl spawn-task my-task -q default -P foo=bar
absurdctl list-tasks --queue=default --task-name=my-task --limit=5
absurdctl dump-task --task-id=<task-id>

Fast path when the user says “spawn a task and debug it”:

absurdctl spawn-task my-task -q default -P foo=bar
absurdctl list-tasks --queue=default --task-name=my-task --limit=5
absurdctl dump-task --task-id=<task-id>
# then either:
absurdctl emit-event <event-name> -q default -P key=value
# or:
absurdctl retry-task <task-id>

Extra reference

  • Use absurdctl <command> --help for full options.
  • dump-task --task-id is usually the best starting point once you know the task.
  • Checkpointed step results are durable JSON state; code outside steps may execute multiple times across retries.
Installs
19
GitHub Stars
1.7K
First Seen
Apr 8, 2026