@mrciphersmith/keryx 0.3.118 → 0.3.119

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -628,7 +628,7 @@ keryx flow complete <id>
628
628
 
629
629
  Completion is allowed only after the PR merge has been confirmed. The merge
630
630
  target must be the base branch captured when the flow was created; do not
631
- silently retarget or close against another branch. The flow folder is committed in the same PR as the code; the commit at closing is a rule to follow, not one the gate checks. `flow complete` fails the `folder-committed` gate while the flow's `flow.json` is not in `HEAD` (flows created from 0.3.53 on; commit it with `git add .metaproject/flows/<dir> && git commit`, then run `flow complete` again). `flow init` and `flow renumber` avoid numbers used on known remote branches, and `keryx flow check` reports a clash with a remote branch (repair with `keryx flow renumber <dir> --to <free id> --reason "<why>"`) and warns about a folder that is not committed. Flows created since the owner gate fail completion while no owner is set (`keryx flow owner set <id> --owner "<name>" --reason "<why>"`); `ac confirm` and `flow complete` append a signature — `--signed-by "<name>"` names the signer, otherwise `KERYX_ACTOR` or the local git identity is recorded, as a claim. A flow created with `--require-confirmation` needs one more gate — `--confirm-token <token>` from `keryx flow confirm <id>`, a short-lived single-use token minted by a typed challenge in a terminal; it proves an interactive step ran outside the agent's tool roster, not that a human ran it. A flow left stuck in `completing` by a crash or interrupted attempt (never a normal gate failure) recovers with `keryx flow recover <id> --reason "<why>"`.
631
+ silently retarget or close against another branch. The flow folder is committed in the same PR as the code; the commit at closing is a rule to follow, not one the gate checks. `flow complete` fails the `folder-committed` gate while the flow's `flow.json` is not in `HEAD` (flows created from 0.3.53 on; commit it with `git add .metaproject/flows/<dir> && git commit`, then run `flow complete` again). `flow init` and `flow renumber` avoid numbers used on known remote branches, and `keryx flow check` reports a clash with a remote branch (repair with `keryx flow renumber <dir> --to <free id> --reason "<why>"`) and warns about a folder that is not committed. Flows created since the owner gate fail completion while no owner is set (`keryx flow owner set <id> --owner "<name>" --reason "<why>"`); `ac confirm` and `flow complete` append a signature — `--signed-by "<name>"` names the signer, otherwise `KERYX_ACTOR` or the local git identity is recorded, as a claim. A flow created with `--require-confirmation` needs one more gate — `--confirm-token <token>` from `keryx flow confirm <id>`, a short-lived single-use token. In keryx shell call the `flow_confirm` tool (the operator answers it as a card; never send the operator to a terminal); outside it, a typed challenge in a terminal mints it. It proves an interactive step ran, not that a human ran it. A flow left stuck in `completing` by a crash or interrupted attempt (never a normal gate failure) recovers with `keryx flow recover <id> --reason "<why>"`.
632
632
 
633
633
  If gates fail, the CLI returns the flow to `in-progress`. Add a journal note,
634
634
  create fix tasks, and repeat Phase 2.
@@ -55,7 +55,7 @@ operator reads in the shell's Schedules section.
55
55
  Call `schedule_create`. The operator then sees a confirmation card: cadence and
56
56
  next runs, prompt, runner, budget, network, every granted tool with its binary
57
57
  and account, and exactly what gets installed. You do not confirm it. Only the
58
- operator can, and every permission mode asks, `auto` included.
58
+ operator can, and every permission mode asks, `auto` included. Changing an existing schedule works the same way (`schedule_pause`, `schedule_resume`, `schedule_run`, `schedule_remove`): the operator answers a card; never send them to a terminal.
59
59
 
60
60
  ### Step 4: Report back
61
61
  After the operator's answer, say what was scheduled and when it next runs, or
@@ -72,7 +72,7 @@ that nothing was written. Point to `/schedules` for the list and the reports.
72
72
  - NEVER read or copy the schedule signing key (`schedule-hmac.key`), and never try to
73
73
  reach the scheduler another way (quoting, `env -u`, a script). The text floor can be
74
74
  worked around, and that is exactly why it is not the gate. `keryx schedule add|resume|run`
75
- need the operator's own terminal, and a schedule without this machine's signature never runs.
75
+ need the operator's own terminal (inside keryx shell the operator answers the card instead), and a schedule without this machine's signature never runs.
76
76
  - NEVER run `loginctl enable-linger`. If linger is off, tell the operator the
77
77
  timer runs only while they are logged in and that the command is theirs to run.
78
78
  - State the limits when they matter: the machine must be on; systemd and launchd
@@ -173,8 +173,8 @@ When one is absent:
173
173
 
174
174
  - **A legacy reviewer a flag asked for** is not dispatched and not replaced.
175
175
  Record it in `Skipped reviewers` with reason `not-installed` and the command
176
- below. `--legacy-profiles` and `--all` dispatch the legacy reviewers that are
177
- installed and record the others the same way. Leave an absent reviewer out of
176
+ below. `--legacy-profiles` dispatches the legacy reviewers that are
177
+ installed and records the others the same way; `--all` never selects them. Leave an absent reviewer out of
178
178
  the `Optional legacy/profile reviewers` preview: offering a flag that cannot
179
179
  run is worse than not offering it.
180
180
  - **`review-pr-feedback`** changes nothing about a round. No caller can declare
@@ -383,7 +383,7 @@ A review must be reproducible from its package: the same inputs give the same co
383
383
 
384
384
  **One memory read, and the log.** The stage query is `keryx memory search "<changed modules and the concepts they touch>" --status accepted`, once. Pass the same text to `keryx review start … --memory-query "<query>"` so the manifest's `cleanContext` block holds it next to the start time. keryx writes the orchestrator log, `<package>/tool-calls/orchestrator.jsonl`, on its own: `keryx review start` and `keryx review attach` mark the package active, and every `keryx memory` command and every MCP memory tool (`memory_search`, `memory_handoff`, `memory_propose`) appends one line naming its caller. You do not write that file and must not edit it; a hand-written line is a forgery the readiness item cannot tell apart from a record. A call by a `spawn_subagent` child is recorded under the child's reviewer id and is not judged by item 10. A call from a host that names no caller is recorded as `unattributed`: it warns in the item's detail line and never blocks. Reviewer logs stay in their own files.
385
385
 
386
- Item 10, `memory-outside-stage`, reads that log. Outside the stage is: a memory search with a different query, a search without `--status accepted`, any other memory command or MCP memory tool, and a stage call stamped before the round started. The same query repeated is the stage. Repair is to rerun from a session whose only memory read is the stage; only `keryx review complete <package> --waive memory-outside-stage "<operator quote>"` from a terminal lifts it. With no orchestrator log the item passes and its evidence says it could not see your reads; with a log that holds no memory call it passes and says so. Only calls that go through keryx are visible: a read through a tool keryx does not wrap is not in the log.
386
+ Item 10, `memory-outside-stage`, reads that log. Outside the stage is: a memory search with a different query, a search without `--status accepted`, any other memory command or MCP memory tool, and a stage call stamped before the round started. The same query repeated is the stage. Repair is to rerun from a session whose only memory read is the stage; only the operator's waiver lifts it: in keryx shell call `review_waive_readiness` (the operator answers a card, never a terminal), then run `keryx review complete <package>`; outside keryx shell `keryx review complete <package> --waive memory-outside-stage "<operator quote>"` in a terminal; unattended, record Not run. With no orchestrator log the item passes and its evidence says it could not see your reads; with a log that holds no memory call it passes and says so. Outside keryx shell, `keryx review start` prints a caller token (`export KERYX_REVIEW_CALLER_TOKEN=…`): export it on this session's keryx calls so they are attributed to you; without it the item reads "not observable on this host" rather than a clean pass. Only calls that go through keryx are visible: a read through a tool keryx does not wrap is not in the log.
387
387
 
388
388
  **Open questions.** One general rule, replacing per-fork wording: the answer is in this skill or `SKILL.detail.md`, or it is the user's. Interactive: one `AskUserQuestion`. Unattended: the whole output is one line, `STATUS: BLOCKED question — <the question>`, and you stop. A guess and a recollection are both wrong answers.
389
389
 
@@ -430,7 +430,7 @@ records them under `### How this review was run`.
430
430
  ```text
431
431
  Context: <PR or branch>, <N> files / <N> lines, <domains>; risk: <class>
432
432
 
433
- 1) Reviewers A) --all (default) B) tests only C) <domain> only D) choose
433
+ 1) Reviewers A) the `required --depth auto` set (default) B) --all C) tests only D) <domain> only E) choose
434
434
  2) Counterpart found: <endpoints, rules, paired PR — or "no link found">
435
435
  A) verify <backend|frontend> (default) B) skip — give a reason
436
436
  3) Models complexity: <low|medium|high>; <role>: <tier> → <model>, …
@@ -462,10 +462,12 @@ flow (`flow-orchestrator` Phase 3), a pre-merge or pre-ship review, a
462
462
  re-request after fixes. A gating round runs the composition the start questions
463
463
  selected.
464
464
 
465
- - **No dispatch tool, no review.** A host that cannot dispatch subagents — no
466
- spawn tool, or the depth budget spent — returns
467
- `STATUS: BLOCKED` with reason `nested_dispatch_unavailable` and no verdict.
468
- It does not review the diff alone and call that a round. Measured (flow 405):
465
+ - **No dispatch tool, no review.** A host with no spawn tool hands the round to
466
+ the autonomous runner (`keryx review run --autonomous --target pr --ref <pull
467
+ request URL>`, or `--target branch --ref <base> [--head <commit>]`), which
468
+ spawns the reviewers itself; a nested session whose depth budget is spent
469
+ returns `STATUS: BLOCKED` with reason `nested_dispatch_unavailable` and no
470
+ verdict. Neither reviews the diff alone and calls that a round. Measured (flow 405):
469
471
  a single-agent round on a 150-line frontend PR wrote "did not dispatch any
470
472
  sub-reviewers", approved it with two cosmetic minors, and the next human round
471
473
  filed six real findings, every one reachable from the diff and the backend at
@@ -649,10 +651,10 @@ certifies the flow is the one that checked the least.
649
651
 
650
652
  ## Readiness — the checklist behind `completed` and behind publishing
651
653
 
652
- `keryx review readiness <review-id-or-path> [--json]` reads the package and prints nine items, READY or NOT READY, each with the evidence and the command that repairs it. It writes nothing, and exits non-zero when the verdict is NOT READY. Only item 9 reaches GitHub (the PR's own file list); the TUI and `review status` read the package alone.
654
+ `keryx review readiness <review-id-or-path> [--json]` reads the package and prints ten items, READY or NOT READY, each with the evidence and the command that repairs it. It writes nothing, and exits non-zero when the verdict is NOT READY. Only item 9 reaches GitHub (the PR's own file list); the TUI and `review status` read the package alone.
653
655
 
654
656
  1. `files-covered` — every changed file is in a wave or carries a recorded waiver.
655
- 2. `blast-radius` — no file the blast radius cut is left unreviewed. A cut radius writes `chunks.json`; run `keryx review chunks plan`, `estimate`, `next`/`record`, `merge`. In an unattended run every wave runs without a question, up to the 25-pass ceiling; in an interactive run the operator is asked once.
657
+ 2. `blast-radius` — no file the blast radius cut is left unreviewed. A cut radius writes `chunks.json`; run `keryx review chunks plan`, `estimate`, `next`/`record`, `merge`. In an unattended run every wave runs without a question, up to the 25-pass ceiling; in an interactive run the operator is asked once: in keryx shell call `review_chunks_answer` (a card, never a terminal); elsewhere the operator runs `keryx review chunks estimate --package <dir> --answer yes --quote "<their words>"` in their terminal; unattended, record Not run.
656
658
  3. `reviewers` — every planned reviewer was dispatched and its result ingested.
657
659
  4. `verifier` — the verifier checked every finding it should, or the report says how many it did not.
658
660
  5. `ledger` — the ledger is READY.
@@ -660,8 +662,9 @@ certifies the flow is the one that checked the least.
660
662
  7. `head` — the report's head is the PR head.
661
663
  8. `report-names` — pending mutation candidates and parity warnings are named in the report.
662
664
  9. `pr-files` — the changed-file count equals the PR's own file list. A recount more than 3x the recorded scope is refused: it came from the wrong base. Recount with `keryx review blast-radius --package <ref> --changed <the PR's files>`.
