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:

  1. Register the gates — add to your gates.list:

    check-gate-tamper
    check-agent-tier-explicit   # every tracked agent definition states a model: tier
    

    They resolve through gate-sdk’s registry path and the pre-commit hook runs those their # graph: manifests trigger, read at commit. --install-hooks on the gate binary GATE_SDK_NATIVE_BIN names places both hooks in a clone once, and a gate registered later is reached with no re-install.

  2. Bind the protocol skill and add its resident pointer:
    • Create agent-execution.md in your agent-skill directory as a binding shim naming templates/agent-execution.md and binding its two slots — the shared-file roster and the validate battery (SPEC.md §One template, a resident pointer).
    • Add a ### Agent execution block to your CLAUDE.md: your delegation pre-authorization sentence plus the /agent-execution pointer. The protocol loads behind that trigger, so the block stays a pointer, never a digest.
  3. Wire a usage.txt producer so usage-verdict has a snapshot to read — point your harness statusLine at bash "<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}), point DELEGATION_KIT_REFRESH_CMD at the gate binary GATE_SDK_NATIVE_BIN names with its --usage-poll arm 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.

  4. Optional — wire the Agent budget guard: register the gate binary GATE_SDK_NATIVE_BIN names, under <project-dir> and with no executable suffix, as a hook’s command under PreToolUse matcher Agent in your harness settings file, with --hook agent-budget-guard as its args. There is nothing to copy: the guard is a binary arm, and the registration names it directly. It fires usage-verdict at every dispatch, blocking on a PAUSE verdict and advising otherwise (SPEC.md §The delegation model). Unwired, it is inert.

  5. 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 being judgment, routing and mechanical unless DELEGATION_KIT_TIER_CLASSES names your own; declare each agent definition’s tier: in its frontmatter, then run the gate binary GATE_SDK_NATIVE_BIN names with --emit agent-tiers --write to generate its model:, and its effort: where the class binds one (SPEC.md §The tier binding).

  6. Optional — wire the turn-end liveness hook: register the same binary as a hook’s command under SubagentStop in your harness settings file (that event takes no matcher), with --hook subagent-stop-liveness as its args. 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’s background_tasks shows a running shell task of its own; it emits no hook JSON on either path, except a systemMessage warning 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.

  7. Optional — retune: copy templates/delegation-config.knobs into 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.