Metadata-Version: 2.5
Name: wade-cli
Version: 1.4.9
Summary: WADE — Workflow for AI-Driven Engineering
Project-URL: Homepage, https://github.com/ivanviragine/wade
Project-URL: Repository, https://github.com/ivanviragine/wade
Author: Ivan Viragine
License-Expression: MIT
Keywords: ai,cli,git,github,workflow
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Version Control :: Git
Requires-Python: >=3.11
Requires-Dist: crossby<0.44.0,>=0.43.2
Requires-Dist: httpx<1.0,>=0.27
Requires-Dist: markdown-it-py<5,>=4.2
Requires-Dist: pydantic-settings>=2.0
Requires-Dist: pydantic>=2.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: questionary>=2.0
Requires-Dist: rich>=13.0
Requires-Dist: structlog>=24.0
Requires-Dist: typer<0.26,>=0.12
Provides-Extra: dev
Requires-Dist: mypy>=1.14; extra == 'dev'
Requires-Dist: pre-commit>=3.7; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
Description-Content-Type: text/markdown

# WADE — Workflow for AI-Driven Engineering

<p align="center">
  <img src="assets/wade.png" alt="WADE" width="250" />
</p>

**AI tools write the code. WADE handles everything else.**

*Every Wade does the dirty work so the heroes don't have to.*

Branches, worktrees, context loading, model routing, PR creation — all the
workflow friction that surrounds AI coding sessions. WADE eliminates it. It
works with `Claude Code`, `Codex`, `Copilot`, `Cursor`, `Antigravity CLI`, and
more, and pulls tasks from **GitHub Issues, ClickUp, or a committed Markdown
file**. Run `wade init` once per project, then drive your work from task numbers
while WADE handles the git and GitHub plumbing around every AI session.

**Highlights**

- **Deterministic planning** — `wade plan` turns a goal into validated plan
  files that become lightweight tasks plus draft PRs, each labeled by complexity.
- **Isolated worktrees** — every task gets its own git worktree, so parallel
  sessions never collide or stash-juggle.
- **Full context, zero copy-paste** — the task, its description, labels, and your
  project conventions are loaded into the AI automatically.
- **Agent guardrails** — on hook-capable AI tools, per-session hooks keep the AI
  inside its worktree and route it through a completion gate before any push;
  hookless tools retain the same fixed workflow and deterministic service gates
  (see the tool support table).
- **Quality gates** — opt-in pre-commit lint/test and conventional-commit checks,
  plus an AI code-review pass before the PR is marked ready.
- **Pluggable task providers** — GitHub Issues, ClickUp, or a committed Markdown
  file; PRs and reviews always run through GitHub.
- **Multi-tool** — automatic model routing by task complexity across every
  supported AI coding tool.

## See It in Action

Starting work on task #42:

*Without* WADE:

```bash
git fetch origin && git checkout main && git pull
git checkout -b feat/issue-42-user-auth
# paste task title + description into AI chat
# explain your branching rules, test locations, linters to run...
```

*With* WADE:

```bash
wade 42
```

With no open PR for task #42 yet, WADE starts an implementation session: it
creates an isolated git worktree, launches your AI tool with the full task
loaded — title, description, labels, and all your project conventions — and a
fixed workflow plus selected methodology guide it from first commit to open PR.
Want the AI to break the work down first? Run `wade plan` — planning is a
deliberate step, not something `wade 42` guesses at from a missing plan.

Finishing work and opening the PR:

*Without* WADE:

```bash
git checkout main && git pull
git checkout feat/issue-42-user-auth
git merge main          # resolve conflicts yourself, if any
git push
gh pr create --title "User Auth (#42)" --body "..."   # write description manually
# don't forget to link the task, clean up the branch...
```

*With* WADE (the AI handles all of this):

```bash
wade implementation-session done
```

The AI merges the latest main into the branch, resolves any conflicts, writes the
PR description from what it built, and marks it ready for review — you get a
clean, already-integrated diff with no noise for your reviewer.

Both session `done` commands promote draft PRs by default. If promotion fails,
completion fails too: restore GitHub access and retry the same command. Pushed
work, PR-body updates, and review evidence remain available. Use `--draft` to
create or preserve a draft without promoting it; an already-ready PR stays ready.
Automatic bot reviews are deferred while the resulting PR is draft, including
with `--trigger-bots`, and can run after a later ready-for-review completion.

Working on multiple tasks at once:

```bash
wade implement-batch 42 43 44   # three worktrees, three AI sessions, zero stashing
```

## Why WADE

| Without WADE | With WADE |
|---|---|
| `git fetch && checkout && pull && checkout -b ...` before every task | `wade 42` — one command, done |
| Copy-paste task title + description into AI chat every time | Full task context + project conventions loaded automatically |
| One task at a time, or stash-juggle between branches | Parallel tasks in isolated worktrees, zero conflicts |
| Re-explain lifecycle and review rules every session | A fixed workflow carries them; dynamic skills add your methodology |
| PRs opened on stale branches — reviewer sees conflict noise, asks for a rebase, CI fails | AI merges the latest main and resolves conflicts before the PR — reviewer sees only your changes |
| Write the PR description, link the task, clean up the branch — manually | The AI ships the PR. You just review |
| Manually pick the right model for each task | Automatic model routing based on task complexity |
| Which terminal tab has which task? No idea | Terminal title shows `wade implement #42 — Feature Name` |
| Which AI session worked on this task? Which tool? Which model? | Every PR and task logs the tool, model, and session resume command — for both Plan and Implement phases |

## Installation