665
+ 10. `memory-outside-stage` — the orchestrator read memory only in its memory stage ("Clean context" above). Outside keryx shell it reads "not observable on this host" unless this session's keryx calls carry the caller token `keryx review start` printed.
663
666
 
664
- `keryx review complete` refuses status `completed` while any item fails. A failing item is waived only by the operator, in a terminal: `keryx review complete --waive <item> "<their own words>"`, repeatable; the waiver is recorded with the quote and the time. `keryx review publish` is held by the same checklist. A not-ready package goes to GitHub only through `keryx review publish --partial`, which puts a "Partial review" header on the post and never approves. The shell guard stops a publish command that runs through the keryx shell; a publisher outside it (CI, another agent, a script file) is not stopped, and that limit is named, not hidden.
667
+ `keryx review complete` refuses status `completed` while any item fails. A failing item is waived only by the operator. In keryx shell call `review_waive_readiness` for the item: the operator answers it as a card, the waiver is recorded with the card text and the time, and then you run `keryx review complete`. Never send the operator to a terminal. Outside keryx shell the operator runs `keryx review complete --waive <item> "<their own words>"` in a terminal, repeatable. Unattended, record the item as Not run. `keryx review publish` is held by the same checklist. Publishing is the operator's decision too: in keryx shell call `review_publish` (a card shows the target and the body; never send the operator to a terminal). A not-ready package goes to GitHub only as a partial review (`review_publish` with `partial: true`, or `keryx review publish --partial`), which puts a "Partial review" header on the post and never approves. The shell guard stops a publish command that runs through the keryx shell; a publisher outside it (CI, another agent, a script file) is not stopped, and that limit is named, not hidden.
665
668
 
666
669
  ## Fix rounds — dispositions, regressions, withdrawal
667
670
 
@@ -756,9 +759,18 @@ and `.json`. A mutant is `killed` (the suite went red), `survived` (nothing pins
756
759
  behaviour), `timeout`, or `invalid` (anchor missing or not unique, file outside the copy).
757
760
  A survivor is evidence for the finding that names it.
758
761
 
759
- `keryx review complete` refuses while proposed mutants are unrun, unless
760
- `dispatch.mutationWaiver = { operatorQuote, reason }` carries the operator's own words.
761
- `keryx review run --autonomous` is unchanged: it records `Not run: mutation pass` as before.
762
+ `keryx review complete` refuses while proposed mutants are unrun on an attended round, unless the
763
+ operator waived the pass themselves: the `review_waive_mutation` card in keryx shell, or `keryx review
764
+ waive-mutation --package <review-dir> --reason "<why>"` in their terminal (it writes
765
+ `operator-decisions.json`; a `dispatch.mutationWaiver` quote you write is not enough on a package
766
+ `keryx review start` created). Attended is what the start recorded in `run-profile.json`: every round
767
+ except `review start --unattended` and `keryx review run --autonomous`, which record `Not run: mutation
768
+ pass` without asking.
769
+
770
+ These operator gates (cards, terminal-only commands, plan confirmation, decision records) keep a
771
+ cooperating agent from cutting corners and make every decision visible and attributable; they do not
772
+ stop a hostile same-user agent, which can fake a pty or write the JSON records itself (the TM-03/TM-04
773
+ boundary). The autonomous runner's mutation-gate exemption predates flow 457 and is kept on purpose.
762
774
 
763
775
  `keryx review mutate --cap <n>` runs the first `n` candidates and records `cap` and `not_reached`
