Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,26 @@ three are printed by every run, and `tests/run.sh` ends by naming the surface it

## Profiles — right-size the loop

`workflow-triage` picks from **blast radius**, not diff size, and can escalate mid-run.

| | `light` | `standard` | `deep` |
| --- | --- | --- | --- |
| Groom | 1 pass | loop-until-dry, 2 lenses | loop-until-dry, all lenses |
| Review | 1 reviewer | 3 lenses + wildcard | all lenses + wildcard + judge |
| Human gates | G7 | G2 + G7 | G2 + G7 |
`workflow-triage` picks from **blast radius**, not diff size, and can escalate mid-run. The operator can
name one instead — `/adw-run superlight`, `/adw-run deep SM-12` — and then it is a decision, not a hint.

| | `superlight` | `light` | `standard` | `deep` |
| --- | --- | --- | --- | --- |
| Scout | skip | skip | agent | agent, multi-angle |
| Groom | **none** | 1 lens, inline | 2 lenses | all lenses |
| Review | 1 reviewer | 1 reviewer | 3 lenses + wildcard + judge | all lenses + wildcard + judge |
| Human gates | G7 | G7 | G2 + G7 | G2 + G7 |

The profile **is** the phase list — `adw-run` runs one column, not the whole loop with cheaper settings.
What never scales down: the worktree, the draft PR, `gate.sh green`, `gate.sh evidence`, and one
reviewer. A run with no independent look at the diff is a solo run awarding itself a green tick.

`superlight` still writes `PLAN.md` and `VALIDATION.md`, because the gates read them — around a dozen
lines, three of the four required headers one word long. No gate is skipped or special-cased for it.

**And there is a floor.** A typo, a version bump, a comment fix: do it in the session, run
`gate.sh green`, show the diff. Blast radius says `superlight` for a one-liner; that is not a reason to
spend a worktree and a PR on it. Below the floor the cost is the operator's attention, not the tokens.

## Agent identities = what they may not touch

Expand Down
4 changes: 2 additions & 2 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
set -uo pipefail

SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VERSION="2.1.0"
VERSION="2.2.0"
SCRIPT_FILES="lib.sh gate.sh guard.sh engines.sh intake.sh review.sh setup-worktree.sh worktree-alloc.sh"

