feat(hooks): session-start defensive checks for .claude.json + vault baseline (SDD-021, SDD-017b)#54
Merged
Conversation
…baseline (SDD-021, SDD-017b)
Two related defensive layers added to the SessionStart hook, both fall in the
same shape (passive monitor flagged via additionalContext) on the same file
(claude-session-start.{sh,ps1}).
SDD-021 -- .claude.json size monitor
- Detects truncation of ~/.claude/.claude.json below 10 KB threshold
(healthy ~75 KB, post-truncation ~1.5 KB).
- Catches recurrence of the deserialize-strip bug from PR #33 (trigger-level
fix) even if a different `claude` CLI subcommand introduces the same write
behavior (mcp add/remove, plugin uninstall, etc).
- Recovery hint with one-liner pointing to ~/.claude/backups/.
- Cross-references anthropics/claude-code#59870 (upstream issue).
SDD-017b -- Vault baseline integrity
- SDD-017 only catches working-tree D entries (unstaged deletes). Auto-commits
by the Obsidian-git plugin remove the deletion from `git status --short`
before the next session-start runs.
- New check: every 00_meta/skills/<name>/ dir must contain a non-empty
SKILL.md, plus a static list of always-required canonical files
(patterns/_index.md, skills/README.md, vault README.md, AGENTS.md).
- Triggered by 2026-05-13 + 2026-05-15 setup-linux skill-sync incidents
(fixed in PR #32 trigger-level).
- Recovery hint via `git log --diff-filter=D` + `git show <commit>:<path>`.
Validation:
- bash -n: clean
- End-to-end hook test (echo input JSON | bash script): healthy vault produces
no Vault baseline / .claude.json warnings (correct -- both pass through silently).
- .claude.json size 75359 bytes -> below-threshold logic returns 0 (correct).
- 7-byte simulated truncation -> below-threshold logic returns 1 (correct, would fire).
This was referenced May 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related defensive layers added to the
claude-session-start.{sh,ps1}hook — both passive monitors that surface state drift viaadditionalContextand never modify the watched files. Bundled in one PR because they share file + shape; functionally they cover two unrelated incident classes.SDD-021 —
.claude.jsonsize monitorDetects truncation of
~/.claude/.claude.jsonbelow a 10 KB threshold (healthy ~75 KB, post-truncation ~1.5 KB). Catches recurrence of the deserialize-strip bug (trigger-level fix landed in PR #33) even if a differentclaudeCLI subcommand introduces the same write behavior —mcp add/remove,plugin uninstall, etc. The bug is in the read/write layer, not the per-feature handlers, so any future surface can re-trigger it.Recovery hint emits a one-liner pointing to
~/.claude/backups/.claude.json.backup.*. Cross-references upstream issueanthropics/claude-code#59870(which I commented on with cross-file evidence; my own report#60407was closed as duplicate).SDD-017b — Vault baseline integrity
SDD-017 already catches working-tree
Dentries (unstaged deletes). It misses auto-commits: when the Obsidian-git plugin commits a deletion before the next session-start,git status --shortis clean but the canonical file is gone.This adds:
00_meta/skills/<name>/directory must contain a non-emptySKILL.md(catches the 2026-05-13 + 2026-05-15 setup-linux skill-sync incidents — trigger-level fix in PR fix(setup): stop deleting vault content via symlink follow in skill sync #32).00_meta/patterns/_index.md,00_meta/skills/README.md, vaultREADME.md, vaultAGENTS.md.Recovery hint via
git log --diff-filter=D --name-only+git show <commit>:<path>.Why one PR
Both sit in the same file (
claude-session-start.{sh,ps1}), follow the identical shape (function check_X/Test-X→ emit toCONTEXT_LINES/$script:ContextLines→ ordered between existing checks), and share a thematic surface (defensive monitors for state drift). Splitting into two PRs would force PR-3 to stack on PR-2 + rebase, with no real reviewer benefit. Diff is +152 LOC, well below the 300-LOC atomic limit.Changes
scripts/claude-session-start.sh—check_vault_baseline()+check_claude_json_size()+ their call sitesscripts/claude-session-start.ps1—Test-VaultBaseline+Test-ClaudeJsonSizePowerShell mirrorsTest plan
bash -n scripts/claude-session-start.sh→ cleanecho '{"cwd":"/home/manu/Projects/knowledge","session_id":"test"}' | bash scripts/claude-session-start.sh | jq→ healthy vault produces no Vault baseline /.claude.jsonwarnings (silent pass through both new functions).claude.jsonhealthy 75359 bytes → no fire (correct)