764
776
  in `mutation-<n>.json`. The table then prints `Not run: N candidates` (and the report must carry
@@ -827,12 +839,13 @@ minute 119, and the PR merged first; the other re-dispatched a finished wave and
827
839
  41 slices. The operator can only act on a risk they were told about.
828
840
 
829
841
  1. On a PR target read merged / approved / CI (`gh pr view <n> --json state,reviewDecision,statusCheckRollup`). Say in one line what the state means for the round: an approved PR with green CI can merge any minute, and a merged PR makes the rest of the round a post-merge audit.
830
- 2. **Later, once the package and slices exist** (after `review scope`, `review start` and `review slice`, before the first reviewer starts): `keryx review required --mode all --scope .metaproject/data/review/scope.json --depth auto [--package <review-dir>]` prints the reviewer set and the depth it chose (see "Depth floor"); auto needs the file list and a line count, else the round is full. `keryx review estimate --package <review-dir> [--manifest <slices/manifest.json>]` prints assignments, waves and a duration range; the manifest defaults to `<package>/slices/manifest.json`, then `.metaproject/data/review/slices/manifest.json`.
842
+ 2. **Later, once the package and slices exist** (after `review scope`, `review start` and `review slice --package`, before the first reviewer starts): `keryx review required --mode all --scope .metaproject/data/review/scope.json --depth auto --package <review-dir>` prints the reviewer set and the depth it chose (see "Depth floor"); auto needs the file list and a line count, else the round is full. `keryx review estimate --package <review-dir> [--manifest <slices/manifest.json>]` prints assignments, waves and a duration range; the manifest defaults to `<package>/slices/manifest.json`, then `.metaproject/data/review/slices/manifest.json`.
831
843
  3. Show the operator depth + estimate and the merge risk before the first dispatch. Attended: let them choose `full` over a light default. Unattended: record both in the report and continue.
844
+ 4. **An over-threshold plan waits for the operator.** `estimate` flags a plan over `reviewPlan.maxDispatches` (default 40) or `reviewPlan.maxMinutes` (default 90) in `routing.config.json`; `dispatch-check --package` and `ledger build` refuse it (`REVIEWER_INPUT_PLAN_UNCONFIRMED`) until `<review-dir>/plan-confirmation.json` exists. Only the operator writes it: the `review_confirm_plan` card in keryx shell, or `keryx review confirm-plan --package <review-dir>` (or `keryx review estimate --package <review-dir> --confirm --quote "<their words>"`) in their terminal; both refuse without a TTY. Unattended, the plan is not dispatched: report the estimate and stop.
832
845
 
833
846
  ## Early verdict — the verdict is interim until `complete`
834
847
 
835
- As soon as every reviewer result and the verifier's are on disk, save each verifier pass to `research-evidence/verifications.json` first (without it a refuted finding still counts), then run `keryx review verdict <id|path> --raw .metaproject/data/review/raw [--json]` on the shell host, or without `--raw` where the round's raw results sit in `<package>/raw`. It prints the interim verdict and writes `verdict-interim.md` into the package. Give the operator that verdict in the same message; do not hold it for the mutation pass, the ledger or `complete`.
848
+ As soon as every reviewer result and the verifier's are on disk, save each verifier pass to `research-evidence/verifications.json` first (without it a refuted finding still counts), then run `keryx review verdict <id|path> [--json]`: it reads `<package>/raw`, where every host keeps the round's results (`--raw <dir>` only for results kept elsewhere; the shared `.metaproject/data/review/raw` mixes in other rounds' replies). It prints the interim verdict and writes `verdict-interim.md` into the package. Give the operator that verdict in the same message; do not hold it for the mutation pass, the ledger or `complete`.
836
849
 
837
850
  The tail keeps running after it: the mutation pass (`keryx review mutate`), `ledger build`, `ingest`, `readiness` and `keryx review complete`. Every managed round finishes them. The round is **not done** until `complete`: the verdict you gave is interim, and the report says so and names what the tail may still change (a surviving mutant, a verifier refutation, a late reviewer result). A light round has no mutation pass: its tail is `ledger build`, `ingest`, `readiness`, `complete`.
838
851
 
@@ -844,7 +857,7 @@ Do not wait for the last reviewer. When the first `blocker` or `major` finding a
844
857
 
845
858
  After a timeout, a crash or a resumed session, in this order:
846
859
 
847
- 1. **Collect.** `keryx review retry-plan --manifest <slices>/manifest.json --package <input-dir> --reviewer <id> --next-wave` reads `raw/` first and collects late reviewer results; it prints `collected late: N` and never re-hands a slice that is finished.
860
+ 1. **Collect.** `keryx review retry-plan --manifest <package>/slices/manifest.json --package <package> --reviewer <id> --next-wave` reads `<package>/raw/` first and collects late reviewer results; it prints `collected late: N` and never re-hands a slice that is finished.
848
861
  2. **Validate** each collected reply (a `STATUS:` line and a `REVIEW_RESULT` block matching `reviewer-finding.schema.json`), **ingest** it, and rebuild `research.json` with `ledger build`; run `ingest --research` once it prints READY.
849
862
  3. **Dispatch only what is missing:** the wave `retry-plan` prints. A wave whose results are on disk is never dispatched again.
850
863
  4. **A second timeout of the same reviewer changes the task, not only the diff size:** fewer slices per dispatch, a narrower focus (one concern or one directory), or a lighter model tier for a mechanical slice. Two results in a row with nothing new are `Not run`, as `retry-plan` prints.
@@ -852,3 +865,41 @@ After a timeout, a crash or a resumed session, in this order:
852
865
  ## dispatch-check on every payload
853
866
 
854
867
  Before any mass dispatch run `keryx review dispatch-check --payload <file> --manifest <dir>/manifest.json` on every payload, on every host. It fails `REVIEWER_INPUT_PATH_MISSING` when a `skill_path` or `context_doc` names a file that is not there, and still fails a payload that names no slice, exceeds the wave ceiling or breaks `reviewer-input.schema.json`. Fix the payload; a dead path costs a whole wave of refusals. Related: `review mutate` runs in a scratch copy under the repo and prints the baseline's tail when the baseline is red; `review start --graph-timeout <s>` bounds the graph build.
868
+
869
+ ## Review Plan Preview — legacy groups
870
+
871
+ ```text
872
+ Optional legacy/profile reviewers:
873
+ - code-ai-review: available via --code-ai or --legacy-profiles
874
+ - code-learned-review: available via --learned or --legacy-profiles
875
+ - code-style-review: available via --code-style or --legacy-profiles
876
+ - code-mobx-store-review: auto-suggest when *.store.ts or MobX patterns are present; available via --mobx-store or --legacy-profiles
877
+
878
+ Skipped reviewers:
879
+ - code-ai-review: profile reviewer, not selected unless --code-ai/--legacy-profiles
880
+ - code-learned-review: profile reviewer, not selected unless --learned/--legacy-profiles
881
+ - code-style-review: legacy style profile, not selected unless --code-style/--legacy-profiles
882
+ - code-mobx-store-review: not selected unless --mobx-store/--legacy-profiles or MobX store files are detected
883
+ ```
884
+
885
+ ## Token and context budget
886
+
887
+ The orchestrator owns the token budget. Sub-reviewers receive only the context their domain needs.
888
+
889
+ Budget rules:
890
+ - Compute a scope digest before dispatch: file list, diff stats, module map, and top risks.
891
+ - Send full diffs only for files relevant to each reviewer.
892
+ - For large files, send changed hunks plus nearby symbols first; include the full file only when path mode or the reviewer requires whole-file context.
893
+ - Never send generated files, lockfiles, snapshots, build output, or vendored code unless the reviewer is specifically about that file type.
894
+ - Cap each reviewer prompt with the `per_reviewer` budget when provided; otherwise use the smallest prompt that preserves evidence.
895
+ - Record omitted files and truncation in `review_context.token_policy.omissions`.
896
+ - If a reviewer returns `NEEDS_CONTEXT`, provide only the missing targeted context, not the entire repository.
897
+
898
+ Default budget guidance:
899
+
900
+ | Review size | Detection | Context mode | Dispatch style |
901
+ |---|---|---|---|
902
+ | small | <= 5 files and <= 300 changed lines | `light` | full relevant diff to selected reviewers |
903
+ | medium | <= 20 files or <= 2,000 changed lines | `light` | per-domain filtered diff |
904
+ | large | > 20 files or > 2,000 changed lines | ask `full` | staged waves by domain |
905
+ | high-risk | auth/API/core/security/data migrations | ask `full` | staged waves, verifier included |
@@ -24,18 +24,46 @@ license: "MIT"
24
24
 
25
25
  ## Preflight — before Step 0, in this order
26
26
 
27
- 1. **Can it spawn?** The round needs a subagent-spawn tool (`Agent`, `spawn_subagent` or the host's equivalent) and depth budget left: Claude Code nests subagents at most 3 deep, and at the limit `Agent` is withheld. If it cannot spawn, the whole output is this line, with no tool calls and no review of your own:
27
+ 1. **Can it spawn?** The round needs a subagent-spawn tool (`Agent`, `spawn_subagent` or the host's equivalent) and depth budget left: Claude Code nests subagents at most 3 deep, and at the limit `Agent` is withheld. A host with no spawn tool at all (Codex and other AGENTS.md hosts) does not stop here and does not review alone: it runs the round through the autonomous runner, `keryx review run --autonomous --target pr --ref <pull request URL>` (no pull request: `--target branch --ref <base> [--head <commit>]`), and relays its verdict and exit code (see "Hosts"). Only a session that has spent the depth budget (a nested subagent) stops; its whole output is this line, with no tool calls and no review of your own:
28
28
  `STATUS: BLOCKED nested_dispatch_unavailable — run this skill in the main session`
29
29
  2. **Is a round needed?** List the files with `git diff --name-status --no-renames <base>...HEAD`, so a rename shows both paths. A diff where EVERY file is prose or machine-written data ends here with one line, `round not needed: <reason>`, and no reviewer. Prose is exactly: README or CHANGELOG at any depth; CONTRIBUTING, LICENSE, SECURITY or CODE_OF_CONDUCT at the top level; and under `docs/`, `.metaproject/wiki`, `.metaproject/reviews` or a flow's `journal.md`, a file with extension md, rst or adoc whose name holds a lowercase letter (an all-uppercase name such as AGENT or WARP is steering by convention). Machine-written data is a file under `coverage/` or `__generated__/` with extension lcov, info or map only; json, txt, html and md there are code. Steering is code wherever it sits, `docs/` included: a directory (singular or plural) named agent, skill, command, rule, prompt, instruction, persona, steering or hook, any path segment starting with a dot (`.claude`, `.cursorrules`, `.mcp.json`), a file name starting with SKILL, AGENT, CLAUDE, GEMINI, QWEN, CODEX, WARP, CRUSH, JULES, KNOWLEDGE, GUIDELINES, CONVENTIONS or copilot-instructions, ending in .local, .override, .instructions, .prompt, .agent or .chatmode before the extension, or containing prompt, instruction or system. A flow's `description.md`, `acceptance-criteria.md` and `tasks/` are gate inputs, so they are code. Every other file counts as code too, including lockfiles, committed `dist/` output, CI, auth, migrations, config JSON, and any path with `..`, a backslash, a leading `./` or `//`. `classifyRoundNeed` in `src/review/round-need.ts` is the tested reference of this rule; the host executes this text, and nothing in the CLI calls the function yet. `--all`, an explicit reviewer list or a gating round forces a round.
30
30
  3. **Round ceiling.** 5 rounds (`REVIEW_ROUND_CAP`) over the same `merge-base..HEAD`. Past it: interactive, ask the operator; unattended, `STATUS: BLOCKED round_cap`.
31
- 4. **PR state, shown before dispatch.** On a PR target read its state (merged, approved, CI) and tell the operator the risk that it merges before the round ends: a verdict ready at minute 55 and held to minute 119 lost the race to the merge. Depth and the estimate need the package and the slice manifest, so they come later: after `review scope`, `review start` and `review slice`, before the first reviewer starts (Step 6; `SKILL.detail.md` § "Preflight 4").
31
+ 4. **PR state, shown before dispatch.** On a PR target read its state (merged, approved, CI) and tell the operator the risk that it merges before the round ends: a verdict ready at minute 55 and held to minute 119 lost the race to the merge. Depth and the estimate need the package and the slice manifest, so they come later: after `review scope`, `review start` and `review slice`, before the first reviewer starts ("Round plan" steps 5-6; `SKILL.detail.md` § "Preflight 4").
32
+
33
+ ## Hosts — the same round, different tools
34
+
35
+ | | keryx shell | Claude Code | Codex, other AGENTS.md hosts | autonomous runner |
36
+ |---|---|---|---|---|
37
+ | Spawn | `spawn_subagent` | `Agent` (`subagent_type`) | its spawn tool; none → the runner (Preflight 1) | its own reviewers |
38
+ | Plan | `plan_set`, `plan_update` | its todo/task list | its plan tool (`update_plan`), else the checklist in your reply | none |
39
+ | Question | `ask_user` | `AskUserQuestion` | one question in your reply, then wait | none: defaults, unattended |
40
+ | Operator decision | the card tool | the exact terminal command, for the operator | the exact terminal command, for the operator | refused: recorded Not run |
41
+ | Result file | `save_result_to: <package>/raw/<reviewer>.txt` | the reviewer writes `<package>/raw/<reviewer>.txt` itself, before it replies | as Claude Code | its own package |
42
+ | Wave bytes | `--host keryx-shell` (60000: a child dies at 5 min) | `--host claude-code` (150000) | `--host codex` / `--host other` (150000) | its own |
43
+
44
+ - **Every host keeps the same files, per package** (`<package>` is the directory `keryx review start` created): slices in `<package>/slices/`, one reply per dispatch in `<package>/raw/<reviewer>.txt` (reviewer replies only), one driver per dispatch in `<package>/drivers/<reviewer>.json`, and `research.json` from `keryx review ledger build --package <package>` — never by hand. `retry-plan --package`, `verdict` and `ledger build` read `<package>/raw` by default; the shared `.metaproject/data/review/raw` holds other rounds' replies and is read only when named, so never write there.
45
+ - **The result is the file, never the spawn tool's output.** keryx shell cuts a child's summary at 16000 characters and Claude Code returns only the last message: parse `<package>/raw/<reviewer>.txt`. A missing file is no result (`BLOCKED`).
46
+ - **Caller token.** `keryx review start` prints `export KERYX_REVIEW_CALLER_TOKEN=…`; outside keryx shell export it on this session's keryx calls, or readiness item 10 reads "not observable on this host".
47
+ - **Operator decisions.** In keryx shell the operator answers a card: `review_confirm_plan`, `review_waive_readiness`, `review_waive_coverage`, `review_waive_mutation`, `review_override_reviewers`, `review_chunks_answer`, `review_publish`, `review_comments_reply`. Elsewhere give the operator the exact command to run in their own terminal — `keryx review confirm-plan --package <package>`, `keryx review complete <package> --waive <item> "<their words>"` or `--waive-coverage "<their words>"`, `keryx review waive-mutation --package <package> --reason "<why>"`, `keryx review override-reviewers --package <package> --reason "<why>"`, `keryx review chunks estimate --package <package> --answer yes --quote "<their words>"`, `keryx review publish <package>`, `keryx review comments reply … --final` — and wait. You never run `--mode automatic` or `--yes`, and never write an operator quote yourself; unattended, the item is Not run.
48
+
49
+ ## Round plan — the order on every host
50
+
51
+ 1. `keryx review scope --ref <base> --json > .metaproject/data/review/scope.json` (Step 3).
52
+ 2. `keryx review start --target <kind> --ref <ref> --scope .metaproject/data/review/scope.json --memory-query "<query>" --host <host>`, plus `--unattended` when nobody is at the keyboard.
53
+ 3. `keryx review blast-radius --ref <base> --package <package> --json > blast-radius.json` (Step 3b). Reasoned omissions (`unrelated-symbol`, `hub-damped`, `text-hub`, `test-consumer`) are context, not scope; only cap drops become chunks. Run the tests in `<package>/tests-to-run.json` with the command the radius prints, instead of reading them, and hand the output to the reviewers as context.
54
+ 4. `keryx review slice --ref <base> --package <package> --host <host>`: each slice carries `roles`, `depth` (deep or light) and `audit`.
55
+ 5. `keryx review required --mode all --scope .metaproject/data/review/scope.json --depth auto --package <package>`.
56
+ 6. `keryx review estimate --package <package>`. Over threshold, the operator confirms (card `review_confirm_plan`, or `keryx review confirm-plan --package <package>` in their terminal); `dispatch-check` and `ledger build` refuse an unconfirmed plan, so never dispatch one. Unattended: report the estimate and stop with `STATUS: BLOCKED plan_over_threshold`.
57
+ 7. Dispatch by slice roles: each role gets the slices that name it; a deep slice names every roster role its path gates allow, an `audit` one the whole core set. A major or worse on a light slice comes back from `retry-plan` as an escalation: dispatch the core roles on it.
58
+ 8. Verifier on the first blocker or major (Step 10). It runs no command itself: run `keryx review verify-run --package <package> --finding <id> -- <command>` and hand it the saved `research-evidence/verify-<id>-<n>.txt` as context.
59
+ 9. `keryx review verdict <package>` to the operator, then the tail: mutation pass, `ledger build`, `ingest`, `readiness`, `complete`.
32
60
 
33
61
  ## Clean context — the round starts empty
34
62
 
35
63
  Context comes only from your own context stages: scope, the Review Context Pack, the memory stage and the layers. Your session memory, the auto-memory index (`MEMORY.md`), remembered feedback and the earlier conversation are not inputs: do not read, quote or apply them.
36
64
  - **Accepted exceptions, the only three:** `prior_findings` of an earlier round of the same package; review-notes and learning that reach you through the memory stage; a reviewer's own `memory_search`.
37
- - **One memory read.** Run the stage query once and record it with `keryx review start … --memory-query "<query>"`. keryx writes `<package>/tool-calls/orchestrator.jsonl` itself from the moment the package starts: never create, edit or append to it. Readiness item 10, `memory-outside-stage`, blocks `review complete` and `review publish` on any other memory call; only `--waive memory-outside-stage "<operator quote>"` from a terminal lifts it. Detail: `SKILL.detail.md` § "Clean context".
38
- - **Open question.** Look in this skill and `SKILL.detail.md` first. If the answer is not there, ask the user: one `AskUserQuestion` interactively; unattended, the whole output is `STATUS: BLOCKED question — <the question>`. Never answer from memory or a guess.
65
+ - **One memory read.** Run the stage query once and record it with `keryx review start … --memory-query "<query>"`. keryx writes `<package>/tool-calls/orchestrator.jsonl` itself from the moment the package starts: never create, edit or append to it. Readiness item 10, `memory-outside-stage`, blocks `review complete` and `review publish` on any other memory call; only the operator's waiver lifts it: in keryx shell call `review_waive_readiness` (a card, never a terminal), outside it `--waive memory-outside-stage "<operator quote>"` in a terminal; unattended, record it as Not run. Detail: `SKILL.detail.md` § "Clean context".
66
+ - **Open question.** Look in this skill and `SKILL.detail.md` first. If the answer is not there, ask the user: one question with the host's question tool (`AskUserQuestion`, `ask_user`; "Hosts") interactively; unattended, the whole output is `STATUS: BLOCKED question — <the question>`. Never answer from memory or a guess.
39
67
  - **Named limit.** keryx cannot stop a runtime that loads the agent's memory before this skill starts, and reads of personal memory outside `keryx memory` are not visible to the log. The guarantee is instruction plus detection, not isolation.
40
68
 
41
69
  ## Workflow
@@ -51,7 +79,7 @@ Review Orchestrator Progress:
51
79
  - [ ] Step 4: Parse flags / auto-detect domain from scope
52
80
  - [ ] Step 5: Start questions, once, after Steps 1 and 3 — reviewers (default: the set `keryx review required --depth auto` prints; `--all` only when the operator asks), counterpart verification (default on), model plan; unattended — and an `auto` or `trust` shell session, which has no operator at the keyboard — takes the defaults (SKILL.detail.md § "Start questions"; legacy/profile reviewers stay flag-only)
53
81
  - [ ] Step 5c: When `review.jev.select` is on, run `keryx review jev-select` over the finalized candidate set and drop its `skip` decisions from Wave A/B — see "Reviewer selection with Jev"
54
- - [ ] Step 6: Plan sub-agent dispatch and token budgets; compute each dispatch's model with `keryx review tier`; after `review scope`, `review start` and `review slice`, and before the first reviewer starts, run `keryx review required --mode all --scope .metaproject/data/review/scope.json --depth auto [--package <review-dir>]` (auto needs the file list AND a line count, which `--scope` gives; without both the round is full) and `keryx review estimate --package <review-dir> [--manifest <slices/manifest.json>]`, and show the operator depth, estimate and merge risk (6a: publish this plan)
82
+ - [ ] Step 6: Plan sub-agent dispatch and token budgets; compute each dispatch's model with `keryx review tier`; after `review scope`, `review start` and `review slice`, and before the first reviewer starts, run `keryx review required --mode all --scope .metaproject/data/review/scope.json --depth auto --package <package>` (auto needs the file list AND a line count, which `--scope` gives; without both the round is full) and `keryx review estimate --package <package>`, show the operator depth, estimate and merge risk, and get an over-threshold plan confirmed before any dispatch ("Round plan" 5-6; 6a: publish this plan)
55
83
  - [ ] Step 7: Stage 1 gate - spec compliance check (if issue/task provided)
56
84
  - [ ] Step 8: Dispatch selected reviewers in parallel with the reviewer-input schema — see "How to spawn"
57
85
  - [ ] Step 9: Collect reviewer-finding schema results and handle NEEDS_CONTEXT
@@ -60,7 +88,7 @@ Review Orchestrator Progress:
60
88
  - [ ] Step 11: Sort by severity, deduplicate, emit unified report
61
89
  - [ ] Step 12: Emit the machine-readable `keryx:findings` block alongside the report
62
90
  - [ ] Step 13: Report the stage counts: dropped by pre-filter, refuted by the verifier, retained
63
- - [ ] Step 14: Managed rounds only (not `lightweight`, which is report-only), after the final round, on an open pull request at the head you reviewed — answer every external comment once with `keryx review comments reply --final` (render with `--dry-run` and get the user's explicit approval first; unattended: do not publish, hand the rendered body back) — never against a pull request the dispatch named as the caller's
91
+ - [ ] Step 14: Managed rounds only (not `lightweight`, which is report-only), after the final round, on an open pull request at the head you reviewed — answer every external comment once with `keryx review comments reply --final` (render with `--dry-run`; the post itself is the operator's: the `review_comments_reply` card in keryx shell, else they run it in their terminal; unattended: do not publish, hand the rendered body back) — never against a pull request the dispatch named as the caller's
64
92
  ```
65
93
 
66
94
  Step 0 runs on **every** round. Step 14 runs **once**, after the last one. They are two commands for that reason: a caller that already runs collection per round would carry the posting along with it, and the reviewer would get six replies to one comment.
@@ -73,37 +101,37 @@ It names a model only if you gave it your session (`--session-provider`/`--sessi
73
101
 
74
102
  ### Step 6a — the plan bridge
75
103
 
76
- Publish this checklist with `plan_set` under the ids `step-0`…`step-14` once, here, when scope and dispatch are fixed (a shell host already published it as `pending` at the start — update it with `plan_update` instead). Publish each of steps 0–5 that ran as `completed` and each that did not run as `skipped`; publish steps 6–14 as `pending` (`in_progress` for the one starting now). From then on move each item with `plan_update` as its step completes — see the `session-plan-bridge` rule. `completed` means the step's artifact exists: steps 8–10 become `completed` (in the plan and in the report) only after `keryx review ingest` has recorded their results, and a step whose reviewers were `BLOCKED` is `blocked`.
104
+ Publish this checklist with `plan_set` under the ids `step-0`…`step-14` once, here, when scope and dispatch are fixed (keryx shell already published it as `pending` at the start — update it with `plan_update` instead; another host uses its own plan tool, "Hosts"). Publish each of steps 0–5 that ran as `completed` and each that did not run as `skipped`; publish steps 6–14 as `pending` (`in_progress` for the one starting now). From then on move each item with `plan_update` as its step completes — see the `session-plan-bridge` rule. `completed` means the step's artifact exists: steps 8–10 become `completed` (in the plan and in the report) only after `keryx review ingest` has recorded their results, and a step whose reviewers were `BLOCKED` is `blocked`.
77
105
 
78
106
  ## How to spawn (Steps 8 and 10)
79
107
 
80
108
  - One `Agent` call per reviewer, all calls of a wave in a single message, so the wave runs in parallel.
81
109
  - Use the reviewer's exact `subagent_type`. If the runtime has no such type, use `general-purpose` and name the reviewer's `SKILL.md` path in the prompt. Neither available: that reviewer is `BLOCKED`.
82
- - Prompts carry pointers, not pasted files — the diff, the SHAs, the PR, the schema paths — and never paraphrase a contract. On a shell host the rule below replaces this one. Run `keryx review dispatch-check --payload <file> --manifest <dir>/manifest.json` on every payload before a mass dispatch; it fails `REVIEWER_INPUT_PATH_MISSING` on a missing `skill_path` or `context_doc`.
110
+ - Prompts carry pointers, not pasted files — the diff, the SHAs, the PR, the schema paths — and never paraphrase a contract. On keryx shell the slices travel in `context_files` instead (below). Run `keryx review dispatch-check --payload <file> --manifest <package>/slices/manifest.json --package <package>` on every payload before a mass dispatch; it fails `REVIEWER_INPUT_PATH_MISSING` on a missing `skill_path` or `context_doc`.
83
111
  - A reply is a result only if it starts with a `STATUS:` line and carries a `REVIEW_RESULT` block matching `reviewer-finding.schema.json`; anything else is `BLOCKED`. The one exception: a reply or dispatch error of `STATUS: RATE_LIMITED` is handled by the Rate limits rule under Control, never marked `BLOCKED`.
84
112
  - The model comes from `keryx review tier` (Step 6). On `spawn_subagent` pass its tier as `model_tier` on every reviewer — `review-verifier` and simple reviewers `light`, normal `standard`, complex `deep`; an omitted tier runs the child on your own model.
85
113
 
86
- ## Shell host (keryx shell, or any host with no file-reading subagent) — mandatory
114
+ ## Slices, waves and results — every host; keryx shell specifics marked
87
115
 
88
- A reviewer on this host starts in the project root and can read project files, but a path outside the project root is unreadable to it, and `context_files` content reaches it wrapped as DATA. The one rule: slices and measurements travel only in `spawn_subagent`'s `context_files` (never pasted or retyped); `skill_path` and `pr_context` stay payload fields. The text reaches the reviewer in slices small enough to finish.
116
+ A reviewer starts in the project root and reads project files; a path outside the project root is unreadable to it. On keryx shell its `context_files` content reaches it wrapped as DATA, and slices and measurements travel only there (never pasted or retyped); elsewhere the prompt names the slice paths. `skill_path` and `pr_context` stay payload fields. The text reaches the reviewer in slices small enough to finish.
89
117
 
90
- 0. **Publish the plan before anything else.** Your first tool call is `plan_set` with `step-0`…`step-14` all `pending`. A shell turn that has open plan items is continued by the host; a turn that ends on a text reply is over, and a message saying the review "cannot be honestly completed" ends the run with nothing reviewed. Never end the turn while a step is `pending` or `in_progress`: mark it `blocked` only with a cause you cannot fix with a command, and otherwise keep working. Step 6a then updates this plan rather than creating it.
91
- 1. **Slice first.** `keryx review slice --ref <base> --out <dir>` after `keryx review scope`. It writes `slice-NN.diff` files of at most 150000 bytes each and a `manifest.json` carrying a wave budget (`--wave-bytes`, default 60000 and never below the slice ceiling): what ONE reviewer dispatch may total, so a role owning many slices is dispatched in slice waves that each fit the child deadline. On a large diff pass a small `--max-bytes` (for example 18000) and `--wave-bytes` (for example 54000) so no wave outruns a reviewer's five minutes; ledgers (csv, lockfiles, snapshots, generated files) are listed under `omissions` with a reason — list them in the report as not reviewed, never as clean.
92
- 2. **Slices travel by `context_files`, and only by `context_files`.** Write the slices under the project (the default `--out`, `.metaproject/data/review/slices`, already is) and pass each reviewer its slice paths in `spawn_subagent`'s `context_files`: the host copies the text into that reviewer's task, so you never paste or retype a slice. Put the `slices` ids in its reviewer-input. A dispatch that names a git range but no slice and no `context_files` leaves the reviewer with nothing to read — that is `BLOCKED`, not a review.
93
- 3. **Check before dispatch, every payload.** `keryx review dispatch-check --payload <file> --manifest <dir>/manifest.json` for the wave; exit 1 means a payload names no slice, is over the wave ceiling (dispatch fewer slices, then the next wave), names a `skill_path` or `context_doc` that does not exist (`REVIEWER_INPUT_PATH_MISSING`), or breaks `reviewer-input.schema.json`. Fix it; do not dispatch around it.
94
- 4. **One batch.** Dispatch the whole wave — up to 10 reviewers — in one message, not one at a time. Give each reviewer `max_rounds` of at least 40 (the default); a reviewer that spends its rounds reading the skill and the schema returns nothing. **Incomplete means the next wave while it makes progress, Not run when it stalls.** Every reviewer result lists the `slices` it covered, in its `REVIEW_RESULT` object, as ids (`"slices": ["slice-01", "slice-02"]`); an object with an `id` and an optional `coverage` note (`{"id": "slice-01", "coverage": "read in full"}`) is read the same way. An object with a `status` counts only when it is DONE, COMPLETE, COVERED or OK (any case); a slice marked NOT_READ, INCOMPLETE, SKIPPED or PARTIAL is not covered, so give a slice you did not read no `status` of that kind and leave it out. An entry with no id counts for nothing. **One driver file per dispatch.** Write `drivers/<reviewer>.json` next to `raw/` with `{"reviewer": "<name>", "slices": [<ids dispatched>], "result": "raw/<reviewer>.txt", "definition": "<path of the reviewer's SKILL.md>"}`: `ledger build` takes the reviewer's rule file from `definition` (a `skill_path` there, or inside a `payload` object, is read too) and the slices it was handed from `slices`. Only reviewer replies belong in `raw/`; a verification payload or a note filed there is reported as set aside and left in place, but it still costs a step. On `INCOMPLETE`, `BLOCKED` or `NEEDS_CONTEXT` (never accepted as a finished scope), and on a `DONE` that left slices open, `keryx review retry-plan --manifest <dir>/manifest.json --result <raw/reviewer.txt>` decides and hands out the next wave of only the slices still uncovered (`--next-wave` plans one without a result). A result that covered a new slice spends no attempt; two results in a row with nothing new, or six failed rounds, print the `- **Not run:** …` line to paste into the report, and a result with nothing new first has its open slices cut in half. Dispatch each wave, save its reply, and run `retry-plan` again until it says `accepted`; after a crash or timeout run `keryx review retry-plan --manifest <slices>/manifest.json --package <input-dir> --reviewer <id> --next-wave` first, which collects late results from `<input-dir>/raw/` (per reviewer) and never re-hands a finished slice. Only the operator can accept a smaller set than that, with their own quote. An unfinished reviewer is never counted as a clean pass. **Persist every result.** Pass each reviewer `save_result_to: .metaproject/data/review/raw/<reviewer>.txt`: the host writes its full final text there, so `review complete` has raw evidence even after the transcript is cut. Build `research.json` with `keryx review ledger build <input-dir> --package <review-dir>` — never by hand: it reads the saved results, `scope-files.json` and the retry state, marks a reviewer complete only when its result is finished and its payloads cover the scope, and prints each gap with the exact next command and the waves left (a `retry-plan --next-wave` call, or a `--dispositions` entry for a raw finding). Run `keryx review ingest --research <review-dir>/research.json` once it prints READY (exit 0). A refused `review complete` names what is missing: fix that with a command and run it again; it is never `blocked`. `--head` wants a SHA (`git rev-parse HEAD`), not `HEAD`.
118
+ 0. **Publish the plan before anything else (keryx shell).** Your first tool call is `plan_set` with `step-0`…`step-14` all `pending`. A shell turn that has open plan items is continued by the host; a turn that ends on a text reply is over, and a message saying the review "cannot be honestly completed" ends the run with nothing reviewed. Never end the turn while a step is `pending` or `in_progress`: mark it `blocked` only with a cause you cannot fix with a command, and otherwise keep working. Step 6a then updates this plan rather than creating it.
119
+ 1. **Slice first.** `keryx review slice --ref <base> --package <package> --host <host>` after `keryx review start`. It writes `<package>/slices/slice-NN.diff` files of at most 150000 bytes each and a `manifest.json` carrying a wave budget (`--wave-bytes`, never below the slice ceiling; default by `--host`: 60000 on keryx-shell, 150000 elsewhere): what ONE reviewer dispatch may total, so a role owning many slices is dispatched in slice waves that each fit the child deadline. On keryx shell with a large diff pass a small `--max-bytes` (for example 18000) and `--wave-bytes` (for example 54000) so no wave outruns a reviewer's five minutes; ledgers (csv, lockfiles, snapshots, generated files) are listed under `omissions` with a reason — list them in the report as not reviewed, never as clean.
120
+ 2. **Slices travel by path; on keryx shell, only by `context_files`.** The slices sit under the project (`<package>/slices`). On keryx shell pass each reviewer its slice paths in `spawn_subagent`'s `context_files`: the host copies the text into that reviewer's task, so you never paste or retype a slice. Elsewhere name the slice paths in the prompt. Put the `slices` ids in its reviewer-input. A dispatch that names a git range but no slice leaves the reviewer with nothing to read — that is `BLOCKED`, not a review.
121
+ 3. **Check before dispatch, every payload.** `keryx review dispatch-check --payload <file> --manifest <package>/slices/manifest.json --package <package>` for the wave; exit 1 means a payload names no slice, is over the wave ceiling (dispatch fewer slices, then the next wave), names a `skill_path` or `context_doc` that does not exist (`REVIEWER_INPUT_PATH_MISSING`), belongs to an unconfirmed over-threshold plan (`REVIEWER_INPUT_PLAN_UNCONFIRMED`), or breaks `reviewer-input.schema.json`. Fix it; do not dispatch around it.
122
+ 4. **One batch.** Dispatch the whole wave — up to 10 reviewers — in one message, not one at a time. On keryx shell every round reviewer's `spawn_subagent` carries `review_wave: {"package": "<package>", "pass": "round"}` (a wave outside it counts against the turn's 32 children) and a `timeout_ms` that fits its wave: the default 300000 is sized for a wave of 60000 bytes, so a larger `--wave-bytes` needs a larger `timeout_ms`. Give each reviewer `max_rounds` of at least 40 (the default); a reviewer that spends its rounds reading the skill and the schema returns nothing. **Incomplete means the next wave while it makes progress, Not run when it stalls.** Every reviewer result lists the `slices` it covered, in its `REVIEW_RESULT` object, as ids (`"slices": ["slice-01", "slice-02"]`); an object with an `id` and an optional `coverage` note (`{"id": "slice-01", "coverage": "read in full"}`) is read the same way. An object with a `status` counts only when it is DONE, COMPLETE, COVERED or OK (any case); a slice marked NOT_READ, INCOMPLETE, SKIPPED or PARTIAL is not covered, so give a slice you did not read no `status` of that kind and leave it out. An entry with no id counts for nothing. **One driver file per dispatch.** Write `<package>/drivers/<reviewer>.json` with `{"reviewer": "<name>", "slices": [<ids dispatched>], "result": "raw/<reviewer>.txt", "definition": "<path of the reviewer's SKILL.md>"}`: `ledger build` takes the reviewer's rule file from `definition` (a `skill_path` there, or inside a `payload` object, is read too) and the slices it was handed from `slices`. Only reviewer replies belong in `raw/`; a verification payload or a note filed there is reported as set aside and left in place, but it still costs a step. On `INCOMPLETE`, `BLOCKED` or `NEEDS_CONTEXT` (never accepted as a finished scope), and on a `DONE` that left slices open, `keryx review retry-plan --manifest <package>/slices/manifest.json --result <package>/raw/<reviewer>.txt --package <package>` decides and hands out the next wave of only the slices still uncovered (`--next-wave` plans one without a result). A result that covered a new slice spends no attempt; two results in a row with nothing new, or six failed rounds, print the `- **Not run:** …` line to paste into the report, and a result with nothing new first has its open slices cut in half. Dispatch each wave, save its reply, and run `retry-plan` again until it says `accepted`; after a crash or timeout run `keryx review retry-plan --manifest <package>/slices/manifest.json --package <package> --reviewer <id> --next-wave` first, which collects late results from `<package>/raw/` (per reviewer) and never re-hands a finished slice. Only the operator can accept a smaller set than that (`review_override_reviewers`, or `keryx review override-reviewers` in their terminal). An unfinished reviewer is never counted as a clean pass. **Persist every result, on every host.** On keryx shell pass each reviewer `save_result_to: <package>/raw/<reviewer>.txt`: the host writes its full final text there. Elsewhere the prompt tells the reviewer to write its whole reply to `<package>/raw/<reviewer>.txt` before it answers. Either way `review complete` has raw evidence after the transcript is cut, and you parse that file, never the tool output. Build `research.json` with `keryx review ledger build --package <package>` — never by hand: it reads the saved results, `scope-files.json` and the retry state, marks a reviewer complete only when its result is finished and its payloads cover the scope, and prints each gap with the exact next command and the waves left (a `retry-plan --next-wave` call, or a `--dispositions` entry for a raw finding). Run `keryx review ingest --research <package>/research.json` once it prints READY (exit 0). A refused `review complete` names what is missing: fix that with a command and run it again; it is never `blocked`. `--head` wants a SHA (`git rev-parse HEAD`), not `HEAD`.
95
123
  5. **Never print a scope or a slice.** The tool output of one call is cut at about 20 KB and `keryx review scope` of a large PR prints hundreds of KB. Run it as `keryx review scope --ref <base> --json > .metaproject/data/review/scope.json` and read the counts from the file (`keryx ctx read`, or `jq`); take `<base>` from `git symbolic-ref refs/remotes/origin/HEAD`, not a guessed `origin/main`. Create `.metaproject/data/review/` first (`mkdir -p`) — a missing directory is a command to run, not a blocker. **Machine artifacts by direct write.** Save slices, payloads and results by redirection or `keryx ctx run --raw -- <command>`; the summarized form of `keryx ctx run` drops the bytes a later step parses.
96
124
  6. **Write mutants whole.** A testing reviewer's `mutant_candidates` cover every changed branch, code downstream of the diff included. Write them to `research-evidence/mutants.json` unchanged; you may not narrow them, and a candidate no pass has run still blocks the coverage gate (`SKILL.detail.md`, mutation pass).
97
- 7. **Measure before `review-layout`.** Run the browser measurements (Playwright/Storybook) yourself and pass them as `context_files` in its first dispatch; they count as evidence when the command and viewport are named. A second `NEEDS_CONTEXT` from one role is Not run (`layout: no engine`), not another refill (`SKILL.detail.md`).
125
+ 7. **Measure before `review-layout`.** Run the browser measurements (Playwright/Storybook) yourself and pass them in its first dispatch (`context_files` on keryx shell, by path elsewhere); they count as evidence when the command and viewport are named. A second `NEEDS_CONTEXT` from one role is Not run (`layout: no engine`), not another refill (`SKILL.detail.md`).
98
126
 
99
127
  ## Control
100
128
 
101
129
  - **Profiles.** The default is read-only: one round, no edits. `--fix` hands the findings to `flow-orchestrator` or `task-implementer`; the orchestrator never edits code. Control returns to this main session after each hand-off, which loads this skill again (never the implementer, which would nest); each re-review counts toward the ceiling (it adds 1 to the count).
102
130
  - **Waves.** Split by diff domain (`core` = logic, security, architecture; `domain` = frontend, backend, conventions; `support` = style, tests), in that order; A/B/C below keep their dependency order, except Wave C, which starts early and overlaps A and B (see Wave C). A full or `--all` round runs at most 10 reviewers per wave (`MAX_REVIEWERS_PER_WAVE_FULL`).
103
131
  - **Rate limits.** On a reviewer dispatch that fails with a rate-limit or overloaded error (reported as `STATUS: RATE_LIMITED`, a harness signal, not a schema status), halve the wave, requeue the rest and honour `retry-after`, at most 300 seconds (`MAX_RETRY_AFTER_SECONDS`). After at most two halvings (`MAX_RATE_HALVINGS`) stop with `STATUS: BLOCKED rate_limited`, naming the queue left.
104
- - **Early verdict.** When every reviewer and the verifier are done, save every verifier pass to `research-evidence/verifications.json`, then run `keryx review verdict <id|path> --raw .metaproject/data/review/raw` (the raw dir this round uses; the default is `<package>/raw`) and give the operator the verdict then; refuted findings are excluded only once `verifications.json` exists. The mutation pass, ledger and `keryx review complete` continue as the tail, and every managed round finishes them. The verdict is interim: the round is not done until `complete` (`SKILL.detail.md` § "Early verdict").
105
- - **Recovery.** After a timeout or crash: collect, validate, ingest, then dispatch only what is missing with `keryx review retry-plan --manifest <slices>/manifest.json --package <input-dir> --reviewer <id> --next-wave` (it prints `collected late: N`). Never re-dispatch a wave whose results are on disk; after a second timeout of the same reviewer change the task (fewer slices, narrower focus), not only the diff size (`SKILL.detail.md` § "Recovery").
106
- - **Done when** the consolidated report and the `keryx:findings` block are emitted and the stage counts and **Not run** are stated. A managed round (not `lightweight`, which is report-only) is also done only when every dispatched reviewer's result is recorded with `keryx review ingest` and `keryx review complete` has run. `completed` means `keryx review readiness` passed, or each failing item is waived by the operator in a terminal (`keryx review complete --waive <item> "<their words>"`); publishing to GitHub is held by the same checklist, and a not-ready package goes out only through `keryx review publish --partial`.
132
+ - **Early verdict.** When every reviewer and the verifier are done, save every verifier pass to `research-evidence/verifications.json`, then run `keryx review verdict <package>` (it reads `<package>/raw`; the shared `.metaproject/data/review/raw` only when passed as `--raw`, which mixes in other rounds) and give the operator the verdict then; refuted findings are excluded only once `verifications.json` exists. The mutation pass, ledger and `keryx review complete` continue as the tail, and every managed round finishes them. The verdict is interim: the round is not done until `complete` (`SKILL.detail.md` § "Early verdict").
133
+ - **Recovery.** After a timeout or crash: collect, validate, ingest, then dispatch only what is missing with `keryx review retry-plan --manifest <package>/slices/manifest.json --package <package> --reviewer <id> --next-wave` (it prints `collected late: N`). Never re-dispatch a wave whose results are on disk; after a second timeout of the same reviewer change the task (fewer slices, narrower focus), not only the diff size (`SKILL.detail.md` § "Recovery").
134
+ - **Done when** the consolidated report and the `keryx:findings` block are emitted and the stage counts and **Not run** are stated. A managed round (not `lightweight`, which is report-only) is also done only when every dispatched reviewer's result is recorded with `keryx review ingest` and `keryx review complete` has run. `completed` means `keryx review readiness` passed, or each failing item is waived by the operator: in keryx shell call `review_waive_readiness` (a card, never a terminal), then run `keryx review complete`; elsewhere the operator runs `keryx review complete <package> --waive <item> "<their words>"` in their terminal; unattended, record Not run; publishing to GitHub is held by the same checklist, and a not-ready package goes out only through `keryx review publish --partial` (in keryx shell: `review_publish` with `partial: true`).
107
135
 
108
136
  ---
109
137
 
@@ -645,25 +673,7 @@ Unattended: take A and record it in the review scope.
645
673
 
646
674
  ## Token and Context Budget Management
647
675
 
648
- The orchestrator owns token budget. Sub-reviewers should receive only the context needed for their domain.
649
-
650
- Budget rules:
651
- - Compute a scope digest before dispatch: file list, diff stats, module map, and top risks.
652
- - Send full diffs only for files relevant to each reviewer.
653
- - For large files, send changed hunks plus nearby symbols first; include full file only when path mode or the reviewer requires whole-file context.
654
- - Never send generated files, lockfiles, snapshots, build output, or vendored code unless the reviewer is specifically about that file type.
655
- - Cap each reviewer prompt with `per_reviewer` budget when provided; otherwise use the smallest prompt that preserves evidence.
656
- - Record omitted files and truncation in `review_context.token_policy.omissions`.
657
- - If a reviewer returns `NEEDS_CONTEXT`, provide only the missing targeted context, not the entire repository.
658
-
659
- Default budget guidance:
660
-
661
- | Review size | Detection | Context mode | Dispatch style |
662
- |---|---|---|---|
663
- | small | <= 5 files and <= 300 changed lines | `light` | full relevant diff to selected reviewers |
664
- | medium | <= 20 files or <= 2,000 changed lines | `light` | per-domain filtered diff |
665
- | large | > 20 files or > 2,000 changed lines | ask `full` | staged waves by domain |
666
- | high-risk | auth/API/core/security/data migrations | ask `full` | staged waves, verifier included |
676
+ The orchestrator owns the token budget: each reviewer gets only the context its domain needs (its slices), never generated files, lockfiles, snapshots, build output or vendored code unless it reviews that file type; omitted files and truncation go in `review_context.token_policy.omissions`, and a `NEEDS_CONTEXT` gets only the missing targeted context. The budget rules and the size table: `SKILL.detail.md` § "Token and context budget".
667
677
 
668
678
  ---
669
679
 
@@ -819,7 +829,7 @@ keryx review blast-radius --ref "${BASE_SHA}" --package "${PKG}" --previous blas
819
829
  ```
820
830
 
821
831
  `PKG` is the review package, created by `keryx review start` before dispatch; `--package` makes scope B read the graph built at the PR head. Give each reviewer `keryx review layers block --package "${PKG}" --files <its slice files> --json` as the payload's `layers` field.
822
- When `dropped` is non-empty, run `keryx review chunks plan|estimate|next|record|merge --package "${PKG}"` until `next` has none left. `review complete` refuses while a file is uncovered; `--waive-coverage "<operator quote>"` takes the operator's own words only, never yours. Pass `review_wave: {"package": "${PKG}", "pass": "<chunk id>"}` on every `spawn_subagent` of a chunk pass (`"pass": "round"` for the round's own reviewers): each wave counts its own 32 children and one turn opens at most 25 waves; a wave the package does not record is ignored and counts against the turn's 32.
832
+ When `dropped` is non-empty, run `keryx review chunks plan|estimate|next|record|merge --package "${PKG}"` until `next` has none left. `review complete` refuses while a file is uncovered; the coverage waiver is the operator's alone, never yours: in keryx shell call `review_waive_coverage` (a card, never a terminal), then run `keryx review complete`; elsewhere the operator runs `keryx review complete <package> --waive-coverage "<their words>"`; unattended, record Not run. The chunk-run question (`estimate`) is answered the same way: `review_chunks_answer`, or the operator's `keryx review chunks estimate --package "${PKG}" --answer yes --quote "<their words>"`. Pass `review_wave: {"package": "${PKG}", "pass": "<chunk id>"}` on every `spawn_subagent` of a chunk pass (`"pass": "round"` for the round's own reviewers): each wave counts its own 32 children and one turn opens at most 25 waves; a wave the package does not record is ignored and counts against the turn's 32.
823
833
  The measurements, the rule table and the recompute table: `SKILL.detail.md` § "Scope B — bounds, rejections and recompute".
824
834
 
825
835
  ### Path Mode
@@ -977,10 +987,10 @@ author's routing note, not a restriction.
977
987
  ### Start questions (Step 5)
978
988
 
979
989
  Ask once, after the context pack (Step 1) and the scope (Step 3), in one interaction — one
980
- `AskUserQuestion` call on Claude Code, the lettered prompt in SKILL.detail.md elsewhere:
990
+ `AskUserQuestion` call on Claude Code, one `ask_user` in keryx shell, the lettered prompt in SKILL.detail.md elsewhere:
981
991
 
982
992
  1. **Reviewers** — default the set `keryx review required --depth auto` prints (light or full); `--all` only when the operator asks; detected convention reviewers are included. Narrowing
983
- (tests only, one domain, custom) is the operator's explicit choice. Passed flags answer it.
993
+ (tests only, one domain, custom) is the operator's explicit choice, recorded by them ("Hosts": reviewer override). Passed flags answer it.
984
994
  Legacy/profile reviewers stay flag-only and are never prompted.
985
995
  2. **Counterpart** — show what the context pack found for the other side of the contract
986
996
  (endpoints, mirrored rules, a paired PR, or "no link found") and a checkbox to verify it,
@@ -994,8 +1004,8 @@ Unattended rounds ask nothing: the `required --depth auto` set, counterpart veri
994
1004
  `convention_reviewers` still answers question 1 (unresolved: include all detected reviewers
995
1005
  and record that choice in the review scope).
996
1006
 
997
- A gating round runs what these answers selected; a host that cannot dispatch subagents
998
- returns `STATUS: BLOCKED` (`nested_dispatch_unavailable`) instead of reviewing alone (Preflight 1). Every
1007
+ A gating round runs what these answers selected; a host with no spawn tool runs `keryx review run --autonomous`, and a
1008
+ nested session at the depth limit returns `STATUS: BLOCKED` (`nested_dispatch_unavailable`), instead of reviewing alone (Preflight 1). Every
999
1009
  round also carries the always-on obligations — SKILL.detail.md § "Depth floor",
1000
1010
  § "Counterpart verification", § "Round obligations".
1001
1011
 
@@ -1007,7 +1017,7 @@ When `review.jev.select` is on, before Wave A/B dispatch: run `keryx review jev-
1007
1017
 
1008
1018
  ## Legacy/Profile Reviewer Auto-Detection
1009
1019
 
1010
- Legacy/profile reviewers are specialized review profiles that predate the review-domain `review-*` naming. They are still valid and must be shown separately from generic and convention reviewers so the user can opt in deliberately. **They are present only when installed:** `code-ai-review`, `code-learned-review`, `code-style-review` and `code-mobx-store-review` are in the `full` profile only, so in any other profile the flags below (`--code-ai`, `--learned`, `--code-style`, `--mobx-store`, `--legacy-profiles`) name reviewers that may not exist. When the inventory's `bundledSource` is `"project"`, a legacy reviewer missing from the `bundled` half of `keryx review reviewers --json` is absent (when it is `"package"` the half is the keryx package's own list and says nothing about this project — decide as `SKILL.detail.md`, "Skills present only when installed", describes): leave it out of the `Optional legacy/profile reviewers` preview, and when a flag asked for it, do not dispatch a substitute — record it in `Skipped reviewers` with reason `not-installed` and the command that adds it (`keryx skills install --profile full`, or the narrower manifest form in `SKILL.detail.md`, "Skills present only when installed"). `--legacy-profiles` and `--all` dispatch the legacy reviewers that are installed and skip the rest the same way.
1020
+ Legacy/profile reviewers are specialized review profiles that predate the review-domain `review-*` naming. They are still valid and must be shown separately from generic and convention reviewers so the user can opt in deliberately. **They are present only when installed:** `code-ai-review`, `code-learned-review`, `code-style-review` and `code-mobx-store-review` are in the `full` profile only, so in any other profile the flags below (`--code-ai`, `--learned`, `--code-style`, `--mobx-store`, `--legacy-profiles`) name reviewers that may not exist. When the inventory's `bundledSource` is `"project"`, a legacy reviewer missing from the `bundled` half of `keryx review reviewers --json` is absent (when it is `"package"` the half is the keryx package's own list and says nothing about this project — decide as `SKILL.detail.md`, "Skills present only when installed", describes): leave it out of the `Optional legacy/profile reviewers` preview, and when a flag asked for it, do not dispatch a substitute — record it in `Skipped reviewers` with reason `not-installed` and the command that adds it (`keryx skills install --profile full`, or the narrower manifest form in `SKILL.detail.md`, "Skills present only when installed"). `--legacy-profiles` dispatches the legacy reviewers that are installed and skips the rest the same way; `--all` never selects them.
1011
1021
 
1012
1022
  | Trigger | Reviewers appended |
1013
1023
  |---|---|
@@ -1028,21 +1038,7 @@ of the flags in the Trigger table above, or when `job-orchestrator` provides `re
1028
1038
  auto-suggestion (MobX/store files present) is informational only — list it in the Review Plan
1029
1039
  Preview below, but do not dispatch it and do not ask about it without an explicit flag.
1030
1040
 
1031
- Review Plan Preview must include an `Optional legacy/profile reviewers` group and a `Skipped reviewers` group with reasons such as:
1032
-
1033
- ```text
1034
- Optional legacy/profile reviewers:
1035
- - code-ai-review: available via --code-ai or --legacy-profiles
1036
- - code-learned-review: available via --learned or --legacy-profiles
1037
- - code-style-review: available via --code-style or --legacy-profiles
1038
- - code-mobx-store-review: auto-suggest when *.store.ts or MobX patterns are present; available via --mobx-store or --legacy-profiles
1039
-
1040
- Skipped reviewers:
1041
- - code-ai-review: profile reviewer, not selected unless --code-ai/--legacy-profiles
1042
- - code-learned-review: profile reviewer, not selected unless --learned/--legacy-profiles
1043
- - code-style-review: legacy style profile, not selected unless --code-style/--legacy-profiles
1044
- - code-mobx-store-review: not selected unless --mobx-store/--legacy-profiles or MobX store files are detected
1045
- ```
1041
+ Review Plan Preview must include an `Optional legacy/profile reviewers` group (each installed one with the flag that selects it) and a `Skipped reviewers` group with each one's reason; the wording: `SKILL.detail.md` § "Review Plan Preview — legacy groups".
1046
1042
 
1047
1043
  ## Routing Table
1048
1044
 
@@ -1063,7 +1059,7 @@ Skipped reviewers:
1063
1059
  | `--flow-graph` | `review-flow-graph` |
1064
1060
  | `--layout` | `review-layout` |
1065
1061
  | `--pr-contract` | `review-pr-contract` |
1066
- | `--all` | all reviewers above (including `review-clean-code`, `review-highload`, applicable legacy/profile reviewers, and project convention reviewers when local convention docs exist) |
1062
+ | `--all` | all reviewers above (including `review-clean-code`, `review-highload`, and project convention reviewers when local convention docs exist); never the legacy/profile reviewers, which stay flag-only |
1067
1063
  | `--verify` | `review-verifier`, started on the first blocker or major and re-run for each later one (`SKILL.detail.md` § "Early verifier"); checks the findings by running something. Delete-only. |
1068
1064
  | (auto) | detected from diff file extensions — see Auto-detection table |
1069
1065
 
@@ -1110,9 +1106,9 @@ whoever reads the merge commit a year later reads the body. When `review.jev.con
1110
1106
 
1111
1107
  **Dispatch integrity is a completion prerequisite.** Use one distinct execution per selected reviewer role; do not combine multiple roles in one child or count sections of one response as independent runs. When budgets are tight, use sequential waves, not role batching. Resolve every project-rule anchor before dispatch; missing rules require context recovery or a blocked result, never silently skipped checks. A read-only pass cannot discharge executable obligations: dispatch a capable execution pass and retain its evidence. Partial scope and NEEDS_CONTEXT remain incomplete until rerun over the outstanding scope.
1112
1108
 
1113
- **The reviewer set is not yours to shrink.** Run `keryx review required --mode all|diff --scope <scope.json>` (`all` for a pull request, for `--all` and for no narrowing flag; `diff` only when the operator named a narrower mode — pass their words as `--operator-quote` and record them as `dispatch.operatorQuote`, without which the dispatch is refused), dispatch every reviewer it prints (`--depth auto` is the default and the only way to a light round: it needs the file list and a line count, `--scope` gives both; `--all`, gating and `--depth full` force full; see `SKILL.detail.md` § "Depth floor"), and declare `mode` in `dispatch`; pass the same `--scope` to `keryx review start|ingest` so the package keeps the file list. Closure refuses a smaller set unless `dispatch.override = { operatorQuote, reason }` carries the operator's own words. New managed packages declare `reviewerDispatch: "research.json"`. Alongside the research ledger, provide `dispatch: { version: 1, mode: "all"|"diff", selected: [...], unresolvedRules: [], runs: [...] }`. Preserve the actual computed reviewer inventory. Each run has `reviewer`, unique `executionId`, `status: "complete"`, `scopeComplete: true`, `rawEvidence`, nonempty `ruleEvidence`, and boolean `executionRequired`. Required execution needs nonempty `executionEvidence` (command/output references); a source-only scope requires `executionReason`. Do not fabricate IDs or mark truncated responses complete. Runtime validates these declarations at closure and the flow gate, not their truth: independently audit evidence and inventory before freezing. Legacy artifact absence is compatibility, not proof of a correct launch. Research closure is mandatory: see [Runtime research-completion ledger](SKILL.detail.md#runtime-research-completion-ledger).
1109
+ **The reviewer set is not yours to shrink.** Run `keryx review required --mode all|diff --scope <scope.json>` (`all` for a pull request, for `--all` and for no narrowing flag; `diff` only when the operator named a narrower mode — pass their words as `--operator-quote` and record them as `dispatch.operatorQuote`, without which the dispatch is refused; on a package `keryx review start` created, closure takes that quote only once the operator recorded the decision themselves — card `review_override_reviewers`, or `keryx review override-reviewers --package <package> --reason "<why>"` in their terminal), dispatch every reviewer it prints (`--depth auto` is the default and the only way to a light round: it needs the file list and a line count, `--scope` gives both; `--all`, gating and `--depth full` force full; see `SKILL.detail.md` § "Depth floor"), and declare `mode` in `dispatch`; pass the same `--scope` to `keryx review start|ingest` so the package keeps the file list. Closure refuses a smaller set unless `dispatch.override = { operatorQuote, reason }` carries the operator's own words and that same operator record exists. New managed packages declare `reviewerDispatch: "research.json"`. Alongside the research ledger, provide `dispatch: { version: 1, mode: "all"|"diff", selected: [...], unresolvedRules: [], runs: [...] }`. Preserve the actual computed reviewer inventory. Each run has `reviewer`, unique `executionId`, `status: "complete"`, `scopeComplete: true`, `rawEvidence`, nonempty `ruleEvidence`, and boolean `executionRequired`. Required execution needs nonempty `executionEvidence` (command/output references); a source-only scope requires `executionReason`. Do not fabricate IDs or mark truncated responses complete. Runtime validates these declarations at closure and the flow gate, not their truth: independently audit evidence and inventory before freezing. Legacy artifact absence is compatibility, not proof of a correct launch. Research closure is mandatory: see [Runtime research-completion ledger](SKILL.detail.md#runtime-research-completion-ledger).
1114
1110
 
1115
- Dispatch selected reviewers in parallel when independent — this engine dispatches them itself (see "How to spawn"); a host without a dispatch tool returns `STATUS: BLOCKED` (`nested_dispatch_unavailable`) and never reviews alone — Preflight 1. Waves follow the diff domains in "Control" and keep their dependency order:
1111
+ Dispatch selected reviewers in parallel when independent — this engine dispatches them itself (see "How to spawn"); a host without a dispatch tool runs `keryx review run --autonomous`, a nested session at the depth limit returns `STATUS: BLOCKED` (`nested_dispatch_unavailable`), and neither reviews alone — Preflight 1. Waves follow the diff domains in "Control" and keep their dependency order:
1116
1112
 
1117
1113
  1. Wave A - core correctness/risk reviewers: logic, architecture, security/highload when selected.
1118
1114
  2. Wave B - domain reviewers: frontend/backend/testing/convention reviewers filtered to relevant files. `review-jev-rules` (flow 330), `review-jev-risk`/`review-jev-scenarios` (flow 332), `review-jev-docs`/`review-jev-comments` (flow 333), `review-jev-contract` (flow 335) also run here, CLI-engine not sub-agent, `"engine": "jev"` in `keryx review reviewers --json`, gated on their own opt-in and a resolvable Jev/OpenRouter credential — measured verdicts per reviewer (keep off by default, experimental, etc.) in `SKILL.detail.md` § "CLI-engine reviewers".
@@ -1126,7 +1122,7 @@ finding class is unreachable for the entire round, and no amount of verification
1126
1122
  downstream recovers it. When a testing reviewer returns without a mutation table,
1127
1123
  treat it the way you would treat a reviewer that returned without findings *and*
1128
1124
  without evidence: ask once, then record that the pass did not run. Unattended: record
1129
- `Not run: mutation pass` without asking. Attended: run `keryx review mutate` ([The mutation pass](SKILL.detail.md#the-mutation-pass-keryx-review-mutate)).
1125
+ `Not run: mutation pass` without asking (unattended = `review start --unattended`, or the autonomous runner). Attended (every other start): run `keryx review mutate` ([The mutation pass](SKILL.detail.md#the-mutation-pass-keryx-review-mutate)); skipping it is the operator's waiver (`review_waive_mutation`, or `keryx review waive-mutation` in their terminal).
1130
1126
 
1131
1127
  ### Wave C — verification, and what it replaced
1132
1128
 
@@ -1194,7 +1190,7 @@ Before dispatching a reviewer through a platform-native sub-agent mechanism, ver
1194
1190
 
1195
1191
  Runtime rules:
1196
1192
  - If the exact reviewer agent type exists, dispatch that reviewer directly.
1197
- - If the exact reviewer agent type does not exist but `skills/<reviewer>/SKILL.md` exists, dispatch `general-purpose` and include the reviewer name, the path of that skill file, bounded review context, and required `REVIEW_RESULT` schema in the prompt. For a bundled reviewer that path goes in the prompt text and in `dispatch_plan`, not in the input's `skill_path` field, which is for project reviewers only (the project-reviewer rule below).
1193
+ - If the exact reviewer agent type does not exist but `.metaproject/skills/gdskills/review/<reviewer>/SKILL.md` exists, dispatch `general-purpose` and include the reviewer name, the path of that skill file, bounded review context, and required `REVIEW_RESULT` schema in the prompt. For a bundled reviewer that path goes in the prompt text and in `dispatch_plan`, not in the input's `skill_path` field, which is for project reviewers only (the project-reviewer rule below).
1198
1194
  - If neither the agent type nor the skill file exists, do not silently substitute another reviewer. Mark that reviewer as `BLOCKED`, include the missing agent/skill name, and continue only with independent reviewers.
1199
1195
  - **A project reviewer is dispatched by its package, on every branch above.** For each entry in the `project` half of `keryx review reviewers --json`, put the absolute path of its registered `<path>/SKILL.md` in the dispatch (`skill_path`) and say in the prompt that this file is the reviewer's definition and overrides the built-in definition of any agent type with the same name. A runtime may ship an agent type called exactly what the project called its reviewer, carrying an older or unrelated prompt; without the path the round runs that prompt and reports it under the project reviewer's name. If the file is missing, mark the reviewer `BLOCKED` and name the missing path — do not fall back to the same-named agent type or to any other reviewer. For every entry in that reviewer's `shadowedRules`, the prompt also says which file to read: "where your text names `<ref>`, read `<resolved>` instead" (the absolute path of `resolved`). The project keeps its own version of that rule beside the one keryx ships under the same name, the reviewer's text still spells the shipped path, and nothing but this sentence in the dispatch makes it read the project's — see `SKILL.detail.md`, "Dispatching a project reviewer".
1200
1196
  - Record the chosen runtime per reviewer in `review_context.review_plan.dispatch_plan`.
@@ -1572,7 +1568,7 @@ Default is do not publish. No resolvable PR number means skip and say so.
1572
1568
 
1573
1569
  The review body is the report rendered from `templates/review-report.md`, English, with the domain file chosen above (`templates/pr-comment-frontend.md` / `templates/pr-comment-backend.md`) — never a summary written freehand. It does not use a tool heading, a finding table, or a meta table. It does not carry a co-author line, a `Generated with` trailer, or any sentence that names a vendor or a product as the author. Name the workflow and reviewers that actually ran, with models, tools, skills and subagents. Never identify the operator from `gh auth status`, git config or a credential; do not sign the review as a person, reviewer or model. A project overlay adds content only through the template's overlay slots.
1574
1570
 
1575
- The follow-up file path and the metadata rules (real model names, Run vs Not run, no `adaptive` in the model slot) live in that same template. Write the body to a temp file and post it as one formal review whose state follows *Where it is posted* in the template: `gh pr review <n> --request-changes|--approve|--comment --body-file <file>`. On a merged or closed PR, post it as `gh pr comment <n> --body-file <file>` instead.
1571
+ The follow-up file path and the metadata rules (real model names, Run vs Not run, no `adaptive` in the model slot) live in that same template. The body goes out as one formal review whose state follows *Where it is posted* in the template, through the one keryx path to GitHub, held by the readiness checklist: `keryx review publish <package> --event COMMENT|REQUEST_CHANGES|APPROVE` (`--partial` when the package is not ready; `--event COMMENT` on a merged or closed PR). Publishing is the operator's decision: the `review_publish` card in keryx shell, elsewhere they run it in their terminal, where it shows the body first. You never pass `--yes`, and never post around it with `gh pr review` or `gh pr comment`.
1576
1572
 
1577
1573
  **No GitHub write without an approved draft.** Before ANY write — the comment, a thread reply, `keryx review comments reply`, a review — show the user the rendered body and wait for explicit approval of that body.
1578
1574
  Picking A or B above chooses *whether* to publish, not *what*; a comment is public and cannot be unsent. With no user to answer (a dispatched run), do not write: hand the rendered body back to the caller.
@@ -1590,7 +1586,7 @@ When a prompt carries `JOB_NAME` and `CONTEXT_PATH`, read that context document
1590
1586
 
1591
1587
  | Rationalization | Why it is wrong |
1592
1588
  |----------------|-----------------|
1593
- | "I'll just run all reviewers for safety" | Over-reviews waste time; auto-detect for relevant scope |
1589
+ | "I'll pick the reviewers myself, by feel" | The set is what `keryx review required` prints, routed per slice; adding by feel over-reviews and dropping one needs the operator's own override |
1594
1590
  | "Spec compliance can wait until after quality review" | Stage 1 gate exists because unimplemented requirements invalidate quality work |
1595
1591
  | "I'll deduplicate findings manually in my head" | Always normalize to [F-NNN] format before consolidation to avoid losing findings |
1596
1592
  | "The reviewer's own severity table said blocker" | There are no reviewer tables. One rubric, in **Severity (canonical)** above; a reviewer that ships one is the defect this replaced |
@@ -1610,6 +1606,10 @@ When a prompt carries `JOB_NAME` and `CONTEXT_PATH`, read that context document
1610
1606
  | "The spec-gate finding is mine, so `reviewer: review-orchestrator`" | That name makes the finding unroutable in Wave C. File it under the reviewer that ran the comparison |
1611
1607
  | "Steps 8–10 are done, I'll tick them now and ingest later" | `completed` means the ingest record exists. A plan that runs ahead of the artifacts is how an unrun review looked finished |
1612
1608
  | "The user said publish, so I can post the comment" | That chose whether, not what. Show the rendered body and get approval of it before any GitHub write |
1613
- | "I cannot spawn reviewers, so I will review it myself" | A review nobody dispatched is not the review the report names. Output the Preflight 1 `BLOCKED` line and stop |
1609
+ | "I cannot spawn reviewers, so I will review it myself" | A review nobody dispatched is not the review the report names. Run `keryx review run --autonomous` (Preflight 1); at the depth limit output the `BLOCKED` line and stop |
1610
+ | "The spawn tool's output has the reviewer's answer" | It is cut (16000 characters on keryx shell) or only the last message. Parse `<package>/raw/<reviewer>.txt` |
1611
+ | "The plan is over threshold, but the reviewers are ready, so I'll dispatch" | `dispatch-check` and `ledger build` refuse an unconfirmed over-threshold plan; the operator confirms it first ("Round plan" 6) |
1612
+ | "I'll keep the results in the shared `.metaproject/data/review/raw`" | It holds other rounds' replies, which then leak into this verdict and retry plan. Results live in `<package>/raw` on every host |
1613
+ | "The operator is busy, I'll pass `--yes` / `--mode automatic` / write the quote" | An operator decision is their card or their terminal ("Hosts"); a quote you typed is not theirs |
1614
1614
  | "User named a module so I'll use diff mode" | Named module/component/store → path mode; diff mode is only for branch changes |
1615
1615
  | "Path mode should only show lines I'd flag in diff mode" | Path mode reviews the entire file — all findings apply, not just added lines |