Skip to content

fix(GRA-1233): add PostToolUse auto_correct hook to claude_code adapter install#245

Open
Gradata wants to merge 2 commits into
mainfrom
fix/gra-1233-claude-code-posttooluse-hook
Open

fix(GRA-1233): add PostToolUse auto_correct hook to claude_code adapter install#245
Gradata wants to merge 2 commits into
mainfrom
fix/gra-1233-claude-code-posttooluse-hook

Conversation

@Gradata

@Gradata Gradata commented Jun 3, 2026

Copy link
Copy Markdown
Owner

gradata install --agent claude-code only installed PreToolUse (inject_brain_rules), missing PostToolUse (auto_correct) -- users got injection but no correction capture.

Fix:

  • Add auto_correct_command() in _base.py (mirrors hook_command)
  • install() now registers both PreToolUse and PostToolUse hooks
  • uninstall() cleans both event keys

Closes GRA-1233

data-engineer and others added 2 commits June 2, 2026 16:45
Zero RULE_PATCHED events in production; oscillation guard untriggered.
Identifies two persistent failure rules and seven burst-only candidates,
with freeze/demote recommendations and delta vs GRA-1382 baseline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er install

gradata install --agent claude-code only installed PreToolUse
(inject_brain_rules), missing the PostToolUse (auto_correct) hook that
captures Edit|Write events for correction learning. Users got injection
but no correction capture — core value prop broken at install.

Changes:
- _base.py: add auto_correct_command() helper mirroring hook_command()
- claude_code.py:install() — add PostToolUse hook with Edit|Write matcher
- claude_code.py:uninstall() — clean both PreToolUse and PostToolUse
  (iterates over both event keys instead of hardcoding PreToolUse only)

Closes GRA-1233

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough
  • Fix for GRA-1233: Added missing PostToolUse hook (auto-correct) to claude_code adapter install; previously only registered PreToolUse (inject brain rules)
  • New public API: Added auto_correct_command(brain_dir: Path) -> str function in _base.py to generate auto-correct shell commands
  • Updated install(): Now registers both PreToolUse and PostToolUse hooks with Edit|Write matcher for correction events
  • Updated uninstall(): Iterates over both PreToolUse and PostToolUse event keys to cleanly remove all registered hooks
  • Research document: Added patch-oscillation-2026-06-02.md analyzing patch convergence/oscillation metrics
  • No breaking changes; existing public function signatures remain unchanged

Walkthrough

This PR adds research findings on patch system convergence and oscillation (GRA-1477), identifying persistent multi-session failure candidates and hook reversion patterns, along with new infrastructure to manage auto-correct hook entries during agent initialization and cleanup in both install and uninstall operations.

Changes

Patch oscillation analysis and auto-correct hook wiring

Layer / File(s) Summary
Patch oscillation analysis and findings
Gradata/docs/research/patch-oscillation-2026-06-02.md
Research document analyzes patch-path events, hook reversion oscillation, and persistent multi-session failure candidates using live SQLite queries against system.db. Identifies two high-risk rules ("Include more detail about detail" and "Verify facts, numbers, and dates") with explicit freeze/demote criteria and updated recommendations including promoting "Include more detail about detail" to a meta-rule and selecting "Verify facts…" as the first controlled patch candidate.
Auto-correct hook infrastructure and lifecycle
Gradata/src/gradata/hooks/adapters/_base.py, Gradata/src/gradata/hooks/adapters/claude_code.py
New auto_correct_command() helper generates the shell command for running auto-correct with BRAIN_DIR. install() now injects both PreToolUse (brain rule) and PostToolUse (auto-correct) hook entries, tracking counts and returning total added hooks. uninstall() removes matching entries from both hook types, prunes empty event blocks when necessary, and returns the count of removed entries.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • Gradata/gradata#244: Contains the same patch-oscillation-2026-06-02.md research artifact and aligns directly with the patch-oscillation analysis findings.
  • Gradata/gradata#242: Modifies the same claude_code.py install/uninstall routines with signature matching and hook-block manipulation logic.

Suggested labels

