feat(w9c): /spawn-agent Claude Code skill — click-only wizard for kei-spawn

5-phase skill wrapping kei-spawn CLI: role pick → task desc → scope
preset → confirm → emit composed Agent-tool invocation.

Pattern matches existing skills/site-create + skills/new-agent.
4 AskUserQuestion minimum, sole free-text is task description.

RULE 0.13 / 0.12 / 0.5 / ZERO enforced. Ready for /spawn-agent
invocation once kei-spawn binary built via install.sh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Parfii-bot 2026-04-23 13:32:01 +08:00
parent e0e6e1720d
commit eac6a7e58c
5 changed files with 687 additions and 0 deletions

131
skills/spawn-agent/SKILL.md Normal file
View file

@ -0,0 +1,131 @@
---
name: spawn-agent
description: Prepare a composed Agent-tool invocation via kei-spawn. Click-only wizard — pick role, describe task, set scope. Emits ready-to-paste prompt + subagent_type + isolation + DNA.
argument-hint: (no arguments)
---
# /spawn-agent — Click-only Agent-tool composer (index)
You convert a user's intent into a ready-to-paste `Agent`-tool invocation
via the `kei-spawn` CLI. Every decision is a click; only the task description
(Phase 2) is typed. No git, no side-effects on disk beyond the task.toml the
CLI consumes.
This `SKILL.md` is the INDEX. Each phase lives in its own file and is
executed in order. Never skip a phase. Never re-order phases.
---
## Pipeline overview
| Phase | File | Purpose | AskUserQuestion |
|---|---|---|---|
| 1 | [phase-1-role.md](phase-1-role.md) | Pick agent role (capability tier) | 1× AskUserQuestion |
| 2 | [phase-2-task.md](phase-2-task.md) | Free-text task description (ONE paragraph) | 0× (only free-text in the skill) |
| 3 | [phase-3-scope.md](phase-3-scope.md) | Files whitelist glob patterns | 2× AskUserQuestion |
| 4 | [phase-4-emit.md](phase-4-emit.md) | Write task.toml, run kei-spawn, emit Agent-tool invocation | 1× AskUserQuestion (confirm-emit) |
**Minimum AskUserQuestion count: 4** — pure-click contract.
---
## Variables the pipeline produces
| Name | Set in | Meaning |
|---|---|---|
| `ROLE` | Phase 1 | One of `read-only` / `explorer` / `edit-local` / `edit-shared` |
| `TASK` | Phase 2 | Free-text task description (1-3 sentences) |
| `WHITELIST` | Phase 3 | List of glob patterns (relative to repo root) |
| `DENYLIST` | Phase 3 | Optional list of glob patterns (auto-derived from role if skipped) |
| `TASK_TOML` | Phase 4 | Path `tasks/<uuid>.toml` relative to current repo |
| `AGENT_ID` | Phase 4 | UUID returned by `kei-spawn spawn` |
| `DNA` | Phase 4 | SHA-256 fingerprint returned by `kei-spawn spawn` |
| `SUBAGENT_TYPE` | Phase 4 | Resolved by kei-spawn from ROLE (e.g. `code-implementer` / `researcher`) |
| `ISOLATION` | Phase 4 | Resolved by kei-spawn from ROLE (`worktree` or `shared`) |
| `WORKTREE_PATH` | Phase 4 | Returned when isolation=worktree |
---
## Role → defaults map (LOAD-BEARING)
| ROLE | subagent_type | isolation | Bash? | Writes? |
|---|---|---|---|---|
| read-only | researcher | shared | no | no |
| explorer | researcher | shared | read-only bash | no |
| edit-local | code-implementer | worktree | cargo/test only | yes (whitelist) |
| edit-shared | code-implementer | worktree | cargo/test only | yes (whitelist) |
`kei-spawn` owns the resolution — this table is reference only. The skill
passes the ROLE string verbatim; the CLI returns the resolved values.
---
## Final report (emit after Phase 4)
```
=== /SPAWN-AGENT REPORT ===
Role: <ROLE>
Task (first 80): <first 80 chars of TASK>...
Whitelist: <N globs><first 3 shown>
Denylist: <auto / explicit N globs>
Agent ID: <UUID>
DNA: <sha256:first-12>...
subagent_type: <SUBAGENT_TYPE>
isolation: <ISOLATION>
Worktree: <WORKTREE_PATH or n/a>
Ready-to-paste Agent-tool invocation:
<JSON block see phase-4-emit.md §4 for exact shape>
Next step on return:
kei-spawn verify <AGENT_ID> <WORKTREE_PATH>
```
---
## Runtime binary resolution
`kei-spawn` must be on `PATH` OR reachable via `$KEI_RUNTIME_BIN_DIR`. The
phases call `kei-spawn <cmd>` directly; if the shell returns `command not
found`, fall back to `"$KEI_RUNTIME_BIN_DIR/kei-spawn" <cmd>`. If both fail,
STOP and surface the error to the user with three constructive paths:
- (A) Build `kei-spawn` from the kit source (`cd _primitives/_rust/kei-spawn && cargo build --release`).
- (B) Export `KEI_RUNTIME_BIN_DIR` to point at an existing build.
- (C) Install via `install.sh` which wires both.
Never silently substitute a mock.
---
## Rules (enforced at every phase)
- **Pure-click contract.** Only `TASK` (Phase 2) is typed. Every other
decision is an `AskUserQuestion`. Count them in the final report.
- **NO DOWNGRADE (RULE -1).** Any phase that fails returns 2-3 constructive
paths, never "can't be done".
- **NO HALLUCINATION (RULE 0.4).** Never fabricate a DNA or agent_id — they
come from `kei-spawn spawn --format=json` verbatim. Never emit an Agent
invocation without a successful spawn call first.
- **Plan Mode First (RULE 0.5).** This skill IS the plan; each phase file
has its own verify-criterion. No `kei-spawn spawn` call before Phase 4's
confirm click.
- **Orchestrator branch first (RULE 0.13).** The skill NEVER invokes git.
The emitted prompt MUST contain the phrase "MUST NOT invoke git" so the
spawned agent respects the rule. Phase 4 inserts this automatically.
- **Constructor Pattern (RULE ZERO).** One file per phase. This SKILL.md
stays <200 LOC; each phase file stays <200 LOC.
- **Surgical Changes.** The skill only writes `tasks/<uuid>.toml`. It does
not modify project source, does not commit, does not push.
---
## References
- [phase-1-role.md](phase-1-role.md) · [phase-2-task.md](phase-2-task.md) · [phase-3-scope.md](phase-3-scope.md) · [phase-4-emit.md](phase-4-emit.md)
- `_primitives/_rust/kei-spawn/` — CLI source (task.toml schema + spawn/verify commands)
- `skills/new-agent/SKILL.md` — sister skill: generates agent *manifests*; this skill spawns *instances* of existing agents
- `skills/hooks-control/SKILL.md` — reference click-only pattern
- RULE 0.12 (agent-git-model) — ledger row auto-written by `kei-spawn spawn`
- RULE 0.13 (orchestrator-branch-first) — ban-phrase injected by Phase 4