die() { echo "[ai-tooling] ERROR: $*" >&2; exit 1; }
Expand Down Expand Up @@ -53,7 +53,7 @@ cmd_list() {
echo "Gates (scripts/ai/):"
echo " gate.sh format|static|test|red|green|workspace|committed|groom|plan|evidence|ready|all"
echo " guard.sh builder|test-author|reviewer|planner (phase file-scope, mechanical)"
echo " review.sh <round> [validation] --profile light|standard|deep (lens fan-out + judge)"
echo " review.sh <round> [validation] --profile superlight|light|standard|deep (fan-out + judge)"
echo " intake.sh fetch <ref>|writeback <ref> --status|--comment|contract"
echo " engines.sh candidates|probe --write|list|pick-review|diversity|run"
echo " setup-worktree.sh · worktree-alloc.sh"
Expand Down
22 changes: 17 additions & 5 deletions scripts/gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -179,19 +179,31 @@ gate_groom() {
adw_warn "G1 FAILED: open blocker in $oq — STOP, resolve with the operator."
fail=1
fi
if [[ ! -f "$log" ]]; then
adw_warn "G1 FAILED: $log missing — run groom-harden at least once."
return 1
fi
# The lens set IS the coverage. Every lens gets one pass; a lens is repeated only if it found something
# major, and then only that lens. Counting "quiet passes" instead rewards re-running the same questions:
# on a real run, passes 5 and 6 produced three minors and no majors, for a third of the token budget.
local lenses lens closed missing="" profile all_lenses
all_lenses="$(adw_cfg LENSES contracts,failure-modes,adversary,meta | tr ',' ' ')"
# The profile decides how much coverage this task earns; PLAN.md records it (workflow-triage).
# 'superlight' has to be IN the alternation, or "**Profile:** superlight" matches the 'light' inside it
# and the run is silently groomed as light. Order does not matter — ERE is leftmost-longest, so both
# alternatives start at the same offset and the longer one wins either way.
profile="$(grep -m1 -iE '^\s*[-*]?\s*\*\*Profile:\*\*' ".tasks/$id/PLAN.md" 2>/dev/null \
| grep -oiE 'light|standard|deep' | head -1 | tr '[:upper:]' '[:lower:]')"
| grep -oiE 'superlight|light|standard|deep' | head -1 | tr '[:upper:]' '[:lower:]')"
profile="${profile:-$(adw_cfg DEFAULT_PROFILE standard)}"

# superlight runs no groom pass, so there is no ledger to demand — but the blocker check above still
# applies, because a blocker found while planning stops the run at every profile.
if [[ "$profile" == "superlight" ]]; then
adw_log "G1: profile 'superlight' → no groom pass required"
(( fail )) || adw_log "G1 OK: no open blocker"
return $fail
fi

if [[ ! -f "$log" ]]; then
adw_warn "G1 FAILED: $log missing — run groom-harden at least once."
return 1
fi
case "$profile" in
light) lenses="$(printf '%s' "$all_lenses" | awk '{print $1}')" ;;
standard) lenses="$(printf '%s\n' $all_lenses | grep -xE 'contracts|adversary' | tr '\n' ' ')"
Expand Down
9 changes: 6 additions & 3 deletions scripts/review.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
# scripts/ai/review.sh — review fan-out: one reviewer per rubric lens + a wildcard, run in parallel,
# then a judge that dedupes and adversarially verifies. Core file.
#
# review.sh <round> [validation-path] [--profile light|standard|deep] [--lenses a,b,c]
# review.sh <round> [validation-path] [--profile superlight|light|standard|deep] [--lenses a,b,c]
#
# superlight → same as light: one reviewer is the floor, not something a profile can drop
# light → 1 reviewer, no wildcard, no judge
# standard → 3 lenses + wildcard, no judge (raw findings)
# deep → all configured lenses + wildcard + judge (deduped, verified, ranked)
Expand Down Expand Up @@ -34,12 +35,14 @@ profile="${profile:-$(adw_cfg DEFAULT_PROFILE standard)}"
lenses="${lenses:-$(adw_cfg REVIEW_LENSES correctness,security,performance,architecture,tests)}"

case "$profile" in
light) IFS=',' read -ra lens_list <<< "correctness"; wildcard=0; judge=0 ;;
# One reviewer is the floor. A run with none is a solo run awarding itself a green tick, and the
# cheapest independent look at a diff is one agent — so superlight buys a smaller loop, not no review.
superlight|light) IFS=',' read -ra lens_list <<< "correctness"; wildcard=0; judge=0 ;;
# wildcard + judge stay on at standard: on the evidence, the majors come from the adversarial angle,
# and an unjudged fan-out hands the operator six overlapping lists instead of one verified one.
standard) IFS=',' read -ra all <<< "$lenses"; lens_list=("${all[@]:0:3}"); wildcard=1; judge=1 ;;
deep) IFS=',' read -ra lens_list <<< "$lenses"; wildcard=1; judge=1 ;;
*) adw_die "unknown profile: $profile (light|standard|deep)" ;;
*) adw_die "unknown profile: $profile (superlight|light|standard|deep)" ;;
esac

rubric_file=""
Expand Down
101 changes: 88 additions & 13 deletions skills/core/adw-run/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
name: adw-run
description: Drive the full AI Developer Workflow for one task end to end — intake, triage, scout, plan, groom passes, worktree, RED/GREEN implementation, validation, review fan-out — pausing only at the human gates (plan approval and final review). Use when the operator says "run the workflow", "прогони воркфлоу", "take this task through the loop", or hands over a ticket id and expects the whole loop.
description: Drive the AI Developer Workflow for one task end to end — intake, triage, scout, plan, groom passes, worktree, RED/GREEN implementation, validation, review fan-out — at the depth the task earns, pausing only at the human gates it earns. Use when the operator says "run the workflow", "прогони воркфлоу", "take this task through the loop", or hands over a ticket id and expects the whole loop.
arguments: [profile, ref]
argument-hint: "[superlight|light|standard|deep] [ticket-ref]"
---

