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.
218 lines
6.3 KiB
Markdown
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.
|