-
Notifications
You must be signed in to change notification settings - Fork 0
Hooks and Guardrails
KMPilot ships two Claude Code hooks that enforce the architecture without you having to think about it. Both live in .claude/hooks/ and are wired up by .claude/settings.json (committed to the repo — no per-developer setup).
| Hook | Type | What it does |
|---|---|---|
protect-feature-files.sh |
PreToolUse (matcher Edit|Write) |
Blocks direct edits to feature/ source files unless an implementation skill is active |
reinject-on-compact.sh |
SessionStart (matcher compact) |
Re-injects the 11 critical rules and 4 integration points after Claude compacts the conversation, and clears any stale skill marker |
A PreToolUse hook intercepts every Edit or Write and checks the target path. If the path is under feature/, the edit is blocked with:
Blocked: Cannot edit feature source files directly.
Use /creating-kmp-feature or /modifying-kmp-feature skill first.
The hook allows the edit through (no skill marker required) for:
-
*/commonTest/*,*/desktopTest/*,*/androidTest/*,*/test/*— test files - Any
build.gradle.kts— so test-dependency setup and integration agent work
These exemptions exist because the test generation agents need to write directly and the integration agent needs to edit gradle files.
An implementation skill activates a marker file before editing, then removes it on completion:
# Activate
touch /tmp/.claude-kmpilot-skill-active
# (… do work …)
# Cleanup
rm -f /tmp/.claude-kmpilot-skill-active| Aspect | Value |
|---|---|
| Marker path | /tmp/.claude-kmpilot-skill-active |
| Staleness window | 2 hours — older markers are auto-removed by the hook |
| Skills that activate it |
/creating-kmp-feature, /modifying-kmp-feature
|
| Tools allowlisted to manage it |
Bash(touch:*), Bash(rm -f /tmp/.claude-kmpilot-skill-active)
|
Test agents write test files directly (bypassed by path rule), so they do not need the marker.
In long conversations Claude Code automatically compacts earlier messages to free context. During compaction, project-specific rules can disappear. The SessionStart hook with the compact matcher runs reinject-on-compact.sh, which:
- Removes any stale
/tmp/.claude-kmpilot-skill-activemarker. - Prints the 11 critical rules and 4 integration points so they re-enter context.
So Claude never "forgets" the project's patterns mid-session.
11 Rules:
1. Interface + Impl pairs for DataSource/Repository
2. Either<T> for errors - NEVER throw exceptions
3. setState { copy() } - NEVER _uiModel.value =
4. 4 UI states: Uninitialized / Loading / Success / Failed
5. X-components from :core:designsystem - NO Material3
6. ImmutableList with .toImmutableList()
7. Lowercase packages only
8. DI: singleOf(::Impl).bind<Interface>() + BaseFeature
9. No UseCases - ViewModels call repositories directly
10. Callback params (onBackClick) - not navController
11. Single *UiModel + DTO-wrapped UiState<T> - NO *UiState.kt; data/ never imports presentation/
4 Integration Points (all required):
1. settings.gradle.kts - include(":feature:{name}")
2. composeApp/build.gradle.kts - implementation(project(":feature:{name}"))
3. initKoin.kt - {Feature}Modules.initialize()
4. BaseAppNavHost.kt - {featurename}(onBackClick = {...})
Plus a pointer to @.claude/skills/_shared/patterns.md for the full reference.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-feature-files.sh"
}
]
}
],
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/reinject-on-compact.sh"
}
]
}
]
}
}$CLAUDE_PROJECT_DIR is set by Claude Code to the repository root, so the same settings.json works for every developer.
Without the hooks, the architecture relies on Claude remembering the rules — which works most of the time, but failure modes are unpredictable. The hooks make the rules enforceable:
- Skills are the only path that can edit
feature/source. - Long sessions don't drift away from the conventions.
- Tests and gradle files remain editable to keep iteration fast.
If you ever see "Blocked: Cannot edit feature source files directly," that's the system working as designed. Run the relevant skill instead.
Back to Home