Skip to content

feat: smarter diff filtering for minDiffLines + pass cache (hook v1.2.0) - #4

Open
darrylmorley wants to merge 2 commits into
mainfrom
claude/project-orchestration-architecture-6zdp49
Open

feat: smarter diff filtering for minDiffLines + pass cache (hook v1.2.0)#4
darrylmorley wants to merge 2 commits into
mainfrom
claude/project-orchestration-architecture-6zdp49

Conversation

@darrylmorley

@darrylmorley darrylmorley commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Fixes #1

Problem

Two sources of wasted/misleading QA runs:

  1. The minDiffLines skip threshold counted every non-empty line of the diff — including diff --git/index/@@ metadata and unchanged context lines — so it barely reflected meaningful change. Import reordering could trigger a review while a one-line logic fix could still be dominated by noise.
  2. Every Stop re-reviewed the full uncommitted diff even when nothing had changed since the last verdict — e.g. asking Claude a question while sitting on an already-reviewed dirty tree paid for a full Ollama inference again.

Changes

hookqa-hook.ts (v1.0.0 → v1.2.0)

Smarter diff counting (#1):

  • Replaced countDiffLines with countMeaningfulDiffLines, which counts only +/- change lines (file headers +++/---, metadata, and context lines are ignored).
  • Filters out noise before counting: blank lines, comment-only lines (//, /* */, * continuations, # style, <!-- -->), and import-only lines (JS/TS/Swift/Python/Java import, Python from … import, JS re-exports, CommonJS require, Ruby require, C/ObjC #include/#import, C# using, Rust/PHP use, Swift @testable import).
  • Whitespace-only edits and pure line moves cancel out: within each hunk, removed/added lines with identical whitespace-normalized content are matched as multisets and contribute 0. Moves across hunks or files still count.
  • Careful edges: #include is filtered as an import, but #define and #! shebangs still count as meaningful; *ptr = x; is not mistaken for a block-comment continuation.
  • SKIPPED log entries now read Diff too small (N meaningful lines, min M).

Pass cache (skip redundant reviews):

  • After a review that doesn't block, the hook stores a SHA-256 fingerprint of the diff plus every config field that can change the outcome (model, temperature, weights, custom instructions, blockOnWarnings) in a per-project file under /tmp (same lifecycle as the existing retry files).
  • On the next Stop, an identical fingerprint skips the Ollama call entirely and logs SKIPPED: Unchanged diff already reviewed — skipping.
  • Any config change or diff change invalidates the cache; ERROR and blocking reviews never populate it, so a FAIL is always re-reviewed after fixes.

Structural:

  • The entry point is now guarded by import.meta.main so the pure functions are importable by tests without executing the hook. Runtime behavior is unchanged and the never-block-Claude-Code exit-0 safety path is preserved. Still fully standalone — Bun built-ins only.
  • Version banner bumped to v1.2.0, so installed copies show the update prompt in the app's Hook tab.

tests/hookqa-hook.test.ts (new)

26 bun test cases: realistic multi-file diffs, per-language import filtering, whitespace cancellation, move semantics within/across hunks, multiset handling of duplicate lines, comment/preprocessor edge cases, fingerprint determinism and sensitivity (changes for diff/model/temperature/weights/instructions/blockOnWarnings; stable for unrelated fields like timeout and logging), and cache-path hygiene.

BehaviourTab.swift

Updated the "Min Diff Lines" slider description to reflect the new semantics (imports, comments, and whitespace don't count).

Behavioral note

Meaningful-line counts are lower than raw counts, so an unchanged minDiffLines value is now effectively stricter — more trivial stops will skip QA. Combined with the pass cache, repeat stops on an unchanged tree no longer hit Ollama at all. Both are the intent of #1, but existing configs will notice the shift.

Testing

  • bun test tests/ — 26 pass, 0 fail.
  • Smoke-ran the hook end-to-end via stdin (exits 0 cleanly with no model configured).
  • Verified importing the script has no side effects (guard works).
  • Swift change is a one-line string edit; not compiled here (no macOS toolchain in this environment) — worth a local xcodebuild before release.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GaLnfPXe58Gthpy6RoCwMJ

claude added 2 commits August 5, 2026 07:25
Replace raw line counting with countMeaningfulDiffLines: only +/- change
lines are considered (git metadata and context lines are ignored), and
import-only, comment-only, and blank lines are filtered out. Within each
hunk, removed/added lines with identical whitespace-normalized content
cancel out, so reindents and pure line moves no longer count toward the
threshold.

The hook script is now guarded by import.meta.main so its pure functions
can be imported by the new bun test suite without executing the hook.
Script version bumped to v1.1.0 so the app offers the update.

Fixes #1

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GaLnfPXe58Gthpy6RoCwMJ
Fingerprint the diff plus review-relevant config (model, temperature,
weights, custom instructions, blockOnWarnings) and store it per-project
in /tmp after a non-blocking review. Subsequent stops with an unchanged
diff skip the Ollama call and log SKIPPED. Config changes invalidate the
cache; ERROR and blocking reviews never populate it. Bumps hook to v1.2.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GaLnfPXe58Gthpy6RoCwMJ
@darrylmorley darrylmorley changed the title feat: smarter diff filtering for minDiffLines threshold feat: smarter diff filtering for minDiffLines + pass cache (hook v1.2.0) Aug 5, 2026
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.

Smarter diff filtering for minDiffLines threshold

2 participants