Skip to content

hookprobe: a run is not blamed for what the service itself wrote to its inputs - #53

Merged
itswl merged 1 commit into
mainfrom
a-run-is-not-blamed-for-the-services-own-writes
Nov 12, 2026
Merged

itswl merged 1 commit into
mainfrom
a-run-is-not-blamed-for-the-services-own-writes

Conversation

@itswl

@itswl itswl commented Nov 12, 2026 •

Copy link
Copy Markdown
Owner

The defect. The investigator compares a digest of its input surface (CLAUDE.md, .claude/, the prompt, configured files) before and after every run, and a run whose inputs changed is marked in the console and has its runbook skipped. But the service writes that surface itself while runs are out:

  • a memory line another run's report offered (HOOKPROBE_MEMORY_AUTO_APPLY is on by default);
  • a finished run's runbook;
  • an operator saving a page.

Every run in flight was blamed for those. Found re-shooting the docs pictures (#52): five runs, two at a time. The run still going when the first finished ended with run rewrote its own inputs: modified /data/CLAUDE.md.

The fix.

  • inputs.service_writes(*roots) records each file the service changed as a step from one digest to the next.
  • It wraps the memory writer behind both auto-applied and accepted suggestions, the post-run auto-distill and consolidation, and every console write door under /v1/memory, /v1/skills, /v1/agents and /v1/system-prompt (one middleware, so a door added later is covered).
  • The engine takes inputs.service_mark() before its fingerprint. changes(before, after, since=mark) lets a change off only when the service's steps since the mark, followed in order from where the run started, end where it ended.

Still caught:

  • a run that edits the memory before the service appends, since the service's step starts from the run's version;
  • a run that edits after, since its end state is no step's end;
  • a run that writes the same bytes as an old step from before its mark;
  • a step evicted from the kept window, which excuses nothing.

Verified

  • tests/test_inputs.py: the service's writes followed in order, run edits before and after them caught, an old step excusing nothing, and the suggestion queue's auto-apply end to end.
  • tests/test_contract.py: an operator's save through two doors while a run is out.
  • tests/test_engine_loop.py: the mark reaching the comparison inside a run. The first fails with the mark removed.
  • scripts/assert_guards_are_tested.py: a new mutation turns the excuse into "any change once a mark is given"; caught.
  • Read back on a live scratch stack: a real run (glm-5.3) with the memory page saved through the console mid-run ended with input_changes: [], and no warning in the log.
  • bash scripts/gate.sh: STACK GATE GREEN; hookprobe 953 passed, 1 skipped.

Decision note: .agents/notes/implemented/2026-11-12-a-run-is-not-blamed-for-the-services-own-writes.md. Not deployed.

…ts inputs

Found re-shooting the docs pictures: two runs overlapping on a demo stack,
the first one's report offered a plain fact, the service applied it to
CLAUDE.md (memory auto-apply is on by default), and the second run ended
flagged "rewrote its own inputs" — the tamper warning on its record and its
runbook skipped. An operator's save or a finished run's runbook while a run
is out did the same.

Every service write to the input surface (the memory writer, the post-run
runbook steps, and every console write door through one middleware) is now
recorded as digest steps; the engine takes a mark beside its fingerprint,
and a change is let off only when the service's steps since the mark lead
from where the run started to where it ended. A run that edits the memory
before or after the service does is still caught; an old step excuses
nothing. Read back on a live stack: a real run with the memory page saved
mid-run ends with no input changes. Mutation-checked.
@itswl
itswl merged commit 4502a42 into main Nov 12, 2026
8 checks passed
@itswl
itswl deleted the a-run-is-not-blamed-for-the-services-own-writes branch November 12, 2026 09:40
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.

1 participant