{ "_comment": [ "InstructionsLoaded hook — logs which instruction file loaded, when, and WHY.", "Installed into ~/.claude/settings.json by scripts/install_workspace.py (merged, never replaced).", "load_reason is the field that answers 'why': session_start | nested_traversal |", "path_glob_match | include | compact. trigger_file_path names the file whose read caused it.", "The hook is observability-only; Claude Code does not let it block.", "Self-rotating at 5 MB, one generation kept, because the log is unbounded otherwise.", "Read it with scripts/rules_report.py — which rules fired, and which NEVER have." ], "hooks": { "InstructionsLoaded": [ { "hooks": [ { "type": "command", "command": "L=\"$HOME/.claude/instructions-loaded.jsonl\"; if [ -f \"$L\" ] && [ \"$(stat -c%s \"$L\" 2>/dev/null || echo 0)\" -gt 5242880 ]; then mv -f \"$L\" \"$L.1\"; fi; jq -c '{ts:(now|todate), file_path, memory_type, load_reason, trigger_file_path, parent_file_path, globs, session_id, cwd}' >> \"$L\" 2>/dev/null || true", "timeout": 5 } ] } ] } }