# adw-run — the orchestrator of one task through the loop
Expand All @@ -9,41 +11,109 @@ You drive the whole loop for a single task. You do not write production code you
enforce gates mechanically, and spawn specialist agents whose identities are defined by what they may not
touch. Everything project-specific comes from `.tasks/_STACK.md`; never hardcode a command.

## Human gates (only two — protect them)
## Arguments

`$profile` — `superlight` · `light` · `standard` · `deep`, when the operator names one. Empty means
they did not, and `workflow-triage` picks. A named profile is a decision, not a hint: record it and
run it. Escalate mid-run only for the reasons in `workflow-triage`, and say so when you do.

`$ref` — the ticket id or the request itself, handed straight to `task-intake`.

## Human gates (at most two — protect the ones the profile keeps)

- **G2 · plan approval.** After the plan and validation criteria exist and grooming converged, present a
compact summary and **stop for the operator**. This is the highest-leverage review point: a bad plan
line becomes hundreds of bad code lines.
- **G7 · merge.** After the review fan-out, present the ranked findings and **stop**. Merging is theirs.
line becomes hundreds of bad code lines. **`standard` and `deep` only** — below those the plan is a
dozen lines the operator would rubber-stamp, and a gate that gets rubber-stamped trains them to skim
the one that matters.
- **G7 · merge.** After the review, present the ranked findings and **stop**. Merging is theirs, at
every profile.

Everywhere else you proceed autonomously — **except** on a `blocker`, which always stops the run.

## What each profile actually runs

The profile is not a label on a run that happens anyway; it *is* the phase list. Read this table before
phase 1 and run only the column you are in.

| Phase | `superlight` | `light` | `standard` / `deep` |
| --- | --- | --- | --- |
| 0 · triage | skip — profile is explicit | skip if explicit | `workflow-triage` |
| 1 · scout | skip, read the files yourself | skip, read the files yourself | `context-scout` agent |
| 2 · plan | `PLAN.md` ~12 lines + `VALIDATION.md`, 1 check | short but complete | full |
| 2 · `OPEN_QUESTIONS.md` / `CHECKLIST.md` | only if a blocker appears | yes | yes |
| 3 · groom | **skip** | one inline `contracts` pass, no agent | one agent per lens |
| 3 · `GROOM_LOG.md` | not created | created | created |
| 4 · **G2** | skip | skip | **stop** |
| 5 · workspace | worktree + branch + draft PR | same | same |
| 6 · RED | one test where `TEST_CMD` exists, else observation | optional | required |
| 7 · GREEN + `gate.sh green` | required | required | required |
| 8 · validate + `gate.sh evidence` | required | required | required |
| 9 · review | `review.sh --profile superlight` (1 reviewer) | 1 reviewer | fan-out + wildcard (+ judge) |
| 10 · **G7** | **stop** | **stop** | **stop** |

Two rows never move, whatever the profile. **The workspace** — worktree, branch, draft PR — is
script-driven and takes seconds, and it is what makes a run revertible in one command and visible while
it happens; cutting it saves nothing measurable and loses everything if the session dies. **One
reviewer** — without it a small run is a solo run that awards itself a green tick, and one agent is the
cheapest independent look at a diff there is.

`superlight` still writes `PLAN.md` and `VALIDATION.md` because the gates read them: `gate.sh plan`
wants the four headers (`Touches`, `Depends-on`, `Out of scope`, `Decisions locked`) and one numbered
check, `gate.sh evidence` wants one evidence file. On a task this size three of those headers are one
word long. Nothing in `gate.sh` is skipped or special-cased for it.

## When not to run this at all

The harness has a floor. A typo, a version bump, a comment fix, a one-line config change with an
observable result: do it in the session, run `bash scripts/ai/gate.sh green`, and let the operator read
the diff. `workflow-triage` will honestly answer `superlight` for a one-liner, because it reasons about
blast radius and a one-liner has none — that is not a recommendation to spend a worktree and a PR on it.

