feat(agent-substrate/phase-1): capability library — 11 declarative bundles
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) <noreply@anthropic.com>
This commit is contained in:
parent
b8b9c12913
commit
c0c3483f02
22 changed files with 549 additions and 0 deletions
21
_capabilities/output/report-format/capability.toml
Normal file
21
_capabilities/output/report-format/capability.toml
Normal file
|
|
@ -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"
|
||||
31
_capabilities/output/report-format/text.md
Normal file
31
_capabilities/output/report-format/text.md
Normal file
|
|
@ -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.
|
||||
21
_capabilities/output/severity-grade/capability.toml
Normal file
21
_capabilities/output/severity-grade/capability.toml
Normal file
|
|
@ -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"
|
||||
34
_capabilities/output/severity-grade/text.md
Normal file
34
_capabilities/output/severity-grade/text.md
Normal file
|
|
@ -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.
|
||||
26
_capabilities/policy/no-git-ops/capability.toml
Normal file
26
_capabilities/policy/no-git-ops/capability.toml
Normal file
|
|
@ -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/<agent>/. 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"
|
||||
20
_capabilities/policy/no-git-ops/text.md
Normal file
20
_capabilities/policy/no-git-ops/text.md
Normal file
|
|
@ -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.
|
||||
21
_capabilities/quality/cargo-check-green/capability.toml
Normal file
21
_capabilities/quality/cargo-check-green/capability.toml
Normal file
|
|
@ -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"
|
||||
27
_capabilities/quality/cargo-check-green/text.md
Normal file
27
_capabilities/quality/cargo-check-green/text.md
Normal file
|
|
@ -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.
|
||||
21
_capabilities/quality/constructor-pattern/capability.toml
Normal file
21
_capabilities/quality/constructor-pattern/capability.toml
Normal file
|
|
@ -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"
|
||||
25
_capabilities/quality/constructor-pattern/text.md
Normal file
25
_capabilities/quality/constructor-pattern/text.md
Normal file
|
|
@ -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.
|
||||
21
_capabilities/quality/tests-green/capability.toml
Normal file
21
_capabilities/quality/tests-green/capability.toml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
[capability]
|
||||
name = "quality::tests-green"
|
||||
category = "quality"
|
||||
version = "1.0"
|
||||
description = "Require `cargo test -p <crate>` 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"
|
||||
26
_capabilities/quality/tests-green/text.md
Normal file
26
_capabilities/quality/tests-green/text.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
## Tests must be green
|
||||
|
||||
On return, `cargo test -p <crate>` 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.
|
||||
26
_capabilities/safety/no-dep-bump/capability.toml
Normal file
26
_capabilities/safety/no-dep-bump/capability.toml
Normal file
|
|
@ -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"
|
||||
27
_capabilities/safety/no-dep-bump/text.md
Normal file
27
_capabilities/safety/no-dep-bump/text.md
Normal file
|
|
@ -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.
|
||||
26
_capabilities/scope/files-denylist/capability.toml
Normal file
26
_capabilities/scope/files-denylist/capability.toml
Normal file
|
|
@ -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"
|
||||
24
_capabilities/scope/files-denylist/text.md
Normal file
24
_capabilities/scope/files-denylist/text.md
Normal file
|
|
@ -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.
|
||||
26
_capabilities/scope/files-whitelist/capability.toml
Normal file
26
_capabilities/scope/files-whitelist/capability.toml
Normal file
|
|
@ -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"
|
||||
24
_capabilities/scope/files-whitelist/text.md
Normal file
24
_capabilities/scope/files-whitelist/text.md
Normal file
|
|
@ -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.
|
||||
29
_capabilities/tools/cargo-only-bash/capability.toml
Normal file
29
_capabilities/tools/cargo-only-bash/capability.toml
Normal file
|
|
@ -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"
|
||||
28
_capabilities/tools/cargo-only-bash/text.md
Normal file
28
_capabilities/tools/cargo-only-bash/text.md
Normal file
|
|
@ -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.
|
||||
21
_capabilities/tools/read-only/capability.toml
Normal file
21
_capabilities/tools/read-only/capability.toml
Normal file
|
|
@ -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"
|
||||
24
_capabilities/tools/read-only/text.md
Normal file
24
_capabilities/tools/read-only/text.md
Normal file
|
|
@ -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 <<EOF`, or by piping a
|
||||
heredoc into `tee`). The orchestrator considers such attempts a
|
||||
policy violation and will reject your return.
|
||||
|
||||
Return your findings as a structured report (see the
|
||||
`output::report-format` and, if applicable, `output::severity-grade`
|
||||
capabilities that accompany this role). Include every file path
|
||||
and line number you think the follow-up editor should touch — the
|
||||
orchestrator will route the actual edits to an `edit-local` or
|
||||
`edit-shared` agent.
|
||||
|
||||
Reading any file in the repository is permitted and encouraged.
|
||||
Loading…
Reference in a new issue