Skip to content

Native host hooks: planr prime injection and session guards#17

Merged
regenrek merged 8 commits into
mainfrom
hooks-native-integration
Jul 7, 2026
Merged

Native host hooks: planr prime injection and session guards#17
regenrek merged 8 commits into
mainfrom
hooks-native-integration

Conversation

@regenrek

@regenrek regenrek commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Wires Planr into the native hook systems of Cursor, Claude Code, and Codex CLI — installed by default, never mandatory (--no-hooks opts out, everything fails open). The problem this solves: "state survives sessions" was discipline (a skill had to remember to read map state); now it is mechanism — every new session, including post-compaction session starts, gets the map state injected automatically.

Reviewed by an independent GPT-5.5 subagent across three rounds (1 blocker, 2 majors, 4 minors found and fixed); final verdict complete.

planr prime (new command)

  • One compact, bounded, deterministic state block: project, map counts, up to five held items with completion-log status (80-char titles, overflow line), goal contract (char-safe truncation — no panics on non-ASCII), registry presence, next command.
  • Host envelopes: --hook-json (Claude SessionStart hookSpecificOutput.additionalContext), --cursor-json (Cursor additional_context), plain stdout for Codex.
  • In a repo without a Planr database it exits silently before opening the db — hooks fire in every repo, and prime must neither fail a session nor create stray .planr directories.

Default hook installation (planr install codex|claude|cursor)

  • Cursor .cursor/hooks.json: sessionStart → prime --cursor-json; subagentStop → evidence guard.
  • Claude .claude/settings.json: SessionStart (matcher startup|resume|compact) → prime --hook-json.
  • Codex .codex/hooks.json: SessionStart → prime, with the one-time /hooks trust note in the install output.
  • Only session-start events are wired, deliberately: they are where all three hosts inject hook output as context; pre/post-compaction events cannot restore model context (review finding) and Claude's compact source covers the compaction case.
  • Reconciling merge: foreign entries are never touched; planr-owned entries (matched by marker) are added when absent, upgraded in place when outdated, and removed from retired events — so re-installs after a planr update refresh the hooks instead of leaving stale ones. Unparseable files are left byte-untouched with a warning.
  • Fail open: every hook command ends in || true with a 10s timeout. --no-hooks skips hook installation entirely.

Evidence guard (Cursor subagentStop)

  • Advisory and identity-scoped: only fires for the stopping worker's own pick (via PLANR_WORKER_ID/PLANR_SESSION_ID); silent without an explicit identity so no agent is steered toward foreign items. JSON built by jq -nc --arg (never string interpolation), always exit 0, shellcheck-clean, refreshed on re-install when the shipped script changed.

Docs

  • New docs/HOOKS.md: per-host table, the session-start-only rationale, fail-open/reconciling-merge/default-not-mandatory rules, the Codex trust model, removal path. CLI_REFERENCE, README, changelog, drift fixture updated.

Dogfood

  • Built through Planr's own plan (4 items + 1 review-fix item, all closed with log evidence), using the 1.2.0 work-type annotations in the task list. The planr repo itself ships .cursor/hooks.json + the guard.

Testing

  • cargo test: 42 unit + 66 e2e green — prime content/envelopes/no-db silence/unicode contract, default hook writes, foreign-entry preservation, idempotent re-install, stale-hook upgrade path (old broken entry upgraded in place, retired event cleaned, old guard refreshed), Claude settings preservation, Codex trust note, --no-hooks skip, broken-file protection, and direct guard execution (anonymous/foreign-worker silence, valid followup JSON for own unlogged pick, silence after evidence, exit 0 always).
  • cargo clippy --all-targets clean; guard passes shellcheck.
  • Independent GPT-5.5 review: round 1 found the Cursor envelope blocker + unicode panic + guard mistargeting; round 2 verified those and found the stale upgrade path; round 3 verdict complete with no remaining actionable findings.
Open in Web Open in Cursor 

@regenrek regenrek marked this pull request as ready for review July 7, 2026 12:26
@regenrek regenrek merged commit 48f92ba into main Jul 7, 2026
3 checks passed
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