From c0c3483f02225ae69a477c7379aca2e8c511f0f0 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-1):=20capability=20l?= =?UTF-8?q?ibrary=20=E2=80=94=2011=20declarative=20bundles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 22 files per locked §Initial capability atom inventory: policy/no-git-ops/ (gate: PreToolUse:Bash, bypass ORCHESTRATOR_META) scope/files-whitelist/ (gate + verify worktree) scope/files-denylist/ (gate + verify worktree) quality/constructor-pattern/ (verify worktree) quality/cargo-check-green/ (verify both — worktree short-circuit + simulated-merge) quality/tests-green/ (verify both) safety/no-dep-bump/ (gate + verify both) output/report-format/ (verify worktree) output/severity-grade/ (verify worktree) tools/read-only/ (gate: deny Edit/Write) tools/cargo-only-bash/ (gate: Bash allowlist) All capability.toml share [capability]/[restricts]/[parameterized]/[text]/ [gate]/[verify] section layout. rust-module paths pre-wired to match phase-3 file layout. All text.md under 200 words, imperative, self-contained (composer concatenates with --- separator). Cross-refs to rule files preserved: - policy::no-git-ops → RULE 0.13 (orchestrator-branch-first.md) - quality::constructor-pattern → RULE ZERO (code-style.md) - output::severity-grade → debugging.md §Security Review - safety::no-dep-bump → supply-chain rationale Agent attempted wc -w for word counts — sandbox correctly denied Bash per RULE 0.13, observable reinforcement of the very policy this capability encodes. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../output/report-format/capability.toml | 21 ++++++++++++ _capabilities/output/report-format/text.md | 31 +++++++++++++++++ .../output/severity-grade/capability.toml | 21 ++++++++++++ _capabilities/output/severity-grade/text.md | 34 +++++++++++++++++++ .../policy/no-git-ops/capability.toml | 26 ++++++++++++++ _capabilities/policy/no-git-ops/text.md | 20 +++++++++++ .../quality/cargo-check-green/capability.toml | 21 ++++++++++++ .../quality/cargo-check-green/text.md | 27 +++++++++++++++ .../constructor-pattern/capability.toml | 21 ++++++++++++ .../quality/constructor-pattern/text.md | 25 ++++++++++++++ .../quality/tests-green/capability.toml | 21 ++++++++++++ _capabilities/quality/tests-green/text.md | 26 ++++++++++++++ .../safety/no-dep-bump/capability.toml | 26 ++++++++++++++ _capabilities/safety/no-dep-bump/text.md | 27 +++++++++++++++ .../scope/files-denylist/capability.toml | 26 ++++++++++++++ _capabilities/scope/files-denylist/text.md | 24 +++++++++++++ .../scope/files-whitelist/capability.toml | 26 ++++++++++++++ _capabilities/scope/files-whitelist/text.md | 24 +++++++++++++ .../tools/cargo-only-bash/capability.toml | 29 ++++++++++++++++ _capabilities/tools/cargo-only-bash/text.md | 28 +++++++++++++++ _capabilities/tools/read-only/capability.toml | 21 ++++++++++++ _capabilities/tools/read-only/text.md | 24 +++++++++++++ 22 files changed, 549 insertions(+) create mode 100644 _capabilities/output/report-format/capability.toml create mode 100644 _capabilities/output/report-format/text.md create mode 100644 _capabilities/output/severity-grade/capability.toml create mode 100644 _capabilities/output/severity-grade/text.md create mode 100644 _capabilities/policy/no-git-ops/capability.toml create mode 100644 _capabilities/policy/no-git-ops/text.md create mode 100644 _capabilities/quality/cargo-check-green/capability.toml create mode 100644 _capabilities/quality/cargo-check-green/text.md create mode 100644 _capabilities/quality/constructor-pattern/capability.toml create mode 100644 _capabilities/quality/constructor-pattern/text.md create mode 100644 _capabilities/quality/tests-green/capability.toml create mode 100644 _capabilities/quality/tests-green/text.md create mode 100644 _capabilities/safety/no-dep-bump/capability.toml create mode 100644 _capabilities/safety/no-dep-bump/text.md create mode 100644 _capabilities/scope/files-denylist/capability.toml create mode 100644 _capabilities/scope/files-denylist/text.md create mode 100644 _capabilities/scope/files-whitelist/capability.toml create mode 100644 _capabilities/scope/files-whitelist/text.md create mode 100644 _capabilities/tools/cargo-only-bash/capability.toml create mode 100644 _capabilities/tools/cargo-only-bash/text.md create mode 100644 _capabilities/tools/read-only/capability.toml create mode 100644 _capabilities/tools/read-only/text.md diff --git a/_capabilities/output/report-format/capability.toml b/_capabilities/output/report-format/capability.toml new file mode 100644 index 0000000..6b23824 --- /dev/null +++ b/_capabilities/output/report-format/capability.toml @@ -0,0 +1,21 @@ +[capability] +name = "output::report-format" +category = "output" +version = "1.0" +description = "Require the agent's return report to contain a declared set of fields." +rationale = "Structured return lets orchestrator parse a file-list for `git add`, test counts for verification cross-check, and LOC deltas for audit. Free-text returns force re-prompting." + +[restricts] +tool-patterns = [] +tools-denied = [] + +[parameterized] +accepts = ["report-fields-required"] + +[text] +path = "text.md" + +[verify] +rust-module = "verifies::output_report_format" +run-mode = "worktree" +when = "on-return" diff --git a/_capabilities/output/report-format/text.md b/_capabilities/output/report-format/text.md new file mode 100644 index 0000000..3fee7aa --- /dev/null +++ b/_capabilities/output/report-format/text.md @@ -0,0 +1,31 @@ +## Report format + +Your final return message MUST contain every field listed in your +task's `output.report-fields-required`. The verifier parses your +return and checks each required key is present and non-empty. + +Use one section per field. Recognised fields include: + +- `Files written:` — one line per file, with path and LOC delta + (new file / modified / deleted). Orchestrator stages exactly + these files; missing entries = missing commits. +- `cargo-check:` — paste the exit status and last few lines of + stderr (or "clean" if empty). +- `cargo-test:` — paste the real `test result:` line with pass + count. Do not paraphrase. +- `loc-delta:` — per-file net lines added minus removed. +- `blockers:` — open issues you hit; empty list if none. +- `next:` — what a follow-up agent should take on, if anything. + +Example skeleton: + + Files written: + - _primitives/_rust/kei-forge/src/lib.rs (new, 120 LOC) + - _primitives/_rust/kei-forge/tests/render.rs (new, 45 LOC) + + cargo-check: clean + cargo-test: test result: ok. 44 passed; 0 failed; 0 ignored + loc-delta: +165 / -0 + +Keep each field on its own section. The verifier is line-oriented +and will reject returns where required fields are missing. diff --git a/_capabilities/output/severity-grade/capability.toml b/_capabilities/output/severity-grade/capability.toml new file mode 100644 index 0000000..2bd1d45 --- /dev/null +++ b/_capabilities/output/severity-grade/capability.toml @@ -0,0 +1,21 @@ +[capability] +name = "output::severity-grade" +category = "output" +version = "1.0" +description = "Require critic-role agents to tag every finding with a [HIGH|MEDIUM|LOW] severity grade." +rationale = "~/.claude/rules/debugging.md Security Review phase 2 output format. Lets the orchestrator triage fixes by priority; ungraded findings become unactionable backlog." + +[restricts] +tool-patterns = [] +tools-denied = [] + +[parameterized] +accepts = [] + +[text] +path = "text.md" + +[verify] +rust-module = "verifies::output_severity_grade" +run-mode = "worktree" +when = "on-return" diff --git a/_capabilities/output/severity-grade/text.md b/_capabilities/output/severity-grade/text.md new file mode 100644 index 0000000..fdde296 --- /dev/null +++ b/_capabilities/output/severity-grade/text.md @@ -0,0 +1,34 @@ +## Severity grade on findings + +Every finding in your return MUST carry a severity grade: +`[HIGH]`, `[MEDIUM]`, or `[LOW]`. Write the grade as the first +token of the finding's header. + +Grading rubric: +- **[HIGH]** — auth, crypto, memory safety, data loss, IP leak, + network protocol flaw, unsound FFI, secret in source, or any + issue that could compromise a production deploy. +- **[MEDIUM]** — input validation, error handling, resource + exhaustion, config drift, missing test coverage on a critical + path, performance regression with measurable impact. +- **[LOW]** — docs inaccuracy, formatting, non-idiomatic code, + comment drift, minor style, opportunistic refactor. + +Example: + + **[HIGH]** Unbounded allocation in request parser + - File: crates/api/src/parse.rs:47 + - Class: resource exhaustion + - Scenario: attacker sends 2GB body, process OOMs + - Fix: cap read at 16 MiB via `take(...)` + + **[LOW]** Typo in module docstring + - File: crates/api/src/lib.rs:3 + +The verifier parses your return, locates every `## ` section +containing the word "Finding" (case-insensitive) or matching the +format above, and rejects the return if any finding lacks a +`[HIGH|MEDIUM|LOW]` token. + +Empty finding lists are fine — state "No findings" and no grade +is required. diff --git a/_capabilities/policy/no-git-ops/capability.toml b/_capabilities/policy/no-git-ops/capability.toml new file mode 100644 index 0000000..4089790 --- /dev/null +++ b/_capabilities/policy/no-git-ops/capability.toml @@ -0,0 +1,26 @@ +[capability] +name = "policy::no-git-ops" +category = "policy" +version = "1.0" +description = "Forbid git, gh repo, and gh api /repos shell operations from the agent." +rationale = "RULE 0.13 (orchestrator-branch-first.md): orchestrator owns branch + commit + push; agents sandbox-deny Bash inside .claude/worktrees//. See ~/.claude/rules/orchestrator-branch-first.md." + +[restricts] +tool-patterns = [ + '^git( |$)', + '^gh repo', + '^gh api /?repos', +] +tools-denied = [] + +[parameterized] +accepts = [] + +[text] +path = "text.md" + +[gate] +rust-module = "gates::policy_no_git_ops" +event = "PreToolUse:Bash" +severity = "block" +bypass-env = "ORCHESTRATOR_META" diff --git a/_capabilities/policy/no-git-ops/text.md b/_capabilities/policy/no-git-ops/text.md new file mode 100644 index 0000000..f697616 --- /dev/null +++ b/_capabilities/policy/no-git-ops/text.md @@ -0,0 +1,20 @@ +## No git operations + +You MUST NOT invoke `git`, `gh repo`, `gh api /repos`, or any shell +command that modifies git state. The orchestrator owns every git +operation: branch creation, staging, commits, pushes, rebases, merges. + +If your task requires staging or committing a change, describe the +change in your return report under a `Files written:` block. Include +one line per file with its path and approximate LOC delta. The +orchestrator will stage exactly those files and author the commit. + +Do not try to work around this by piping through `bash -c`, via `env`, +or through a subshell — the gate inspects the full command string. + +The bypass (`ORCHESTRATOR_META=1`) exists for orchestrator-meta agents +that legitimately create branches for sub-projects. It is not +available to you. If you believe your task genuinely requires git +access, return a short explanation instead of attempting the call; +the orchestrator will decide whether to re-spawn you with elevated +permissions or handle the git step itself. diff --git a/_capabilities/quality/cargo-check-green/capability.toml b/_capabilities/quality/cargo-check-green/capability.toml new file mode 100644 index 0000000..419c84e --- /dev/null +++ b/_capabilities/quality/cargo-check-green/capability.toml @@ -0,0 +1,21 @@ +[capability] +name = "quality::cargo-check-green" +category = "quality" +version = "1.0" +description = "Require `cargo check --workspace` to pass on return, in worktree AND in simulated merge onto main." +rationale = "Worktree-green is not enough — E1 jsonschema regression showed agent-local green can break main integration. Simulated-merge pass catches that class of failure before merge." + +[restricts] +tool-patterns = [] +tools-denied = [] + +[parameterized] +accepts = ["cargo-check-crates"] + +[text] +path = "text.md" + +[verify] +rust-module = "verifies::quality_cargo_check_green" +run-mode = "both" +when = "on-return" diff --git a/_capabilities/quality/cargo-check-green/text.md b/_capabilities/quality/cargo-check-green/text.md new file mode 100644 index 0000000..49987dd --- /dev/null +++ b/_capabilities/quality/cargo-check-green/text.md @@ -0,0 +1,27 @@ +## Cargo check must be green + +On return, `cargo check --workspace` MUST pass cleanly. This is +enforced in two passes: + +1. **Worktree pass** — runs from inside your worktree. This is what + you saw while iterating. It must be green before you hand off. +2. **Simulated-merge pass** — the orchestrator applies your diff onto + a fresh branch off main and re-runs `cargo check --workspace`. + Your change must still compile once integrated. + +Both passes must succeed. Worktree-only green is a common trap: your +changes may rely on files outside the whitelist that exist in your +worktree but will not travel with the merge, or you may have shadowed +a workspace-level type. The simulated-merge pass catches that. + +Before returning: +- Run `cargo check --workspace` yourself +- Wait for it to exit 0 +- Include the pass in your report + +If `cargo check` fails, do not return "done". Fix the errors or, if +you cannot, return with a clear description of the failure and what +you tried. Do not claim green without evidence. + +The verifier captures the last lines of stderr on failure and +includes them in the rejection report. diff --git a/_capabilities/quality/constructor-pattern/capability.toml b/_capabilities/quality/constructor-pattern/capability.toml new file mode 100644 index 0000000..ae9dc6b --- /dev/null +++ b/_capabilities/quality/constructor-pattern/capability.toml @@ -0,0 +1,21 @@ +[capability] +name = "quality::constructor-pattern" +category = "quality" +version = "1.0" +description = "Enforce Constructor Pattern: file <= 200 LOC, function <= 30 LOC." +rationale = "~/.claude/rules/code-style.md (RULE ZERO). Keeps one file = one class = one responsibility; forces decomposition before complexity compounds." + +[restricts] +tool-patterns = [] +tools-denied = [] + +[parameterized] +accepts = [] + +[text] +path = "text.md" + +[verify] +rust-module = "verifies::quality_constructor_pattern" +run-mode = "worktree" +when = "on-return" diff --git a/_capabilities/quality/constructor-pattern/text.md b/_capabilities/quality/constructor-pattern/text.md new file mode 100644 index 0000000..870f661 --- /dev/null +++ b/_capabilities/quality/constructor-pattern/text.md @@ -0,0 +1,25 @@ +## Constructor Pattern — size limits + +You MUST keep every file you write or edit under 200 lines of code, +and every function under 30 lines of code. These are hard limits, +not guidelines. + +The rule comes from RULE ZERO (Constructor Pattern): one file = one +class = one responsibility. Files that breach 200 LOC should be +decomposed into sibling modules. Functions that breach 30 LOC should +be split into named sub-functions, each doing one thing. + +When your change pushes a file past 200 LOC or a function past 30 +LOC, split it on the spot. Do not commit with `TODO: refactor later`. + +Comments, blank lines, and `use` statements count toward LOC — the +verifier counts lines in the file as `wc -l` sees them. + +Exceptions: +- Auto-generated code (e.g. `include!(...)` expansions) is skipped. +- Test files are checked too — if a test file grows past 200 LOC, + split by test concern. + +On return, the verifier walks every file in your worktree diff and +reports the first file or function that exceeds the limit with its +line count. No partial credit. diff --git a/_capabilities/quality/tests-green/capability.toml b/_capabilities/quality/tests-green/capability.toml new file mode 100644 index 0000000..0f2ceff --- /dev/null +++ b/_capabilities/quality/tests-green/capability.toml @@ -0,0 +1,21 @@ +[capability] +name = "quality::tests-green" +category = "quality" +version = "1.0" +description = "Require `cargo test -p ` to pass, with a minimum test count per task, in worktree AND simulated merge." +rationale = "Self-reported green without a run is the most common substrate v1 failure. Test-count minimum prevents silently deleting tests to make the bar. Dual-pass catches integration regressions." + +[restricts] +tool-patterns = [] +tools-denied = [] + +[parameterized] +accepts = ["cargo-test-crates", "test-count-min"] + +[text] +path = "text.md" + +[verify] +rust-module = "verifies::quality_tests_green" +run-mode = "both" +when = "on-return" diff --git a/_capabilities/quality/tests-green/text.md b/_capabilities/quality/tests-green/text.md new file mode 100644 index 0000000..fe9656e --- /dev/null +++ b/_capabilities/quality/tests-green/text.md @@ -0,0 +1,26 @@ +## Tests must be green + +On return, `cargo test -p ` MUST pass for each crate listed in +your task's `verification.cargo-test-crates`. Passing is two checks: + +1. Exit code 0 +2. Test count greater than or equal to `verification.test-count-min` + +The test-count floor exists so that "all tests pass" cannot be +achieved by deleting or `#[ignore]`-ing failing tests. If the floor +says 44, the run must show `test result: ok. 44 passed` or more. + +Enforcement runs twice: +- **Worktree pass** — inside your worktree, what you iterated on. +- **Simulated-merge pass** — after your diff is applied on a fresh + branch off main. Tests must still pass once integrated. + +Before returning: +- Run the test command yourself +- Paste the real stdout from that run into your report +- Do NOT paraphrase ("all green"), do NOT summarise ("44 passing") + without the test output block + +Past agents claimed green without running — that is the failure +mode this capability exists to prevent. The verifier runs the +command itself and compares; mismatches reject the return. diff --git a/_capabilities/safety/no-dep-bump/capability.toml b/_capabilities/safety/no-dep-bump/capability.toml new file mode 100644 index 0000000..da730f4 --- /dev/null +++ b/_capabilities/safety/no-dep-bump/capability.toml @@ -0,0 +1,26 @@ +[capability] +name = "safety::no-dep-bump" +category = "safety" +version = "1.0" +description = "Block dependency additions/upgrades: deny Edit to Cargo.toml dep sections; verify Cargo.lock is unchanged on return." +rationale = "Supply-chain risk. A silent dep bump expands the attack surface and may trigger breaking-change cascades. Requires explicit task opt-in; orchestrator reviews Cargo.lock diff separately." + +[restricts] +tool-patterns = [] +tools-denied = [] + +[parameterized] +accepts = [] + +[text] +path = "text.md" + +[gate] +rust-module = "gates::safety_no_dep_bump" +event = "PreToolUse:Edit|Write" +severity = "block" + +[verify] +rust-module = "verifies::safety_no_dep_bump" +run-mode = "both" +when = "on-return" diff --git a/_capabilities/safety/no-dep-bump/text.md b/_capabilities/safety/no-dep-bump/text.md new file mode 100644 index 0000000..ae33b8f --- /dev/null +++ b/_capabilities/safety/no-dep-bump/text.md @@ -0,0 +1,27 @@ +## No dependency bumps + +You MUST NOT add, remove, or upgrade dependencies. Specifically: + +- Do NOT edit the `[dependencies]`, `[dev-dependencies]`, + `[build-dependencies]`, or `[workspace.dependencies]` sections of + any `Cargo.toml` +- Do NOT write or regenerate `Cargo.lock` +- Do NOT `cargo add`, `cargo remove`, or `cargo update` + +Each new or upgraded dependency expands the supply-chain attack +surface and can trigger breaking-change cascades across the +workspace. Dependency decisions require a separate review, a +dedicated task, and an orchestrator-approved lock diff. + +Editing other sections of `Cargo.toml` (e.g. `[package]`, +`[features]`, `[[bin]]`, `[lib]`, `[package.metadata.*]`) is allowed +if the file is in your whitelist and not in your denylist. The gate +inspects the specific region of the diff. + +If your task genuinely requires a new dependency, STOP. Describe the +crate, version, and reason in your return. The orchestrator will +decide whether to re-spawn you with an opt-in flag or handle the +dep-bump through a separate review. + +On return, the verifier diffs `Cargo.lock` against main; any change +rejects the return. diff --git a/_capabilities/scope/files-denylist/capability.toml b/_capabilities/scope/files-denylist/capability.toml new file mode 100644 index 0000000..531b7df --- /dev/null +++ b/_capabilities/scope/files-denylist/capability.toml @@ -0,0 +1,26 @@ +[capability] +name = "scope::files-denylist" +category = "scope" +version = "1.0" +description = "Block Edit/Write to paths matching a per-task denylist, even if otherwise whitelisted." +rationale = "Protects SSoT files (Cargo.toml / Cargo.lock / rules / settings.json / CI configs) that are easy to touch accidentally and hard to recover once committed. Denylist overrides whitelist." + +[restricts] +tool-patterns = [] +tools-denied = [] + +[parameterized] +accepts = ["files-denylist"] + +[text] +path = "text.md" + +[gate] +rust-module = "gates::scope_files_denylist" +event = "PreToolUse:Edit|Write" +severity = "block" + +[verify] +rust-module = "verifies::scope_files_denylist" +run-mode = "worktree" +when = "on-return" diff --git a/_capabilities/scope/files-denylist/text.md b/_capabilities/scope/files-denylist/text.md new file mode 100644 index 0000000..2f63e53 --- /dev/null +++ b/_capabilities/scope/files-denylist/text.md @@ -0,0 +1,24 @@ +## Scope — files denylist + +You MUST NOT Edit or Write any file whose path matches a glob in your +task's `scope.files-denylist` list. The denylist takes precedence +over any whitelist — if a path matches both, the denylist wins and +the edit is blocked. + +Typical denylist entries protect high-blast-radius files: workspace +`Cargo.toml`, `Cargo.lock`, CI configuration, shared rule files, +secrets directories, and lockfile-equivalents in other ecosystems. +Changing these demands a separate review and a different role. + +Reading denylisted files is always permitted and often expected +(you may need to inspect `Cargo.toml` to understand a crate's +dependencies, for example). The restriction applies only to mutating +tools. + +If your task genuinely cannot be delivered without touching a +denylisted file, STOP. Do not try to work around the restriction. +Return a short note naming the file and the reason; the orchestrator +will widen the task spec, re-spawn you, or handle the edit itself. + +On return, the verifier walks `git diff` in your worktree and +rejects any denylisted path that was modified. diff --git a/_capabilities/scope/files-whitelist/capability.toml b/_capabilities/scope/files-whitelist/capability.toml new file mode 100644 index 0000000..2de63ca --- /dev/null +++ b/_capabilities/scope/files-whitelist/capability.toml @@ -0,0 +1,26 @@ +[capability] +name = "scope::files-whitelist" +category = "scope" +version = "1.0" +description = "Restrict Edit/Write to paths matching a per-task whitelist of glob patterns." +rationale = "Scope violations surfaced only after merge in substrate v1 audit waves. Whitelist makes scope explicit at spawn time; gate blocks at PreToolUse, verify walks git diff on return to catch any bypass." + +[restricts] +tool-patterns = [] +tools-denied = [] + +[parameterized] +accepts = ["files-whitelist"] + +[text] +path = "text.md" + +[gate] +rust-module = "gates::scope_files_whitelist" +event = "PreToolUse:Edit|Write" +severity = "block" + +[verify] +rust-module = "verifies::scope_files_whitelist" +run-mode = "worktree" +when = "on-return" diff --git a/_capabilities/scope/files-whitelist/text.md b/_capabilities/scope/files-whitelist/text.md new file mode 100644 index 0000000..72fd049 --- /dev/null +++ b/_capabilities/scope/files-whitelist/text.md @@ -0,0 +1,24 @@ +## Scope — files whitelist + +You MUST only Edit or Write files whose path matches one of the glob +patterns in your task's `scope.files-whitelist` list. Any other path +is outside your scope. + +The whitelist is the full set of files you are authorised to touch. +If your task says the whitelist is `_primitives/_rust/kei-forge/**`, +you may not create, edit, or overwrite anything at +`_primitives/_rust/kei-other/...`, at `scripts/...`, or at the +workspace root. + +Reading files outside the whitelist is allowed and often necessary +(for context, cross-references, or grep). The restriction applies +only to mutating tools (Edit, Write). + +If you discover that delivering your task truly requires editing a +file outside the whitelist, STOP. Do not attempt the edit. Return a +short note describing the file and the reason. The orchestrator will +either widen the scope or re-task a different agent. + +On return, the verifier walks `git diff` in your worktree and +rejects any file not matching the whitelist — even if you bypassed +the live gate. diff --git a/_capabilities/tools/cargo-only-bash/capability.toml b/_capabilities/tools/cargo-only-bash/capability.toml new file mode 100644 index 0000000..6e817b1 --- /dev/null +++ b/_capabilities/tools/cargo-only-bash/capability.toml @@ -0,0 +1,29 @@ +[capability] +name = "tools::cargo-only-bash" +category = "tools" +version = "1.0" +description = "Restrict Bash to cargo and a handful of safe read/navigate/cleanup helpers." +rationale = "Bash is the highest-blast-radius tool. A narrow allowlist keeps agents on the cargo + inspect loop and prevents accidental `curl | sh`, `npm install`, or `sudo` escalation." + +[restricts] +tool-patterns = [ + '^cargo( |$)', + '^mkdir( |$)', + '^ls( |$)', + '^cat( |$)', + '^grep( |$)', + '^find( |$)', + '^rm -rf /tmp/', +] +tools-denied = [] + +[parameterized] +accepts = [] + +[text] +path = "text.md" + +[gate] +rust-module = "gates::tools_cargo_only_bash" +event = "PreToolUse:Bash" +severity = "block" diff --git a/_capabilities/tools/cargo-only-bash/text.md b/_capabilities/tools/cargo-only-bash/text.md new file mode 100644 index 0000000..04f6b3c --- /dev/null +++ b/_capabilities/tools/cargo-only-bash/text.md @@ -0,0 +1,28 @@ +## Bash — cargo-only allowlist + +You MAY use `Bash`, but only for commands that match this allowlist. +Anything else is blocked at the gate. + +Allowed command prefixes: +- `cargo ...` — build, check, test, fmt, clippy, run +- `mkdir ...` — create directories inside the worktree +- `ls ...` — directory listing +- `cat ...` — read a file +- `grep ...` — search +- `find ...` — locate files +- `rm -rf /tmp/...` — cleanup under `/tmp` only + +Everything else is denied, including (non-exhaustive): `git`, +`gh`, `curl`, `wget`, `npm`, `pip`, `python`, `node`, `bash -c`, +`sudo`, `sh`, `env VAR=...`, `docker`, `kubectl`, `ssh`, `scp`, +process-tree manipulation, and compound commands that chain an +allowed prefix with a denied one via `;`, `&&`, `||`, or pipes. + +The gate inspects the full command string. Do not try to hide a +denied call behind a heredoc, variable expansion, or `xargs`. If +you need a tool that is not on the allowlist, STOP and describe +the need in your return — the orchestrator will either widen the +role or handle the step directly. + +Prefer dedicated tools over Bash whenever possible: `Read`/`Write` +for files, `Glob`/`Grep` for search. diff --git a/_capabilities/tools/read-only/capability.toml b/_capabilities/tools/read-only/capability.toml new file mode 100644 index 0000000..b7a7004 --- /dev/null +++ b/_capabilities/tools/read-only/capability.toml @@ -0,0 +1,21 @@ +[capability] +name = "tools::read-only" +category = "tools" +version = "1.0" +description = "Deny Edit and Write tools entirely — agent may read but not mutate the filesystem." +rationale = "Read-only agents (research, critic, explorer) must never alter source. A denial at the tool level is simpler and more robust than per-path scope checks." + +[restricts] +tool-patterns = [] +tools-denied = ["Edit", "Write"] + +[parameterized] +accepts = [] + +[text] +path = "text.md" + +[gate] +rust-module = "gates::tools_read_only" +event = "PreToolUse:Edit|Write" +severity = "block" diff --git a/_capabilities/tools/read-only/text.md b/_capabilities/tools/read-only/text.md new file mode 100644 index 0000000..82c8993 --- /dev/null +++ b/_capabilities/tools/read-only/text.md @@ -0,0 +1,24 @@ +## Read-only agent + +You MUST NOT use the `Edit` or `Write` tools. Any attempt to call +them is blocked at the gate. + +You are a read-only role. Your job is to inspect, explain, analyse, +or review — never to mutate the filesystem. Use `Read`, `Glob`, +`Grep`, and (where permitted) `Bash` for read-only commands and +`WebFetch` to work through what is already on disk and on the web. + +If your task appears to require an edit, STOP. Do not try to work +around the tool denial (e.g. by shelling out `sed`/`awk` through +`Bash`, by creating a file via `cat > file <