Parallel yolo/nightshift/blitz sessions each ran the full suite and OOMed the box. gate.sh caps concurrency, memory and build parallelism; skills now run scoped checks in the loop and one gated full run per push. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015YUXS63P1WCdC6bEKWcnAE
12 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 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". | 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 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
Agentper 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
- 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. - 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. 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:
- Recompute the ready set (§2.4).
- Fan out: spawn one issue subagent per ready issue, in parallel (multiple
Agentcalls 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<skills-root>/linear-common/scripts/gate.sh --statusshows the machine already contended. Each subagent prompt:- "Implement Gitea issue #N (
<title>) in this repo following the/yoloflow andCOMMON.md. You are on integration branchblitz/<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; runbuildCommandat 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 returnbuildPassed: nullrather 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 innewFindings." - 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.
- "Implement Gitea issue #N (
- Integrate serially (orchestrator, to avoid parallel-merge conflicts): for each finished subagent whose
doneand whosebuildPassedis notfalse, merge its branch intoblitz/<slug>and resolve conflicts. RunbuildCommandonce 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 --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
newFindingsitem 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. - 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/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. 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)
{
"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).