From dc83cb21176a5e33070edf5a59ae0db4eef31281 Mon Sep 17 00:00:00 2001 From: Parfii-bot Date: Thu, 23 Apr 2026 02:35:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(agent-substrate/phase-2):=20role=20matrix?= =?UTF-8?q?=20=E2=80=94=205=20roles=20+=20AGENT-ROLES.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 5 _roles/*.toml per locked §Initial role inventory: - read-only → tools::read-only + output::report-format + severity-grade - explorer → read-only caps + tools::cargo-only-bash - edit-local → no-git-ops + scope::files-{white,deny}list + quality::* + safety::no-dep-bump + output::report-format - edit-shared → edit-local caps + relaxed SSoT whitelist (task-time param) + escalation tightened to orchestrator-notify - git-ops → spawnable = false, documentation-only All 11 capability names referenced match phase-1 deliverable path _capabilities/// (cross-ref verified before commit). docs/AGENT-ROLES.md (223 LOC) — human-readable matrix: per-role sections + cross-role capability matrix + explicit non-spawnable-git-ops block. Drift note for orchestrator integration review: edit-local/edit-shared use inline bash-patterns-allowed = ['^cargo( |$)', '^mkdir( |$)', '^rm -rf /tmp/'] instead of composing with tools::cargo-only-bash capability (extra patterns not in that atom). Agent footnoted — resolution deferred to post-integration (either parameterize the cap or introduce tools::cargo-plus-basic-bash variant). Co-Authored-By: Claude Opus 4.7 (1M context) --- _roles/edit-local.toml | 27 +++++ _roles/edit-shared.toml | 29 ++++++ _roles/explorer.toml | 23 +++++ _roles/git-ops.toml | 20 ++++ _roles/read-only.toml | 22 ++++ docs/AGENT-ROLES.md | 223 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 344 insertions(+) create mode 100644 _roles/edit-local.toml create mode 100644 _roles/edit-shared.toml create mode 100644 _roles/explorer.toml create mode 100644 _roles/git-ops.toml create mode 100644 _roles/read-only.toml create mode 100644 docs/AGENT-ROLES.md diff --git a/_roles/edit-local.toml b/_roles/edit-local.toml new file mode 100644 index 0000000..8910296 --- /dev/null +++ b/_roles/edit-local.toml @@ -0,0 +1,27 @@ +[role] +name = "edit-local" +display-name = "code-implementer (local edit scope)" +description = "Write code within whitelisted files, run cargo check/test, emit structured report. No git, no workspace-level touches, no dep bumps." +spawnable = true + +[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 tools::cargo-only-bash-adjacent patterns +bash-patterns-allowed = ['^cargo( |$)', '^mkdir( |$)', '^rm -rf /tmp/'] + +[escalation] +policy = "ask-via-return" diff --git a/_roles/edit-shared.toml b/_roles/edit-shared.toml new file mode 100644 index 0000000..4621cdc --- /dev/null +++ b/_roles/edit-shared.toml @@ -0,0 +1,29 @@ +[role] +name = "edit-shared" +display-name = "code-implementer (shared-SSoT edit scope)" +description = "Same baseline as edit-local, with one relaxed scope entry permitting edits to a task-specified SSoT path (e.g. workspace Cargo.toml, registry file). The relaxation is configured per task via `[scope].files-whitelist` in task.toml." +spawnable = true + +[capabilities] +# Ordered list — text.md fragments concatenated in this order +# Identical to edit-local; the SSoT relaxation rides on scope::files-whitelist +# parameterization in task.toml, not on a separate capability. +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-patterns-allowed = ['^cargo( |$)', '^mkdir( |$)', '^rm -rf /tmp/'] + +[escalation] +# Tightened vs edit-local: SSoT edits notify orchestrator on any unclear case +policy = "orchestrator-notify" diff --git a/_roles/explorer.toml b/_roles/explorer.toml new file mode 100644 index 0000000..19d6457 --- /dev/null +++ b/_roles/explorer.toml @@ -0,0 +1,23 @@ +[role] +name = "explorer" +display-name = "explorer + cargo-check (read-only analyst with build probe)" +description = "Read-only analyst that may run cargo-family commands for build/test introspection. No edits, no git, no non-cargo shell." +spawnable = true + +[capabilities] +# Ordered list — text.md fragments concatenated in this order +required = [ + "tools::read-only", + "tools::cargo-only-bash", + "output::report-format", + "output::severity-grade", +] + +[tools] +# Tool allowlist — anything not in this list is denied +allowed = ["Read", "Glob", "Grep", "WebFetch", "Bash"] +# Bash restricted by tools::cargo-only-bash — cargo invocations only +bash-patterns-allowed = ['^cargo( |$)'] + +[escalation] +policy = "ask-via-return" diff --git a/_roles/git-ops.toml b/_roles/git-ops.toml new file mode 100644 index 0000000..4bc49c4 --- /dev/null +++ b/_roles/git-ops.toml @@ -0,0 +1,20 @@ +[role] +name = "git-ops" +display-name = "git operator (orchestrator-only, NOT spawnable)" +description = "Documented boundary of git authority. Per RULE 0.13, only the orchestrator (main session) holds git power: branch creation, commit, push, merge, rebase, reset, tag. This role is documented for completeness and is refused by kei-agent-runtime at spawn time." +spawnable = false + +[capabilities] +# No capability restrictions declared here — this role is never composed into +# a prompt. The sole consumer is `kei-agent-runtime spawn`, which checks +# `spawnable = false` and refuses the invocation with a pointer to RULE 0.13. +required = [] + +[tools] +# Documented as "everything"; not enforced because role is non-spawnable. +allowed = ["Read", "Write", "Edit", "Glob", "Grep", "Bash", "WebFetch"] +bash-patterns-allowed = ['.*'] + +[escalation] +# Not reachable at runtime — orchestrator owns this context directly. +policy = "fail-fast" diff --git a/_roles/read-only.toml b/_roles/read-only.toml new file mode 100644 index 0000000..c235dec --- /dev/null +++ b/_roles/read-only.toml @@ -0,0 +1,22 @@ +[role] +name = "read-only" +display-name = "explorer (read-only analyst)" +description = "Read-only agent: inspects code, emits structured report with severity grades. No shell, no edits, no git." +spawnable = true + +[capabilities] +# Ordered list — text.md fragments concatenated in this order +required = [ + "tools::read-only", + "output::report-format", + "output::severity-grade", +] + +[tools] +# Tool allowlist — anything not in this list is denied +allowed = ["Read", "Glob", "Grep", "WebFetch"] +# No Bash → no bash-patterns-allowed +bash-patterns-allowed = [] + +[escalation] +policy = "ask-via-return" diff --git a/docs/AGENT-ROLES.md b/docs/AGENT-ROLES.md new file mode 100644 index 0000000..1e7977c --- /dev/null +++ b/docs/AGENT-ROLES.md @@ -0,0 +1,223 @@ +# Agent Roles — Human-Readable Matrix + +**SSoT:** `_roles/*.toml` (5 files). +**Schema:** [AGENT-SUBSTRATE-SCHEMA.md](./AGENT-SUBSTRATE-SCHEMA.md) §Role. +**Lock marker:** [AGENT-SCHEMA-LOCKED.md](./AGENT-SCHEMA-LOCKED.md). + +Five roles compose locked capability atoms from `_capabilities///` into prompt bundles for the Agent tool. Each role is a declarative TOML; the `kei-agent-runtime compose` step concatenates the listed capability `text.md` fragments in order to produce the final `prompt.md` for an agent invocation. Tool and bash-pattern allowlists are enforced at PreToolUse by `kei-capability check`; verify-class capabilities are enforced on agent return by `kei-capability verify`. + +This document is derived from the 5 role TOMLs by hand. When `kei-agent-runtime` ships (phase 3), a `kei-agent-runtime doc-roles` subcommand will regenerate this file mechanically. + +--- + +## 1. `read-only` + +**Display name:** explorer (read-only analyst) +**Spawnable:** yes +**Escalation:** `ask-via-return` + +Pure inspection agent. Reads code and docs, optionally fetches a URL, emits a structured report with severity grades. No shell, no edits, no git. Cheapest and safest role. + +**Capabilities bundled:** + +- [`tools::read-only`](../_capabilities/tools/read-only/text.md) — denies `Edit` and `Write` entirely at PreToolUse +- [`output::report-format`](../_capabilities/output/report-format/text.md) — verify: parse report, assert required fields present +- [`output::severity-grade`](../_capabilities/output/severity-grade/text.md) — verify: each finding tagged with E1-E6 evidence grade + +**Tools allowed:** + +| Tool | Allowed | Notes | +|---|---|---| +| Read | yes | — | +| Glob | yes | — | +| Grep | yes | — | +| WebFetch | yes | external references | +| Edit | no | blocked by `tools::read-only` | +| Write | no | blocked by `tools::read-only` | +| Bash | no | not in allowlist | + +**Typical use cases:** + +- Code audit / critic passes (anti-pattern sweep, Constructor Pattern compliance) +- Prior-art search, docs survey, dependency research + +--- + +## 2. `explorer` + +**Display name:** explorer + cargo-check (read-only analyst with build probe) +**Spawnable:** yes +**Escalation:** `ask-via-return` + +Read-only baseline plus a single permitted shell family: `cargo` invocations. Use when an audit needs `cargo check`, `cargo test`, `cargo tree`, `cargo metadata` to ground findings in actual build state — still cannot edit, still cannot git. + +**Capabilities bundled:** + +- [`tools::read-only`](../_capabilities/tools/read-only/text.md) +- [`tools::cargo-only-bash`](../_capabilities/tools/cargo-only-bash/text.md) — PreToolUse:Bash denies unless command matches `^cargo( |$)` +- [`output::report-format`](../_capabilities/output/report-format/text.md) +- [`output::severity-grade`](../_capabilities/output/severity-grade/text.md) + +**Tools allowed:** + +| Tool | Allowed | Notes | +|---|---|---| +| Read | yes | — | +| Glob | yes | — | +| Grep | yes | — | +| WebFetch | yes | — | +| Bash | yes | only `^cargo( |$)` patterns | +| Edit | no | blocked by `tools::read-only` | +| Write | no | blocked by `tools::read-only` | + +**Typical use cases:** + +- Build-state-grounded audits (test counts, compile errors, workspace graph) +- Reproduction of integration-test failures without risk of edits + +--- + +## 3. `edit-local` + +**Display name:** code-implementer (local edit scope) +**Spawnable:** yes +**Escalation:** `ask-via-return` + +The default code-writing role. Writes to task-whitelisted files, runs cargo-family commands, emits a required-field report. Cannot touch git, cannot bump deps, cannot edit files outside its whitelist or inside its denylist. On return, four verify-class capabilities run: Constructor Pattern limits, workspace cargo check, per-crate tests, and lock-file stability. + +**Capabilities bundled:** + +- [`policy::no-git-ops`](../_capabilities/policy/no-git-ops/text.md) — blocks `git`, `gh repo`, `gh api /repos` at PreToolUse:Bash +- [`scope::files-whitelist`](../_capabilities/scope/files-whitelist/text.md) — PreToolUse:Edit|Write denies paths outside whitelist; on-return git diff check +- [`scope::files-denylist`](../_capabilities/scope/files-denylist/text.md) — denies paths in denylist (overrides whitelist) +- [`quality::constructor-pattern`](../_capabilities/quality/constructor-pattern/text.md) — verify: no file > 200 LOC, no fn > 30 LOC +- [`quality::cargo-check-green`](../_capabilities/quality/cargo-check-green/text.md) — verify: `cargo check --workspace` from MAIN passes (simulated-merge) +- [`quality::tests-green`](../_capabilities/quality/tests-green/text.md) — verify: `cargo test -p ` passes, count ≥ task min +- [`safety::no-dep-bump`](../_capabilities/safety/no-dep-bump/text.md) — PreToolUse:Edit on Cargo.toml denies unless task opts in +- [`output::report-format`](../_capabilities/output/report-format/text.md) + +**Tools allowed:** + +| Tool | Allowed | Notes | +|---|---|---| +| Read | yes | — | +| Write | yes | scope-gated | +| Edit | yes | scope-gated | +| Glob | yes | — | +| Grep | yes | — | +| Bash | yes | patterns: `^cargo( |$)`, `^mkdir( |$)`, `^rm -rf /tmp/` | + +**Typical use cases:** + +- Implement a new crate / feature within a bounded file whitelist +- Refactor inside one crate with dep-lock stability guaranteed + +--- + +## 4. `edit-shared` + +**Display name:** code-implementer (shared-SSoT edit scope) +**Spawnable:** yes +**Escalation:** `orchestrator-notify` + +Same capabilities as `edit-local`. The difference is **operational, not declarative**: for `edit-shared`, the orchestrator parameterizes `scope::files-whitelist` in `task.toml` to include one or more SSoT paths (e.g. workspace `Cargo.toml`, a registry file, a cross-crate type definition). Escalation is tightened from `ask-via-return` to `orchestrator-notify` so SSoT edits surface immediately. + +**Capabilities bundled:** identical to `edit-local` — the SSoT relaxation rides on per-task `scope::files-whitelist` parameterization, not on a different capability set. + +**Tools allowed:** identical to `edit-local`. + +**Typical use cases:** + +- Workspace-level edits (add a member crate, bump a shared version) +- Cross-crate API changes where one type SSoT must be edited alongside its consumers + +**Difference from `edit-local` at a glance:** + +| Dimension | `edit-local` | `edit-shared` | +|---|---|---| +| Capability set | identical | identical | +| Whitelist (task.toml) | local crate paths only | local crate + one SSoT path | +| Escalation | `ask-via-return` | `orchestrator-notify` | +| Typical use | inside one crate | crosses a crate or workspace boundary | + +--- + +## 5. `git-ops` — NON-SPAWNABLE (orchestrator-only) + +**Display name:** git operator (orchestrator-only, NOT spawnable) +**Spawnable:** **no** +**Escalation:** `fail-fast` (not reachable at runtime) + +Documented boundary of git authority — not a live role. Present in the inventory so that "who can run git" has an explicit declarative answer. + +Per [RULE 0.13 — ORCHESTRATOR BRANCH FIRST](../../.claude/rules/orchestrator-branch-first.md), only the orchestrator (main session) holds git power: + +- branch creation (`git checkout -b …`) +- commit (`git add && git commit -m …`) +- push (`git push `) +- merge (`git merge --no-ff`, `git merge --squash`) +- rebase, reset, tag + +Agents running inside `.claude/worktrees//` cannot invoke `git` — the sandbox denies Bash inside the worktree path. This role exists to make that boundary visible in the capability substrate, not to enable spawns. + +`kei-agent-runtime spawn` MUST refuse any `task.toml` whose `[task].role = "git-ops"` with a pointer to RULE 0.13. The refusal is a hard error, not a warning. + +**Rationale (why documented at all):** + +- Future contributors see "git is a role" and "that role is unreachable" in the same place +- `kei-agent-runtime doc-roles` regeneration will surface the non-spawnable notice so it never goes stale +- Matches how `_roles/git-ops.toml` holds `spawnable = false` explicitly — declarative, greppable + +--- + +## Cross-role capability matrix + +Capabilities as rows, roles as columns. A ✓ means the role lists the capability in `[capabilities].required`; ✗ means it does not. + +| Capability | `read-only` | `explorer` | `edit-local` | `edit-shared` | `git-ops` | +|---|:-:|:-:|:-:|:-:|:-:| +| `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` | ✓ | ✓ | ✓ | ✓ | ✗ | +| `output::severity-grade` | ✓ | ✓ | ✗ | ✗ | ✗ | +| `tools::read-only` | ✓ | ✓ | ✗ | ✗ | ✗ | +| `tools::cargo-only-bash` | ✗ | ✓ | ✗ (¹) | ✗ (¹) | ✗ | + +(¹) `edit-local` and `edit-shared` do not compose `tools::cargo-only-bash` as a capability atom; instead they carry an inline `bash-patterns-allowed` list in `[tools]` that encodes the same restriction. Both routes converge at the PreToolUse:Bash gate. Phase 3 runtime may later collapse the inline list into `tools::cargo-only-bash-plus-mkdir-and-tmp` capability atoms — non-breaking. + +## Tool allowlist matrix + +| Tool | `read-only` | `explorer` | `edit-local` | `edit-shared` | `git-ops` (²) | +|---|:-:|:-:|:-:|:-:|:-:| +| Read | ✓ | ✓ | ✓ | ✓ | ✓ | +| Glob | ✓ | ✓ | ✓ | ✓ | ✓ | +| Grep | ✓ | ✓ | ✓ | ✓ | ✓ | +| WebFetch | ✓ | ✓ | ✗ | ✗ | ✓ | +| Edit | ✗ | ✗ | ✓ | ✓ | ✓ | +| Write | ✗ | ✗ | ✓ | ✓ | ✓ | +| Bash | ✗ | cargo-only | cargo+mkdir+tmp | cargo+mkdir+tmp | any | + +(²) `git-ops` values are documentation only — the role is non-spawnable. + +## Escalation policy matrix + +| Role | Policy | Meaning | +|---|---|---| +| `read-only` | `ask-via-return` | Surface questions in the final report; orchestrator reads them | +| `explorer` | `ask-via-return` | Same | +| `edit-local` | `ask-via-return` | Same | +| `edit-shared` | `orchestrator-notify` | Touching SSoT ⇒ notify orchestrator before completing | +| `git-ops` | `fail-fast` | Unreachable; any spawn attempt errors | + +--- + +## Maintenance + +- Changes to any `_roles/*.toml` require updating this file in the same commit. +- New roles are added as new sections 6+ with the same structure, and new columns added to the two matrices above. +- When `kei-agent-runtime doc-roles` ships in phase 3, it replaces the hand-authored matrix; the top-of-file "derived by hand" note is removed then.