dev-task-queue

SKILL.md

Agent Task Queue

Persistent, cross-session task queue for AI agents aligned with the Claude Code Tasks schema. Tasks survive beyond sessions, carry full context including conversation transcripts, and support dependency tracking with blocks/blockedBy. Backed by a git repo with move-based locking for conflict-free multi-agent access.

Prerequisites

Clone the task queue repository:

git clone git@github.com:OlechowskiMichal/agent-task-queue.git ~/.agent-task-queue

All commands below assume the repo is at ~/.agent-task-queue.

Quick Reference

Operation Command Description
Add python3 ~/.agent-task-queue/scripts/add_task.py Create a new task
List python3 ~/.agent-task-queue/scripts/list_tasks.py List/filter tasks
Claim python3 ~/.agent-task-queue/scripts/claim_task.py Claim a pending task
Complete python3 ~/.agent-task-queue/scripts/complete_task.py Mark task done
Release python3 ~/.agent-task-queue/scripts/release_task.py Unclaim a task
Reassign python3 ~/.agent-task-queue/scripts/reassign_task.py Reassign to different agent
Index python3 ~/.agent-task-queue/scripts/render_index.py Regenerate root index
Prune python3 ~/.agent-task-queue/scripts/prune_completed.py Archive old tasks

Adding a Task

Save a task for a future agent session:

python3 ~/.agent-task-queue/scripts/add_task.py \
  --assignee go-expert \
  --subject "Fix error handling in API middleware" \
  --description "The error handler swallows context. Wrap errors with fmt.Errorf." \
  --priority high \
  --project my-api \
  --tags "error-handling,api" \
  --active-form "Fixing error handling" \
  --ttl-hours 48 \
  --blocked-by "3,7" \
  --context-repo "git@github.com:OlechowskiMichal/my-api.git" \
  --context-files "pkg/middleware/error.go,pkg/middleware/error_test.go" \
  --context-notes "See issue #42 for the original bug report"

--assignee is optional and defaults to general-purpose. Omit it for tasks that any agent can pick up.

When --project matches an entry in projects.json (see Project Registry), --context-repo is auto-filled from the registry. You can still pass --context-repo explicitly to override. If --project does not match any known project, the script warns and suggests close matches.

The script reads .highwatermark to assign the next monotonic integer ID, writes the task JSON to pending/{id}.json, regenerates the root index, and pushes to the repo.

Multi-line descriptions

For descriptions with # characters or complex formatting (which break zsh heredocs), write to a temp file and use --description-file:

cat > /tmp/task-desc.md << 'EOF'
## Problem
The error handler swallows context.

### Acceptance criteria
- Wrap errors with fmt.Errorf
- Add retry logic for transient failures
EOF

python3 ~/.agent-task-queue/scripts/add_task.py \
  --assignee go-expert \
  --subject "Fix error handling in API middleware" \
  --description-file /tmp/task-desc.md \
  --priority high

--description-file overrides --description when both are provided.

Listing Tasks

# All pending tasks for an agent
python3 ~/.agent-task-queue/scripts/list_tasks.py --agent go-expert --status pending

# Stale tasks (claimed but exceeded TTL)
python3 ~/.agent-task-queue/scripts/list_tasks.py --stale

# Blocked tasks (unmet dependencies)
python3 ~/.agent-task-queue/scripts/list_tasks.py --blocked

# Tasks for a specific project
python3 ~/.agent-task-queue/scripts/list_tasks.py --project my-api

# Tasks created after a date
python3 ~/.agent-task-queue/scripts/list_tasks.py --since 2026-03-01

# List all known projects and their repos (from projects.json)
python3 ~/.agent-task-queue/scripts/list_tasks.py --list-projects

Claiming a Task

Start working on a pending task:

python3 ~/.agent-task-queue/scripts/claim_task.py 1 --session-id $(uuidgen)

The script:

  1. Verifies all blockedBy dependencies are completed
  2. Moves the task from pending/ to active/
  3. Sets owner and metadata.claimed_at
  4. Updates status to in_progress
  5. Pushes with conflict detection — if another agent claimed it first, the push fails

Completing a Task

Always link the conversation transcript when completing a task. Find the transcript first, then pass it via --jsonl-path:

