PR Jangler
Autonomous PR backlog handler. Cron fires prj-orchestrator every N minutes; the orchestrator picks one queued action and dispatches to one phase skill. Per-PR state is durable; every transition is logged; nothing acts on a PR until a claim has been independently reproduced.
DISCOVER TRIAGE VERIFY REVIEW PLAN VALIDATE IMPLEMENT DECIDE REPORT
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌────────┐ ┌────────┐
│ GH → Q │───▶│Classify│───▶│Reproduce│──▶│ Read │───▶│Fix + │───▶│Adversarial │──▶│ New PR │──▶│ Label │───▶│ Email │
│ sync │ │ PR/cmt │ │ claim │ │ diff │ │test plan │ │6-item check │ │ to branch│ │ verdict│ │ digest │
└────────┘ └────────┘ └────────┘ └────────┘ └──────────┘ └──────────────┘ └──────────┘ └────────┘ └────────┘
prj-discover prj-triage prj-verify prj-review prj-plan-fix prj-validate- prj-implement prj-decision prj-report-
-claim adversarial -fix daily
Also dispatched on cadence: prj-detect-overlap (duplicate scoring), prj-setup (one-shot install).
Orchestrated by: prj-orchestrator (cron heartbeat).
Operating Principles
These are non-negotiable across every phase skill. Hoisted here so each child does not redefine them.
- The queue is authoritative.
{project-root}/_bmad-output/pr-workflow/state.json is the single source of truth. Phase skills NEVER read GitHub for queue facts — they read state.json. Only prj-discover writes from GitHub into the queue.
- All state I/O goes through
state_io.py. Atomic via tmp + fsync + rename. Direct file writes to state.json are forbidden — a corrupted queue blocks the whole pipeline.
- Idempotent per heartbeat. Every action survives mid-run interruption: the next heartbeat picks up cleanly without double-acting or losing the transition.
- Never silently fail. Every outcome — dispatch, stub, abort, error, no-op — emits a structured JSONL entry to
{project-root}/_bmad-output/pr-workflow/logs/{YYYY-MM-DD}.jsonl. Errors also surface on stderr.
PleaseAdvise is the escape hatch. Any phase may transition a PR to PleaseAdvise (priority 1000, outranks everything). Stays there until the user-acknowledged override label is applied.
- Never push to the contributor's branch.
prj-implement-fix opens a new PR targeting the contributor's branch. Force-pushes are forbidden. Every fix is a new commit; every fix is a new PR.
- Verify before acting on any claim. A comment asserting a bug is a hypothesis, not a fact.
prj-verify-claim reproduces in an isolated worktree before prj-plan-fix is allowed to run.
- System actions outrank per-PR actions. Daily report (priority 700) and discovery sweep (priority 500) preempt the per-PR backlog (base 100).
Routing Table
When the orchestrator's next action — or a user invocation — names one of these signals, dispatch to the matching child skill.
| Signal |
Skill |
What it does |
| First-time module install in a project |
prj-setup |
Writes _bmad/config.{yaml,user.yaml}, creates GH labels, validates gh/op/SMTP, initializes the workflow tree |
| Cron heartbeat (every N minutes) |
prj-orchestrator |
Picks one action, dispatches one phase, persists state, logs, exits |
Queue empty, or cadence tick heartbeat_count % prj_discover_every_n == 0 |
prj-discover |
Syncs GH open-PR set into the queue; adds Discovered, archives closed, bumps new_comments_since_triage |
PR newly Discovered (--mode pr), or new comment on a triaged PR (--mode comment) |
prj-triage |
Classifies the PR/comment into one of the rubric buckets; sets next_action |
| Triage produced an actionable claim that asserts a bug |
prj-verify-claim |
Reproduces in an isolated worktree; verdict drives transition |
| PR triaged as needs-review (no claim, full-PR examination) |
prj-review |
Reads diff + changed files; emits review.md with findings |
| Triage flagged possible duplicate |
prj-detect-overlap |
Scores file-footprint overlap vs every open PR; LLM-compares high-overlap pairs |
| Claim verified, ready to design a fix |
prj-plan-fix |
Produces fix-plan.md: failing test + smallest diff + rationale + risks |
| Fix-plan written, needs adversarial gate |
prj-validate-adversarial |
Runs full regression suite + 6-item adversarial checklist; default verdict reject |
| Adversarial gate passed |
prj-implement-fix |
Opens fix-PR targeting the contributor's branch |
| Review/fix complete, ready for verdict |
prj-decision |
Aggregates per-PR cache, applies rubric, labels prj/decision:{class}, posts summary comment |
Local hour ≥ prj_report_hour_local and no report sent today |
prj-report-daily |
Renders HTML+text digest, sends via SMTP, archives to reports/ |
Common Combinations
The canonical per-PR pipeline, in order:
prj-discover → prj-triage → prj-verify-claim → prj-review → prj-plan-fix
→ prj-validate-adversarial → prj-implement-fix → prj-decision
Not every PR walks the full sequence. Triage may shortcut: a duplicate goes straight to prj-detect-overlap → prj-decision (close-as-not-now); a clean review with no claim goes prj-review → prj-decision (ready-to-merge); a verification failure goes prj-verify-claim → prj-decision (request-changes-with-pushback).
Cross-Cutting Conventions
State tree layout
Owned by prj-orchestrator. Every phase reads from and writes to it (via state_io.py).
{project-root}/_bmad-output/pr-workflow/
├── state.json # the queue
├── prs/{pr_number}/ # per-PR cache
│ ├── meta.json
│ ├── triage.md
│ ├── verification.md
│ ├── review.md
│ ├── overlap.md
│ ├── fix-plan.md
│ ├── adversarial.md
│ └── decisions.log # JSONL audit
├── logs/{YYYY-MM-DD}.jsonl # run-log
├── reports/{YYYY-MM-DD}.md # daily-report archive
└── worktrees/ # ephemeral, owned by prj-verify-claim
Configuration
Module config lives under [modules.prj] in {project-root}/_bmad/config.toml (or config.yaml per the active BMad version). Required: prj_repo. Common: prj_discover_every_n, prj_report_hour_local, prj_smtp_host, prj_smtp_port, prj_email_to, prj_bot_user. User-only keys (user_name, communication_language) live in config.user.yaml.
Path tokens
{project-root}/... is a literal token in config values. Do not substitute it. Filesystem operations resolve it at the point of use; the config itself keeps it symbolic.
Dependency map
| Tool |
Used by |
Failure mode |
gh (authenticated as prj_bot_user) |
discover, triage, detect-overlap, review, implement-fix, decision |
Rate-limit aware: defer cleanly when core quota < 100 |
op (1Password CLI, signed in) |
report-daily, setup |
SMTP creds resolution; setup warns but does not block |
git + worktrees |
verify-claim, plan-fix, validate-adversarial, implement-fix |
Worktrees are ephemeral; clean up after each verification |
| Project test runner (auto-detected) |
verify-claim, validate-adversarial |
Required for the adversarial gate to pass |
| SMTP (configured host/port + STARTTLS) |
report-daily, setup test |
Daily digest delivery |
Label vocabulary
Pre-created on prj_repo by prj-setup (from skills/prj-setup/assets/labels.txt). Every phase that writes to GitHub uses these labels — never invents new ones.
prj/phase:{Discovered,Triaged,VerifyClaim,Reviewing,FixPlan,AdversarialCheck,FixImpl,Decision,ReadyToMerge,Blocked,Rejected,Archived,PleaseAdvise}
prj/decision:{ready-to-merge,request-changes,close-as-not-now}
prj/duplicate-of:{n}, prj/please-advise, prj/needs-retriage, prj/user-ack
Install Modes
This skill is the hub. There are three ways to use PR Jangler:
- Full pack — install the whole plugin. The hub plus all 12 phase skills land in
.claude/skills/. Recommended for autonomous operation.
- Hub only — install just
pr-jangler/. You get the routing knowledge, conventions, label vocab, and operating principles, but no executable scripts. Useful for orienting an agent that will hand off to a separately-installed phase skill, or for understanding the workflow before committing all 12.
- À la carte — copy individual
prj-* skills into your project. Skip the hub; each child SKILL.md is self-contained for its phase.
When this hub is loaded and a user task names a phase action that requires an uninstalled child, point them at the missing skill rather than improvising it: "this needs prj-verify-claim, which is not installed at .claude/skills/prj-verify-claim/. Install via <plugin install line> or copy from the pack repo."
Out of Scope
This hub routes the PR Jangler module. It does NOT:
- Review PRs in repos other than
prj_repo. The configured repo is the workspace; multi-repo orchestration is not built.
- Write or modify skills. Skill authoring belongs to
skill-creator, not here.
- Handle non-PR GitHub events (Issues, Discussions, Actions failures). Use a dedicated issue-triage skill if you need that.
- Replace BMad core or other modules. It runs alongside them under
_bmad/.
- Generate code changes from scratch. It can plan and implement fixes for verified bugs on existing PRs — not greenfield feature work. Use
bmad-bmm-dev-story or incremental-implementation for that.
- Bypass the verification gate. A claim that cannot be reproduced gets a polite pushback comment, not a speculative fix.
1---2name: pr-jangler3description: PR Jangler hub. Cron-driven autonomous workflow for triaging, verifying, reviewing, fix-planning, adversarially validating, fix-implementing, deciding, and daily-reporting on a GitHub PR backlog. Use when the user mentions "PR jangler", "pr-jangler", "PR backlog workflow", "PR triage", "autonomous PR review", "fix-plan", "fix-PR", "adversarial validator", "PleaseAdvise", or wants to install, configure, or extend the PR Jangler BMad module. Routes to 12 phase skills, owns the cross-cutting state-file conventions, idempotency rules, GitHub-label vocabulary, and 1Password/SMTP/`gh`/`op` dependency map shared by every phase. Triggers on prj-setup, prj-orchestrator, prj-discover, prj-triage, prj-verify-claim, prj-review, prj-detect-overlap, prj-plan-fix, prj-validate-adversarial, prj-implement-fix, prj-decision, prj-report-daily.4---56# PR Jangler78Autonomous PR backlog handler. Cron fires `prj-orchestrator` every N minutes; the orchestrator picks one queued action and dispatches to one phase skill. Per-PR state is durable; every transition is logged; nothing acts on a PR until a claim has been independently reproduced.910```11 DISCOVER TRIAGE VERIFY REVIEW PLAN VALIDATE IMPLEMENT DECIDE REPORT12 ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌────────┐ ┌────────┐13 │ GH → Q │───▶│Classify│───▶│Reproduce│──▶│ Read │───▶│Fix + │───▶│Adversarial │──▶│ New PR │──▶│ Label │───▶│ Email │14 │ sync │ │ PR/cmt │ │ claim │ │ diff │ │test plan │ │6-item check │ │ to branch│ │ verdict│ │ digest │15 └────────┘ └────────┘ └────────┘ └────────┘ └──────────┘ └──────────────┘ └──────────┘ └────────┘ └────────┘16 prj-discover prj-triage prj-verify prj-review prj-plan-fix prj-validate- prj-implement prj-decision prj-report-17 -claim adversarial -fix daily1819 Also dispatched on cadence: prj-detect-overlap (duplicate scoring), prj-setup (one-shot install).20 Orchestrated by: prj-orchestrator (cron heartbeat).21```2223## Operating Principles2425These are non-negotiable across every phase skill. Hoisted here so each child does not redefine them.26271. **The queue is authoritative.** `{project-root}/_bmad-output/pr-workflow/state.json` is the single source of truth. Phase skills NEVER read GitHub for queue facts — they read `state.json`. Only `prj-discover` writes from GitHub into the queue.282. **All state I/O goes through `state_io.py`.** Atomic via tmp + fsync + rename. Direct file writes to `state.json` are forbidden — a corrupted queue blocks the whole pipeline.293. **Idempotent per heartbeat.** Every action survives mid-run interruption: the next heartbeat picks up cleanly without double-acting or losing the transition.304. **Never silently fail.** Every outcome — dispatch, stub, abort, error, no-op — emits a structured JSONL entry to `{project-root}/_bmad-output/pr-workflow/logs/{YYYY-MM-DD}.jsonl`. Errors also surface on stderr.315. **`PleaseAdvise` is the escape hatch.** Any phase may transition a PR to `PleaseAdvise` (priority 1000, outranks everything). Stays there until the user-acknowledged override label is applied.326. **Never push to the contributor's branch.** `prj-implement-fix` opens a new PR targeting the contributor's branch. Force-pushes are forbidden. Every fix is a new commit; every fix is a new PR.337. **Verify before acting on any claim.** A comment asserting a bug is a hypothesis, not a fact. `prj-verify-claim` reproduces in an isolated worktree before `prj-plan-fix` is allowed to run.348. **System actions outrank per-PR actions.** Daily report (priority 700) and discovery sweep (priority 500) preempt the per-PR backlog (base 100).3536## Routing Table3738When the orchestrator's next action — or a user invocation — names one of these signals, dispatch to the matching child skill.3940| Signal | Skill | What it does |41|---|---|---|42| First-time module install in a project | `prj-setup` | Writes `_bmad/config.{yaml,user.yaml}`, creates GH labels, validates `gh`/`op`/SMTP, initializes the workflow tree |43| Cron heartbeat (every N minutes) | `prj-orchestrator` | Picks one action, dispatches one phase, persists state, logs, exits |44| Queue empty, or cadence tick `heartbeat_count % prj_discover_every_n == 0` | `prj-discover` | Syncs GH open-PR set into the queue; adds Discovered, archives closed, bumps `new_comments_since_triage` |45| PR newly Discovered (`--mode pr`), or new comment on a triaged PR (`--mode comment`) | `prj-triage` | Classifies the PR/comment into one of the rubric buckets; sets `next_action` |46| Triage produced an actionable claim that asserts a bug | `prj-verify-claim` | Reproduces in an isolated worktree; verdict drives transition |47| PR triaged as needs-review (no claim, full-PR examination) | `prj-review` | Reads diff + changed files; emits `review.md` with findings |48| Triage flagged possible duplicate | `prj-detect-overlap` | Scores file-footprint overlap vs every open PR; LLM-compares high-overlap pairs |49| Claim verified, ready to design a fix | `prj-plan-fix` | Produces `fix-plan.md`: failing test + smallest diff + rationale + risks |50| Fix-plan written, needs adversarial gate | `prj-validate-adversarial` | Runs full regression suite + 6-item adversarial checklist; default verdict `reject` |51| Adversarial gate passed | `prj-implement-fix` | Opens fix-PR targeting the contributor's branch |52| Review/fix complete, ready for verdict | `prj-decision` | Aggregates per-PR cache, applies rubric, labels `prj/decision:{class}`, posts summary comment |53| Local hour ≥ `prj_report_hour_local` and no report sent today | `prj-report-daily` | Renders HTML+text digest, sends via SMTP, archives to reports/ |5455## Common Combinations5657The canonical per-PR pipeline, in order:5859```60prj-discover → prj-triage → prj-verify-claim → prj-review → prj-plan-fix61 → prj-validate-adversarial → prj-implement-fix → prj-decision62```6364Not every PR walks the full sequence. Triage may shortcut: a duplicate goes straight to `prj-detect-overlap → prj-decision (close-as-not-now)`; a clean review with no claim goes `prj-review → prj-decision (ready-to-merge)`; a verification failure goes `prj-verify-claim → prj-decision (request-changes-with-pushback)`.6566## Cross-Cutting Conventions6768### State tree layout6970Owned by `prj-orchestrator`. Every phase reads from and writes to it (via `state_io.py`).7172```73{project-root}/_bmad-output/pr-workflow/74├── state.json # the queue75├── prs/{pr_number}/ # per-PR cache76│ ├── meta.json77│ ├── triage.md78│ ├── verification.md79│ ├── review.md80│ ├── overlap.md81│ ├── fix-plan.md82│ ├── adversarial.md83│ └── decisions.log # JSONL audit84├── logs/{YYYY-MM-DD}.jsonl # run-log85├── reports/{YYYY-MM-DD}.md # daily-report archive86└── worktrees/ # ephemeral, owned by prj-verify-claim87```8889### Configuration9091Module config lives under `[modules.prj]` in `{project-root}/_bmad/config.toml` (or `config.yaml` per the active BMad version). Required: `prj_repo`. Common: `prj_discover_every_n`, `prj_report_hour_local`, `prj_smtp_host`, `prj_smtp_port`, `prj_email_to`, `prj_bot_user`. User-only keys (`user_name`, `communication_language`) live in `config.user.yaml`.9293### Path tokens9495`{project-root}/...` is a **literal token** in config values. Do not substitute it. Filesystem operations resolve it at the point of use; the config itself keeps it symbolic.9697### Dependency map9899| Tool | Used by | Failure mode |100|---|---|---|101| `gh` (authenticated as `prj_bot_user`) | discover, triage, detect-overlap, review, implement-fix, decision | Rate-limit aware: defer cleanly when core quota < 100 |102| `op` (1Password CLI, signed in) | report-daily, setup | SMTP creds resolution; setup warns but does not block |103| `git` + worktrees | verify-claim, plan-fix, validate-adversarial, implement-fix | Worktrees are ephemeral; clean up after each verification |104| Project test runner (auto-detected) | verify-claim, validate-adversarial | Required for the adversarial gate to pass |105| SMTP (configured host/port + STARTTLS) | report-daily, setup test | Daily digest delivery |106107### Label vocabulary108109Pre-created on `prj_repo` by `prj-setup` (from `skills/prj-setup/assets/labels.txt`). Every phase that writes to GitHub uses these labels — never invents new ones.110111- `prj/phase:{Discovered,Triaged,VerifyClaim,Reviewing,FixPlan,AdversarialCheck,FixImpl,Decision,ReadyToMerge,Blocked,Rejected,Archived,PleaseAdvise}`112- `prj/decision:{ready-to-merge,request-changes,close-as-not-now}`113- `prj/duplicate-of:{n}`, `prj/please-advise`, `prj/needs-retriage`, `prj/user-ack`114115## Install Modes116117This skill is the hub. There are three ways to use PR Jangler:1181191. **Full pack** — install the whole plugin. The hub plus all 12 phase skills land in `.claude/skills/`. Recommended for autonomous operation.1202. **Hub only** — install just `pr-jangler/`. You get the routing knowledge, conventions, label vocab, and operating principles, but no executable scripts. Useful for orienting an agent that will hand off to a separately-installed phase skill, or for understanding the workflow before committing all 12.1213. **À la carte** — copy individual `prj-*` skills into your project. Skip the hub; each child SKILL.md is self-contained for its phase.122123When this hub is loaded and a user task names a phase action that requires an uninstalled child, point them at the missing skill rather than improvising it: "this needs `prj-verify-claim`, which is not installed at `.claude/skills/prj-verify-claim/`. Install via `<plugin install line>` or copy from the pack repo."124125## Out of Scope126127This hub routes the PR Jangler module. It does NOT:128129- **Review PRs in repos other than `prj_repo`.** The configured repo is the workspace; multi-repo orchestration is not built.130- **Write or modify skills.** Skill authoring belongs to `skill-creator`, not here.131- **Handle non-PR GitHub events** (Issues, Discussions, Actions failures). Use a dedicated issue-triage skill if you need that.132- **Replace BMad core or other modules.** It runs alongside them under `_bmad/`.133- **Generate code changes from scratch.** It can plan and implement fixes for *verified bugs* on existing PRs — not greenfield feature work. Use `bmad-bmm-dev-story` or `incremental-implementation` for that.134- **Bypass the verification gate.** A claim that cannot be reproduced gets a polite pushback comment, not a speculative fix.