View file

@ -0,0 +1,84 @@
# Phase 1 — Role selection
> Goal: pick the agent role (capability tier). This single choice resolves
> `subagent_type` + `isolation` + Bash/Write permissions via `kei-spawn`.
> **Verify criterion:** `ROLE` set to one of the four fixed labels.
---
## 1.a — Single AskUserQuestion
Send ONE `AskUserQuestion` call. `multiSelect: false`. Do NOT fall through
to a default — the user must click.
```json
{
"questions": [
{
"question": "Agent role / capability tier?",
"header": "Role",
"multiSelect": false,
"options": [
{
"label": "read-only",
"description": "Researcher. Read + Grep + Glob only. No Bash. No writes. Shared worktree. Use for: literature lookup, prior art, code exploration reports."
},
{
"label": "explorer",
"description": "Researcher + read-only Bash (ls, cat, rg, git log — no state changes). Shared worktree. Use for: audits, diagnostics, benchmark snapshots."
},
{
"label": "edit-local",
"description": "Code-implementer with worktree isolation. Can Write/Edit within a narrow whitelist (usually one module). Bash limited to cargo/test runners. Use for: single-crate changes, localized refactors."
},
{
"label": "edit-shared",
"description": "Code-implementer with worktree isolation + broader whitelist (multiple modules / cross-cutting). Bash limited to cargo/test. Use for: workspace-wide refactors, feature-slice work touching ≥2 crates."
}
]
}
]
}
```
Store the clicked label verbatim as `ROLE`.
---
## 1.b — Guidance (for the user, shown WITH the question)
Before sending the question, print one short paragraph:
> Pick the capability tier. Read-only and explorer never modify disk and
> are cheap to spawn. Edit-local and edit-shared get their own git worktree
> (per RULE 0.12) and inherit a Bash allowlist restricted to test runners.
> If unsure, pick `explorer` — it's the safest writeable-ish tier and
> covers most audit / diagnostic requests.
---
## 1.c — Verify criterion
`ROLE ∈ {read-only, explorer, edit-local, edit-shared}`. If the user
dismissed the question or picked something else, loop back. Do NOT proceed
to Phase 2 without ROLE set.
Emit a single-line confirmation: `Role locked: <ROLE>`. Proceed to Phase 2.
---
## 1.d — Failure paths (NO DOWNGRADE)
If the user wants a role outside the four fixed tiers (e.g. "I want a
writer with full Bash"):
- (A) Explain that the 4 tiers are the full capability grid in `kei-spawn`
— anything else would need a new role added to the CLI. Offer to open
a task for that.
- (B) Suggest the closest existing tier (usually `edit-shared` with a
broader whitelist covers 90% of "I need more power" requests).
- (C) Abort this skill invocation and escalate to `/new-agent` if the user
actually needs a new *agent manifest*, not a new *role tier*.
Never invent a fifth tier. `kei-spawn` will reject any ROLE not in the
fixed set.

View file

@ -0,0 +1,91 @@
# Phase 2 — Task description (the only typed phase)
> Goal: capture a 1-3 sentence task description. This is the SOLE free-text
> input in the skill.
> **Verify criterion:** `TASK` non-empty, ≤2000 chars, not a single word.
---
## 2.a — Prompt
Print this exact message to the user (NOT an AskUserQuestion — regular chat):
> Describe the task in 1-3 sentences. Be concrete about the deliverable.
>
> Good: "Add a --verbose flag to kei-ledger list that prints the parent_branch and dna columns. Update the integration test accordingly."
>
> Weak: "Improve ledger output" (too vague, agent will ask clarifying questions and burn turns).
>
> Excellent tasks name: (1) the target file or module, (2) the verifiable
> deliverable, (3) any existing pattern to mirror.
Wait for the user's reply. Capture the full message as `TASK`.
---
## 2.b — Validation
Apply these checks in order. On any failure, re-print the guidance and
ask again. Do NOT fall through to Phase 3 on failure.
1. **Non-empty.** `len(TASK.trim()) > 0`.
2. **Minimum length.** `len(TASK.trim()) >= 20`. Shorter than 20 chars is
almost certainly a single-word request — ask for more detail.
3. **Maximum length.** `len(TASK) <= 2000`. If longer, ask the user to
trim: `kei-spawn` accepts up to 4000 chars but tasks >2000 typically
indicate the user is trying to write the agent's plan for it. Push back.
4. **No secrets.** Quick regex scan for `sk-`, `ghp_`, `Bearer `, `AKIA`,
`xoxb-`, `-----BEGIN`, `:AAG` (Telegram bot token infix). If any hits,
STOP and ask the user to remove — tokens belong in `~/.claude/secrets/.env`
per RULE 0.8, never in task descriptions.
5. **No git commands.** If `TASK` contains `git commit`, `git push`,
`git add`, `git merge`, warn the user that per RULE 0.13 the spawned
agent will be explicitly banned from git — if the task REQUIRES git
operations, this skill is the wrong tool (use `/new-project` or
orchestrator-meta flow instead).
---
## 2.c — Auto-augmentation
Before storing `TASK`, prepend a fixed preamble so the spawned agent sees
the orchestrator-branch-first rule verbatim. This is NOT optional — RULE
0.13 requires the ban-phrase in every non-trivial agent prompt:
```
You MUST NOT invoke git, bash state-changing commands, or shell commands
that mutate the repo. Tools allowed: Read, Write, Edit, Glob, Grep (plus
read-only Bash for explorer role; test-runners only for edit-* roles).
Write files to the paths inside the whitelist. Return a file-list block
in your final report. Orchestrator owns git.
--- TASK ---
<user's TASK here, verbatim>
```
Store the composed prompt as `TASK_FULL`. Keep the raw user text as
`TASK` for the report.
---
## 2.d — Verify criterion
Both `TASK` and `TASK_FULL` populated. `TASK` passes all 5 validation
checks. Emit a single-line confirmation:
`Task captured: <first 60 chars of TASK>... (<N> chars)`
Proceed to Phase 3.
---
## 2.e — Failure paths (NO DOWNGRADE)
If the user cannot articulate the task after two prompts:
- (A) Suggest invoking `/research` or `/debug-deep` first to clarify the
problem, then return to `/spawn-agent` once the deliverable is concrete.
- (B) Offer to scaffold a skeleton task description from the user's rough
words — show the skeleton, ask for approval.
- (C) Abort this invocation — better no agent than a confused one.

View file

@ -0,0 +1,163 @@
# Phase 3 — Scope (files whitelist + optional denylist)
> Goal: produce a concrete `WHITELIST` (glob patterns the agent may touch)
> and optionally an explicit `DENYLIST`. For read-only / explorer roles
> the whitelist is advisory; for edit-* roles it is ENFORCED by kei-spawn.
> **Verify criterion:** `WHITELIST` non-empty list of glob strings.
---
## 3.a — First AskUserQuestion: scope preset
Send ONE `AskUserQuestion` call. Presets cover ≥80% of real invocations;
pick "Custom" only if none fit.
```json
{
"questions": [
{
"question": "Scope preset?",
"header": "Scope",
"multiSelect": false,
"options": [
{
"label": "Single crate (Rust)",
"description": "crates/<name>/** — typical edit-local scope. Also includes that crate's tests/ and Cargo.toml."
},
{
"label": "Single skill (Markdown)",
"description": "skills/<name>/** — pure-markdown skill authoring. No code compilation."
},
{
"label": "Single agent manifest",
"description": "agents/_manifests/<name>.toml + agents/_blocks/*.md — agent fleet authoring."
},
{
"label": "Docs / rules only",
"description": "**/*.md — read-only or explorer roles that only touch documentation."
},
{
"label": "Whole project (read-only)",
"description": "** — read-only or explorer roles. Not valid for edit-* (too broad; use edit-shared with explicit globs)."
},
{
"label": "Custom",
"description": "Enter glob patterns as one free-text line (comma- or newline-separated)."
}
]
}
]
}
```
Store the clicked label as `SCOPE_PRESET`.
---
## 3.b — Resolve preset to `WHITELIST`
- **Single crate (Rust)** → follow up with ONE free-text prompt: `Crate name?`
Validate `[a-z0-9-]+`. Build: `[ "crates/<name>/**", "Cargo.toml" ]`.
- **Single skill (Markdown)** → follow up: `Skill name?` Validate `[a-z0-9-]+`.
Build: `[ "skills/<name>/**" ]`.
- **Single agent manifest** → follow up: `Agent name?` Validate `[a-z0-9-]+`.
Build: `[ "agents/_manifests/<name>.toml", "agents/_blocks/*.md" ]`.
- **Docs / rules only** → Build: `[ "**/*.md" ]`. Warn if ROLE is edit-* —
docs-only edits rarely need worktree isolation; suggest `explorer` or
`read-only` instead.
- **Whole project (read-only)** → BLOCK if ROLE is edit-local or edit-shared.
Print: "Whole-project scope is not allowed for edit roles. Use edit-shared
with explicit globs naming the ≥2 modules you will touch." Loop back to 3.a.
Otherwise build: `[ "**" ]`.
- **Custom** → follow up: `Enter glob patterns (comma- or newline-separated).`
Parse, trim, validate each glob against the rules in 3.c. Build the list.
---
## 3.c — Glob validation rules
Apply to every pattern in `WHITELIST`:
1. **No absolute paths.** Must not start with `/` or `~/`. globs are
repo-relative.
2. **No parent traversal.** Reject any pattern containing `..`.
3. **No leading dot-dir unless explicit.** `.git/**`, `.claude/**` must
be typed in full; reject accidental `.**`.
4. **At least one literal char.** Reject `**` alone without a scoping prefix
unless ROLE is read-only or explorer AND SCOPE_PRESET was "Whole project".
5. **Max count.** ≤20 globs. If the user pastes more, ask them to consolidate.
On any failure, print the offending pattern and the rule that tripped;
re-prompt for that one line; do NOT fall through.
---
## 3.d — Second AskUserQuestion: explicit denylist?
Send the second `AskUserQuestion` call:
```json
{
"questions": [
{
"question": "Denylist?",
"header": "Deny",
"multiSelect": false,
"options": [
{
"label": "Auto (recommended)",
"description": "kei-spawn applies the role-default denylist: secrets/**, **/*.env, target/**, node_modules/**, .git/**, dist/**, .keisei/** — covers 95% of cases."
},
{
"label": "Explicit",
"description": "Enter additional deny globs on top of the auto default. Use when the task whitelist accidentally includes sensitive subpaths."
},
{
"label": "None (override auto)",
"description": "Override the auto defaults and pass an empty denylist. BLOCKED for edit-* roles — read-only / explorer only."
}
]
}
]
}
```
Resolve:
- **Auto**`DENYLIST = []`, let `kei-spawn` apply its role defaults. Most
common path.
- **Explicit** → follow up: `Enter deny globs (comma- or newline-separated).`
Validate via 3.c rules. `DENYLIST = [ "<user globs>" ]`. `kei-spawn` will
UNION these with the role defaults (not replace).
- **None (override auto)** → if ROLE ∈ {edit-local, edit-shared} BLOCK and
loop back. Otherwise set a marker `DENYLIST_OVERRIDE = true`; Phase 4
will pass `--no-default-deny` to `kei-spawn`. Warn the user that this
disables the `secrets/**` and `.env` safety nets.
---
## 3.e — Verify criterion
- `WHITELIST` is a non-empty list (length ≥ 1).
- Every pattern passes 3.c validation.
- `DENYLIST` resolved (may be empty list — Auto path).
- If ROLE is edit-* and WHITELIST == `[ "**" ]`, REJECT and loop to 3.a.
Emit confirmation:
`Scope locked: <N> whitelist globs, deny=<auto|explicit:N|override>`
Proceed to Phase 4.
---
## 3.f — Failure paths (NO DOWNGRADE)
If the user cannot choose a preset and Custom produces invalid globs twice:
- (A) Offer to inspect the current repo with `rg --files | head -50` and
propose 2-3 concrete whitelists based on what's actually there.
- (B) Suggest downgrading ROLE from `edit-shared` to `explorer` — explorer
accepts `[ "**" ]` and still reads everything, without write risk.
- (C) Abort this invocation and ask the user to run `/spawn-agent` again
once the target files are clearer.

View file

@ -0,0 +1,218 @@
# Phase 4 — Write task.toml, spawn via kei-spawn, emit Agent-tool invocation
> Goal: serialize `ROLE / TASK_FULL / WHITELIST / DENYLIST` into a
> `task.toml`, call `kei-spawn spawn --format=json`, parse the JSON, and
> emit a ready-to-paste Agent-tool invocation. One confirm click before
> the spawn call (it writes a ledger row).
> **Verify criterion:** `AGENT_ID`, `DNA`, `SUBAGENT_TYPE`, `ISOLATION`
> all set; Agent-tool invocation printed.
---
## 4.a — Compute `TASK_TOML` path
Generate a short UUID (v4, first 8 hex chars is sufficient — kei-spawn
validates uniqueness on write). Path:
```
tasks/<uuid8>.toml
```
Relative to the current repo root. If `tasks/` does not exist, do NOT
create it from this skill — `kei-spawn spawn` will fail loudly and the
constructive path is to run `mkdir tasks && git add tasks/.gitkeep` in
the orchestrator session.
---
## 4.b — Render task.toml
Use this exact schema (verify against `_primitives/_rust/kei-spawn/src/task.rs`
before any drift; if the CLI schema changes, this phase file updates too):
```toml
# Generated by /spawn-agent skill — do not hand-edit.
# kei-spawn consumes this file; the spawn ceremony writes a ledger row
# (RULE 0.12) and returns agent_id + dna in JSON.
role = "<ROLE>"
task = """
<TASK_FULL verbatim, triple-quoted>
"""
[scope]
whitelist = [
"<glob 1>",
"<glob 2>",
# ...
]
denylist = [
# empty if Auto; populated if Explicit; absent if Override (see 4.c)
]
```
TOML rules:
- Use triple-quoted `"""..."""` for `task` so newlines in TASK_FULL survive.
- Escape any triple-quote inside TASK_FULL as `\"\"\"` (unlikely but
validate before write).
- Every whitelist entry is a TOML string (double-quoted, 4-space indented,
trailing comma).
- If `DENYLIST_OVERRIDE = true` from Phase 3.d, OMIT the `denylist` key
entirely — `kei-spawn` distinguishes "empty list" (union with defaults)
from "absent" (no defaults applied) via the `--no-default-deny` flag
passed in 4.d.
Write the file via the `Write` tool.
---
## 4.c — Confirm-emit click
Send ONE `AskUserQuestion` before calling `kei-spawn`:
```json
{
"questions": [
{
"question": "Spawn agent with this configuration?",
"header": "Confirm",
"multiSelect": false,
"options": [
{
"label": "Yes — spawn now",
"description": "Writes ledger row, returns agent_id + DNA + subagent_type + isolation. The Agent-tool invocation will be emitted for you to paste."
},
{
"label": "No — show me the task.toml first",
"description": "Print the generated tasks/<uuid>.toml contents verbatim, then re-ask."
},
{
"label": "Abort",
"description": "Delete the task.toml and exit the skill."
}
]
}
]
}
```
Resolve:
- **Yes** → proceed to 4.d.
- **No** → Read the file back and print it in a fenced code block; loop
the same AskUserQuestion.
- **Abort** → delete `tasks/<uuid>.toml` and emit a short summary: no
spawn, no ledger row, no Agent invocation.
---
## 4.d — Run kei-spawn
Call exactly one command (no chaining, so errors surface cleanly):
```bash
kei-spawn spawn tasks/<uuid>.toml --format=json
```
If `DENYLIST_OVERRIDE = true`, append `--no-default-deny`.
If `kei-spawn` is not on PATH, fall back to `"$KEI_RUNTIME_BIN_DIR/kei-spawn"`.
If both fail, surface the SKILL.md runtime-resolution paths (A/B/C) and
STOP — do NOT fabricate a response.
Parse the stdout JSON. Expected shape (exact field names per
`_primitives/_rust/kei-spawn/src/spawn.rs`):
```json
{
"agent_id": "6a48ca7b-...",
"dna": "sha256:b37e2d1e9a...",
"subagent_type": "code-implementer",
"isolation": "worktree",
"worktree_path": ".claude/worktrees/agent-6a48ca7b",
"branch": "agent/<slug>-<ts>"
}
```
Store each field as named (`AGENT_ID`, `DNA`, `SUBAGENT_TYPE`, `ISOLATION`,
`WORKTREE_PATH`, `BRANCH`). If `isolation == "shared"`, `worktree_path`
and `branch` may be absent — handle that gracefully.
On non-zero exit from `kei-spawn`: print stderr verbatim, surface the
error, offer three constructive paths:
- (A) Fix the task.toml and re-spawn (show the diff first).
- (B) Fall back to a smaller scope (loop back to Phase 3).
- (C) Abort — delete task.toml, no ledger row written.
---
## 4.e — Emit the Agent-tool invocation
Print a single fenced code block with the ready-to-paste JSON. Use the
Agent tool's exact parameter names:
```json
{
"subagent_type": "<SUBAGENT_TYPE>",
"description": "<first 60 chars of TASK>",
"prompt": "<TASK_FULL verbatim, with the orchestrator-branch-first preamble>",
"isolation": "<ISOLATION>"
}
```
Plus a short annotation under the block:
```
Agent ID: <AGENT_ID>
DNA: <DNA>
Branch: <BRANCH or n/a for shared>
Worktree: <WORKTREE_PATH or n/a>
Paste the JSON above into an Agent tool call. On return, run:
kei-spawn verify <AGENT_ID> <WORKTREE_PATH>
to check the 6-file artefact bundle (RULE 0.12) and merge-readiness.
```
---
## 4.f — Final report
Emit the SKILL.md-defined `=== /SPAWN-AGENT REPORT ===` block. Populate
every field from the variables above. Do NOT invent any field value — if
something is absent (e.g. worktree for shared isolation), print `n/a`.
---
## 4.g — Verify criterion
- `AGENT_ID` non-empty UUID
- `DNA` non-empty sha256 prefix
- `SUBAGENT_TYPE ∈ {researcher, code-implementer, ...}` (whatever kei-spawn
returns — the skill does not enforce a specific set)
- `ISOLATION ∈ {shared, worktree}`
- The Agent-tool invocation JSON is printed in a fenced block
- `TASK_FULL` in the `prompt` field contains the literal phrase
`MUST NOT invoke git` (RULE 0.13 ban-phrase — added in Phase 2.c)
If the ban-phrase check fails (should be impossible after Phase 2.c), STOP
and surface a bug report — do NOT emit the invocation.
---
## 4.h — Failure paths (NO DOWNGRADE)
Covered inline in 4.d. Additionally, if the user tries to paste the
emitted JSON but the Agent tool rejects it:
- (A) Likely `subagent_type` mismatch — run `kei-spawn roles` to list
valid subagent_type values in the current runtime and reconcile.
- (B) Likely `isolation` unsupported in the caller's Claude Code version
— fall back to `shared` and loop back to Phase 1 to re-pick a
read-only / explorer role.
- (C) Escalate to `/new-agent` if the user actually needs a new agent
manifest rather than a new instance.