Skip to content

feat(hooks): session-start defensive checks for .claude.json + vault baseline (SDD-021, SDD-017b)#54

Merged
mlorentedev merged 2 commits into
mainfrom
feat/SDD-021-017b-session-start-defensive
May 19, 2026
Merged

feat(hooks): session-start defensive checks for .claude.json + vault baseline (SDD-021, SDD-017b)#54
mlorentedev merged 2 commits into
mainfrom
feat/SDD-021-017b-session-start-defensive

Conversation

@mlorentedev
Copy link
Copy Markdown
Owner

Summary

Two related defensive layers added to the claude-session-start.{sh,ps1} hook — both passive monitors that surface state drift via additionalContext and never modify the watched files. Bundled in one PR because they share file + shape; functionally they cover two unrelated incident classes.

SDD-021 — .claude.json size monitor

Detects truncation of ~/.claude/.claude.json below 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 different claude CLI 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 issue anthropics/claude-code#59870 (which I commented on with cross-file evidence; my own report #60407 was closed as duplicate).

SDD-017b — Vault baseline integrity

SDD-017 already catches working-tree D entries (unstaged deletes). It misses auto-commits: when the Obsidian-git plugin commits a deletion before the next session-start, git status --short is clean but the canonical file is gone.

This adds:

  • (a) Every 00_meta/skills/<name>/ directory must contain a non-empty SKILL.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).
  • (b) Static list of always-required canonical files must exist: 00_meta/patterns/_index.md, 00_meta/skills/README.md, vault README.md, vault AGENTS.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 to CONTEXT_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.shcheck_vault_baseline() + check_claude_json_size() + their call sites
  • scripts/claude-session-start.ps1Test-VaultBaseline + Test-ClaudeJsonSize PowerShell mirrors

Test plan

  • bash -n scripts/claude-session-start.sh → clean
  • End-to-end hook smoke: echo '{"cwd":"/home/manu/Projects/knowledge","session_id":"test"}' | bash scripts/claude-session-start.sh | jq → healthy vault produces no Vault baseline / .claude.json warnings (silent pass through both new functions)
  • .claude.json healthy 75359 bytes → no fire (correct)
  • Simulated 7-byte truncation via mktemp → fire (correct)
  • No skill dirs missing SKILL.md in current vault → baseline silent (correct)
  • No critical canonical files missing → baseline silent (correct)
  • Manual simulation of skill-dir-without-SKILL.md (reviewer can do this; I haven't to avoid touching vault state)

…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).
@mlorentedev mlorentedev merged commit 0f46973 into main May 19, 2026
5 checks passed
@mlorentedev mlorentedev deleted the feat/SDD-021-017b-session-start-defensive branch May 19, 2026 02:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant