delegation-kit
Safe delegated-Agent execution for budget-bounded sessions. A supervisor dispatches sub-agents that are cheap to spawn and expensive to trust; this kit packages the supervisor-side protocol plus the pieces of it a machine can hold: a trustworthy budget verdict (usage-verdict), the gates and the opt-in hooks the Install steps wire.
Why: three failure surfaces dominate delegation. Shared mutable state — two committing agents race the git index and one sweeps the other’s staged files under the wrong message (the index and HEAD are shared for every committing agent, disjoint source files notwithstanding). Interrupted long units — a usage-window wall fires mid-flight and the uncommitted investigation dies with the session. Untrustworthy self-reports — a sub-agent’s “passed” claim, or a gate quietly weakened to make its commit pass. The protocol closes all three, and those pieces are its mechanical floors. See SPEC.md for the full contracts.
This file ships in the installer payload; what that payload withholds, and where this page’s SPEC.md links then resolve, is gate-sdk/SPEC.md §Consumer payload.
Install
Vendor the kit beside gate-sdk (required), then:
-
Register the gates — add to your
gates.list:check-gate-tamper check-agent-tier-explicit # every tracked agent definition states a model: tierThey resolve through gate-sdk’s registry path and the pre-commit hook runs those their
# graph:manifests trigger, read at commit.--install-hookson the gate binaryGATE_SDK_NATIVE_BINnames places both hooks in a clone once, and a gate registered later is reached with no re-install. - Bind the protocol skill and add its resident pointer:
- Create
agent-execution.mdin your agent-skill directory as a binding shim namingtemplates/agent-execution.mdand binding its two slots — the shared-file roster and the validate battery (SPEC.md §One template, a resident pointer). - Add a
### Agent executionblock to yourCLAUDE.md: your delegation pre-authorization sentence plus the/agent-executionpointer. The protocol loads behind that trigger, so the block stays a pointer, never a digest.
- Create
-
Wire a
usage.txtproducer sousage-verdicthas a snapshot to read — point your harnessstatusLineatbash "<project-dir>/gate-sdk/bin/run-gates.sh" --statusline(<project-dir>is your harness’s project-directory variable, here and in the two hook registrations; Claude Code’s is${CLAUDE_PROJECT_DIR}), pointDELEGATION_KIT_REFRESH_CMDat the gate binaryGATE_SDK_NATIVE_BINnames with its--usage-pollarm so every verdict call refreshes the snapshot on demand (or wire the same poller under a timer), keeping it fresh while a supervising session sits static, or have any producer honour the snapshot contract (SPEC.md §The usage.txt contract). The statusline producer also renders a status bar, whose field set is SPEC.md §The statusline arm’s; the three mandatory snapshot lines are the floor, and supplying the optional weekly keys arms the second (7-day) pause axis. -
Optional — wire the Agent budget guard: register the gate binary
GATE_SDK_NATIVE_BINnames, under<project-dir>and with no executable suffix, as a hook’scommandunderPreToolUsematcherAgentin your harness settings file, with--hook agent-budget-guardas itsargs. There is nothing to copy: the guard is a binary arm, and the registration names it directly. It firesusage-verdictat every dispatch, blocking on a PAUSE verdict and advising otherwise (SPEC.md §The delegation model). Unwired, it is inert. -
Optional — bind your tiers: set one
DELEGATION_KIT_TIER_MODEL[] = <class>=<model>[,<effort>]element per class in your delegation config, an alias to follow the newest model or an exact model id to pin one, the classes beingjudgment,routingandmechanicalunlessDELEGATION_KIT_TIER_CLASSESnames your own; declare each agent definition’stier:in its frontmatter, then run the gate binaryGATE_SDK_NATIVE_BINnames with--emit agent-tiers --writeto generate itsmodel:, and itseffort:where the class binds one (SPEC.md §The tier binding). -
Optional — wire the turn-end liveness hook: register the same binary as a hook’s
commandunderSubagentStopin your harness settings file (that event takes no matcher), with--hook subagent-stop-livenessas itsargs. It logs one line per subagent turn end saying whether any launch record named a live producer, and refuses the turn end — exit 2, its stderr the blocking reason — when that reading is a refusal verdict, or while the payload’sbackground_tasksshows a runningshelltask of its own; it emits no hook JSON on either path, except asystemMessagewarning when a knob it reads cannot resolve and the turn end went unguarded (SPEC.md §The turn-end liveness hook). There is no knob: unwiring is the valve. Unwired, it is inert; wiring it is a permission-surface change, so it is yours to make. - Optional — retune: copy
templates/delegation-config.knobsinto your gates dir and override the budget thresholds, the gate-file globs, or the meta-layer prefixes. Defaults are this repo’s single-operator layout.
Use
Run these arms with the gate binary GATE_SDK_NATIVE_BIN names, where init places it. In PowerShell, spell each line ./scripts/checkwright-gates <arm> at the repository root, with .exe on native Windows:
gates="${GATE_SDK_NATIVE_BIN:-./scripts/checkwright-gates}"
"$gates" --usage-verdict # budget verdict: exit 0 OK/RESET-OK, 1 PAUSE, 2 STALE
"$gates" --usage-verdict <snapshot> # verdict for an explicit usage.txt (test injection)
"$gates" --model-verdict --expect judgment # running-model verdict against the tier binding: exit 0 OK, 1 BELOW/UNBOUND, 2 UNKNOWN
"$gates" --foreign-run <adapter> <prompt-file> # one audit on a configured foreign adapter, in a scratch clone: exit 0 OK, 1 REFUSED, 2 FAILED
"$gates" --foreign-run <adapter> --budget # that adapter's budget verdict, spawning nothing: exit 0 OK/RESET-OK, 1 PAUSE, 2 STALE/OFF
"$gates" --foreign-resume <key> <prompt-file> # the next turn of a session a resumable adapter opened: exit 0 OK, 1 REFUSED, 2 FAILED; --close ends it
"$gates" --emit usage-trend # footprint trend over the sample log (needs DELEGATION_KIT_USAGE_HISTORY)
"$gates" --wait-probe sweep # wait-primitive probe: the harness-uninvolved reproducer (sleeps for its declared sweep)
"$gates" --wait-probe report # classify the recorded trials and print the verdict (exit 1 when none are)
With DELEGATION_KIT_USAGE_HISTORY set, usage-verdict logs one sample per call; --emit usage-trend reads that log and reports per-window footprint evolution and weekly headroom (advisory — exit 0/2, never a pause verdict).
check-gate-tamper runs from the pre-commit hook and the battery; invoke it directly with --fixture <dir> only for testing.
Test
Run this arm the same way:
gates="${GATE_SDK_NATIVE_BIN:-./scripts/checkwright-gates}"
"$gates" --run-gate-tests delegation-kit/gate-tests delegation-kit/checks # every gate's fixture pair
The usage-verdict decision table and the usage-trend assertions are not shell runners: they live in the gate binary’s crate test lane, where they read the same usage-tests/ fixtures off disk and reach both subjects in process — no shell subject remains on either (SPEC.md §Testing).
License
Licensed under Apache-2.0, with the text in LICENSE beside this file in an installed copy and at the repository root in the source tree.