docs: add scheduled-tasks documentation and update CLAUDE.md routing (#2)

Adds scheduled-tasks/CONTEXT.md documenting the implemented headless
Claude scheduled task system: runner.sh framework, custom dispatcher
scripts, active tasks, settings reference, auth, monitoring, and
cost safety guardrails.

Updates CLAUDE.md routing table to load CONTEXT.md alongside the
SKILL.md for all scheduled-task-related topics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-03-04 02:04:45 -06:00 committed by cal
parent 48efafe217
commit b50808eb8e
2 changed files with 145 additions and 1 deletions

View File

@ -30,7 +30,7 @@ When a topic comes up, load `{tech}/CONTEXT.md` + `{tech}/troubleshooting.md`. F
| backup, restic, snapshot, restore, retention | `backups/` |
| workstation, dotfiles, symlink, fish, starship, mangohud, zed, fish config, fish alias, tmux, tmux alias | `workstation/` |
| tui testing, mcp-tui-driver, tui automation, terminal ui test | `development/tui-testing.md` |
| scheduled task, claude-scheduled, headless claude, timer, cowork | `~/.claude/skills/create-scheduled-task/SKILL.md` |
| scheduled task, claude-scheduled, headless claude, timer, cowork, issue-poller, issue-worker, pr-reviewer, backlog-triage | `scheduled-tasks/CONTEXT.md` + `~/.claude/skills/create-scheduled-task/SKILL.md` |
**Special loads:**
- Pi-hole → also `networking/pihole-ha-setup.md`

144
scheduled-tasks/CONTEXT.md Normal file
View File

@ -0,0 +1,144 @@
# Scheduled Tasks — Headless Claude Sessions on a Timer
Headless Claude Code sessions triggered by systemd timers. Runs `claude -p` with task-specific prompts, tools, and cost limits — no TTY required.
For a guide on **creating new tasks**, see: `~/.claude/skills/create-scheduled-task/SKILL.md`
## File Layout
```
~/.config/claude-scheduled/
├── runner.sh # Universal task runner (template-based tasks)
├── disabled # Touch to globally pause all template tasks
├── tasks/
│ ├── backlog-triage/ # Example template task
│ │ ├── prompt.md # What Claude should do
│ │ ├── settings.json # Model, budget, tools, working dir
│ │ └── mcp.json # MCP servers (optional)
│ ├── issue-poller/ # Used by issue-poller.sh (not runner.sh)
│ ├── issue-worker/ # Agent instructions + settings
│ └── pr-reviewer/ # Agent instructions + settings
├── issue-poller.sh # Custom dispatcher for issue scanning
├── issue-dispatcher.sh # Dispatches AI agent per issue
├── pr-reviewer-dispatcher.sh # Dispatches AI reviewer per open PR
└── repos.json # Gitea repos to scan
~/.config/systemd/user/
├── claude-scheduled@.service # Template service (used by runner.sh tasks)
├── claude-scheduled@backlog-triage.timer
├── claude-issue-poller.service/timer # Every 30 min
├── claude-pr-reviewer.service/timer
├── claude-issue-dispatcher.service
└── claude-daily-report.service/timer
~/.local/share/claude-scheduled/
└── logs/<task-name>/ # Timestamped .log files (last 30 kept)
```
Source files are in `~/dotfiles/claude-scheduled/` and symlinked into place.
## Two Execution Patterns
### 1. runner.sh (Template Framework)
Used by tasks with only a prompt and settings — no custom logic needed.
```
claude-scheduled@<task>.timer
→ claude-scheduled@.service
→ runner.sh <task>
reads tasks/<task>/{prompt.md,settings.json,mcp.json}
invokes: claude -p <prompt> --model --effort --max-budget-usd
--allowedTools --output-format json
stores result to cognitive-memory
optionally POSTs to Discord webhook
```
**Kill switch**: `touch ~/.config/claude-scheduled/disabled` pauses all runner.sh tasks.
### 2. Custom Dispatcher Scripts
Used for tasks that need conditional dispatch, parallel agents, or complex logic.
```
claude-issue-poller.timer (every 30 min)
→ issue-poller.sh
→ scans repos.json for new open issues
→ calls issue-dispatcher.sh per issue
→ spawns claude -p (issue-worker agent)
claude-pr-reviewer.timer
→ pr-reviewer-dispatcher.sh
→ scans open PRs needing review
→ spawns claude -p (pr-reviewer agent) per PR
claude-daily-report.timer
→ daily-report.py
→ generates summary, posts to Discord
```
## Active Tasks
| Task | Trigger | Type | Description |
|------|---------|------|-------------|
| `backlog-triage` | Weekdays 09:15 | runner.sh | Scan Gitea issues, prioritize, suggest focus |
| `issue-poller` | Every 30 min | custom | Find new issues, dispatch AI workers |
| `issue-dispatcher` | On-demand | custom | Fix a single issue, open a PR |
| `pr-reviewer` | On timer | custom | Review open PRs, post formal reviews |
| `daily-report` | Daily | custom | Summarize activity, post to Discord |
## Settings Reference (`settings.json`)
```json
{
"model": "sonnet",
"effort": "medium",
"max_budget_usd": 0.75,
"allowed_tools": "Read(*) Glob(*) Grep(*)",
"graph": "default",
"working_dir": "/mnt/NV2/Development/claude-home",
"timeout_seconds": 300,
"notify_webhook": "https://discord.com/api/webhooks/..."
}
```
| Field | Default | Notes |
|-------|---------|-------|
| `model` | `sonnet` | Model alias. Use `sonnet` for cost efficiency. |
| `effort` | `medium` | `low`, `medium`, `high` — controls reasoning depth |
| `max_budget_usd` | `0.25` | Hard cost ceiling per session |
| `allowed_tools` | `Read(*) Glob(*) Grep(*)` | Principle of least privilege |
| `graph` | `default` | Cognitive-memory graph for output storage |
| `working_dir` | `claude-home` | `cd` here before running — loads that project's CLAUDE.md |
| `timeout_seconds` | `300` | Hard timeout via `timeout(1)` |
| `notify_webhook` | — | Discord webhook URL for result posting (optional) |
## Auth and Environment
- Uses Cal's Claude Max subscription via **OAuth** (no API key needed)
- `runner.sh` unsets `CLAUDECODE` to allow nested sessions (running from within Claude Code)
- MCP servers configured per-task via `mcp.json` + `--strict-mcp-config`
## Monitoring
```bash
# List all active timers
systemctl --user list-timers 'claude*'
# Check logs for a task
journalctl --user -u claude-scheduled@backlog-triage.service --since today
# Latest log file for runner.sh task
ls -t ~/.local/share/claude-scheduled/logs/backlog-triage/ | head -1
# Manual test run
~/.config/claude-scheduled/runner.sh backlog-triage
```
## Cost Safety
- Per-task `max_budget_usd` cap — runner.sh detects `error_max_budget_usd` and warns
- `--allowedTools` restricts what Claude can do (read-only tasks can't Edit/Write/Bash)
- `timeout_seconds` kills hung sessions
- Global kill switch: `touch ~/.config/claude-scheduled/disabled`
- Typical cost: $0.150.30 per Sonnet run with MCP tools