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

6.3 KiB

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):

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

{
  "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):

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):

{
  "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:

{
  "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.