Skip to content

Give the board agent real write authority, behind a flag, governed by the gates - #19

Merged
digitalmasterykit-rgb merged 1 commit into
mainfrom
feat/board-agent-write-authority
Aug 26, 2026
Merged

digitalmasterykit-rgb merged 1 commit into
mainfrom
feat/board-agent-write-authority

Conversation

@digitalmasterykit-rgb

Copy link
Copy Markdown
Contributor

Why

PRD §5 says the board agent holds "authority to write". Production means that literally — its board
agent runs a create command and a guard layer decides whether the command lands.

This repo shipped a board agent that could not write at all, and AGENTS.md justified that by
claiming production's board agent only proposed and never performed writes. That claim was
wrong
, and it was the sentence holding the divergence in place. Both are fixed here.

What

BOARD_AGENT_WRITES, off by default. On, the board agent gets write tools behind a new
governedTracker, which rebuilds every write it originates into a CategorizationItem and re-runs
the same applyGates the pipeline's own answer faced. A write the gates refuse becomes a hold,
not a card. Off, none of that code is in the process and Pass 2c writes exactly as before.

Off by default on purpose: nothing in this layer has governed a real board the way the pipeline has,
and defaulting a model into the write path of a repo people clone and point at their own tracker is
not a claim this repo has earned.

The security claim, stated honestly

The injection guarantee genuinely differs per mode, so SECURITY.md now states both instead of
stating the stronger one twice:

Mode Guarantee
default A successful injection cannot author a write — no write tool exists to reach
flag on A successful injection cannot author a write the gates would not already have approved

The second is smaller. Saying so plainly is the point.

Evidence is stricter here than on the pipeline path

The evidence gate wants comment history cited. On the pipeline path that citation is the model's own
claim, parsed out of its prose. Here it is a fact: readComments reports whether the agent actually
called get_task_comments on that card. A model that says it checked but did not gets held.

Two bugs found while building it

  • A subtask create hardcoded tier2Cited: false, so no agent-originated subtask could ever cite the
    parent it had just read — every one was unwritable. Would have read as "the gate is strict" rather
    than as the bug it was.
  • The write loop swallowed a thrown error, so a missing cassette produced a tidy 0 created and left
    Pass 2d to infer the problem from four mismatches. That is exactly the quiet-wrong-number failure
    the cassette client refuses to ship. A loop that wrote nothing now rethrows; one that wrote
    something reports what actually landed.

Verification

  • npx tsc --noEmit clean
  • npm run lint clean
  • npm test — 1018 passing (1000 before, 18 new)
  • All five demo modes replay with zero cassette drift
  • npm run eval clean
  • New guard mutation-checked per CONTRIBUTING.md:44-49 — removing applyGates from the
    governed path fails 7 tests

The tool list is part of the prompt fingerprint, so zero drift is the proof the flag is not leaking
into the default path
— that was the one failure mode this design existed to prevent.

Not included

No cassettes for the new mode. Recording them needs live keys against both providers, so the mode is
proven by unit tests with scripted models and npm run demo -- --agents --board-writes fails loudly
on a missing cassette, which is the correct behaviour rather than a silent green run.

… the gates

PRD §5 says the board agent holds "authority to write". Production means that
literally — its board agent runs a create command and a guard layer decides
whether the command lands. This repo shipped a board agent that could not write
at all, and AGENTS.md justified that by claiming production's agent only
proposed. That claim was wrong, and it was the sentence holding the divergence
in place.

BOARD_AGENT_WRITES (off by default) is the production shape. On, the board agent
gets write tools behind governedTracker, which rebuilds every write it originates
into a manifest item and re-runs the full deterministic gate set over it — a
write the gates refuse becomes a hold, not a card. Off, none of that code is in
the process and Pass 2c writes as before.

Off by default on purpose. Nothing in this layer has governed a real board the
way the pipeline has, and defaulting a model into the write path of a repo people
clone and point at their own tracker is not a claim this repo has earned.

The injection guarantee differs per mode and SECURITY.md now states both rather
than stating the stronger one twice: "cannot author a write" by default, and
"cannot author a write the gates would not already have approved" with the flag
on. The second is smaller. Saying so is the point.

Evidence is stricter here than on the pipeline path. The gate wants comment
history cited; on the pipeline that citation is the model's own claim parsed out
of prose, and here it is a fact — readComments reports whether the agent actually
called get_task_comments on that card.

Two bugs found while building it, both kept fixed:
- a subtask create hardcoded tier2Cited: false, so no agent-originated subtask
  could ever cite the parent it had just read, and every one was unwritable
- the write loop swallowed a thrown error, so a missing cassette produced a tidy
  "0 created" and left Pass 2d to infer the problem from four mismatches. A loop
  that wrote nothing now rethrows; one that wrote something reports what landed

Default paths are byte-identical: 1018 tests pass, and all five demo modes replay
with zero cassette drift. The tool list is part of the prompt fingerprint, so that
zero is what proves the flag is not leaking into the default path.
@digitalmasterykit-rgb
digitalmasterykit-rgb merged commit 42eb737 into main Aug 26, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants