# Dev Framework Rl

> RL orchestrator: runs a problem idea-to-ship with critic gates and trajectory logging. Use for 'run an RL episode' or 'loop <target>'.

- Skill: `kitfunso/dev-framework-rl` (Agent Skill)
- Install (CLI): `npx skillmds@latest add kitfunso/dev-framework-rl`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kitfunso/dev-framework-rl/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: kitfunso (https://skillmd.com/u/kitfunso)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/kitfunso/dev-framework-rl

---


# /dev-framework-rl — Experiential-RL Orchestrator

Runs a development problem as an **episode**: brainstorm a framing, walk the `/dev-framework` stages, gate each stage with a critic sub-agent, log the whole trajectory to SQLite, and (later phases) compute a reward and learn from it.

The policy is prompts + memories — Claude's weights are frozen. This is experiential-RL: the system improves by accumulating trajectories and updating skill prompts, not by gradient descent.

## Current state

All phases through Tier 8 are shipped and tested (real-DB suite under `tests/`).
Full tier history, ship dates, and design detail live in `RL-PLAN.md` (v0.7) and
`README.md` — this file holds only operating instructions. Running today: episode
lifecycle + host locks (`episodes.db`), per-stage manifests, 7 verdict critics, 8
per-type reward rubrics, secret scrubbing on every DB write, per-episode +
cross-episode learning, the batch ritual, critic-trust attribution
(observation-only), and every command in the CLI table at the bottom.

Post-deploy outcomes: finalize auto-schedules a 7-day check; the `devrl-post-deploy`
cron (daily 09:00 Europe/London, prompt at
`C:/Users/skf_s/clawd/memory/cron-prompts/devrl-post-deploy.md`) runs the
verification protocol (`gh pr checks` + revert-grep per episode). HARD GUARD:
never mass-mark `--clean` to clear the queue — uncertain outcomes stay `unknown`
and go to the human via Telegram.

## Prerequisites

`episodes.db` must be migrated:

```bash
python ~/.claude/dev-framework/scripts/migrate.py
```

**Invoking devrl — absolute path, never a persisting `cd` (CRITICAL anti-drift
rule).** The Bash-tool cwd PERSISTS across calls: a bare `cd ~/.claude/dev-framework`
silently re-homes every later feature-repo command — the single most recurring
orchestrator friction, and memory-recalled discipline demonstrably fails under load.
Mechanics: `python ~/.claude/dev-framework/scripts/devrl.py <cmd>` run FROM the
feature repo (the `python scripts/devrl.py` snippets below are shorthand for that
absolute form), OR a non-persisting subshell `( cd ~/.claude/dev-framework && ... )`;
give EVERY feature-repo command its own explicit `cd <repo> &&` in the SAME call.
Full incident detail: AUDIT-RULES.md "cwd-drift".

## Episode lifecycle

### 1. Init and lock

```bash
python scripts/devrl.py episode-init "<problem statement>" --token-budget 200000 --wallclock-budget-sec 5400
```

Capture the printed 26-char episode id as `$EID`. Then claim the host lock:

```bash
python scripts/devrl.py lock-acquire $EID --session "<this-session-id>"
```

Exit 1 / "denied" → another session owns this episode. **Stop.** Do not proceed.

Heartbeat the lock (`lock-heartbeat $EID`) at least every few minutes during long stages — a lock un-heartbeated for 5 minutes is reaped and the episode marked `stalled`.

**Background-op heartbeat discipline.** Long background ops (Workflow builds, full
test suites, codex reviews) silently burn the 5-minute lock window: heartbeat
immediately BEFORE and AFTER every such op; on `lock-vanished`, re-acquire and
continue. Never `&&`-chain a manifest write (or any required step) after
`lock-heartbeat` — a vanished lock exits non-zero and aborts the chain; write the
manifest unconditionally on its own line. Full incident: AUDIT-RULES.md
"background-op heartbeat".

**Isolate into a dedicated worktree at episode START for high-contention repos.**
TRIGGER: `git worktree list` shows active `.claude/worktrees/` agents, or another
session may checkout the shared tree. Right after `lock-acquire`:
`git worktree add -b <feat-branch> <path> origin/master`, then `npm install`
+ `npm run build` in the worktree (do NOT junction `node_modules`: npm
destroys a non-directory junction on its first install, arriving-empty cost a
build round on 2026-08-02; a real install is seconds on this repo), do ALL
edits/commits/codex/build/test there; remove at close-out (if a legacy
junction exists, `rmdir` it BEFORE `git worktree remove` or the removal can
follow it into the main repo's node_modules). Before any version bump:
`git fetch origin && git show origin/master:package.json | grep version` and bump
PAST a concurrent merge. Full incidents: AUDIT-RULES.md "episode-start worktree
isolation".

**Probation-memory exposure (R9, 2026-06-09).** Right after `lock-acquire`, run `python scripts/devrl.py memory-list --status probation`. Carry the relevant memories into this episode's stage briefs tagged `[PROBATION]` — they are unconfirmed hints, not settled law — and record each one's id on the steps where it was in scope (`step-record ... --memory-ref <id>`). This is what lets a useful memory earn its 3 shipped-episode confirmations and a useless one earn its deprecation; a probation memory kept out of context can never do either.

### 1.5 Codex availability probe (recommended)

The `review` stage gates on `codex-review-critic`, which requires the `codex` CLI. Probe at episode-init so an unreachable codex doesn't surface as a surprise blocker mid-episode. Run:

```bash
_CODEX_BIN=$(which codex 2>/dev/null || echo "")
if [ -z "$_CODEX_BIN" ]; then
  echo "WARN: codex CLI not installed. codex-review-critic at the review stage will fail."
  echo "  Either install: npm install -g @openai/codex"
  echo "  Or proceed knowing the review stage will need manual /codex from another session"
  echo "  (paste the verdict back to the orchestrator)."
  python scripts/devrl.py episode-friction $EID --note "codex CLI not installed at episode-init; codex-review-critic will need manual /codex from another session."
fi
```

If codex is missing, decide upfront whether to (a) install it, (b) skip codex-review-critic with explicit human override at the review stage, or (c) defer the episode entirely. Surfacing this at init beats surfacing it at review-stage round 1.

(Incident 2026-05-26, hippo C5: mid-episode codex unavailability paused the review stage; an init-time probe would have caught it upfront.)

**Pin cwd for `codex review --uncommitted` — use the wrapper.** `codex review
--uncommitted` reviews the CURRENT cwd's repo; after any devrl command that can
mean dev-framework itself. Never call it bare from an orchestrator session — invoke
`bash ~/.claude/dev-framework/scripts/codex-review-pinned.sh <feature-repo> [args]`
(subshell-cds, verifies codex's `workdir` line, exit 3 = review VOID). Full
incident: AUDIT-RULES.md "codex review cwd pinning".

**Codex-on-Windows root cause + hard timeout (2026-08-02, episode 01KZ1FHCK).**
Codex's Windows sandbox helper receives its policy on the command line; at
~33.9KB payload it exceeds the 32,767-char CreateProcess limit (os error 206),
EVERY shell exec fails, and `codex review` stalls forever instead of failing
fast — a 2h14m silent hang. The wrapper now defaults
`-c 'sandbox_mode="danger-full-access"'` (bypasses the broken helper),
`-c 'notify=[]'` (disables the computer-use turn-end hook), and a hard
`timeout` (default 600s, override `CODEX_REVIEW_TIMEOUT`; 124 => exit 3 VOID).
The old `-c "mcp_servers={}"` guard no longer disables MCP in codex 0.144.x —
do not rely on it. NEVER run codex as an unbounded background job: watchdog by
output-file mtime, not just content.

**Base-aware invocation (2026-08-15, episode 01M025CW434ZAPVSFC61BGFGCT).**
Check `git status` in the feature repo BEFORE invoking the wrapper:
- Work already committed => `--uncommitted` reviews NOTHING ("working tree is
  clean") and wastes a round. Pass `--base origin/master` (or the base sha).
  Codex rejects `--base` combined with a positional prompt — use the flag alone.
- Re-review rounds after fix commits => scope to the DELTA with
  `--base <prior-commit-sha>`. A grown multi-commit branch (~4k insertions)
  blew a 900s timeout on a full re-review; the delta-scoped run completed.
- Full-branch reviews of substantial diffs => set `CODEX_REVIEW_TIMEOUT=900`
  or higher. The 600s default killed one review AFTER it had written complete
  findings (VOID by wrapper contract; findings salvageable only as unverified
  input, re-verify against source).
Memory: `feedback_codex_review_base_aware.md` (tier-1, probation).

### 1.6 Publish credentials probe (recommended for `library` / package releases)

If the episode will publish a package at the deploy stage (npm, PyPI, crates,
etc.), the publish credentials live with the human, not the orchestration
session. An episode that bumps versions, passes every gate, and merges, only to
hit a 401 at `npm publish`, has surfaced an avoidable blocker after the work is
already done. Probe at episode-init, the same way §1.5 probes codex:

```bash
# npm — only if this release publishes to npm
npm whoami >/dev/null 2>&1 || {
  echo "WARN: not authenticated to npm; deploy-stage 'npm publish' will 401."
  python scripts/devrl.py episode-friction $EID --note "npm not authenticated at episode-init; publish will be a manual operator step or needs 'npm login' first."
}
# PyPI — only if this release publishes a Python package
[ -n "$TWINE_PASSWORD" ] || [ -f ~/.pypirc ] || {
  echo "WARN: no PyPI token in env or ~/.pypirc; deploy-stage twine upload will hang/fail."
  python scripts/devrl.py episode-friction $EID --note "no PyPI token at episode-init; SDK publish will be a manual operator step (TWINE_USERNAME=__token__ TWINE_PASSWORD=pypi-... --non-interactive)."
}
```

If creds are missing, decide upfront whether to (a) have the operator authenticate (`! npm login`, export the PyPI token) so the deploy stage can publish, (b) treat publish as a documented manual handoff and tag-only, or (c) defer. Either way the deploy-gate human checkpoint should state the publish path explicitly rather than discovering the 401 after merge.

(Incident 2026-05-28, hippo v1.15.0: all gates passed and the PR merged, then `npm publish` 401'd — publish became a manual after-the-fact operator step.)

### 2. Brainstorm pre-stage

Run `/office-hours` (builder mode) or `/brainstorming` to generate 3-5 candidate framings of the problem. Pick the strongest.

**Then work your own pick, twice: build it out, then break it.**

**Build it out with `/grilling`.** Map the framing as a design tree and work the frontier: every decision whose prerequisites are already settled. Ask the whole frontier in one round, numbered, each question carrying your recommended answer. Then answer your own round — the orchestrator is the respondent here, and the recommendations exist so it can decide without a human. Escalate only a question whose answer is on the ASK-FIRST list (cost, destructive, live-schema, a genuine two-readings fork); everything else takes the recommendation and records it. Finding facts is the orchestrator's job: dispatch a sub-agent for anything the repo, the DB or a tool can answer, and ask the rest of the frontier while it runs. The pre-stage is done when the frontier is empty, so nothing reaches `plan` silently assumed.

**Then break it with `/grill-me`.** "Me" is the orchestrator (you). The grill is adversarial: interrogate the assumptions in your own selection, name the weakest premise, surface what would have to be true for this framing to win, and what would falsify it. If the grill breaks the framing, return to the candidate list and pick a different one (or refine).

Two passes, not one. `/grill-me` attacks the answers you already have; it never surfaces the question you failed to ask. Every brainstorm concern from either pass is carried forward into `plan` under the §4 rule, so an unvisited branch here is an unvisited branch the plan-eng-critic will never see. Record the chosen framing + the grill's strongest counter-point + your response in the step summary:

```bash
python scripts/devrl.py step-record $EID brainstorm --skill /office-hours --critic-status n/a --summary "<chosen framing + grill's strongest objection + how you addressed it>"
```

(The `brainstorm-judge` critic that ranks candidates automatically is a separate follow-up — it needs a ranking contract distinct from the pass/fail verdict critics. For now, choose deliberately, grill your own choice, and record your reasoning in the summary.)

### 3. Discover and stage plan

Invoke `/dev-framework discover` with `$EID` in context. It emits `trajectories/$EID/stage-plan.json`. Validate it:

```bash
python scripts/validate_manifest.py plan trajectories/$EID/stage-plan.json
```

Read the plan's `project_type` and `rubric_file`, then:

```bash
python scripts/devrl.py set-project-type $EID <project_type> <rubric_file>
python scripts/devrl.py step-record $EID discover --skill /dev-framework --critic-status n/a --summary "<what discover found>"
```

### 3b. Pre-plan codebase audit (mandatory between discover and the first plan stage)

A deterministic audit pass between discover and the first plan invocation: greps are
free; the same drifts cost a critic round each when found later. Run the applicable
rules in ONE batch of parallel Grep calls before invoking `/dev-framework plan`.
Each rule: TRIGGER → action. Full detail + incidents: `AUDIT-RULES.md` (read a rule's
section when it fires or the plan matches its shape).

1. **TODOS staleness reproduce-check** — TRIGGER: the brief references TODO/backlog
   items. Grep each item's named symbol/test/CLI-flag/file; already shipped → abort
   the episode as a reproduce-check WIN (sync TODOS.md, finalize `aborted`; a WIN is
   a successful episode and breaker-exempt). → AUDIT-RULES.md #1
2. **Parallel allow-list grep** — TRIGGER: the plan adds an entry to ANY
   allow-list/registry/`Set`/`Map` (incl. repeatable-CLI-flag allow-lists in argv
   parsers). Grep the exact pattern repo-wide; N>1 sites → the plan must enumerate
   all N (a repeatable flag missing from the parser list is silently last-wins).
   → AUDIT-RULES.md #2
3. **Version-bump target count** — TRIGGER: the plan ships a version bump. Enumerate
   every manifest via git grep; for version CONSTANTS in code, also grep the TEST
   assertions that hard-code the old number. → AUDIT-RULES.md #3
4. **Roadmap-sync sweep** — TRIGGER: the episode cites a canonical-roadmap claim
   ("never measured", "Track X blocked"). Grep `docs/evals/` for results docs newer
   than the canonical doc's mtime. → AUDIT-RULES.md #4
5. **Public-API caller audit** — TRIGGER: the plan changes a function signature.
   Grep callers at EVERY import surface (`from X import`, `sys.path` inserts,
   `spec_from_file_location`, dynamic `require()`), not just internal helpers.
   → AUDIT-RULES.md #5
6. **Functional-duplicate check** — TRIGGER: the plan ADDS a user-facing artifact
   (page/calculator/route/endpoint). Grep existing artifacts' titles AND
   descriptions for the same CAPABILITY — a slug/name collision check alone is
   insufficient. → AUDIT-RULES.md #6
7. **Temporal / as-of query check** — TRIGGER: the plan builds a "what was X at time
   T" query. Mirror an existing temporal pattern (hippo `src/recall-history.ts`
   successor-aware `asOf`); the plan must enumerate valid-time vs transaction-time,
   which row statuses are included, and the date-granularity contract.
   → AUDIT-RULES.md #7
8. **Sibling-clone audit** — TRIGGER: the plan mirrors an existing sibling
   route/module/handler. Audit the pattern being cloned for latent bugs before
   replicating (e.g. `?limit=` needs `Number.isInteger`); prefer one shared helper
   over an Nth copy. → AUDIT-RULES.md #8
9. **Cross-cap consistency** — TRIGGER: the plan generates/assembles a value stored
   into a length-capped field. Prove `N × per_item_cap + overhead <= column_cap` or
   require budget-aware assembly. → AUDIT-RULES.md #9
10. **Reserved-word column check** — TRIGGER: a migration adds a column. Check each
    name against SQL reserved words (`trigger`, `order`, `group`, ...); rename with
    a safe suffix and map back to the domain field. → AUDIT-RULES.md #10
11. **Bidirectional denormalized-value guard** — TRIGGER: a child row denormalizes a
    parent value with a "child matches parent" guard. A forward guard on the child
    is NOT sufficient — also spec a parent-side reverse guard (or explicitly accept
    parent immutability). Cover BOTH directions. → AUDIT-RULES.md #11
12. **Fail-soft post-commit side-effect** — TRIGGER: the plan adds a best-effort
    side-effect after a committed write (enqueue/mark-dirty/notify/invalidate).
    Enumerate the loss windows UP FRONT; state per window: closed, or accepted
    self-healing (and how). → AUDIT-RULES.md #12
13. **Bounded-neighbourhood / focus query** — TRIGGER: the plan builds a
    focus/subgraph/k-hop query with a limit. Run the six-point correctness
    checklist UP FRONT (seed, edges, SQL-pushed bound, truncation flags, cap
    alignment, one snapshot). → AUDIT-RULES.md #13
14. **FK-action / trigger firing — verify empirically** — TRIGGER: a migration
    combines FK `ON DELETE` actions with `BEFORE` triggers on the same table. Do NOT
    reason from docs; probe in a `:memory:` db (`node:sqlite` fires BEFORE UPDATE
    from `ON DELETE SET NULL` even with recursive_triggers OFF). → AUDIT-RULES.md #14
15. **Dual-provenance invariant matrix** — TRIGGER: row validity depends on >1
    provenance source or a denormalized copy. Enumerate the guard matrix at plan
    time; ONE shared invariant helper at EVERY write path. → AUDIT-RULES.md #15
16. **Already-shipped / already-fixed check** — TRIGGER: every episode, before
    brainstorm (direct invocations included). `git fetch origin`; resolve the
    DEFAULT branch (never hardcoded); diff `HEAD..origin/<default>` + grep the
    feature's key terms across the origin tree; `gh pr list --search "<terms>"`.
    Hit → STOP and reframe to the user. Network/auth failure → warn and proceed.
    → AUDIT-RULES.md #16
17. **Per-site fix-plan greps** — TRIGGER: the plan maps fixes to specific sites.
    BEFORE writing the mapping, grep: existing helpers with the same purpose; the
    real call chain into each edited function; the enclosing function at each cited
    line; same-function siblings sharing the defect class. Names/line-proximity
    reasoning is reliably wrong. → AUDIT-RULES.md #17
18. **Search-first for external solutions** — TRIGGER: the plan BUILDS new
    non-trivial machinery (parser, scheduler, diff engine, retry/queue layer,
    protocol client, algorithm implementation). Before the plan stage, search for an
    existing library/built-in/pattern ("<runtime> <thing> built-in", "<thing> best
    practice <year>") and state in the plan what was found and why build-vs-adopt.
    Rules 6/16 catch internal and already-shipped duplicates; this catches
    reinventing an external wheel. → AUDIT-RULES.md #18
19. **Dataset-invariant audit (eval episodes)** — TRIGGER: the episode builds or
    pre-registers an eval harness/protocol over a dataset. BEFORE any pre-reg doc
    locks, script three <5-min checks against the REAL data: (a) temporal ordering
    (any context timestamps after the evaluation timestamp?), (b) per-feature
    variance through the real ingest path on a small sample, (c) duplicate-content
    rates (tie-break stress). Paste the outputs into the plan. Memory:
    `feedback_eval_prereg_dataset_invariant_audit`. (LC2-E1 2026-08-09: skipping
    this cost 2 protocol amendments + two 77-minute full reruns; a critic and codex
    each found one of the missed invariants empirically.) → AUDIT-RULES.md #19
20. **Researched-dataset verifier pass** — TRIGGER: the plan builds a facts table
    (vendor terms, prices, fees, perks) by sub-agent research from web sources. The
    plan must add a separate READ-ONLY verifier task: a second sub-agent re-opens each
    row's cited source and reports MATCH / MISMATCH per field before the execute
    manifest emits; the producer never grades its own rows. (boring-maths card
    rewards 2026-09-02: a 5-row spot-check of a 22-row Sonnet dataset found 3 field
    errors.) → AUDIT-RULES.md #20

Record the audit as a step:
```bash
python scripts/devrl.py step-record $EID codebase-audit --skill /dev-framework-rl --critic-status n/a --summary "<grep results: N TODOS items reproduce-checked; M allow-list sites identified; K version-bump targets enumerated; J docs/evals/ results-docs scanned; L public-API caller surfaces audited>"
```

**Then record every rule you RAN, and say whether it found anything (C1 policy decay).** Record the ones that ran clean too, not only the ones that hit:
```bash
python scripts/devrl.py audit-record $EID \
  --rule <slug>[:what-you-checked] \      # ran, found nothing (repeatable)
  --caught <slug>[:what-it-found]         # ran and found something (repeatable)
```
A rule that runs every episode and never once catches anything costs tokens forever and scores highest on firing count alone, so `--rule` vs `--caught` is the whole measurement. Passing the same slug to both exits 2. Only the FIRST colon splits, so a note may contain `file.ts:42`.
Stable slugs (never renumbered; rule № → slug): 1=`todos-staleness`, 2=`parallel-allow-list`, 3=`version-bump-targets`, 4=`roadmap-evals-freshness`, 5=`public-api-callers`, 6=`functional-duplicate`, 7=`temporal-as-of`, 8=`sibling-clone`, 9=`cross-cap-consistency`, 10=`reserved-word-column`, 11=`bidirectional-guard`, 12=`fail-soft-post-commit`, 13=`bounded-neighbourhood`, 14=`fk-trigger-empirical`, 15=`dual-provenance-matrix`, 16=`already-shipped-origin`, 17=`per-site-plan-greps`, 18=`search-first-external`, 19=`dataset-invariant-audit`, 20=`researched-dataset-verify`. `policy-compact-report` proposes two kinds of retirement, both propose-only, and absence alone never demotes: `demotion_candidates` never fire at all, `never_caught_candidates` fire but have never found anything across at least `--min-scored` scored firings. Firings recorded before migration 0017 carry no verdict and count as `unscored`, never as clean, so they can never retire a rule.

If the audit surfaces a reproduce-check WIN, jump to finalize (skip plan/execute/verify/review/ship). The win IS the deliverable.

### 4. Stage loop

Iterate the `stages` list from `stage-plan.json` (NOT a hardcoded 9). For each stage after `discover`:

1. Invoke `/dev-framework <stage>` with `$EID` in context.
2. Validate the emitted manifest:
   ```bash
   python scripts/validate_manifest.py manifest trajectories/$EID/<stage>.manifest.json
   ```
   Validation failure → record the step with `--critic-status error` and **escalate to the human**.
3. If the stage has critic(s) — `critic_registry.STAGE_CRITICS` maps each gated stage to its critics (`plan` → `plan-eng-critic` + `plan-design-critic`, `execute` → `code-review-critic`, `review` → `independent-review-critic` + `codex-review-critic`, `ship` → `ship-readiness-critic`, `deploy` → `canary-monitor`). These are the exact names `critic-check` accepts — pass them verbatim (every critic carries the `-critic` suffix except `canary-monitor`). For each critic of this stage:
   - Launch a `senior-code-reviewer` sub-agent. Brief it with the full contents of that critic's briefing (`prompts/critic-<role>.md`) plus the milestone goal and the stage's artifacts from the manifest. Pass the briefing's findings shape through verbatim — `{"severity", "message", "location"}` per finding, extra keys forbidden — and never restate the output contract in your own words: a brief that asked for `{severity, section, issue, fix}` made `critic-check` reject a real plan-eng verdict and cost a round (2026-09-02, boring-maths `01M1H905JWGMPA1GGC9PR24JBB`). (`plan-design-critic` runs only for UI projects.) **Exception: `independent-review-critic` gets NO sub-agent wrapper** — the orchestrator runs `/code-review` directly and grades its findings into the critic contract (see the `review` bullet in §4a); the zero-tool-calls check below applies to sub-agent critics only.
   - **If the plan artifact is an existing repo doc** (e.g. `docs/.../plans/<date>-<name>.md`) rather than an in-episode draft, brief the critic explicitly that any `Status: Draft` or `not yet reviewed` marker means the doc has NOT been engineering-reviewed yet; fresh-eyes scrutiny is the point. A pre-existing plan author's reasoning is not pre-vetted, and the orchestrator should not defer to its existing prose. (Incident 2026-05-23, resona Phase A: fresh-eyes briefing caught a cross-org schema leak in a 5-day-old draft.)
   - **For the `plan` stage specifically: pre-stage brainstorm concerns must be carried forward into the revised plan.** When entering `plan`, re-read the brainstorm step's `summary` field (via `episode-steps`). Each concern surfaced there must be either explicitly addressed in the revised plan artifact OR explicitly noted as out-of-scope in the plan manifest. Don't let brainstorm-stage framing dissolve before the plan-eng-critic runs — the critic only judges what's IN the revised plan, not what was flagged during brainstorm. (Incident 2026-05-23, resona Phase B: brainstorm-flagged rate-limiting never reached the revised plan; the gap surfaced at review and cost a retry.)
   - **Confirm the sub-agent actually read the artifacts.** If its result reports zero tool calls, it produced a verdict from the prompt text without opening the diff / plan / files — discard it, do NOT `step-record` it, and re-launch the critic. If a re-launched critic again returns zero tool calls, record `--critic-status error` and escalate. A critic that read nothing has not reviewed; the critic briefings mandate file reads for the same reason.
   - Save the sub-agent's response to a temp file, then:
     ```bash
     python scripts/devrl.py critic-check <critic-name> <temp-file>
     ```
   - **Exit 0 (pass)** → `step-record ... --critic <critic-name> --critic-status pass --critic-score <n>`.
   - **Exit 1 (fail)** → `step-record ... --critic <critic-name> --critic-status fail --critic-score <n> --must-fix "..."`. Re-run the stage with the must-fix fed back, up to the per-stage cap (plan 3, execute 2, review 1, ship 1; `--retry-count <n+1> --retry-strategy revise_with_feedback`). Cap hit → **escalate to the human**. **Root-cause pass before the final retry (added
2026-07-18):** when a stage's critic has failed twice consecutively AND the must-fix
describes a DEFECT (failing test, wrong behavior, crash) rather than a plan/scope gap,
run `/investigate` on the defect BEFORE the last permitted retry — the final attempt
must be built on a named root cause, never a third revise-with-feedback guess (global
rule: after 2 failed iterations, reconsider instead of retry). Record the
investigation's one-line cause in the retry step's summary. **Cap-extension carve-out (learned 2026-06-02, hippo A7 recall-trace):** when a plan round returns score ≥ 75, 0 `crit`, and the SOLE remaining finding is a one-clause fix the orchestrator introduced in its OWN prior revision, a single one-round cap extension (with operator notification) is permitted before full escalation; escalate if that extension round also fails or introduces a new finding. The cap exists to stop thrashing on a broken plan, not to block a converged one over a self-inflicted typo.
   - **Exit 2 (parse error)** → `step-record ... --critic <critic-name> --critic-status error`, **escalate to the human**.

   **`--critic` is mandatory on every `pass` and `fail`** and `step-record` exits 2 without it. Pass the same registry name you gave `critic-check`. A verdict with no critic named cannot be attributed, and `critic_pass_rate` and `critic-trust` both read exactly these rows. Between 2026-05 and 2026-09-01 the flag was optional and 548 of 658 graded steps went unattributed (all 47 of July's), which is why no per-critic trend can be reconstructed for that period.
   The stage advances only when all of its critics pass.

   **CI gate (`ship` / `deploy` stages) — a critic verdict is not the repo's CI.**
   Before the `ship` stage opens a PR and before the `deploy` stage merges one,
   run `gh pr checks <PR>` and require every *required* check green. A red
   required check blocks — do not merge. A red *non-required* check must be
   explicitly classified as pre-existing (`gh run list` evidence it failed on
   the base branch before this episode) or fixed first. Never tell the human
   "all gates passed" meaning the critic gates while repo CI is red — they are
   different gates; report both.

   **Stacked PRs on a squash-merge repo (2026-09-02, boring-maths #25 → #26 → #27).**
   Squashing the base PR rewrites the commits the next PR still carries, so GitHub
   reports that PR CONFLICTING the moment it is retargeted. For each next PR, BEFORE
   `gh pr edit <N> --base master`: `git rebase --onto origin/master <old-base-tip>
   <branch>`, confirm `git diff --stat <old-tip> <branch>` is empty, then
   `git push --force-with-lease=refs/heads/<branch>:<old-sha>`. Never
   `--delete-branch` on an intermediate merge — GitHub auto-closes every PR stacked
   on a deleted base (memory `feedback_gh_pr_merge_delete_branch_cascade`).
4. If the stage has no critic → `step-record $EID <stage> --critic-status n/a --summary "..."`, advance.
5. Heartbeat the lock.

### 4a. Mandatory skill hooks per stage

Three Keith-validated release-chain skills are wired into the stage loop. They are NOT critics (no `critic-check` parsing) — they run *before* the stage's critics so the diff arriving at each critic has already been self-reviewed and sanity-checked. Skip them and the critics waste sub-agent budget on issues the skill would have caught.

- **`execute` stage — run `/self-review` as the tail step before manifest emit.** Same-session pass over the diff just produced. Catches missed requirements, regressions, and forgotten edge cases that `code-review-critic` would otherwise spend a sub-agent finding. Record the summary in the manifest's `self_review_summary` field — the schema enforces this at `stage: execute, status: completed`, so the validator will reject the manifest without it. If `/self-review` surfaces a must-fix, address it in-stage before emitting the manifest — do not advance.

- **`verify` stage — drive the affected flow end-to-end as the tail step before manifest emit (reworded 2026-08-02; the 2026-07-18 text named a `/verify` skill that was never installed).**
  Re-running the test suite is necessary but not sufficient: exercise the AFFECTED
  FLOW in the real app/CLI and observe behavior — the class of runtime
  regression that green tests and `gh pr checks` both miss (the CI-red-merge incident's
  root cause). For UI projects use `/qa-only` (browser QA, report-only) as the driver;
  for CLIs/APIs run the real commands/requests against the built artifact. Record what was driven + observed in the verify manifest's
  `verify_skill_summary` field (optional schema field today; may be promoted to required
  once the workflow shape stabilises). A behavioral mismatch found here is fixed in-stage
  before the manifest emits — do not advance.

- **`review` stage — the official `/code-review` plugin implements `independent-review-critic` (rewired 2026-08-02, no fallback — Keith directive).** Sequence: commit the episode's work in the worktree, push the branch, open a DRAFT PR (`gh pr create --draft`), then invoke the plugin (`claude-plugins-official:code-review` — its command carries `disable-model-invocation: false`, so the orchestrator CAN launch it; pass the PR number). It fans out 5 parallel Sonnet reviewers + per-issue confidence scorers and filters findings below 80/100 — adversarially verified findings, which matters because the review stage has a retry cap of 1 and one false positive burns the only retry. **NEVER use Haiku anywhere in this skill (Keith directive 2026-09-13).** The plugin's prose prescribes Haiku for the eligibility check, the guidance-file list, the PR summary and the per-issue scorers; run every one of those on `model: "sonnet"`. Measured 2026-09-13 on hippo PR 191: the Haiku PR summary called a pre-existing file new, one Haiku scorer gave a comment-length nitpick 75, and another gave a real one-token bug 90 that Sonnet re-scored at 75 with better evidence. A judge that misfires in both directions defeats the 80 filter. Grade the surviving findings into the critic contract (findings -> `--must-fix`, score per `prompts/critic-independent-review.md`, then `critic-check independent-review-critic`); pass the briefing's house blind-spot checklist (SQL safety, LLM trust boundaries, conditional side effects, CLI dual-write patterns) in the invocation args. TWO TRAPS, both verified 2026-08-02: (a) the BUNDLED skill also named `code-review` is operator-only (`disable-model-invocation`) — a bare `Skill(code-review)` call errors; use the plugin-qualified name; (b) NEVER wire `/code-review ultra` — billed cloud review, user-triggered only. The `ship` stage then marks the PR ready-for-review instead of creating it. Both `independent-review-critic` and `codex-review-critic` still gate independently; codex stays the cross-model second opinion.

- **`ship` stage — run `/ship-check` as the first step.** Pre-PR sanity pass: what shipped, is it worth shipping, did we do enough QA? Save the output to the manifest's `ship_check_summary` field — the schema enforces this at `stage: ship, status: completed`, so the validator will reject the manifest without it. Pass the summary to `ship-readiness-critic` as input. A blocker from `/ship-check` short-circuits the stage — escalate to the human rather than asking the critic to rubber-stamp it.

- **`ship` stage — Fable final-review pass for the hardest changes (Keith directive, 2026-08-23).** TRIGGER (any): a schema migration; security- or tenant-isolation-touched code; a change to a core write-path / invariant primitive (the `upsertEntryRow` class); or a diff over ~500 changed lines. When it fires, after `/ship-check` but BEFORE `ship-readiness-critic`: spawn exactly ONE Agent sub-agent with `model: "fable"`, briefed with the full diff, the plan artifact, and all prior critic + codex verdicts. Its job is a fresh adversarial "would you ship this?" pass — surface what every earlier gate missed, not re-run their checklists. Real defects it finds are must-fix in-stage before `ship-readiness-critic` runs; append its one-line verdict to the ship step's `--summary`. This bullet is the standing explicit ask the global routing rule requires ("`fable` sub-agents: never unprompted") — it authorises this ONE pass at this ONE point only: never at earlier stages, never as a fan-out, never more than one per episode. Non-triggering episodes skip it silently — Sonnet critics + codex already cover routine diffs (A/B verdict, §4b). Cost context: one Fable review pass is a fraction of Fable orchestrating an episode; run orchestrator sessions on Opus/Sonnet and let this bullet be Fable's only slot.

- **`ship` stage — record deploy metadata right after the PR opens.** `python ~/.claude/dev-framework/scripts/devrl.py episode-deploy-record $EID --pr-url <PR-URL>` (the ship manifest schema now REQUIRES a `pr_url` artifact at `status: completed`; a sanctioned no-PR ship — direct-commit / quant pipelines — records the literal `none`). At the `deploy` stage after merge, re-run with `--merge-commit <SHA>`. This is the producer feeding `episode-deploy-meta`, the `devrl-post-deploy` cron, and every outcome-weighted learning leg — an episode that skips it can never resolve a post-deploy outcome (the 0/95 outcome-starvation root cause, fixed 2026-06-09, PLAN-continuous-loop Phase A).

- **`ship` stage — run `/quiz-me from-diff HEAD` after `/ship-check`, before `ship-readiness-critic`.** Operator-knowledge gate. Generates 5 MC + 1 explain-back from the episode's diff, quizzes the human operator interactively, grades honestly. Then runs `python ~/.claude/skills/quiz-me/scripts/quiz.py gate` — exit 1 blocks the ship stage until the operator clears any failed cards via a follow-up `/quiz-me` session. Save the quiz summary in the manifest's `quiz_me_summary` field (optional schema field today; may be promoted to required in a future iteration once the workflow shape stabilises). This is what enforces "no new features until the operator understands the last one" — the orchestrator's other gates protect against bad code, this one protects against bad operator mental models. Skip this gate in headless / spawned-session mode (no human at the keyboard to quiz); record `quiz_me_summary: "skipped (headless)"` in the manifest so the gap is visible. **Loop mode: defer, don't skip** — record `quiz_me_summary: "deferred to batch gate (loop mode)"` and run the accumulated quizzes (plus `quiz.py gate`) at the batch deploy gate, one sitting with the human present, before the deploy decision.

- **`plan` stage — run `/grill-me` as the tail step before plan-eng-critic.** "Me" here is the orchestrator (you). The grill interrogates the orchestrator's own plan: weakest premise, hidden assumptions, what would have to be true for this plan to work, what would falsify it, which scope claims are unsupported. The grill output is INPUT to plan-eng-critic — the critic should judge whether the plan addresses the grill's objections or explicitly accepts them as out-of-scope. Save the grill summary in the manifest's `self_grill_summary` field (optional schema field today; may be promoted to required in a future iteration once the workflow shape stabilises). If the grill destroys the plan (a premise can't be defended), revise the plan before plan-eng-critic runs — do not present a known-broken plan to the critic.

- **`plan` stage — run `/domain-modeling` against the repo's `CONTEXT.md` before the plan artifact is written.** If the repo has a `CONTEXT.md`, the plan uses its terms and no synonyms; a term the plan needs that the glossary does not define is a gap, and the fix is to settle it and write it there inline, not to invent a second word for it in the plan. If the repo has no `CONTEXT.md`, create one at the first term the episode settles and leave it at that: lazy creation, glossary only, no implementation detail, never a spec. Offer an ADR only when all three of the skill's tests hold (hard to reverse, surprising without context, a real trade-off with alternatives). Record the terms touched in the plan manifest's `self_grill_summary` alongside the grill. This is the cheapest defence the orchestrator has against multi-session drift: episodes on one target span compactions, and a glossary on disk survives what the context window does not.

- **`plan` and `execute` critic briefings — pass the `/codebase-design` vocabulary verbatim.** Brief `plan-eng-critic` and `code-review-critic` to phrase every structural finding in that skill's terms: module, interface, implementation, depth, seam, adapter, leverage, locality, plus the deletion test and "one adapter is a hypothetical seam, two is a real one". Do not let a critic reach for component, service, API or boundary. The reason is the learn step, not style: §6 clusters findings across episodes, and clustering is string-shaped, so two critics describing one defect in two vocabularies produce two clusters of one instead of one cluster of two, and the delta never crosses the tier threshold. Findings shape is unchanged (`{"severity", "message", "location"}`); this constrains the wording inside `message`.

- **(Optional, opt-in) Meta-critic mode** — when a critic returns `pass`, the orchestrator MAY run `/grill-me` against the critic's verdict + reasoning, asking "did this pass actually hold up under adversarial pressure?" If the grill breaks the pass (surfaces an issue the critic missed), the verdict becomes provisional: record as `friction` via `episode-friction` and either re-run the stage with the missed concern in the must-fix, or escalate to the human. This is expensive (extra sub-agent per pass) and OFF by default — opt-in via `episode-init --meta-critic-grill`. Reserve for high-stakes episodes (production deploys, schema migrations, security-touched diffs). Tier 10's `learn-evolve` reads `friction` notes flagged this way to propose critic-prompt mutations.

`/full-power` is d

…(truncated)
