ADR-074: SessionStart Governance Hook - Mandatory Skill Checking
Status
Deprecated - January 23, 2026 (Originally Accepted January 16, 2026)
MoE Judges Score: 9.1/10 (APPROVED)
Deprecation Notice
The SessionStart hook (session_start_governance.py) has been removed from settings.json.
Reason: Redundancy with CLAUDE.md
| Source | Loads At | Contains |
|---|---|---|
| CLAUDE.md | Session start (automatic by Claude Code) | Task ID protocol, safety directive, governance rules |
| SessionStart hook | Session start (manual injection) | Same content duplicated from governance skill |
Additional Issue: Claude Code bug causes "SessionStart:resume hook error" messages even when hooks succeed, creating noise without value.
What Remains Active:
task-id-validator.py(PreToolUse) - Validates task IDs on Bash/Write/Edit/Tasktask-tracking-enforcer.py(PreToolUse:TodoWrite) - Validates track nomenclature- All governance rules in CLAUDE.md (loaded automatically)
Recommendation: Use CLAUDE.md for governance injection. It's cleaner, doesn't cause error noise, and loads automatically.
Context
Problem Statement
CODITECT has 221 skills and 152 agents, but no mechanism to ensure agents CHECK for applicable skills before taking action. Agents may:
- Skip Skill Lookup: Proceed directly with tasks without consulting skill library
- Rationalize Avoidance: Use justifications like "I know what that means" to bypass skills
- Task ID Protocol Violations: Forget to include task IDs in tool calls (CLAUDE.md Principle #12)
- Agent Discovery Bypass: Execute tasks without running
/which <task>first
Source Analysis: Superpowers Pattern
Analysis of submodules/superpowers (obra/superpowers v4.0.3) reveals:
SessionStart Hook → Injects using-superpowers skill → Agent sees governance first
Key Innovation: Skills are MANDATORY process guides, not optional suggestions
Superpowers Red Flags (Rationalizations to Block):
| Thought | Reality |
|---|---|
| "I know what that means" | Knowing ≠ Using. Invoke skill. |
| "This is just a simple question" | Questions are tasks. Check skills. |
| "I'll just do this one thing first" | Check BEFORE doing anything. |
| "Let me gather information first" | Skill may specify HOW to gather. |
Current CODITECT Limitation
- SessionStart: Only runs installation verification (CLAUDE.md Section 2)
- No Skill Injection: Governance rules buried in CLAUDE.md (1,500+ lines)
- No Red Flags: Rationalization patterns not documented
- No Enforcement: Task ID protocol is stated but not enforced at hook level
Requirements
- SessionStart Injection: Load governance skill content at conversation start
- Red Flags Table: Document CODITECT-specific rationalization patterns
- Task ID Enforcement: Hook validates task IDs on tool calls
- Agent Discovery Reminder: Prompt to run
/whichbefore complex tasks - Skill-First Protocol: Mandate skill check before implementation
- TDD Compliance: All changes must have tests written first
Decision
Implement a Governance Hook System that injects mandatory skill checking at session start and enforces compliance via pre-tool hooks.
1. Architecture Overview
┌─────────────────────────────────────────────────────────────────────────────┐
│ SESSIONSTART GOVERNANCE ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ SESSION START EVENT │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ hooks/session-start-governance.py │ │
│ │ │ │
│ │ 1. Load coditect-governance skill content │ │
│ │ 2. Inject into conversation via hookSpecificOutput │ │
│ │ 3. Agent sees governance as FIRST system message │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ PRE-TOOL HOOKS (Enforcement Layer) │ │
│ │ │ │
│ │ PreToolUse:Bash → Validate task ID in description │ │
│ │ PreToolUse:Edit → Validate task ID in description │ │
│ │ PreToolUse:Write → Validate task ID in description │ │
│ │ PreToolUse:TodoWrite → Validate track nomenclature (ADR-054) │ │
│ │ │ │
│ │ Validation Failure → Block tool call + remind protocol │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ GOVERNANCE SKILL CONTENT │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ skills/coditect-governance/SKILL.md │ │
│ │ │ │
│ │ ## The Rule │ │
│ │ BEFORE any response or action: │ │
│ │ 1. Ask: "Might any skill apply?" │ │
│ │ 2. If YES (even 1%), invoke Skill tool │ │
│ │ 3. If uncertain, invoke and see if relevant │ │
│ │ 4. Follow skill exactly │ │
│ │ │ │
│ │ ## CODITECT-Specific Requirements │ │
│ │ 1. Task ID Protocol (every tool call) │ │
│ │ 2. Agent Discovery (/which <task>) │ │
│ │ 3. Track Nomenclature (ADR-054) │ │
│ │ 4. Cloud Sync Verification │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2. Governance Skill Content
# CODITECT Governance Skill
## The Rule (Mandatory)
BEFORE any response or action:
1. **Ask:** "Might any skill apply to this task?"
2. **If YES (even 1%):** Invoke the Skill tool with the skill name
3. **If uncertain:** Invoke anyway - skills self-describe applicability
4. **Follow skill exactly:** Skills are tested process guides, not suggestions
## CODITECT-Specific Requirements
### Task ID Protocol (Principle #12)
Every tool call MUST include task ID in description:
```bash
# ✅ CORRECT
Bash(description="A.9.1.3: Check migration status")
# ❌ WRONG - Violations blocked by PreToolUse hook
Bash(description="Check migration status")
Agent Discovery (Before Complex Tasks)
Run /which <task> before any non-trivial task to find best agent.
Track Nomenclature (ADR-054)
Format: Track.Section.Task[.Subtask]
- A = Backend, B = Frontend, C = DevOps, D = Security, E = Testing, F = Docs
Task Specification (ADR-115)
Full task specifications in PILOT plans use YAML frontmatter format per CODITECT-STD-003.
Validation: python3 scripts/validate-pilot-tasks.py
Red Flags (Rationalizations to Catch)
| If You Think This... | The Reality Is... |
|---|---|
| "I know what that means" | Knowing ≠ Using. Invoke the skill. |
| "This is just a simple question" | Questions are tasks. Check for skills. |
| "I'll just do this one thing first" | Check BEFORE doing anything. |
| "I'll add the task ID next time" | Task ID protocol is non-negotiable. |
| "This is too simple for /which" | /which reveals best-practice agents. |
| "I remember what the pattern is" | Skills evolve. Current version is truth. |
### 3. Hook Implementation
#### SessionStart Hook (Python)
```python
#!/usr/bin/env python3
"""
SessionStart Governance Hook
Injects coditect-governance skill content into conversation start.
Implements Superpowers' using-superpowers pattern for CODITECT.
TDD: tests/hooks/test_session_start_governance.py
"""
import json
import sys
from pathlib import Path
SKILL_PATH = Path.home() / ".coditect" / "skills" / "coditect-governance" / "SKILL.md"
def load_governance_skill() -> str:
"""Load governance skill content, stripping frontmatter."""
if not SKILL_PATH.exists():
return ""
content = SKILL_PATH.read_text()
# Strip YAML frontmatter
if content.startswith("---"):
end_idx = content.find("---", 3)
if end_idx != -1:
content = content[end_idx + 3:].strip()
return content
def main():
"""Main hook execution."""
skill_content = load_governance_skill()
if not skill_content:
# Fallback: minimal governance reminder
skill_content = """
## CODITECT Governance (Fallback)
Before ANY action:
1. Include task ID in all tool descriptions: `Bash(description="A.1.1: action")`
2. Run `/which <task>` for complex tasks
3. Check if any skill applies: `Skill(skill="skill-name")`
"""
# Output hook response
output = {
"hookSpecificOutput": skill_content,
"continue": True
}
print(json.dumps(output))
if __name__ == "__main__":
main()
PreToolUse Hook (Task ID Validation)
#!/usr/bin/env python3
"""
PreToolUse Task ID Validator Hook
Validates task IDs in tool call descriptions per CLAUDE.md Principle #12.
Blocks tool calls without valid task IDs.
TDD: tests/hooks/test_task_id_validator.py
"""
import json
import re
import sys
# Task ID pattern: Track.Section.Task[.Subtask]
# Examples: A.9.1.3, F.4.2, E.2.3.1
TASK_ID_PATTERN = re.compile(r'^[A-Z]\.\d+\.\d+(\.\d+)?:')
# Tools that require task ID validation
VALIDATED_TOOLS = {"Bash", "Edit", "Write", "Read", "Grep", "Glob"}
def validate_task_id(tool_name: str, description: str) -> tuple[bool, str]:
"""
Validate that description contains task ID.
Returns:
(is_valid, message)
"""
if tool_name not in VALIDATED_TOOLS:
return True, ""
if not description:
return False, f"Tool {tool_name} requires description with task ID"
if TASK_ID_PATTERN.match(description):
return True, ""
return False, f"Missing task ID. Format: 'X.N.N: action' (e.g., 'A.9.1.3: Check status')"
def main():
"""Main hook execution."""
# Read hook input from stdin
input_data = json.load(sys.stdin)
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
description = tool_input.get("description", "")
is_valid, message = validate_task_id(tool_name, description)
if is_valid:
output = {"continue": True}
else:
output = {
"continue": False,
"hookSpecificOutput": f"⚠️ GOVERNANCE VIOLATION: {message}\n\nTask ID Protocol (CLAUDE.md Principle #12) requires every tool call to include task ID."
}
print(json.dumps(output))
if __name__ == "__main__":
main()
4. Hook Registration (settings.json)
{
"hooks": {
"SessionStart": [
{
"command": "python3 ~/.coditect/hooks/session-start-governance.py",
"description": "Load CODITECT governance skill"
}
],
"PreToolUse": [
{
"matcher": {"tool_name": "Bash"},
"command": "python3 ~/.coditect/hooks/task-id-validator.py",
"description": "Validate task ID in Bash descriptions"
},
{
"matcher": {"tool_name": "Edit"},
"command": "python3 ~/.coditect/hooks/task-id-validator.py",
"description": "Validate task ID in Edit descriptions"
},
{
"matcher": {"tool_name": "Write"},
"command": "python3 ~/.coditect/hooks/task-id-validator.py",
"description": "Validate task ID in Write descriptions"
}
]
}
}
5. TDD Test Specifications
Test: SessionStart Governance Injection
# tests/hooks/test_session_start_governance.py
import pytest
from pathlib import Path
from unittest.mock import patch, mock_open
class TestSessionStartGovernance:
"""TDD tests for SessionStart governance hook."""
def test_loads_governance_skill_content(self):
"""RED→GREEN: Hook loads skill content from SKILL.md."""
skill_content = "# CODITECT Governance\n\nThe Rule..."
with patch("builtins.open", mock_open(read_data=f"---\nname: test\n---\n{skill_content}")):
from hooks.session_start_governance import load_governance_skill
result = load_governance_skill()
assert "CODITECT Governance" in result
assert "---" not in result # Frontmatter stripped
def test_returns_fallback_when_skill_missing(self):
"""RED→GREEN: Hook returns fallback if skill file missing."""
with patch.object(Path, "exists", return_value=False):
from hooks.session_start_governance import load_governance_skill
result = load_governance_skill()
assert result == ""
def test_hook_output_format(self):
"""RED→GREEN: Hook outputs correct JSON format."""
# Hook must output hookSpecificOutput and continue: true
pass
def test_governance_contains_task_id_protocol(self):
"""RED→GREEN: Governance content includes task ID requirements."""
pass
def test_governance_contains_red_flags_table(self):
"""RED→GREEN: Governance content includes rationalization red flags."""
pass
Test: Task ID Validator
# tests/hooks/test_task_id_validator.py
import pytest
import json
from hooks.task_id_validator import validate_task_id, TASK_ID_PATTERN
class TestTaskIdValidator:
"""TDD tests for task ID validation hook."""
@pytest.mark.parametrize("description,expected", [
("A.9.1.3: Check migration status", True),
("F.4.2: Update documentation", True),
("E.2.3.1: Run unit tests", True),
("Check migration status", False), # Missing task ID
("", False), # Empty description
("9.1.3: No track letter", False), # Invalid format
])
def test_task_id_validation(self, description, expected):
"""RED→GREEN: Validates task ID patterns correctly."""
is_valid, _ = validate_task_id("Bash", description)
assert is_valid == expected
def test_non_validated_tools_always_pass(self):
"""RED→GREEN: Tools not in VALIDATED_TOOLS always pass."""
is_valid, _ = validate_task_id("AskUserQuestion", "No task ID here")
assert is_valid == True
def test_validation_failure_message_format(self):
"""RED→GREEN: Failure message explains correct format."""
is_valid, message = validate_task_id("Bash", "No task ID")
assert not is_valid
assert "X.N.N: action" in message
def test_hook_blocks_on_validation_failure(self):
"""RED→GREEN: Hook returns continue: false on failure."""
pass
6. SDD: Component Design
┌─────────────────────────────────────────────────────────────────────────────┐
│ COMPONENT: Governance Hook System │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ COMPONENTS │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ session-start-governance.py │ │
│ │ ──────────────────────────── │ │
│ │ Responsibility: Load and inject governance skill at session start │ │
│ │ Interface: hookSpecificOutput → conversation context │ │
│ │ Dependencies: skills/coditect-governance/SKILL.md │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ task-id-validator.py │ │
│ │ ──────────────────── │ │
│ │ Responsibility: Validate task IDs in tool call descriptions │ │
│ │ Interface: PreToolUse hook → continue: true/false │ │
│ │ Dependencies: None (standalone regex validation) │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ coditect-governance/SKILL.md │ │
│ │ ──────────────────────────── │ │
│ │ Responsibility: Define governance rules and red flags │ │
│ │ Interface: Markdown content loaded by SessionStart hook │ │
│ │ Dependencies: None │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ DATA FLOW │
│ ───────── │
│ 1. Session starts → SessionStart hook fires │
│ 2. Hook loads coditect-governance/SKILL.md │
│ 3. Hook injects content via hookSpecificOutput │
│ 4. Agent sees governance as first system context │
│ 5. Agent makes tool call → PreToolUse hook fires │
│ 6. Hook validates task ID in description │
│ 7. Valid → continue: true | Invalid → continue: false + message │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Consequences
Positive
- Governance Visibility: Rules visible from message 1, not buried in CLAUDE.md
- Enforcement at Hook Level: Task ID violations blocked before execution
- Red Flags Documentation: Common rationalizations explicitly addressed
- Skill-First Culture: Agents learn to check skills before acting
- Superpowers Compatibility: Pattern aligns with Superpowers workflow
- Testable Compliance: TDD tests verify hook behavior
Negative
- Hook Latency: SessionStart hook adds ~50ms to session initialization
- False Positives: Some legitimate tool calls may lack task IDs initially
- Learning Curve: Contributors must adapt to task ID protocol
- Maintenance Overhead: Governance skill requires updates as rules evolve
Mitigations
- Grace Period: Initial deployment with warnings (not blocks)
- Clear Documentation: CLAUDE.md updated with hook behavior
- Escape Hatch:
CODITECT_SKIP_GOVERNANCE=1for debugging - Automated Updates: Governance skill version tracked in component-counts.json
Implementation
Files to Create
| File | Purpose | LOC Est. |
|---|---|---|
hooks/session-start-governance.py | SessionStart hook | ~60 |
hooks/task-id-validator.py | PreToolUse validation hook | ~80 |
skills/coditect-governance/SKILL.md | Governance skill content | ~150 |
tests/hooks/test_session_start_governance.py | TDD tests | ~100 |
tests/hooks/test_task_id_validator.py | TDD tests | ~120 |
Files to Modify
| File | Changes |
|---|---|
~/.claude/settings.json | Add hook registrations |
config/component-counts.json | Add governance skill to counts |
CLAUDE.md | Reference governance hook behavior |
Test Coverage Requirements
- Unit Tests: 100% coverage for hook logic
- Integration Tests: SessionStart injection verified
- Pressure Tests: Rationalization scenarios with subagents
References
- Source Analysis:
submodules/superpowers(obra/superpowers v4.0.3) - Related ADR: ADR-057 (Initial Setup), ADR-060 (MoE Verification)
- CLAUDE.md: Section 2 (Installation), Principle #12 (Task ID Protocol)
- Superpowers:
using-superpowers/SKILL.md,hooks/session-start.sh
Author: CODITECT Architecture Team Reviewers: Architecture Council Source Commit: Analysis of submodules/superpowers and submodules/rlm