bug

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly matches the PR's main objective: adding PostToolUse auto_correct hook to claude_code adapter install, and references the issue GRA-1233.
Description check ✅ Passed The description clearly explains the problem (missing PostToolUse hook), the fix (adding auto_correct_command and registering both hooks), and relates to the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/gra-1233-claude-code-posttooluse-hook
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch fix/gra-1233-claude-code-posttooluse-hook

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 OpenGrep (1.22.0)

OpenGrep fatal error (exit code 2):
┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m
[00.16][ERROR]: Error: exception Glob.Lexer.Syntax_error("malformed glob pattern: missing ']'")
Raised at Glob__Lexer.syntax_error in file "libs/glob/Lexer.mll", line 8, characters 2-26
Called from Glob__Lexer.__ocaml_lex_token_rec in file "libs/glob/Lexer.mll", line 29, characters 26-53
Cal


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

@coderabbitai coderabbitai Bot added the bug Something isn't working label Jun 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Gradata/src/gradata/hooks/adapters/claude_code.py`:
- Around line 79-94: The smoke test and adapter are out of sync:
tests/HOST_MATRIX for claude-code only expects "PreToolUse" so the new
PostToolUse wiring in Gradata/src/gradata/hooks/adapters/claude_code.py will not
be asserted, and the adapter uses a single matcher "Edit|Write" while
auto_correct.generate_hook_config() emits separate "Edit" and "Write" matchers
(risking accidental matches like "MultiEdit"). Fix by (1) updating the test
HOST_MATRIX entry for "claude-code" to include "PostToolUse" in expected_events
so the smoke matrix will catch regressions, and (2) change the PostToolUse hook
installation in claude_code.py to either add two distinct matcher entries
("Edit" and "Write") or use an exact-match regex like "^(Edit|Write)$" (adjust
the code that appends the dict using auto_correct_command and sig) to match
auto_correct.generate_hook_config() semantics exactly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f3b78391-dbe5-4204-9bfd-02bcb3744987

📥 Commits

Reviewing files that changed from the base of the PR and between b5cdb4b and 40e9692.

📒 Files selected for processing (3)
  • Gradata/docs/research/patch-oscillation-2026-06-02.md
  • Gradata/src/gradata/hooks/adapters/_base.py
  • Gradata/src/gradata/hooks/adapters/claude_code.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
Gradata/src/**/*.py

📄 CodeRabbit inference engine (Gradata/AGENTS.md)

