# Spec: Hook Python Port

## Purpose

Defines the requirements for porting the memory plugin's bash hooks to Python, including shared module structure, error handling conventions, and correctness verification via golden fixtures.

## Requirements

### Requirement: Single config loader
A `config.py` module SHALL expose `load_config() -> Config` (frozen dataclass) that parses `config.yaml`, expands paths, applies defaults, and handles missing file — replacing all duplicated inline YAML parsers.

#### Scenario: Config loaded once per hook invocation
- **WHEN** any hook script runs
- **THEN** it calls `load_config()` exactly once and accesses all config values through the returned `Config` dataclass

### Requirement: Uniform stdin parser
A `hook_io.py` module SHALL expose `read_input() -> HookInput` (dataclass with fields: session_id, cwd, file_path, reason) that parses hook stdin JSON, replacing all inline stdin parsers and eliminating the `python3`-vs-`jq` inconsistency.

#### Scenario: stdin parsed uniformly across all hooks
- **WHEN** any of the five hook entry points (session_start, session_context, post_tool_use_write, session_end, memsearch_sync) runs
- **THEN** stdin is parsed via `read_input()`, not inline shell commands or ad-hoc python

### Requirement: Named session-state contract
A `session_state.py` module SHALL expose `record_touch(session_id, path)` and `read_touches(session_id) -> list[str]`, encapsulating the `/tmp/claude-vault-touched-$SESSION_ID` temp-file handoff between post-tool-use-write and session-end hooks.

#### Scenario: Touch handoff uses named functions
- **WHEN** post_tool_use_write records a vault touch
- **THEN** it calls `record_touch(session_id, path)` rather than writing the temp file directly

#### Scenario: Session-end reads touches via named function
- **WHEN** session_end reads which vault files were touched
- **THEN** it calls `read_touches(session_id)` rather than reading the temp file directly

### Requirement: Fail-open hook mains
Every hook `main()` function MUST wrap execution in try/except; uncaught exceptions MUST log the error and exit with code 0.

#### Scenario: Exception does not disrupt session
- **WHEN** a hook raises an uncaught exception
- **THEN** the hook logs the exception to stderr and exits 0 (session continues normally)

### Requirement: Behavior-faithful port with golden fixtures
The Python port MUST demonstrate identical output to the bash hooks against golden fixtures before any cleanup or refactoring.

#### Scenario: Golden fixtures captured before port
- **WHEN** the port task begins
- **THEN** golden fixture files exist for each of the 4 bash hooks (known stdin → snapshot of side effects)

#### Scenario: Python output matches bash output
- **WHEN** Python hooks are run against golden fixtures
- **THEN** output matches the bash hook snapshots exactly before any refactor proceeds