Below the floor the cost is not the tokens, it is the operator's attention: a PR that says nothing
teaches them to stop reading PRs.

## Phases

### 0 · Intake + triage
1. `task-intake` — turn the request (ticket / prompt / bug report) into `.tasks/<id>/` with the goal and a
back-reference to its source.
2. `workflow-triage` — pick the profile (`light` / `standard` / `deep`) and record it in `PLAN.md`.
Never ask the operator for the profile; state your choice and its reason in one line.
2. `workflow-triage` — pick the profile and record it in `PLAN.md`. **Skip this when `$profile` was
given**; write the operator's choice into `PLAN.md` instead, marked as theirs. Never ask them to
choose when they did not: state your reasoning in one line and let them override.

### 1 · Scout (agent: `context-scout`)
### 1 · Scout (agent: `context-scout`) — `standard` / `deep`
Read-only recon → provenance index. It burns its own context on grep/read/trace and returns conclusions,
so your context stays clean and the plan is not anchored on the first file anyone opened.
Below `standard` you read the files yourself: the agent's whole value is protecting a context you are
not going to fill on a task this size.

### 2 · Plan (`task-plan` + `slice-verify`)
`PLAN.md` (incl. `Touches` / `Depends-on` / scope OUT / decisions locked) and `VALIDATION.md` (every
acceptance line → ≥1 runnable check, with where evidence lands). Uncertainties → `OPEN_QUESTIONS.md`.

### 3 · Groom passes (`groom-harden`, agent: `groom-hardener`)
On `superlight` this is a dozen lines: the four headers `gate.sh plan` requires, three of them one word
long, and one numbered check. Short is the point — but the headers are not optional, because a task with
no stated scope OUT is a task that grows silently.

### 3 · Groom passes (`groom-harden`, agent: `groom-hardener`) — `light` and up
**One fresh-context pass per lens** from `LENSES` in `_STACK.md` — the lens set is the coverage. Each pass
reads `GROOM_LOG.md` first and appends its row. A lens that reports blockers or majors re-runs **after its
findings are folded in**; minors are recorded and never trigger another pass.
Check mechanically: `bash scripts/ai/gate.sh groom <id>`.

### 4 · G2 — stop for the operator
`light` runs the first lens inline, in this session, without spawning `groom-hardener` — one lens does
not need a fresh context to stay honest, and the agent's cost is the context it rebuilds.
`superlight` skips grooming entirely; `gate.sh groom` requires no lens there and passes on the blocker
check alone.

### 4 · G2 — stop for the operator — `standard` / `deep`
Present: goal, scope IN/OUT, the 3–5 decisions locked, open `clarify` assumptions, the check list, the
profile, and the estimated tier count. Wait. Fold their corrections back into the artifacts.

Skipped below `standard`. Not because the plan matters less, but because a twelve-line plan is one the
operator approves without reading, and a gate that gets rubber-stamped devalues the one that must not.

