feat(decision-capture): log the surviving silent suppression (bug 011 option 4)#120
Conversation
… option 4) #117 removed containment-based dropping on both recorded-corpus paths, but deliberately kept `dedup_scan_against_stubs` dropping — leaving one silent suppression the bug record's own residual 1 documents (a Tier-3 agent reversal of an in-flight stub). That path was still indistinguishable from an empty scan on disk. Every drop in `dedup_scan_against_stubs` now appends one JSON line to `.jig/decision-suppressions.log` — dropped candidate (quote/tier/who), the covering stub, the raw containment score, and the call site. Additive only: the drop itself is unchanged, and logging is fail-open (a broken sink never changes what is suppressed) and opt-in via `project_dir` so pure-function callers are untouched. Containment gains a `containment()` measure that `is_contained` thresholds over, so the score that decides a drop and the score written to the log share one home. Corrects bug 011's overclaim ("no silent suppression remains to log") inline and appends an `## Amendments` note; refinement-todo #255 gains an observability line. The fix class stays deferred to the maintainer — this removes the silence, not the residual. Hosts regenerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ecord prose Addresses PR review feedback on the bug 011 option-4 suppression logging: - Log the dropped candidate's transcript `turn` (guarded like matched's), the owner's locator for the possibly-reversing statement the log exists to surface. - Note in `suppression_log_path` that the log is append-only/unbounded by design (never read into context, clipped lines, git-ignored — owner prunes if needed). - Restore the bug 011 Fix-class overclaim verbatim and annotate it with a footnote instead of rewriting it, per ADR-0010 closed-record discipline; move the correction into the footnote + existing amendment. - Add unit coverage: turn is logged; the no-turn sentinel (-1) omits the key. - Regenerate claude + codex hosts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lity Record the deferred option to narrow decision-suppressions.log to agent-sourced drops (the only class that can hide a lost decision). Not acted on now — kept the fix additive with the simple 'every drop is logged' contract; revisit if the log proves noisy in practice or when the flag-don't-drop mitigation is taken up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Ran a multi-perspective self-review on this and pushed follow-up fixes. Summary for @ramboz before validating: Outcome: no blockers, nothing on security/reliability — the change is genuinely additive and well-tested. Three polish items found and addressed in
Deferred, with a note in Verification: decision suites green on Python 3.9 — Still your call: the fix class — whether Separately: while reviewing I confirmed the new |
🤖 I have created a release *beep* *boop* --- ## [2.8.0](v2.7.1...v2.8.0) (2026-07-21) ### Features * **decision-capture:** log the surviving silent suppression (bug 011 option 4) ([#120](#120)) ([7952238](7952238)) * **migrate:** flag ADR status the DONE gate can't read ([#106](#106)) ([e9f50dc](e9f50dc)) * **orient:** add /jig:orient project-orientation skill (Tier 1) ([#90](#90)) ([0b76648](0b76648)) * **workflow:** close adoption and discovery gaps ([#104](#104)) ([414fe82](414fe82)) ### Bug Fixes * **decision-capture:** capture hygiene — misattribution, dismissed dialogs, decision routing ([#108](#108)) ([da342b0](da342b0)) * **decision-scan:** flag duplicate decisions instead of suppressing them ([#117](#117)) ([cb6ce8d](cb6ce8d)) * **decisions:** drop the "No entries yet" placeholder when the first entry lands ([#115](#115)) ([5b08b91](5b08b91)) * **decisions:** seed lightweight-decisions from template; fail loud on foreign format ([#113](#113)) ([af53265](af53265)) * **scaffold:** copy templates/ in Claude scaffold mode so record helpers resolve ([#109](#109)) ([#119](#119)) ([a3d35a4](a3d35a4)) ### Documentation * **bugs:** record bug 011 — decision dedup suppresses recorded-decision reversals ([#112](#112)) ([fea869d](fea869d)) * **readme:** cleanup ([91427b4](91427b4)) * **specs:** reserve 094-capture-hygiene ([#114](#114)) ([e5930b4](e5930b4)) * **specs:** reserve 095-scaffold-template-copy ([#118](#118)) ([fbd846d](fbd846d)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
additive observability only, fix class still the maintainer's — @ramboz to validate before merge.
Implements bug 011 option 4 (suppression logging), the piece #109 finding 2 left on the table. Follows #117, which decided the fix class and removed dropping on both recorded-corpus paths.
Why this exists after #117
#117 deliberately kept one drop:
decision_scratch.dedup_scan_against_stubs. Its own residual 1 documents the consequence — a Tier-3 agent reversal of an in-flight stub is still silently dropped there, since agent prose never produces a stub of its own to survive as. On disk, that drop was indistinguishable from "nothing was found." Bug 011's record even overclaimed "no silent suppression remains to log" — this corrects that.So the original A4 scope (
dedup()/prune_recorded_stubs()) collapsed: #117 turned both into flags that surface, so there is nothing silent left to log there. The one surviving silent drop is the only thing worth instrumenting, and this PR instruments exactly it.The change
dedup_scan_against_stubs.jig/decision-suppressions.log(dropped candidate quote/tier/who, covering stub, containment score, call site). The drop is unchanged — same rule, same result, logging is a gated side-effect.decision_scan.containment()is_containedis now a threshold over it, so the number that decides a drop and the number logged share one home.project_dir— pure-function callers log nothing and behave identically..gitignoredecision-scratchprecedent (jig-local runtime state, per-checkout, never tracked).## Amendmentsnote added; residual gains an observability line. Fix class stays deferred.hosts/(claude + codex)Scope guardrails (out of scope, on purpose)
dedup_scan_against_stubsshould drop at all stays the maintainer's call — this removes the silence, not the residual. Bug 011 staysDONE; the residual stays a residual.Test evidence (red → green, TDD)
Red — the drop happened, no log existed:
Green after implementation:
test_decision_scratch.SuppressionLogTests(4): a drop writes one inspectable line; a kept candidate logs nothing; logging is opt-in (noproject_dir→ no write, unchanged behaviour); a broken log sink never changes what is dropped (fail-open).test_jig_decision_capture.py(+2, end-to-end through the real Stop hook): the dedup drop writes a suppression line namingdedup_scan_against_stubs; a plain surfacing with no drop writes no log.test_decision_scan+test_decision_scratch(73), inflight hook (12), capture hook (13). Ruff clean. Validated on Python 3.9.6.The one drift-guard flake on
hosts/claude/.claude-plugin/plugin.jsonis the known, pre-existing intermittent one (docs/bugs/008) — that file is untouched here and clean in the tree.