Add worker agent selection to /kickoff#39
Merged
Conversation
Foundation for kickoff agent selection. One CLI x model per entry
(claude:fable/opus/sonnet, codex:gpt-5.6-terra/sol, grok:grok-4.5/composer),
registry-driven — no hardcoded alias if-chains.
Subcommands:
- list [--json] — probe every entry (per-CLI install+auth), table/JSON
- resolve <sel> — map a shorthand flag / cli:model name / bare cli to the
launch argv + metadata; exit 3 if the CLI is unavailable
- auto — first available entry per a configurable ranking (exit 1
if none); deterministic, not an LLM judgment
- default/last — persisted selection state (~/.claude/work-system-agent)
- rank — the effective --auto ranking
Availability probe is work-system-owned (no swarm dependency). supports=
capability metadata (continue/close-exit/statusline vs commit/pr) feeds the
later /close + /continue degradation paths. bash 3.2-safe; covered by
test_agent_registry.py with fake-CLI stubs for deterministic availability.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
launch mode no longer hardcodes `claude … /continue`. It takes an optional agent selector (4th positional) and resolves it through agent-registry.sh into the worker argv, then execs it — staying argv-exec (no shell-typing race) and CLI-agnostic. With no selector it preserves the legacy claude-default path. - exit 2 on unknown selector, exit 3 (with unavailable=/note= on stdout) when the chosen CLI is not available, so the caller shows a clear "run: … login" - emits agent=<cli:model> so the caller reports which worker launched - resume mode is unchanged (claude -c is claude-session-specific) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
/kickoff <task> now takes an optional agent selector and wires it through agent-registry.sh + herdr-launch.sh: - Arguments section: the task name is the non-`--` token; the trailing `--…` selector picks the worker (never mistaken for the task name) - Step 12 "Select the worker agent": flag -> selector; --auto/--last/--default via the registry; no flag -> AskUserQuestion picker over `list` output (unavailable entries marked, not hidden) - Step 13 launch: passes SELECTOR to herdr-launch; branches on exit 2/3 (unknown / unavailable) and reports the resolved agent=; records `last set` - Manual (non-herdr) block resolves the selector to the exact per-CLI command Resolution/availability stay in the script — the skill never hardcodes the alias table (mirrored in prose for readers only). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
Non-claude workers (codex/grok) lack the claude-session lifecycle hooks, so the two skills that assume them now say so honestly: - /close: note that teardown is CLI-safe by construction — Scenario A (close-tab) is tab-level for any worker; Scenario B (self-exit /exit + SessionEnd hook) is only reachable from inside a claude session, so /exit is never injected into a codex/grok worker (those close via Scenario A). - /continue reopen: `claude -c` resumes a claude worker; for a codex/grok task the tab-reopen is CLI-agnostic but the user must resume the real worker (`codex resume --last` / `grok -c`), not claude -c. git/PR-derived state (/status, /list, [ws] statusline) already works for any worker. Document, don't fake — per-task agent memory is a later idea. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
- plugin.json + marketplace.json → 1.9.0 - CHANGELOG: 1.9.0 entry - work-system README: /kickoff agent choice, new "Worker agent selection" section (flags/picker/ranking/degradation + AGENTS.md option), herdr bullets made CLI-agnostic, workflow step 2 shows the flags - top-level README: kickoff worker-choice summary Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
….2.101)
Live testing showed grok 0.2.101 rejects `grok-composer-2.5-fast` ("unknown
model id") — `grok models` lists only grok-4.5. The per-CLI auth probe would
mislabel composer as available, so `--composer` / the picker would fail at
launch instead of degrading.
The grok probe now also checks the model appears in `grok models` (memoized,
short timeout). Data-driven, not a hardcoded drop: composer becomes available
again on a grok CLI that lists it. codex/claude stay auth-only (no clean CLI
model-list; their shipped models are stable). Covered by two new test cases.
Verified live: claude:fable, codex:gpt-5.6-sol, grok:grok-4.5 all start and
respond; composer correctly reads unavailable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
grok 0.2.101 no longer offers grok-composer-2.5-fast — `grok models` lists only grok-4.5, and `-m grok-composer-2.5-fast` errors with "unknown model id". So the `--composer` selector / grok:grok-composer-2.5-fast entry is dead: remove it from the registry, the --auto ranking, the flag lists (kickoff SKILL, herdr-launch usage, README), and the tests. The model-aware grok probe stays (it now guards grok-4.5 against a future drop/rename, which grok does between releases). swarm still references composer and is handled separately (its own task). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
Simpler model per user direction: no flag = launch the configured default, not a picker or a ranking. - Two-level `default`: a project default committed in the repo's `.claude/work-system-agent` overrides a global per-user default in `~/.claude/work-system-agent`, which overrides the shipped fallback claude:opus. `default get [--project|--global]`, `default set <name> [--project|--global]`. - Remove `--auto` + the whole ranking machinery (DEFAULT_RANK, rank subcommand, WORK_SYSTEM_AGENT_RANK[_FILE]) and the `--default` flag (no flag now == the default). The interactive picker moves behind `--pick`; `--last` stays. - kickoff SKILL, both READMEs, CHANGELOG updated; tests reworked for the two-level default + removed subcommands. The model-aware grok probe and per-CLI launch argv are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
Per user direction, drop the global default, the shipped opus fallback, and `--last` — so there's no per-user state to store at all. The only persisted selection is one committed per-repo default (`.claude/work-system-agent`). - No flag: launch the repo default if set; otherwise show the picker, and in the same AskUserQuestion offer to save the pick as the project default (applied after a successful launch, so a failed one never persists). - `--pick` forces the picker even when a default is set. - `default get`/`default set <name>` are now project-only (no --global/--project, no ranking, no auto/last subcommands). `default set` errors clearly outside a git repo. - kickoff SKILL, both READMEs, CHANGELOG, and the tests updated to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
Address the agreed findings from the local swarm review: - agent-registry grok probe: add a `gtimeout` fallback so `grok models` is time-bounded on stock macOS (only gtimeout ships there), not just where GNU `timeout` exists (#1). - grok availability: a failed/unreachable `grok models` fetch is now inconclusive (trust auth, soft note) rather than "unavailable" — a network hiccup no longer wrongly blocks launch. Fetch status rides the function's exit code, since a command-substitution subshell can't carry a global flag back (#6). - agent-registry resolve: reject control chars in `--session`, closing the newline→forged-`argv=`-line injection through the launch protocol (#2). - kickoff SKILL manual (non-herdr) block: shell-quote each argv word (the codex/grok bootstrap prompt is one word with spaces) instead of space-joining (#3); and persist a picker "save as default" on the manual path too, not only the herdr path (#4). - plugin.json: refresh the stale description (dropped the wrong lifecycle verbs, mention worker-agent choice) (#8). Not changed (reviewed, disagreed): /continue's claude-only resume is a documented degradation; README model-id duplication is accepted human-facing docs; the CHANGELOG paragraph matches the repo's entry style. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
- default get now VALIDATES the committed value against the registry: a stale/ removed/attacker-supplied name (from a cloned repo) reads as "no default" → picker, instead of routing or bricking every no-flag kickoff (#1). kickoff also announces a non-claude project default before launch — visibility, not a prompt, so a committed default can't silently route your code off-Claude (#1 security). - grok probe: when neither `timeout` nor `gtimeout` exists, skip `grok models` and return inconclusive (trust auth) rather than risk an unbounded call that hangs the picker (#2). - kickoff argument grammar: `--agent` consumes the next token as its value, so it isn't mistaken for the task name (#4). - agent-registry.sh committed executable (100755), matching herdr-launch.sh, so the documented direct invocation works (#5). - kickoff "Critical" note: step 12 → step 13 cross-ref (launch was renumbered) (#6). - `supports=` comment marked RESERVED/not-yet-consumed (a seed for the orchestration design), not "already driving" degradation (#7). - herdr-launch bad-mode usage string shows the launch arity incl. [agent-selector] (#8). - continue reopen: note reworded to match behavior (`claude -c` IS always sent) and the codex/grok caveat surfaced inline in the success report (#3). Not changed (reviewed): README model-id duplication (accepted human-facing docs), CHANGELOG paragraph (matches repo style); per-task worker persistence for a true per-CLI resume stays a later idea. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
- grok probe now always runs BOUNDED: when neither `timeout` nor `gtimeout` exists, self-bound with a background killer (fds detached so the command substitution doesn't block on it) instead of skipping the check — so a dropped model is still caught on hosts without a timeout binary, and the probe still can't hang the picker (#1). - a successful `grok models` that parses to nothing (a reformatted listing that dropped the `*` bullet) is treated as inconclusive → availability assumed, rather than marking every grok entry unavailable and disabling the backend (#3). - herdr-launch surfaces resolve's real stderr on exit 2 instead of labelling every cause "unknown agent selector" (#5). - README: `/continue` reopen wording corrected — it always sends `claude -c` and the user resumes a codex/grok worker themselves; no automatic per-CLI resume is claimed (#2, doc half; per-task persistence stays a later idea). - marketplace.json work-system description refreshed to match plugin.json (#6). Not changed: committed-external-default consent gate (#4) — an explicit product decision to announce, not prompt (a cloned repo can already run hooks/CLAUDE.md). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
Capture the non-obvious decisions: single per-repo committed default (no global/ fallback/ranking), agent-registry as single source of truth, the bounded model-aware grok probe (inconclusive -> trust auth), non-claude "document don't fake" degradation, and the announce-not-prompt security choice. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
- Bound EVERY external-CLI probe: factor out run_bounded (timeout/gtimeout, else a self-watchdog that escalates SIGTERM -> SIGKILL) and use it for codex `login status` (was completely unbounded) and grok `models`. Its stdout goes to a temp file so an orphaned grandchild can't hold the command-substitution pipe open — verified a SIGTERM-ignoring probe is now killed at ~12s, not left to hang the picker (#4). - kickoff manual (non-herdr) block no longer auto-persists the project default: it only prints a command, so no launch is confirmed — tell the user to run `default set` once the worker is up, matching the herdr path's "persist only after a successful launch" rule (#2). - kickoff picker: set OFFER_DEFAULT from the interpreted Yes/No answer, not a literal label match, so the save-as-default gate can't miss on case (#3). - knowledge: add the missing `prime:` key to the new entry (#6); refresh the stale herdr-kickoff-automation entry (step 13, registry-resolved worker argv, not a hardcoded `claude … /continue`) (#7). Not changed: the committed-external-default consent gate (#1) stays announce-not- prompt (a settled product decision); herdr-launch's `${0%/*}` sibling lookup (#5) is latent and matches the file's existing convention (callers always pass an absolute path). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
Mostly real regressions from the round-4 batch, caught by the re-review: - run_bounded: add `-k 1` to the timeout/gtimeout branches. GNU timeout only SIGTERMs at the deadline then waits, so a SIGTERM-ignoring probe ran forever on the COMMON path (any host with a timeout binary — e.g. all Linux CI); --kill-after escalates to SIGKILL like the self-watchdog. Normalize a bounded kill to a single 124 "timed out" code (#4). - codex probe: a run_bounded timeout (124) is now inconclusive -> assume available (mirrors grok), not a genuine auth failure — a slow `codex login status` no longer tells a logged-in user to re-login and disables the backend (#5). - grok probe: match the model id as a SUBSTRING of the raw `grok models` output (here-string, no pipe) instead of a positional awk field + exact-line grep, so a reformatted listing can't yield a wrong token and a false "model not offered" (#6). - project default resolves the MAIN repo root via --git-common-dir, so a `default set` run from inside a linked worktree lands in the main checkout, not the disposable worktree copy (#3). - kickoff manual path: the main-repo session runs `default set` after the user confirms the worker started — not a command for the user's terminal, where $REG is undefined and the cwd is the worktree (#2). - knowledge: correct the herdr-kickoff entry's grok argv (`grok -m …`, not codex) (#7). Not changed: committed-external-default consent gate (#1) — settled announce-not- prompt product decision. Verified: gtimeout -k bounds a SIGTERM-ignoring probe at ~11s; a hung codex probe reads available; `default set` from the worktree writes the main repo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NjTKpmBkXxxYkn7orMfZCC
gering
force-pushed
the
task/kickoff-agent-selection
branch
from
July 17, 2026 23:10
dc64df0 to
a3f7bf3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/kickoffno longer hardcodesclaudeas the worktree worker — it launches the repo's default agent (a single committed per-project setting), or shows a picker and offers to save the pick when none is set.plugins/work-system/scripts/agent-registry.shis the single source of truth for the known agents (CLI × model), their aliases, launch argv, availability, and the project default.work-systemto 1.9.0.Changes
list/resolve <selector>/default get|set. Registry-driven aliases (--fable/--opus/--codex/--sol/--grok/--agent cli[:model]), per-CLI launch argv, and a project-only default (<repo>/.claude/work-system-agent) thatdefault getvalidates against the registry. grok availability checksgrok models(always bounded via timeout/gtimeout/self-watchdog; a failed or empty-but-successful fetch is inconclusive → trust auth).launchexecs the resolved worker argv (CLI-agnostic, argv-exec preserved) instead of a hardcodedclaude; clear exit-2/3 handling with resolve's real stderr surfaced.--pick;--agentconsumes its value token; announces a non-Claude project default before launch.claude -c) with the codex/grok caveat surfaced inline.Readiness
features/kickoff-agent-selection.md)check-structure.pygreen;test_agent_registry.pypassesbash -nvia check-structure)main(includes Remove the dead grok composer backend from swarm #37 swarm composer removal), 0 behindTest plan
bash agent-registry.sh listshows claude/codex/grok with correct availability/kickoff <task>with no project default shows the picker + save-as-default offer/kickoff <task> --sollaunches codex on gpt-5.6-sol;--groklaunches grok-4.5python3 plugins/work-system/scripts/test_agent_registry.pypasses🤖 Generated with Claude Code