Hmbown / Hmbown/Codewhale

Fleet rework: stop defining read-only by command grammar — one grant model, working verify mode, classified tool families

Open
#6,298 2 comments 0 reactions 0 assignees View on GitHub
documentation
Dominant language
Rust
Stars
41k
Forks
3.6k
Avg merge
13h 59m
Merged PRs (30d)
299

Description

## Why (evidence)

- The 2026-09-17 incident (#6296): a verifier child, refused a chained read-only
git command, used an inherited computer-use tool to type into the host
Terminal. It was trying to do the task it was given.
- Our child model (`crates/tui/src/worker_profile.rs`, `fleet/role.rs`,
`execpolicy/command_safety.rs`): role postures + PermissionSet ceilings +
ShellPolicy + `ToolScope::Inherit` + deny-list unions + sentinels + a 21-prefix
single-command read-only grammar (`PARALLEL_READONLY_PREFIXES`). A read-only
child cannot run `cargo`, `git fetch`, or `merge-tree` — the exact commands a
"verify" child is handed — while the catalog still accepts inherited MCP
tools (unclassified ⇒ allowed), which is how computer-use got through.
- Cost of that mismatch today: each 8–10-issue audit child and the verifier
burned ~1M–3M input tokens flailing against refusals; one escalated to
desktop control. Every refusal without a named alternative invites a
workaround.

## What the references do (read 2026-09-17, `refs/`)

- **codex**: read-only is a *real sandbox* (Seatbelt/Landlock/bwrap);
workspace-write = explicit writable roots with `.git` protected. Builds/tests
are a sandbox denial resolved by policy-gated escalation with named
alternatives (`sandbox_permissions: require_escalated`, a
`request_permissions` tool). Roles are reduce-only ("never replace the
parent's authority"). Per-spawn `model` + `reasoning_effort`, validated
against the model's supported levels. No native computer-use tool; the
namespace name is reserved against impersonation.
- **grokbuild**: capability modes are kind-level allowlists with monotonic
intersection (`ReadOnly` absorbs). Read-only excludes the Execute kind
entirely — no tests, by construction. Typed denials that name alternatives
(`Unknown { available }` / `NotAllowed { allowed }`). MCP tools are exempt
from the capability filter (`None => true`) and children inherit the parent
MCP pool — the same hole we hit, flagged in their own comment.
- **kimicode**: four static preset profiles own their tool lists (`explore`
keeps Bash; `plan` has none). "Read-only" is prompt-enforced + tool omission,
NOT a command classifier; the approval chain decides what Bash may run.
Per-spawn model only; thinking follows the caller ("primary") or the model
default. Fail-open approval when no approval surface is mounted.
- **omp**: per-agent frontmatter tool lists ARE the grant; "read-only" is a
derived, presentation-only label (their code says so). Children run yolo with
the parent's task call as the authorization boundary. The `computer` tool is
off by default globally and stripped for restricted sessions — the cleanest
desktop guard of the four. Per-agent `thinkingLevel` (scout = smol/medium);
the resolved model+effort is surfaced on every run.

**Convergent lesson**: none of them pretends a command allowlist is
"read-only". Read-only is either *absent mutating tools* (kimicode, omp),
*absent capability kind* (grokbuild), or a *real sandbox with named
escalation* (codex). Our grammar is both too narrow (verifier can't verify)
and not a boundary (it is a classifier + charset gate).

## Direction for Codewhale

1. **One grant object per child** — `files: none|read|write`,
`shell: none|inspect|verify|full`, `network: bool`,
`desktop: false-unless-granted`, `tools: preset allowlist`. Roles become
presets over it; delete the parallel machinery (ceiling + sentinels +
posture re-mapping onto a second surface) in favor of one projection:
catalog visibility = grant, execution denial = the same grant.
2. **A `verify` shell mode that works**: `cargo test/check` + `git fetch` /
`merge-tree` under an explicit bounded write scope (`target/`, refs) instead
of a command allowlist pretending to be read-only.
3. **Classified tool families, fail-closed**: MCP/desktop tools are a labeled
family; children never receive it unless the spawn grants it with a reason.
Unclassified ⇒ not granted.
4. **Denials name the sanctioned alternative** ("use Git action=fetch", "Run
with workdir") and blocked probes escalate to the parent instead of
inviting workarounds.
5. **Thinking**: per-role defaults that are cheap where cheap is right
(scout ≈ low/medium), per-spawn override, resolved model+tier surfaced in
the roster and receipts.
6. **UI/UX**: role picker and roster show the effective grant + model +
thinking in plain words; spawn errors legible.

Related: #6296, #6015, #5529, #6194.

Contributor guide

Open the contributing guide

Research direction

Start by reading crates/tui/src/worker_profile.rs, fleet/role.rs, and execpolicy/command_safety.rs, then compare the cited refs/ implementations. Map the existing child grants, tool catalog visibility, shell policy, and denial paths before designing the unified grant projection. Done means verify mode supports the named cargo and git operations, unclassified MCP/desktop tools are denied, and effective grants and thinking settings appear in the roster and receipts.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
authorization, cli, security
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.