Skip to main content

ADR-183: Governance Hook Architecture

Status

ACCEPTED - January 10, 2026

Context

Current State

Claude Code provides a hook system for extending functionality:

  • PreToolUse: Validates or modifies tool calls before execution
  • PostToolUse: Processes results after tool execution
  • SessionStart: Runs at session initialization
  • SessionEnd: Runs at session termination

CODITECT uses these hooks for governance enforcement, but the architecture was undocumented.

Problem Statement

  1. Missing Documentation: No ADR documenting hook architecture
  2. Scattered Implementation: Hooks spread across multiple files
  3. Inconsistent Patterns: Different validation approaches
  4. No Error Handling Standard: Hooks fail inconsistently
  5. SessionStart Redundancy: Overlaps with CLAUDE.md auto-loading

Requirements

  1. Hook Catalog: Document all governance hooks
  2. Consistent Patterns: Standard hook implementation
  3. Error Handling: Fail-open vs fail-closed policies
  4. Environment Control: Enable/disable via environment variables
  5. Logging Standard: Centralized hook logging

Decision

We adopt a Layered Governance Hook Architecture:

Hook Types

Hook TypeTimingPurposeFail Mode
PreToolUseBefore executionValidate, block, modifyConfigurable
PostToolUseAfter executionSync, log, notifyFail-open
SessionStartSession initDEPRECATEDN/A
SessionEndSession closeExport, backupFail-open

Governance Hooks Catalog

PreToolUse Hooks

HookFilePurposeValidated Tools
Task ID Validatortask-id-validator.pyEnforce task ID in descriptionsBash, Edit, Write, Read, Grep, Glob, Task
Task Tracking Enforcertask-tracking-enforcer.pyValidate track nomenclature on TodoWriteTodoWrite
Install Script Validatorvalidators/install-script-validator.pyValidate installation scriptsBash
Skill Checkerskill-checker.pyValidate skill invocationsSkill
Governance Enforcergovernance-enforcer.pyGeneral governance rulesMultiple

PostToolUse Hooks

HookFilePurposeTriggered By
Task Plan Synctask-plan-sync.pySync TodoWrite to PILOT + CloudTodoWrite
Session Log Syncsession-log-sync.pyAuto-append to daily session logMultiple

SessionEnd Hooks

HookFilePurpose
Session Retrospectivesession-retrospective.pyAnalyze session for patterns
Auto Exportauto-export.pyExport session before close

SessionStart Deprecation

SessionStart hooks are deprecated because:

  1. CLAUDE.md auto-loads at session start (automatic)
  2. SessionStart hooks cause race conditions with MCP servers
  3. Redundant validation already in PreToolUse hooks

Migration: Move all SessionStart logic to CLAUDE.md or PreToolUse hooks.

Implementation Pattern

All hooks MUST follow this structure:

#!/usr/bin/env python3
"""
CODITECT {Hook Type} {Hook Name} Hook (ADR-183)

{Brief description of purpose}

Hook Type: {PreToolUse|PostToolUse|SessionEnd}
Input: JSON from stdin with {relevant fields}
Output: JSON with continue: true/false

Environment Variables:
CODITECT_SKIP_{HOOK_NAME}=1 - Skip this hook
CODITECT_{HOOK_NAME}_WARN_ONLY=1 - Warn but don't block

Author: AZ1.AI INC
Framework: CODITECT
ADR: ADR-183 Governance Hook Architecture
"""

import json
import os
import sys
from typing import Tuple

# Import centralized hook logger
try:
from hook_logger import get_hook_logger
logger = get_hook_logger("{hook_name}")
except ImportError:
logger = None

# Environment variable controls
ENV_SKIP = "CODITECT_SKIP_{HOOK_NAME}"
ENV_WARN_ONLY = "CODITECT_{HOOK_NAME}_WARN_ONLY"


def is_disabled() -> bool:
"""Check if hook is disabled via environment variable."""
return os.environ.get(ENV_SKIP, "").lower() in ("1", "true", "yes")


