Skip to content

fix(hook): post-commit sync reminder must use hookSpecificOutput envelope#169

Closed
jinhongkuan wants to merge 1 commit into
devfrom
fix/post-commit-hook-envelope
Closed

fix(hook): post-commit sync reminder must use hookSpecificOutput envelope#169
jinhongkuan wants to merge 1 commit into
devfrom
fix/post-commit-hook-envelope

Conversation

@jinhongkuan

Copy link
Copy Markdown
Contributor

Summary

The PostToolUse/Bash hook installed by setup_wizard._install_claude_hooks is supposed to nudge the agent to run /bicameral:sync (or call bicameral.link_commit) after every git write-op. Its inline python3 -c one-liner prints "bicameral: new commit detected — run /bicameral:sync …" to stdout. The bicameral-sync skill watches for that exact phrase.

Per Claude Code 2.x hook docs:

For most events, stdout is written to the debug log but not shown in the transcript. The exceptions are UserPromptSubmit, UserPromptExpansion, and SessionStart, where stdout is added as context that Claude can see and act on.

PostToolUse is not in that exception list. The hook has been silently broken since Claude Code 2.x — its output never reaches the model. Symptom in the e2e harness: in Flow 3 the agent commits but never follows through to call link_commit / /bicameral:sync, and the ledger assertion fails:

"no compliance_check rows written (0→0) and no verdicts written. Either the bound decisions never had their sync triggered (no bicameral call after HEAD moves) ..."

Fix

Move the inline command to a proper script file that emits the structured envelope:

{"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": "..."}}

Reminder text preserves the canonical "bicameral: new commit detected" prefix so the bicameral-sync skill's existing trigger keeps matching.

What's in this PR

  • scripts/hooks/post_commit_sync_reminder.py — new hook script. Reads {tool_name, tool_input} from stdin; if tool_name == Bash and the command contains git commit / git merge / git pull / git rebase --continue (the legacy tuple, byte-for-byte), emits the envelope.
  • tests/test_post_commit_sync_hook.py — 11 cases covering each git write-op verb, read-only git commands, non-Bash tools, non-git Bash, malformed stdin, missing/non-dict tool_input, and idempotency.
  • pyproject.tomlbicameral-mcp-post-commit-sync-reminder console script.
  • setup_wizard.py_BICAMERAL_POST_COMMIT_COMMAND now references the console script. Existing _install_claude_hooks merge logic and dedup-by-substring pattern are unchanged.
  • .claude/settings.json — dogfood entry invokes the source script via python3 (mirrors the existing UserPromptSubmit dogfood line).

The e2e harness's _BICAMERAL_POST_COMMIT_COMMAND import is unchanged because the value changed, not the name.

Validation

  • ruff check . — clean (216 files).
  • ruff format --check . — clean (216 files).
  • pytest tests/test_post_commit_sync_hook.py tests/test_preflight_hook.py tests/test_preflight_intent.py — 22/22 PASS.
  • Setup-wizard byte-stability: _install_claude_hooks produces identical settings.json on repeated calls.

Test plan

  • CI: e2e assertions (auto) — Flow 3's ledger assertion (compliance_check rows written) reliably PASSes.
  • CI: ruff + mypy passes.
  • CI: MCP Regression Suite (ubuntu + windows) passes.

Companion PR

#168 applies the same envelope fix to a different PostToolUse hook (the new one on mcp__bicameral__bicameral_preflight for Flow 2a / #154). Both PRs surfaced from the same root cause (plain stdout silently dropped); each addresses a distinct broken hook. Either can land first.

…lope

The PostToolUse/Bash hook installed by setup_wizard prints
"bicameral: new commit detected — run /bicameral:sync ..." after every
git write-op. The bicameral-sync skill watches for that exact prefix
as one of its trigger signals.

Per Claude Code 2.x hook docs (https://code.claude.com/docs/en/hooks),
plain stdout from PostToolUse hooks is silently dropped to the debug
log — only UserPromptSubmit / UserPromptExpansion / SessionStart treat
raw stdout as agent-visible context. Symptom in the e2e harness: the
agent commits in Flow 3 but never follows through to call link_commit
or /bicameral:sync because the reminder never reaches the model. Flow
3's ledger assertion then fails: "no compliance_check rows written
(0→0) and no verdicts written. Either the bound decisions never had
their sync triggered (no bicameral call after HEAD moves) ..."

Fix: move the inline `python3 -c` one-liner to a proper script file
that emits the structured envelope:

  {"hookSpecificOutput": {"hookEventName": "PostToolUse",
                           "additionalContext": "<reminder text>"}}

The reminder text preserves the canonical "bicameral: new commit
detected" prefix verbatim so the bicameral-sync skill's trigger keeps
matching.

Files:
- scripts/hooks/post_commit_sync_reminder.py — new hook script
- tests/test_post_commit_sync_hook.py — 11 cases (commit, merge, pull,
  rebase --continue, read-only-git, non-Bash tool, non-git Bash,
  malformed stdin, missing/non-dict tool_input, idempotent)
- pyproject.toml — bicameral-mcp-post-commit-sync-reminder console script
- setup_wizard.py — _BICAMERAL_POST_COMMIT_COMMAND now refs the console
  script; existing _install_claude_hooks merge logic unchanged
- .claude/settings.json — dogfood entry invokes the source script via
  python3 (mirrors existing UserPromptSubmit dogfood line)

The e2e harness's _BICAMERAL_POST_COMMIT_COMMAND import is unchanged
because the constant's value is what changed, not its name.

Companion to #168 which applies the same envelope fix to the new
PostToolUse hook on bicameral_preflight (#154 / Flow 2a).
@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81aeb413-bb52-4bf7-afe3-5b618193368a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/post-commit-hook-envelope

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jinhongkuan

Copy link
Copy Markdown
Contributor Author

Combined into #171 (one commit cherry-picked from this branch's f536bf9). Closing in favor of the bundled PR.

@jinhongkuan jinhongkuan closed this May 3, 2026
@jinhongkuan jinhongkuan deleted the fix/post-commit-hook-envelope branch May 3, 2026 23:05
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