One-line install script (installs [`uv`](https://docs.astral.sh/uv/) automatically if missing):

```bash
curl -LsSf https://raw.githubusercontent.com/ivanviragine/wade/main/install.sh | sh
```

Or, if you already have `uv` or `pipx`:

```bash
uv tool install wade-cli    # recommended
pipx install wade-cli
```

Requires [gh CLI](https://cli.github.com/) (authenticated) and at least one supported AI coding tool.

```bash
gh auth login    # if not already authenticated
```

To let WADE move issues across a GitHub Projects (v2) board (e.g. to *In Progress*),
also grant the `project` scope. This is optional — everything else works without it:

```bash
gh auth refresh -s project
```

## GitHub Projects v2

`wade project items`, `rank`, `pick`, and `sync` operate on one explicit user or
organization board. They currently require `provider.name: github`; other task
providers and the existing best-effort startup Status move retain their behavior.
Only `sync --apply` writes, and only to configured existing numeric fields.
Outputs must have `isIssueField: false` in GitHub’s schema: organization issue
fields and unknown ownership are rejected before any mutation, including when
the output has no value yet.

Configure optional top-level `github_projects` in `.wade.yml` (version stays 2):

```yaml
github_projects:
  target:
    owner: my-org
    kind: organization             # or user
    number: 7
    hostname: github.com           # optional: selected gh/GH_HOST/origin host
  fields:
    impact: {name: Impact, type: NUMBER}
    effort: {name: Effort, type: NUMBER}
    priority:
      name: Priority
      type: SINGLE_SELECT
      options:
        high: {id: YOUR_HIGH_OPTION_ID, name: High}
        low: {id: YOUR_LOW_OPTION_ID, name: Low}
      option_order: [high, low]
    status: {name: Status, type: SINGLE_SELECT}
    computed_score: {name: Score, type: NUMBER}
    computed_rank: {name: Rank, type: NUMBER}
  columns: [repository, number, title, impact, effort, priority]
  sort: [{field: impact, direction: desc}]
  ranking:
    factors:
      - field: impact
        weight: 2
        minimum: 0
        maximum: 10
        missing: {policy: exclude}
      - field: effort
        weight: 1
        minimum: 0
        maximum: 10
        lower_is_better: true
        missing: {policy: value, normalized: 0.5}
      - field: priority
        weight: 1
        options: {high: 10, low: 0}
        missing: {policy: exclude}
  wip:
    status: status
    ready: [Ready]                  # exact option names, IDs or configured aliases
    in_progress: [Doing]
    pool: project                  # assignee requires --assignee
    limit: 3                       # zero deliberately permits no selections
  outputs:
    score: computed_score
    rank: computed_rank
```

Discover actual project/field/option IDs with `gh project list --owner my-org`
and `gh project field-list 7 --owner my-org --format json` (check `gh --help` for
Enterprise host setup). Replace example option IDs before use. A field reference
needs an ID or exact unique name and its expected type. IDs are authoritative;
when both ID and name are supplied, both must match. Option aliases follow the
same rules. Renames, ambiguous names, stale IDs, unknown nested settings and
present-null sections fail explicitly. Init/update preserves this optional section
without querying GitHub. It does not create a board or fields.

```bash
wade project items --columns repository,number,title,impact --sort impact:desc --json
wade project items --filter 'priority=high' --label bug --assignee octocat
wade project rank --repository my-org/my-repo --limit 10 --json
wade project pick --count 2 --json
wade project sync --dry-run --json
wade project sync --apply --json
# A complete CLI target overrides the configured target:
wade project items --owner octocat --owner-kind user --number 3 --hostname github.com
```

Filters are exact `alias=value`, repeatable with AND semantics; quote shell spaces.
`--repository` requires `owner/repo`; repository identity filters follow GitHub’s
case-insensitive owner/name rules. Assignee filters and assignee-pool WIP counts
likewise compare GitHub logins without case; output retains canonical API
spelling. Other text and label filters remain case-sensitive. `--kind` accepts
Issue, PullRequest or DraftIssue. `--status` uses the explicit `wip.status` mapping. Selected option
filters accept IDs, unique exact names or configured aliases. Labels, assignees
and multiselect fields support membership filters. Numeric filters reject
nonfinite values; date filters require canonical `YYYY-MM-DD`. Listing can
include archived items with `--include-archived`; ranking, selection and sync
always exclude them.

Listing sorts numbers numerically, canonical dates chronologically, and text
case-sensitively by ordinal order. Single-select sorts use `option_order`, then
remaining options by name and ID. Nulls sort last in both directions. Multivalue
and iteration scalar sorts fail. Repeat `--sort alias:asc|desc` for listing keys;
ranking commands always order by reported score descending. Final ties use
qualified repository/content identity and item node ID, independent of API order.
`--limit` is positive and applies only after the complete snapshot is evaluated;
it does not truncate WIP counts or sync rank positions. JSON stdout is one
object containing resolved project identity, rows, contributions, exclusions,
visibility limits and, for sync, operation outcomes. Human tables use the same
rows and ordering. Valid empty results exit zero; validation/provider errors go
to stderr and exit nonzero. Human apply output also sends operation-specific
permission, drift and readback failures to stderr; its outcome table preserves
confirmed/failed/unknown/not-attempted results. JSON retains these messages in
the single stdout object.

Ranking uses fixed bounds, never the current population or clock. Numeric values
are normalized to [0,1], clamped, and optionally inverted; explicit single-select
scores are normalized using the minimum/maximum of the **configured score map**,
which requires at least two distinct scores. Missing inputs explicitly exclude
an item or supply its configured normalized value. Scores are
`100 * sum(weight * normalized) / sum(weight)`, rounded half-even to six decimals;
ordering uses that reported rounded score. In the example above, Impact=10,
Effort=10 and Priority=low yields **50**; Impact=5, Effort=0 and Priority=high
yields **75** (contributions 1, 1 and 1 over weight total 4). JSON reports
raw inputs, normalized values, weights and weighted contributions. Initially,
only NUMBER and SINGLE_SELECT fields can be ranking factors; text, date,
iteration, multiselect, labels, assignees, repository and milestone mappings
remain available for supported display/filter operations.

`pick` is advisory: it neither reserves a slot nor starts implementation. It
returns ranked ready open issues in the current repository unless
`--repository owner/repo`, `--filter repository=owner/repo`, or a filter on a
mapped repository field explicitly selects another. Explicit filters retain AND
semantics; the current-repository default is added only without a repository
constraint. PRs, drafts and closed/archived issues
are never candidates. The nonarchived in-progress WIP denominator includes all
content kinds and repositories before candidate filters; an assignee pool counts
that assignee across the whole board. Missing status, unavailable content or an
incomplete required connection fails instead of inventing capacity. GitHub may
hide resources entirely from the authenticated principal; reported visibility is
limited to that principal, never a guarantee of global board completeness.

All reads use gh authentication for the selected hostname, bounded independent
connection pagination and bounded read retry. Classic/OAuth credentials need
`read:project` for reads and `project` for apply, plus underlying repository
visibility. Apps/fine-grained credentials need the corresponding project and
repository permissions. WADE does not refresh credentials automatically; a
configured host conflicting with `GH_HOST` fails. Older Enterprise schemas may
lack required unions/archive metadata and fail clearly rather than falling back
to classic Projects. Errors with HTTP-success partial GraphQL data are errors,
never successful partial boards.

Sync preview reports numeric old/new values and exact item/field IDs. Score/rank
outputs must differ from each other and every ranking/filter/WIP input by alias
**and resolved field ID**. Excluded/filtered-out items retain their existing
values; narrowed cohorts can intentionally leave stale values outside the cohort.
Exactly equal stored numeric values are no-ops. `--apply` and `--dry-run` conflict;
no config setting enables writes. Before apply, WADE refreshes the board and
schema and sends zero writes if the baseline changed. It re-reads each affected
item before its write and verifies identity and numeric readback afterward.
This narrows races; GitHub does not provide a conditional-write transaction here.
Writes are sequential, paced and never blindly retried. A failure exits nonzero
and reports confirmed, failed/unknown and not-attempted operations; confirmed
writes remain, with no automatic rollback. Re-run from current truth to converge;
a second unchanged apply sends no mutations. Sync cannot create/remove/archive
items, edit issues/status/schema, clear fields or reorder the board.

## Quick Start

Initialize WADE in your project (once):

```bash
wade init
```

Then work in two deliberate steps — plan, then implement:

```bash
# Plan deliberately — the AI writes validated plan files, each becoming a
# lightweight task plus a draft PR
wade plan

# Start (or resume) implementation on task 42
wade 42
```

`wade <N>` routes on the task's **PR state**, not on whether a plan exists:

- **No PR yet, or a closed unmerged one** → starts (or resumes) an implementation
  session.
- **Open draft PR** → continue the in-flight session (or start it if the
  worktree is gone).
- **Open ready PR** → choose to continue working, address review comments, merge,
  or open the PR in your browser.
- **Merged PR** → reports the task is already merged and stops — no session.

It never launches a planning session on its own — reach for `wade plan` when you
want planning.

### The WADE lifecycle

```
wade plan ─▶ tasks + draft PRs ─▶ wade <N> ─▶ AI implements in an isolated worktree ─▶ PR opened ─▶ wade review pr-comments <N> ─▶ merge
```

- **You run** the entry points: `wade plan`, `wade <N>` / `wade implement <N>`,
  `wade review pr-comments <N>`, and `wade cd <N>`.
- **The AI runs** the session commands that enforce the workflow and open/update
  the PR: `wade implementation-session {check,sync,done}`,
  `wade review-pr-comments-session {check,sync,done,fetch,resolve}`. Native
  planning returns an artifact; the parent runs its review and validation gates.

## Commands

### Commands you run

| Command | Description |
|---------|-------------|
| `wade <N>` | Smart entry — routes by the task's PR state to implement, continue, review, merge, or open-in-browser |
| `wade plan` | AI planning session — writes validated plans that become tasks + draft PRs |
| `wade implement <N>` | Create a worktree and start an implementation session for a task |
| `wade implement-batch <N> <M> ...` | Start parallel sessions for multiple tasks *(beta)* |
| `wade review pr-comments <N>` | Start a session to address PR review comments |
| `wade review trigger <N>` | Post configured bot-review triggers on the task's PR |
| `wade review plan <file>` | AI-powered plan review |
| `wade review implementation` | AI-powered code review of the current diff |
| `wade review batch <N>` | Coherence review across parallel implementation branches |
| `wade cd <N>` | Navigate to a task's worktree (requires shell integration) |
| `wade task create` | Create a task interactively (in your configured provider) |
| `wade task list` | List open tasks |
| `wade task read <N>` | Show task details |
| `wade task update <N>` | Update a task body or add a comment |
| `wade task close <N>` | Close a task |
| `wade task deps <N> <M> ...` | Analyze dependencies between tasks |
| `wade worktree list` | List active worktrees |
| `wade worktree remove <N>` | Remove a worktree |
| `wade init` | Initialize WADE in the current project |
| `wade update` | Upgrade WADE and refresh project files |
| `wade deinit` | Remove WADE from the current project |
| `wade check-config` | Validate `.wade.yml` configuration |
| `wade skills list` | List built-in methods and discovered project skills |
| `wade skills resolve --session <kind>` | Explain effective session bindings and precedence |
| `wade skills resolve --delegation <kind>` | Explain an effective standalone delegation binding |
| `wade skills check` | Deep-check discovered trees and configured skill refs |
| `wade session describe` | Show the active session's frozen workflow/bindings |
| `wade session refresh-skills` | Explicitly rediscover and replace active skill snapshots |
| `wade knowledge add` | Append a project learning from stdin (unavailable in a plan/deps session) |
| `wade knowledge get` | Print the current project knowledge file |
| `wade knowledge rate` | Record an up / down / stale vote for a knowledge entry |
| `wade knowledge status` | Show uncommitted knowledge/ratings changes and any pending ratings migration |
| `wade knowledge enable [--path PATH]` | Enable knowledge capture and optionally set custom file path |
| `wade knowledge check <id> current --file <path> --note <reason>` | Record committed-evidence assessment |
| `wade knowledge feedback <id> useful` | Record usefulness, independently of correctness |
| `wade knowledge reconcile --check --json` | Read-only committed proof/publication report |
| `wade knowledge proposals list` | Inspect ignored owner-bound observations |
| `wade knowledge disable` | Disable knowledge capture (keeps existing knowledge file) |

Short aliases: `wade p` (plan), `wade i <N>` (implement), `wade r <N>` (review pr-comments).

Interactive pickers (the arrow-key menus commands like `wade task create` show)
wrap long choices to the terminal width instead of cropping them, so no option
text is lost in narrow terminals and the list reflows automatically on resize.

`wade worktree remove <N> --force` skips confirmation while protecting uncommitted
changes and unmerged local history. Clean squash/rebase leftovers can be removed
when GitHub records that exact local branch tip as the final merged PR head for
origin's repository and the configured base. WADE fetches that base and verifies
its history still contains the PR merge commit; deleted source branches and later
base edits are supported. Repository/head mismatches, unavailable API/fetches,
and unverified cherry-picks remain protected. Ordinary ancestor-contained
branches can still be cleaned up offline. The worktree may be removed while
`git branch -d` conservatively retains its non-ancestral local branch.
`--discard-dirty` is the separate explicit opt-in to discard work; `--force`
never grants that permission.

### Session commands the AI runs

These are invoked by the AI during a session — you normally don't run them by hand.

| Command | Description |
|---------|-------------|
| `wade implementation-session check` | Verify the implementation session's Git, GitHub, and required local capabilities before edits |
| `wade implementation-session docs --updated \| --not-needed "reason"` | Record the required documentation-impact decision for current HEAD |
| `wade implementation-session sync` | Sync the branch onto the base branch |
| `wade implementation-session done` | Completion gate — runs the gates, pushes, and opens/updates the PR |
| `wade review-pr-comments-session check \| docs \| sync \| done` | Same lifecycle and documentation receipt for a review session |
| `wade review-pr-comments-session fetch <N>` | Fetch unresolved PR review comments as markdown |
| `wade review-pr-comments-session resolve <thread>` | Mark a PR review thread as resolved on GitHub |
| `wade plan-session refresh <original-plan-dir>` | Explicitly refresh retained native/collected contracts; archive originals and require the appropriate fresh review |
| `wade plan-session check` | Verify detached planning capabilities before writing plan artefacts or knowledge votes |
| `wade plan-session done <plan_dir>` | Import an optional native plan (`--from-file` / `--from-stdin`), validate, and complete the reviewed handoff |
| `wade deps-session check` | Verify the detached dependency-analysis runtime before writing output or staging a knowledge vote |

Most workflow commands accept `--ai <tool>`, `--model <model>`, `--effort <level>`, `--permission-mode <tier>`, and `--yolo` to override configured defaults. `review pr-comments` additionally accepts `--effort none` to pin the tool default instead of a configured effort across a later wait/relaunch. `plan`, `implement`, and `review pr-comments` accept repeatable `--skill` and `--review-skill` methodology bindings plus `--refresh-skills` for an existing frozen session; `implement-batch` forwards the same binding request to every child session. Standalone plan/code/batch review and `task deps` accept repeatable `--skill`. `plan`, `implement`, `implement-batch`, standalone reviews (`review plan`, `review implementation`, `review batch`), `review pr-comments`, `task deps`, and the `wade <N>` shorthand also accept `--sandbox` / `--no-sandbox` (see [AI runtime sandbox profile](#ai-runtime-sandbox-profile)); batch and shorthand forward an explicit flag to the child session they launch. `implement` additionally supports `--detach` (new terminal tab), `--cd` (print worktree path only), and `--base <branch>` (see [Planning & base branches](#planning--base-branches)).

## Planning & base branches

`wade plan` launches **Claude, Cursor, Copilot, OpenCode, Antigravity CLI, and Codex**
in their own interactive terminal, with **native Plan mode active before the
first task turn**. You keep the tool's UI, questions, and conversation. Crossby
validates native support and translates approval flags. For Codex, Crossby
submits `/plan` first and WADE sends the task on its ready event. Native questions
and subsequent interaction stay in the tool.

Use `--permission-mode` (or `--yolo`) for the native approval policy:

| CLI | Supported modes while planning |
|-----|--------------------------------|
| Claude | `default`, `auto`, `yolo` |
| Cursor | `default`, `auto`, `yolo` |
| Copilot | `default`, `accept-edits`, `yolo` |
| OpenCode | `default`, `yolo` |
| Antigravity CLI | `default`, `yolo` |
| Codex CLI | `default`, `accept-edits`, `auto`, `yolo` |

Unsupported combinations fail before creating a planning worktree. These are
native approval settings, not permission to implement the plan. The later
implementation offer still requires an explicit decision.

Where the tool supports a native output-directory setting (Claude), WADE uses
`.wade/plans/native/`. Other tools can keep their native location. Before exit, the planning
workflow submits it with `wade plan-session done .wade/plans --from-file <path>`;
`--from-stdin` accepts explicit Markdown when no file is available. WADE writes
its canonical copies. `wade review plan <file>` reviews those copies inside the
session; prompt-mode review emits the frozen method and requires actual
self-review followed by `--ack-self-review`. Run `done` again after review.
Changed content requires a current review before completion. A missing-review
error on the first import means the file was imported and awaits review.

One task can be plain Markdown (`# feat: title`, `## Complexity`, tasks and
acceptance criteria). Multiple tasks use the explicit versioned
[plan bundle contract](templates/workflows/reference/plan-output-contract.md),
with filenames and `depends_on` relationships; headings do not imply task
boundaries. Knowledge-enabled sessions include typed `knowledge_proposals` in that bundle,
including an explicit empty list when appropriate. The parent revalidates the
completed handoff after exit, then creates tasks and draft PRs. Failed or missing
handoffs retain output and create no tasks.

**Codex requires CLI 0.154.0 or newer and a POSIX terminal (macOS/Linux).**
Crossby 0.43.2 repairs startup with CLI 0.162.1, including its Folder access
prompt and transient Plan footer. Explicitly trust the intended folder when
Codex asks; WADE never supplies that consent. Crossby observes native Plan
activation before WADE sends the initial task, then confirms its submission
across terminal redraws. Unrecognized startup screens fail closed without
retrying the task. This temporary terminal adapter can be replaced by a native
launch flag when Codex provides one. WADE requires the task-submitted
event before collecting the explicit reviewed handoff. Codex can submit plan
Markdown through `--from-stdin`; no native file destination is assumed.

All native terminals now use the ordinary sandbox default (off), including Codex;
use `--sandbox` or `ai.plan.sandbox: true` to retain confinement. The retained
collector fallback has its own safe default (on). Explicit `--sandbox`, `--network-access` /
`--no-network-access`, and `--trusted-dir` require support from the selected tool.
No network flag adds no network grant and does not promise isolation.
`--approval-policy on-request|untrusted|never` and `--timeout` are collector
settings; interactive terminals use `--permission-mode` and have no time limit.
Set `ai.plan.mode` to `interactive` or leave it unset for native terminals.
GUI launchers remain unsupported for planning.

Authentication, model availability, and successful handoff are runtime checks.
Native terminal output is retained for recovery; usage is reported only when
the adapter can extract it from that output. WADE's handoff ID is not a native
CLI session ID.

If the OS denies the parent access after the terminal exits, WADE reports the
permission failure instead of calling the handoff absent, leaves the detached
planning worktree intact, and prints a recovery command. Restore filesystem
access, then run `wade plan --recover <planning-worktree>` from the source repo.
Recovery does not launch an AI or regenerate content: it accepts only a retained
registered detached worktree, then rechecks the frozen bindings, completed state,
exact plan content, review receipts, dependencies, and plan validation before any
task/provider write. It rejects a handoff whose original launch binding was never
durably recorded rather than applying settings changed after collection. If even the plans directory cannot be enumerated, WADE says
the retained plan count is unknown rather than reporting zero. WADE never changes
OS privacy or sandbox settings for you.

Malformed or unsupported persisted handoff/review metadata and incompatible
frozen contracts also retain the intact original planning owner, including raw
output, launch binding and existing progress. Required metadata is validated
before editable Markdown; an invalid plan cannot mask a launch/progress failure
and trigger owner cleanup. WADE reports bounded public field
locations/error codes, the current reader version and available declared schema
and workflow versions; an unrecorded writer version remains unavailable. Strict
unknown-field/version rejection stays in force. This differs from editable plan
validation: a copied plan archive cannot replace the registered owner or its
review authority, and unresolved metadata failure stages no knowledge or tasks.

Finish or safely stop active planning parents before changing their shared
editable source or environment. A workflow refresh cannot replace Python models
already imported by a running parent. After stabilizing the installation, start a
fresh compatible WADE process and use the printed original-root recovery command.
Recovery may initialize missing progress from valid original launch metadata;
malformed or mismatched present progress is never silently replaced. If supported
tooling can read the schema but frozen contracts need refresh, explicitly run
`wade plan-session refresh <original-worktree>/.wade/plans`, genuinely review each
plan, and run `wade plan-session done` before recovery. An unknown future or
unreadable schema remains retained until supported tooling can interpret it.
Deleted/unregistered owners and copied archives require a fresh planning owner,
content-only import and new review/completion gates.

`wade plan --issue <N>` re-plans an existing task. If the session produces a
single plan file, its reviewed body replaces the plan in the task's existing
open PR, and the task stays open. A task title change keeps the same PR and head
branch. Current issue edits and unrelated PR sections are preserved; a missing
Full plan link is appended to the freshly read issue body. A failed lookup,
read, write, structural check, or retarget reconciliation retains the original
planning workspace and handoff instead of reporting success and deleting it.
Retry with `wade plan --recover <planning-worktree>` after repairing the cause.
A completed, cleaned-up session or a copied archive cannot recover its old
receipt authority: use a fresh `wade plan --issue <N>` session with archived
content as input, then complete new review/done gates and verify the PR-backed
plan before implementing. The next implementation reads the same structurally
owned plan; fenced marker examples cannot redirect or truncate it. Generic
implementation resume keeps the existing plan. Fresh PR creation rejects unsafe
plan ownership before branch or PR creation.

If the session decides the work should be split into several independent pieces
(2+ plan files), `#N` is **superseded**: a new task + draft PR is created
per plan file, a comment and a `> **Superseded by ...**` banner are added to
`#N`, and it's closed as *not planned* (confirmed via prompt unless
`--yolo`/non-interactive). If any plan file fails to become a task, `#N` is
left open with a warning instead of superseding on a partial split.

### Base branch

By default WADE branches from and merges into the project's configured main branch (`project.main_branch` in `.wade.yml`, falling back to the repo's detected default). To target a different branch — e.g. `develop` or a `release/*` branch — declare it once, at planning time, in an optional `## Base Branch` section of the plan file:

```markdown
## Base Branch
develop
```

The draft PR is then branched from and targeted at `develop`, and `wade implement` cuts the worktree from it and merges back into it. Omit the section to keep the default (main). The base is persisted as the draft PR's own base, so `wade implement <N>` recovers it automatically — no extra flags needed.

To choose or override the base at implement time, pass `--base`:

```bash
wade implement 42 --base develop   # branch from, target, and merge into develop
```

`--base` overrides the plan-declared base and retargets the existing draft PR to match. A malformed base value is rejected up front — whether declared in the plan (fails `wade plan-session done`) or passed as `--base` (fails fast when you run `wade implement`); a well-formed base branch that does not exist fails draft-PR creation with an actionable message. Changing the base of a PR whose work is already in flight — whether by re-running planning or by `wade implement --base` — is not applied silently: it requires explicit confirmation, because the branch's existing history cannot be moved onto the new base without discarding it.

A scaffold branch checked out in a worktree can be moved onto a different base only when its tracked files are clean and no untracked or ignored paths obstruct the destination tree. Colliding files, symlinks, and directories cause refusal before the branch or PR base changes; noncolliding generated session files remain allowed. Preserve or move the reported paths outside the affected worktree before retrying. Ignored files receive the same protection as ordinary untracked work. Retarget and rollback explicitly leave submodule worktrees unchanged, including when `submodule.recurse` is enabled; update submodules separately when needed.

## Reviews & bot triggers

WADE splits review into an **AI review pass** you or the agent can invoke, and
**external bot triggers** for services like CodeRabbit, Codex, and Bugbot.

| Command | What it reviews |
|---------|-----------------|
| `wade review plan <file>` | A plan file, before it becomes tasks |
| `wade review implementation` | The current implementation change (mandatory gate before `done`) |
| `wade review batch <N>` | Coherence across parallel implementation branches |
| `wade review pr-comments <N>` | Starts a session to address human/bot PR comments |
| `wade review trigger <N>` | Posts configured bot-review trigger comments on the task's PR |

When `wade review pr-comments <N>` recreates a removed worktree, a successful
fetch selects the current remote branch commit, including a force-pushed head.
If the local branch points elsewhere, WADE first preserves that exact commit
under `refs/wade/recovery/<branch>/<old-commit>` and prints a `git branch` command
to recover it. These refs are retained automatically; existing worktrees are
never realigned by this recovery path. Fetch never follows a symbolic tracking
ref into a local branch.

A failed fetch uses a local branch only after a live remote query confirms that
the remote head is absent, and warns that the local code may be stale. Fetch or
query errors, occupied destinations, and branches held by another worktree stop
recovery without discarding local work. Partial recovery retains its worktree,
backup refs and an incomplete-state record under the shared Git directory's
`.wade/review-recovery/`. Later review startup refuses that destination before
checking the PR or launching an agent. Inspect the reported files, branch tips,
backup refs and marker to resolve it manually; running implementation or deleting
the marker alone does not repair an incomplete checkout.

Prompt-mode implementation review prints the bounded review prompt and exits 2;
printing it does not certify that a review happened. After performing that
self-review and addressing its findings, run
`wade review implementation --ack-self-review` to write the current commit and
frozen-review-binding receipt. Successful headless or interactive reviews write
the same receipt directly. In an implementation session, once the active frozen
review binding has consumed `done.max_review_passes` (default 2), `wade review
implementation` exits successfully without launching another reviewer and directs
you to `wade implementation-session done`. The dispatch cap does not block a
valid `--ack-self-review`, which still writes its active-binding receipt.
PR-comment sessions have no such cap: their review pass notice reports only the
running count and the `wade review-pr-comments-session done` requirement for a
satisfying review of the current commit, and a successful review there points
to that command rather than `implementation-session done`.

The first implementation review for a frozen reviewer binding receives the
complete branch change. After a successful review and a new fix commit, a
re-review receives only the committed delta since that successful same-binding
review, plus any staged or unstaged changes and the local `PLAN.md` when safely
available. This narrows repeated review work without weakening `done`: the final
receipt is still written for the exact current commit. An unreadable record,
changed binding, unreachable baseline, or merge on the baseline-to-HEAD path
falls back to the complete branch change.

If you run session sync after review and it changes HEAD, the earlier receipt
does not satisfy the review gate for the new commit, even for a clean merge.
Run `wade review implementation` again before retrying `done`; the existing
implementation review-pass cap and explicit bypasses still apply.

To fetch the unresolved comments themselves during a review session, the agent
runs `wade review-pr-comments-session fetch <N>`; it resolves individual threads with
`wade review-pr-comments-session resolve`.

When a PR-comment session starts, WADE records its pre-edit commit and a feedback
snapshot under local `.wade/review-cycles/` state, outside the immutable session
bundle. Re-fetching retains prior feedback while adding new feedback, but never
advances that baseline. Its closing review receives the accumulated feedback and
the cycle delta. Unsafe, mismatched, stale, or merged lineage instead uses the
complete branch change and its full-branch result contract, which overrides the
feedback-fix method's narrow scope. A successful `review-pr-comments-session
done` clears the cycle so later feedback starts from the then-current PR head.

### The auto-launched review session

The auto-launched **review session** — the one started when you pick **"Wait
for reviews"** after `wade implementation-session done` and comments land —
resolves its tool, model, effort, and autonomy tier from a dedicated
`ai.review_pr_comments` section (the same keys every `ai.<command>` section
accepts: `tool`, `model`, `effort`, `mode`, `permission_mode` / `yolo`,
`sandbox`, `enabled`, `timeout`):

```yaml
ai:
  review_pr_comments:
    tool: claude
    model: claude-sonnet-5
    effort: high
    permission_mode: yolo   # auto-launched review runs unattended
```

With this set, the review session runs in the configured tier even when the
implementation session ran without it (the `permission_mode: yolo` above starts
the review session with no prompts). With `ai.review_pr_comments` **unset**,
tool / model / autonomy fall back to the global `ai.*` defaults (`default_tool`,
`default_model`, `ai.permission_mode` / `ai.yolo`) — **not** through
`ai.implement.*`. Projects that relied on `ai.implement.tool` / `model`
implicitly governing the review session should set `ai.review_pr_comments.tool`
/ `model` (or the global defaults). An explicit `wade implement --yolo` /
`--permission-mode` still carries into the auto-launched review session.

### Triggering external bot reviews

External review bots (CodeRabbit, Codex, Bugbot) normally auto-review on push,
but WADE can **post a trigger comment** to force a fresh review — after
addressing comments, pushing fixups, or when a bot has paused. The workflow gains
an explicit trigger phase: `implement → done → trigger bots → poll/address
comments`.

```bash
wade review trigger <N>            # post every enabled bot's trigger to #N's PR
wade review trigger <N> --bot codex --bot bugbot   # only a named subset
wade review trigger <N> --dry-run  # print what would be posted, post nothing
```

Each bot reports its own status line — `posted` / `skipped (disabled)` /
`failed: <error>` / `would post (dry-run)`. A failing post for one bot never
stops the others; the command exits non-zero only when the PR can't be resolved,
an unknown `--bot` name is given, or **every** attempted post fails. An explicit
`--bot <name>` triggers that bot even if it is `enabled: false`.

The bots and their trigger phrases live in an optional top-level `bot_review:`
block. It is fully defaulted — a project with no block still triggers CodeRabbit,
Codex, and Bugbot — and every field is overridable:

```yaml
bot_review:
  auto_trigger: false            # opt-in; when true, `done` posts triggers after it pushes
  offer_on_done: true            # default; when auto_trigger is off, `done` OFFERS them
  arrival_timeout: 300           # seconds to wait for an enabled bot to review HEAD
  ack_timeout: 900               # longer ceiling once a bot acknowledges (👀/+1 reaction)
  bots:
    - { name: coderabbit, trigger: "@coderabbitai review", enabled: true }
    - { name: codex,      trigger: "@codex review",        enabled: true }
    - { name: bugbot,     trigger: "bugbot run",           enabled: true }
```

Each bot's `name` keys both `--bot` selection and the per-bot trigger
marker (a file under `.wade/`), so it must be **unique** and a **safe
identifier** — letters, digits, `.`, `_`, `-` only (no path separators or
spaces). A duplicate or invalid name is rejected when the config loads, not only
under `wade check-config`.

With `auto_trigger: true`, both `wade implementation-session done` and `wade
review-pr-comments-session done` post the enabled bots' triggers **after a
successful push**, at most **once per bot per commit SHA** (repeated `done`/`sync`
on the same commit post nothing further; a bot whose post failed retries). The
manual `wade review trigger` command always fires and ignores those markers, so a
same-SHA `done` still auto-triggers independently. `wade init` writes the block
for you and asks which of the three modes below you want. Whatever a bot posts
back is **untrusted context** — the review session's verify-before-fixing rule
still applies.

#### Not auto-triggering? WADE offers instead of forgetting

With `auto_trigger: false` (the default) WADE does **not** silently drop the
triggers — `offer_on_done: true` (also the default) makes it offer them once the
work is pushed, so you never have to remember `wade review trigger <N>`:

| Where | What you see |
|-------|--------------|
| `done`, at a terminal | `Post the bot-review triggers now (coderabbit, codex)?` |
| `done`, in an AI session (no TTY) | a line telling the agent to offer it in its closing dialog |
| the post-session menu (`Merge PR` / `Wait for reviews`) | an extra `Trigger bot reviews (…), then wait` entry |
| the quiet-PR menu (`Keep polling` / …) | an extra `Trigger bot reviews (…)` entry |

Both `done` commands also take an explicit override for a single run:

```bash
wade implementation-session done --trigger-bots      # post them, whatever the config says
wade review-pr-comments-session done --no-trigger-bots  # stay quiet, even with auto_trigger: true
```

Every one of these paths is **once per bot per commit SHA**, like the
auto-trigger: a bot already triggered for the current commit is neither posted
again nor offered again (`wade review trigger <N>` remains the way to force a
re-post). Repos whose bots already review every push automatically can turn the
whole offer off with `offer_on_done: false` — then WADE stays silent unless you
ask.

**Expectation-verified completion.** WADE no longer reports a review "done" just
because no blocking comments are present — it verifies that every **enabled** bot
has actually posted a review covering the latest commit. While an expected bot has
not, `wade review pr-comments` / `poll` / `fetch` keep waiting (and name the bot)
rather than printing "All review comments resolved". The wait is bounded by
`arrival_timeout` (default 300s); past it the bot stops blocking and is surfaced
distinctly (`⚠ No review from <bot>`), never silently swallowed into "done". A bot
that **acknowledges** with a reaction (👀 / +1) is treated as actively reviewing
and waited for up to the longer `ack_timeout` (default 900s). **Latency note:**
because all three bots ship enabled, a repo without one installed adds up to
`arrival_timeout` to each review check before proceeding — disable an unused bot
(`enabled: false`) to remove its floor. `ack_timeout` must be `>= arrival_timeout`.

## Guardrails & completion gates

### Session guards

When WADE creates a worktree it installs hooks into whichever tools your project
uses, so session rules are enforced in code rather than left to the agent to
honour. Guards are written per-worktree at session start — upgrading WADE is
enough to pick up improvements, with no re-init or migration.

| Guard | What it blocks |
|-------|----------------|
| Worktree containment | Writes that land outside your worktree |
| Plan-artifact | During `wade plan`, writes to anything but plan artifacts |
| Session completion | Finishing an implement/review session with unfinished work not yet run through `done` (nudges once) |
| Native plan completion (parent gate) | Creating tasks before authoritative import, review, and strict plan validation |

The session-completion guard keys on the same fact `done` records — a sha-keyed
`.wade/done@<HEAD>` marker written when `done`'s gates pass — so it nudges only
when the branch has commits ahead of its base **and** `done` has not finalized
the current commit. An early "stopping to ask a question" turn (no commits ahead)
never triggers it. This Stop nudge fails **open**. Native planning does not
install the old file-based Stop nudge. Interactive sessions explicitly import,
review, and complete their plans before exit; the parent revalidates them. Codex
retains parent-side collection and review. Native transports may differ in hook
support; completion does not depend on hooks firing.

Independently of that nudge, `wade plan` now **strictly validates** plan files
before creating tasks: a `PLAN*.md` missing a valid `## Complexity` or a
conventional-commit title is dropped with a loud error instead of silently
becoming a task with no complexity label.

`wade task create` enforces the same conventional-commit **title** rule (the type
list is the single Python source in `utils/conventional.py`): a non-conventional
`--title` (or a plan file's `# Title`) is rejected, and interactive create
re-prompts. The PR title is derived from the task title verbatim, so this is
what keeps a wade-opened PR from ever failing the `PR Title Lint` CI check;
`done` also blocks on — and syncs — a stale non-conventional title on an already
open PR (see the completion-gate table below).

Like `wade plan`, `wade task create` also reads a `## Complexity` section from the
task body (from `--body`, a plan file, or interactive input): when present, its
value is attached as a `complexity:X` label on top of the project label — the same
signal that drives automatic model routing, so a hand-created task routes to the
right model just like a planned one. Labeling is best-effort: a failure to apply
the label never fails task creation, and a body with no `## Complexity` section
just skips it. An explicit `--label complexity:X` takes precedence over the body
value, so the task never ends up with two conflicting complexity labels.

If some files pass and others fail, a human must explicitly accept a
dependency-closed valid subset, even with `--yolo`. Noninteractive runs abort.
If you decline, or if every plan file fails validation, no tasks are created —
the generated `PLAN*.md` are preserved to a temp directory so you can fix the
reported errors and re-run instead of regenerating from scratch.

Both write guards cover **shell commands** as well as file edits, so a redirect
like `printf x > ../other-repo/app.py` is blocked, not just an `Edit` call. Shell
coverage is best-effort defense-in-depth: it catches ordinary commands, not
deliberate obfuscation (variable indirection, command substitution, subshells).

| Tool | Write guard | Session-completion guard |
|------|-------------|--------------------------|
| Claude Code | ✅ | ✅ |
| Cursor | ✅ | ✅ |
| GitHub Copilot | ✅ | ✅ |
| OpenAI Codex | ✅ shell (writes are sandboxed natively) | ✅ |
| Antigravity CLI | ✅ | ✅ |

The remaining supported tools (OpenCode, VS Code, Antigravity IDE) expose no hook
mechanism WADE can install into, so they receive neither hook guard. Their
session still loads the fixed workflow, and deterministic lifecycle commands and
completion gates remain authoritative.

### Completion gates

`wade implementation-session done` (and `review-pr-comments-session done`) is the
authoritative completion gate: it refuses to finalize until the work is actually
ready, then writes the `.wade/done@<HEAD>` marker and pushes. Policy gates normally
have an escape hatch under a `done:` block in `.wade.yml` (all default on).
Decision and integrity gates deliberately require an explicit valid outcome:

| Gate | Refuses when… | Hatch |
|------|---------------|-------|
| PR-SUMMARY | `PR-SUMMARY.md` is missing, empty, or still a template placeholder (both change-producing session types) | `done.require_pr_summary: false` |
| Documentation decision | neither an `--updated` nor reasoned `--not-needed` receipt exists for current HEAD | *no disable toggle; `docs --not-needed "<reason>"` records the explicit no-change outcome* |
| Sync | the branch is behind main — auto-syncs first, refuses only on conflict (both change-producing session types) | `done.require_sync: false` |
| Review ran | `wade review implementation` has no successful external-review or explicitly acknowledged self-review record for the current commit **and frozen REVIEW binding**, or the frozen workflow/skill bundle no longer matches its manifest. Bundle validation happens before receipts or pass counts are trusted. Prompt emission alone never satisfies this gate. **Implementation sessions bound this loop:** at `done.max_review_passes` (default 2), `wade review implementation` stops before launching an excess reviewer, while a valid `--ack-self-review` may still record its receipt; `done` remains authoritative and completes an unreviewed later commit with a cap-reached status instead of looping forever | `--skip-review`, `done.require_review: false` (auto-off when `ai.review_implementation.enabled: false`) |
| Resolved threads | unresolved PR review threads remain (review-comments only) | `done.require_resolved_threads: false` |
| Conventional title | the task title is not a conventional-commit title (the PR title is derived from it, so it would fail `PR Title Lint`) — blocks; when valid but the open PR's title differs, syncs the PR title to match (both session types) — if that sync fails while the PR's current title is itself non-conventional (lint would fail), `done` fails so it can be retried | `done.require_conventional_title: false` |
| Knowledge valid | committed knowledge/history has structural conflicts, invalid references, or uncommitted knowledge after sync; recovery pushes repeat reconciliation and cleanliness | *none — gated by `knowledge.enabled`; no `done.*` hatch* |

Finalizing an existing PR requires reading its current body successfully. If
that read or the body update fails, completion stops, clears the local done
marker, and leaves ready promotion, label removal, and bot-review triggers
pending. Re-run the session's `done` command after resolving access; it reads the
body again and preserves existing plan and user content. A successfully read
empty body remains valid. The branch may already have been pushed before this
retryable failure.

Summary and dependency updates preserve embedded plans, fenced examples, and
unrelated sections. An existing managed block is replaced in place. When an
empty update removes its content while a same-named human section remains, an
empty marker pair retains ownership so retries cannot delete the human section,
including headings exposed by removing old HTML content. Summary and review-status
changes are assembled together, so preservation and empty-pair decisions reflect
the complete update.
Otherwise the empty owned block is removed. Older unmarked `## Summary` and `## Dependencies` sections migrate only from a unique,
column-zero heading, stopping at the next top-level H1/H2 heading (including
indented and setext headings), managed block, or code fence. Ambiguous headings, broken or nested markers, duplicate owned blocks, and
unclosed fences, excessive nesting, or context changes that invalidate unrelated
managed ownership stop the update with a repair message. This also applies to new
`PR-SUMMARY.md` content: close its fences and keep marker examples inside fenced
code. Repair the indicated input and retry; dependency batches skip the affected
issue and continue with the others.

A **pre-push git hook** (`done.pre_push_backstop`, default on) backs the gate up:
a push of the session branch without a current `.wade/done@<sha>` marker is
refused, so committing-and-pushing straight past `done` doesn't work. It is
worktree-scoped (never touches your main checkout or sibling worktrees) and
chains to any pre-existing `pre-push` hook. **Honesty:** `git push --no-verify`
bypasses the backstop in one flag — this is a quality/backstop layer that makes
the gate hard to skip, not an airtight boundary.

`done` also writes a **`## Review Status`** line into the PR body recording the
review outcome — reviewed at `<sha>`, reviewer changed, skipped via
`--skip-review`, gate disabled (`done.require_review: false` /
`ai.review_implementation.enabled: false`), or completed at the active review
binding's pass cap. A skipped, stale-reviewer, or never-run review is therefore
visible to reviewers rather than hidden in worktree state.

After a PR merge, WADE updates the main checkout and restores any tracked local
changes using the exact stash SHA it captured, even if another worktree pushes
its own stash meanwhile. WADE preserves any remaining owned stash entry after
restoration because removing a positional stash entry can race with another push.
Recovery commands are printed on separate copyable lines, with shell-quoted
checkout paths and intact arguments even in narrow terminals. Inspect the
reported checkout using the printed stash-list command, compare SHA/ref/message,
and verify the current ref still names the intended SHA before manual cleanup.
Resolve conflicts before retrying the printed apply command; if the stash object
is unavailable, recover it first. Sync's apply command runs from the checkout
named in its explanation. Local recovery warnings do not mean the remote PR
merge failed.

## Task Providers

WADE can pull tasks from three backends — pick one when you run `wade init`:

| Provider | Where tasks live | Auth |
|----------|-------------------|------|
| `github` *(default)* | GitHub Issues | `gh` CLI |
| `clickup` | ClickUp list | API token in env var |
| `markdown` | A single committed `ISSUES.md` | None |

**PRs and reviews always flow through GitHub, regardless of the task provider.**
Non-GitHub providers compose a delegate that forwards every PR/review operation —
review-thread listing and resolution, PR comments, and review status — to GitHub
via the `gh` CLI, so `wade review pr-comments`, the auto-poll loop, and
`wade review-pr-comments-session fetch` / `resolve` behave identically across
providers. Only where a task itself lives differs.

### ClickUp provider

Task state follows ClickUp's canonical `status.type`, regardless of the display
name: `closed` maps to WADE Closed; `done` and every other nonempty type remain
Open. A renamed Closed status still closes the task, and a custom status named
“Complete” does not. Legacy payloads with missing, null or blank type fall back
to the names `closed`, `complete` and `done`; typeless “Done” is therefore
ambiguous and retains the older Closed interpretation.

Closing a task reads its home List's effective statuses, including inherited
statuses, and sends the unique canonical Closed status's exact display name.
Missing or ambiguous metadata fails before the update; the returned task must
have canonical Closed type before WADE reports success. WADE does not assume
that “closed” or “Done” is the configured closing name.

### Markdown provider

Useful when you want tasks versioned alongside the code, with no external
service. Each task is one `##` heading in the file:

```markdown
# Wade Issues

## #47239185 Add login feature

<!-- wade
state: open
labels: feature, complexity:medium
-->

Description body here. Sub-headings, code blocks, anything markdown.
```

- The file is resolved against the **main worktree**, so every linked
  worktree reads/writes the same physical file. No textual merge conflicts
  on `ISSUES.md`.
- IDs are random 8-digit decimal so two parallel `wade implement` sessions
  in different worktrees can't collide. They stay numeric so existing
  `#NN` checklist refs in tracking-issue bodies still work.
- Configure via `.wade.yml`:

  ```yaml
  provider:
    name: markdown
    settings:
      path: ISSUES.md      # relative to repo root, or absolute
      auto_commit: false   # attempt a close-only commit for a clean tracked file
  ```

- After a PR merges, `provider.close_task` flips the section's `state` to
  `closed` in `ISSUES.md`. By default the file is left modified in your
  working tree — commit it yourself. Set `auto_commit: true` to have
  wade attempt a `chore: close #N` commit containing only the selected task's
  state and `in-progress` label changes. Other file content and line endings
  remain intact, and unrelated staged or unstaged work stays untouched.
- Auto-commit requires the task file to be a clean tracked regular file with
  matching HEAD, index, and disk contents. Preexisting staged, partially staged,
  or unstaged task-file edits, hidden index flags, replacement objects, and
  uncertain repository state cause a warning and skip the commit. The task still closes on disk;
  its original staged version and flags remain intact. Optional task comments
  are separate edits and follow this same skip policy.
- Ordinary hooks and signing remain enabled. An invocation-local guard rejects
  a hook-expanded candidate before HEAD advances; it does not change installed
  hooks or persistent Git configuration. Unsupported Git hook dispatch
  (Git older than 2.36), hook rejection, signing failure, and Git errors are
  logged without undoing the disk closure. Hooks that directly edit other
  files or refs retain their own side effects.
- Task-like headings inside backtick or tilde fenced code blocks remain body
  or prelude examples. A real heading after a valid closing fence starts the
  next task; an unclosed fence continues through the end of the file. Fences
  use zero to three leading spaces and at least three matching delimiters;
  closing runs use the same delimiter, at least the opening length, and only
  spaces or tabs afterward.
- Tracking tasks with `- [ ] #N` checklist bodies work the same as with
  GitHub Issues — markdown's `find_parent_issue` scans every section's
  body for child refs.

## Permission modes, autonomy & sandboxing

### Permission modes

These autonomy tiers apply to ordinary launches and supported native Plan
terminals. Codex planning retains parent-only default/YOLO with a separate
collector approval policy, as described above.

`--permission-mode` sets how much autonomy the AI tool is granted — an axis
independent of the delegation `--mode` (which controls *how* a tool is
dispatched: prompt/interactive/headless). The tiers, most→least permissive, are
`yolo` > `auto` > `accept-edits` > `default`:

| Tier | Behavior |
|------|----------|
| `default` | Prompt for every action (no autonomy grant). |
| `accept-edits` | Auto-apply file edits; still prompt for shell/commands. Claude and Antigravity CLI. |
| `auto` | Classifier-mediated auto mode (a model reviews each non-read action). Claude and Cursor. |
| `yolo` | Full autonomy — no prompts. |

`--yolo` is a back-compat alias for `--permission-mode yolo`; an explicit
`--permission-mode` wins when both are given. The same values are accepted in
`.wade.yml` as `ai.permission_mode` (global) or `ai.<command>.permission_mode`
(per-command), with `yolo: true` still honored as the alias. A tier a tool
doesn't support is downgraded automatically for ordinary launches (for example,
`auto` → `accept-edits` when classifier auto is unavailable), with a warning — WADE forwards the requested tier and
[`crossby`](https://github.com/ivanviragine/crossby) owns the downgrade ladder. Native Plan combinations are validated strictly and
rejected if unsupported.
**Headless launches are always read-only** — any of `deps` /
`review_plan` / `review_implementation` / `review_batch` dispatched in headless
delegation mode runs at `default` regardless of the configured tier, and no
`--yolo` is forwarded to the subprocess. The *interactive* variants honor the
tier: `wade review plan`, `wade review implementation`, `wade review batch`, and
`wade task deps` all accept `--yolo` / `--permission-mode` (matching `wade review
pr-comments`), and `ai.review_batch.yolo: true` / `ai.deps.yolo: true` apply when
those commands run interactively; the auto-launched review session honors its own
tier via `ai.review_pr_comments` (see above). `plan` is not a permission mode —
it's driven separately — and is rejected (warn + fall back to `default`) if
configured.

The **resolved permission mode is always displayed** at launch, on every path
(TTY, non-TTY, headless, all-flags-explicit), with a one-line descriptor — so a
`default` session states what `default` means, and what is shown always equals
what is applied.

### Headless review budget

Those headless commands **auto-scale** their subprocess budget from the prompt
size and reasoning effort (600s floor → 1500s ceiling), so a large diff or a
high-effort run no longer times out at a flat budget. If a run does time out,
wade keeps whatever partial output the reviewer produced (rather than discarding
it) and **retries once** with a longer budget (1.5x the first attempt, always
strictly more time than the run that just timed out), bounded to a ~62.5-minute
worst-case total that the pre-launch advisory announces. Set `ai.<command>.timeout`
(seconds) to override: an explicit value is used verbatim and **turns off both
the scaling and the retry** — the escape hatch when your terminal/orchestrator
enforces a hard tool-timeout (set it just under that limit).

A **headless** reviewer also gets told its own deadline: the plan/code/batch
review prompt states the current attempt's budget in seconds, so it can
prioritize the highest-severity findings and wrap up before being cut off
rather than getting killed mid-thought. Interactive and self-review (prompt)
reviews have no subprocess kill, so they get "no hard deadline" wording instead.
When a headless subprocess exits non-zero, wade retains trimmed stdout and
appends a clearly labeled stderr tail containing at most the final 20 non-empty
lines and 4,000 characters; if stdout is empty, that diagnostic is shown as the
failure feedback, and any truncation is labeled.

### AI runtime sandbox profile

The defaults below apply to **non-plan launches**. Native planning uses the
independent request policies described in [Planning & base branches](#planning--base-branches).

`ai.sandbox` is a single cross-tool boolean that decides whether WADE launches
the **AI runtime** inside its own filesystem sandbox. It governs the external
runtime boundary only — it never relaxes WADE's own guards (see below).

| Setting | Meaning |
|---------|---------|
| `sandbox: false` (**default**) | Launch the runtime unrestricted. Codex gets `--sandbox danger-full-access`, Cursor `--sandbox disabled`. Repository commands and delegated child tools keep **normal host access and credentials**. |
| `sandbox: true` | Launch the runtime confined. Codex gets `--sandbox workspace-write`, Cursor `--sandbox enabled`. |

Network access is **on in both non-plan profiles** and is not configurable for
ordinary launches. Not every phase needs it (dependency analysis does not),
but the phases that do — `git
fetch`/`push`, `gh`, package installs — cannot work without it, and the profile
is fixed at launch.

Set it globally or per command, and override it on any launch command
(`implement`, `implement-batch`, `plan`, `review pr-comments`, `task deps`, `smart-start`,
and the `i`/`r`/`p` aliases) with `--sandbox` / `--no-sandbox`:

```yaml
ai:
  sandbox: true                 # confine every session's runtime
  implement:
    sandbox: false              # ...except implementation (per-command wins)
```

Precedence: `--sandbox`/`--no-sandbox` > `ai.<command>.sandbox` > `ai.sandbox` >
**unrestricted**. WADE pins the resolved profile explicitly in *both* directions,
so an ambient setting in your own Codex `config.toml` can never silently move the
boundary of a WADE-managed session. The resolved profile is printed at launch
next to the tool, model, effort, and permission mode, with its meaning spelled
out — the security posture is never left implicit.

> **Why unrestricted is the default.** WADE runs a deliberately multi-model
> workflow: a session delegates to reviewers and dependency analysers that
> authenticate separately. Every `wade` command is a child of the AI runtime, so
> a sandboxed outer runtime hands its sandbox to every child — and a separately
> authenticated reviewer such as Claude Code loses its normal host login. That is
> what makes delegated multi-model sessions fail in practice (#478).

**`unrestricted` is not "unattended".** The profile is orthogonal to
`permission_mode`: it moves the OS boundary, not the approval tier. Codex's
`--sandbox danger-full-access` is a distinct flag from
`--dangerously-bypass-approvals-and-sandbox`, so disabling the sandbox never
implies `yolo` and never changes the resolved autonomy tier. Treat it as an
intentional project opt-in to host access, not as a synonym for skipping prompts.

Only tools with a sandbox toggle (Codex, Cursor) act on the setting. The
capability handling is deliberately **asymmetric**, because the default is
`false`:

- `sandbox: false` on a tool that never sandboxed anything (Claude, Copilot, …)
  is a silent no-op — those runtimes are already unrestricted.
- `sandbox: true` **explicitly requested** on a tool with no sandbox toggle is a
  visible error naming the tool. WADE cannot impose a boundary the runtime does
  not have, and will not pretend it did. No tool ever receives an invented flag.

#### Worktree git metadata under `sandbox: true`

A worktree's git metadata lives **outside** the worktree tree
(`<main>/.git/worktrees/<wt>` and `<main>/.git`), so under `workspace-write`
every git write — `git add`/commit, ref updates, stash, and `wade sync`/`done` —
would fail with `Unable to create …/index.lock` or `could not write index`. WADE
fixes this automatically: it passes the worktree's absolute path to the launcher,
which grants those out-of-root git-metadata dirs as sandbox writable roots (the
OS sandbox otherwise stays fully enabled — this widens nothing else). It is
transparent, needs no Codex config or manual approval, and is inert under
`sandbox: false`, where there is no boundary to widen.

#### WADE's own guards are unaffected

Disabling the runtime sandbox does not disable WADE's containment. The
worktree-containment guard actually gets **wider**: under `sandbox: true` it is
narrowed to the shell token (Codex's sandbox already covers tool-call writes, but
permits `/tmp` and `$TMPDIR`, so a shell redirect can still escape), while under
`sandbox: false` the **full** matcher is installed because nothing else covers
tool-call writes outside the worktree. The plan-artifact guard, lifecycle hooks,
the Stop hook, and the pre-push backstop are identical in both profiles.

#### Plan → implement handoff

The sandbox is a launch-time OS property: a process started confined cannot drop
its sandbox later. An accepted plan handoff runs after the planner child exits,
so WADE assesses the **actual parent runtime enclosing the handoff**, not the
former planner's requested profile. When that runtime is detected as sandboxed
and implementation resolves to **unrestricted**, WADE fails the handoff rather
than claiming a terminal child escaped the parent boundary. It names the runtime
it could not escape, and you open a new host terminal and run the displayed
`wade implement <issue> --no-sandbox` command. Without a published sandbox
signal, the ordinary nested-launch guard applies rather than guessing.

#### An inner `wade` process cannot escape a parent sandbox

Every `wade` command you run from inside an AI CLI session is a **child** of that
session. If the session was launched under an OS sandbox, every descendant
inherits the boundary — no profile WADE resolves, and no flag it passes to a
delegated runtime, can widen it. WADE will never claim otherwise, and there is no
setting that changes it. **The fix is always to relaunch the outer session with
the profile you want**, from a host terminal.

What WADE does instead is say so. When the resolved profile is unrestricted and
the enclosing runtime is *known* to be sandboxed, it names that runtime when it
can (or says **the enclosing AI runtime** when an identity marker is absent) and
prints the exact command to re-run — on implementation, plan,
PR-comment review, and every delegated operation (dependency analysis, plan/code
review, batch review), plus batch implementation before it dispatches terminal
brokers. That command preserves the resolved tool, model, effort,
and permission mode; only the sandbox profile changes to `--no-sandbox`.
Implementation and PR-comment retries reuse their frozen method bindings rather
than repeating `--skill` / `--review-skill` flags, while fresh delegated
operations carry their requested bindings. It warns and proceeds rather than
blocking: a delegated tool whose credentials happen to be reachable from inside
the sandbox may still
succeed, so refusing to try would break sessions that work today.

Detection is deliberately **best-effort and conservative**. WADE reads only
sandbox signals a runtime actually publishes and never infers confinement from
tool identity — a tool that *can* be sandboxed is not a tool that *is* sandboxed.
Today Codex publishes `CODEX_SANDBOX` for macOS Seatbelt, but its Linux Landlock
sandbox with network enabled publishes neither that marker nor a usable
`CODEX_SANDBOX_NETWORK_DISABLED` marker. WADE must report that Linux environment
as *unknown*, not unrestricted; if the distinction matters, relaunch from a host
terminal rather than expecting a diagnostic. When there is no signal WADE stays
silent rather than asserting a cause it cannot support. A confident wrong
diagnosis is worse than the opaque error it would replace. Managed Codex sessions
are still recognised when they expose session/thread markers instead of the
legacy `CODEX_CLI` marker; their displayed identity remains **Codex CLI**.

An explicit unrestricted signal (for example, Codex's
`CODEX_SANDBOX=danger-full-access`) is different from an absent signal: WADE
does **not** recommend relaunching the outer session for a permission- or
network-shaped launch failure in that case. The parent has already confirmed it
is unrestricted, so check executable permissions and network configuration
instead.

**Troubleshooting order** when a delegated tool fails to launch:

1. **Read what WADE printed.** An explicit sandbox-policy denial plus a named
   runtime and command means relaunch the outer session with that command. A
   generic permission or network denial also needs local checks below.
2. **Check whether the reviewer actually started.** A reviewer that ran and
   exited non-zero is a different problem; "relaunch the outer session" is wrong
   advice for it. WADE distinguishes the two. A generic launch denial can also
   be bad executable permissions or network configuration; a refusal WADE made
   itself (an unknown tool, a tool with no headless mode) never reached the
   sandbox and gets generic remediation instead.
3. **Check executable permissions, network, and the tool's own login/PATH** in the runtime where it must run
   (`gh auth status`, the delegate's own auth command). A separately
   authenticated delegate needs its *own* credentials, not WADE's.
4. **Re-run the phase readiness check** (`wade implementation-session check`,
   `wade review-pr-comments-session check`, …) for a narrow `reason=…` and its
   least-privilege remediation.
5. **Only then** widen the profile — and widen the *outer* session, not the
   child.

> **Narrow, optional exception: Codex project prefix rules.** Codex can be
> configured to run specific command prefixes outside its sandbox, which elevates
> a single command without relaunching the session. It is a Codex-side setting
> WADE neither writes nor reads, it applies per command rather than to the
> session, and it does not change anything above: the session is still sandboxed
> and every other child still inherits that. Treat it as an escape hatch for one
> stubborn command, **not** as WADE's path for running unrestricted — that path
> is relaunching the outer session.

#### Migrating from `ai.network_access`

`ai.network_access` was a Codex-only network pin, enabled by default for
`implement` and `review_pr_comments` and off everywhere else. It is **retired**.
An existing `.wade.yml` that still carries it keeps loading and passes
`wade check` with a **warning, not an error**; the key is ignored, and `wade
update` strips it — that is the only command that runs the config migration
pipeline. Replace it with `ai.sandbox` if you want the old confinement back:

```yaml
ai:
  sandbox: true                 # restores the pre-#478 Codex behaviour
```

### Session readiness and least-privilege remediation

Every `wade` command an agent runs is a child of the AI tool. It inherits that
tool runtime's filesystem sandbox, environment/PATH, network policy, `gh`
credentials, and the effects of any command or hook approval. WADE does not
become privileged because it launched the session: a `wade … done` call and its
own `git`/`gh` subprocesses are subject to the same containment.

Run the phase-specific readiness check as the first agent action. The
implementation and PR-comment lifecycle commands also repeat the same
non-mutating check immediately before `catchup`, `sync`, `done`, `fetch`, or
`resolve`, so a resumed session cannot turn a changed sandbox/PATH/credential
state into a late raw Git or GitHub failure. `done` checks the worktree it will
act on, so running it from the main checkout with a worktree/issue target or
`--plan` still works. It reports stable capability failures:
`WORKTREE_GIT_BLOCKED` (3), `GITHUB_CLI_BLOCKED` (4),
`GITHUB_AUTH_BLOCKED` (5), `GITHUB_API_BLOCKED` (6),
`KNOWLEDGE_STAGING_BLOCKED` (7), and `PLAN_DIR_BLOCKED` (8) — plus one
non-failure state, `PLAN_DIR_ONLY` (exit 0), described next. The result includes a
machine-readable `reason=…` and narrow remediation; do not disable a sandbox
globally or give the AI write access to the main checkout.

When `wade plan` cannot create a planning worktree — bootstrap failed, or you
are not in a git repo — it falls back to a standalone plan directory and runs
the agent from your checkout. That supported mode reports `PLAN_DIR_ONLY`
(exit 0) with a `plandir=…`: planning proceeds, but the agent may write only
that directory. `PLAN_DIR_BLOCKED` means even the plan directory is not
writable.

| Agent session | Run in the AI runtime | Required there | Intentionally not required there |
|---|---|---|---|
| Planning | `wade plan-session check` | a worktree with local `.wade/` handoff artifacts — or, in the `PLAN_DIR_ONLY` fallback, just a writable plan directory | GitHub, remote network, and task-persistence Git metadata — the parent `wade plan` finalizes tasks/PRs after exit |
| Dependency analysis | `wade deps-session check` | worktree output and optional local vote staging | GitHub and Git metadata writes — the parent `wade task deps` reads/updates task data after exit |
| Implementation | `wade implementation-session check` | worktree plus Git metadata writes, usable `gh` authentication, and a read-only GitHub API route | main-checkout writes |
| PR comments | `wade review-pr-comments-session check` | the same Git/GitHub capabilities as implementation, because it fetches threads, syncs, resolves, and completes a PR | main-checkout writes |

Direct knowledge proposal commands additionally need write access to the ignored
Git-common-directory queue. Native plan observations can instead stay in the
explicit handoff artifact for the parent to persist; local handoff readiness does
not promise that direct common-queue writes are available.

Implementation and PR-comment checks require GitHub regardless of the task
provider: Markdown and ClickUp can own tasks, but PR creation, review threads,
and `done` still use GitHub. The check first verifies that `gh` can start in the
actual runtime (`GITHUB_CLI_BLOCKED` distinguishes a missing/blocked executable
from bad credentials), then runs `gh auth status` and `gh api user --method
GET`; all three are non-mutating and have a short bounded timeout. It cannot
prove credentials for an arbitrary Git remote, test dependencies, or a
project-specific hook — those run later under the same sandbox and surface
their own exact error.

`permissions.allowed_commands` and a tool's approval UI only decide whether the
agent may launch a command such as `wade`; they do not grant filesystem or
network authority to the child process. Likewise, WADE's installed hooks are
guardrails, not privilege escalation: a failing hook may block a command, but
cannot make `gh`, Git metadata, or a network route available.

If `wade review implementation` cannot launch its reviewer because of missing
credentials, PATH, or sandbox execution access, it does not consume a
`done.max_review_passes` slot: repair the runtime and retry. A real headless
timeout and a reviewer that launched but failed both consume a review pass
without certifying completion. A reviewer that exits successfully with empty or
whitespace-only output is such a launched failure: empty output is not evidence
of a review, so the command reports the missing assessment (with any reviewer
stderr), records a non-satisfying `failed` receipt, exits 1 and repeats the exact
retry command. An explicit nonempty "no findings" assessment is still a
successful review. Prompt/preflight failures, skipped dispatches,
and explicit human cancellation do not consume a pass.

A reviewer that **never started** — including a sandbox-capability refusal before
the launch — is recorded as an *unattempted* review so the state is auditable,
and that record is deliberately inert: it satisfies no review
requirement, spends no review pass, cannot overwrite a real receipt already
written for the same commit, and leaves `wade implementation-session done` and
`wade review-pr-comments-session done` closed. An infrastructure failure must
never be able to certify a commit. A reviewer that started and exited non-zero
writes a consuming, non-satisfying `failed` receipt: inspect its output before
retrying. Failed receipts cannot serve as incremental review baselines. Below
the configured cap, implementation completion still requires a satisfying
review; PR-comment completion always requires one. At the existing implementation
cap, completion reports unreviewed/cap-reached status rather than approval.

Passes count distinct commit-and-frozen-binding tuples, not process launches.
Repeated crashes on the same commit consume one tuple; a later successful review
promotes that receipt without adding a pass. A timeout, no-diff, or successful
receipt takes precedence over a later failure for the same tuple. Older WADE
versions do not understand the new `failed` outcome; use the current runtime to
read its receipts rather than expecting an older runtime to count them.

For Codex, WADE launches unsandboxed by default so delegated tools keep their
own host credentials; set `ai.sandbox: true` (or pass `--sandbox`) to keep
`--sandbox workspace-write`, where WADE/crossby add only the linked worktree's
private/common Git metadata directories. Every session has network access in
both profiles for its GitHub lifecycle.
Standalone `wade review plan`, `wade review implementation`, and `wade review
batch` runs also accept `--sandbox` / `--no-sandbox`; an inherited-sandbox
diagnosis gives the host-terminal relaunch command with `--no-sandbox` so it
overrides an `ai.sandbox: true` configuration. The command also preserves the
selected review mode, tool, model, effort, permission mode, staged scope, and
review-skill overrides so the retry reviews the same operation.
For Claude Code and Cursor, allowlist the worktree/Git metadata paths and only
the GitHub domains needed by the session rather than choosing unrestricted shell
access. Copilot and VS Code need network plus usable `gh` credentials in their
host runtime. OpenCode shells execute with host authority, so treat the selected
host runtime and its credentials as the boundary rather than assuming a
worktree is one. These are external tool/runtime settings; WADE reports missing
capabilities but never rewrites them.

Project knowledge separates committed-evidence assessment, publication, usefulness,
and historical relationships. Use `wade knowledge check <id> current|contradicted
--file <path> --note <reason>` for a fresh check and `knowledge feedback` for
usefulness. Legacy up/down/stale votes remain historical; they never hide claims
or establish correctness. `knowledge reconcile --against HEAD --check --json`
reports committed proof without fetching or changing HEAD/index/working files.
Source evidence marked skip-worktree/assume-unchanged is refused; normal generated
pointer flags do not block other source paths. Proof ignores replacement objects.
`--apply` appends exact-duplicate aliases while retaining every member's history.

Main, detached planning/dependency sessions and bounded reviewers persist ignored
owner-bound proposals in the Git common directory. Bootstrap never drains old
main feedback into the next PR. `knowledge proposals list|retain|discard|accept`
provides explicit disposition; acceptance requires the original parent's verified
task/content association and grants no correctness. Check fresh committed proof
after acceptance. Bounded planning/dependency observations retain their original
parent or service-verified destination through cleanup; ambiguous multi-task
observations remain pending. Switched branches cannot inherit knowledge author authority; repeated same-task
acceptance reuses shared event bytes. Local prose edits withhold committed labels.
Invalid evidence history is visible in `knowledge get`
and withholds correctness labels. Legacy logs remain intact and require explicit owner-verified
`knowledge proposals recover --worktree <original-parent>`. See
[the knowledge reference](docs/dev/knowledge.md) for proof types and recovery.

## Supported AI Tools

Adapters, model/effort resolution, and per-tool config (allowlists, hooks) are powered by [`crossby`](https://github.com/ivanviragine/crossby), WADE's AI-tool-integration dependency.

| Tool | Binary |
|------|--------|
| [Claude Code](https://claude.com/product/claude-code) | `claude` |
| [Cursor](https://www.cursor.com/) | `cursor` |
| [GitHub Copilot](https://github.com/features/copilot/cli) | `copilot` |
| [OpenAI Codex](https://developers.openai.com/codex/cli/) | `codex` |
| [OpenCode](https://opencode.ai/) | `opencode` |
| [VS Code](https://github.com/features/copilot/ai-code-editor) | `code` |
| [Antigravity IDE](https://antigravity.google/) | `antigravity` |
| [Antigravity CLI](https://antigravity.google/) | `agy` |

> In `.wade.yml`, refer to a tool by its **config ID**, which matches the binary
> above except for VS Code (`vscode`) and Antigravity CLI (`antigravity-cli`).
> Antigravity IDE (config ID `antigravity`) is launch-only — WADE opens your
> workspace in the desktop app; its workflow files are provisioned via the
> Antigravity CLI's shared `.agents/` layout.

## Agent Skills

WADE keeps the workflow fixed while letting you replace the methodology. Every
interactive session gets an authoritative `.wade/session/WORKFLOW.md`; it still
contains planning/implementation/review steps, documentation, synchronization,
and `done` regardless of which skills are selected. Agent Skills control only
*how* the WORK or REVIEW step reasons.

Default replaceable methods:

| Operation | WORK method | REVIEW method |
|---|---|---|
| planning session | `builtin:planning` | `builtin:plan-review` |
| implementation session | `builtin:implementation` | `builtin:code-review` |
| PR-comment session | `builtin:review-comments` | `builtin:feedback-fix-review` |
| batch review | `builtin:batch-review` | — |
| dependency analysis | `builtin:dependency-analysis` | — |

Custom skills stay generic: they do not need to mention WADE, worktrees,
session commands, markers, review budgets, or `done`. WADE wraps their
methodology in its fixed lifecycle or bounded operation contract.

Project skills are discovered from every skill root supported by the selected
tool integration, including real or symlinked `.claude/skills`,
`.agents/skills`, `.cursor/skills`, and `.github/skills` layouts. WADE merges
the new worktree inventory with the main checkout so local-only/ignored skills
are available too, while worktree content wins for matching tracked paths.
Generated `task` and `knowledge` command-support projections are excluded from
methodology discovery, so they cannot shadow a project skill with the same name.
WADE copies the complete skill directory—`SKILL.md`, references, scripts, and
assets—into the session snapshot; source directories are never overwritten or
pruned.

Use explicit refs:

- `builtin:<name>` for a bundled default;
- `project:<name>` for a unique discovered project skill;
- `path:<repo-relative-directory>` to select one exact source when names collide.

Bindings are ordered and frozen in `.wade/session/manifest.json`. Resuming uses
the same physical copies even if config or the main checkout changes. To change
an active session, use `--refresh-skills` with the session command or run
`wade session refresh-skills`; an override without explicit refresh fails.
In a worktree-less planning fallback, session diagnostics and refresh target the
temporary bundle advertised to the agent while continuing to load config and
project skills from the checkout that launched it.

Example configuration (all sections are optional; omitted values keep the
defaults):

```yaml
skills:
  project:
    discover: true
    include: ["*"]
    exclude: []

sessions:
  implementation:
    skills:
      work: [project:django-implementation]
      review: [project:security-review]
  plan:
    skills:
      work: [builtin:planning]
  review_pr_comments:
    skills:
      work: [builtin:review-comments]
      # Omit review to use builtin:feedback-fix-review.

delegations:
  code_review:
    skills:
      work: [project:security-review]
  plan_review:
    skills:
      work: [builtin:plan-review]
  batch_review:
    skills:
      work: [builtin:batch-review]
  dependency_analysis:
    skills:
      work: [builtin:dependency-analysis]
```

For a new session the order is CLI override, session binding, mapped delegation
binding for REVIEW, then built-in default. A session REVIEW binding wins over a
different `delegations.code_review`/`plan_review` value for that session.
Standalone review, batch, and dependency operations use their delegation
binding. `wade skills resolve` prints every candidate and the winner.

The shared `delegations.code_review.skills.work` setting is an explicit fallback
for both implementation and PR-comment session REVIEW slots. It intentionally
replaces the stage-specific built-in default—including
`builtin:feedback-fix-review` for PR-comment sessions—unless that session sets
its own `skills.review`; existing frozen sessions keep their recorded binding
until explicitly refreshed.

Tool-native roots receive only fixed command-support skills. Session lifecycle
comes from the rendered workflow, while default or custom methodology is frozen
inside the session bundle. Deprecated phase skills are not installed.
Resume revalidates workflow identity and the complete physical bundle against
its manifest digest; completion does the same before trusting a review receipt.
Stale metadata and edited, missing, extra, or unsafe snapshot content require an
explicit `--refresh-skills` or `wade session refresh-skills` replacement.

This redesign is a clean cutover. It does not import pre-redesign phase-skill or
review-marker state. Resuming an older in-flight worktree creates the current
session bundle, and its current commit must complete the new review and
documentation-decision gates before `done` can accept it.

### Session communication

While a session works, it reports **by exception**: terse on success — just
the actionable handles (task/PR numbers, URLs, the next command), no running
recap. When something needs your attention or a decision only you can make,
the agent reports it in 1–2 sentences with brief context, its complexity
(easy/medium/complex/very_complex), and a recommendation. It then asks
through the native question component, with the recommended option first and
labelled "(recommended)".

Each session **ends** with a compact emoji step-status summary instead of a
prose recap: one glyph per step (✅ done · ⚠️ needs your attention · ❌
failed/blocked · ⏭️ skipped/disabled) on a single line, the handles, an
explicit attention line
(either "Nothing needs your attention" or the items that do), and a bold
**Next:** action. Every finite-choice decision — including whether to exit —
is a native dialog with the recommended option first.

## Extension Hooks

Configure automated setup when worktrees are created via `wade implement` or `wade implement-batch`. Add a `hooks` section to `.wade.yml`:

```yaml
hooks:
  post_worktree_create: scripts/setup-worktree.sh
  copy_to_worktree:
    - .env
    - .env.example
```

**`post_worktree_create`** — A setup script to run after each worktree is created. Use it to install dependencies, run builds, or prepare the environment so worktrees are ready to use immediately.

**`copy_to_worktree`** — Files to copy from the project root into the worktree before running the hook. Useful for secrets and config files (e.g., `.env`) that are gitignored and wouldn't otherwise be present in a new worktree.

See [`templates/setup-worktree.sh.example`](templates/setup-worktree.sh.example) for a starter script.

### Repo-quality gates

Three **opt-in, off-by-default** quality gates enforce code hygiene at the
commit boundary (and in-turn). Nothing is installed unless you configure it:

```yaml
hooks:
  pre_commit:
    lint: ./scripts/check.sh --lint   # runs on `git commit`; non-zero blocks it
    test: ./scripts/test.sh           # runs on `git commit`; non-zero blocks it
  commit_msg:
    conventional: true                # rejects a non-Conventional-Commit subject
  post_tool_use:
    enabled: true                     # feed lint findings back to the agent in-turn
    lint_cmd: ruff check              # FILE-SCOPED linter (the edited path is appended)
    timeout: 10                       # seconds; the linter is skipped on overrun
```

- **`pre_commit`** installs a per-worktree `pre-commit` git hook that runs the
  configured `lint`, then `test`, command(s). A non-zero exit blocks the commit
  and surfaces to the agent as a normal error. Set only the step(s) you want.
  Running the full `test` suite on every commit can be slow — prefer a fast
  subset there, or use `lint` alone.
- **`commit_msg`** installs a per-worktree `commit-msg` git hook that validates
  the subject line against [Conventional Commits](https://www.conventionalcommits.org/).
  A non-conforming subject blocks the commit. A `BREAKING CHANGE:` footer only
  marks an already-typed commit as breaking (an alternative to the subject `!`);
  it does not exempt the subject from the type requirement.
- **`post_tool_use`** feeds lint findings back to the agent *in-turn* — the
  cheapest moment to fix them, while the edit is still in working memory. It is
  **file-scoped**: `lint_cmd` runs on just the edited path (appended as a
  positional arg), on tools whose hooks can inject context back to the agent
  (Claude, Cursor, Codex, Copilot; Antigravity CLI is skipped). If `lint_cmd`
  is unset, wade falls back to `pre_commit.lint` run **whole-repo** on every
  edit — which is slower and may error on an unexpected positional arg, so
  prefer configuring a file-scoped `lint_cmd`. This layer never blocks.

Like the pre-push backstop, the git hooks are worktree-scoped (never touch your
main checkout or sibling worktrees) and chain to any pre-existing hook of the
same name. **Honesty:** `git commit --no-verify` bypasses the pre-commit and
commit-msg hooks in one flag — these are **quality** gates that make messy or
untested commits hard to land, not airtight enforcement boundaries.

## Shell Integration

To make `wade cd <N>` actually change your directory (instead of just printing the path), add this to your shell profile:

```bash
eval "$(wade shell-init)"
```

Tab completion:

```bash
wade --install-completion bash   # or zsh / fish
```

## Upgrading

```bash
wade update
```

Detects your install method (`uv tool`, `pipx`, Homebrew) and upgrades automatically, then refreshes all managed project files.

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md).

Maintainers ship batches through the [deliberate release process](docs/dev/releases.md).
Merged PRs run CI; they do not automatically bump versions or create releases.

## License

MIT
