Files
agent-skills/skills/blitz/SKILL.md
T
naps62 999f759691 feat(skills): machine-wide gate for heavy test runs
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
2026-07-27 12:41:03 +00:00

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
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 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=<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. 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. 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 <skills-root>/linear-common/scripts/gate.sh --status shows the machine already contended. Each subagent prompt:
    • "Implement Gitea issue #N (<title>) 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.jsonblitz.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)

{
  "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).