Skip to content

0.46.0 — Holmes reviews work that never reached the board

Choose a tag to compare

@mikebronner mikebronner released this 14 Sep 19:15
f4001c6

⚡ After you update

Restart any session that should be governed. A running session holds the plugin it loaded at start. An open conversation keeps the old hooks until it restarts.

No setup re-run is needed. This release changes agent prompts, skills, and hooks. All three are read live. 0.44.0's requirement to run /workbench-dev-team:setup still stands if you have not run it.

✨ Holmes reviews work that never reached the board

Holmes accepted Item ID: <n> and nothing else. Every review therefore needed a board item behind it.

Watson has had no such coupling. His Direct mode takes a prose brief. It runs on any local repo.

That asymmetry had a cost. Watson's Direct mode hands work back as an uncommitted working tree. That tree had no review path. You reviewed it yourself, or it shipped unreviewed.

Local mode is now Holmes's default. Hand him a five-slot brief. He reviews the uncommitted working tree in its Workdir:. Index mode is entered only on an explicit item-ID token.

Local mode Index mode
Entered by a prose brief (the default) Item ID: <n>
Reviews the uncommitted working tree the PR on the board
Rubric the brief's Goal: and Done when: the issue's acceptance criteria
Tests runs the repo's own suite reads CI status
Verdict prose, to the dispatching session an App-signed review
Writes one vault note the board and the PR

Ambiguous prose resolves to Local mode. It never resolves to Index mode. The two mistakes cost different amounts. A misread brief wastes a report. A guessed id posts a signed verdict onto somebody else's PR.

The rubric binds exactly as acceptance criteria bind. Goal: and Done when: go verbatim into the lens prompts. Holmes never amends them. A rubric that is itself wrong comes back as a dispute with three options.

Local mode makes no The Index call and no GitHub write. There is no board item to move. A local review carries no consent to post under your identity.

The lens fan-out, the adversarial verification, the memory pass, and the finding-routing matrix are unchanged.

🔒 A guard, because the prose did not hold

Local mode reads your live working directory. There the uncommitted change is the only copy of the work.

The rule protecting that tree started as prose at five sites. It sat verbatim in every sub-agent prompt Local mode dispatches.

On the mode's first real exercise, a lens sub-agent ran chmod against that directory. It changed a script from 755 to 644. It disclosed the breach itself. The prohibition had reached it and it acted anyway.

Prose in an agent prompt is advisory. A second PreToolUse hook now enforces the rule. It sits beside the commit gate rather than inside it.

Event Behaviour
A Holmes dispatch carrying a prose brief arms a record for that session
A Bash call from a sub-agent of an armed session denied when it mutates a tree
The dispatch returns releases the hold

Nothing asks the reviewed agent to arm anything. The hook reads the Agent tool's own subagent type and prompt. It applies Holmes's own mode detection. The prose that drifts is not in the loop.

The signal is the harness-supplied session_id plus a non-empty agent_id. Your own window keeps working. A concurrent session is untouched. A host-wide marker would be the watson.lock leak with the sign reversed.

What it refuses is a class, not a roster. Git's reading verbs are enumerated. Every other git verb is refused. That is how it sees git restore, which the old prose list never named. File metadata joins it, since that is what the breach used.

Reads and the repository's own suite stay legal. That is the binding constraint. A guard that stops either makes the mode useless.

What it costs you

While a review is armed, every sub-agent of that session is read-only. That includes ones unrelated to the review. It is bounded by the release and a two-hour expiry.

A review that was never armed fails open silently. The hook is a backstop. The prose stays.

🔍 The approval prompt now names the commit

The commit-approval gate denied a commit. It said nothing about the description on the approval command. Each session therefore worded that line itself. One wrote "Request human approval for this commit". That names the action and never the commit.

A prompt carrying no decision content gets cleared unread. That habit reaches the other rules on the same ask list. git reset --hard and gh pr merge are among them.

The denial now dictates the line rather than describing an intent. It names the parameter, and gives the literal shape Commit: <first line of the commit message>. It stops at the subject. Nothing about what the gate approves has changed.

🧪 Tests

Suite Cases
hooks/scripts/test-local-review-guard.sh 79, new
hooks/scripts/test-commit-approval-gate.sh 84, up from 83
agents/lint-holmes-local-mode.sh 8 checks across four files, new

The lint keeps no copy of the forbidden-verb rule. It feeds the reference's fenced blocks to the guard's own classifier. The documentation and the enforcement therefore cannot drift apart.