KeiSeiKit-1.0/skills/spawn-agent/phase-4-emit.md
Parfii-bot 0be354a920 KeiSeiKit-public — clean state
Single-commit clean baseline after security scrub of niche-tells,
project codenames, internal jargon, and contributor-email leaks.

Contents:
- 100 Rust crates (_primitives/_rust/)
- 37 agent manifests (_manifests/) + generated specs (_generated/)
- 67 user-invocable skills (skills/)
- 33 hooks (hooks/)
- Composition blocks (_blocks/)
- Documentation (docs/, README.md)
- TS adapter packages (_ts_packages/)
- Assembler (_assembler/)
- Roles (_roles/)
- Templates (_templates/)
- Forgejo CI (.forgejo/)

Author: Denis Parfionovich <info@greendragon.info>

License: see LICENSE.
2026-05-01 12:09:03 +08:00

218 lines
6.3 KiB
Markdown

# 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.