# No Github Backlog

> Drain a GitHub issue backlog autonomously. Spawns isolated subagents per issue across 7 stages (investigate, implement, review, security-review, qa, fix, merge), logs every stage to backlog.csv. Use when user asks to clear, drain, close, or resolve an issue backlog.

- **Type:** Skill
- **Install:** `agentstack add skill-dzianisv-skills-no-github-backlog`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [dzianisv](https://agentstack.voostack.com/s/dzianisv)
- **Installs:** 0
- **Category:** [Developer Tools](https://agentstack.voostack.com/c/developer-tools)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [dzianisv](https://github.com/dzianisv)
- **Source:** https://github.com/dzianisv/skills/tree/main/skills/no-github-backlog

## Install

```sh
agentstack add skill-dzianisv-skills-no-github-backlog
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# no-github-backlog

Autonomous backlog drain. Main agent = orchestrator. Each stage = fresh subagent. Per-issue work is parallel; per-issue stages are sequential.

## Stop contract

Only stop condition: user explicitly says `STOP_GOAL` (or equivalent: "stop", "halt", "done for now").

- **Never** mark a run complete while new issues exist or in-flight stages remain.
- **Never** stamp `completed_at` in status.json mid-run — only on `STOP_GOAL`.
- After each batch drains, re-fetch the backlog (new issues may have opened) and loop.
- Do not pause, wrap up, or send a final summary while the goal remains active.

## Inputs

- `$REPO` — repo `owner/name` (e.g. `VibeTechnologies/VibeWebAgent`); resolved from the first `owner/repo` slug in the args, else the current repo (`gh repo view` / `git remote origin`)
- `$FILTER` — optional `gh issue list` flags (default: `--state open`); any non-slug args, ignored if not flag-style

> **Note:** Orchestrator uses `$ISSUE` for the issue number internally. Templates receive `$ISSUE_NUMBER` as a substituted parameter — orchestrator sets `ISSUE_NUMBER=$ISSUE` when building each template prompt.

## Per-pipeline context variables

Orchestrator maintains these per issue being processed. Capture from `gh issue list` JSON and subagent results. Pass them to the CSV append (rule 7) on every stage exit.

| Variable | Source |
|---|---|
| `$ISSUE` | `gh issue list` JSON `.number` |
| `$ISSUE_TITLE` | `gh issue list` JSON `.title` |
| `$ISSUE_URL` | `gh issue list` JSON `.url` |
| `$STAGE` | Orchestrator — current stage name |
| `$DECISION` | Subagent JSON result field `decision` or `verdict` |
| `$REASON` | Subagent JSON result field `reason` or `findings` summary |
| `$SUBAGENT_TYPE` | Hardcoded per stage (`general-purpose`) |
| `$MODEL` | Hardcoded per stage (`sonnet`) |
| `$TEMPLATE` | Template filename, e.g. `investigate.md`; empty for orchestrator-only stages |
| `$PR_NUMBER` | IMPLEMENT result `.pr_number`; carry forward to subsequent stages |
| `$PR_URL` | IMPLEMENT result `.pr_url`; carry forward |
| `$CI_STATUS` | REVIEW result `.ci_status`; update at QA and MERGE |
| `$DURATION_S` | Wall-clock seconds: `$(( $(date +%s) - STAGE_START ))` |

Set `STAGE_START=$(date +%s)` immediately before each Agent spawn. Reset per stage.

## Setup / resume

Before fanning out, check for an unfinished prior run.

```!
# Repo resolution — the skill is invoked with the user's free-text args, not a clean
# positional owner/repo slug. Never hard-fail on a missing $1. Precedence:
#   1. an explicit owner/repo slug anywhere in the args, else
#   2. the current repo (gh, then git remote origin), else
#   3. clear usage error.
# Any args that are NOT the repo slug become the optional gh-issue-list filter; if that
# leftover text isn't valid filter flags it's ignored gracefully rather than erroring.
ARGS="$*"
REPO=""
for tok in $ARGS; do
  if printf '%s' "$tok" | grep -Eq '^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$'; then
    REPO="$tok"; break
  fi
done
if [ -z "$REPO" ]; then
  REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null) || REPO=""
fi
if [ -z "$REPO" ]; then
  REPO=$(git remote get-url origin 2>/dev/null \
    | sed -E 's#^.*github\.com[:/]##; s#\.git$##') || REPO=""
  printf '%s' "$REPO" | grep -Eq '^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$' || REPO=""
fi
if [ -z "$REPO" ]; then
  echo "ERROR: could not resolve a repo. Usage: /no-github-backlog [owner/repo] [optional-filter]" >&2
  exit 1
fi

# Leftover args (everything that isn't the repo slug) = optional gh-issue-list filter.
# Default to open issues; if the leftover isn't a flag-style filter, ignore it.
FILTER=""
for tok in $ARGS; do
  [ "$tok" = "$REPO" ] && continue
  FILTER="$FILTER $tok"
done
case "$FILTER" in
  *-*) : ;;          # looks like gh flags (e.g. --state open, --label bug); keep it
  *) FILTER="" ;;    # free text like "work on issues in the backlog" → ignore