# 1. Find this session's transcript (most recently modified .jsonl)
JSONL_PATH=$(ls -t ~/.claude/projects/*/*.jsonl 2>/dev/null | head -1)

# 2. Complete the task with transcript linked
python3 ~/.agent-task-queue/scripts/complete_task.py 1 \
  --session-id "$(uuidgen)" \
  --jsonl-path "$JSONL_PATH" \
  --notes "Fixed by wrapping errors with context in middleware chain"

The task moves to completed/YYYY-MM/{id}.json with the conversation transcript exported and linked.

Releasing a Task

If you cannot finish a claimed task:

python3 ~/.agent-task-queue/scripts/release_task.py 1

Moves back to pending/ with metadata.previously_claimed=true and status reset to pending so the next agent knows it was attempted.

Reassigning a Task

Transfer a task to a different agent type:

python3 ~/.agent-task-queue/scripts/reassign_task.py 1 --to re-expert

This is a metadata-only update — it sets metadata.assignee to the new agent. No file move occurs. The task stays in its current directory (pending/ or active/).

Dependency Management

Tasks support bidirectional dependency tracking via blocks and blockedBy fields, aligned with the Claude Code Tasks schema.

  • blockedBy: Array of task IDs that must be completed before this task can be claimed. claim_task.py enforces this — it refuses to claim a task with unresolved blockedBy entries.
  • blocks: Array of task IDs that are waiting on this task. When a task is completed, the system can check whether downstream tasks are now unblocked.

Both sides are kept in sync: adding task 5 to the blockedBy of task 8 also adds task 8 to the blocks of task 5.

Cycle detection runs at add time and at claim time. If adding a dependency would create a circular chain (e.g., A blocks B blocks A), the operation is rejected with an error.

# Add a task that depends on tasks 3 and 7
python3 ~/.agent-task-queue/scripts/add_task.py \
  --subject "Deploy after migrations" \
  --blocked-by "3,7"

# List all currently blocked tasks
python3 ~/.agent-task-queue/scripts/list_tasks.py --blocked

Project Registry

The projects.json file at the repo root maps project names to their canonical repository URLs:

{
  "my-api": "git@github.com:OlechowskiMichal/my-api.git",
  "agent-skills": "git@github.com:OlechowskiMichal/agent-skills.git"
}

The registry enables two behaviors:

  1. Auto-fill --context-repo: When add_task.py receives --project my-api and my-api is in the registry, --context-repo is set automatically. An explicit --context-repo overrides the registry value.
  2. Unknown-project warnings: If --project does not match any registry entry, the script prints a warning with close-match suggestions (via difflib.get_close_matches). The task is still created — the warning is advisory.

List all registered projects with --list-projects:

python3 ~/.agent-task-queue/scripts/list_tasks.py --list-projects

Output:

Project                    | Repository
---------------------------+---------------------------------------------------------------
my-api                     | git@github.com:OlechowskiMichal/my-api.git
agent-skills              | git@github.com:OlechowskiMichal/agent-skills.git

Total: 2 project(s)

Race Condition Handling

All scripts use push-reject, pull-rebase, retry (once). If a claim push is rejected after rebase, it means another agent already claimed the task. The script exits with an error message.

Conversation Linking

Link Claude Code conversation transcripts to tasks when completing. The transcript is exported to markdown alongside the completed task JSON and referenced in metadata.conversations.

Finding Your Transcript

Claude Code writes session transcripts to ~/.claude/projects/<project-hash>/<session-id>.jsonl. The project hash is derived from the working directory (path with / and . replaced by -).

Quick method — find the most recently modified .jsonl (works when you are the only active session):

JSONL_PATH=$(ls -t ~/.claude/projects/*/*.jsonl 2>/dev/null | head -1)

Targeted method — when multiple agents run concurrently, scope to your project directory:

PROJECT_HASH=$(pwd | tr '/.' '-')
JSONL_PATH=$(ls -t "$HOME/.claude/projects/${PROJECT_HASH}"/*.jsonl 2>/dev/null | head -1)

Passing the Transcript

Always pass --jsonl-path when completing a task:

python3 ~/.agent-task-queue/scripts/complete_task.py 1 \
  --jsonl-path "$JSONL_PATH" \
  --notes "Fixed the issue"
  • The transcript is exported to completed/YYYY-MM/{id}-transcript.md
  • The path is stored in the task's metadata.conversations array
  • Multiple sessions can contribute to the same task

Staleness Detection

Tasks have a metadata.ttl_hours field (default: 24). A claimed task is stale when metadata.claimed_at + ttl_hours < now. Find stale tasks with --stale flag. Release stale tasks manually with release_task.py.

Pruning Completed Tasks

Remove old completed tasks:

# Dry run — show what would be deleted
python3 ~/.agent-task-queue/scripts/prune_completed.py --older-than 90d

# Actually delete
python3 ~/.agent-task-queue/scripts/prune_completed.py --older-than 90d --execute

Task Schema

See references/task-schema.md for the full JSON schema with field descriptions.

Directory Layout

agent-task-queue/
├── .highwatermark              # Monotonic ID counter (single integer)
├── pending/                    # Flat — no agent subdirs
│   └── {id}.json
├── active/
│   └── {id}.json
├── completed/
│   └── YYYY-MM/
│       └── {id}.json
├── projects.json               # Project-to-repo registry
├── index.md                    # Single root index
├── scripts/
│   ├── _lib.py
│   ├── add_task.py
│   ├── claim_task.py
│   ├── complete_task.py
│   ├── list_tasks.py
│   ├── release_task.py
│   ├── reassign_task.py
│   ├── render_index.py
│   └── prune_completed.py
├── AGENTS.md
├── CLAUDE.md
├── TODO.md
└── README.md
Weekly Installs
2
First Seen
2 days ago
Installed on
claude-code2
mcpjam1
kilo1
junie1
windsurf1
zencoder1