diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87810f2..a0133cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,5 +21,10 @@ jobs: shellcheck -S warning $files - name: Node syntax run: node --check skills/delegate-kit/scripts/agent-run + - name: SKILL.md stays a policy, not a manual + run: | + size=$(wc -c < skills/delegate-kit/SKILL.md) + echo "SKILL.md: $size bytes" + test "$size" -le 8000 - name: Validate result schema JSON run: python3 -m json.tool skills/delegate-kit/references/result-schema.json >/dev/null diff --git a/README.md b/README.md index 8f39e4d..b2748db 100644 --- a/README.md +++ b/README.md @@ -2,141 +2,107 @@ # delegate-kit -**Your coding agent hands work to fresh workers — and the review always comes from the other model family.** +**Your coding agent picks the shape of a task before doing it, hands the big ones to fresh workers, and gets the result reviewed by someone who did not write it — the other model family whenever its CLI is installed.** -One skill for Claude Code, Codex CLI and T3 Code. Your logins, your subscriptions, no new harness. +One skill for Claude Code, T3 Code and Codex CLI. Your logins, your subscriptions, no new harness. [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Claude Code](https://img.shields.io/badge/Claude%20Code-parent%20or%20worker-blueviolet)](https://claude.com/claude-code) -[![Codex CLI](https://img.shields.io/badge/Codex%20CLI-parent%20or%20worker-black)](https://github.com/openai/codex) -[![T3 Code](https://img.shields.io/badge/T3%20Code-parent-orange)](https://t3.chat) +[![Claude Code](https://img.shields.io/badge/Claude%20Code-coordinator%20%7C%20worker-blueviolet)](https://claude.com/claude-code) +[![Codex CLI](https://img.shields.io/badge/Codex%20CLI-coordinator%20%7C%20worker-black)](https://github.com/openai/codex) +[![T3 Code](https://img.shields.io/badge/T3%20Code-coordinator-orange)](https://t3.chat) -[Install](#install) · [How it works](#how-it-works) · [Usage](#usage) · [The rules](#the-rules-that-do-the-work) · [Docs](skills/delegate-kit/) +[Why](#why) · [How it works](#how-it-works) · [Shapes](#the-shapes) · [Review](#review) · [Install](#install) · [Usage](#usage) · [Docs](skills/delegate-kit/) --- -## What you get +## Why -You talk to one agent. When a task is big enough, it plans with the strongest model, implements in an isolated git worktree, and gets the change **reviewed by a model from the other vendor** — then reports back with what was checked and what was not. Small tasks it just does. You decide which subscription pays with one word. +Capable models already delegate. What they do inconsistently is *when* and *how*: a one-line fix spawns a 50k-token worker, a ten-module feature starts with no plan, three subagents share one working tree, and the author approves its own diff. delegate-kit makes one decision the same every time: -| Without delegate-kit | With delegate-kit | -|---|---| -| The agent delegates on a whim: a one-line fix spawns a 50k-token worker; a ten-module change starts with no plan. | **Triage first.** Up to ~3 files it does itself. Prose requirements or >1 module → a planner before any code. Signals, not moods. | -| Codex reviews Codex. Claude reviews Claude. Same blind spots twice. | **Independent review, always.** The reviewer is from the other model family than the author. No preset, no quota pressure moves that. | -| Workers write into your working tree. Two of them collide. | **Writers live in worktrees**, one per worktree, locked. Your tree stays yours; you merge what you accept. | -| Every worker is a full headless CLI session, even one the agent could have spawned itself. | **Native inside the family, external across it.** A Claude parent spawns Claude workers natively; only the Codex reviewer is a separate session — the cheapest shape there is. | -| One subscription runs out and the work stops. | **Presets.** `main-claude` or `main-codex` moves the heavy roles to the family with quota left; the reviewer stays independent; on a rate limit the other vendor picks up once, and says so. | -| "Review this" means one opinion, however big the diff. | **Review depth from the diff.** Small → one reviewer. Large or risky → a panel of lenses (spec / correctness / standards), proposed with numbers, run on your yes. | -| The worker says "done". Was anything checked? | **One JSON contract.** `status`, `changes`, `checks_run`, `not_verified`, `findings`, `questions`. A check that did not run is reported as not run. | - -
-Table of contents - -- [What you get](#what-you-get) -- [How it works](#how-it-works) -- [The rules that do the work](#the-rules-that-do-the-work) - - [Independence is the first slot](#1-independence-is-the-first-slot) - - [Native inside the family, external across it](#2-native-inside-the-family-external-across-it) - - [Presets: which subscription pays](#3-presets-which-subscription-pays) - - [Review depth: one reviewer or a panel of lenses](#4-review-depth-one-reviewer-or-a-panel-of-lenses) -- [The roles](#the-roles) -- [Install](#install) -- [Usage](#usage) -- [The tools](#the-tools) -- [What it will not do](#what-it-will-not-do) -- [Layout](#layout) -- [License and acknowledgments](#license-and-acknowledgments) +> **Do it here, send one scout, plan first, one worker, two or three in parallel, or a sequence — and who reviews the result.** -
+| Without | With | +|---|---| +| Delegation on a whim | **Shape first**, from numbers: ≤ ~3 files → done here; prose requirements or > 1 module → a plan before code | +| Three workers on one coupled feature → merge conflicts, two designs | **Worker count = independent outcomes**; coupled edits stay in one pair of hands | +| Claude reviews Claude; Codex reviews Codex | **Independent review**: the other family when available, a fresh session otherwise — reported either way | +| Workers write into your tree and collide | **One writer per git worktree**, locked; you merge what you accept | +| A cross-vendor worker treated as a subagent, then lost | **Native inside the family, external across it** — named as such, launched once, collected once | +| One subscription runs dry, work stops | **Presets** move the heavy roles; the reviewer follows the author; a rate limit retries once on the other vendor and says so | +| "Done." Was anything checked? | **One JSON contract**: `status`, `changes`, `checks_run`, `not_verified`, `findings`, `questions` | ## How it works +You talk to one agent — the **coordinator**. It keeps the intent, the plan, every brief, integration and the answer. Workers are fresh sessions that start from a brief and read the repository themselves. + ``` -you ──► parent (Claude Code / T3 / Codex) "change the tariff logic in the billing module" - │ - │ triage: small? → done here. big / ambiguous / risky? → delegate - │ spec → .scratch/tariffs/spec.md - ▼ - planner ────────────────► strongest model, read-only ──────────► plan + blocking questions - │ - agent-wt create tariffs (git worktree + write-lock) - │ - implementer ────────────► workhorse model, writes, sandboxed ──► commits on dk/tariffs - │ - agent-wt diff tariffs (the frozen diff) - agent-run route --role reviewer --diff → single | panel | led, with the numbers - │ - reviewer(s) ────────────► THE OTHER FAMILY than the author ───► findings - │ - │ mechanical finding → parent fixes. real one → same implementer, resumed. - │ dispute → a command first, then a verifier (third party). - ▼ - merge / PR · agent-wt remove tariffs · report: done, checked, not checked, open +you ──► coordinator "change the tariff logic in the billing module" + │ + │ shape? DIRECT → done here. otherwise: + │ spec → .scratch/tariffs/spec.md + ▼ + planner ──────────► strongest model, read-only ──────► steps, risks, blocking questions + │ + agent-wt create tariffs git worktree + write-lock + │ + implementer ──────► native subagent or external CLI ─► commits on dk/tariffs + │ + agent-wt diff tariffs the frozen diff + agent-run route --role reviewer single | panel | led · family · cost + │ + reviewer ─────────► the OTHER family if its CLI is installed, + │ else a fresh session of the same family ─► findings + │ + │ mechanical → coordinator fixes · substantive → same implementer, resumed + │ dispute → a command first, then a verifier + ▼ + merge / PR · report: done · checked · not checked · open · who reviewed ``` -Every worker starts empty. It never sees your conversation; it gets a brief and reads the repository itself, then returns one JSON object. That is the whole interface — and it is why briefs matter more than models. - -## The rules that do the work +Two kinds of worker, never confused: -### 1. Independence is the first slot - -A review by the same family that wrote the code shares its blind spots. So the reviewer is **always** the other family than the author — a Codex-written change is reviewed by Claude, a Claude-written one by Codex, and a change the parent wrote itself goes to the other vendor too. A verifier, when one is needed, is a third party again. Nothing in the skill can flip this; it is the one property delegation is for. - -### 2. Native inside the family, external across it - -A parent can only spawn its own family natively: - -| Worker family | parent = Claude Code / T3 | parent = Codex | +| | Native | External | |---|---|---| -| Claude (`fable`, `opus`, `sonnet`, `haiku`) | **native** — `Agent` tool, `dk-` | external — `agent-run run --backend claude` | -| GPT (`gpt-5.6-sol`, `-terra`, `-luna`) | external — `agent-run run --backend codex` | **native** — `spawn_agent`, `dk-` | +| What | a subagent of the host, same family as the coordinator | a headless `claude -p` / `codex exec` session of the other family, via `agent-run` | +| Steerable | yes — visible, continuable | no — launched once with a full brief, collected once, resumed by id | +| Why | cheap, default inside the family | the only way to get the other family; also an enforced sandbox, a ledger, timeouts, quota fallback | +| Docs | [`references/hosts.md`](skills/delegate-kit/references/hosts.md) | [`references/external.md`](skills/delegate-kit/references/external.md) | -Native is cheaper and steerable, so it is the default inside the family. External is what `agent-run` adds: an enforced read-only sandbox, the strict JSON schema, a ledger, a resumable run id, a timeout, detached parallel writers and the cross-vendor quota fallback — worth going external for even inside the family when one of those matters. `agent-run route --role ` answers all of it in one call. Reasoning: [`references/dispatch.md`](skills/delegate-kit/references/dispatch.md). +## The shapes -### 3. Presets: which subscription pays +| Shape | When | Who | +|---|---|---| +| **DIRECT** | ≤ ~3 files, clear requirements; explanations; micro-fixes; anything destructive or production-adjacent | the coordinator, in the foreground | +| **SCOUT** | the hard part is *finding*: a large repo, several plausible causes, docs to quote | one read-only worker, then decide again | +| **PLAN** | prose requirements, ambiguity, > 1 module or > ~10 files; any question that ends in a verdict | planner, read-only, strongest model | +| **SINGLE** | one vertical slice too big for DIRECT | one implementer in its own worktree | +| **PARALLEL** | 2–3 slices with disjoint write scopes and stable interfaces | one implementer per slice, each in a worktree | +| **SEQUENTIAL** | one result changes the next task's assumptions (schema → API → UI) | one worker at a time, resumed | -| Preset | planner | implementer | researcher | reviewer / verifier | -|---|---|---|---|---| -| `auto` (default) | Claude | Codex | Claude | the other family than the author | -| `main-claude` | Claude | Claude | Claude | the other family than the author | -| `main-codex` | Codex | Codex | Codex | the other family than the author | +Limits: 2 writers, 4 workers, delegation depth 1. Repository size changes the cost of *finding* context, not the number of writers. -A preset moves the token-heavy roles. It never moves the reviewer — so `main-claude` implies a Codex reviewer and `main-codex` a Claude one. That is cheap: a review is one read-only pass over a frozen diff, a fraction of what the implementer spends. +## Review -Say it in words ("let Codex implement this", "main model Claude"), type it (`/delegate-kit main-claude`, `$delegate-kit main-codex`), or persist it (`agent-run preset main-claude`). Your own per-role defaults go in `~/.delegate-kit/config.json` — `"roles": { "planner": { "claude": ["fable", "xhigh"] } }` — so "always plan on xhigh" is one edit, and the repository's table stays the default for everyone else. +The author of a non-trivial change does not certify it. The reviewer is a fresh, read-only worker with the frozen diff and the spec — and independence has an order of preference, not a hard requirement: -### 4. Review depth: one reviewer, or a panel of lenses +1. **The other family than the author.** Picked whenever that CLI is installed. Claude wrote it → Codex reads it, and the reverse; a change the coordinator wrote itself goes to the other vendor too. +2. **A fresh session of the author's family.** When the other CLI is missing, `agent-run route` falls back to it, marks `independent: false`, and the report says which kind ran. -A second reviewer with the same brief finds the same things twice. What a second slot should buy is a second **lens**: +Depth is measured from the diff: | Depth | Reviewers | When | |---|---|---| -| `single` | one, the other family | the default — small diff, one module, no risk zone | -| `panel` | two lenses, parallel and blind to each other | ~400+ changed lines, 10+ files, 2+ modules, or a risk zone | -| `led` | a review lead plans → three lenses → the lead merges | ~1200+ lines, 25+ files, 3+ modules | - -Lenses: `spec` (does it do what was asked), `correctness` (is it right), `standards` (the repo's conventions plus a fixed smell baseline). Slot A is the other family; slot B may be the author's family, because independence is already paid for. `agent-run route --role reviewer --diff review.diff` measures the frozen diff and prints the depth, the composition and the cost. A panel is **proposed with those numbers and run on your yes** — it is the one place the skill spends more than one session on one step. Rules, lenses and the baseline: [`references/review.md`](skills/delegate-kit/references/review.md). +| `single` | one | small diff, one module, no risk zone — the default | +| `panel` | two, parallel and blind to each other | large diff, several modules, or a risk zone | +| `led` | a lead plans, three review, the lead merges | very large or risky | -## The roles - -| Role | Does | Default | Access | -|---|---|---|---| -| **planner** | Ordered steps, files per step, risks, blocking vs non-blocking questions, the checks that prove completion | Claude **Fable** high · Codex **Sol** xhigh | read-only | -| **implementer** | One vertical slice in its own worktree; runs the acceptance checks; commits on its branch | Codex **Sol** high · Claude **Opus** high for UI-heavy work | write, sandboxed, one per worktree | -| **reviewer** | Reads the frozen diff against the spec; findings with severity, kind, lens, file:line, evidence, fix | **the other family than the author**, high effort | read-only | -| **review-lead** | On a large diff: plans the reviewers before, merges their findings after. Two short calls | the planner's family at its strongest | read-only | -| **verifier** | Settles one disputed or high-risk finding: confirmed / refuted / needs-human, with evidence | a third party to the reviewer, strongest tier | read-only | -| **researcher** | Quotes current documentation with URL and date; marks what it could not verify | Claude **Sonnet** medium · Codex **Terra** medium | read-only, web | - -And the non-role that matters most: **the parent itself** — up to ~3 files, a clear spec, low risk, micro-fixes after review, explanations, diagnoses, and everything destructive or production-adjacent, in the foreground, with you watching. - -Why these defaults: planning is one read-only call whose mistakes propagate everywhere, so it gets the strongest model. Within a family, raising *effort* moves review and planning quality more than raising the tier — which is also why the strongest Codex role is Sol at `xhigh` (the CLI offers Sol, Terra and Luna; there is no `pro` worker). A small model on a micro-task never pays back the ~50k-token start-up cost of a worker. Reasoning per role: [`references/roles.md`](skills/delegate-kit/references/roles.md). +Lenses: `spec` · `correctness` · `standards`. A panel is **proposed with the numbers and run on your yes**. Rules, lenses, merge and the smell baseline: [`references/review.md`](skills/delegate-kit/references/review.md). ## Install -**Prerequisites:** bash, git, jq, node ≥ 20; `claude` and/or `codex` installed and logged in with your own account. +**Prerequisites.** The policy and native workers need only the host. Workers from the other family need that CLI installed and logged in with your own account, plus `bash`, `git`, `jq`, `node ≥ 20`. ```bash # 1. the skill @@ -147,100 +113,111 @@ ln -s ~/dev/delegate-kit/skills/delegate-kit ~/.agents/skills/delegate-kit ln -s ../../.agents/skills/delegate-kit ~/.claude/skills/delegate-kit ln -s ../../.agents/skills/delegate-kit ~/.codex/skills/delegate-kit -# 2. the safety hook + the native role definitions (backs up your settings, shows the diff first) +# 2. native role definitions + safety hook (backs up your settings, shows the diff first) ~/.agents/skills/delegate-kit/hooks/install.sh --dry-run -~/.agents/skills/delegate-kit/hooks/install.sh # or --claude / --codex / --hooks-only / --agents-only +~/.agents/skills/delegate-kit/hooks/install.sh # --claude / --codex / --hooks-only / --agents-only # 3. optional: scripts on PATH echo 'export PATH="$HOME/.agents/skills/delegate-kit/scripts:$PATH"' >> ~/.zshrc ``` -Restart running `claude` / `codex` sessions afterwards. +Restart running `claude` / `codex` sessions. The symlinks keep it live: `git pull` is the update; re-run `install.sh` after a change to the roles. + +**Always-on.** The skill triggers on its own words (below). To have it triage every non-trivial task, add one line to your global instructions: -The symlinks make the policy live: `SKILL.md`, the scripts and the references are read straight out of your clone, so a `git pull` is the whole update. The native role definitions are the exception — a harness discovers subagents only in `~/.claude/agents/` and `~/.codex/config.toml` — which is why `install.sh` exists: it symlinks the Claude ones and splices the Codex block between markers, replacing it on every re-run. After pulling a change to the roles, run it again. +```text +Before repository work described in prose or spanning several modules, apply delegate-kit; a DIRECT verdict needs no announcement. +``` -**Uninstall:** `hooks/uninstall.sh` (removes the hook, the `dk-*` symlinks and the `[agents.dk-*]` block), remove the three skill symlinks, and `rm -rf ~/.delegate-kit` if you do not want to keep the ledger, the preset and the run logs. +**Uninstall.** `hooks/uninstall.sh`, remove the three symlinks, `rm -rf ~/.delegate-kit` if you do not want to keep the ledger and run logs. ## Usage -Work as usual. The parent loads the skill when a task spans modules, needs a review, or you say "delegate", "plan this", "review this"; invoke it explicitly with `/delegate-kit` in Claude Code or `$delegate-kit` in Codex. - -**What you say and what it does:** +Work as usual. Invoke explicitly with `/delegate-kit` (Claude Code) or `$delegate-kit` (Codex), or just say it: | You say | Effect | |---|---| -| "delegate", "plan this", "review this", "subagent" | the skill triggers | -| "let Codex implement", "main model Claude", `main-claude` / `main-codex` | preset for this task — the heavy roles move, the reviewer stays independent | +| "delegate", "subagent", "scout", "plan this", "review this", "second opinion" | the skill triggers | +| a feature or refactor described in prose; "which should we adopt?" | the skill triggers — PLAN | +| "let Codex implement", "main model Claude", `main-claude` / `main-codex` | preset for this task: heavy roles move, the reviewer follows the author | | "plan with Fable", "review with Sol" | one model for one role; the preset is untouched | | "panel", "two reviewers", "led" | your yes to a deeper review, given in advance | -| "this is mechanical" / "a refactor" / "UI work" | `--kind` — always `single` / lenses `correctness`+`standards` / implementer on Claude under `auto` | +| "this is mechanical" / "a refactor" / "UI work" | `--kind`: always `single` / lenses `correctness`+`standards` / implementer on Claude under `auto` | -**What it decides alone:** whether to delegate at all; the family, model, effort and native-vs-external per role; the reviewer's family (always the other one); review depth from the diff; a command before a verifier; one cross-vendor retry on a rate limit. +**Decides alone:** the shape; family, model, effort and native-vs-external per role; the reviewer; review depth from the diff; a command before a verifier; one cross-vendor retry on a rate limit. -**Where it stops and asks:** before a panel (with the numbers); when a worker returns `blocked` with questions; when your working tree is dirty and the task touches that work; before any command `gate.sh` classes as dangerous. +**Stops and asks:** before a panel (with the numbers); when a worker returns `blocked` with questions; when your working tree is dirty and the task touches it; before any command the safety gate classes as dangerous. -If you interview yourself first (a grill skill, a spec session), save the outcome as `.scratch//spec.md` and the workers are pointed at it instead of a retold version. +If you interview yourself first (a grill skill, a spec session), save the outcome as `.scratch//spec.md` — workers and the reviewer read that file, not a retelling. + +
+The roles + +| Role | Does | Default | Access | +|---|---|---|---| +| **planner** | ordered steps, files per step, risks, blocking questions, the checks that prove completion | Claude Fable high · Codex Sol xhigh | read-only | +| **implementer** | one vertical slice in its own worktree; runs the acceptance checks; commits on its branch | Codex Sol high · Claude Opus high for UI | write, sandboxed | +| **reviewer** | reads the frozen diff against the spec; findings with severity, lens, file:line, evidence, fix | the other family than the author when available | read-only | +| **review-lead** | plans a `led` review before, merges findings after; two short calls | the planner's family, strongest | read-only | +| **verifier** | settles one disputed or high-risk finding that a command cannot | third party to the reviewer | read-only | +| **researcher** | quotes current docs with URL and date; marks what it could not verify | Claude Sonnet medium · Codex Terra medium | read-only, web | + +Presets move planner, implementer and researcher; the reviewer follows the author. Reasoning per role: [`references/roles.md`](skills/delegate-kit/references/roles.md). + +
Driving the scripts by hand ```bash agent-run route --role implementer --preset main-claude # who runs this, where, how -agent-run route --role reviewer --diff review.diff # how deep the review should go -agent-run preset main-claude # persist the preset; alone: show the effective table +agent-run route --role reviewer --diff review.diff # depth, family, cost +agent-run preset main-claude # persist; alone: show the effective table agent-run run --role planner --brief .scratch/tariffs/brief.md agent-wt create tariffs agent-run run --role implementer --backend codex --cwd ../repo.worktrees/tariffs --brief .scratch/tariffs/impl.md agent-wt diff tariffs > .scratch/tariffs/review.diff -agent-run run --role reviewer --backend claude --lens correctness --panel t1 --cwd ../repo.worktrees/tariffs --brief .scratch/tariffs/review.md -agent-run resume --prompt "Fix findings 1 and 3 from the review: ..." +agent-run run --role reviewer --backend claude --cwd ../repo.worktrees/tariffs --brief .scratch/tariffs/review.md +agent-run resume --prompt "Fix findings 1 and 3: ..." agent-run list | status | wait | kill | log | notify [] ``` -Two implementers in parallel: `--detach` on each, then either block on `agent-run wait ` or have each one report for itself with `--on-finish CMD` — the only channel an external worker has, since no harness announces it. A native writer: `agent-wt lock tariffs` before dispatch, `agent-wt release tariffs` after. `--help` on either script is the reference for flags. +Two writers in parallel: `--detach` on each, then `agent-run wait ` or `--on-finish CMD` so each reports for itself. A native writer: `agent-wt lock ` before dispatch, `agent-wt release ` after. `--help` on either script is the flag reference.
-## The tools - -**`agent-run`** — routes, starts, resumes, waits for, lists and kills workers, and delivers each completion to an `--on-finish` hook exactly once, retried and surviving a dead supervisor. Applies role defaults; read-only roles run `codex -s read-only` / `claude --permission-mode plan`, writers `workspace-write` / `acceptEdits`, never the dangerous modes. Writers must be in a git worktree, one per worktree, at most 2 writers and 4 workers at once. Delegation depth is 1: a worker cannot start workers. Every run returns the JSON contract and is archived under `~/.delegate-kit/runs//`; the ledger `~/.delegate-kit/ledger.jsonl` records model, effort, preset, lens, tokens, duration and outcome per run. - -**`agent-wt`** — git worktrees next to the repo (`.worktrees/`, branch `dk/`): create, status, frozen diff against the recorded base, lock (for a native writer), release, remove, cleanup of merged worktrees. - -**`agents/dk-*.md` · `references/codex-agents.toml`** — the six roles as native subagent definitions, one set per harness, carrying model, effort and tool list. - -**`hooks/gate.sh`** — a PreToolUse hook in both CLIs. `sudo`, `rm -rf`, service and firewall changes, certificates, destructive SQL, force-push, history-destroying git, destructive commands over SSH, disk operations — require your explicit confirmation. On Claude Code that is the normal approval prompt; Codex hooks cannot prompt, so the command is denied with an instruction to confirm with you and re-run prefixed `DELEGATE_KIT_CONFIRMED=1`. - -## What it will not do - -- **Steer an external worker mid-run.** Headless sessions run to completion; you read the result and resume with a follow-up. Native subagents can be steered — one more reason the skill prefers them inside the family. -- **Sandbox.** The gate is a list of known dangerous command shapes; the real isolation is the CLIs' own sandboxes plus the worktree. A native read-only role is read-only by instruction and tool list, not by sandbox — dispatch externally when that boundary matters. -- **Make delegation cheap.** A worker is a full session. The skill exists so you pay that price only when independence, parallelism or a clean context is worth it. -- **Record native dispatches in the ledger.** Only external runs are there; a model comparison has to be run externally on both sides. -- **Mix the families.** Claude is `fable | opus | sonnet | haiku`, Codex is `gpt-5.6-sol | gpt-5.6-terra | gpt-5.6-luna`; `agent-run` rejects a name from the family that does not match `--backend`. When vendors rename models, update the table at the top of `scripts/agent-run` and the two role-definition sets. - -Workers run through the official CLIs with the logins you already have — ordinary use of Claude Code and Codex. Do not wrap this into a product that routes other people's subscriptions; that is what the vendors prohibit. - -## Layout +
+Layout ``` skills/delegate-kit/ - SKILL.md the policy the parent reads - agents/dk-*.md the six roles as native Claude Code subagents - references/codex-agents.toml the six roles as native Codex subagents - references/dispatch.md native vs external, presets — the reasoning behind agent-run route - references/roles.md families, tiers, reasoning per role, prompt hints, tuning - references/review.md review depth, lenses, panel composition, merge rules, smell baseline + SKILL.md the policy: shapes, spec, route, brief, worktree, review, report + references/hosts.md native dispatch per host; git with parallel writers + references/external.md the other family through agent-run: preflight, collecting, presets, limits + references/roles.md families, tiers, reasoning per role, prompt hints + references/review.md depth, lenses, panel composition, merge rules, smell baseline references/brief-template.md how to write a brief references/result-schema.json the JSON contract + references/codex-agents.toml the six roles as native Codex subagents + agents/dk-*.md the six roles as native Claude Code subagents scripts/agent-run route / preset / run / resume / list / status / wait / kill / log / notify scripts/agent-wt create / list / status / diff / lock / release / remove / cleanup - hooks/gate.sh PreToolUse safety gate (Claude Code + Codex) - hooks/install.sh, uninstall.sh + hooks/gate.sh, install.sh, uninstall.sh tests/delivery.sh completion-delivery bench; --race N for the concurrency hammer ``` +
+ +## What it will not do + +- **Steer an external worker mid-run** — headless sessions run to completion; you read the result and resume. That is why native is preferred inside the family, and why an external worker is never called a subagent. +- **Run a swarm** — two writers, four workers. Larger fleets need an unusually clear partition and your explicit ask. +- **Make delegation cheap** — a worker is a full session; the shapes exist so you pay for it only for independence, parallelism or a clean context. +- **Sandbox by itself** — the gate is a list of dangerous command shapes; the real isolation is the CLIs' own sandboxes plus the worktree. + +Workers run through the official CLIs with the logins you already have — ordinary use of Claude Code and Codex. Do not wrap this into a product that routes other people's subscriptions. + ## License and acknowledgments -MIT. The review lenses `spec` and `standards` and the smell baseline are adapted from [mattpocock/skills — code-review](https://github.com/mattpocock/skills/blob/main/skills/engineering/code-review/SKILL.md) (MIT), which keeps its two axes separate for the same reason this skill keeps reviewers blind to each other: one angle must not mask another. README structure after [Best-README-Template](https://github.com/othneildrew/Best-README-Template). +MIT. The review lenses `spec` and `standards` and the smell baseline are adapted from [mattpocock/skills — code-review](https://github.com/mattpocock/skills/blob/main/skills/engineering/code-review/SKILL.md) (MIT). The host dispatch table and git-coordination facts borrow from [Hyperskills](https://github.com/hyperb1iss/hyperskills); one worker per independent outcome from [Superpowers](https://github.com/obra/superpowers). README structure after [Best-README-Template](https://github.com/othneildrew/Best-README-Template). diff --git a/skills/delegate-kit/SKILL.md b/skills/delegate-kit/SKILL.md index faecdb2..d01c0b8 100644 --- a/skills/delegate-kit/SKILL.md +++ b/skills/delegate-kit/SKILL.md @@ -1,77 +1,75 @@ --- name: delegate-kit -description: Delegate work to fresh workers from Claude Code or Codex, with the review always on the other model family. Use for multi-module changes, anything that needs an independent review, parallel independent work, a preset ("main-claude", "main-codex") to choose which subscription pays, or when the user says "delegate", "subagent", "worker", "plan this", "review this". +description: Pick the shape of a task before doing it — DIRECT, SCOUT, PLAN, SINGLE, PARALLEL or SEQUENTIAL — and get a non-trivial change reviewed by a fresh worker, on the other model family when its CLI is installed. Use for a feature or refactor described in prose, work across several modules, a question that ends in a verdict ("which do we adopt"), or when the user says "delegate", "subagent", "worker", "scout", "plan this", "review this", "second opinion", or names a preset ("main-claude", "main-codex"). license: MIT -compatibility: Requires bash, git, node >= 20, jq; claude (Claude Code CLI) and/or codex (Codex CLI) logged in with your own subscription. +compatibility: Policy runs anywhere. Native workers need a host with subagents (Claude Code, T3 Code, Codex). Workers from the other family need that family's CLI logged in, plus bash, git, node >= 20, jq. --- # delegate-kit -One delegation policy from any parent — Claude Code, T3 Code (it runs Claude Code), or Codex CLI. The parent stays the orchestrator. A worker from the parent's own family is spawned natively; a worker from the other family is a headless `claude -p` / `codex exec` session started by `scripts/agent-run`. Either way a worker starts empty: it gets a brief and reads the repository itself. Everything below follows from that. +The current session is the **coordinator**: it owns the user's intent, the plan, every brief, integration, verification and the final answer. Workers are fresh sessions that start from a brief and read the repository themselves. Two kinds exist, and the policy names which one it means: -## 1. Default: do it yourself +- **native** — a subagent of the host, same model family as the coordinator: steerable, visible, cheap. +- **external** — a headless CLI session of the other family (`claude -p` / `codex exec`) through `scripts/agent-run`: launched once, collected once, never steered. `references/external.md`. -A worker is a fresh session — system prompt, project instructions, re-reading files — before it does anything useful. Do the task in the current session when any of these hold: +## 1. Shape -- up to ~3 files, clear requirements, low risk; -- an explanation, a question, a diagnosis without code changes; -- a micro-fix after review (typo, rename, missing null check); -- anything destructive or production-adjacent: `sudo`, deletes, services, firewall, certificates, production DB, secrets, SSH. These stay with the parent, in the foreground, with the user watching. +Triage before acting. Pick the first shape that fits; state it in one line. -Delegate for independence, parallelism, or a clean context — those are the only three reasons. Tightly coupled edits stay in one pair of hands: split across workers they come back as merge conflicts and contradictory designs. +| Shape | When | Who | +|---|---|---| +| **DIRECT** | ≤ ~3 files with clear requirements; an explanation or diagnosis; a micro-fix after review; anything destructive or production-adjacent (`sudo`, deletes, services, secrets, prod DB, SSH) | coordinator, in the foreground | +| **SCOUT** | the hard part is *finding*: code spread across a large repo, several plausible causes, current docs to quote | one read-only worker (researcher for docs, otherwise the host's explorer) — then triage again | +| **PLAN** | prose requirements with business rules, ambiguity that reading code cannot settle, > 1 module or > ~10 files; any reading that ends in a *verdict* ("which do we adopt", "is this upgrade safe") | planner, read-only, strongest model | +| **SINGLE** | one well-specified vertical slice too large for DIRECT | one implementer in its own worktree | +| **PARALLEL** | 2–3 slices with disjoint write scopes, stable interfaces between them, each verifiable alone | one implementer per slice, each in its own worktree | +| **SEQUENTIAL** | a result changes the assumptions of the next task: schema → API → UI, diagnosis → fix | one worker at a time; the same worker resumed keeps its context | -## 2. When to delegate — signals +Worker count equals the number of independent outcomes. Coupled edits stay in one pair of hands: split, they return as merge conflicts and two designs. Limits: 2 writers, 4 workers at once, delegation depth 1. -| Signal | Delegate to | -|---|---| -| Requirements in prose with business rules, ambiguity that reading code cannot resolve, > 1 module, or > ~10 files | **planner** (read-only) before any implementation | -| A well-specified vertical task of moderate size; or 2 independent parts that can run in parallel | **implementer**, one per task, each in its own worktree | -| Any delegated implementation; any change in a risk zone (auth, payments, migrations, prod config); a diff > ~50 lines the parent wrote itself; or the user asks | **reviewer** — the other family than the author, always | -| A finding the implementer disputes, or a high-severity finding in a risk zone — **and one that a command cannot settle** | **verifier** (strongest model, read-only, rare) | -| "Fetch the current docs and quote them" — extraction, no recommendation at the end | **researcher** (cheap model, read-only, web) | -| Reading that ends in a **recommendation or a choice** ("which do we adopt", "is this upgrade safe") | **planner**, not researcher — the routing trap | +**Routing trap:** "research" that ends in a recommendation is PLAN, not SCOUT. A quote is research; a verdict is planning, and a wrong verdict propagates downstream. -**Routing trap: research that ends in a decision is planning.** "Research" covers both "read the docs and quote them" and "read the docs and tell me what to do"; only the first is the researcher. Ask what the worker returns: a quote is research, a verdict is planning, and a wrong verdict propagates into everything downstream. +## 2. Spec and preset -**Verify mechanically before spending a verifier.** If one command settles a finding — `npm ls`, a test, a typecheck, a grep — the parent runs it and closes the finding. Reserve the verifier for disputes about intent, severity, or design. +- A grill/interview output or requirements longer than a paragraph → `.scratch//spec.md`; workers are pointed at it. +- The user's words set the **preset**: any phrasing naming who carries the bulk of the work ("main model Claude", "let Codex implement") is `main-claude` / `main-codex`; naming a model for one role ("plan with Fable") is a per-call override. Presets move planner, implementer, researcher; the reviewer follows the author. `references/external.md`. -If the user ran a grill/interview first, its output is the spec: save it as `.scratch//spec.md` (or the repo's own spec location) and point workers at it. +## 3. Route -## 3. Process for one task +Once per role: `agent-run route --role [--preset P]`. Keep its answer — family, model, effort, native or external, exact invocation — for the whole task. `native` → dispatch through the host (`references/hosts.md`); `external` → `references/external.md`. -1. **Triage** with §1–2. State in one line what you do yourself, what you delegate, and under which preset. **The user's words set the preset**: any phrasing that names who should write the code or carry the bulk of the work — "main model Claude", "let Codex implement", "Claude as the implementer", in any language — is `main-claude` / `main-codex`; naming one model for one role ("plan with Fable", "review with Sol") is a per-call override and leaves the preset alone. The roles by their everyday names: implementer = the one who writes the code (executor, coder, worker); planner = the one who decomposes; reviewer = the one who reads the diff. -2. **Spec**: grill output or the user's text → `.scratch//spec.md` when it is more than a paragraph. -3. **Route** each delegated role once — `agent-run route --role [--preset P]` — and keep its answer (family, model, effort, native or external, exact invocation) for the rest of the task. The reasoning behind the answer: `references/dispatch.md`, `references/roles.md`. -4. **Plan**, when the signals say so: `dk-planner` natively or `agent-run run --role planner --brief brief.md`. Summarise the plan to the user and ask only the questions it marked blocking. -5. **Brief** each worker with `references/brief-template.md`: goal, acceptance criteria, where to look, constraints, what to return. Done when a stranger with the repository and nothing else could start. -6. **Worktree** for every writer: `agent-wt create `. External writer → `--cwd ` on `agent-run`, which locks it. Native writer → `agent-wt lock ` and the path in the brief. One writer per worktree; at most 2 writers and 4 workers at once. - The worktree branches from the **current HEAD commit**, so uncommitted work is invisible to the worker. If `git status` is dirty and the task touches that work, tell the user and commit or stash first. Read-only roles see the working tree as it is. -7. **Implement**: `dk-implementer` in the locked worktree, or `agent-run run --role implementer --cwd --brief brief.md`. Two in parallel: `--detach` externally, background dispatch natively. +## 4. Brief - **Learning a worker finished: block on it, or have it push.** One worker and nothing else to do — `agent-run wait ` blocks. Several staggered workers, or a parent that must stay responsive — pass `--on-finish CMD` on `run`. An external worker reports through no harness, so a Codex worker under a Claude parent (or the reverse) stays invisible until you happen to poll. The hook fires once on any terminal state, with the result at `$DK_PAYLOAD_PATH` and `$DK_STATUS` (`--help` has the rest), and has to land where the parent will actually look: a log it tails, a desktop notifier. A non-zero exit is a failed delivery and is retried; delivery survives a dead supervisor and never repeats, so it needs no polling fallback. +Write every brief from `references/brief-template.md`: goal, spec, acceptance criteria as commands, where to look, constraints, worktree path, what to return. Done when a stranger with the repository and nothing else could start. An external worker cannot ask mid-run, so its brief also says: on ambiguity return `status: blocked` with precise `questions`. - **Run states.** `list` for a snapshot of several. `wait`, `list` and `status` all reconcile a supervisor that died without recording its result, so none can report a corpse as still running: it comes back `orphaned`, or `timeout` when the clock ran out. Both are terminal — but neither means the work is lost: a killed worker has usually committed before dying, so read the worktree before you rerun anything. Alongside `status` each run carries a `lifecycle`: `parked` while its session can still be revived with `resume`, `done` once it cannot. +## 5. Worktree - **Timeouts are a fuse, not a schedule.** Defaults are per role — 90 min for writers and planners, 45 for reviewers and researchers — and `--timeout MIN` overrides one run. Raise it for a brief you expect to be long rather than discovering the ceiling by losing a run to it. -8. **Freeze and size the review**: `agent-wt diff > review.diff`, then `agent-run route --role reviewer --diff review.diff` (add `--author-backend self` when the parent wrote the change itself — the review still goes to the other family). It returns the depth (`single` | `panel` | `led`), the reviewers with lens and family, and the cost. `single` runs straight away; a panel is **proposed with those numbers and run on the user's yes**. Rules and lenses: `references/review.md`. -9. **Review**: each reviewer gets the diff, the spec and — on a panel — its lens, in parallel and blind to the others. Merge by the rules in `references/review.md`; at `led` depth the `dk-review-lead` plans before and merges after. -10. **Findings**: mechanical ones the parent fixes; substantive ones go back to the same implementer (`agent-run resume `, or continue the native subagent); disputes → a command first, then the verifier. -11. **Integrate**: merge or open a PR with `gh` per repo conventions; `agent-wt release `, `agent-wt remove `. -12. **Report**: what was done, what was checked, what was not, open questions, which preset ran, which family reviewed at which depth. A check that did not run is reported as not run. +Every writer gets one: `agent-wt create `. External writer → `--cwd ` locks it; native writer → `agent-wt lock ` and the path in the brief. One writer per worktree. The worktree branches from **HEAD**: uncommitted work is invisible to the worker — if `git status` is dirty and the task touches it, tell the user and commit or stash first. -## 4. Worker result contract +## 6. Review -Every worker returns one JSON object (`references/result-schema.json`): `status` (`done` | `blocked` | `failed`), `summary`, `changes`, `checks_run`, `not_verified`, `findings` (reviewer, verifier, lead), `plan` (planner, lead), `questions`, `sources`, `next_steps`. `agent-run` prints it and stores it under `~/.delegate-kit/runs//result.json`. +The author of a non-trivial change does not certify it. A review is **independent** when a fresh read-only worker gets the frozen diff and the spec. Independence, in order of preference: -A worker cannot talk to the user. If it needs an answer it returns `status: blocked` with `questions`; the parent asks the user and continues the same worker with `agent-run resume `. +1. **the other family** than the author — `route` picks it when that family's CLI is installed; +2. **a fresh worker of the author's family** — what `route` returns when the other CLI is missing (`independent: false`, with a note). Report which one ran. -## 5. Limits and safety +Review when: any delegated implementation; any change in a risk zone (auth, payments, migrations, prod config); a coordinator-written diff > ~50 lines; the user asks. Freeze and size it: -- **Delegation depth is 1.** Workers get no subagents of their own — `agent-run` disables them on both CLIs, and the shipped `dk-*` definitions carry no `Agent` tool. -- **Writers** run in a worktree under the backend's own sandbox (`workspace-write` / `acceptEdits`). The dangerous modes (`danger-full-access`, `bypassPermissions`) are outside this skill. -- **Read-only roles** are enforced externally (`read-only` / `plan`) and by instruction plus tool list natively. When the boundary matters — an untrusted diff, a risk zone — dispatch that role externally. -- `hooks/gate.sh` makes dangerous shell commands require the user's confirmation in the parent (Claude: the approval prompt; Codex: denied with instructions to confirm and re-run prefixed `DELEGATE_KIT_CONFIRMED=1`). -- **Quota fallback.** On a usage or rate limit `agent-run` retries the brief once on the other vendor and marks the result `fallback_from`. For a reviewer that can land the review on the author's family — the result says so; report it, or re-run later. `--fallback none` disables it. Resumes never fall back. -- The ledger `~/.delegate-kit/ledger.jsonl` records model, effort, preset, lens, tokens, duration and outcome per external run. Read it before changing a default. +``` +agent-wt diff > review.diff +agent-run route --role reviewer --diff review.diff [--author-backend self] +``` -Scripts: `scripts/agent-run` and `scripts/agent-wt`, by absolute path or on `PATH`; `--help` on each is the reference for flags. Native role definitions: `agents/dk-*.md` (Claude Code) and `references/codex-agents.toml` (Codex), installed by `hooks/install.sh`. +`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`. + +**Findings.** Mechanical ones the coordinator fixes. Substantive ones go back to the same implementer (`agent-run resume `, or continue the native subagent). A dispute is settled by a command first — a test, a typecheck, `npm ls`, a grep; spend a **verifier** only on intent, severity or design. A behavior-changing fix re-runs the affected checks and review. + +## 7. Integrate and report + +Merge or open a PR per repo conventions; `agent-wt release `, `agent-wt remove `. Report: what was done, what was checked, what was not, open questions, which preset ran, which family reviewed at which depth. A check that did not run is reported as not run; a worker's "done" is evidence to inspect. + +## Worker contract + +Every worker returns one JSON object (`references/result-schema.json`): `status` (`done` | `blocked` | `failed`), `summary`, `changes`, `checks_run`, `not_verified`, `findings`, `plan`, `questions`, `sources`, `next_steps`. `blocked` + `questions` → the coordinator asks the user and resumes the same worker. + +Roles, models and prompt hints: `references/roles.md`. Native definitions: `agents/dk-*.md` (Claude Code) and `references/codex-agents.toml` (Codex), installed by `hooks/install.sh`. `--help` on `scripts/agent-run` and `scripts/agent-wt` is the flag reference. diff --git a/skills/delegate-kit/agents/dk-reviewer.md b/skills/delegate-kit/agents/dk-reviewer.md index 6e2f1dc..46bc27c 100644 --- a/skills/delegate-kit/agents/dk-reviewer.md +++ b/skills/delegate-kit/agents/dk-reviewer.md @@ -1,6 +1,6 @@ --- name: dk-reviewer -description: delegate-kit REVIEWER — read-only review of a frozen diff against its spec, returning findings with severity, kind, file, line, evidence and a suggested fix. Dispatch natively from a Claude Code parent only when the change was written by the GPT family; a Claude-written change must be reviewed by `agent-run run --role reviewer --backend codex`. +description: delegate-kit REVIEWER — read-only review of a frozen diff against its spec, returning findings with severity, kind, file, line, evidence and a suggested fix. Dispatch natively from a Claude Code parent when the change was written by the GPT family, or when the Codex CLI is not installed (then report the review as same-family); otherwise a Claude-written change is reviewed by `agent-run run --role reviewer --backend codex`. model: opus effort: high tools: Read, Glob, Grep, Bash diff --git a/skills/delegate-kit/agents/dk-verifier.md b/skills/delegate-kit/agents/dk-verifier.md index a5d2c3d..a7273ba 100644 --- a/skills/delegate-kit/agents/dk-verifier.md +++ b/skills/delegate-kit/agents/dk-verifier.md @@ -1,6 +1,6 @@ --- name: dk-verifier -description: delegate-kit VERIFIER — settles one disputed or high-risk review finding as confirmed, refuted or needs-human, with evidence. Rare and read-only. Dispatch natively from a Claude Code parent when the reviewer was on the GPT family; otherwise use `agent-run run --role verifier --backend codex`. +description: delegate-kit VERIFIER — settles one disputed or high-risk review finding as confirmed, refuted or needs-human, with evidence. Rare and read-only. Dispatch natively from a Claude Code parent when the reviewer was on the GPT family or the Codex CLI is not installed; otherwise use `agent-run run --role verifier --backend codex`. model: fable effort: high tools: Read, Glob, Grep, Bash diff --git a/skills/delegate-kit/references/codex-agents.toml b/skills/delegate-kit/references/codex-agents.toml index eb2d3fe..be3bc6d 100644 --- a/skills/delegate-kit/references/codex-agents.toml +++ b/skills/delegate-kit/references/codex-agents.toml @@ -7,7 +7,8 @@ # These roles are for the case where the *parent* is Codex and the worker should # stay in the GPT family: the parent spawns them with its own `spawn_agent` tool # instead of paying for a fresh `codex exec` session. A worker from the Claude -# family is always started externally with `agent-run run --backend claude`. +# family is started externally with `agent-run run --backend claude`; when that +# CLI is missing, `agent-run route` falls the reviewer back to dk-reviewer here. # # Models: the Codex CLI exposes gpt-5.6-sol, gpt-5.6-terra and gpt-5.6-luna # (check `~/.codex/models_cache.json`). There is no `gpt-5.6-pro` worker, so the diff --git a/skills/delegate-kit/references/dispatch.md b/skills/delegate-kit/references/dispatch.md deleted file mode 100644 index 83911b8..0000000 --- a/skills/delegate-kit/references/dispatch.md +++ /dev/null @@ -1,72 +0,0 @@ -# Dispatch: native or external, and which subscription pays - -Every worker has two independent properties: which **family** the model comes from (Claude or GPT) and how it is **dispatched** — natively by the parent harness, or externally as a headless `claude -p` / `codex exec` session through `agent-run`. `agent-run route --role ` resolves both; this file is the reasoning behind its answer. - -## Native inside the family, external across it - -A parent can only spawn its own family natively: - -| Worker family | parent = Claude Code / T3 | parent = Codex | -|---|---|---| -| Claude (`fable`, `opus`, `sonnet`, `haiku`) | **native** — `Agent` tool, `subagent_type: dk-` | external — `agent-run run --backend claude` | -| GPT (`gpt-5.6-sol`, `-terra`, `-luna`) | external — `agent-run run --backend codex` | **native** — `spawn_agent`, agent `dk-` | - -Native is the default inside the family: no CLI cold start, no log to read back, the result lands in the parent's own turn, and the subagent can be continued in place. The role definitions carry the model, the effort and the tool list — `agents/dk-*.md` for Claude Code, `[agents.dk-*]` in `~/.codex/config.toml` for Codex, both installed by `hooks/install.sh` — so a native dispatch is the role name plus the brief. The brief is the same document either way; `references/brief-template.md`. - -### What native gives up - -Exactly the things a bad delegation loses first, so choose external — even inside the family — when one of them matters: - -- **An enforced sandbox.** External read-only roles run under `claude --permission-mode plan` / `codex -s read-only`. A native role is read-only by instruction and by a tool list with no writer in it — fine for a reviewer you dispatched yourself, not fine as the isolation boundary around an untrusted change. -- **The strict result schema.** External workers are held to `references/result-schema.json` by the vendor's structured-output flag; native ones follow it because the definition asks. Expect to re-read a malformed result occasionally. -- **Ledger, run id, timeout, quota fallback.** All live in `agent-run`. To compare models later, resume a worker tomorrow, or have the other vendor pick up when this one hits its limit, go external. -- **The write-lock for free.** `agent-run` takes the worktree lock itself; a native writer needs the parent to take and release it: - -``` -agent-wt create && agent-wt lock --label dk-implementer -# dispatch the native subagent; the brief names the worktree path -agent-wt diff > review.diff ; agent-wt release -``` - -`agent-run` refuses to start an external writer in a worktree locked this way, and the reverse — one writer per worktree, however it was dispatched. - -## Presets: which subscription pays - -Quota is not symmetric over time. A preset moves the token-heavy roles — **planner, implementer, researcher** — onto one family: - -| Preset | planner | implementer | researcher | reviewer / verifier / lead | -|---|---|---|---|---| -| `auto` (default) | claude | codex | claude | derived from the author | -| `main-claude` | claude | claude | claude | derived from the author | -| `main-codex` | codex | codex | codex | derived from the author | - -The reviewer and verifier are **never moved by a preset**. They are derived from whoever wrote the code, because independence is the reason they exist; a knob that could flip them would buy quota with the one property delegation is for. The consequence is explicit: `main-claude` implies a Codex reviewer, `main-codex` a Claude one. That is cheap — a review is one read-only pass over a frozen diff, a fraction of what the implementer spends — so the preset still moves the bulk of the cost. If the family you are sparing is also the one that must review, run the review later rather than on the author's own family. - -Under `auto`, a UI/design-heavy implementation goes to Claude (`--kind ui`); the reviewer follows. - -Set it, in order of precedence: - -1. in the invocation — `/delegate-kit main-claude`, `$delegate-kit main-codex`, or the words in the request. Aliases: `main-gpt`, `main-openai` → `main-codex`; `main-anthropic` → `main-claude`. Pass it as `--preset` to every `agent-run` call in that task; -2. per call — `agent-run run --preset main-claude …`; -3. for the shell — `DELEGATE_KIT_PRESET=main-claude`; -4. persistently — `agent-run preset main-claude` (`~/.delegate-kit/config.json`; `agent-run preset` alone prints the current one). - -Explicit `--backend` / `--model` / `--effort` always win over the preset, and the user can simply name a model ("plan with Fable", "review with Sol"). - -## Changing the defaults for yourself - -The role table shipped in `scripts/agent-run` is the default for every user of the skill: planner, verifier and review lead on the strongest model, implementer and reviewer on the workhorse tier. Change it for yourself — not for everyone — in `~/.delegate-kit/config.json`: - -```json -{ - "preset": "main-claude", - "roles": { - "planner": { "claude": ["fable", "xhigh"] }, - "reviewer": { "codex": ["gpt-5.6-sol", "xhigh"] } - } -} -``` - -Each entry is `["model", "effort"]` per family; anything you leave out keeps the shipped default, and `agent-run preset` prints the effective table. A malformed entry is reported and ignored, never applied half-way. This is the place for "I always want the planner on xhigh" — one edit instead of saying it every time, and the repository's defaults stay universal. - -A Claude parent under `main-claude` runs almost everything natively and pays for exactly one external session — the Codex reviewer. That is the cheapest shape this skill has. diff --git a/skills/delegate-kit/references/external.md b/skills/delegate-kit/references/external.md new file mode 100644 index 0000000..9279350 --- /dev/null +++ b/skills/delegate-kit/references/external.md @@ -0,0 +1,64 @@ +# External workers: the other family, through `agent-run` + +A coordinator can spawn only its own family natively. A worker from the other family is a **headless CLI session** — `claude -p` or `codex exec` — started by `scripts/agent-run`. It is launched once with a complete brief, runs to a terminal state, and returns one JSON result. Nothing steers it mid-run; a follow-up is `agent-run resume `. That is the whole difference from a native worker, and every rule below follows from it. + +| Worker family | coordinator = Claude Code / T3 | coordinator = Codex | +|---|---|---| +| Claude (`fable`, `opus`, `sonnet`, `haiku`) | native | **external** — `agent-run run --backend claude` | +| GPT (`gpt-5.6-sol`, `-terra`, `-luna`) | **external** — `agent-run run --backend codex` | native | + +`agent-run route --role ` resolves family and dispatch in one call and prints the exact invocation. `run` without `route` works too; it warns when the worker could have been native. + +## Preflight + +Before the first external call of a task, confirm the CLI is there: `command -v codex` / `command -v claude`. `route` does this itself: when the other family's CLI is missing, a reviewer or verifier comes back as a fresh **native** worker with `independent: false` and a note — the review still happens, on the author's family, and the report says so. Any other role in that state needs the CLI installed or `--backend` on the coordinator's own family. + +## Starting and collecting + +``` +agent-run run --role --backend [--cwd ] --brief brief.md [--detach] [--timeout MIN] [--on-finish CMD] +agent-run wait | status | list | log [out|err|prompt] | kill | resume --brief next.md +``` + +- **One worker, nothing else to do** → `run` blocks and prints the result. +- **Several staggered workers, or a coordinator that must stay responsive** → `--detach`, then either `wait ` or `--on-finish CMD`. The hook fires exactly once per run on any terminal state, with the result on stdin and `DK_RUN_ID`, `DK_STATUS`, `DK_RESULT_PATH` in the environment; a non-zero exit is retried, delivery survives a dead supervisor. Point it where the coordinator will actually look — a log it tails, a desktop notifier. Without a hook, an external worker is invisible until polled. +- **Run states.** `status` is how a run ended; `lifecycle` is whether `resume` can still revive it (`parked`) or not (`done`). `orphaned` (supervisor died) and `timeout` are terminal but the worker has usually committed before dying — read the worktree before rerunning. +- **Timeouts are a fuse.** Writers and planners 90 min, reviewers and researchers 45; `--timeout` raises one run. Set it up front for a brief you expect to be long. + +### Under a Codex coordinator + +Codex's shell tool yields after `yield_time_ms` (default 1 s) and returns `Process running with session ID N` — the process is alive, the output is simply not there yet. A blocking `agent-run run` or `wait` therefore needs `yield_time_ms: 300000` on every call, and an early return is reaped by session id, never retried with a fresh `run` (each retry starts one more worker). `--detach` + `status` polling sidesteps the issue. + +## Presets: which subscription pays + +Quota is not symmetric over time. A preset moves the token-heavy roles — planner, implementer, researcher — onto one family: + +| Preset | planner | implementer | researcher | reviewer / verifier | +|---|---|---|---|---| +| `auto` (default) | claude | codex | claude | derived from the author | +| `main-claude` | claude | claude | claude | derived from the author | +| `main-codex` | codex | codex | codex | derived from the author | + +The reviewer follows the **author**, never the preset: `main-claude` implies a Codex reviewer, `main-codex` a Claude one. A review is one read-only pass over a frozen diff, a fraction of what the implementer spends, so the preset still moves the bulk of the cost. Under `auto`, `--kind ui` sends the implementation to Claude and the reviewer follows. + +Precedence: the words in the request (`main-claude`, `main-codex`; aliases `main-gpt`/`main-openai`, `main-anthropic`) → `--preset` per call → `DELEGATE_KIT_PRESET` → `agent-run preset

` persisted in `~/.delegate-kit/config.json` → `auto`. Explicit `--backend` / `--model` / `--effort` win over the preset, and the user can name a model for one role ("review with Sol"). + +Per-role defaults for one user, in the same file — the shipped table in `scripts/agent-run` stays the default for everyone: + +```json +{ "preset": "main-claude", + "roles": { "planner": { "claude": ["fable", "xhigh"] }, "reviewer": { "codex": ["gpt-5.6-sol", "xhigh"] } } } +``` + +`agent-run preset` alone prints the effective table. A malformed entry is reported and ignored. + +## Limits and safety + +- **Delegation depth is 1.** `agent-run` disables subagents on both CLIs; the shipped `dk-*` definitions carry no `Agent` tool. +- **Writers** run in a worktree under the backend's own sandbox (`workspace-write` / `acceptEdits`); the dangerous modes are outside this skill. `agent-run` refuses a worktree locked for a native writer, and the reverse. +- **Read-only roles** run under `codex -s read-only` / `claude --permission-mode plan` — the enforced boundary a native role lacks. When it matters (an untrusted diff, a risk zone), dispatch that role externally even inside the family. +- **Quota fallback.** On a usage or rate limit `agent-run` retries the brief once on the other family and marks the result `fallback_from`. For a reviewer that can land the review on the author's family — the result says so; report it or re-run later. `--fallback none` disables it; resumes never fall back. +- `hooks/gate.sh` makes dangerous shell commands need the user's confirmation in the coordinator (Claude: the approval prompt; Codex: denied with instructions to confirm and re-run prefixed `DELEGATE_KIT_CONFIRMED=1`). +- The ledger `~/.delegate-kit/ledger.jsonl` records model, effort, preset, lens, tokens, duration and outcome per external run. Read it before changing a default. + +A Claude coordinator under `main-claude` runs everything natively and pays for exactly one external session — the Codex reviewer. That is the cheapest shape this skill has. diff --git a/skills/delegate-kit/references/hosts.md b/skills/delegate-kit/references/hosts.md new file mode 100644 index 0000000..dfa1917 --- /dev/null +++ b/skills/delegate-kit/references/hosts.md @@ -0,0 +1,27 @@ +# Hosts: how a native worker is dispatched + +A native worker is a subagent of the host, in the coordinator's own family. The policy is the same everywhere; the fan-out verb differs. A worker from the other family is never native — `external.md`. + +| Host | Native fan-out | Role definition | Isolation for a writer | Completion | +|---|---|---|---|---| +| Claude Code, T3 Code | `Agent` tool, `subagent_type: dk-`; several in one message run in parallel; `run_in_background` for long ones | `~/.claude/agents/dk-*.md` (symlinked by `hooks/install.sh`) | `agent-wt create` + `agent-wt lock`, path in the brief; or the tool's own `isolation: worktree` when the branch does not need `agent-wt diff` | task notification; `SendMessage` continues the same subagent | +| Codex CLI | `spawn_agent`, agent `dk-` | `[agents.dk-*]` in `~/.codex/config.toml` (spliced by `hooks/install.sh`) | `agent-wt create` + `agent-wt lock` | the tool result; Codex asks the user before spawning unless a standing grant exists | +| Pi, OpenCode, other | the host's dispatch tool if one exists | the brief carries the role preamble from `roles.md` | `agent-wt create` + `agent-wt lock` | the tool result | +| No fan-out at all | **serial**: the coordinator runs one worker after another, or does the slice itself | — | `agent-wt` still applies | — | + +Serial is a dispatch mode, not a failure: one long-lived worker resumed with each next slice beats a queue of orphaned parallel ones. External review still works on any host that can run a shell. + +## Native gives up three things + +Choose external even inside the family when one matters: + +- **An enforced sandbox.** External read-only roles run under `claude --permission-mode plan` / `codex -s read-only`. A native role is read-only by instruction and tool list — fine for a reviewer you dispatched yourself, thin as the boundary around an untrusted diff. +- **The strict result schema, ledger, run id, timeout, quota fallback.** All live in `agent-run`. +- **The write-lock for free.** `agent-run --cwd` takes it; a native writer needs `agent-wt lock` before and `agent-wt release` after. + +## Git with parallel writers + +- Each writer commits only its own paths; `git add `, never `git add -A`. +- On `index.lock`, find the owner (`lsof`, `ps`) before touching it: live owner → wait; none → stale, remove. +- `git log --oneline -20` in each worktree shows a stalled or off-pattern worker sooner than its report does. +- Push is the coordinator's call after integration, never the worker's. diff --git a/skills/delegate-kit/references/review.md b/skills/delegate-kit/references/review.md index 9a5dc76..a9ab01d 100644 --- a/skills/delegate-kit/references/review.md +++ b/skills/delegate-kit/references/review.md @@ -4,7 +4,7 @@ How many reviewers a diff deserves, which angle each one takes, and how their fi ## Independence is the first slot, not the whole review -One reviewer from the other family than the author buys **independence**: two families share fewer blind spots than one. That is the invariant of this skill and nothing moves it — not a preset, not a panel, not quota. +One reviewer from the other family than the author buys **independence**: two families share fewer blind spots than one. No preset, panel or quota pressure moves slot A off the other family. The one thing that does is availability: when that family's CLI is not installed, `route` places slot A as a fresh read-only worker of the author's family, marks it `independent: false`, and the report names which one ran. A fresh context is still a real review; the other family is the stronger one. A second reviewer with the same brief buys almost nothing: the obvious findings come back twice and the subtle ones stay missed, because both reviewers looked from the same angle. What a second slot should buy is a second **lens**. So a panel is composed as lenses first, families second: diff --git a/skills/delegate-kit/references/roles.md b/skills/delegate-kit/references/roles.md index 8352aa8..8cf88fe 100644 --- a/skills/delegate-kit/references/roles.md +++ b/skills/delegate-kit/references/roles.md @@ -1,6 +1,6 @@ # Roles: who, why, and how to prompt them -`agent-run route --role ` prints the default for any role; this file is the reasoning, so you can deviate deliberately. Where a worker runs and which subscription pays are in `dispatch.md`; review depth and lenses in `review.md`. +`agent-run route --role ` prints the default for any role; this file is the reasoning, so you can deviate deliberately. Where a worker runs and which subscription pays are in `external.md`; review depth and lenses in `review.md`. ## The two families @@ -21,7 +21,7 @@ The Codex CLI is offered `sol`, `terra` and `luna` only (`~/.codex/models_cache. |---|---|---|---| | planner | `fable` high / `gpt-5.6-sol` xhigh | preset | read-only | | implementer | `opus` high / `gpt-5.6-sol` high | preset; `--kind ui` → Claude under `auto` | write, in a worktree | -| reviewer | `opus` high / `gpt-5.6-sol` high | **the other family than the author** | read-only | +| reviewer | `opus` high / `gpt-5.6-sol` high | **the other family than the author** when its CLI is installed; else a fresh worker of the author's family, reported | read-only | | review-lead | `fable` high / `gpt-5.6-sol` xhigh | the planner's family | read-only | | verifier | `fable` high / `gpt-5.6-sol` xhigh | third party to the reviewer | read-only | | researcher | `sonnet` medium / `gpt-5.6-terra` medium | preset | read-only, web | @@ -51,7 +51,7 @@ The Codex CLI is offered `sol`, `terra` and `luna` only (`~/.codex/models_cache. ## reviewer - **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 — and it is the first slot of a panel, never something a preset moves. +- **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`. - **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). @@ -88,7 +88,7 @@ Almost never as workers, though. The start-up cost dominates — a worker that r - Routine orchestration: Opus 5 / Sol / Sonnet 5 / Terra. The parent writes briefs and reads reports; that does not need the top model. - Switch the parent up (Fable / Sol xhigh) for a grill session, an architecture decision, or a hard bug the parent must reason about itself. Switch back afterwards. -- The parent's family decides which roles can be native at all (`dispatch.md`). +- The parent's family decides which roles can be native at all (`external.md`, `hosts.md`). ## Tuning diff --git a/skills/delegate-kit/scripts/agent-run b/skills/delegate-kit/scripts/agent-run index 4bc5235..2a2b7fa 100755 --- a/skills/delegate-kit/scripts/agent-run +++ b/skills/delegate-kit/scripts/agent-run @@ -136,6 +136,7 @@ function reconcile(m) { return m; } const sh = (cmd, args, opts = {}) => spawnSync(cmd, args, { encoding: "utf8", ...opts }); +const cliAvailable = (backend) => sh("/bin/sh", ["-c", `command -v ${backend === "codex" ? "codex" : "claude"}`]).status === 0; function parseArgs(argv) { const out = { _: [] }; @@ -489,16 +490,34 @@ function resolveRoute(opts = {}) { backend = opts.backend; } - const [defModel, defEffort] = ROLES[role][backend]; - const model = opts.model && opts.model !== true ? opts.model : defModel; - const effort = opts.effort && opts.effort !== true ? opts.effort : defEffort; const write = ROLES[role].write; - const dispatch = parent && parent === backend ? "native" : "external"; - why.push(dispatch === "native" - ? `the parent (${parent}) is in the same family, so it can spawn this worker natively` - : parent + let dispatch = parent && parent === backend ? "native" : "external"; + // Independence prefers the other family, but is not gated on it: when that family's CLI + // is not installed, the reviewer/verifier lands on the parent's own family as a fresh, + // read-only native worker, and `independence` records which one the caller got. + let independence = ["reviewer", "verifier"].includes(role) ? (backend === author ? "same-family" : "other-family") : undefined; + let fellBack = false; + if (dispatch === "external" && !cliAvailable(backend)) { + const userPinned = opts.backend && opts.backend !== true && !opts._composed; + if (parent && ["reviewer", "verifier"].includes(role) && !userPinned) { + why.push(`the ${backend} CLI is not installed, so the ${role} falls back to a fresh native ${parent} worker — say so in the report`); + backend = parent; dispatch = "native"; fellBack = true; + independence = backend === author ? "same-family" : "other-family"; + } else { + why.push(`the ${backend} CLI is not installed; install it or pass --backend ${otherBackend(backend)}`); + } + } + if (dispatch === "native") { + if (!fellBack) why.push(`the parent (${parent}) is in the same family, so it can spawn this worker natively`); + } else { + why.push(parent ? `the parent (${parent}) cannot spawn a ${backend} worker natively, so it goes through agent-run` : "no parent harness detected (pass --parent), so external dispatch is assumed"); + } + + const [defModel, defEffort] = ROLES[role][backend]; + const model = opts.model && opts.model !== true ? opts.model : defModel; + const effort = opts.effort && opts.effort !== true ? opts.effort : defEffort; const external = ["agent-run run", `--role ${role}`, `--backend ${backend}`, `--model ${model}`, `--effort ${effort}`, write ? "--cwd " : "", "--brief "].filter(Boolean).join(" "); @@ -515,7 +534,7 @@ function resolveRoute(opts = {}) { ? "take the lock yourself: `agent-wt create ` then `agent-wt lock --label dk-implementer`, name the worktree path in the prompt, and `agent-wt release ` when the subagent returns" : "role definition lives in ~/.codex/config.toml under [agents.dk-*]; install it with hooks/install.sh" }; - return { role, preset, parent: parent ?? "unknown", backend, model, effort, write, dispatch, invoke, why, + return { role, preset, parent: parent ?? "unknown", author, backend, model, effort, write, dispatch, independence, invoke, why, prefer_external_when: dispatch === "native" ? [ "you need the strict JSON result contract, a ledger entry or a resumable run id", "you need a hard timeout, a detached parallel writer, or automatic cross-vendor quota fallback", @@ -583,10 +602,11 @@ function cmdRoute(argv) { const suggested = stats ? suggestDepth(stats, kind) : null; let depth = argv.depth && argv.depth !== true ? String(argv.depth) : (suggested ? suggested.depth : "single"); if (!DEPTHS.includes(depth)) die(`--depth must be ${DEPTHS.join("|")} (got ${depth})`); - const author = base.backend === "claude" ? "codex" : "claude"; // base already placed the reviewer opposite the author + const author = base.author; const reviewers = reviewComposition(depth, author, kind).map((r) => { - const rr = resolveRoute({ ...argv, role: "reviewer", backend: r.backend, depth: undefined, diff: undefined }); - return { ...r, model: rr.model, effort: rr.effort, dispatch: rr.dispatch, invoke: rr.invoke }; + const rr = resolveRoute({ ...argv, role: "reviewer", backend: r.backend, _composed: true, depth: undefined, diff: undefined }); + const note = rr.why.find((w) => w.includes("falls back")); + return { ...r, backend: rr.backend, independent: rr.backend !== author, model: rr.model, effort: rr.effort, dispatch: rr.dispatch, invoke: rr.invoke, ...(note ? { note } : {}) }; }); const lead = depth === "led" ? (() => { const l = resolveRoute({ ...argv, role: "review-lead", backend: undefined, depth: undefined, diff: undefined }); return { backend: l.backend, model: l.model, effort: l.effort, dispatch: l.dispatch, invoke: l.invoke }; })() : null; const sessions = reviewers.length + (lead ? 2 : 0); @@ -899,7 +919,8 @@ lifecycle is orthogonal to status: running, or parked when the session can still \`resume\`, or done when it cannot. status says how it ended, lifecycle whether you can go back to it. Presets move the token-heavy roles (planner, implementer, researcher) to one family; reviewer and -verifier always stay on the other family than the author. Precedence: --preset > DELEGATE_KIT_PRESET +verifier follow the author: the other family when its CLI is installed, otherwise a fresh native +worker of the parent's family (route marks it independent:false). Precedence: --preset > DELEGATE_KIT_PRESET > ${CONFIG_FILE} > auto. Per-role model/effort defaults can be overridden in the same file under "roles": { "": { "claude": ["model","effort"], "codex": ["model","effort"] } }; \`agent-run preset\` shows the effective table.