aicp-ops-dlq
Inspect or retry the AICP dead-letter queue (~/.aicp/dlq/<UTC-date>.jsonl) via the CLI surface. Replaces the deprecated aicp_dlq_status MCP tool per `wiki/decisions/00_inbox/aicp-mcp-tool-surface-audi
SKILL.md.claude/skills/aicp-ops-dlq/SKILL.md
GitHubaicp-ops-dlq
Operate on the AICP Dead-Letter Queue via the aicp CLI. AICP's reliability
stack (circuit breaker → failover chain → DLQ — see
wiki/patterns/01_drafts/per-day-jsonl-dlq-with-retry-budget.md) persists
failed tasks that exhausted the failover chain. This skill teaches the
inspection and retry workflow using the CLI surface, NOT the deprecated
aicp_dlq_status MCP tool.
Trigger phrases (when to load this skill)
Load when the conversation matches any of:
- Direct verb: operator says "what's in the DLQ", "show pending failures", "retry the queue", "did anything get DLQ'd today", "DLQ growing", "clear out the queue", "anything stuck in DLQ"
- Post-incident: backend outage just resolved → check DLQ for tasks that failed during the outage and retry them
- Pre-cleanup: before archiving old DLQ files, list what's pending so the operator confirms it's safe to discard
- Reliability audit: monitor DLQ growth rate over time as a signal of upstream backend health
Do NOT load when:
- The concern is "why did this task fail in the first place" (load
systematic-debugging— the DLQ entry has the error string, but root cause analysis lives elsewhere) - The concern is monitoring/alerting setup (load
infra-monitoringto add a DLQ-growth alert) - The concern is the underlying DLQ implementation (the
per-day-jsonl-dlq-with-retry-budgetpattern doc covers it; this skill is about USING the queue, not changing its design)
Operations
Operation 1 — Inspect pending entries
When: operator wants to see what's in the queue without modifying it.
Process:
- Run
aicp --dlq-status - Output shows
Dead-Letter Queue: N pending entriesplus up to the first 10 entries (timestamp, mode, truncated error string) - The output's closing
NEXT:line will recommendaicp --retry-dlqif entries are present — follow that recommendation only if the operator has confirmed the upstream cause is fixed - If
N > 10, the output indicates how many additional entries exist; full inspection requires reading the JSONL files directly:ls -la ~/.aicp/dlq/thencat ~/.aicp/dlq/<UTC-date>.jsonl | jq
Quality bar: never retry without confirming the upstream cause is fixed — otherwise retries fail again, increment retry_count, and exhaust the retry budget on already-failing entries.
Operation 2 — Retry pending entries
When: upstream backend was down, has recovered, and operator wants to process the failed tasks.
Process:
- Confirm with operator that the upstream cause is fixed (e.g., LocalAI is
back up — verify with
aicp --check) - Run
aicp --retry-dlq - Output shows
Retrying N pending DLQ entries...thenRetried: <result> - Per the DLQ pattern, entries are filtered by
retry_delay_seconds(default 300s = 5min) — recently-enqueued entries may be skipped on first call. Re-run after the delay window if expected entries weren't retried - Re-run
aicp --dlq-statusto confirm pending count dropped
Quality bar: after a retry, the pending count should fall and succeeded
status should rise. If it doesn't, investigate — entries may be hitting the
same failure (poison pills), in which case retry_count increments toward
max_retries (default 3, profile-tunable to 5).
Operation 3 — Diagnose a stuck entry
When: an entry has been pending for hours/days and isn't being retried.
Process:
- List entries:
ls -la ~/.aicp/dlq/shows files by date - Find the entry:
cat ~/.aicp/dlq/<date>.jsonl | jq 'select(.status == "pending" and .retry_count >= 3)' - If
retry_count >= max_retries, the entry is exhausted (poison pill) — the retry pipeline correctly excludes it. Decision: investigate the error string, fix root cause, manually editretry_countback to 0 if the cause is fixed, or delete the entry if the task is no longer relevant - If the entry is "fresh" but not being retried, check
retry_delay_secondsinconfig/default.yamldlq:section — the entry may not yet be eligible
Quality bar: never silently delete entries unless the operator confirms the task is no longer needed. Exhausted entries are signal, not noise.
Operation 4 — Tune the DLQ policy
When: operator wants to change retry budgets or pruning thresholds.
Process:
- Edit
config/default.yaml(or the active profile YAML)dlq:section:enabled(default true) — disable to skip persistence entirelymax_retries(default 3, reliable profile uses 5) — bump for more forgiving retry budgetretry_delay_seconds(default 300) — shorten for faster retry cadencemax_entries(default 1000) — bump if expecting high failure volume
- The profile system (per
wiki/patterns/01_drafts/profile-as-coordination-bundle.md) means changes can be scoped per profile — don't editdefault.yamlif the change is for production-only; editconfig/profiles/reliable.yamlinstead - No restart needed — values are read on each enqueue/retry
Quality bar: increasing max_retries past 5 is rare — the retry budget
exists to prevent infinite reprocessing. If 5 retries aren't enough, the
upstream issue is structural (not transient) and tuning the budget is the
wrong fix.
Gotchas
Detection: agent uses
aicp_dlq_statusMCP tool instead ofaicp --dlq-statusCLI. Rule: NEVER callaicp_dlq_statusMCP tool — it's deprecated and will be removed. Reasoning: per audit decision, MCP overhead is paid per turn for tools used during specific workflows; CLI+Skills loads on demand only when this skill is invoked.Detection: agent retries after every
--dlq-statuswithout checking upstream. Rule: only retry when the operator confirms upstream cause is fixed. Reasoning: retrying against still-broken upstream burns retry budget and reachesmax_retriesfaster — the entry then becomes "exhausted" and won't auto-retry even after upstream recovers.Detection: agent assumes empty
--dlq-statusoutput means all retries succeeded. Rule: empty output means zero pending; doesn't say anything about historical retries. Reasoning: succeeded entries' status is "succeeded", not removed; for retry history, parse the JSONL files directly withjq 'select(.status == "succeeded")'.Detection: agent inspects
~/.aicp/dlq/files manually first instead of using CLI. Rule: preferaicp --dlq-statusfor the standard summary; only drop to file inspection (Operation 3) when investigating a specific stuck entry. Reasoning: the CLI summary is the standard read; direct file inspection is the diagnostic escape hatch, not the routine path.Detection: agent runs
aicp --retry-dlqrepeatedly in a tight loop. Rule: respectretry_delay_secondswindow (default 300s = 5min); a single retry call processes all entries that are eligible at that moment, repeated calls in <5min add nothing. Reasoning: the delay filter intentionally throttles retries to give transient failures time to clear; tight-loop retries defeat that.
Reference exemplars
aicp/core/dlq.py— full DLQ implementation (per-day JSONL, retry filter, status tracking)aicp/cli/main.py_run_check()line 771+ for similar CLI pattern shapewiki/patterns/01_drafts/per-day-jsonl-dlq-with-retry-budget.md— pattern doc with full design rationalewiki/decisions/00_inbox/aicp-mcp-tool-surface-audit-2026-04-19.md— Category D rationale for this skill's existencewiki/decisions/00_inbox/aicp-cli-mcp-outputs-adopt-gateway-output-contract.md— explains theNEXT:lines this skill's commands produce
Domain context
AICP's DLQ is part of the three-layer reliability stack: per-backend circuit breakers (fast-fail per backend) → failover chain (cross-backend recovery) → DLQ (durable persistence when chain exhausts). All three layers are profile-tunable. The DLQ pattern doc explains why per-day JSONL was chosen over SQLite/Redis (operator-inspectable, no daemon, append-only safe).
Related skills
| Skill | When to use |
|---|---|
infra-monitoring |
When the concern is alerting on DLQ growth rate, not the queue contents |
systematic-debugging |
When the concern is "why did this task fail" — DLQ entry has the error; root cause analysis is elsewhere |
aicp-ops-metrics |
When the concern is broader system metrics (DLQ growth IS one signal among many) |