### 5 · Workspace — before any code
Worktree, branch, empty start commit, push, **draft PR** — in that order, before a single source file is
touched. Then `bash scripts/ai/gate.sh workspace <id>` must exit 0.
Expand Down Expand Up @@ -75,6 +145,10 @@ Tests first, written by an agent that may not touch source (`guard.sh test-autho
not an import error. No test command configured → say so plainly, skip RED, and rely on observation
checks; do not pretend TDD happened.

On `superlight` and `light` the test-author agent is overhead: write the one failing test yourself, then
run the same gate. The guard exists so an implementer cannot bend a test to fit; when you are about to
write both, the honest substitute is writing the test first and not touching it afterwards.

### 7 · GREEN (agent: `builder`)
Builder receives the plan, the tests, and the signatures — not the grooming history. It may not touch test
paths (`guard.sh builder`). Gate: `bash scripts/ai/gate.sh green`. **Cap 3 fix attempts per failing gate**;
Expand All @@ -92,12 +166,13 @@ view of the run — an uncommitted step is invisible to the operator and lost if
Run every `VALIDATION.md` check, save evidence to `.tasks/<id>/evidence/`.
Gate: `bash scripts/ai/gate.sh evidence <id>`. Do not open a PR with red or unrun checks.

### 9 · Review fan-out
### 9 · Review
Fill in the PR summary + how-to-verify (including the "Not verified" section), `gh pr ready`, and confirm
with `bash scripts/ai/gate.sh ready <id>` — the run marks its own PR ready, not the operator. Then
`bash scripts/ai/review.sh <round> .tasks/<id>/VALIDATION.md --profile <profile>` —
lens reviewers in parallel, plus a wildcard hunting what those lenses cannot see, plus (on `deep`) a judge
that dedupes and adversarially verifies. In parallel, run `harness-improver` on the diff + `FRICTION.md`.
`bash scripts/ai/review.sh <round> .tasks/<id>/VALIDATION.md --profile <profile>` — pass the profile you
actually ran, so the fan-out matches it: one reviewer below `standard`, lens reviewers in parallel above,
plus a wildcard hunting what those lenses cannot see and a judge that dedupes and adversarially verifies.
In parallel, run `harness-improver` on the diff + `FRICTION.md`.

### 10 · G7 — stop for the operator
Present the ranked, verified findings, the diversity label, **and what was never verified** — NOT RUN
Expand Down
3 changes: 2 additions & 1 deletion skills/core/groom-harden/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ The ledger is what keeps passes moving forward instead of re-deriving the same t
## Lenses (one per pass, rotating)

From `LENSES` in `.tasks/_STACK.md` — the four core ones, plus any domain lenses the project added. The
profile decides how many run: `light` → `contracts`; `standard` → `contracts` + `adversary`; `deep` → all.
profile decides how many run: `superlight` → none at all; `light` → `contracts`, run inline in the
session rather than by an agent; `standard` → `contracts` + `adversary`; `deep` → all.

1. **contracts** — every API/event/persisted contract the slice introduces or touches: source of truth,
payload shape, writers/readers, validation, versioning, forbidden writes. Ownership ambiguity between
Expand Down
5 changes: 3 additions & 2 deletions skills/core/slice-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,9 @@ operator reads one short list instead of six overlapping ones.
- Diversity is labelled honestly: `CROSS-ENGINE` (≥2 vendors) > `CROSS-MODEL` (one vendor, ≥2 models,
e.g. `claude:opus` + `claude:sonnet`) > `DEGRADED` (one entry — shares the implementer's blind spots,
never an independent verdict).
- Profiles: `light` = correctness only; `standard` = first three lenses + **wildcard + judge**; `deep` =
all lenses + wildcard + judge. Set by `workflow-triage`. The wildcard survives every profile above
- Profiles: `superlight` and `light` = correctness only; `standard` = first three lenses + **wildcard +
judge**; `deep` = all lenses + wildcard + judge. Set by `workflow-triage` or named by the operator.
One reviewer is the floor, not a step a profile can remove. The wildcard survives every profile above
`light` because the findings that matter most come from outside the technical lenses — "what does a user
who already paid see?" is not a correctness question. The judge survives because an unverified fan-out
spends the operator's attention on duplicates and plausible-but-wrong findings.
Expand Down
3 changes: 2 additions & 1 deletion skills/core/task-plan/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ File: `.tasks/<id>/PLAN.md`
### (a) Overview — 10-second what/why/scale
- **Type:** slice / feature / tech-task / bug / refactor / audit.
- **Intake:** where this came from (ticket URL, operator prompt, bug report) — set by `task-intake`.
- **Profile:** `light` / `standard` / `deep` + the one-line reason — set by `workflow-triage`.
- **Profile:** `superlight` / `light` / `standard` / `deep` + the one-line reason — set by `workflow-triage`,
or by the operator, in which case say so (`operator's choice`).
- **Crux** (2–3 sentences): what we're doing and why.
- **Affected:** which layers/modules — be concrete for this codebase (API, domain, data/persistence,
jobs, client/UI, infra, tooling).
Expand Down
Loading
Loading