Decisions
- Pending: Which validations have the highest value-to-effort ratio?
- Pending: Should violations block (exit 2) or warn (exit 0 + stderr)?
- Pending: Should validation scripts be bash or python?
User Tasks
FR-012: Deterministic Validation Hooks
Summary
Turn CLAUDE.md conventions into hard constraints using PreToolUse hooks that programmatically reject invalid actions.
Problem / Motivation
CLAUDE.md rules are prompt-based — they’re requests, not enforcement. Claude can (and does) violate them: uppercase filenames, missing frontmatter, imports not at top, non-English vault content, files in wrong directories. These are all deterministic rules that don’t need AI judgment — they should be enforced by code.
This FR focuses specifically on validation hooks — the subset of FR-011 that turns conventions into constraints. FR-011 covers all hook types (session, automation, etc.); this FR goes deep on the validation/enforcement category.
Proposed Solution
A set of PreToolUse hooks (matcher: Edit|Write) that validate Claude’s output before it hits disk.
Planned Validations
| Validation | Rule | Trigger |
|---|---|---|
| filename-convention | Lowercase + hyphens only for new .md files | Write |
| frontmatter-schema | FR files must have required frontmatter fields (id, title, status, priority, created) | Write, Edit |
| import-ordering | Python/JS imports must be at top of file | Write, Edit |
| language-check | Vault .md files must be in English (heuristic: no Dutch stopwords in first 500 chars) | Write, Edit |
| directory-placement | FR files in in-progress/ must have an FR number; ideas/ files must NOT have FR numbers | Write |
| template-compliance | New FR files must contain required sections (Summary, Problem, Phase Overview) | Write |
| no-emoji | Block emoji in files unless user explicitly requested | Write, Edit |
Architecture
.claude/hooks/
├── validate-write.sh # Dispatcher for Write validations
├── validate-edit.sh # Dispatcher for Edit validations
└── validators/
├── filename-convention.sh
├── frontmatter-schema.sh
├── import-ordering.sh
├── language-check.sh
├── directory-placement.sh
├── template-compliance.sh
└── no-emoji.sh
Each validator:
- Receives the tool input via stdin (JSON with
file_pathandcontent) - Exits 0 to allow, exits 2 to block
- Writes rejection reason to stderr (fed back to Claude as feedback)
The dispatcher runs all applicable validators and blocks if any fail.
Open Questions
1. Block vs Warn
Question: Should validation failures block the action or just warn?
| Option | Description |
|---|---|
| A) Block (exit 2) | Claude gets feedback and must fix before proceeding |
| B) Warn (exit 0 + stderr) | Claude sees the warning but action goes through |
| C) Configurable per validator | Each validator has a severity setting |
Recommendation: Option A — the whole point is enforcement. If we just warn, we’re back to prompt-based suggestions.
2. Language detection approach
Question: How to detect non-English content?
| Option | Description |
|---|---|
| A) Dutch stopword list | Check for common Dutch words (de, het, een, van, is, dat, op, voor) |
| B) Python langdetect | More accurate but adds dependency |
| C) Simple heuristic | Just check for non-ASCII density |
Recommendation: Option A — simple, no dependencies, covers the actual failure mode.
Phase Overview
| Phase | Description | Status |
|---|---|---|
| Phase 1 | Core validators (filename, frontmatter, directory) | — |
| Phase 2 | Code validators (imports, language, emoji) | — |
| Phase 3 | Template compliance + edge case hardening | — |
Phase 1: Core Validators —
Goal: Enforce the most commonly violated conventions.
| File / Feature | Details | Owner | Status |
|---|---|---|---|
validate-write.sh | Dispatcher script for Write hook | opus | — |
validators/filename-convention.sh | Reject uppercase/spaces in .md filenames | opus | — |
validators/frontmatter-schema.sh | Validate required FR frontmatter fields | opus | — |
validators/directory-placement.sh | FR number rules per directory | opus | — |
| Register in settings.json | Add PreToolUse hook entry | opus | — |
Phase 2: Code Validators —
Goal: Enforce code-level conventions.
| File / Feature | Details | Owner | Status |
|---|---|---|---|
validate-edit.sh | Dispatcher script for Edit hook | opus | — |
validators/import-ordering.sh | Check imports at top for .py/.js/.ts files | opus | — |
validators/language-check.sh | Dutch stopword detection in vault .md files | opus | — |
validators/no-emoji.sh | Block emoji unless explicitly allowed | opus | — |
Phase 3: Template Compliance —
Goal: Ensure structural consistency for feature requests.
| File / Feature | Details | Owner | Status |
|---|---|---|---|
validators/template-compliance.sh | Check required sections in new FR files | opus | — |
| Edge case handling | Whitelist paths, handle partial edits | opus | — |
| Documentation | Update hooks README with all validators | opus | — |
Test
Manual tests
| Test | Expected | Owner | Actual | Last |
|---|---|---|---|---|
| Write file with uppercase name | Blocked with “lowercase only” message | mv | pending | - |
| Write FR without frontmatter | Blocked with “missing fields” message | mv | pending | - |
| Write idea with FR number | Blocked with “ideas don’t get FR numbers” | mv | pending | - |
| Write FR to in-progress without FR number | Blocked | mv | pending | - |
| Write valid file | Allowed (exit 0) | mv | pending | - |
| Edit Python file with import mid-file | Blocked with “imports at top” message | mv | pending | - |
| Write Dutch content to vault | Blocked with “English only” message | mv | pending | - |
AI-verified tests
| Scenario | Expected behavior | Verification method |
|---|---|---|
| … | … | … |
E2E tests
| Scenario | Assertion |
|---|---|
| … | … |
Integration tests
| Component | Coverage |
|---|---|
| … | … |
Unit tests
| Component | Tests | Coverage |
|---|---|---|
| … | … | … |
History
| Date | Event | Details |
|---|---|---|
| 2026-03-12 | Created | Born from discussion about deterministic enforcement beyond CLAUDE.md |
References
- FR-011 (Hook Scripts) — parent FR for all hooks; this FR deep-dives on validation
- FR-003 (Protected Files System) — complementary; protects specific files, this validates content