esac
FILTER="${FILTER:---state open}"

gh auth status --hostname github.com || { echo "ERROR: gh not authenticated"; exit 1; }
mkdir -p .agents/no-github-backlog
DEFAULT_BRANCH=$(gh repo view "$REPO" --json defaultBranchRef --jq '.defaultBranchRef.name' 2>/dev/null || echo "main")
[ -f .agents/no-github-backlog/backlog.csv ] || \
  printf 'date|run_id|issue|issue_title|issue_url|stage|decision|reasoning|subagent_type|model|prompt_template|pr_number|pr_url|ci_status|duration_s\n' \
  > .agents/no-github-backlog/backlog.csv

# Find any in-progress run (status.json missing completed_at).
RESUME_RUN=$(ls -t .agents/no-github-backlog/run-*.status.json 2>/dev/null \
  | xargs -I{} sh -c 'jq -e "select(.completed_at == null)" {} >/dev/null 2>&1 && echo {}' \
  | head -n1)

if [ -n "$RESUME_RUN" ]; then
  RUN_ID=$(jq -r .run_id "$RESUME_RUN")
  echo "RESUMING run $RUN_ID"
else
  RUN_ID=$(date -u +%Y%m%dT%H%M%SZ)
  printf '{"run_id":"%s","started_at":"%s","cycle":0,"queued":0,"running":0,"done":0,"failed":0,"quarantined":0,"completed_at":null}\n' \
    "$RUN_ID" "$(date -u +%FT%TZ)" > .agents/no-github-backlog/run-$RUN_ID.status.json
  echo "NEW run $RUN_ID"
