Files
agent-skills/skills/blitz/SKILL.md
T
Miguel Palhas c9e1c68ea2
ci / nix (push) Successful in 8s
ci / lint (push) Failing after 11s
feat(blitz): account for provider usage pressure
2026-08-25 14:08:07 +01:00

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
name description required
input A milestone identifier: its number/id (e.g. 3), or a substring of its title (e.g. M0). Omit to auto-pick the lowest-numbered open milestone with unclosed issues. false

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 aoe session per issue, with per-difficulty model routing across tools/providers. Read AOE-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

  1. Load project config + source ~/.env.claude (for $GITEA_TOKEN). Set BASE=$remoteBaseUrl, REPO=<owner>/<repo> (from git remote get-url origin).
  2. 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).
  3. Integration branch blitz/<slug> off defaultBranch. Create + push if absent, else check it out. Everything merges here; defaultBranch stays untouched until Ship.
  4. Load the model roster and notes. ~/.config/agent-skills/config.json is the shared roster (agents, queried through ~/.claude/scripts/roster.sh) and also carries machine-wide blitz defaults — maxSessions, notifyService — which the repo's .claude/tracker.json overrides. ~/.local/state/agent-skills/models.md is what previous blitzes learned about those models. Both drive routing in §3.2; you rewrite the notes in §7. See AOE-WORKERS.md for both.
  5. 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 its recommendedMaxSessions for this run, capped by blitz.maxSessions, and honor preferMedium. 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.
  6. kitty tab title blitz/<slug> (silent skip if unavailable).

2. Build the issue DAG

  1. List the milestone's open issues: GET $BASE/api/v1/repos/$REPO/issues?state=open&type=issues&limit=100, filter to those whose milestone.id == MS_ID (or pass &milestones=<MS_TITLE>). type=issues excludes PRs.
  2. For each, GET .../issues/$N/dependencies → its blocked-by set. Build the dependency graph.
  3. 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.
  4. 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:

  1. Recompute the ready set (§2.4).
  2. Fan out: spawn one aoe session per ready issue, following AOE-WORKERS.md for spawn mechanics, model routing, prompt phrasing, and the per-session stall Monitor. Cap concurrency at the lower of blitz.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 --status shows the machine already contended. Each session's prompt carries the task ("Implement Gitea issue #N following COMMON.md; branch <slug>/N-<issue-slug> off integration branch blitz/<slug>; the issue body plus the repo is your full context; run buildCommand at most once at the end through gate.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.
  3. 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. Run buildCommand once 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 --oneline on the failing area tells you which one to bounce.
  4. Close each successfully integrated issue on Gitea (Closes #N in the merge commit, or PATCH state:closed). Epics whose blockers are now all closed: close them too.
  5. 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> vs defaultBranch — not each issue in isolation. Run /code-review on 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.
  6. 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/dependencies with {"index": <other>}, not just prose) so §2's DAG picks it up next pass. Fix trivial bugs inline instead of filing.
  7. 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.
  • buildCommand green on blitz/<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 /verify or /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:
    1. Milestone is user-facing / shippable — NOT a throwaway spike (check the milestone description for "throwaway"/"spike"/"disposable").
    2. A deploy target is wired — a Dokploy app for this repo exists, or blit.deployTarget / deployUrl is configured.
    3. 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)

  1. Merge blitz/<slug>defaultBranch, push.
  2. Trigger the deploy via the Dokploy MCP for the configured app (application-deploy / application-redeploy, or compose-deploy for compose apps). Poll until healthy.
  3. Preview URL = the app's Dokploy domain (domain-byApplicationId) or configured deployUrl.

Local path (spike / not prod-safe)

  1. Keep blitz/<slug> (do not merge to defaultBranch unless the change is trivially safe).
  2. Launch a local dev instance in the background — use the /run skill, or run the app's dev server directly, bound to 0.0.0.0 (global rule: this VM is reached from other devices), e.g. pnpm --dir <app> dev --host 0.0.0.0 with run_in_background: true.
  3. Preview URL = http://<LAN-IP>:<port> (LAN IP, never localhost): 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.