# Peaks-Loop RD transition verification gates

> Extracted from `skills/bee/peaks-rd/SKILL.md` on 2026-06-09 (slice 019 — slim skill files to references) to keep SKILL.md under the 800-line cap from `.peaks/standards/common/coding-style.md`. The content below is the verbatim "Transition verification gates" section that was previously inline; nothing was paraphrased, just relocated.

## Transition verification gates (MANDATORY — full per-gate contract)

You cannot declare a phase complete from memory. Each gate below is a `ls` or `grep` command you **MUST run** and whose output you **MUST see** before proceeding. If any file shows "No such file" or any command returns empty, the phase is incomplete.

> **CLI enforcement (NEW)**: the gates below are now ALSO enforced by `peaks request transition`. The CLI checks the same files before allowing the transition and fails with `code: PREREQUISITES_MISSING` if any are absent. The exact required files depend on the request type chosen at `peaks request init --type <feature|bugfix|refactor|docs|config|chore>` (default `feature`):
>
> | Type | rd:implemented requires | rd:qa-handoff also requires |
> |---|---|---|
> | feature / refactor | `rd/tech-doc.md` | `rd/code-review-<rid>.md` + `audit/security-<rid>.md` + `audit/perf-<rid>.md` (filled Results table, or `N/A — no perf surface` in Notes) + **`qa/test-cases/<rid>.md`** (added in slice 004; pre-drafted by the 4th sub-agent in the parallel fan-out) |
> | bugfix | `rd/bug-analysis-<rid>.md` (lighter than tech-doc; root cause + fix + regression test plan) | `rd/code-review-<rid>.md` + `audit/security-<rid>.md` + **`qa/test-cases/<rid>.md`**; `audit/perf-<rid>.md` only when the bug is performance-shaped (matches the L449-452 "When this applies" criteria) |
> | config | (none) | `rd/security-review-<rid>.md` only — `SECURITY_REVIEW.relativePath` carries the `<rid>` since slice 9.37 |
>
> The `<rid>`-bearing names are the ones `peaks request transition` resolves first; the pre-rid names (`rd/bug-analysis.md`, `rd/code-review.md`, `rd/security-review.md`, `audit/security.md`, `audit/perf.md`) stay **readable** as back-compat tiers for sessions written before the rid was added to the filename — read, never written. Exception: `rd/perf-baseline.md` is the RD-side Gate B9 baseline (a different artifact from `audit/perf-<rid>.md`) and is still both written by `peaks perf baseline --apply` and read as `AUDIT_PERF`'s oldest legacy tier.
> | docs / chore | (none) | (none) |
>
> The escape hatch `--allow-incomplete --reason "<text>"` still exists for one-off exceptions; the bypass is recorded in the artifact transition note.

**Peaks-Loop Gate A — After project-scan read (before any implementation):**
```bash
ls .peaks/project-scan/project-scan.md
# Expected output: .peaks/project-scan/project-scan.md
# "No such file" → STOP, create the project-scan first. Do not write code.
```

**Peaks-Loop Gate A2 — Before tech-doc write: project structure verified (PATH CORRECTNESS — CRITICAL):**
```bash
# Verify EVERY file path and directory in the tech-doc exists in the actual project.
# Do not assume paths. Do not guess directory structures. Open the files and verify.
# Example verification (adapt paths to the actual tech-doc):
ls <every-single-directory-path-in-tech-doc> 2>&1 | grep -c "No such file"
# Expected: 0 (zero "No such file" errors)
# Any "No such file" → WRONG PATH. Fix the tech-doc BEFORE writing another word.
# This gate exists because a tech-doc with wrong paths wastes QA time,
# breaks the implementation, and forces the user to correct the engineer.
```

**Peaks-Loop Gate A3 — Before implementation: the project's standards files are in place:**

The standards bundle has three parts, and only the first has a fixed name:

- `AGENTS.md` — the **body** (the cross-tool constitution). Always written, in every layout.
- `CLAUDE.md` — the **pointer** (`@AGENTS.md`). Written ONLY when the IDE profile needs it.
  A profile that reads `AGENTS.md` directly (e.g. `--ide codex`) writes no pointer at all,
  so its absence is NOT a failure.
