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

ValidationRuleTrigger
filename-conventionLowercase + hyphens only for new .md filesWrite
frontmatter-schemaFR files must have required frontmatter fields (id, title, status, priority, created)Write, Edit
import-orderingPython/JS imports must be at top of fileWrite, Edit
language-checkVault .md files must be in English (heuristic: no Dutch stopwords in first 500 chars)Write, Edit
directory-placementFR files in in-progress/ must have an FR number; ideas/ files must NOT have FR numbersWrite
template-complianceNew FR files must contain required sections (Summary, Problem, Phase Overview)Write
no-emojiBlock emoji in files unless user explicitly requestedWrite, 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_path and content)
  • 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?

OptionDescription
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 validatorEach 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?

OptionDescription
A) Dutch stopword listCheck for common Dutch words (de, het, een, van, is, dat, op, voor)
B) Python langdetectMore accurate but adds dependency
C) Simple heuristicJust check for non-ASCII density

Recommendation: Option A — simple, no dependencies, covers the actual failure mode.


Phase Overview

PhaseDescriptionStatus
Phase 1Core validators (filename, frontmatter, directory)
Phase 2Code validators (imports, language, emoji)
Phase 3Template compliance + edge case hardening

Phase 1: Core Validators —

Goal: Enforce the most commonly violated conventions.

File / FeatureDetailsOwnerStatus
validate-write.shDispatcher script for Write hookopus
validators/filename-convention.shReject uppercase/spaces in .md filenamesopus
validators/frontmatter-schema.shValidate required FR frontmatter fieldsopus
validators/directory-placement.shFR number rules per directoryopus
Register in settings.jsonAdd PreToolUse hook entryopus

Phase 2: Code Validators —

Goal: Enforce code-level conventions.

File / FeatureDetailsOwnerStatus
validate-edit.shDispatcher script for Edit hookopus
validators/import-ordering.shCheck imports at top for .py/.js/.ts filesopus
validators/language-check.shDutch stopword detection in vault .md filesopus
validators/no-emoji.shBlock emoji unless explicitly allowedopus

Phase 3: Template Compliance —

Goal: Ensure structural consistency for feature requests.

File / FeatureDetailsOwnerStatus
validators/template-compliance.shCheck required sections in new FR filesopus
Edge case handlingWhitelist paths, handle partial editsopus
DocumentationUpdate hooks README with all validatorsopus

Test

Manual tests

TestExpectedOwnerActualLast
Write file with uppercase nameBlocked with “lowercase only” messagemvpending-
Write FR without frontmatterBlocked with “missing fields” messagemvpending-
Write idea with FR numberBlocked with “ideas don’t get FR numbers”mvpending-
Write FR to in-progress without FR numberBlockedmvpending-
Write valid fileAllowed (exit 0)mvpending-
Edit Python file with import mid-fileBlocked with “imports at top” messagemvpending-
Write Dutch content to vaultBlocked with “English only” messagemvpending-

AI-verified tests

ScenarioExpected behaviorVerification method

E2E tests

ScenarioAssertion

Integration tests

ComponentCoverage

Unit tests

ComponentTestsCoverage

History

DateEventDetails
2026-03-12CreatedBorn 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