13 KiB
name, description, user-invocable, args
| name | description | user-invocable | args | |||||||
|---|---|---|---|---|---|---|---|---|---|---|
| blitz | Autonomously drive an entire tracker milestone to done — sweep every open issue (one aoe worker session per issue, models routed by difficulty, parallel where dependencies allow), fix bugs found along the way, then either deploy (safe to debug in prod) or spawn a local dev instance, and push a Home Assistant notification with the preview URL. Built for long unattended runs. Use when the user wants to blitz / sweep / complete a whole milestone, e.g. "/blitz M0". | true |
|
Blitz — Milestone Autopilot
Milestone-scale sibling of /yolo. yolo ships one issue; blitz drives a whole milestone to done, unattended — sweeping its issues, fixing bugs it discovers, then shipping (deploy or local dev) and pinging the user's phone with a preview URL.
Design goal: keep working productively for long stretches while spiking an idea, so the user only steps in once there's something to preview.
First: read tracker-common/COMMON.md (sibling skill, same skills root) for shared config, worktree, and implementation conventions. Everything there applies; this doc only adds the milestone orchestration on top.
This skill targets tracker: gitea (milestones live in the repo's Gitea tracker). For tracker: linear, treat a Linear cycle or sub-project as the milestone and adapt the API calls; the orchestration shape is identical.
Roles
- Orchestrator = the main blitz thread (you). Owns the DAG, spawns workers, merges branches, closes issues, runs the readiness gate, ships, notifies. Does not implement issues itself.
- Issue worker = one external
aoesession per issue, with per-difficulty model routing across tools/providers. ReadAOE-WORKERS.md(this skill's directory) before spawning any — it carries the spawn mechanics, default model table, prompt phrasing, stall babysitting, and cleanup rules, and replaces §3.2's fan-out mechanics. One session per issue — do not batch. The operator can override models for a run by just saying so; no config needed.
1. Setup
- Load project config +
source ~/.env.claude(for$GITEA_TOKEN). SetBASE=$remoteBaseUrl,REPO=<owner>/<repo>(fromgit remote get-url origin). - Resolve the milestone from
$ARGUMENTS:GET $BASE/api/v1/repos/$REPO/milestones?state=open→ match by id or title-substring (case-insensitive). No arg → lowest-numbered open milestone that still has open issues.- Save
MS_ID,MS_TITLE, and a slug (lowercase-hyphenated, e.g.m0).
- Integration branch
blitz/<slug>offdefaultBranch. Create + push if absent, else check it out. Everything merges here;defaultBranchstays untouched until Ship. - Load the model roster and notes.
~/.config/agent-skills/config.jsonis the shared roster (agents, queried through~/.claude/scripts/roster.sh) and also carries machine-wideblitzdefaults —maxSessions,notifyService— which the repo's.claude/tracker.jsonoverrides.~/.local/state/agent-skills/models.mdis what previous blitzes learned about those models. Both drive routing in §3.2; you rewrite the notes in §7. SeeAOE-WORKERS.mdfor both. - Check provider budget before spawning. Run
python3 <skills-root>/blitz/usage-budget.py --json. It combines the calibrated Claude rolling-window estimate with the latest Codex rollout rate-limit event. Use itsrecommendedMaxSessionsfor this run, capped byblitz.maxSessions, and honorpreferMedium. Unknown usage is treated as one-worker pressure; a confirmed limit means finish/park rather than starting another worker. If the probe says zero but work is already running, let those workers finish and do not refill the wave. - kitty tab title
blitz/<slug>(silent skip if unavailable).
2. Build the issue DAG
- List the milestone's open issues:
GET $BASE/api/v1/repos/$REPO/issues?state=open&type=issues&limit=100, filter to those whosemilestone.id == MS_ID(or pass&milestones=<MS_TITLE>).type=issuesexcludes PRs. - For each,
GET .../issues/$N/dependencies→ its blocked-by set. Build the dependency graph. - Classify each issue:
- Epic / tracking-only: title contains "Epic" or body is a children checklist with no own implementation scope. Do NOT assign a subagent — it closes automatically when its children (its blockers) all close.
- Workable: everything else.
- Ready set = workable, open issues whose every blocker is closed.
3. Execution pass (the loop body)
Blitz drives its own loop — no external /loop needed. The orchestrator thread stays alive and repeats the pass below until the milestone is done. Worker sessions run in their own tmux panes; per-session Monitors (see AOE-WORKERS.md) re-invoke you as they finish or stall, which advances the next wave naturally. Only use ScheduleWakeup as a fallback heartbeat when you're blocked waiting on something the harness can't notify you about (e.g. polling a deploy's health). Wrapping blitz in /loop is unnecessary and not the intended usage.
Each pass:
- Recompute the ready set (§2.4).
- Fan out: spawn one
aoesession per ready issue, followingAOE-WORKERS.mdfor spawn mechanics, model routing, prompt phrasing, and the per-session stall Monitor. Cap concurrency at the lower ofblitz.maxSessions, the provider budget probe's recommendation, and the machine contention limit (default 2) — the limit is memory and the shared rate-limit window, not cores: each worktree carries its own build artifacts and test run, and other autonomous sessions on the same box are drawing from the same budget. Drop to 1 when<skills-root>/tracker-common/scripts/gate.sh --statusshows the machine already contended. Each session's prompt carries the task ("Implement Gitea issue #N followingCOMMON.md; branch<slug>/N-<issue-slug>off integration branchblitz/<slug>; the issue body plus the repo is your full context; runbuildCommandat most once at the end throughgate.sh"), the file fencing against parallel issues, and exit criteria ending at branch pushed — "do not merge to any shared branch and do not close the issue; if you find out-of-scope bugs, comment them on the issue instead of fixing silently".- Strict rule: never spawn a session for a blocked issue. Dependencies are load-bearing.
- Integrate serially (orchestrator, to avoid parallel-merge conflicts): for each finished session — issue branch pushed, detected by the Monitor, never by grepping commit messages — merge its branch into
blitz/<slug>and resolve conflicts. RunbuildCommandonce per wave, after the last merge — not once per branch — and through the gate:<skills-root>/tracker-common/scripts/gate.sh -- <buildCommand>. If the merge or build breaks, fix on the integration branch (or bounce the issue back for another pass); with several branches merged,git log --onelineon the failing area tells you which one to bounce. - Close each successfully integrated issue on Gitea (
Closes #Nin the merge commit, or PATCHstate:closed). Epics whose blockers are now all closed: close them too. - Integration review (cadence-gated) — do NOT skip. After each wave (or every ~3 integrated issues, whichever comes first), audit the accumulated diff of
blitz/<slug>vsdefaultBranch— not each issue in isolation. Run/code-reviewon that diff, or spawn a reviewer subagent, hunting the cross-issue drift that blind parallel work causes: inconsistent data shapes / contracts between issues, divergent naming, duplicated or conflicting logic, dead code, regressions, misbehavior. Findings are top priority: fix them (inline, or file + wire as blocking issues) before spawning the next fan-out wave. This is the load-bearing coherence check — parallel subagents can't see each other's work, so this is the only place drift gets caught. - Fold in findings: for each out-of-scope bug a worker commented on its issue and any bug you find, create a new Gitea issue in this milestone (
milestone: MS_ID). If it blocks or relies on others, wire it as a real dependency (POST .../issues/$N/dependencieswith{"index": <other>}, not just prose) so §2's DAG picks it up next pass. Fix trivial bugs inline instead of filing. - Repeat passes until: no open workable issues, no open epics, the latest integration review is clean, and a full pass produced no new findings.
4. Readiness gate
Declare the milestone ready only when ALL hold:
- Every workable issue closed; every epic closed.
buildCommandgreen onblitz/<slug>(through the gate; a run that never happened is not green — wait for a slot here, this is the one check that must actually execute).- A real smoke/verify of the app passes — drive the actual feature (use
/verifyor/run, or the project's verify skill), not just unit tests. This catches integration breakage the per-issue subagents couldn't see.
If the gate fails, file/fix the gap as a finding and run another pass.
5. Ship — deploy OR local dev
Pick the path per the milestone's nature and config. Resolve deployPolicy:
- Explicit:
.claude/tracker.json(or legacy.claude/linear.json) →blitz.deploy(a{ "<slug>": "deploy" | "local" }map) wins if present. - Heuristic (when unset), deploy only if ALL true:
- Milestone is user-facing / shippable — NOT a throwaway spike (check the milestone description for "throwaway"/"spike"/"disposable").
- A deploy target is wired — a Dokploy app for this repo exists, or
blit.deployTarget/deployUrlis configured. - Change is safe to debug in prod — static site or additive change, no destructive migration.
- When genuinely unsure, choose LOCAL. Never surprise-deploy to prod.
Deploy path (safe to debug in prod)
- Merge
blitz/<slug>→defaultBranch, push. - Trigger the deploy via the Dokploy MCP for the configured app (
application-deploy/application-redeploy, orcompose-deployfor compose apps). Poll until healthy. - Preview URL = the app's Dokploy domain (
domain-byApplicationId) or configureddeployUrl.
Local path (spike / not prod-safe)
- Keep
blitz/<slug>(do not merge todefaultBranchunless the change is trivially safe). - Launch a local dev instance in the background — use the
/runskill, or run the app's dev server directly, bound to0.0.0.0(global rule: this VM is reached from other devices), e.g.pnpm --dir <app> dev --host 0.0.0.0withrun_in_background: true. - Preview URL =
http://<LAN-IP>:<port>(LAN IP, neverlocalhost):hostname -I | awk '{print $1}'.
6. Notify (Home Assistant MCP)
Push to the user's phone so they know it's ready to look at. Target: blitz.notifyService from the repo's .claude/tracker.json, else from ~/.config/agent-skills/config.json, else notify.mobile_app_pixel_7_naps:
mcp__ha-mcp__ha_call_service
domain: "notify"
service: "mobile_app_pixel_7_naps"
service_data:
title: "blitz: <MS_TITLE> ready ✅"
message: "<n> issues shipped · <deploy|local dev> · tap to preview"
data:
url: "<preview-url>" # opens the companion app at the URL
clickAction: "<preview-url>" # Android notification tap target
7. Record what the models did
Before finishing, update ~/.local/state/agent-skills/models.md with what this run taught you about the models you used — which tier handled what, where one stalled or needed escalating, what a cheap model got away with. AOE-WORKERS.md ("Model notes") carries the format and the rules: compiled summary, rewritten in place, under ~60 lines, never a run log. Do this on a blocked run too — a model that couldn't finish is the evidence hardest to come by.
Then post a one-line summary + preview URL in the chat too, and finish the run — the self-driven loop ends here (cancel any pending ScheduleWakeup heartbeat with stop: true).
8. Autonomy & stop conditions
- Fully autonomous. Never ask the user except on a genuine blocker (missing credentials, architectural contradiction, an irreversible/destructive op, or a deploy that would break prod). Away ≠ approval — if you must ask, wait; don't decide for them.
- Build it can't fix after a few honest attempts, or a hard blocker: file a blocker issue in the milestone, do §7, send an HA notification describing the blocker (same call, title
blitz: <MS_TITLE> BLOCKED ⚠️), and stop. Don't thrash. - Never auto-deploy to prod when the ship decision is uncertain — fall back to local + notify.
- Idempotent: a re-run picks up where it left off (open issues + integration branch already reflect progress).
Config (optional, .claude/tracker.json)
{
"blitz": {
"deploy": { "m1": "deploy", "m0": "local" }, // per-slug override of the ship path
"deployTarget": "<dokploy app name or id>",
"deployUrl": "https://mapmatch.naps.pt", // preview URL for the deploy path
"devCommand": "pnpm --dir spike dev --host 0.0.0.0",
"notifyService": "mobile_app_pixel_7_naps"
}
}
All optional — sane fallbacks apply (heuristic ship decision, /run for local, default notify target). notifyService and maxSessions also read from the machine-wide blitz block in ~/.config/agent-skills/config.json; the repo file wins where both set a key.