Skip to content

feat: machine-written evidence — Evidence Logger hook, citation corroboration, opt-in acceptance replay - #2

Open
Tespera wants to merge 2 commits into
cozytab:mainfrom
Tespera:pr-b-machine-evidence
Open

feat: machine-written evidence — Evidence Logger hook, citation corroboration, opt-in acceptance replay#2
Tespera wants to merge 2 commits into
cozytab:mainfrom
Tespera:pr-b-machine-evidence

Conversation

@Tespera

@Tespera Tespera commented Jul 16, 2026

Copy link
Copy Markdown

Stacked on #1 (needs its installer fix to register a second PostToolUse/Bash hook). Review the last commit only; merging #1 first shrinks this diff to just the feature.

The gap this closes

fable-mode's strongest idea is evidence, not adjectives — but today the evidence note is self-reported. The Close Guard checks that -- evidence: exists and is substantive; it cannot check that it is true. A model under pressure to close a card can write -- evidence: pytest 21/21 without ever running pytest, and every gate passes. The one thing the enforcement layer most needs to know — did the acceptance actually run? — is exactly what it can't currently see. In other words: the gates verify form; this PR starts verifying substance.

What this adds

1. Evidence Logger (new PostToolUse/Bash hook, passive). Appends every command's real outcome {ts, cmd, exit, tail} to .fable/evidence.jsonl. The key property: this file is written by the hook from actual tool results, never typed by the model. Fabricating evidence stops being a plausible sentence and becomes a visible, auditable act (hand-editing a log). Rotates at 512KB. Records even while PAUSED — pausing enforcement shouldn't create evidence gaps.

2. Citation corroboration (Close Guard). A - [x] card that cites a command in its evidence note must have a successful run of that command in the log. Never ran → blocked. Ran but exited non-zero → blocked. This turns the honor system into a receipts system for the common case (command-based acceptance), while staying fully backward compatible:

  • prose-only evidence (screenshot paths etc.) keeps the existing substantive-string rule;
  • projects with no log file (pre-logger sessions) are untouched — fail-open, per the safety contract;
  • backticks before the evidence marker (the card's acceptance: declaration) are not treated as citations.

3. Acceptance replay (opt-in via a REPLAY: on ledger line). Before the round may end, re-run each cited acceptance. Rationale: 'passed once' is not 'still passes' — card N's fix routinely breaks card N-2's acceptance, and milestone-only checks miss it. Budgeted (30s/command via FABLE_REPLAY_TIMEOUT, 120s total) so a heavy suite can never hang the Stop hook; off by default because replay costs real wall-clock time and should be a deliberate choice.

Why it fits this project

It's the same philosophy the README already states — discipline enforced by code, not willpower — applied to the layer that was still willpower. All design invariants preserved: fail-open everywhere, .fable/ opt-in, loop-safe via stop_hook_active, stdlib only, POSIX.

Tests

tests/test_evidence.py — 14 cases: recorder writes real cmd/exit, inert without opt-in, records while paused; corroboration allows cited+ran, blocks never-ran and ran-but-failed, fail-open without a log, prose evidence unchecked, acceptance-part not treated as citation, PAUSED regression; replay pass/fail/off-by-default. test_install.py updated for the fifth hook. All existing suites green (47+27 guards/inject).

🤖 Generated with Claude Code

Tespera and others added 2 commits July 16, 2026 09:34
…ling groups on a shared event

With two hooks registered on the same event (e.g. two PostToolUse/Bash
entries), processing the second spec re-pruned the event list and silently
dropped the group just added for the first. Latent with a single spec per
event; bites as soon as a second one exists.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…boration, opt-in acceptance replay

Closes the honor-system gap in evidence-on-close. Today the Close Guard
checks that an '-- evidence:' note EXISTS and is substantive — but the note
is self-reported: a model can write 'evidence: pytest 21/21' without ever
running pytest, and the guard passes. The most-needed check (did the
acceptance actually run?) was the one thing the hooks couldn't see.

Three pieces:

1. Evidence Logger (new PostToolUse/Bash hook, passive): appends every
   command's real outcome {cmd, exit, output tail} to .fable/evidence.jsonl.
   Evidence is machine-written from tool results, not typed by the model.
   Fabricating now requires a visible act (hand-editing the log) instead of
   a plausible sentence. Rotates at 512KB; records even while PAUSED.
2. Citation corroboration (Close Guard): a checked card that cites a
   `command` in its evidence note must have a successful run of that
   command in the log — never ran, or never exited 0, blocks the stop.
   Prose-only evidence (screenshots etc.) keeps the existing substantive-
   string rule; projects without a log (pre-logger) are untouched (fail-open).
3. Acceptance replay (opt-in, 'REPLAY: on' ledger line): before the round
   may end, re-run each cited acceptance — 'passed once' is not 'still
   passes'; a later card silently breaking an earlier one is caught at the
   door. Budgeted (30s/cmd via FABLE_REPLAY_TIMEOUT, 120s total) so a heavy
   suite can't hang the stop; off by default because replay costs real time.

All existing invariants preserved: fail-open everywhere, .fable/ opt-in,
loop-safe, stdlib only. Tests: tests/test_evidence.py (14 cases) +
test_install updated for the fifth hook; existing suites green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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