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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ Presets move planner, implementer and researcher; the reviewer follows the autho

```bash
agent-run route --role implementer --preset main-claude # who runs this, where, how
agent-run route --role reviewer --diff review.diff # depth, family, cost
agent-run route --role reviewer --diff review.diff --author-backend codex # depth, family, cost; self when you wrote it
agent-run preset main-claude # persist; alone: show the effective table

agent-run run --role planner --brief .scratch/tariffs/brief.md
Expand Down
2 changes: 1 addition & 1 deletion skills/delegate-kit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Review when: any delegated implementation; a risk zone (auth, payments, migratio

```
agent-wt diff <task> > review.diff
agent-run route --role reviewer --diff review.diff [--author-backend self]
agent-run route --role reviewer --diff review.diff --author-backend <implementer's family | self>
```

`single` runs straight away. `panel` / `led` are **proposed with the printed numbers and run on the user's yes**. Reviewers run in parallel and blind to each other; merge by `references/review.md`.
Expand Down
2 changes: 1 addition & 1 deletion skills/delegate-kit/references/review.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Review: depth, lenses, composition

How many reviewers a diff deserves, which angle each one takes, and how their findings become one list. `agent-run route --role reviewer --diff <file>` applies all of it and prints the result; this file is the reasoning behind that output.
How many reviewers a diff deserves, which angle each one takes, and how their findings become one list. `agent-run route --role reviewer --diff <file> --author-backend <family|self>` applies all of it and prints the result; this file is the reasoning behind that output.

## Independence is the first slot, not the whole review

Expand Down
1 change: 1 addition & 0 deletions skills/delegate-kit/references/roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ The Codex CLI is offered `sol`, `terra` and `luna` only (`~/.codex/models_cache.
- **Default** the other family than the author, high effort. After a Codex implementer: `claude` opus high. After a Claude implementer: `codex` gpt-5.6-sol high. UX/product review: `claude` opus.
- **Why the other family**: the same family reviewing itself shares blind spots. Independence is the value — the first slot of a panel, never something a preset moves. When the other CLI is missing, `route` falls back to a fresh native reviewer and says so; a fresh context is the floor, the other family the preference.
- **Depth and lenses**: one reviewer by default; a panel of two or three lenses for large or risky diffs, proposed with numbers and run only on the user's yes. `review.md`.
- **The author is named, never assumed.** `route` and `run` take `--author-backend claude|codex|self` for reviewer and verifier; a diff the coordinator wrote itself is `self`, so the same family is never marked independent by default.
- **Read-only.** Give it the frozen diff (`agent-wt diff`) and the spec. Ask for findings with severity, file:line, the claim, the evidence, and a suggested fix. Ask it to separate "spec mismatch" from "standards" from "nit". Ask it not to restate the diff.
- **Effort**: high by default; xhigh only in risk zones (auth, payments, migrations).

