Skip to content

Hook BLOCK/REMINDER/WARN decisions have no durable sink, only ephemeral stderr #186

Description

@SUaDtL

Hook BLOCK/REMINDER/WARN decisions have no durable sink — only ephemeral stderr

Severity: medium | Confidence: 0.7 | Effort: M
Where:
plugins/ca/hooks/_hooklib.py:596-609
Evidence: block()/remind()/warn() (the three primitives every one of the 16 entry hooks funnels every gate decision through, 21 consumers of _hooklib.py) each do nothing but print(..., file=sys.stderr) (block also sys.exit(2)). No call anywhere in plugins/ca/hooks writes a gate decision (which tag fired, block vs remind vs degrade-warn, timestamp) to any file. The only durable .codearbiter logs are the H-05 audit set (overrides.log, triage.log, sprint-log.md), which record /override and ADR actions that Claude itself writes in prose, not the mechanical hook decision stream.
Impact: Every BLOCKED/REMINDER/degradation event is visible only in the live transcript for that one turn. There is no way to answer, after the fact or across a session/repo, "how often did H-09b fire", "which gates degraded silently (warn()) this week", or "did a block get worked around by a retried tool call" without scraping transcripts. This weakens the audit-completeness story the plugin sells (gates-as-enforcement) for the mechanical gate layer, distinct from the already-open /override log-write-compulsion question (CONFIRM-09) which concerns only the prose-driven override log.
Recommendation: Add an optional, best-effort append to a dedicated .codearbiter/gate-events.log (or reuse the H-05 audit-log machinery) from block()/remind()/warn(), gated so it never turns a fail-open hook into a fail-closed one on write failure, mirroring the fail-open discipline already used throughout _hooklib.py.
Acceptance criteria:

  • A repo-local run of a hook that hits block() or warn() produces a corresponding durable, greppable record outside the live transcript
  • The added write path is proven fail-open (a locked/missing log file does not change the hook's exit code)

Metadata

Metadata

Assignees

No one assigned

    Labels

    decisionNeeds a user decision / ADR (not a straight fix)sev:medTribunal/triage: medium severity

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions