| name | autospec |
| description | Use when the user wants to ship a feature end-to-end across multiple commits — bootstraps repo if missing, brainstorms a design spec, decomposes into linked GitHub issues, or splits an existing spec into issues, then runs an autonomous implementation loop with auto-merge. |
autospec workflow (harness-neutral)
Take the following feature request and ship it through the full pipeline:
bootstrap repo (if missing) → investigate → design → spec → decomposed GitHub issues → autonomous implementation with auto-merge → periodic status updates → final report.
If the request asks to split, materialize, roadmap, decompose, or turn an
already-written spec into GitHub issues, use Existing spec mode below:
select a tracked docs/specs/*.md file, skip Phases 1-2, run Phase 3 and
Phase 3.5 against that spec, then continue to the Phase 3 pre-impl gate.
Manage your own context — never exceed 60%. Delegate to subagents whenever your harness supports it; do not investigate, write code, or design directly in the main conversation when a subagent can do it.
Self-update mode
Decide this purely from the request text the harness handed you. Do NOT
shell out (no grep, sed, [[ =~ ]], command substitution, etc.) to
test the user's free-form request — passing it through a shell is what
historically tripped harness permission engines (e.g. parse errors near
backtick/pipe characters in the user's prose). Read the request, normalize
it in your reasoning (collapse whitespace, trim, lowercase), and if the result is
exactly update, this skill enters self-update mode and does NOT run the
normal pipeline.
- Detect harness by checking which install path exists for this skill:
- Claude Code:
~/.claude/skills/autospec/SKILL.md
- OpenCode:
~/.config/opencode/agent/autospec.md
- Codex CLI:
~/.codex/prompts/autospec.md
- Re-install the full autospec suite from
main by piping the canonical installer:
curl -fsSL https://raw.githubusercontent.com/berlinguyinca/autospec/main/bootstrap.sh | bash -s -- --skill all --harness all --update
Run this one-liner once; it refreshes all autospec skills across all harnesses.
- Show the diff between the prior installed file(s) and the freshly fetched copy (e.g.
diff <(cat <prior>) <(curl -fsSL ...SKILL.md) or the equivalent recorded by the installer).
- Stop. Do not enter Phase 0 / Phase 1 / any pipeline phase. Print the upgrade summary and return to the user.
If no install path is detected, print Self-update: no installed copy of autospec found; run install.sh first. and exit.
Stop mode
Apply the same read-and-normalize approach used for self-update mode (do
NOT shell out the user's request). If the normalized request is exactly
stop, or stop followed by one or more --<word> flags (examples:
stop, stop --graceful, stop --immediate, stop --status,
stop --resume, stop --help, stop --flag), this skill enters stop
mode and does NOT run the normal pipeline. When dispatching, pass any
--<flag> tokens the user provided as separate words to the helper.
- Dispatch to
bash "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/autospec-stop.sh" <args>.
- Print the helper's stdout to the user.
- Stop. Do not enter Phase 0 or any pipeline phase.
Feature request
{FEATURE_DESCRIPTION}
Continuous loop mode (--loop)
When the operator invokes /autospec --loop "<initial prompt>" (alias
--continuous), this skill runs the full pipeline (Phases 1-6) once, then
harvests the Phase 6 final report (.autospec/run-summary.md written by
autospec-write-run-summary.sh) and re-iterates until one of six
termination conditions trips. The loop is driven by the shared library at
lib/autospec-loop.sh — the single source of truth shared with
/autospec-refine --continue and /autospec-continue --loop.
Flags:
--loop (alias --continuous) — enable continuous-iteration mode.
--max-iterations N — cap the loop at N iterations (default 5, env
override AUTOSPEC_LOOP_MAX_ITERATIONS).
--skip-refine — pipe the harvested next-prompt straight into the next
/autospec invocation without first running /autospec-refine on it.
Termination conditions (priority order):
evidence_based_stop — the run-summary contains a STOP: <reason> marker.
convergence_clean — harvest returns empty / (none — converged).
oscillation_detected — iteration N+1's harvested prompt hash equals
iteration N's (same work proposed twice in a row).
operator_stop — ~/.autospec/stop.flag or
~/.autospec/refine-loop-stop.flag present at iteration boundary.
budget_cap_reached — AUTOSPEC_LOOP_TOKEN_CAP (default 2M tokens) or
AUTOSPEC_LOOP_TIME_CAP (default 6h / 21600s) exceeded.
round_cap_reached — --max-iterations hit without any of the above.
Per-iteration loop:
- Run the full /autospec pipeline (Phase 1-6) on the current prompt.
- Read
$REPO_ROOT/.autospec/run-summary.md and pass it to
autospec_loop_harvest_next_prompt from the shared library.
- If harvest is empty →
convergence_clean. If it returns
STOP::<reason> → evidence_based_stop. Otherwise the harvested text
becomes the next iteration's prompt.
- Unless
--skip-refine is set, run the harvested prompt through
/autospec-refine (4 lenses) before the next /autospec invocation.
- Hash the harvested prompt; oscillation trips when the hash matches
the previous iteration's.
Output artifacts:
.autospec/loop-summary.md — markdown table with one row per iteration
- final status + iterations executed.
<artifact-dir>/<slug>-loop.json — per-iteration JSON record validated
against schemas/autospec-refinement-loop.schema.json.
Safety guardrails inherit from the existing autonomy gate
(autospec-autonomy-gate.sh) — destructive remote actions still
surface for confirmation per iteration. The rate limit + injection guard
from autospec-continue.sh (PR #704) also apply.
Default behavior (no --loop) is unchanged: a single end-to-end pipeline
run with no harvest/re-iteration.
Existing spec mode
Use this mode when the request asks to split, materialize, roadmap, decompose,
or turn an existing spec into GitHub issues. Examples:
split existing spec, split latest spec, turn this spec into GitHub issues,
roadmap docs/specs/2026-05-01-example-design.md, or
materialize the newest spec.
Do not run Phase 1 or Phase 2 in this mode. The design already exists; this
mode is a shortcut into Phase 3 using the same issue decomposition and Phase 3.5
classification path as the normal pipeline.
- Verify repo. Run the Phase 0 repo probes. If the current directory is
not a git repo with a GitHub remote, use Phase 0 bootstrap before selecting
a spec.
- Resolve candidate specs.
- If the request contains an explicit
docs/specs/*.md path, use that file.
- Otherwise list tracked and untracked local files matching
docs/specs/*.md.
- Sort by ISO date in the filename (
YYYY-MM-DD-...) descending. For files
without a date prefix, sort after dated files by filesystem modified time.
- The first item is the default "newest" spec.
- Ask on ambiguity.
- If zero specs exist, stop with:
No docs/specs/*.md files found. Create or point me at a spec before running existing spec mode.
- If exactly one spec exists and no explicit path was provided, use it.
- If more than one spec exists and no explicit path was provided, ask the user
to confirm the default newest spec or choose another path. Show at most the
five newest candidates with relative paths. Do not file issues until the
user answers.
- Verify selected spec is filed.
- The selected path must be under
docs/specs/ and end in .md.
- The selected file must be tracked on
origin/main before Phase 3, so child
issues can cite a stable GitHub URL. Verify with:
git fetch origin and git cat-file -e origin/main:<spec-path>.
- If the selected file is missing from
origin/main, stop and tell the user:
Selected spec is not on origin/main yet: <spec-path>. Land the spec first, or run normal /autospec so Phase 2 can create and merge the spec PR.
- Continue at Phase 3. Capture
{selected_spec_path} and its GitHub URL as
https://github.com/{repo}/blob/main/{selected_spec_path}. Run Phase 3 and
Phase 3.5 using that selected spec. Then proceed to the existing Phase 3
pre-impl gate.
Physical STL / CAD design guardrails
When the request involves STL, CAD, 3D-printable fixtures, workholding,
vacuum, pneumatic, hydraulic, dust-collection, hose, fitting, gasket, or
other physical models, carry these requirements into Phase 2 specs, Phase 3
child issues, and Phase 4 verification:
- Treat physical function as acceptance criteria, not visual intent. Verify
mountability, tool access, fitting clearance, gasket compression, storage fit,
printer build volume, and expected operating orientation.
- Do not allow blocked ports, obstructed hoses/tubes, capped passages, hidden
dead-end channels, or geometry that interferes with the model's purpose.
- Around every port, fitting, connector, tap bore, hose socket, screw head, and
service tool path, require at least 5 mm of free working clearance in every
direction unless the user explicitly specifies a tighter envelope.
- Around every gasket path, require at least 5 mm of continuous plastic margin
outside the gasket. Gasket grooves must not expose an edge or be crossed by
mounts, screws, ribs, channels, holders, or workpiece contact geometry.
- For NPT or other tapped ports, prove the tap and installed fitting can enter
straight or along the specified angled axis without collisions. Ports and
fitting bosses must be integrated into reinforced body geometry, not left as
fragile stand-alone protrusions.
- For low-flow vacuum systems, do not add relief valves, vents, restriction
orifices, small balancing holes, or intentional leaks unless the user
explicitly asks for them and the spec records why the pump can tolerate them.
- For sealed or flowing parts, require gas/fluid/vacuum/dust-flow simulation or
deterministic geometry checks that prove no leaks, no disconnected internal
circuits, no blocked passages, and adequate inlet-to-outlet connectivity.
- For dust collection, preserve maximum practical duct cross-section and
suction path continuity. No vacuum tubing, air port, dust passage, or hose
connection may be narrowed or blocked by ribs, mounts, bosses, decorative
geometry, or slicer support assumptions.
- For functional openings such as dust hood mouths, plenum intakes, vacuum
channels, gasket windows, and hose throats, add deterministic projection or
section keepout checks. Prove that reinforcements, sockets, bosses, ribs, and
cutters do not intrude into the working opening from any operating angle.
- For every generated STL, require visual QA from at least 16 angles: right,
left, front, back, top, bottom, top-right 45, top-left 45, top-front 45,
top-back 45, front-right 45, front-left 45, back-right 45, back-left 45,
bottom-right 45, and bottom-left 45. Renders must be nonblank, well-framed,
and reviewed for obvious gaps, overlaps, obstructions, unsupported fragile
features, and unexpected protrusions.
- Regenerate from a clean build directory before release, then verify the build
directory contains every expected STL plus any PDFs, sections, renders, and
acceptance artifacts named by the spec.
Harness detection (run once at skill start, before Phase 0)
Detect your harness by checking available tools before any phase:
-
Claude Code — the Agent tool with a subagent_type parameter is available.
TIER_A = opus + ultrathink (model ID: claude-opus-4-7)
TIER_B = sonnet (model ID: claude-sonnet-4-6)
-
OpenCode — a task tool with model/tier configuration is available (no subagent_type).
TIER_A = top-tier task model + high reasoning
TIER_B = smaller-tier task model + medium reasoning
-
Codex CLI — neither Agent nor a configurable task tool is available; apply_patch is the primary edit tool.
TIER_A = current top GPT model + reasoning_effort=high
TIER_B = gpt-5.1-codex-spark + reasoning_effort=medium
Fallback rule: If TIER_B is not available in your harness (model unknown, quota/capacity failure, authorization failure, or tool call returns an error for that model), silently retry the same subagent dispatch with TIER_A. Preserve the parent context on retry; for Codex native subagents, fork/inherit the current conversation context and use the latest top GPT model instead of moving the work into the main session. Never ask the user.
Hold TIER_A and TIER_B for the entire skill run. Every "Tier A" and "Tier B" reference below resolves to these harness-specific values.
Project configuration preflight
Before Phase 0, look for .autospec/autospec.yml at the repository root.
If the file is missing and the current directory is already inside a git repo,
run /autospec-sweep init behavior first:
bash "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/autospec-sweep-wizard.sh" init
Accept the wizard defaults unless repo findings require a project-specific
answer. The generated .autospec/autospec.yml is tracked project source and
must be committed with the next autospec spec/config change. After it exists,
read it before deciding which autospec phases are enabled.
If the file is missing because Phase 0 must bootstrap a brand-new repo, create
it immediately after the initial repo scaffold and before Phase 1. Use all
steps enabled by default, strict isolation, team_personality: auto, and
continuous improvement enabled for docs, tests, and code.
Phase 0 — Bootstrap repo (if missing)
Verify gh auth status is authenticated. If not, ask the user to run gh auth login and stop until they confirm.
Probe the working directory:
git rev-parse --is-inside-work-tree 2>/dev/null
git remote get-url origin 2>/dev/null
If either check fails — no git repo, or no GitHub remote — bootstrap a new repo:
-
Suggest a name. Slugify the feature description: lowercase, hyphens, drop stop-words, prefix with the obvious stack if inferable (e.g. "Go TUI for X" → go-tui-x; "Python ML pipeline that does Y" → py-ml-y). Offer 1–2 candidates.
-
Ask the user once (single interactive question; combine the three sub-questions if your harness supports it, otherwise ask sequentially):
- Name (your top suggestion as default).
- Visibility:
private | public (default: private).
- Owner: enumerate via
gh org list; default to the user's personal account.
-
Initialize locally:
- If
.git is absent: git init -b main.
- Write a stack-appropriate
.gitignore (Go: bin/ vendor/ *.exe *.test; Node: node_modules/ dist/ .next/ .env*; Python: __pycache__/ .venv/ *.egg-info/ build/ dist/; mixed/unknown: skip).
- Write a one-line
README.md containing the feature description.
- Write a starter
AGENTS.md listing the project's coding standards (TDD non-negotiable, no DB mocks, conventional commits, branch-per-issue, no force-push) — this is the source of truth every agent reads.
git add -A && git commit -m "chore: initial scaffold".
-
Create the remote and push:
gh repo create <owner>/<name> --<private|public> --source=. --remote=origin --push
-
Verify: gh repo view <owner>/<name> --json url,defaultBranchRef. Capture <owner>/<name> as {repo} — every subsequent phase uses this value.
If a repo already exists (cwd is in a git tree with a github.com:<owner>/<name> remote), capture that as {repo} and skip the bootstrap.
Phase 1 — Investigate (delegate)
Spawn a read-only research subagent to map relevant files, schema, services. Get back a 300-word summary with file paths and line numbers. Do NOT read files directly from the main thread.
Model tier: TIER_A (spec work) — top model with extended thinking; resolved at startup.
Phase 1 bounded-context rule. Launch the research subagent with a fresh,
bounded handoff. Do NOT fork, inherit, or compact the full parent conversation into Phase 1. The handoff may contain only: the feature request,
repo slug/root, a short git status summary, relevant memory-injection output,
explicit user-mentioned paths/surfaces, and these Phase 1 instructions. For
Codex native subagents, set fork_context=false; for Claude/OpenCode, use the
equivalent fresh/no-history task mode when available. If the harness cannot
start a fresh subagent, or returns a context window or remote compact failure,
do not retry with inherited context. Fall back to bounded local read-only rg/file-read investigation, label the result as Phase 1 fallback, and
continue to Phase 2 with the best evidence gathered.
Hard limits. Max 25 tool calls. If 3 consecutive read/grep calls return nothing useful, stop and write your best-effort summary even if incomplete. Do not retry the same query verbatim. No wall-clock cap.
If the feature touches a remote system (DB, server, S3), run a real query against the actual data to confirm the problem statement before designing. Surface the concrete numbers in the design.
For a freshly-bootstrapped empty repo, Phase 1 may be a no-op — proceed to Phase 2.
Phase 2 — Brainstorm + design
Spec quality is the bottleneck. Phase 2's output drives every downstream cycle's cost; if you care about spec quality, invoke this skill with your top-tier model (Claude Code: claude-code --model opus; Codex: top GPT). Phase 2 itself runs in the orchestrator (no subagent dispatch) — your invocation model IS the spec model. Subagents in Phases 1, 3, 3.5 follow this lead by selecting Tier A; Phase 4 implementation work uses Tier B. See AGENTS.md.
Team personality selection
Before the Architecture question, decide what kind of team should solve this
request. Infer the team personality from the user's request, Phase 1 evidence,
repository labels, relevant memory files, and past specs under docs/specs/.
The goal is to shape the design as a fitting team would reason about it, not to
treat every request as a generic "fix this bug" task.
Write a Team personality section into the design spec with:
- the selected team name,
- 3-6 roles,
- why this team fits the request,
- the risks this team is expected to notice,
- any team emphasis that should carry into child issues.
Also write a Review counter-team subsection. It must be a different
emphasis from the implementation team and should challenge likely blind spots,
not duplicate the same perspective. Include:
- the counter-team name,
- 2-5 roles,
- which assumptions or failure modes this team should challenge,
- how review should stay inside the issue scope while applying that lens.
If confidence is low, ask the user explicitly: What kind of team should solve this problem?
Offer exactly these five starter combinations:
- Core product engineering — product manager, architect, backend developer, frontend developer, test engineer.
- Reliability/backend — backend developer, platform engineer, sysadmin/SRE, database engineer, security advisor.
- Frontend/product — frontend developer, UX designer, accessibility reviewer, API/backend developer, QA engineer.
- Security-sensitive — security advisor, architect, backend developer, platform engineer, test engineer.
- Legacy/refactor — architect, maintainer, backend/frontend developer as needed, test engineer, documentation owner.
If one option is clearly best, proceed without asking and record the confidence
and evidence in the spec. If two or more options are plausible and would change
the architecture, tests, or decomposition, ask before continuing.
Derive the Review counter-team after choosing the implementation team:
- Frontend/product implementation teams should be reviewed by accessibility, API contract, and QA perspectives.
- Reliability/backend implementation teams should be reviewed by security, operations, and data-integrity perspectives.
- Security-sensitive implementation teams should be reviewed by product/UX, maintainer, and test perspectives.
- Legacy/refactor implementation teams should be reviewed by maintainer, regression-test, and architecture perspectives.
- Core product engineering implementation teams should be reviewed by security, operations, accessibility, and maintainability perspectives.
Run a structured brainstorm — one question at a time, get explicit approval after each section:
- Architecture — where does new code live, what existing patterns does it follow.
- Interactivity / API shape — how does the user/caller drive it; commands, keys, endpoints, fields.
- Data model — types, columns, persistence boundary.
- Error handling — failure modes, recovery, user-visible signals.
- Testing — unit / integration / e2e split, real services vs anything else (rule per AGENTS.md: real services). For app/UI work, include an
autospec-qa revalidation plan that maps each spec requirement to running-app checks for text inputs, validation, selects/dropdowns, buttons, forms, API effects, accessibility, responsive behavior, negative paths, regenerated automated tests, proof artifacts, reliability contract, control intent ledger, no-mock minimum coverage, mutation/breakage proof, duplicate-code review, post-merge canary, new-code intent checks, artifact freshness, evidence provenance, console/network error gates, spec contradiction detection, data lifecycle proof, observability, flake quarantine, legacy removal/spec cleanup, and reliability exhaustion loop.
Write the agreed design to docs/specs/YYYY-MM-DD-<topic>-design.md. Self-review for placeholders, contradictions, ambiguity, and scope. Then run a critical improvement check: ask "What else could fail even if this spec and its obvious tests pass, and how could this be better?" Add the highest-risk answer to Testing, Acceptance Criteria, or Review counter-team. For app/UI work, explicitly consider mocked-vs-deployed behavior, backend fallbacks, user-visible outcomes, and no-mock smoke coverage. The spec must be implementable end-to-end by an agent reading only the spec.
Documentation visualization
When the requested work creates or regenerates documentation, the design spec
MUST require Mermaid diagrams and charts where acceptable. Add a concrete visual
plan for queues, routers, state machines, data flow, control flow, algorithms,
architecture boundaries, timelines, ownership relationships, decision spaces,
roadmaps, or metric trends whenever one of those structures exists in the target
feature. Select the Mermaid type that fits the explanation: flowchart, sequence
diagram, state diagram, class/entity diagram, Gantt, timeline, journey, quadrant
chart, gitGraph, mindmap, block, architecture, Sankey, XY, pie, kanban, or
another Mermaid-supported diagram type. Prefer Mermaid blocks in Markdown for
reviewer-visible explanations; use docs/assets/diagrams/ only when a generated
asset is needed outside a Markdown page. If no diagram is useful, the spec must
state Mermaid: not applicable with the reason, so omission is explicit rather
than accidental.
If this is a fresh repo, commit the spec to main directly (git add docs/... && git commit -m "docs: <topic> design spec" && git push) so subsequent issues can reference it as a tracked file.
For an existing repo, land the spec via a short-lived PR so CI can validate it:
- Create branch
feat/spec-<topic>, commit docs/specs/...design.md, push.
gh pr create --base main --head feat/spec-<topic> --title "docs: <topic> design spec" --body "Source spec: docs/specs/..."
- Wait for required CI checks to pass (
gh pr checks <#> --repo {repo}).
- Unless
AUTOSPEC_NO_AUTOMERGE_SPEC=1 is set, admin-merge: gh pr merge <#> --admin --squash --delete-branch --repo {repo}. If the env var is set, pause and ask the user to merge before continuing.
- Verify the spec is reachable at
https://github.com/{repo}/blob/main/docs/specs/... before dispatching Phase 3.
Phase 3 — Decompose into linked GitHub issues (delegate)
If Existing spec mode is active, use {selected_spec_path} and its GitHub URL.
Otherwise use the spec path written and merged in Phase 2.
Dispatch a foreground subagent with this prompt (substitute the spec path and {repo}):
Model tier: TIER_A (spec work) — top model with extended thinking; resolved at startup.
Read the selected design spec at <spec-path> (<spec-github-url>) and split it into linked GitHub issues for {repo}.
Create labels (idempotent with --force): auto-implement (#0e8a16), epic (#b60205), plus any domain labels the spec calls for. Then create exactly N issues — first an EPIC umbrella (no auto-implement label, just epic + domain), then N-1 children all carrying auto-implement. After creating children, edit the umbrella body with a checklist linking them. Return JSON: {umbrella, children:[…], labels_created:[…]}. Use gh CLI only. Do NOT modify code. Do NOT push branches. Do NOT create PRs.
Each child body must be a self-contained mini-spec sized for execution by a 32B-class local LLM, with these sections in order:
- Goal — 1 sentence outcome.
- Source spec —
<spec-path> + <spec-github-url> of the design doc this issue derives from.
- Team personality — copy the spec's selected team name, roles, and issue-relevant emphasis. If the selected spec lacks this section, infer it from the request, past specs, repository labels, and memory; if confidence is low, stop and ask the operator to choose from the five starter combinations in Phase 2 before filing issues.
- Review counter-team — copy the spec's counter-team name, roles, and issue-relevant blind spots to challenge. If the selected spec lacks this section, derive a different review emphasis from the Team personality and issue risk before filing.
- Files to read first — 3–7 entries. Each entry is one of: a path with section anchors (do not say "read the whole spec"), the closest existing-file analogue to mirror, the test file or fixture pattern to follow, or a dependency issue with a one-line summary so the LLM doesn't fetch its body. Bias toward sectional anchors over full files.
- Local-LLM execution notes — one-line context-window recommendation (
32k routine, 64k stretch, or split into N subagents along <criterion> for issues exceeding ~30k tokens of staged context) and whether single-pass or subagent-split is recommended.
- Implementation scope and Out of scope as separate subsections (replaces the prior single "Scope" section).
- Implementation outline — file paths + function signatures + data flow.
- Tests required — TDD per AGENTS.md, real services, no DB mocks, 80%+ coverage.
- Acceptance criteria — checkbox list
[ ] only, no prose. Each item machine-checkable.
- Verification — split into a Primary smoke test (inner loop) with exactly one fast command, and Operator/full verification listing the remaining commands.
- Branch name —
feat/<slug>.
- Dependencies —
Depends on issue #N lines (parsed by the monitor).
Sizing rule: aim for ≤ 4 KB body. Issues that span more than 4 canonical tables, more than 3 packages, or schema-wide changes must be split — better to emit two children with a Depends on edge than one oversized child a small LLM can't hold in working memory.
Sizing caps (hard, per spec §3.4):
- Body ≤400 words including all sections.
- Implementation outline ≤30 lines (file paths + function signatures).
- Files touched ≤3 per child issue.
- If a candidate child would exceed any cap, split into a parent + child pair with a
Depends on edge.
- The whole spec + a single child issue body must fit comfortably in a 60–120k context window.
Self-check each issue against the caps before calling gh issue create. If a cap is violated and a split is not feasible, surface the issue inline (print the over-cap body to the operator) instead of filing it.
Pre-filing lint loop (adaptive, MAX_LINT_RETRIES=5): For each candidate child body, before calling gh issue create, write the body to /tmp/draft-<slug>.md and run bash "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/lint-issue.sh" /tmp/draft-<slug>.md. If the exit code is non-zero, map each RULE_ID: <desc> finding to an actionable directive using the table below, append all directives to the next generation prompt as cumulative context, and regenerate. Repeat up to MAX_LINT_RETRIES=5 attempts. If attempt 5 still fails, print all 5 drafts plus accumulated findings inline and skip that child (do not file); continue to the next child. On pass (exit 0), proceed to the safety loop and only call gh issue create after safety passes.
| Finding | Directive appended to next prompt |
|---|
GOAL_VAGUE: "improve" used without concrete object | AVOID: bare verb \improve` without naming a file path, command, label, or number in the same sentence.` |
GOAL_HEDGE: "should probably" | AVOID: hedging words \should/might/could try/try to`. State the outcome flatly.` |
GOAL_NOT_ONE_SENTENCE: N terminals | REWRITE: Goal must be exactly one sentence ending with a single . ? or ! |
AC_PROSE: line N not a checkbox | FORMAT: every AC line must start with \- [ ] ` followed by content.` |
AC_SUBJECTIVE: "looks clean" | AVOID: subjective adjectives \looks/feels/seems/clean/elegant` in AC items. Use a `grep`/`test`/`diff`/`bats` command instead.` |
AC_TOO_LONG: N chars | SHORTEN: AC item exceeds 120 chars; split into two items or compress to one assertion. |
AC_EMPTY | ADD: Acceptance criteria section must contain at least one \- [ ] ` checkbox item.` |
SMOKE_MULTI_LINE: N lines | COLLAPSE: Primary smoke test must be exactly one command line. Use \&&` to chain or move setup to Operator/full verification.` |
SMOKE_PLACEHOLDER: contains "<TODO>" | RESOLVE: Replace placeholders \/TBD/XXX/...` with the actual command before filing.` |
SMOKE_NOT_FENCED | ADD: Primary smoke test section must contain exactly one fenced code block. |
Pre-filing safety loop (adaptive, MAX_SAFETY_RETRIES=5): For each candidate child body, after the issue-quality lint passes and before gh issue create, run "${AUTOSPEC_BIN:-autospec}" lint issue safety --title "<candidate title>" /tmp/draft-<slug>.md. If the exit code is 1 or 2, append the safety findings to the next generation prompt as cumulative directives:
| Finding | Directive appended to next prompt |
|---|
SAFETY_BLOCK: production-data-destruction | BLOCKED: remove production data destruction from scope; rewrite for test/dev data only or split to human-reviewed production plan. |
SAFETY_BLOCK: secret-exfiltration | BLOCKED: never request printing, dumping, sending, or exposing secrets or tokens. |
SAFETY_BLOCK: instruction-bypass | BLOCKED: never ask the implementer to ignore AGENTS.md, system/developer instructions, CI, review, hooks, or guardian checks. |
SAFETY_AMBIGUOUS | CLARIFY: add explicit non-production scope, affected paths, guardrails, and verification command; otherwise skip filing. |
Repeat up to MAX_SAFETY_RETRIES=5. If attempt 5 still returns non-zero, print all drafts plus safety findings inline and skip that child. Do not file unsafe or ambiguous child issues.
Small-LLM friendliness (applies to every child issue)
Children are written assuming the implementer is a 32B-class local model with pre-staged context, not a search-driven cloud agent:
- Every file the implementer needs is named in Files to read first with a sectional anchor or a one-line reason. Do not assume the model will grep the codebase.
- Spec docs are cited by section heading, not as "read this 20 KB doc".
- Acceptance criteria are checkbox-only so the model can self-verify line-by-line.
- One Primary smoke test runs in the inner loop; the heavier verification list runs once at the end.
- If the work fans out across many tables/packages, split it. Two 3 KB children chained by
Depends on beat one 7 KB child a 32B model garbles at 60k tokens of working context.
Capture the umbrella + child issue numbers.
Phase 3.5 — Review and label (delegate)
Dispatch a foreground subagent to retro-review the child issues just
created in Phase 3 and apply the model-fit rubric. The subagent must NOT modify
issue titles or remove existing labels; it only adds ctx:* and reasoning:*
labels and patches each body with a ## Model fit block.
Model tier: TIER_A (spec work) — top model with extended thinking; resolved at startup.
Walk every child issue created in Phase 3 (skip any issue carrying the
type:tracker label). For each:
-
Stage context. Read gh issue view <N> --repo {repo} --json title,body,labels. The
body should already contain ## Files to read first, ## Implementation scope, ## Team personality, and ## Review counter-team. If any is missing, add label
needs-autospec-template (idempotent gh label create --force once at run
start) and skip — do not classify or patch.
-
Apply the rubric. Pick the smallest ctx:* tier that holds the
staged context (issue body + every file in ## Files to read first +
cited spec sections). Pick the reasoning:* depth required to derive
(not just transcribe) the implementation.
ctx:* — context-window axis
| Label | Trigger |
|---|
ctx:32k | One canonical table or shell script; ≤3 files in Files to read first; short spec anchors. |
ctx:64k | Multi-file change; 4-7 files staged; one trio + one installer; medium spec sections (~1-3 KB). |
ctx:120k | Cross-skill or cross-package; 8+ files; long spec excerpts; deep call graphs. |
reasoning:* — reasoning-depth axis
| Label | Trigger |
|---|
reasoning:shallow | Mechanical: copy-and-rename, regex-replace, README transcription, runbook authoring. Verbs: copy, rename, transcribe, list. |
reasoning:medium | Template-following with judgment: synthesize a SKILL.md mirroring an existing one, modify a script with new flags, write tests for a documented contract. Verbs: mirror, adapt, integrate, wire. |
reasoning:deep | Novel design choices: pick a new abstraction, resolve a contradiction in the spec, reconcile cross-cutting concerns. Verbs: design, reconcile, resolve, redesign. |
Default for issues that lack any of these signals: ctx:64k,
reasoning:medium. If unsure between two ctx tiers, prefer the larger.
-
Sibling normalization. When 5+ split children share a structural
criterion (e.g. all per-source-table writers, all per-skill installers),
harmonize their ctx:*/reasoning:* labels so the operator can run a
single profile across the whole group. Override individual classifications
only when the difference is a true outlier (e.g. one sibling pulls in a
schema-wide refactor that no other sibling touches).
Issue intent safety gate
Phase 3.5 may only persist model-fit, quality, board, and dependency
metadata. It must not manufacture a safety decision. Defer Rust admission
until the final issue body is persisted by step 8 below.
-
Apply labels. Idempotent at run start:
gh label create ctx:32k --color c5def5 --force --repo {repo},
gh label create ctx:64k --color c5def5 --force --repo {repo},
gh label create ctx:120k --color c5def5 --force --repo {repo},
gh label create reasoning:shallow --color c2e0c6 --force --repo {repo},
gh label create reasoning:medium --color c2e0c6 --force --repo {repo},
gh label create reasoning:deep --color c2e0c6 --force --repo {repo},
gh label create needs-quality-bar --color fbca04 --force --repo {repo}.
Then per issue:
gh issue edit <N> --add-label "ctx:<tier>,reasoning:<depth>" --repo {repo}.
-
Patch body — ## Model fit block. Insert immediately before the first
## Dependencies line (or at end of body if absent):
## Model fit
- **ctx:** `ctx:<tier>` — <1-line rationale>.
- **reasoning:** `reasoning:<depth>` — <1-line rationale>.
<!-- autospec-classify:begin -->
*Auto-classified by Phase 3.5 on YYYY-MM-DD.*
<!-- autospec-classify:end -->
Idempotency: if a ## Model fit block already exists between the
<!-- autospec-classify:begin --> and <!-- autospec-classify:end -->
markers, replace it in place. Never stack duplicates. Apply via
gh issue edit <N> --body-file <tmp>.
-
Board assignment — read ~/.autospec/project-map.yml and assign each
just-classified child to the GitHub Projects mapped from its labels.
File schema (auto-init if missing — see below):
multi_match: union
mappings:
ctx:32k: <project_number>
ctx:64k: <project_number>
ctx:120k: <project_number>
reasoning:shallow: <project_number>
reasoning:medium: <project_number>
reasoning:deep: <project_number>
<any-other-label>: <project_number>
Reader procedure for each issue I:
- For each label L on I, look up
mappings[L]. Skip null / missing entries.
- With
multi_match: union (default), collect all matching project numbers and assign to every one of them. With multi_match: first, take the first match in label-order and assign to that single project.
- For each chosen
<P>: gh project item-add <P> --owner <owner> --url <issue-url>. The gh command is idempotent — repeated calls do not duplicate items, so re-running Phase 3.5 is safe.
Auto-init when the file is missing. Probe gh project list --owner <owner> --format json to confirm the user can author projects. Probe gh label list --repo {repo} --json name -q '.[].name' to enumerate the repo's labels. Write a starter file with every label as a mappings: key and null project numbers, plus multi_match: union at the top. Print:
Wrote ~/.autospec/project-map.yml. Edit project numbers (currently null) and re-run.
Then exit Phase 3.5 without assigning any boards (the labels and ## Model fit blocks remain applied — only the assign step is deferred).
Hard rules.
- Never call
gh project item-add in --dry-run.
- Missing file in
autospec / autospec-define is non-fatal at run time once auto-init has populated it; if auto-init itself fails (e.g. gh project list denied), warn and skip board assignment for the rest of the run.
-
Dependency-edge sanity checks. After labeling, validate the dep graph
of the just-created children:
- closed-dep warning — emit
WARN: child #<N> depends on closed issue #<M> for each Depends on #M line where gh issue view #<M> --json state is CLOSED.
- child-less tracker dep warning — emit
WARN: child #<N> depends on tracker #<M> with no children when #<M> carries type:tracker and has no other open auto-implement deps pointing at it.
- circular sibling-dep hard fail — exit non-zero if any cycle exists among the just-created children's
Depends on #N edges.
-
Post-filing quality audit. For each child issue (skip type:tracker):
- Pull body:
gh issue view <N> --repo {repo} --json body -q .body > /tmp/audit-<N>.md
- Run:
bash "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/lint-issue.sh" /tmp/audit-<N>.md
- On non-zero exit (lint fails):
- Do NOT remove
auto-implement label. Operator decides whether to proceed.
-
Rust safety admission. After all prior body writes complete, make each
child an interim candidate (if it is not already one) and review that exact
issue:
gh issue edit <N> --add-label auto-implement --remove-label needs-classify --repo {repo}
"${AUTOSPEC_BIN:-autospec}" queue review-safety --repo {repo} --limit 1 --issue <N>
Read the JSON totals. Only pass: 1 admits this invocation. Any other
result is already recorded by Rust and must be skipped without manual
labels, comments, body patches, or a semantic safety override. The Rust
command is the only automatic writer of issue-intent safety outcomes.
-
Run-end summary. Print to stdout:
Phase 3.5 summary on {repo}
- classified: N
- skipped (needs-autospec-template): M
- ctx:32k=A ctx:64k=B ctx:120k=C
- reasoning:shallow=X reasoning:medium=Y reasoning:deep=Z
- boards assigned: <K> (or "skipped — no project-map.yml")
- dep warnings: <count>; circular cycles: <count>
Phase 3 pre-impl gate
After Phase 3 decomposition completes (and Phase 3.5 review-and-label has
applied ctx:* / reasoning:* labels), end Phase 3 by asking the user
this verbatim question (no auto-launch — always ask explicitly, per spec
§3.3):
"Spec written, N issues filed. Start /autospec-run now, defer to your external daemon, or keep refining? [run / defer / refine]"
Substitute N with the actual issue count from Phase 3. Default highlight
is run for /autospec (the umbrella end-to-end skill — the user
invoked /autospec expecting end-to-end shipping, so the gate defaults
to run).
run (default for /autospec) — proceed to Phase 4 (the existing
background autonomous monitor launch path below).
defer — print
"Issues are ready. Your external monitor will pick them up. Exiting."
and stop. For /autospec, this means skipping Phase 4–6 entirely; the
just-filed auto-implement queue persists on the GitHub side so an
external daemon (or a later /autospec-run invocation) can pick it up.
refine — re-enter Phase 2 from question 1 (Architecture).
No daemon auto-detection — always ask explicitly.
Once run is selected and /autospec-run starts, do not ask operator questions again for this run unless a true hard blocker requires explicit manual recovery.
Phase 4 — Background autonomous monitor
Record this durable preference in AGENTS.md (idempotent — skip if already present):
Auto-merge authority for auto-implement PRs. Admin-merge auto-implement PRs (gh pr merge <#> --admin --squash --delete-branch) when (a) the full target-repo validation/test suite has passed locally after the branch is current with main, (b) all required CI checks pass — slow optional checks like TeamCity may be pending only after the full local suite is green, (c) the self-review subagent returned LGTM, (d) PR closes an auto-implement issue from a feat/* branch.
Off-peak tip: For queues of 10+ issues (8+ hour runs), consider launching at night or on weekends. Usage limits are shared across all sessions — running long batches off-peak preserves daytime tokens for interactive work.
Fresh-subagent-per-issue (canonical Phase 4 path, formerly single-agent absorbed-discipline). Each issue is processed by a FRESH top-level subagent dispatched by the orchestrator — expand → implement → finalize → peer-review → evaluate-findings → PR → merge — in that subagent's own context. The orchestrator NEVER implements in its own context; it only claims, dispatches, and waits. Each subagent receives full tool access because it is a top-level Agent call launched directly by the main session orchestrator. Constraint: Subagents spawned by background Agent calls do NOT inherit the Agent tool, so nested monitors cannot dispatch inner implementers. Phase 4 implementers must be top-level agents launched directly by the main session orchestrator, not nested inside a monitor. The process(ISSUE) notation below is shorthand for "dispatch a fresh top-level subagent to do this work", NOT in-context implementation by the orchestrator. Batch>1 is an explicit operator opt-in via AUTOSPEC_BATCH_SIZE=N; the default is 1 (one issue per subagent). The reasoning:deep force-to-1 rule is retained: deep issues stay batch=1 even under an operator AUTOSPEC_BATCH_SIZE=N override.
Then launch a background monitor loop — the orchestrator relaunches the monitor with fresh context after each batch of AUTOSPEC_BATCH_SIZE issues (default: 1). The monitor is stateless: all persistent state lives in GitHub labels and heartbeat files, so relaunches are always safe.
batch_num=1
while true:
launch background subagent (pass batch_num; AUTOSPEC_BATCH_SIZE=${AUTOSPEC_BATCH_SIZE:-1})
wait for task-notification (monitor agent completes)
# Read and consume the batch-done signal.
if [ -f "$HOME/.autospec/batch-done.json" ]; then
status=$(jq -r .status "$HOME/.autospec/batch-done.json" 2>/dev/null || echo "BATCH_COMPLETE")
rm -f "$HOME/.autospec/batch-done.json"
else
status="BATCH_COMPLETE" # monitor crashed / overflowed — safe to relaunch
fi
if [ "$status" = "ALL_DONE" ]; then
break # proceed to Phase 6 final report
fi
batch_num=$((batch_num + 1))
echo "[orchestrator] batch $((batch_num - 1)) complete — relaunching monitor with fresh context (batch ${batch_num})"
# continue immediately, no sleep
Pass the following prompt verbatim to each background subagent:
You are the auto-implement monitor for {repo}. Process auto-implement issues one at a time. Exit after processing AUTOSPEC_BATCH_SIZE issues (default: 1) by writing ~/.autospec/batch-done.json — the orchestrator will relaunch you with fresh context.
Harness adaptation (loop persistence). The while true: below is pseudocode. In Claude Code, use /loop or ScheduleWakeup to persist across turns. In Codex CLI and OpenCode, you lack a built-in loop primitive — implement persistence via one of these patterns:
- Shell wrapper (preferred): `exec bash << 'EOF'
while true; do
... monitor logic ...
done
EOF— keeps a single bash process alive with your agent dispatching subcommands inside it. 2. **nohup background process:**nohup bash -c 'while true; do ...; sleep 1; done' > /.autospec/monitor.log 2>&1 &3. **tmux pane:**tmux new-window 'bash << '''HEREDOC'''
while true; do ...; done
HEREDOC'**Session batching:** Exit after processingAUTOSPEC_BATCH_SIZEissues (default 1) by writing/.autospec/batch-done.jsonwithstatus=BATCH_COMPLETE. The orchestrator relaunches you with fresh context. When the queue is fully drained, write status=ALL_DONE` instead. This keeps each monitor session short to prevent context overflow.
Shared helper scripts. Helper scripts live at ${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts} after installation. Do not assume the target repository has an autospec scripts/ directory.
Outer loop:
# Before the loop (run-once init):
# batch_issue_count=0
# BATCH_SIZE="${AUTOSPEC_BATCH_SIZE:-1}"
# [ "$BATCH_SIZE" -gt 0 ] 2>/dev/null || BATCH_SIZE=3 # guard against 0 or negative
# rm -f "$HOME/.autospec/batch-done.json" # clear stale file from prior crash
while true:
# Startup/per-scan heartbeat reconciliation — run before candidate selection.
# This deletes closed/merged/orphaned heartbeats, rejects old schemas like
# {"issue":407,"status":"in_progress"}, normalizes current schemas, and
# releases any `claimed` heartbeat older than AUTOSPEC_WATCHDOG_CLAIMED_TIMEOUT_SECS (default: 300).
if [ -d "$HOME/.autospec/process-heartbeats" ]; then
if command -v bash >/dev/null 2>&1; then
bash "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/autospec-watchdog.sh"
elif command -v pwsh >/dev/null 2>&1; then
pwsh -NoProfile -ExecutionPolicy Bypass -File "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/autospec-watchdog.ps1"
elif command -v powershell >/dev/null 2>&1; then
powershell -NoProfile -ExecutionPolicy Bypass -File "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/autospec-watchdog.ps1"
else
echo "[watchdog] neither bash nor powershell found; skipping heartbeat reconciliation."
fi
fi
all_open = [open auto-implement issues, sorted ascending by issue number]
ready = [all_open issues whose Depends-on deps are all CLOSED, sorted ascending]
blocked = [all_open issues with unmet Depends-on deps]
claimed_issue, claimed_step = [newest valid heartbeat issue/step, or "-" / "-"]
print "[monitor] queue scan: open=N ready=N blocked=N deferred=0 claimed=#X step=Y order=ascending(oldest-first)"
# GitHub may display newer/high-numbered issues first; autospec intentionally processes ready issues ascending.
if ready is empty:
latest_close = most recent closedAt of any auto-implement issue
open_count = count of open auto-implement issues
if open_count == 0 AND latest_close > 2h ago:
printf '{"batch":%s,"processed":%s,"repo":"%s","ts":%s,"status":"ALL_DONE"}\n' \
"${batch_num:-1}" "$batch_issue_count" "{repo}" "$(date -u +%s)" \
> "$HOME/.autospec/batch-done.json"
echo "[monitor] all issues processed — writing ALL_DONE and exiting"
HARD SHUTDOWN — return final report
else: print state ("blocked: N unmet deps" / "drained, waiting 2h idle"), sleep 300, continue
# autospec-stop sentinel check — outer loop, top of each iteration
if [ -f "$HOME/.autospec/stop.flag" ]; then
MODE=$(head -1 "$HOME/.autospec/stop.flag" 2>/dev/null || echo "")
TIMESTAMP=$(sed -n '2p' "$HOME/.autospec/stop.flag" 2>/dev/null | awk '{print $1}')
AGE_SECS=$(( $(date -u +%s) - $(date -u -j -f '%Y-%m-%dT%H:%M:%SZ' "$TIMESTAMP" +%s 2>/dev/null \
|| date -u -d "$TIMESTAMP" +%s 2>/dev/null || echo 0) ))
if [ "$AGE_SECS" -gt 86400 ]; then
echo "WARN: stale stop.flag ($AGE_SECS s old); ignoring" >&2
elif [ "$MODE" = "graceful" ] || [ "$MODE" = "immediate" ]; then
printf '{"batch":%s,"processed":%s,"repo":"%s","ts":%s,"status":"ALL_DONE"}\n' \
"${batch_num:-1}" "$batch_issue_count" "{repo}" "$(date -u +%s)" \
> "$HOME/.autospec/batch-done.json"
echo "[monitor] stop signal received: $MODE — exiting"
# HARD SHUTDOWN with final report
exit 0
fi
fi
# Service watch: heartbeat reconciliation already runs before each candidate scan; every 12 iterations also runs a cheap nudge/reclaim pass for long-lived workers.
monitor_tick=$((monitor_tick + 1))
if [ "$monitor_tick" -ge 12 ]; then
monitor_tick=0
if [ -d "$HOME/.autospec/process-heartbeats" ]; then
# Default reclaim window: 10800s (3h). For local single-threaded workers set
# AUTOSPEC_WATCHDOG_RECLAIM_SECS=43200 (12h) before launch.
export AUTOSPEC_WATCHDOG_RECLAIM_SECS="${AUTOSPEC_WATCHDOG_RECLAIM_SECS:-10800}"
export AUTOSPEC_WATCHDOG_STALE_SECS="${AUTOSPEC_WATCHDOG_STALE_SECS:-1800}"
# Cheap service wake-up pass: use low-cost model only.
if command -v bash >/dev/null 2>&1; then
# Dispatch one background watchdog helper (cheap model) to iterate stale entries.
bash "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/autospec-watchdog.sh"
elif command -v pwsh >/dev/null 2>&1; then
# Windows fallback: PowerShell helper.
pwsh -NoProfile -ExecutionPolicy Bypass -File "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/autospec-watchdog.ps1"
elif command -v powershell >/dev/null 2>&1; then
# Windows fallback: classic PowerShell fallback.
powershell -NoProfile -ExecutionPolicy Bypass -File "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/autospec-watchdog.ps1"
else
echo "[watchdog] neither bash nor powershell found; skipping service-watch pass."
fi
fi
fi
ISSUE = ready[0]
# Claim check: verify the issue is still labeled auto-implement before processing.
# Multiple monitors can query the same candidate list simultaneously;
# the first to claim wins, others must skip to the next candidate.
CURRENT_LABELS=$(gh issue view ISSUE --json labels --jq -r '.labels[].name')
if ! echo "$CURRENT_LABELS" | grep -q "^auto-implement$"; then
echo "[monitor] ISSUE $ISSUE already claimed (no auto-implement label); skipping"
READY_REMOVE=$(printf "%s\n%s" "$READY_REMOVE" "$ISSUE" | grep -v "^${ISSUE}$" || true)
ready=($READY_REMOVE)
continue
fi
gh label create in-progress-by-bot --color ededed --force
if ! gh issue edit ISSUE --remove-label auto-implement --add-label in-progress-by-bot; then
echo "[monitor] ISSUE $ISSUE claim failed (another monitor claimed it); skipping"
continue
fi
mkdir -p "$HOME/.autospec/process-heartbeats"
printf '{"issue":"%s","branch":"","step":"claimed","ts":%s,"pr":"","repo":"%s"}\n' "$ISSUE" "$(date -u +%s)" "{repo}" > "$HOME/.autospec/process-heartbeats/$ISSUE.json"
# Issue start summary — print before dispatching process(ISSUE) so the operator
# knows exactly what the monitor is about to work on.
# Single body fetch — all later steps consume this file (D5: duplicate-read elimination).
_issue_body_file="/tmp/issue-${ISSUE}-body.md"
gh issue view ISSUE --json body --jq .body 2>/dev/null > "$_issue_body_file" || true
ISSUE_TITLE=$(gh issue view ISSUE --json title --jq .title 2>/dev/null || echo "")
ISSUE_URL=$(gh issue view ISSUE --json url --jq .url 2>/dev/null || echo "")
ISSUE_LABELS=$(gh issue view ISSUE --json labels --jq -r '[.labels[].name] | join(", ")' 2>/dev/null || echo "")
ISSUE_GOAL=$(awk '
BEGIN{in_goal=0}
/^## Goal[[:space:]]*$/ {in_goal=1; next}
/^## / && in_goal {exit}
in_goal && NF {print; exit}
' "$_issue_body_file")
[ -n "$ISSUE_GOAL" ] || ISSUE_GOAL=$(awk 'NF && $0 !~ /^#/ {print; exit}' "$_issue_body_file")
ISSUE_SMOKE=$(awk '
/### Primary smoke test/ {seen=1; next}
seen && /^```/ {fence++; next}
seen && fence==1 && NF && $0 !~ /^[[:space:]]*#/ {print; exit}
' "$_issue_body_file")
ISSUE_SCOPE=$(awk '
/^## Implementation outline[[:space:]]*$/ {in_scope=1; next}
/^## / && in_scope {exit}
in_scope && /^- / {gsub(/^- /,""); print; count++; if (count>=3) exit}
' "$_issue_body_file" | paste -sd '; ' -)
echo "[monitor] starting #$ISSUE: ${ISSUE_TITLE:-<untitled>}"
echo "[monitor] url: ${ISSUE_URL:-<unknown>}"
echo "[monitor] labels: ${ISSUE_LABELS:-<none>}"
echo "[monitor] goal: ${ISSUE_GOAL:-<not provided>}"
echo "[monitor] smoke: ${ISSUE_SMOKE:-<not provided>}"
echo "[monitor] scope: ${ISSUE_SCOPE:-<not provided>}"
process(ISSUE) # single-agent absorbed-discipline — the monitor IS the implementer; see template below
batch_issue_count=$((batch_issue_count + 1))
if [ "$batch_issue_count" -ge "$BATCH_SIZE" ]; then
printf '{"batch":%s,"processed":%s,"repo":"%s","ts":%s,"status":"BATCH_COMPLETE"}\n' \
"${batch_num:-1}" "$batch_issue_count" "{repo}" "$(date -u +%s)" \
> "$HOME/.autospec/batch-done.json"
echo "[monitor] batch ${batch_num:-1}: processed $batch_issue_count/$BATCH_SIZE issues — writing batch-done.json and exiting for fresh context"
exit 0
fi
# Immediate next-issue pickup: NO SLEEP after process(ISSUE). Re-enter the top
# of this loop immediately so the fresh queue scan can pick any issue unblocked
# by the merge or failure cleanup that just completed.
process(ISSUE) is single-agent absorbed-discipline: the monitor agent performs the implementation work itself, in-context, using the prompt below as its working brief. There is NO nested subagent dispatch here — subagents spawned by background Agent calls do NOT inherit the Agent tool, so the monitor must own the work directly:
**Model tier:** `TIER_B` (implementation work) — cheaper model with medium thinking; resolved at startup. Silently fall back to `TIER_A` if unavailable.
**Hard limits.** Max 40 tool calls per issue. Max 3 self-review iterations. If you rewrite the same file twice with no test progress, abort: comment the blocker on the issue, release the lock label, exit. No wall-clock cap.
Implement GitHub issue #<ISSUE>: "<TITLE>" on {repo}. Spec is the issue body below.
===ISSUE BODY===
<BODY>
===END===
## Team personality as execution lens
Read the issue body's **Team personality** section before choosing an
approach. Let that team shape what you emphasize: a reliability/backend team
should scrutinize operational safety and data boundaries, a frontend/product
team should scrutinize user workflow and accessibility, a security-sensitive
team should scrutinize trust boundaries and abuse cases, and so on. Do not
invent extra scope; use the team personality to decide which risks deserve
extra attention while still satisfying the issue's concrete acceptance
criteria.
Keep a progress heartbeat so the monitor can prove forward movement:
- Create/update `~/.autospec/process-heartbeats/<ISSUE>.json` at each major step:
- `claimed`, `worktree_ready`, `tests_started`, `tests_passed`, `pr_created`, `smoke_retry`, `reviewed`, `merged`, `failed`
- Schema: `{"issue":"<ISSUE>","branch":"<BRANCH>","step":"<STEP>","ts":<unix_epoch>,"pr":"<PR>","repo":"{repo}"}`
- Delete this file on terminal SUCCESS/FAILURE in both clean and failure paths.
1. **PR-aware recovery ladder, then worktree.** Resolve the branch state FIRST, then act on the verdict. NEVER `cd`/`git checkout`/`git commit` in the primary checkout — all work happens in a linked worktree off `origin/main`.
<!-- worktree-ladder:begin -->
```bash
cd {repo_root} && git fetch origin
LADDER=$(bash ${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/worktree-guard.sh resolve-branch --branch <BRANCH> --repo {repo})
STATE=$(printf '%s' "$LADDER" | sed -n 's/.*"state":"\([^"]*\)".*/\1/p')
PR=$(printf '%s' "$LADDER" | sed -n 's/.*"pr":\([0-9]*\).*/\1/p')
case "$STATE" in
open-pr)
# #886 recovery: a PR already exists. SKIP implementation entirely.
# Check out the existing PR in a fresh worktree, run the issue's
# verification (tests + validate.sh) and the standard review loop
# against the EXISTING PR, then merge if green. Never re-implement.
bash ${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/worktree-guard.sh create --branch <BRANCH> --path /tmp/wt-<BRANCH> --adopt
cd /tmp/wt-<BRANCH>
gh pr checkout "$PR"
echo "[ladder] open-pr #$PR — skip implementation; verify + review + merge existing PR"
;;
branch-only)
# #917 recovery: the branch exists with un-PR'd work. Adopt it in a
# fresh worktree and CONTINUE the remaining work (do not start over).
bash ${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/worktree-guard.sh create --branch <BRANCH> --path /tmp/wt-<BRANCH> --adopt
cd /tmp/wt-<BRANCH>
echo "[ladder] branch-only — adopted <BRANCH>; continue remaining work"
;;
fresh|*)
# No branch, no PR: create a new worktree off origin/main.
bash ${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/worktree-guard.sh create --branch <BRANCH> --path /tmp/wt-<BRANCH>
cd /tmp/wt-<BRANCH>
echo "[ladder] fresh — created <BRANCH>"
;;
esac
# MANDATORY assert gate: MUST exit 0 before the first file edit/commit. A
# non-zero exit (in_primary_checkout / dirty / stale_base) is NEVER worked
# around — comment the emitted code_health identifier on the issue, restore
# the `auto-implement` label (swap `in-progress-by-bot` → `auto-implement`),
# remove the heartbeat, and stop this issue.
if ! bash ${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/worktree-guard.sh assert; then
gh issue comment <ISSUE> --body "worktree-guard assert failed (see code_health identifier above); restoring auto-implement"
gh issue edit <ISSUE> --remove-label in-progress-by-bot --add-label auto-implement
exit 1
fi
On the open-pr path the verification bar EQUALS fresh work — full tests + the standard review loop, never a blind merge. Cleanup is identical for every path: after the merge is confirmed (or on terminal failure), git worktree remove the linked worktree and git worktree prune; never delete un-pushed work before merge.
- TDD per AGENTS.md: failing test first → implement → refactor → commit. NO DB/external mocks. Follow file paths and signatures from the issue body verbatim.
- Full test suite gate. Run the target repo's full validation/test suite, not only the Primary smoke test. Command resolution order:
- If
AUTOSPEC_FULL_TEST_COMMAND is set, run bash -lc "$AUTOSPEC_FULL_TEST_COMMAND".
- Else run every command listed under the issue's Operator/full verification section.
- Else run the repo-standard full suite:
autospec validate when present; otherwise use the ecosystem default (npm test, pytest, go test ./..., cargo test, mvn test, etc.).
If the full suite fails, fix the failure, recommit, rerun the full suite, and repeat. Do NOT dispatch LGTM review while the full suite is failing. Do NOT run gh pr merge while the full suite is failing. Record the exact full-suite command and passing output summary in the PR comment or final report.
3b.
Docs drift gate
Run after full test suite gate, before LGTM review. Skip if issue body contains a line matching ^docs:\s*skip\s*$ (case-insensitive). On drift/missing_scope/example_stale the classifier emits the pinned regenerate action carrying the affected scopes; the gate self-heals by invoking /autospec-doc (via doc-orchestrator.mjs) scoped to ONLY those scopes, re-verifying the regenerated pages with verify-examples.mjs, and committing docs: regenerate <scopes> onto the SAME PR branch. Doc generation NEVER blocks the code PR — generation/verification failure only warns, applies the docs:failed label, and comments; the code review loop continues. Only the regenerate commit's own example verification gates the regenerated docs (failing pages are NOT committed):
if ! grep -qiE '^docs:[[:space:]]*skip[[:space:]]*$' "/tmp/issue-<ISSUE>-body.md" 2>/dev/null; then
SCRIPTS_DIR="${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}"
DOC_SCRIPTS_DIR="${AUTOSPEC_DOC_SCRIPTS_DIR:-$HOME/.autospec/skills/autospec-doc/scripts}"
DRIFT_JSON="$(bash "$SCRIPTS_DIR/check-doc-drift.sh" --pr "<PR>" 2>/tmp/drift-<PR>.err)"; drift_exit=$?
if [ "$drift_exit" = "0" ]; then
:
else
VERDICT_JSON="$(printf '%s' "$DRIFT_JSON" | node "$SCRIPTS_DIR/loop-classifier-docs-extension.mjs" --drift-json - --issue "<ISSUE>" --pr "<PR>" 2>/dev/null || true)"
ACTION="$(printf '%s' "$VERDICT_JSON" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{process.stdout.write((JSON.parse(s).action)||"")}catch{}})' 2>/dev/null || true)"
if [ "$ACTION" = "regenerate" ]; then
mapfile -t SCOPES < <(printf '%s' "$VERDICT_JSON" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{for(const x of (JSON.parse(s).scopes||[]))console.log(x)}catch{}})' 2>/dev/null || true)
gh issue edit <ISSUE> --add-label "docs:drift" 2>/dev/null || true
_REGEN=$(node --input-type=module <<'__REGEN_EOF__' 2>/dev/null || echo "0"
import { resolveAutoRegenerate, loadConfig } from '${AUTOSPEC_DOC_SCRIPTS_DIR:-$HOME/.autospec/skills/autospec-doc/scripts}/doc-config.mjs';
import fs from 'node:fs';
const cfg = (() => { try { return loadConfig('.autospec/autospec.yml'); } catch { return {}; } })();
const body = (() => { try { return fs.readFileSync('/tmp/issue-<ISSUE>-body.md','utf8'); } catch { return ''; } })();
const flag = process.env.AUTOSPEC_WITH_DOCS === '1';
const { generate } = resolveAutoRegenerate({ config: cfg, issueBody: body, withDocsFlag: flag });
process.stdout.write(generate ? '1' : '0');
__REGEN_EOF__
)
if [ "${_REGEN:-0}" = "1" ]; then
# auto_regenerate is ON — run the regenerate self-heal path.
doc_ok=1
if node "$DOC_SCRIPTS_DIR/doc-orchestrator.mjs" 2>/tmp/docgen-<PR>.err; then
# Re-verify regenerated pages; only verified pages are committed.
if [ "${#SCOPES[@]}" -gt 0 ] && node "$DOC_SCRIPTS_DIR/verify-examples.mjs" "${SCOPES[@]}" 2>/tmp/docverify-<PR>.err; then
if ! git diff --quiet -- "${SCOPES[@]}" 2>/dev/null; then
git add -- "${SCOPES[@]}"
git commit -m "docs: regenerate ${SCOPES[*]}" || doc_ok=0
git push || doc_ok=0
fi
else
doc_ok=0 # example verification failed — do NOT commit regenerated docs
fi
else
doc_ok=0 # generation failed
fi
if [ "$doc_ok" = "0" ]; then
gh issue edit <ISSUE> --add-label "docs:failed" 2>/dev/null || true
gh pr comment <PR> --body "$(printf 'docs: regenerate self-heal failed (generation or example verification) — code PR NOT blocked. Scopes: %s' "${SCOPES[*]:-<none>}")" 2>/dev/null || true
else
gh pr comment <PR> --body "$(printf 'docs: regenerated %s in-PR (examples re-verified).' "${SCOPES[*]:-<none>}")" 2>/dev/null || true
fi
else
# auto_regenerate is OFF — log and continue; detection/labels already applied.
echo "docs: regeneration skipped (auto_regenerate=false)"
fi
else
# No regenerate verdict — surface drift for operator review; do not block.
gh pr comment <PR> --body "$(printf 'docs drift detected — no self-heal action emitted:\n\n```json\n%s\n```' "$DRIFT_JSON")" 2>/dev/null || true
gh issue edit <ISSUE> --add-label "docs:drift" 2>/dev/null || true
if [ "$drift_exit" = "2" ]; then gh issue edit <ISSUE> --add-label "docs:missing-scope" 2>/dev/null || true; fi
fi
fi
else
gh pr comment <PR> --body "docs: drift check skipped (docs:skip in issue body)" 2>/dev/null || true
gh issue edit <ISSUE> --add-label "docs:skipped" 2>/dev/null || true
fi
- Conventional commits (feat:/fix:/test:/docs:/refactor:). NEVER bypass hooks. NEVER amend.
- Push: git push -u origin
if ! bash "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/autospec-stop-check.sh" "$ISSUE" "$BRANCH" "$LAST_STEP"; then
exit 0
fi
- PR: gh pr create --base main --head --title "" --body "Closes #<ISSUE>\n\n<summary>". Capture PR.
- Inner loop (max 3 iterations):
if ! bash "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/autospec-stop-check.sh" "$ISSUE" "$BRANCH" "$LAST_STEP"; then
exit 0
fi
-
Run the Primary smoke test from the issue body. If it fails, fix and recommit before review.
-
Run the Full test suite gate before fused guardian + LGTM review. If it fails, fix and recommit, rerun the full suite, and do not dispatch review until the full suite passes.
-
Fused guardian + LGTM review (one subagent does both — saves one dispatch per inner-loop iteration):
Run deterministic lint first (no subagent cost):
rm -f /tmp/guardian-.md
if [ "${AUTOSPEC_NO_GUARDIAN:-0}" != "1" ]; then
bash "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/lint-implementation.sh" --issue >> /tmp/guardian-.md 2>&1
fi
det_exit=$?
Reuse lens (issues #1439/#1440/#1442): when armed, extract the reuse-triage
RULE_IDs from the deterministic findings so the reviewer prompt receives the
build-vs-buy block. Flag-OFF or no reuse hits → _reuse_flags_file stays empty
→ the reviewer prompt is byte-identical to today (the ${_reuse_flags_file:+…}
expansion below adds nothing):
_reuse_flags_file=""
if [ "${AUTOSPEC_REUSE_LENS:-}" = "1" ] && [ -f /tmp/guardian-<PR>.md ]; then
_reuse_candidate=$(mktemp -t autospec-reuse-flags-XXXXXX)
grep -E '^(REINVENT_REPO_UTIL|NEW_DEP_UNJUSTIFIED|NEW_ABSTRACTION_SINGLE_CALLER):' \
/tmp/guardian-<PR>.md > "$_reuse_candidate" 2>/dev/null || true
if [ -s "$_reuse_candidate" ]; then _reuse_flags_file="$_reuse_candidate"; fi
fi
Model tier: TIER_B for ALL issues — including regression and priority:high. The single fused reviewer carries the regression gap-check (see brief below), so no second Tier-A pass is dispatched. Escape hatch: AUTOSPEC_REVIEWER_TIER overrides the reviewer tier — unset (or any value other than opus) → TIER_B (sonnet); set AUTOSPEC_REVIEWER_TIER=opus to restore TIER_A for the reviewer. Silently fall back to TIER_A if TIER_B is unavailable.
Assemble reviewer prompt — call gen-reviewer-prompt.sh to compose the combined prompt (static cached prefix + dynamic suffix):
_current_head=$(gh pr view <PR> --json headRefOid --jq .headRefOid 2>/dev/null || echo "")
if [ -z "${_reviewer_pr_diff_file:-}" ] || [ "${_reviewer_last_head:-}" != "$_current_head" ]; then
_reviewer_pr_diff_file=$(mktemp -t autospec-pr-diff-XXXXXX.diff)
gh pr diff <PR> > "$_reviewer_pr_diff_file"
_reviewer_last_head="$_current_head"
fi
combined_reviewer_prompt=$(bash "${AUTOSPEC_SCRIPTS_DIR:-$HOME/.autospec/scripts}/gen-reviewer-prompt.sh" \
--pr-diff "$_reviewer_pr_diff_file" \
--issue-body "/tmp/issue-<ISSUE>-body.md" \
--prev-findings "/tmp/guardian-<PR>.md" \
--issue-labels "<ISSUE_LABELS>" \
--repo "<REPO>" \
${_reuse_flags_file:+--reuse-flags "$_reuse_flags_file"})
Pass combined_reviewer_prompt as the reviewer subagent prompt. The static cached prefix is framed by <!-- CACHE BOUNDARY --> markers; pass it with cache_control: { type: "ephemeral" } so Anthropic's prompt cache can reuse it across inner-loop iterations.
Dispatch ONE foreground subagent with this brief:
You are the implementation reviewer for PR # on {repo}, closing issue #.
Review counter-team as review lens
Read the issue body's Review counter-team section before reviewing.
Review from that independent team's perspective and challenge likely blind spots
from the implementation team's Team personality. Stay inside the issue
scope: do not request unrelated rewrites, but do raise findings when the PR
misses risks the counter-team was selected to notice.
Part 1 — Guardian (contract compliance) — skip if AUTOSPEC_NO_GUARDIAN=1:
- Read AGENTS.md
## Implementation-quality contract for the RULE_ID table and directive map.
- Read issue # body — note
## Implementation scope, ## Implementation outline, ## Tests required, and any Guardian: skip-* lines.
- Read deterministic findings in /tmp/guardian-.md (populated by lint-implementation.sh; may be empty if guardian disabled).
- Run
gh pr diff <PR> and gh pr view <PR> --json files,title,body.
- Apply LLM-tier RULE_IDs (HALLUCINATED_API, DUPLICATE_CODE, STRING_MATCH_DOMAIN_LOGIC, REPEATED_STRUCTURE_AS_CODE, DOC_OUT_OF_SYNC semantic pass, INVENTED_CONFIG). For DUPLICATE_CODE, explicitly search for existing components, helpers, validators, API clients, request wrappers, error banners, fixtures, and test utilities before accepting new generated code. For STRING_MATCH_DOMAIN_LOGIC, flag any function that encodes domain meaning via substring-on-name checks when the file already imports a proper-representation library (Python rdkit/ast/urllib.parse/datetime/ipaddress/lxml/jsonschema; JS/TS URL/Date/AST/zod/ajv/joi; Go net/url/time/go/ast/net.ParseIP; Java java.net.URI/java.time/JavaParser/javax.validation; Scala java.net.URI/java.time/scalameta/refined/circe; Rust url::Url/chrono/time/syn/std::net::IpAddr/serde). For REPEATED_STRUCTURE_AS_CODE, flag any function whose body has ≥5 branches sharing identical structural shape (Python if/elif, Java/Scala switch/match, Rust match arms, Go switch cases, JS if/else) — direct the implementer to extract into a table + single dispatcher loop. For each new component, module, function, endpoint, worker, hook, or test helper, require a spec-linked reason, reuse check, public contract, proof test, and "what breaks if wrong" answer. Collect as
RULE_ID:<path>:<line>: <desc>. Honor Guardian: skip-* with INFO: lines.
Part 2 — LGTM (correctness review): Using the same diff and issue body already in context:
6. Check correctness, edge cases, missing tests, AGENTS.md compliance (TDD, no mocks, conventional commits), whether every new code unit exists for a concrete issue/spec requirement rather than convenience, and whether deprecated routes, caches, buckets, stores, workers, config keys, UI paths, docs, specs, tests, or fixtures were removed instead of revived to make tests pass.
7. Collect findings as a numbered list.
8. Critical self-question before LGTM: "What else could still pass here while the real user workflow fails, and how could this be better?" Check especially mocked-vs-deployed behavior, external service assumptions, fallback paths, user-visible outcomes, and missing no-mock smoke coverage. If the answer is actionable inside the issue scope, emit it as a finding or required test.
> 8a. **data-scope invariant lens (diagnostic/filter endpoints):** When the issue touches endpoints, dashboards, reports, or diagnostics that accept optional job/sample/filter parameters, verify filters never widen to unrelated records. empty optional filters reject unless documented as a deliberate all-records mode. Require concrete evidence for `job-only`, `sample-only`, `job+sample`, `unsupported-filter`, and `empty-filter` paths; unsupported-filter and empty-filter cases must prove rejection or a documented scoped response, not silent broadening.
> 9. **Regression gap-check (MANDATORY for `regression`/`priority:high` issues; skip otherwise):** ask "would the reviewer have caught the original gap?" If the fused review as written would NOT have caught the gap this regression closes, add the missing checklist item(s) to `reports/autospec-review/reviewer-lessons.md` (one entry per item, with parent `gap_id` and date) and apply those new checks to this diff before issuing the verdict. This folds the former second-pass regression meta-review into this single reviewer pass — the reviewer-lessons write-path is preserved here; there is no second Tier-A dispatch.
>
> **Hard limit:** max **25 tool calls total** (Parts 1 + 2 combined). If budget exhausted, append `RULE_ID:OUT_OF_SCOPE: reviewer budget exhausted; PR needs human review` and proceed to verdict.
>
> **Simplicity axis is ADVISE-only (anti-gold-plating):** the reuse / build-vs-buy / "how could this be better?" axis may argue only toward *less* code — reuse a named existing util (`scripts/lib/`, repo source), adopt a named library, or delete an unneeded abstraction — and only when tied to a named acceptance criterion. It may NEVER emit a `BLOCK` that demands *more* code, a new abstraction, or speculative generality; such suggestions are at most `ADVISE` and never halt the commit. Every reuse verdict must name the matched util or library (evidence-bound), never assert a match from belief.
>
> **Verdict:** If Part 1 has ZERO blocking findings (INFO lines OK) AND Part 2 has no findings: return ONLY the token: `LGTM`. Otherwise return a numbered findings list — RULE_ID findings first, then LGTM findings. A reuse `BLOCK` is provisional until it survives the refute pass below.