KeiSeiKit-1.0/docs/AGENT-SUBSTRATE-SCHEMA.md
Parfii-bot 329d7e2a4d feat(agent-substrate/phase-5): migrate 5 kit agents to role+task-spec — substrate v1 FULL
Final phase of agent substrate v1. 5 shipped agents now declare role at
manifest level; assembler expands role's capability text fragments into
the generated .md at a new `# AGENT SUBSTRATE — role <name>` section.
Non-migrated agents byte-identical (golden snapshots green).

Migrated agents:
- kei-code-implementer → edit-local (8 caps: no-git-ops + scope/* +
  quality/* + safety::no-dep-bump + report-format)
- kei-critic → read-only (tools::read-only + output::report-format +
  output::severity-grade)
- kei-architect → read-only
- kei-security-auditor → read-only
- kei-validator → read-only

_assembler/ extensions:
- manifest.rs: substrate_role: Option<String>
- assembler.rs: write_substrate() before blocks (backward-compat; no
  role = no substrate section)
- substrate.rs (new, 102 LOC): loads _roles/<name>.toml, iterates
  capabilities.required, reads _capabilities/<cat>/<slug>/text.md,
  joins with \n\n---\n\n separator
- validator.rs: substrate role existence + cap-text presence check
- tests/substrate_role.rs (4 tests): happy path, unknown role, missing
  capability text, byte-parity on non-migrated
- tests/regenerate_migrated.rs (ignored by default): regeneration gate

_templates/task-examples/ — 5 example task.toml per migrated agent
showing orchestrator the valid invocation shape.

docs/AGENT-SUBSTRATE-SCHEMA.md: Phase 5 row ticked ✓ + Migrated agents
subsection listing 5 agents with roles + pointer to examples.

tests/substrate_integration.sh: +8 Phase-5 assertions
- All 5 migrated .md files contain "# AGENT SUBSTRATE — role"
- kei-code-implementer.md contains "MUST NOT invoke git" (policy::no-git-ops)
- Every _templates/task-examples/*.toml parses as valid TOML
- cargo check --workspace still passes post-migration
- kei-agent-runtime compose works on edit-local-forge.toml example

Tests: assembler 40/40 (was 30, +4 substrate_role + +1 ignored regen),
kei-agent-runtime + kei-capability 37/37 preserved.

Deferred: remaining 7 non-core agents (cost-guardian, modal-runner,
fal-ai-runner, infra/ml-implementer, ml-researcher, researcher) migrate
in v0.24 wave.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 03:07:18 +08:00

25 KiB
Raw Blame History

KeiSeiKit Agent Substrate Schema v1

STATUS: Decisions resolved 2026-04-23 — see updated Decision log at bottom. LOCK active upon AGENT-SCHEMA-LOCKED.md commit. 3-week parallel phase window.

PURPOSE: Sibling SSoT to SUBSTRATE-SCHEMA.md. That one decomposes code primitives (atoms). This one decomposes agent invocations (capabilities).

Motivation from substrate v1 orchestration pain: across 7 agent spawns in audit+follow-up waves, the same friction recurred — 40% prompt boilerplate, self-reported green tests that broke at integration, scope violations surfacing only after merge. Fix: capabilities become enforced triplets, not suggestions in freetext prompts.


Core concept: capability atom = triplet

An agent capability is not a reusable text block. It is a declarative bundle + Rust implementation that gives every restriction meaning across three layers:

Artifact Format Who consumes
capability.toml TOML declarative metadata (name, category, patterns, parameters) kei-agent-runtime at compose + lint time
text.md Markdown prompt fragment Agent (via LLM context)
Rust module gates/<slug>.rs Rust impl Capability trait kei-capability check binary at PreToolUse
Rust module verifies/<slug>.rs Rust impl Capability trait kei-capability verify binary at on-return

The two Rust modules live in _primitives/_rust/kei-agent-runtime/src/ — one compilation unit, one registry, cargo test on all gates/verifies at once. Shell hooks are 3-line glue that execs the binary.

The invariant: if any of the four artifacts is missing or fails, the capability did not hold. Self-reported compliance is not trusted — verification runs via worktree short-circuit → simulated merge pattern (see §Verify execution below) after agent return, catching integration regressions before merge to main.


File layout

_capabilities/                        — DECLARATIVE artefacts (phase 1 writes these)
├── policy/
│   └── no-git-ops/
│       ├── capability.toml
│       └── text.md
├── scope/
│   ├── files-whitelist/{capability.toml, text.md}
│   └── files-denylist/{capability.toml, text.md}
├── quality/
│   ├── constructor-pattern/{capability.toml, text.md}
│   ├── cargo-check-green/{capability.toml, text.md}
│   └── tests-green/{capability.toml, text.md}
├── safety/
│   └── no-dep-bump/{capability.toml, text.md}
├── output/
│   ├── report-format/{capability.toml, text.md}
│   └── severity-grade/{capability.toml, text.md}
└── tools/
    ├── read-only/{capability.toml, text.md}
    └── cargo-only-bash/{capability.toml, text.md}

_roles/                               — DECLARATIVE (phase 2 writes these)
├── read-only.toml
├── explorer.toml
├── edit-local.toml
├── edit-shared.toml
└── git-ops.toml                      — documented; NOT spawnable (orchestrator-only)

_primitives/_rust/kei-agent-runtime/  — BINARY (phase 3 writes this)
├── Cargo.toml
├── src/
│   ├── lib.rs                        — exports Capability trait + registry
│   ├── main.rs                       — CLI: compose | spawn | verify | run
│   ├── compose.rs                    — task.toml + role + capabilities → prompt.md
│   ├── spawn.rs                      — Agent-tool invocation with composed prompt
│   ├── verify.rs                     — worktree short-circuit → simulated merge
│   ├── simulated_merge.rs            — create temp branch + apply diff + run checks
│   ├── registry.rs                   — &str → Box<dyn Capability> dispatch
│   ├── gates/                        — PreToolUse logic
│   │   ├── mod.rs
│   │   ├── policy_no_git_ops.rs
│   │   ├── scope_files_whitelist.rs
│   │   ├── scope_files_denylist.rs
│   │   ├── safety_no_dep_bump.rs
│   │   ├── tools_read_only.rs
│   │   └── tools_cargo_only_bash.rs  — 6 gates
│   └── verifies/                     — on-return logic
│       ├── mod.rs
│       ├── quality_constructor_pattern.rs
│       ├── quality_cargo_check_green.rs
│       ├── quality_tests_green.rs
│       ├── safety_no_dep_bump.rs
│       ├── scope_files_whitelist.rs
│       ├── scope_files_denylist.rs
│       ├── output_report_format.rs
│       └── output_severity_grade.rs   — 8 verifies
└── tests/

_primitives/_rust/kei-capability/     — BINARY (phase 3)
├── Cargo.toml                        — depends on kei-agent-runtime
└── src/main.rs                       — clap CLI:
                                         kei-capability check <name>   (stdin JSON, exit 0|2)
                                         kei-capability verify <name>  (env-driven, exit 0 or fail)

hooks/                                — 3-line shell glue (phase 4 ✓ shipped)
├── agent-capability-check.sh         — `exec kei-capability check "$KEI_CAPABILITY_NAME"` — PreToolUse:Bash|Edit|Write, no-op when env unset, fail-open on missing binary
└── agent-capability-verify.sh        — orchestrator-driven post-agent: `exec kei-capability verify "$KEI_CAPABILITY_NAME"` with AGENT_ID/TASK_TOML/WORKTREE_PATH/MAIN_REPO/RUN_MODE env

tasks/                                — ephemeral, gitignored
└── <agent-id>/{task.toml, prompt.md}

docs/AGENT-SUBSTRATE-SCHEMA.md       — this file
docs/AGENT-ROLES.md                  — human-readable role matrix (generated from _roles/*.toml)
docs/AGENT-SCHEMA-LOCKED.md          — lock marker

Capability atom — capability.toml shape

[capability]
name = "policy::no-git-ops"           # <category>::<slug> namespace
category = "policy"                   # policy | scope | quality | safety | output | tools
version = "1.0"
description = "RULE 0.13 — orchestrator owns git, agent writes files only"
rationale = "See ~/.claude/rules/orchestrator-branch-first.md"

[restricts]
# What this capability forbids. Runtime gate enforces.
tool-patterns = [                     # matched against tool_input.command
  '^git( |$)',
  '^gh (repo|api /repos)',
]
tools-denied = []                     # e.g. ["Edit", "Write"] for read-only

[parameterized]
# Is this capability instance-configurable per task?
accepts = []                          # e.g. ["files-whitelist"] for scope/* caps

[text]
path = "text.md"                      # relative to capability dir

[gate]
# Rust module path inside kei-agent-runtime — registry dispatches by capability.name
rust-module = "gates::policy_no_git_ops"   # or empty if capability has no gate (verify-only)
event = "PreToolUse:Bash"              # PreToolUse:Bash | PreToolUse:Edit|Write | PreToolUse:Agent
severity = "block"                     # block (exit 2) | warn (exit 0 + stderr) | advisory (log only)
bypass-env = "ORCHESTRATOR_META"       # optional env var to disable

[verify]
rust-module = "verifies::policy_no_git_ops"  # or empty if gate-only
run-mode = "simulated-merge"           # worktree | simulated-merge | both
when = "on-return"                     # on-return | per-tool-call

run-mode values:

  • worktree — run predicate inside the agent's worktree (fastest; what the agent saw)
  • simulated-merge — orchestrator creates test-merge/<agent-id> branch off main, applies agent diff, runs predicate from there (catches integration regressions of the E1-jsonschema-class — see §Verify execution)
  • both — worktree first (fail-fast), then simulated-merge (integration guarantee). Default for quality::* capabilities.

Capability text.md conventions

  • Imperative, second-person, short.
  • ≤ 200 words per fragment.
  • No overlap — if two capabilities say the same thing, extract into a shared one.
  • Fragment stands alone — composer concatenates multiple fragments with \n\n---\n\n separator; fragments must not reference each other.
  • Lead with the rule ("You MUST NOT X"), follow with the why ("because Y").

Example (_capabilities/policy/no-git-ops/text.md):

## No git operations

You MUST NOT invoke `git`, `gh repo`, `gh api /repos`, or any shell
command that modifies git state. Orchestrator handles all git operations
(commits, branches, pushes, rebases).

If your task requires staging a change, describe it in the return
file-list — the orchestrator will commit on your behalf.

Bypass exists for orchestrator-meta agents only; it is not available here.

Capability trait contract (Rust)

All gates and verifies implement the same trait, dispatched by string name. Registry in kei-agent-runtime/src/registry.rs maps "policy::no-git-ops" to Box<dyn Capability>.

// kei-agent-runtime/src/capability.rs

pub trait Capability: Send + Sync {
    fn name(&self) -> &'static str;

    /// PreToolUse gate. Called by `kei-capability check <name>` binary.
    /// Receives the hook JSON payload from Claude Code on stdin.
    /// Returns Allow / Deny{reason} / NotApplicable.
    fn check(&self, ctx: &GateContext) -> GateDecision {
        GateDecision::NotApplicable  // default: no gate, verify-only
    }

    /// On-return verification predicate. Called by `kei-capability verify <name>`.
    /// Receives task context (agent-id, worktree path, main repo, task.toml values).
    /// Returns Pass / Fail{reason}.
    fn verify(&self, ctx: &VerifyContext) -> VerifyResult {
        VerifyResult::Pass  // default: no verify, gate-only
    }
}

pub struct GateContext<'a> {
    pub tool_name: &'a str,
    pub tool_input: &'a Value,
    pub task: &'a TaskSpec,          // parsed task.toml
    pub env: &'a HashMap<String, String>,
}

pub enum GateDecision {
    Allow,
    Deny { reason: String },
    NotApplicable,
}

pub struct VerifyContext<'a> {
    pub agent_id: &'a str,
    pub task: &'a TaskSpec,
    pub worktree_path: &'a Path,
    pub main_repo: &'a Path,
    pub run_mode: RunMode,           // Worktree | SimulatedMerge | Both
}

pub enum VerifyResult {
    Pass,
    Fail { reason: String, detail: Option<String> },
}

Example implementation (_primitives/_rust/kei-agent-runtime/src/gates/policy_no_git_ops.rs):

use crate::capability::*;
use regex::Regex;
use once_cell::sync::Lazy;

pub struct NoGitOps;

static GIT_PATTERNS: Lazy<Vec<Regex>> = Lazy::new(|| vec![
    Regex::new(r"(?m)(?:^|[;&|]|\s)git(?:\s|$)").unwrap(),
    Regex::new(r"(?m)(?:^|[;&|]|\s)gh\s+repo").unwrap(),
    Regex::new(r"(?m)(?:^|[;&|]|\s)gh\s+api\s+/?repos").unwrap(),
]);

impl Capability for NoGitOps {
    fn name(&self) -> &'static str { "policy::no-git-ops" }

    fn check(&self, ctx: &GateContext) -> GateDecision {
        if ctx.tool_name != "Bash" { return GateDecision::NotApplicable; }
        if ctx.env.get("ORCHESTRATOR_META").map(|v| v == "1").unwrap_or(false) {
            return GateDecision::Allow;
        }
        let cmd = ctx.tool_input.get("command").and_then(|v| v.as_str()).unwrap_or("");
        for pat in GIT_PATTERNS.iter() {
            if pat.is_match(cmd) {
                return GateDecision::Deny {
                    reason: format!("RULE 0.13 — git operation blocked (pattern {})", pat.as_str()),
                };
            }
        }
        GateDecision::Allow
    }
}

Example verify (_primitives/_rust/kei-agent-runtime/src/verifies/quality_cargo_check_green.rs):

use crate::capability::*;
use std::process::Command;

pub struct CargoCheckGreen;

impl Capability for CargoCheckGreen {
    fn name(&self) -> &'static str { "quality::cargo-check-green" }

    fn verify(&self, ctx: &VerifyContext) -> VerifyResult {
        let run_dir = match ctx.run_mode {
            RunMode::Worktree => ctx.worktree_path,
            RunMode::SimulatedMerge => &ctx.simulated_merge_path(),
            RunMode::Both => unreachable!("runtime runs `both` as two sequential calls"),
        };
        let out = Command::new("cargo")
            .arg("check")
            .arg("--workspace")
            .current_dir(run_dir.join("_primitives/_rust"))
            .output();
        match out {
            Err(e) => VerifyResult::Fail {
                reason: "cargo invocation failed".to_string(),
                detail: Some(e.to_string()),
            },
            Ok(o) if !o.status.success() => {
                let tail = String::from_utf8_lossy(&o.stderr).lines().rev().take(5).collect::<Vec<_>>();
                VerifyResult::Fail {
                    reason: "cargo check --workspace FAILED — agent-local green ≠ integration green".to_string(),
                    detail: Some(tail.into_iter().rev().collect::<Vec<_>>().join("\n")),
                }
            }
            Ok(_) => VerifyResult::Pass,
        }
    }
}

Verify execution — worktree → simulated merge

The orchestrator runs verification in two sequential passes for run-mode = "both":

Pass 1 — worktree (fail-fast)
  cd <agent-worktree>
  run capability.verify(RunMode::Worktree)
  if Fail → reject immediately, don't bother with pass 2

Pass 2 — simulated-merge (integration guarantee)
  git checkout -b test-merge/<agent-id> main   # from MAIN repo, not worktree
  git apply <agent-diff>                        # apply agent's changes on clean main
  cd <test-merge branch>
  run capability.verify(RunMode::SimulatedMerge)
  if Fail → reject with regression report
  if Pass → safe to merge, orchestrator proceeds

Why both: agent's worktree passing doesn't mean merged-main passing. E1's jsonschema regression was green in worktree (no real atoms there) but broke main integration (real atom schemas triggered the 0.17→0.18 breaking change). Simulated merge catches this class before it lands on main.

Implementation lives in kei-agent-runtime/src/simulated_merge.rs — creates a temp worktree via git worktree add, applies diff, runs verify, cleans up.


Role — _roles/<name>.toml shape

[role]
name = "edit-local"
display-name = "code-implementer (local edit scope)"
description = "Write code + run cargo check/test + emit report. No git, no workspace touches."

[capabilities]
# Ordered list — text.md fragments concatenated in this order
required = [
  "policy::no-git-ops",
  "scope::files-whitelist",
  "scope::files-denylist",
  "quality::constructor-pattern",
  "quality::cargo-check-green",
  "quality::tests-green",
  "safety::no-dep-bump",
  "output::report-format",
]

[tools]
# Tool allowlist — anything not in this list is denied
allowed = ["Read", "Write", "Edit", "Glob", "Grep", "Bash"]
# Bash further restricted by quality/tools atoms
bash-patterns-allowed = ['^cargo( |$)', '^mkdir( |$)', '^rm -rf /tmp/']

[escalation]
policy = "ask-via-return"              # ask-via-return | orchestrator-notify | fail-fast

Task spec — task.toml shape (orchestrator writes per spawn)

[task]
role = "edit-local"
agent-id = "abc123…"                  # allocated by kei-ledger fork
parent-agent = null                    # or parent ID for nested

[scope]
# Parameterizes scope::files-whitelist + scope::files-denylist
files-whitelist = [
  "_primitives/_rust/kei-forge/**",
]
files-denylist = [
  "_primitives/_rust/Cargo.toml",
  "_primitives/_rust/Cargo.lock",
  "scripts/**",
  ".github/**",
]

[verification]
# Parameterizes quality/* caps
cargo-check-crates = ["kei-forge"]
cargo-test-crates = ["kei-forge"]
test-count-min = 44

[output]
# Parameterizes output/report-format
report-fields-required = ["files-touched", "cargo-check", "cargo-test", "loc-delta"]

[body]
# Free-text task instructions, concatenated AFTER role capability fragments
text = """
Replace shell-out with pure-Rust templating. …
"""

Runtime execution contract

kei-agent-runtime crate provides:

# Compose prompt from task spec
kei-agent-runtime compose <task.toml>
# → writes <task-dir>/prompt.md

# Spawn agent with composed prompt + install gates + record ledger
kei-agent-runtime spawn <task.toml>
# → returns agent-id; background-task notification semantics

# Run all capability verify predicates against agent's return
kei-agent-runtime verify <task.toml> <worktree-path>
# → exit 0 if all hold, non-zero with report of violations

# One-shot helper: compose + spawn + verify
kei-agent-runtime run <task.toml>

Execution flow:

1. orchestrator writes task.toml
2. `kei-agent-runtime compose` → prompt.md
3. `kei-agent-runtime spawn` →
     a. kei-ledger fork <agent-id>
     b. install PreToolUse gates parameterized by task.scope
     c. Agent tool call with isolation=worktree + composed prompt
4. [agent executes]
5. `kei-agent-runtime verify` →
     a. run each capability verify.sh from MAIN repo (not worktree)
     b. collect all violations
     c. exit 0 if empty, non-zero with report
6. orchestrator decides: merge | reject + respawn | reject + rollback

Initial capability atom inventory (phase 1 builds these 10)

Name Category text / gate / verify Core restriction
policy::no-git-ops policy ✓/✓/✓ Block git, gh repo, gh api /repos
scope::files-whitelist scope ✓/✓/✓ PreToolUse:Edit|Write denies paths outside whitelist; on-return git diff check
scope::files-denylist scope ✓/✓/✓ PreToolUse:Edit|Write denies paths in denylist (overrides whitelist)
quality::constructor-pattern quality ✓/—/✓ On return: no file > 200 LOC, no fn > 30 LOC
quality::cargo-check-green quality ✓/—/✓ On return: cargo check --workspace from MAIN passes
quality::tests-green quality ✓/—/✓ On return: cargo test -p <crate> passes, count ≥ task min
safety::no-dep-bump safety ✓/✓/✓ PreToolUse:Edit on Cargo.toml denies unless task opts in; on-return lock-diff check
output::report-format output ✓/—/✓ On return: parse report, assert required fields present
tools::read-only tools ✓/✓/— PreToolUse denies Edit/Write entirely
tools::cargo-only-bash tools ✓/✓/— PreToolUse:Bash denies unless command matches allowlist pattern

Initial role inventory (phase 2 builds these 5)

Role Capabilities Tools
read-only tools::read-only + output::report-format + output::severity-grade Read / Glob / Grep / WebFetch
explorer read-only caps + tools::cargo-only-bash (for cargo check) + Bash-cargo
edit-local policy::no-git-ops + scope::* + quality::* + safety::no-dep-bump + output::report-format + Edit / Write / Bash-cargo
edit-shared edit-local caps + permission for specified SSoT patterns Same + SSoT paths
git-ops Documented-only, NOT spawnable (orchestrator holds this) All

Decision log — resolved 2026-04-23

# Question Decision Rationale
1 Layout per capability Declarative bundle (capability.toml + text.md) + Rust modules in runtime crate Declarative artefacts live with capability; executable logic lives with its sibling capabilities in one Rust crate for shared tests + type safety
2 Gate language Rust via kei-capability check <name> binary; shell hook = 3-line exec glue Type safety, unit tests, one compilation unit for all gates. Shell remains only as Claude-Code-hook-protocol adapter
3 Verify language Rust same binary, kei-capability verify <name> subcommand Same reasoning. Cargo output parsing, LOC checks, diff analysis — all better in Rust
4 Config format (capability.toml / role.toml / task.toml) TOML Consistent with Cargo ecosystem. YAML reserved only for locked atom .md frontmatter (immutable under atom substrate v1 lock)
5 Capability ID separator :: Consistent with atom IDs. Rust-native
6 Capability path layout Nested _capabilities/<category>/<slug>/ Scales to 50+ capabilities, category browsability
7 Text fragment max 200 words per capability Agent context budget; forces atomicity
8 Verify execution worktree short-circuit → simulated-merge (default both for quality::*) Catches E1-jsonschema-class integration regressions before main merge. See §Verify execution

Locked values: all 8 above. Breaking changes require explicit user revocation + all-phases sync.


Phase plan (post-lock, parallel)

Phase What Depends on Agent Estimate
0 This schema + lock marker me 0.5 day ✓
1 Capability library — 10 × (capability.toml + text.md) = 20 declarative files phase 0 1 code-implementer 1-2 days
2 Role matrix — 5 _roles/*.toml + auto-gen docs/AGENT-ROLES.md phase 0 1 code-implementer 0.5 day
3 kei-agent-runtime + kei-capability binaries — compose/spawn/verify CLI + 6 gate modules + 8 verify modules + registry + simulated-merge executor phase 0 1 code-implementer 5-6 days
4 ✓ Hook wiring — agent-capability-check.sh + agent-capability-verify.sh 3-line glue + settings.json registration phases 1+3 1 code-implementer 0.5 day (shipped)
5 ✓ Migration — 5 kit-shipped agents (code-implementer / critic / architect / security-auditor / validator) adopt role+task-spec invocation via new substrate_role manifest field phases 1+2+3+4 1 code-implementer 1 day (shipped)

Phases 1, 2, 3 start in parallel immediately after lock (different dirs, zero file overlap). Phase 4 depends on 1+3. Phase 5 depends on everything.

Total wall-time with parallel phases 1+2+3: ~7-8 days from lock (phase 3 is critical path).


Integration with substrate v1

This schema is additive to locked SUBSTRATE-SCHEMA.md. The two SSoTs sit side by side:

  • SUBSTRATE-SCHEMA.md — how code decomposes into atoms (locked 2026-04-22)
  • AGENT-SUBSTRATE-SCHEMA.md — how agent invocation decomposes into capabilities (this doc)

Cross-ref: agent capability quality::cargo-check-green verifies that atoms compiled; atom agents produced via kei-forge can themselves be invoked through kei-runtime (atom substrate) OR composed into role definitions (agent substrate).

Eventually (post-both-locks): agents compose atoms, atoms compose agents. Symmetric substrates.


Lock declaration

Once this document is approved by the user and docs/AGENT-SCHEMA-LOCKED.md is committed, the capability-triplet shape + role shape + task-spec shape + runtime contract are immutable for 3 weeks (shorter lock than atom substrate because agent substrate is greenfield, expected revisions).

Breaking changes during lock require:

  1. Explicit revocation by user
  2. All parallel phase agents paused
  3. Lock marker amended with revocation reason
  4. kei-ledger row: bypass reason + revocation timestamp

Non-breaking additions (new capability atoms beyond the initial 10, new roles, new parameterized fields on existing capabilities) are allowed during lock.


Migrated agents

Phase 5 wired the 5 kit-shipped agents to role+task-spec invocation via a new substrate_role field on the manifest. The assembler reads the declared role, expands each of its capability text.md fragments, and emits them under a # AGENT SUBSTRATE — role <name> section placed immediately after # ROLE and before the first behavioural block.

Agent manifest Role Capabilities expanded
_manifests/kei-code-implementer.toml edit-local policy::no-git-ops, scope::files-whitelist, scope::files-denylist, quality::constructor-pattern, quality::cargo-check-green, quality::tests-green, safety::no-dep-bump, output::report-format
_manifests/kei-critic.toml read-only tools::read-only, output::report-format, output::severity-grade
_manifests/kei-architect.toml read-only tools::read-only, output::report-format, output::severity-grade
_manifests/kei-security-auditor.toml read-only tools::read-only, output::report-format, output::severity-grade
_manifests/kei-validator.toml read-only tools::read-only, output::report-format, output::severity-grade

Backward compatibility: the substrate_role field is optional. The 7 non-migrated kit agents (kei-cost-guardian, kei-fal-ai-runner, kei-infra-implementer, kei-ml-implementer, kei-ml-researcher, kei-modal-runner, kei-researcher) continue to assemble without change; a deferred v0.24 migration wave will promote them. Task-spec examples showing how the orchestrator invokes each migrated agent live under _templates/task-examples/.

Deferred extension candidates (non-breaking post-lock)

Capability atoms NOT in the initial 10 but good follow-up PRs (non-breaking additions during lock window):

  • safety::no-mass-delete — PreToolUse denies rm -rf on more than N files
  • output::ledger-row-required — verify agent emitted ledger row per RULE 0.12
  • quality::no-warningscargo build --workspace with -D warnings
  • scope::no-rule-edits — denies edits to ~/.claude/rules/*.md unless orchestrator-meta

Role git-ops — documented in docs/AGENT-ROLES.md only; _roles/git-ops.toml has spawnable = false field. Orchestrator code refuses to spawn it. Exists for documentation of "who can do git" boundary.

Task spec persistence: task.toml files are ephemeral (gitignored under tasks/). Ledger row includes spec-SHA so historical specs are recoverable from kei-sage archive if someone wants cold-storage replay.