- the **rules tree** — `.peaks/standards/**` for the 2.0 canonical layout, `.claude/rules/**`
  for a legacy 1.x project that still carries a thick `.claude/rules/` tree.

Never hard-code a path in this gate: ask the CLI which files this project actually uses.

```bash
# 1) The CLI decides the file set. Never hard-code a rules path here.
peaks standards init --project . --dry-run --json
# → data.plannedWrites[] = [{ relativePath, status }, ...]
#   init reports TWO states: planned (absent → will be created) | existing (present on
#     disk — init does not compare content, so a stale file still reads 'existing').
#   The four-state view of the body file comes from
#     `peaks standards update --project . --dry-run --json`:
#     planned | appended (managed block missing) | existing (managed
#     block matches) | review (managed block differs → a human decides; that command exits
#     1 with code STANDARDS_UPDATE_REVIEW_REQUIRED). The four states cover AGENTS.md and,
#     when the IDE profile writes one, CLAUDE.md; rules-tree files are planned | existing
#     in both commands.
# → data.skippedFiles[] = the entries already present on disk
#
# 2) The body file is invariant — it is in plannedWrites in every layout. Verify it landed:
ls AGENTS.md
# "No such file" → BLOCKED
#
# 3) Judge the rest from the JSON, not from a hard-coded path list:
#    BLOCKED when plannedWrites is empty, or when any entry has status 'planned'
#    (that required standards file is absent).
#    PASS otherwise. `init` never reports 'appended' / 'review' — when the update
#    dry-run does, report each one as a follow-up:
#    'appended' → `peaks standards update --project . --apply` adds the managed block;
#    'review'  → the managed block differs; a human decides (see `peaks standards update`).
#
# The layout is read off the paths themselves:
#    .peaks/standards/** → 2.0 canonical layout
#    .claude/rules/**    → legacy 1.x layout
# CLAUDE.md is listed only when the IDE profile needs a pointer — its ABSENCE from
# plannedWrites is NOT a failure and must not be reported as BLOCKED.
#
# Any 'planned' entry → BLOCKED. Run `peaks standards init --project . --apply` (first
# time) or `peaks standards update --project . --apply` (existing) FIRST, then re-run the
# dry-run until no entry is 'planned'.
# Do not write a single line of implementation code without standards files in place.
# Without the standards body + rules tree, code review and security review triggers won't fire.
```

**Peaks-Loop Gate B — Before QA handoff:**
```bash
ls .peaks/_runtime/<sessionId>/rd/requests/<rid>.md \
   .peaks/_runtime/<sessionId>/rd/tech-doc.md
# Both must exist. Missing either → BLOCKED, do not hand off to QA
```

**Peaks-Loop Gate B2 — Before QA handoff: unit tests exist and pass for the changed surface:**
```bash
# Run the project's test command against changed files. Record the output.
# Example (adapt to project test runner):
npx vitest run --changed --reporter=verbose 2>&1 | tail -20
# Expected: exit code 0, all changed-surface tests passing, coverage for new/changed code recorded
# Any failing test or zero tests for new code → BLOCKED. Write tests, then re-run.
#
# To run the FULL suite (slower; not the default for `peaks slice check`),
# drop `--changed` or use `npx vitest run --reporter=verbose`. The peaks-code-test
# skill is the user-facing wrapper for the full suite; the slice check's
# `--run-tests` flag is the CLI opt-in.
```

**Peaks-Loop Gate B3 — Before QA handoff: code review evidence exists:**
```bash
ls .peaks/_runtime/<sessionId>/rd/code-review-<rid>.md 2>&1
# Expected: .peaks/_runtime/<sessionId>/rd/code-review-<rid>.md
# "No such file" → BLOCKED. Run code review (use code-reviewer agent or equivalent),
# record findings, fix CRITICAL/HIGH issues, then re-check.
```