def is_warn_only() -> bool:
"""Check if running in warn-only mode."""
return os.environ.get(ENV_WARN_ONLY, "").lower() in ("1", "true", "yes")


def validate(input_data: dict) -> Tuple[bool, str]:
"""
Main validation logic.

Returns:
Tuple of (is_valid, message)
"""
# Hook-specific validation logic here
return True, ""


def main() -> None:
"""Main hook entry point."""
if is_disabled():
print(json.dumps({"continue": True}))
return

try:
input_data = json.load(sys.stdin)
is_valid, message = validate(input_data)

if is_valid:
print(json.dumps({"continue": True}))
elif is_warn_only():
print(json.dumps({
"continue": True,
"hookSpecificOutput": {"message": f"⚠️ WARNING: {message}"}
}))
else:
print(json.dumps({
"continue": False,
"hookSpecificOutput": {"message": f"🚫 BLOCKED: {message}"}
}))

except Exception:
# Fail-open on errors
print(json.dumps({"continue": True}))


if __name__ == "__main__":
main()

Environment Variable Convention

Variable PatternPurpose
CODITECT_SKIP_{HOOK}=1Completely skip the hook
CODITECT_{HOOK}_WARN_ONLY=1Warn but don't block
CODITECT_HOOKS_DISABLED=1Disable ALL hooks
CODITECT_HOOKS_VERBOSE=1Enable verbose logging

Logging Standard

All hooks use centralized logging via hook_logger.py:

from hook_logger import get_hook_logger
logger = get_hook_logger("hook_name")

# Standard log events
logger.log_hook_start(tool_name=..., description_preview=...)
logger.log_hook_end(success=True, decision="allow|warn|block")
logger.log_hook_error(exception, context="...")

Log Location: ~/PROJECTS/.coditect-data/logs/hooks/

Hook Registration

Hooks are registered in ~/.claude/settings.json:

{
"hooks": {
"PreToolUse": [
{
"matcher": {"tool_name": "Bash|Edit|Write|Read|Grep|Glob|Task"},
"hooks": [
{"type": "command", "command": "python3 ~/.coditect/hooks/task-id-validator.py"}
]
},
{
"matcher": {"tool_name": "TodoWrite"},
"hooks": [
{"type": "command", "command": "python3 ~/.coditect/hooks/task-tracking-enforcer.py"}
]
}
],
"PostToolUse": [
{
"matcher": {"tool_name": "TodoWrite"},
"hooks": [
{"type": "command", "command": "python3 ~/.coditect/hooks/task-plan-sync.py"}
]
}
]
}
}

Consequences

Positive

  1. Documented Architecture: Clear hook catalog and patterns
  2. Consistent Implementation: Standard template for all hooks
  3. Environment Control: Hooks can be disabled for debugging
  4. Centralized Logging: All hooks log to standard location
  5. Fail-Open Default: Errors don't block work

Negative

  1. Maintenance Overhead: Must keep hook catalog updated
  2. Performance: Each hook adds latency to tool calls
  3. Complexity: New contributors must understand hook system

Mitigations

  1. Hook Catalog: This ADR serves as documentation
  2. Minimal Hooks: Only add hooks with clear governance value
  3. Onboarding: CLAUDE.md references this ADR

Implementation

Phase 1: Documentation (Complete)

  • Create ADR-183 (this document)
  • Document existing hooks
  • Define implementation pattern

Phase 2: Standardization (In Progress)

  • Update all hooks to follow pattern
  • Add environment variable support to all hooks
  • Implement centralized logging

Phase 3: Validation (Pending)

  • Fix task-id-validator.py regex (see ADR-054)
  • Add integration tests for hooks
  • Create hook health dashboard
  • ADR-054: Track Nomenclature Extensibility (defines valid track patterns)
  • ADR-056: Action-Level Task Tracking Protocol (defines task ID requirement)
  • ADR-116: Track-Based Plan Architecture (defines TRACK file structure)
  • Standard: CODITECT-STANDARD-AUTOMATION.md (Principle #12)
  • Skill: coditect-governance

Version: 1.0.0 Author: Hal Casteel Approved By: Platform Architecture Team