voyage/agents
Kjell Tore Guttormsen 52b87978cb
fix(verification): the runner refuses writes a brief may not perform
/trekreview now runs the commands a BRIEF declares, and a brief is an artifact
that can arrive from outside the repo. Measured 2026-09-18 on 6cafb4c: the
executor denylist stopped a download piped into a shell, but the remote-writing
git subcommand and a recursive delete of a path both RAN. The denylist screens
catastrophe (root deletion, fork bombs, mkfs); it was never meant to screen an
artifact under review.

A second screen, in the runner and ahead of the denylist, refuses four classes:

- a remote-writing git subcommand. The subcommand is found by walking git's own
  options (`-C`, `-c`, `--git-dir`, ... take a value), so `git status` and
  `git log` still run and `git -C sub push` does not.
- a recursive delete: any `rm` carrying `-r`/`-rf`/`--recursive`. A plain
  `rm build/artifact.txt` still runs.
- a download piped straight into a shell (also caught by the denylist; pinned
  here so the runner does not depend on another file for it).
- a write outside the working tree. `/dev/null`-class devices are fine, and so
  is anything under the working tree; `~/...`, an absolute path elsewhere, and
  a target carrying an unexpanded `$VAR` are refused - the runner cannot know
  where a variable points, and guessing is how a screen stops screening.

A refusal is its own outcome, REFUSED_BY_POLICY: the command never reaches a
shell, and `summary.ok` is false in both plan and brief mode. For the reviewer,
REFUSED is like NOT RUN - the absence of a measurement, never on its own a
finding - and the rubric and the evidence block both say so.

Chosen deliberately, and it is stricter than today's habit: writing scratch to
/tmp is refused too. The repo's own example plan does `> /tmp/out`. Verification
output belongs in the working tree; exempting the whole system temp dir would
have made the rule unstatable, since a working tree created under /tmp then
contains its own escape hatch.

NOT covered, stated rather than implied:
- other writing git subcommands (tag, remote, config, gc) - only push is listed
- writes through a wrapper: `sh -c '...'`, `xargs`, `find -exec`, a Makefile
  target, a script the criterion invokes. The screen reads the command it is
  given, not what that command goes on to do.
- `>` inside a quoted string reads as a redirect, so a criterion echoing a
  literal `>` is refused. Fail-closed, on purpose.
- the whole surface still runs with the invoking process's permissions; this is
  a refusal list, not a sandbox.

The denylist-layer test now uses a stand-in command with a screen double: the
refusal list catches a recursive delete first, so naming one there would have
stopped exercising the denylist layer at all.

Red first: the 6 new tests failed before this change (`refuseCommand` did not
exist), and the fixture brief's four writes ran.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 02:14:01 +02:00
..
architecture-mapper.md refactor(agents): relocate example blocks to body (retrieval agents) 2026-06-29 10:16:48 +02:00
brief-conformance-reviewer.md fix(verification): the runner refuses writes a brief may not perform 2026-09-18 02:14:01 +02:00
brief-reviewer.md refactor(agents): relocate example blocks to body (reviewer/planning agents) 2026-06-29 10:19:24 +02:00
code-correctness-reviewer.md chore(voyage): pin all sub-agents to Opus permanently (operator request) 2026-05-13 20:20:08 +02:00
community-researcher.md refactor(agents): relocate example blocks to body (researchers + gemini-bridge) 2026-06-29 10:14:35 +02:00
contrarian-researcher.md refactor(agents): relocate example blocks to body (researchers + gemini-bridge) 2026-06-29 10:14:35 +02:00
convention-scanner.md refactor(agents): relocate example blocks to body (retrieval agents) 2026-06-29 10:16:48 +02:00
dependency-tracer.md refactor(agents): relocate example blocks to body (retrieval agents) 2026-06-29 10:16:48 +02:00
docs-researcher.md refactor(agents): relocate example blocks to body (researchers + gemini-bridge) 2026-06-29 10:14:35 +02:00
git-historian.md refactor(agents): relocate example blocks to body (retrieval agents) 2026-06-29 10:16:48 +02:00
plan-critic.md refactor(agents): relocate example blocks to body (reviewer/planning agents) 2026-06-29 10:19:24 +02:00
planning-orchestrator.md fix(trekplan): drop TaskCreate/TaskUpdate from the tool lists - measured dead weight 2026-09-17 15:34:50 +02:00
research-orchestrator.md release(v5.10.1): drop gemini-bridge from the pipeline; correct the T1 §6 PoC status 2026-09-03 20:29:39 +02:00
research-scout.md refactor(agents): relocate example blocks to body (reviewer/planning agents) 2026-06-29 10:19:24 +02:00
review-coordinator.md fix(review): an anonymous invalid payload is unattributable, not a reviewer named "unnamed reviewer" 2026-09-01 22:54:31 +02:00
review-orchestrator.md fix(trekplan): drop TaskCreate/TaskUpdate from the tool lists - measured dead weight 2026-09-17 15:34:50 +02:00
risk-assessor.md refactor(agents): relocate example blocks to body (reviewer/planning agents) 2026-06-29 10:19:24 +02:00
scope-guardian.md refactor(agents): relocate example blocks to body (reviewer/planning agents) 2026-06-29 10:19:24 +02:00
security-researcher.md refactor(agents): relocate example blocks to body (researchers + gemini-bridge) 2026-06-29 10:14:35 +02:00
session-decomposer.md refactor(agents): relocate example blocks to body (reviewer/planning agents) 2026-06-29 10:19:24 +02:00
synthesis-agent.md chore(voyage): release v5.6.1 — one-line descriptions for reference/dormant agents (~700 tok trim) 2026-06-24 11:52:59 +02:00
task-finder.md refactor(agents): relocate example blocks to body (retrieval agents) 2026-06-29 10:16:48 +02:00
test-strategist.md refactor(agents): relocate example blocks to body (retrieval agents) 2026-06-29 10:16:48 +02:00