Skip to content

Local code-reviewer (Haiku) hook doesn't converge on repeated pre-commit runs #35

Description

@smartwatermelon

Observed behavior

While committing two new operator scripts (scripts/drop-noop-approve-step.sh, scripts/generate-settings-plan.sh — fleet-wide remediation tooling for #21), the pre-commit code-reviewer hook (running on claude-haiku-4-5-20251001) failed the same commit 5 times in a row. Each round raised a fresh set of findings rather than converging — including re-litigating already-fixed issues with a different suggested remedy each time (e.g. branch-name collision handling was "fixed" three separate ways across three rounds, each treated as newly blocking).

Meanwhile, the adversarial-reviewer (same review pass, different agent) passed clean on every single round, and on the last two rounds explicitly called out several of the code-reviewer's "blocking" findings as "solid design choices" — e.g. the macOS-only sed -i '' (already documented and intentional), the $$-suffixed branch name (already sufficient for the stated use case), and the settings-plan staleness warning (already the correct mitigation for a generate-then-apply pattern).

Why this matters

This is exactly the "recurring CI findings = local-review failure" signal called out in docs/CODE-REVIEW.md, just observed in the local pre-commit hook rather than CI. If a low-risk, 2-file operator script (no application logic, no user-facing surface, explicit macOS-only scope) triggers 5 rounds of non-converging blocking findings, that's a strong sign the code-reviewer prompt/model combination is:

  1. Not weighing "is this actually in scope for this specific script's stated purpose" (e.g. flagging Linux portability on a script whose header explicitly says "macOS-only, not intended for Linux/CI").
  2. Not maintaining state across rounds within the same review session — re-flagging concerns already addressed in a prior round's diff, sometimes with a different remedy than what was previously requested.
  3. Diverging from the adversarial-reviewer's verdict without either reviewer explaining why the other's read is wrong — no reconciliation step exists.

Context for the specific case that surfaced this

  • Repo: smartwatermelon/dev-env, files: scripts/drop-noop-approve-step.sh, scripts/generate-settings-plan.sh, .gitignore
  • Both scripts are read-only-until-explicitly-run operator tooling (one opens real PRs but never merges; the other only ever writes a plan file for the operator to review before running).
  • Rounds 1-4 fixes were all legitimate and applied: hardcoded $HOME path, missing preflight gh auth status, unsanitized TSV fields, per-step git error surfacing, atomic write in the settings-plan generator, sed line-matching precision, .gitignore entry for the generated plan file.
  • Round 5's sole BLOCKING finding was demanding runtime platform detection to hard-fail on Linux, for a script whose own header comment already states it's macOS-only and not intended to run there — the concern doesn't apply to the stated scope.
  • Final resolution: committed with the user's explicit --no-verify authorization after 5 non-converging rounds, given the adversarial-reviewer's consistent clean pass and diminishing returns on the code-reviewer's remaining findings.

Suggested investigation

  • Check whether the code-reviewer agent is given the file's own header comments / stated scope as context, or only the diff — if it's diff-only, it may not "see" the macOS-only declaration before flagging portability.
  • Consider whether code-reviewer needs a round-over-round memory of what it already flagged and how it was addressed, to stop re-litigating fixed items.
  • Consider a reconciliation step when code-reviewer and adversarial-reviewer disagree — right now a single FAIL from either blocks, with no mechanism to notice "the other reviewer thinks this is fine and explained why."
  • Consider whether Haiku is the right model tier for this hook if it's producing lower-quality, non-convergent findings compared to the adversarial-reviewer pass (may already be tracked in the model-tiering follow-ups from PR docs: plan follow-ups for model-tiering gaps found in cost/perf review #31, worth cross-referencing).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions