Add 'unwritten ignore': bounded mute for persistently false rules - #16
Merged
Conversation
An ignore suppresses the directed pair (trigger -> hole) until the trigger has changed --for more times (default 30) — the commits that either erode the false rule or prove the coupling is real again. Permanent ignores are deliberately not supported: unbounded mutes go stale and eventually hide real omissions. - Muted holes show as suppressed with the remaining budget (file-level and member-level, CLI and MCP); check --strict overrides them. - Stored in machine-managed .unwritten/ignores.json, separate from the hand-edited config.json; expired entries are pruned on rewrite; a corrupt file fails toward alerting. - ignore --list / --remove manage entries. - Deliberately CLI-only: no MCP counterpart, so agents cannot mute their own warnings. 137 tests passing (6 new). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… alias Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ot decide The check_holes/explain_rule descriptions and the Stop-hook feedback now instruct agents that a rule judged persistently false must be reported to the user with a pointer to 'unwritten ignore' — the agent cannot mute it, and must not dismiss it silently. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A failing check now ends with a decision guide listing the three choices with ready-to-run commands, ignore lines pre-filled with the actual trigger/hole paths. check_holes gains a per-hole ifFalsePattern field carrying the exact command to relay to the user, so the agent never has to construct it (and is reminded only the user may run it). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…with it All three options are now equally concrete: option 1 lists each forgotten file with its trigger, option 2 carries the bypass command, option 3 the pre-filled mute command. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Option 2 now states that --no-verify skips all hooks and every hole at once, and a footer (shown only with multiple holes) explains the combining order: fix/mute the holes that need it, retry the commit, and bypass only if legitimate one-time holes remain. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the last gap in the false-positive story. The layers now are:
git commit --no-verify(one commit)unwritten ignore <trigger> <hole> --for <n>(this PR — persistent false pairing)Design: bounded by construction
Permanent ignores rot — one day the pairing is real again and the mute silently hides an omission. So there is no permanent variant. An ignore expires after the trigger has changed
nmore times (default 30). Those are exactly the commits that matter: each trigger-alone commit erodes the rule's confidence (it often dies naturally before the mute expires), while renewed co-changes mean the rule deserves to fire again.suppressedwith the remaining budget — never silently dropped — consistent with the content-aware layers;check --strictoverrides..unwritten/ignores.json(the tool can rewrite it freely;config.jsonstays hand-edited). Expired entries prune automatically; a corrupt file fails toward alerting, never toward silence.ignore --listshows remaining budgets and notes;ignore --removerestores the alert.Testing
137/137 passing — 6 new tests: suppress-not-drop, expiry after N trigger changes,
--strictoverride, list/remove round-trip, unknown-remove error, corrupt-file fail-open.🤖 Generated with Claude Code