Gradata/src/**/*.py: Prefer sentence-transformers for local embeddings, google-genai for Gemini embeddings, cryptography for AES-GCM encrypted system.db, bm25s for BM25 rule ranking, and mem0ai for external memory adapters — guard all optional dependency imports with try / except ImportError at the call site, never at module level
Maintain strict layering: Layer 0 (Primitives: _types.py, _db.py, _events.py, _paths.py, _file_lock.py; Patterns: contrib/patterns/) must never import from Layer 1 (Enhancements: enhancements/, rules/) or Layer 2 (Public API: brain.py, cli.py, daemon.py, mcp_server.py)
Never use bare except: pass — use typed exceptions or at minimum logger.warning(...) with exc_info=True to avoid silent failure in a memory product
Never import from out-of-scope sibling directories ../Sprites/ or ../Hausgem/ within gradata/* code — that is a layering bug
Never leak private-sibling paths into public docs/code — no references to ../Sprites/, ../Hausgem/, email addresses, OneDrive paths, or Sprites-specific examples from inside gradata/*
Use atomic-write helper when writing JSON files to prevent corruption from mid-write crashes

Files:

  • Gradata/src/gradata/hooks/adapters/_base.py
  • Gradata/src/gradata/hooks/adapters/claude_code.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: Gradata
Repo: Gradata/gradata PR: 0
File: :0-0
Timestamp: 2026-04-17T17:18:07.439Z
Learning: In PR `#102` (gradata/gradata), Round 2 addressed: cli.py env-first brain resolution (GRADATA_BRAIN > --brain-dir > cwd), _tenant.py corrupt .tenant_id overwrite, _env_int default clamping to minimum, and _events.py tenant-scoped fallback SELECT for dedup. All ruff and 99 tests green after these fixes.
📚 Learning: 2026-05-01T15:50:32.772Z
Learnt from: CR
Repo: Gradata/gradata PR: 0
File: Gradata/AGENTS.md:0-0
Timestamp: 2026-05-01T15:50:32.772Z
Learning: Use `from gradata import Brain` as the public entry point — `brain.correct()` is THE entry point for the headline product promise

Applied to files:

  • Gradata/src/gradata/hooks/adapters/_base.py
🪛 LanguageTool
Gradata/docs/research/patch-oscillation-2026-06-02.md

[uncategorized] ~142-~142: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...cific content fragments (code snippets, markdown table rows, PR references) | Content-ch...

(MARKDOWN_NNP)


[uncategorized] ~167-~167: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ... description contains code fragments or markdown table syntax 2. Add session-burst filte...

(MARKDOWN_NNP)

🪛 markdownlint-cli2 (0.22.1)
Gradata/docs/research/patch-oscillation-2026-06-02.md

[warning] 166-166: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 171-171: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 172-172: Ordered list item prefix
Expected: 1; Actual: 4; Style: 1/2/3

(MD029, ol-prefix)


[warning] 173-173: Ordered list item prefix
Expected: 2; Actual: 5; Style: 1/2/3

(MD029, ol-prefix)


[warning] 174-174: Ordered list item prefix
Expected: 3; Actual: 6; Style: 1/2/3

(MD029, ol-prefix)

🔇 Additional comments (4)
Gradata/docs/research/patch-oscillation-2026-06-02.md (1)

1-197: LGTM!

Gradata/src/gradata/hooks/adapters/_base.py (1)

139-143: LGTM!

Gradata/src/gradata/hooks/adapters/claude_code.py (2)

56-104: LGTM!


107-153: LGTM!

Comment on lines +79 to +94
# PostToolUse: auto_correct (Edit|Write capture)
post_tool = hooks.setdefault("PostToolUse", [])
post_present = any(sig in str(item) for item in post_tool)
if not post_present:
post_tool.append(
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": auto_correct_command(brain_dir),
"id": sig,
}
],
}
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1) Inspect HOST_MATRIX expected_events for claude-code in the smoke test
fd -t f 'test_install_smoke_matrix.py' --exec rg -n -C3 'HOST_MATRIX|expected_events|claude-code|PostToolUse|PreToolUse' {}

# 2) Compare matcher shapes between adapter install and generate_hook_config
rg -nP -C3 "matcher" --type=py -g '!**/tests/**' gradata/src/gradata/hooks 2>/dev/null || \
  rg -nP -C3 "matcher" --type=py

Repository: Gradata/gradata

Length of output: 21624


Fix smoke-matrix coverage for Claude Code PostToolUse + clarify matcher semantics

  1. tests/test_install_smoke_matrix.py sets HOST_MATRIX for claude-code to expected_events={"PreToolUse"}; since the test only checks expected_events <= set(hooks), it will not assert the new PostToolUse wiring—so a regression in Gradata/src/gradata/hooks/adapters/claude_code.py won’t fail the smoke test.
  2. claude_code.py installs a single PostToolUse entry with matcher "Edit|Write", while auto_correct.generate_hook_config() emits two separate matchers "Edit" and "Write". Confirm that Claude Code’s matcher evaluation treats "Edit|Write" as intended and does not accidentally trigger on MultiEdit (or add a test to lock this down).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Gradata/src/gradata/hooks/adapters/claude_code.py` around lines 79 - 94, The
smoke test and adapter are out of sync: tests/HOST_MATRIX for claude-code only
expects "PreToolUse" so the new PostToolUse wiring in
Gradata/src/gradata/hooks/adapters/claude_code.py will not be asserted, and the
adapter uses a single matcher "Edit|Write" while
auto_correct.generate_hook_config() emits separate "Edit" and "Write" matchers
(risking accidental matches like "MultiEdit"). Fix by (1) updating the test
HOST_MATRIX entry for "claude-code" to include "PostToolUse" in expected_events
so the smoke matrix will catch regressions, and (2) change the PostToolUse hook
installation in claude_code.py to either add two distinct matcher entries
("Edit" and "Write") or use an exact-match regex like "^(Edit|Write)$" (adjust
the code that appends the dict using auto_correct_command and sig) to match
auto_correct.generate_hook_config() semantics exactly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant