du-dum
About
The du-dum skill implements a two-clock architecture to separate continuous, cheap observation from occasional, expensive decision-making in autonomous agents. It uses a fast clock to accumulate data into a digest and a slow clock that only triggers costly actions (like LLM calls) when pending work is found. This pattern is ideal for cost-sensitive agents where API expenses dominate and most observation cycles require no action.
Quick Install
Claude Code
Recommendednpx skills add pjt222/agent-almanac -a claude-code/plugin add https://github.com/pjt222/agent-almanacgit clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/du-dumCopy and paste this command in Claude Code to install this skill
Documentation
Du-Dum: Batch-Then-Act Pattern
Separate observation from action using two clocks running at different frequencies. The fast clock (analysis) collects data cheaply and writes a compact digest. The slow clock (action) reads the digest and decides whether to act. If the digest says nothing pending, the action clock exits immediately -- zero cost for idle cycles.
The name comes from the heartbeat rhythm: du-dum, du-dum. The first beat (du) observes; the second beat (dum) acts. Most of the time, only the first beat fires.
When to Use
- Building autonomous agents that run on a budget and must observe more often than they act
- An existing heartbeat loop calls the LLM every tick, even when nothing has changed
- Observation is cheap (API reads, file parsing, log scanning) but action is expensive (LLM calls, write operations, notifications)
- You need decoupled failure: if observation fails, the last good digest should still be valid for the action clock
- Designing cron-based agent architectures where analysis and action run as separate jobs
Inputs
- Required: List of data sources the fast clock should observe (APIs, files, logs, feeds)
- Required: Action the slow clock should take when the digest indicates pending work
- Optional: Fast clock interval (default: every 4 hours)
- Optional: Slow clock interval (default: once per day)
- Optional: Cost ceiling per day (to validate the clock configuration)
- Optional: Digest format preference (markdown, JSON, YAML)
Procedure
Step 1: Identify the Two Clocks
Separate all work into observation (cheap, frequent) and action (expensive, rare).
- List every operation in the current loop or planned workflow
- Classify each as observation (reads data, produces summary) or action (calls LLM, writes output, sends messages)
- Verify the split: observations should have zero or near-zero marginal cost; actions should be the expensive operations
- Assign frequencies: the fast clock runs often enough to catch events; the slow clock runs often enough to meet response-time requirements
| Clock | Cost profile | Frequency | Example |
|---|---|---|---|
| Fast (analysis) | Cheap: API reads, file parsing, no LLM | 4-6x/day | Scan GitHub notifications, parse RSS, read logs |
| Slow (action) | Expensive: LLM inference, write operations | 1x/day | Compose response, update dashboard, send alerts |
Got: A clear two-column split where every operation is assigned to exactly one clock. The fast clock has no LLM calls; the slow clock has no data gathering.
If fail: If an operation needs both reading and LLM inference (e.g., "summarize new issues"), split it: the fast clock collects the raw issues into the digest; the slow clock summarizes them. The digest is the boundary.
Step 2: Design the Digest Format
The digest is the low-bandwidth message that bridges the two clocks. It must be compact, human-readable, and machine-parseable.
- Define the digest file path and format (markdown recommended for human debugging)
- Include a header with timestamp and source metadata
- Define a "pending" section listing items that require action
- Define a "status" section with current state (for dashboards or logging)
- Include a clear empty-state indicator (e.g.,
pending: noneor empty section)
Example digest structure:
# Digest — 2026-03-22T06:30:00Z
## Pending
- PR #42 needs review response (opened 2h ago, author requested feedback)
- Issue #99 has new comment from maintainer (action: reply)
## Status
- Last analyzed: 2026-03-22T06:30:00Z
- Sources checked: github-notifications, rss-feed, error-log
- Items scanned: 14
- Items pending: 2
When nothing is pending:
# Digest — 2026-03-22T06:30:00Z
## Pending
(none)
## Status
- Last analyzed: 2026-03-22T06:30:00Z
- Sources checked: github-notifications, rss-feed, error-log
- Items scanned: 8
- Items pending: 0
Got: A digest template with clear pending/empty states. The action clock can determine whether to proceed by checking a single field or section.
If fail: If the digest grows too large (>50 lines), the fast clock is including too much raw data. Move details to a separate data file and keep the digest as a summary with pointers.
Step 3: Implement the Fast Clock (Analysis)
Build the observation scripts that run on the fast schedule.
- Create one script per data source (keeps failures independent)
- Each script reads its source, extracts relevant events, and appends to or rewrites the digest
- Use file locking or atomic writes to prevent partial digests
- Log the analysis run (timestamp, items found, errors) to a separate log file
- Never call the LLM or perform write operations beyond updating the digest
# Pseudocode: analyze-notifications.sh
fetch_notifications()
filter_actionable(notifications)
format_as_digest_entries(filtered)
atomic_write(digest_path, entries)
log("analyzed {count} notifications, {pending} actionable")
Schedule example (cron):
# Fast clock: analyze every 4 hours
30 */4 * * * /path/to/analyze-notifications.sh >> /var/log/analysis.log 2>&1
0 6 * * * /path/to/analyze-pr-status.sh >> /var/log/analysis.log 2>&1
Got: One or more analysis scripts, each producing or updating the digest file. Scripts run independently -- if one fails, the others still update their sections.
If fail: If a data source is temporarily unavailable, the script should log the error and leave the previous digest entries intact. Do not clear the digest on source failure -- stale data is better than missing data for the action clock.
Step 4: Implement the Slow Clock (Action)
Build the action script that reads the digest and decides whether to act.
- Read the digest file (Step 0 of every action cycle)
- Check the pending section: if empty or "none", exit immediately with a log entry
- If items are pending, invoke the expensive operation (LLM call, message composition, etc.)
- After acting, clear or archive the processed digest entries
- Log the action run (items processed, cost, duration)
# Pseudocode: heartbeat.sh (the slow clock)
digest = read_file(digest_path)
if digest.pending is empty:
log("heartbeat: nothing pending, exiting")
exit(0)
# Only reaches here if work exists
response = call_llm(digest.pending, system_prompt)
execute_actions(response)
archive_digest(digest_path)
log("heartbeat: processed {count} items, cost: {tokens} tokens")
Schedule example (cron):
# Slow clock: act once per day at 7am
0 7 * * * /path/to/heartbeat.sh >> /var/log/heartbeat.log 2>&1
Got: The action script exits in under 1 second on idle cycles (just a file read and empty check). On active cycles, it processes pending items and clears the digest.
If fail: If the LLM call fails, do not clear the digest. The pending items remain for the next action cycle. Consider implementing a retry counter in the digest to avoid infinite retries on permanently failing items.
Step 5: Configure Idle Detection
The cost savings come from idle detection -- the action clock must reliably distinguish "nothing to do" from "something to do" with minimal overhead.
- Define the idle check as a single, fast operation (file read + string check)
- Verify the idle path has zero external calls (no API, no LLM, no network)
- Measure the idle path duration -- it should be under 1 second
- Log idle cycles differently from active cycles for monitoring
# Minimal idle check
if grep -q "^(none)$" "$DIGEST_PATH" || grep -q "pending: 0" "$DIGEST_PATH"; then
echo "$(date -u +%FT%TZ) heartbeat: idle" >> "$LOG_PATH"
exit 0
fi
Got: The idle path is a single file read followed by a string match. No network calls, no process spawning beyond the script itself.
If fail: If the idle check is unreliable (false positives causing missed work, or false negatives causing unnecessary LLM calls), simplify the digest format. A single boolean field (has_pending: true/false) at the top of the file is the most reliable approach.
Step 6: Validate the Cost Model
Calculate the expected cost to confirm the two-clock architecture delivers savings.
- Count fast clock runs per day:
fast_runs = 24 / fast_interval_hours - Count slow clock runs per day: typically 1
- Calculate observation cost:
fast_runs * cost_per_analysis_run(should be ~$0 if no LLM) - Calculate action cost:
active_days_fraction * cost_per_action_run - Calculate idle cost:
(1 - active_days_fraction) * cost_per_idle_check(should be ~$0) - Compare with the original single-loop cost
Example cost comparison:
| Architecture | Daily cost (active) | Daily cost (idle) | Monthly cost (80% idle) |
|---|---|---|---|
| Single loop (LLM every 30min) | $13.74/37h | $13.74/37h | ~$400 |
| Du-dum (6 analyses + 1 action) | $0.30 | $0.00 | ~$6 |
Got: A cost model showing the du-dum architecture is cheaper than the original by at least 10x on idle days.
If fail: If the cost model does not show significant savings, one of these is likely true: (a) the fast clock is too frequent, (b) the fast clock includes hidden LLM calls, or (c) the system is rarely idle. Du-dum benefits systems with high idle ratios. If the system is always active, a simpler polling approach may be more appropriate.
Validation
- Fast and slow clocks are cleanly separated with no LLM calls in the fast path
- Digest format has a clear empty-state indicator
- Idle detection exits in under 1 second with zero external calls
- Fast clock failure does not corrupt the digest (stale data preserved)
- Slow clock failure does not clear pending items (retry on next cycle)
- Cost model shows at least 10x savings on idle days vs. single-loop architecture
- Both clocks log their runs for monitoring and debugging
- Digest does not grow unbounded (old entries archived or cleared after processing)
Pitfalls
- Digest growing unbounded: If the fast clock appends but the slow clock never clears, the digest becomes a growing log. Always clear or archive processed entries after the action cycle completes.
- Fast clock too fast: Running analysis every 5 minutes when events arrive daily wastes API quota and disk I/O. Match the fast clock frequency to the actual event rate of your data sources.
- Slow clock too slow: If the action window is once per day but events need same-hour response, the slow clock is too slow. Increase its frequency or add an urgent-event shortcut that triggers immediate action.
- LLM calls in the fast clock: The entire cost model breaks if the fast clock includes LLM inference. Audit every fast-clock script to confirm zero LLM calls. If summarization is needed, defer it to the slow clock.
- Coupling fast clock scripts: If one analysis script depends on another's output, a failure in the first cascades. Keep fast-clock scripts independent -- each reads its own source and writes its own digest section.
- Silent idle logging: If idle cycles produce no log output, you cannot distinguish "running and idle" from "crashed and not running." Always log idle cycles, even if just a timestamp.
- Clearing digest on analysis failure: If a data source is down, do not write an empty digest. The slow clock would see "nothing pending" and skip work that is actually pending. Preserve the last good digest on failure.
Related Skills
manage-token-budget-- cost control framework that du-dum makes practical; du-dum is the architectural pattern, token budget is the accounting layercircuit-breaker-pattern-- handles the failure case (tools breaking); du-dum handles the normal case (nothing to do). Use together: du-dum for idle detection, circuit-breaker for failure recoveryobserve-- observation methodology for the fast clock; du-dum structures when and how observations become actionable via the digestforage-resources-- strategic exploration layer; du-dum is the execution rhythm that forage-resources operates withincoordinate-reasoning-- stigmergic signaling patterns; the digest file is a form of stigmergy (indirect coordination through environmental artifacts)
GitHub Repository
Related Skills
content-collections
MetaThis skill provides a production-tested setup for Content Collections, a TypeScript-first tool that transforms Markdown/MDX files into type-safe data collections with Zod validation. Use it when building blogs, documentation sites, or content-heavy Vite + React applications to ensure type safety and automatic content validation. It covers everything from Vite plugin configuration and MDX compilation to deployment optimization and schema validation.
polymarket
MetaThis skill enables developers to build applications with the Polymarket prediction markets platform, including API integration for trading and market data. It also provides real-time data streaming via WebSocket to monitor live trades and market activity. Use it for implementing trading strategies or creating tools that process live market updates.
creating-opencode-plugins
MetaThis skill helps developers create OpenCode plugins that hook into 25+ event types like commands, files, and LSP operations. It provides the plugin structure, event API specifications, and implementation patterns for JavaScript/TypeScript modules. Use it when you need to intercept, monitor, or extend the OpenCode AI assistant's lifecycle with custom event-driven logic.
sglang
MetaSGLang is a high-performance LLM serving framework that specializes in fast, structured generation for JSON, regex, and agentic workflows using its RadixAttention prefix caching. It delivers significantly faster inference, especially for tasks with repeated prefixes, making it ideal for complex, structured outputs and multi-turn conversations. Choose SGLang over alternatives like vLLM when you need constrained decoding or are building applications with extensive prefix sharing.