fi
export RUN_ID
```

### Resume protocol (mandatory if `RESUME_RUN` non-empty)

A prior orchestrator session may have crashed, been `/compact`-ed, or had background subagents orphaned. The CSV + status file are the resume ledger. Before spawning **any** new subagent:

1. **Reconcile per issue.** For each issue in the backlog, compute `last_stage|last_decision` from `backlog.csv` (awk returns the last row for that issue, pipe-separated). Field positions: `$3=issue $6=stage $7=decision`.
   ```
   flock .agents/no-github-backlog/backlog.csv.lock \
     awk -F'|' -v iss="#$ISSUE" 'NR>1 && $3==iss {last_stage=$6; last_dec=$7} END {print last_stage "|" last_dec}' \
     .agents/no-github-backlog/backlog.csv
   ```
2. **Skip terminal states.** If `last_dec` ∈ {`close`, `triage`, `skipped`} for stage `investigate`, OR `last_stage=merge` AND `last_dec=merged`, OR any `last_dec=quarantine` → issue done, skip.
3. **Resume at next stage.** Map `last_stage|last_dec` to entry point:
   - `investigate|fix` → `implement`
   - `implement|fix` → **skip INVESTIGATE** (own PR exists); get PR number via:
     ```
     gh pr list --repo "$REPO" --state open --search "#$ISSUE in:body" \
       --json number,headRefName \
       | jq --arg pfx "fix/issue-${ISSUE}-" \
           '[.[] | select(.headRefName | startswith($pfx))] | .[0].number'
     ```
     Start at `review`.
   - `review|approve` → `security-review`
   - `security-review|approve` → `qa`
   - `qa|approve` → `merge`
   - `fix|fix` → `review` (fix was applied; full re-validation)
   - `*|reject` or `*|timeout` → restart that stage as `retry-1` (or `retry-2` if `retry-1` exists; else quarantine)
4. **Detect orphaned own PRs.** For issues where `last_stage=implement` AND `last_dec=fix`: **do not call INVESTIGATE** — it would find the harness's own branch and return `existing_pr` causing a false skip. Query the PR directly as shown in step 3, then enter at `review`.
5. **Clear orphaned harness tasks.** Run `TaskList` — any tasks tagged `no-github-backlog:` from the prior session whose `agentId` is no longer live are dead. TaskUpdate them to `cancelled` with reason `compact-orphan` so the index is clean before new fan-out.

If `RESUME_RUN` is empty, skip this section.

## Fetch backlog

```!
gh issue list --repo "$REPO" --json number,title,labels,url --limit 1000 $FILTER
```

## Orchestration rules

1. **Parallel fanout.** Spawn one INVESTIGATE subagent per issue in a single message (multiple Agent tool calls in one block). Cap concurrency at 10. Queue overflow waits for any in-flight slot to free before dispatching next.
2. **QA concurrency cap.** QA stage runs real services (docker-compose, DBs, test fixtures). Cap parallel QA subagents at **2** (port conflicts, RAM pressure, secret races). Override to 1 if repo requires a singleton service (e.g. shared cloud sandbox). Other stages stay at 10.
   **Enforcement:** maintain a `qa_in_flight` counter. Before spawning QA, if `qa_in_flight >= 2`, push issue to `qa_pending` queue and continue draining other stages. When a QA subagent returns (approve/reject/timeout), decrement `qa_in_flight` and dispatch next item from `qa_pending` if non-empty.
3. **Sequential per-issue gates.** Within an issue, stages run in order. Each stage = new Agent call (fresh context). Never reuse a subagent across stages.
4. **Hard merge gate.** Merge only if review=approve AND security=approve AND qa=approve AND CI=green. No exceptions.
5. **Full re-validation after FIX.** Any FIX commit invalidates prior approvals. After a fix lands on the PR branch, re-run REVIEW + SECURITY-REVIEW + QA in order (full pipeline from REVIEW onward), not just the stage that rejected. A QA-fix can introduce a security regression; a security-fix can break tests.
6. **Per-stage timeouts.** Kill + log `decision=timeout` if a subagent exceeds budget:

   | Stage | Wall-clock budget |
   |---|---|
   | investigate | 10 min |
   | implement | 30 min |
   | review | 10 min |
   | security-review | 10 min |
   | qa | 45 min |
   | fix | 30 min |
   | merge | 30 min (CI watch) |

7. **Log every stage.** Append a row to `backlog.csv` immediately after each subagent returns (success or fail). Use `flock` to serialize writes — parallel subagents WILL race the CSV append otherwise.

   ```
   flock .agents/no-github-backlog/backlog.csv.lock \
     printf '%s|%s|#%d|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%d\n' \
       "$(date -u +%FT%TZ)" "$RUN_ID" "$ISSUE" \
       "${ISSUE_TITLE:-}" "${ISSUE_URL:-}" \
       "$STAGE" "$DECISION" "${REASON:-}" \
       "${SUBAGENT_TYPE:-general-purpose}" "${MODEL:-sonnet}" "${TEMPLATE:-}" \
       "${PR_NUMBER:-}" "${PR_URL:-}" "${CI_STATUS:-}" "${DURATION_S:-0}" \
     >> .agents/no-github-backlog/backlog.csv
   ```

8. **Retry cap + if-stuck protocol.** Same stage may retry once (retry-1). Before reaching retry-2 → apply if-stuck (see section below). If stuck protocol also fails → quarantine. Retry rows append with reasoning prefix `retry-`. Never exceed n=2.

   **Retry count detection** — before each stage dispatch, check how many times this stage has already run for this issue:
   ```
   RETRY_COUNT=$(flock .agents/no-github-backlog/backlog.csv.lock \
     awk -F'|' -v iss="#$ISSUE" -v stg="$STAGE" \
       'NR>1 && $3==iss && $6==stg {count++} END {print count+0}' \
     .agents/no-github-backlog/backlog.csv)
   # 0 = first attempt → run normally
   # 1 = already ran once → this dispatch is retry-1
   # ≥2 = apply if-stuck before running retry-2; quarantine if still failing
   ```
9. **Quarantine.** On quarantine, add two labels: `harness-quarantine` + `harness-stage:`. Comment with last error. Move on.
10. **Worktree isolation.** Each implementation/fix/qa subagent runs in `.claude/worktrees/issue-` via `isolation: "worktree"`. Cleanup automatic if no diff.
11. **Observability.** Update `.agents/no-github-backlog/run-$RUN_ID.status.json` after every stage transition (queued/running/done/failed/quarantined counters). User can `cat` it to see live progress without scraping CSV.
12. **TaskCreate pinning (compact-survival).** Background Agent agentIds live in harness state, not model context — `/compact` can drop them. To survive compaction:
    - **Before** every `Agent(...)` spawn for a stage, create a harness task:
      ```
      TaskCreate description="no-github-backlog::# agentId=" status="pending"
      ```
    - **Immediately after** the Agent call returns its agentId (or for `run_in_background=true` agents), TaskUpdate to set status=`in_progress` and rewrite the description to include the actual agentId:
      ```
      TaskUpdate description="no-github-backlog::# agentId=a54572..." status="in_progress"
      ```
    - **After** the subagent completes (or on quarantine), TaskUpdate status=`completed` (or `cancelled` with reason).
    - Post-`/compact`, the orchestrator re-reads TaskList; any `in_progress` tasks tagged `no-github-backlog:` whose agentId is still live can be resumed via `SendMessage(to=, ...)`. Dead agentIds get cancelled per resume rule 5.
    - **Always tag the description with the `no-github-backlog:` prefix** — that's the discriminator for resume reconciliation.

## Stages

### 1. INVESTIGATE (subagent: general-purpose, model=sonnet, effort=high)

Prompt: `templates/investigate.md` with `$ISSUE_NUMBER`, `$REPO` substituted.

Returns JSON: `{decision: "fix"|"close"|"triage", reason, scope_files: [...], loc_estimate, existing_pr: }`

`loc_estimate` = net lines added + modified in `scope_files`, excluding generated files, lockfiles, and snapshot fixtures. Strict integer.

Gate:
- `close` → orchestrator runs `gh issue close  --repo  --comment ""`, log `stage=investigate,decision=close`, stop.
- `triage` → vague/ambiguous issue; orchestrator adds label `harness-needs-triage`, comments with the agent's `reason`, log `stage=investigate,decision=triage`, stop.
- `existing_pr` present AND NOT `internal_pr` → external PR exists; log `stage=investigate,decision=skipped,reasoning="existing PR #"`, stop. Do not open duplicate PR.
- `existing_pr` present AND `internal_pr=true` → own harness PR found (stale run); treat as `implement|fix` row and start at REVIEW (skip IMPLEMENT).
- `fix` + `loc_estimate>300` → quarantine with `harness-stage:investigate`, log `stage=investigate,decision=skipped,reasoning="loc>300"`.
- `fix` + `loc_estimate-`
- Make minimal diff scoped to listed files
- Push branch
- Open draft PR with `Closes #`
- Return JSON: `{pr_url, pr_number, branch}`

