--- name: blitz description: "Autonomously drive an entire tracker milestone to done — sweep every open issue (one subagent per issue, 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\"." user-invocable: true args: - name: input description: "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." required: 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 `linear-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 subagents, merges branches, closes issues, runs the readiness gate, ships, notifies. Does *not* implement issues itself. - **Issue subagent** = one `Agent` per issue (`isolation: "worktree"`). Implements exactly one issue via the yolo flow, returns a structured result. **One subagent per issue is the default and is incentivized** — do not batch multiple issues into one agent. --- ## 1. Setup 1. Load project config + `source ~/.env.claude` (for `$GITEA_TOKEN`). Set `BASE=$remoteBaseUrl`, `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/` off `defaultBranch`. Create + push if absent, else check it out. Everything merges here; `defaultBranch` stays untouched until Ship. 4. kitty tab title `blitz/` (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=`). `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. Fan-out subagents run in the background; when they finish they re-invoke you, 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 issue subagent per ready issue, **in parallel** (multiple `Agent` calls in a single message), `isolation: "worktree"`. **Cap concurrency at 3** — each worktree carries its own build artifacts and test run, and other autonomous sessions are on the same box. Drop to 2 when `/linear-common/scripts/gate.sh --status` shows the machine already contended. Each subagent prompt: - "Implement Gitea issue #N (``) in this repo following the `/yolo` flow and `COMMON.md`. You are on integration branch `blitz/<slug>`; create branch `<slug>/N-<issue-slug>` **off it**. Read the issue body + its linked spec/epic; that plus the repo is your full context. Implement and commit in logical steps. Check **only what you touched** as you go; run `buildCommand` at most once at the end, and run it as `<skills-root>/linear-common/scripts/gate.sh -- <buildCommand>` — exit 75 means the machine was busy and it did not run, so return `buildPassed: null` rather than retrying. **Do not merge to any shared branch and do not close the issue** — push your branch and return the result. If you discover a bug or missing work outside this issue's scope, do not fix it silently; report it in `newFindings`." - Force a structured return (schema): `{ issue, done, branch, summary, buildPassed, newFindings: [{title, body}] }`. `buildPassed: null` = the gate was busy, so the integration build is the first real check that branch gets. - **Strict rule**: never spawn a subagent for a blocked issue. Dependencies are load-bearing. 3. **Integrate serially** (orchestrator, to avoid parallel-merge conflicts): for each finished subagent whose `done` and whose `buildPassed` is not `false`, 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>/linear-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 `newFindings` item and any bug you find, create a new Gitea issue in this milestone (`milestone: MS_ID`), wire dependencies if it blocks/relies on others, and let the next pass pick it up. 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/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. Default target `notify.mobile_app_pixel_7_naps` (override with `blitz.notifyService` in config): ``` 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 ``` 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`). ## 7. 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, 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/linear.json`) ```jsonc { "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).