**Peaks-Loop Gate B4 — Before QA handoff: security review evidence exists:**
```bash
ls .peaks/_runtime/<sessionId>/audit/security-<rid>.md 2>&1
# Expected: .peaks/_runtime/<sessionId>/audit/security-<rid>.md
# "No such file" → BLOCKED. Run security review (use security-reviewer agent or equivalent),
# fix CRITICAL/HIGH issues, record findings, then re-check.
```

**Peaks-Loop Gate B5 — RD artifact body has no unfilled placeholders:**
```bash
peaks request lint <rid> --role rd --project <repo> --session-id <session-id> --json
# Expected: ok=true. exit 0.
# ok=false → BLOCKED. The lint output lists every <placeholder>, "- ..." stub,
# and TBD/TODO marker with line numbers. Fill them in before attempting handoff.
```

**Peaks-Loop Gate B6 — Declared --type matches the actual diff:**
```bash
peaks scan request-type-sanity --project <repo> --type <type> --json
# Expected: consistent=true. exit 0.
# consistent=false → BLOCKED. Either the implementation scope-creeped beyond what
# the declared type covers, or the type was mis-classified at PRD time. Re-classify
# (`peaks request init` with the corrected --type) or trim the scope.
```

**Peaks-Loop Gate B7 — Repair cycle cap (only relevant during RD↔QA repair loop):**
```bash
peaks request repair-status <rid> --project <repo> --session-id <session-id> --json
# Expected: atCap=false. exit 0.
# atCap=true → BLOCKED. Three repair cycles already attempted; emit a blocked TXT
# handoff via Code rather than entering a fourth cycle.
```

**Peaks-Loop Gate B8 — Diff stays inside the declared red-line scope:**
```bash
peaks scan diff-vs-scope --rid <rid> --project <repo> --session-id <session-id> --json
# Expected: ok=true. exit 0.
# violations[] non-empty → BLOCKED. A changed file matches an explicit out-of-scope
#   pattern. Revert it, or — only with PRD approval — expand the RD red-line scope.
# unclassified[] non-empty → BLOCKED. A changed file does not match any declared
#   in-scope pattern. Either add it to the in-scope list (intentional widening, requires
#   PRD approval) or revert the change.
# patternsDeclared=false → BLOCKED. The RD artifact's `## Red-line scope` section has
#   no concrete path or glob patterns. Fill it in with paths like `src/services/login/**`
#   before re-running. Auto-allowed paths (test files, .peaks/, __mocks__/) never need a pattern.
```

**Peaks-Loop Gate B9 — RD-side perf-baseline output present (when slice has a user-perceivable perf surface):**
```bash
ls .peaks/_runtime/<sessionId>/rd/perf-baseline.md 2>&1
# Expected: .peaks/_runtime/<sessionId>/rd/perf-baseline.md
# "No such file" + slice is feature / refactor / bugfix-when-perf → BLOCKED.
#   Run the perf-baseline sub-agent from "Parallel review fan-out" below (or
#   `peaks perf baseline --apply` inline), then fill in the Results table
#   with measurements (lighthouse / k6 / autocannon / project-local bench —
#   the CLI does not run these; that is the RD's job), then re-verify.
# "No such file" + slice is docs / chore / pure-bugfix-no-perf → OK to proceed;
#   this gate does not apply to those slice types.
# File exists but Results table is empty (only the header row, no data rows) →
#   BLOCKED. The sub-agent scaffolds the file; the main RD loop must fill in
#   the Path / route | Workload | Tool | Metric | Baseline | Threshold table
#   with actual numbers before handoff.
# File contains the marker `N/A — no perf surface` in its Notes section →
#   OK to proceed. This is the explicit opt-out the sub-agent writes when
#   the slice has no user-perceivable perf surface (e.g. a feature that only
#   adds an internal flag with no runtime cost, or a refactor that does not
#   alter any hot path).
#
# The CLI enforcement table below the section header also gates this at the
# `peaks request transition rd:qa-handoff` call, so a missing or empty file
# is rejected by the CLI with `code: PREREQUISITES_MISSING`.
```