Gate: missing PR URL → quarantine with `harness-stage:implement`. Log `stage=implement,decision=quarantine`.

### 3. REVIEW (subagent: **general-purpose**, model=sonnet, effort=high)

Note: `caveman:cavecrew-reviewer` outputs line-format text, not JSON, and refuses to suggest fixes — incompatible with this stage's contract. Use `general-purpose`.

**Diff-grounded review.** Subagent fetches diff itself via `gh pr diff`. May read PR body/title for context, but findings must cite code, not author intent. Commit messages are out of scope.

Prompt: `templates/review.md` with `$PR_NUMBER`, `$REPO`, `$SCOPE_FILES` (pipe-separated list from INVESTIGATE output; used for scope-creep check). Exception to context-isolation: scope_files is INVESTIGATE output passed as a substituted parameter, not shared agent context.

Returns JSON: `{verdict, findings: [...], ci_status, failing_checks, ci_break_predicted, scope_creep, regression_risk}`.

Gate:
- approve → log `stage=review,decision=approve`, proceed to SECURITY-REVIEW.
- reject → spawn FIX subagent, then **re-run from REVIEW** (full chain: REVIEW → SECURITY-REVIEW → QA). Second reject at REVIEW → quarantine with `harness-stage:review`.

### 4. SECURITY-REVIEW (subagent: general-purpose, model=sonnet, effort=high)

**Independent context.** Reads diff + project security policies if present (`SECURITY.md`, `CLAUDE.md` security sections).

Prompt: `templates/security-review.md`.

Returns JSON: `{verdict, severity: "critical"|"high"|"medium"|"low"|"none", findings: [...]}`.

Gate:
- approve OR (reject AND severity ∈ {low, medium}) → log `stage=security-review,decision=approve` (post low/medium findings as PR comment for visibility), proceed to QA.
- reject AND severity ∈ {critical, high} → spawn FIX subagent, **re-run from REVIEW** (a security fix may affect logic/scope). Second high+ reject → quarantine with `harness-stage:security`.

### 5. QA (subagent: general-purpose, model=sonnet, effort=high, isolation=worktree, concurrency_cap=2)

**Independent context.** Fresh subagent. Validates the change actually works against the real product and is covered by real tests. NO mocks. NO isolated unit tests of trivial functions. Real product, real data, end-to-end.

Prompt: `templates/qa.md` with `$PR_NUMBER`, `$ISSUE_NUMBER`, `$REPO`.

Subagent must:
- Check out PR branch in worktree.
- Map: what changed (files, functions), what existin

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [dzianisv](https://github.com/dzianisv)
- **Source:** [dzianisv/skills](https://github.com/dzianisv/skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-dzianisv-skills-no-github-backlog
- Seller: https://agentstack.voostack.com/s/dzianisv
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
