BOHICA-LABS / BOHICA-LABS/vsdd-factory
bug(dispatcher): PostToolUse block (block_intent=true exit_code=2) does not roll back the write, so a blocked edit reports as failed but has landed
- Dominant language
- Rust
- Stars
- 2
- Forks
- 1
- Avg merge
- 6h 43m
- Merged PRs (30d)
- 29
Description
## Summary
When a PostToolUse hook blocks, the dispatcher emits a blocking result — `block_intent=true`, `exit_code=2` — and the agent-visible outcome is an error. **But the write has already been applied to disk.** PostToolUse runs, by definition, after the tool succeeded. There is nothing to roll back and the dispatcher does not attempt to.
The result is that a blocked edit and a rejected edit are indistinguishable to the caller, while being opposite on disk.
## Why this is a correctness problem, not a UX wart
The agent's next action depends on which happened:
- If the edit was **rejected**, the correct response is to retry, possibly with different content.
- If the edit **landed**, retrying re-applies it — duplicating an inserted row, double-bumping a version, appending a changelog entry twice.
Because the signal says "blocked," the naive response is to retry, which is the wrong one. I only avoided it by re-reading the file after every blocked write, which is a workaround an operator has to already know about.
The failure is quiet in the other direction too. A tool result that reads as failure invites the caller to treat the artifact as unchanged and move on — leaving a landed, unreviewed, possibly half-formed edit in a spec artifact with no record that anything happened.
## Observed
On **every** edit to two large `.factory/` artifacts across a full working session (a 616 KB decision note and a ~515 KB story). I am deliberately not giving a count — I did not tally the occurrences and will not publish an uncounted figure — but it was the invariable outcome for those two files, not an intermittent one.
Shape observed each time:
- dispatcher reports `block_intent=true exit_code=2`
- the tool result surfaces as an error to the caller
- reading the file back immediately afterward shows the edit **present**
- the content subsequently committed cleanly, confirming it was on disk and not a transient view
The blocking hooks in this project's case were the fuel-exhaustion arms of `validate-factory-path-root`, `validate-input-hash`, and `validate-template-compliance` (see the companion fuel-cap issue), but the rollback gap is independent of *why* a PostToolUse hook blocks.
## The interaction that makes it worse
Combined with fuel exhaustion, the two defects compound: the hook that blocks is a hook that did not actually complete its validation. So the caller receives a hard block, derived from a validator that never ran, against a write that already landed. Three states, all wrong, all consistent-looking.
## Asks
Pick a lane and make the semantics honest:
1. **Preferred** — distinguish the two signals. A PostToolUse hook cannot reject; it can only report. Emit a warning/advisory result (write landed, validation objected) that is textually distinct from a PreToolUse rejection (write did not land). Reserve `block_intent` for hooks that run before the tool.
2. If a PostToolUse block must stay blocking, state the disposition in the message itself — e.g. `BLOCKED (write already applied; do not retry)` — so a caller can act correctly without out-of-band knowledge.
3. Failing both, document it prominently. Right now the natural reading of a blocking exit is that the operation did not happen.
## Environment
Plugin `1.0.0-rc.24`, `hooks-registry.toml` `schema_version = 2`, `factory-dispatcher` native binary running hooks as wasmtime 46.0.2 WASM modules; the hooks in question route through `legacy-bash-adapter.wasm`.
Contributor guide
Research direction
Look at the dispatcher code handling PostToolUse hooks, likely in a module like `dispatcher.rs` or `hooks.rs`. Find where the blocking result with `block_intent=true` and `exit_code=2` is emitted. Examine the tool execution flow to see where the write is applied versus when the hook runs. The fix involves changing the result signaling or adding rollback logic. Testing will require setting up a hook environment to simulate the blocking case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust, wasm
- Domain
- backend, devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100