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.
6.3 KiB
Phase 4 — Write task.toml, spawn via kei-spawn, emit Agent-tool invocation
Goal: serialize
ROLE / TASK_FULL / WHITELIST / DENYLISTinto atask.toml, callkei-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,ISOLATIONall 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
"""..."""fortaskso 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 = truefrom Phase 3.d, OMIT thedenylistkey entirely —kei-spawndistinguishes "empty list" (union with defaults) from "absent" (no defaults applied) via the--no-default-denyflag 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>.tomland 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_IDnon-empty UUIDDNAnon-empty sha256 prefixSUBAGENT_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_FULLin thepromptfield contains the literal phraseMUST 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_typemismatch — runkei-spawn rolesto list valid subagent_type values in the current runtime and reconcile. - (B) Likely
isolationunsupported in the caller's Claude Code version — fall back tosharedand loop back to Phase 1 to re-pick a read-only / explorer role. - (C) Escalate to
/new-agentif the user actually needs a new agent manifest rather than a new instance.