Expand Down
23 changes: 20 additions & 3 deletions skills/delegate-kit/scripts/agent-run
Original file line number Diff line number Diff line change
Expand Up @@ -607,8 +607,24 @@ function resolveRoute(opts = {}) {
// Who wrote the code decides the reviewer. Default to whoever this preset would have
// implemented with — including the UI exception, or the reviewer flips to the wrong family.
const defaultAuthor = preset === "auto" && uiKind ? "claude" : table.implementer;
/*
* Для reviewer и verifier автор — обязательный флаг. Раньше он молча
* подставлялся из implementer'а пресета, и diff, написанный самим
* координатором на Claude под auto, получал автора codex, ревьюера claude и
* пометку independent: true — ревью того же семейства выдавалось за
* независимое. Явный --backend на run автора не требует: семейство уже
* названо, а независимость там не считается.
*/
const authorGiven = opts["author-backend"] && opts["author-backend"] !== true;
// The exemption is for `run` only (family named, independence not computed there) and for a
// resume (the family is the persisted one). `route` always needs the author: it is what
// `independent` is computed against.
const runWithBackend = opts._run && opts.backend && opts.backend !== true;
if (["reviewer", "verifier"].includes(role) && !authorGiven && !runWithBackend && !opts._composed) {
die(`--author-backend claude|codex|self is required for --role ${role}: who wrote the diff decides the family. A delegated implementer → its family; a diff the coordinator wrote itself → self.`);
}
// `self`: the parent wrote the change itself, so the author is the parent's family
let author = opts["author-backend"] && opts["author-backend"] !== true ? String(opts["author-backend"]) : defaultAuthor;
let author = authorGiven ? String(opts["author-backend"]) : defaultAuthor;
if (author === "self") { if (!parent) die("--author-backend self needs a detectable parent (or --parent)"); author = parent; }
if (!["claude", "codex"].includes(author)) die("--author-backend must be claude, codex or self");

Expand Down Expand Up @@ -860,7 +876,7 @@ async function cmdRun(opts, resumeOf = null) {
const role = resumeOf ? resumeOf.role : opts.role;
if (!role || !ROLES[role]) die(`--role must be one of ${Object.keys(ROLES).join("|")}`);
const spec = ROLES[role];
const route = resolveRoute({ ...opts, role });
const route = resolveRoute({ ...opts, role, _run: true, backend: resumeOf ? resumeOf.backend : opts.backend });
const backend = resumeOf ? resumeOf.backend : route.backend;
const model = resumeOf ? (opts.model && opts.model !== true ? opts.model : resumeOf.model) : route.model;
const effort = resumeOf ? (opts.effort && opts.effort !== true ? opts.effort : resumeOf.effort) : route.effort;
Expand Down Expand Up @@ -1049,7 +1065,8 @@ switch (sub) {
console.log(`agent-run — headless workers for Claude Code / Codex with role defaults, worktree locks and a ledger

agent-run route --role <${Object.keys(ROLES).join("|")}> [--preset P] [--parent claude|codex] [--author-backend claude|codex|self] [--kind ui|refactor|mechanical]
who should run this role, on which family, natively or externally
who should run this role, on which family, natively or externally;
--author-backend is required for reviewer and verifier (self = the coordinator wrote the diff)
agent-run route --role reviewer [--diff FILE] [--depth single|panel|led]
how many reviewers this diff deserves, which lens and family each gets, and the lead
agent-run preset [${Object.keys(PRESETS).join("|")}] show or persist the default preset
Expand Down
19 changes: 15 additions & 4 deletions skills/delegate-kit/tests/route.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ echo "── пресет auto, родитель Claude, оба CLI"
ok "planner → claude fable, нативно" "$(route both --role planner --parent claude '[.backend,.model,.dispatch,.invoke.subagent_type]|join(" ")')" "claude fable native dk-planner"
ok "implementer → codex sol, внешне" "$(route both --role implementer --parent claude '[.backend,.model,.effort,.dispatch]|join(" ")')" "codex gpt-5.6-sol high external"
ok "researcher → claude sonnet medium" "$(route both --role researcher --parent claude '[.backend,.model,.effort]|join(" ")')" "claude sonnet medium"
ok "reviewer после codex-автора → claude, независим, нативно" "$(route both --role reviewer --parent claude '.reviewers[0]|[.backend,(.independent|tostring),.dispatch]|join(" ")')" "claude true native"
ok "verifier — третья сторона к ревьюеру: обратно на codex" "$(route both --role verifier --parent claude '.backend + " " + (.why[0]|contains("third party")|tostring)')" "codex true"
ok "reviewer после codex-автора → claude, независим, нативно" "$(route both --role reviewer --parent claude --author-backend codex '.reviewers[0]|[.backend,(.independent|tostring),.dispatch]|join(" ")')" "claude true native"
ok "verifier — третья сторона к ревьюеру: обратно на codex" "$(route both --role verifier --parent claude --author-backend codex '.backend + " " + (.why[0]|contains("third party")|tostring)')" "codex true"
ok "review-lead следует за planner'ом" "$(route both --role review-lead --parent claude '[.backend,.model]|join(" ")')" "claude fable"

echo "── пресеты и приоритет"
ok "main-claude: implementer нативно на claude" "$(route both --role implementer --parent claude --preset main-claude '[.backend,.dispatch]|join(" ")')" "claude native"
ok "main-claude: ревьюер следует за автором → codex" "$(route both --role reviewer --parent claude --preset main-claude '.author + " " + .reviewers[0].backend')" "claude codex"
ok "main-claude: ревьюер следует за автором → codex" "$(route both --role reviewer --parent claude --preset main-claude --author-backend claude '.author + " " + .reviewers[0].backend')" "claude codex"
ok "main-codex под Codex-родителем: всё нативно" "$(route both --role planner --parent codex --preset main-codex '[.backend,.dispatch]|join(" ")')" "codex native"
ok "флаг сильнее env" "$(DELEGATE_KIT_PRESET=main-codex route both --role planner --parent claude --preset main-claude '.preset')" "main-claude"
ok "env сильнее config" "$($NODE "$AR" preset main-claude >/dev/null; DELEGATE_KIT_PRESET=main-codex route both --role planner --parent claude '.preset')" "main-codex"
Expand All @@ -51,7 +51,7 @@ rm -f "$DELEGATE_KIT_HOME/config.json"
echo "── автор и независимость"
ok "--author-backend self: ревьюер на другом семействе" "$(route both --role reviewer --parent claude --author-backend self '.author + " " + .reviewers[0].backend + " " + (.reviewers[0].independent|tostring)')" "claude codex true"
ok "--kind ui под auto: implementer на claude" "$(route both --role implementer --parent claude --kind ui '.backend')" "claude"
ok "--kind ui: ревьюер следует за UI-автором → codex" "$(route both --role reviewer --parent claude --kind ui '.author + " " + .reviewers[0].backend')" "claude codex"
ok "--kind ui: ревьюер следует за UI-автором → codex" "$(route both --role reviewer --parent claude --kind ui --author-backend claude '.author + " " + .reviewers[0].backend')" "claude codex"

echo "── отсутствие CLI другого семейства"
ok "ревьюер падает на нативного, independent=false" "$(route none --role reviewer --parent claude --author-backend self '.reviewers[0]|[.backend,.dispatch,(.independent|tostring)]|join(" ")')" "claude native false"
Expand Down Expand Up @@ -91,6 +91,17 @@ ok "led: 5 сессий в cost_note" "$(route both $R --diff "$D/led.diff" '.co
ok "--kind refactor: линзы correctness+standards" "$(route both $R --diff "$D/400.diff" --kind refactor '[.reviewers[].lens]|join(" ")')" "correctness standards"
ok "merge_rules только при панели" "$(route both $R --diff "$D/399.diff" '.merge_rules==null')" "true"

echo "── автор обязателен для reviewer и verifier"
ok "route reviewer без автора — отказ" "$(PATH="$BASE/both" "$NODE" "$AR" route --role reviewer --parent claude 2>&1 | grep -c 'author-backend claude|codex|self is required')" "1"
ok "route verifier без автора — отказ" "$(PATH="$BASE/both" "$NODE" "$AR" route --role verifier --parent claude 2>&1 | grep -c 'is required for --role verifier')" "1"
ok "run reviewer без автора и без --backend — отказ" "$(PATH="$BASE/both" "$NODE" "$AR" run --role reviewer --prompt x --parent claude 2>&1 | grep -c 'is required')" "1"
ok "run reviewer с явным --backend автора не требует" "$(PATH="$BASE/both" "$NODE" "$AR" run --role reviewer --backend claude --model gpt-5.6-sol --prompt x --parent claude 2>&1 | grep -c 'is a codex model')" "1"
ok "planner автора не требует" "$(route both --role planner --parent claude '.backend')" "claude"
ok "route reviewer с --backend, но без автора — всё равно отказ" "$(PATH="$BASE/both" "$NODE" "$AR" route --role reviewer --parent claude --backend claude 2>&1 | grep -c 'is required')" "1"
mkdir -p "$DELEGATE_KIT_HOME/runs/rv-1"; jq -n '{id:"rv-1",role:"reviewer",backend:"codex",model:"gpt-5.6-sol",effort:"high",cwd:"/tmp",write:false,status:"finished",pid:1,started:"2026-01-01T00:00:00Z",finished:"2026-01-01T00:01:00Z",sessionId:"sess-1"}' > "$DELEGATE_KIT_HOME/runs/rv-1/meta.json"
ok "resume ревьюера без автора не отказывает (семейство из meta)" "$(PATH="$BASE/both" "$NODE" "$AR" resume rv-1 --prompt x --parent claude --no-route-hint 2>&1 | grep -c 'is required')" "0"
ok "resume ушёл на семейство прогона" "$(PATH="$BASE/both" "$NODE" "$AR" list | jq -r '[.[]|select(.id!="rv-1")][0].backend')" "codex"

echo "── отказы"
ok "модель чужого семейства на run" "$(PATH="$BASE/both" "$NODE" "$AR" run --role planner --backend claude --model gpt-5.6-sol --prompt x --parent claude 2>&1 | grep -c 'is a codex model but --backend is claude')" "1"
ok "--depth вне списка" "$(PATH="$BASE/both" "$NODE" "$AR" route $R --diff "$D/399.diff" --depth deep 2>&1 | grep -c 'depth must be')" "1"
Expand Down
Loading