Pre-commit hook re-staging loops block auto-commits across many repos #23

Closed
opened 2026-06-21 16:05:15 +00:00 by Mark · 0 comments
Owner

What this is

Multiple repos have pre-commit hooks that reformat staged files in place
(typically ruff-format or prettier). When the hook modifies a file
in-place during git commit, the file is now staged-with-different-content
than HEAD. Pre-commit's own check then detects "unstaged files detected"
because the modification is unstaged w.r.t. the index, and re-runs the
hook, which reformats again. The commit never completes.

This is a recurring pattern that has blocked commits across many repos
in the Hermes-managed fleet. Examples from session history:

  • pc2cad#37 — ruff-format re-stages files; --no-verify workaround
  • mesh-seed — check-python-version fails (Python 3.11 vs 3.12 req)
  • docker_backup#12, #22 — lint-staged runs tsc/vitest on 160 files
  • GSD2_agent#22 — .gsd/ tracked files cause conflicts
  • mc-hermes-mod#10 — pre-commit blocks commit gate
  • spnav-joystick, ironthrone, sadcop_joins_ice — same family

The runner-side workaround (git commit --no-verify) was added to
~/.hermes/scripts/quickfix-kilo to keep quickfix flowing, but that
bypasses the hooks entirely which defeats their purpose.

Reproduction (pc2cad as canonical case)

cd ~/Projects/pc2cad
echo 'x=1' > foo.py
git add foo.py
git commit -m test
# ruff-format reformats: x = 1
# pre-commit: 'unstaged files detected' (re-staged by hook)
# pre-commit: re-runs ruff-format
# loops

Expected

git commit should commit once after hooks run, regardless of whether
hooks re-stage files.

Actual

git commit runs hooks, hooks reformat, pre-commit detects
'unstaged files', re-runs hooks, etc. Loop. Manual --no-verify
works.

Suggested structural fix

Three layers, pick one or do all:

  1. Per-repo fix (cheapest): document the --no-verify workaround
    in each repo's .pre-commit-config.yaml and add
    pass_filenames: false to auto-formatting hooks (e.g. ruff-format)
    so they don't re-stage. Tracked in pc2cad#37 as canonical example.

  2. Runner-side enhancement (medium): add a pre-commit-loop detector
    to ~/.hermes/scripts/quickfix-kilo and kilo-gsd.py that:

    • detects the re-staging-loop signature in stderr
    • records the bypass event to runs.jsonl
    • retries with --no-verify only on loop detection, not by default
    • emits a loud warning log line so future sessions see why the
      bypass fired
      (Implemented in commit quickfix-kilo 2026-06-21 — pending
      cross-script rollout to kilo-gsd.py)
  3. Repo-agnostic sanitizer (best): add a hermes-managed
    ~/.hermes/bin/pre-commit-loop-detector.sh that wraps
    git commit, watches for re-staging loops, and either:

    • force-aborts after N iterations with a clean error, OR
    • auto-applies the loop fix once per repo and retries

Environment

  • pre-commit version: 3.x
  • ruff version: per .venv/bin/ruff --version
  • Reproduced on 2026-06-21 in quickfix-kilo session

Why I'm filing this here

This is bigger than any one repo. The runner-side patch
(commit-fallback in quickfix-kilo) is a band-aid. The right
fix is either per-repo (layer 1) or hermes-managed (layer 3),
and either way needs to be tracked centrally so the workaround
can eventually be retired.

## What this is Multiple repos have pre-commit hooks that reformat staged files in place (typically `ruff-format` or `prettier`). When the hook modifies a file in-place during `git commit`, the file is now staged-with-different-content than HEAD. Pre-commit's own check then detects "unstaged files detected" because the modification is unstaged w.r.t. the index, and re-runs the hook, which reformats again. The commit never completes. This is a recurring pattern that has blocked commits across many repos in the Hermes-managed fleet. Examples from session history: - **pc2cad#37** — `ruff-format` re-stages files; `--no-verify` workaround - **mesh-seed** — `check-python-version` fails (Python 3.11 vs 3.12 req) - **docker_backup#12, #22** — lint-staged runs tsc/vitest on 160 files - **GSD2_agent#22** — `.gsd/` tracked files cause conflicts - **mc-hermes-mod#10** — pre-commit blocks commit gate - **spnav-joystick, ironthrone, sadcop_joins_ice** — same family The runner-side workaround (`git commit --no-verify`) was added to `~/.hermes/scripts/quickfix-kilo` to keep quickfix flowing, but that bypasses the hooks entirely which defeats their purpose. ## Reproduction (pc2cad as canonical case) ```bash cd ~/Projects/pc2cad echo 'x=1' > foo.py git add foo.py git commit -m test # ruff-format reformats: x = 1 # pre-commit: 'unstaged files detected' (re-staged by hook) # pre-commit: re-runs ruff-format # loops ``` ## Expected `git commit` should commit once after hooks run, regardless of whether hooks re-stage files. ## Actual `git commit` runs hooks, hooks reformat, pre-commit detects 'unstaged files', re-runs hooks, etc. Loop. Manual `--no-verify` works. ## Suggested structural fix Three layers, pick one or do all: 1. **Per-repo fix (cheapest):** document the `--no-verify` workaround in each repo's `.pre-commit-config.yaml` and add `pass_filenames: false` to auto-formatting hooks (e.g. ruff-format) so they don't re-stage. Tracked in pc2cad#37 as canonical example. 2. **Runner-side enhancement (medium):** add a pre-commit-loop detector to `~/.hermes/scripts/quickfix-kilo` and `kilo-gsd.py` that: - detects the re-staging-loop signature in stderr - records the bypass event to runs.jsonl - retries with `--no-verify` only on loop detection, not by default - emits a loud warning log line so future sessions see why the bypass fired (Implemented in commit `quickfix-kilo` 2026-06-21 — pending cross-script rollout to kilo-gsd.py) 3. **Repo-agnostic sanitizer (best):** add a hermes-managed `~/.hermes/bin/pre-commit-loop-detector.sh` that wraps `git commit`, watches for re-staging loops, and either: - force-aborts after N iterations with a clean error, OR - auto-applies the loop fix once per repo and retries ## Environment - pre-commit version: 3.x - ruff version: per `.venv/bin/ruff --version` - Reproduced on 2026-06-21 in quickfix-kilo session ## Why I'm filing this here This is bigger than any one repo. The runner-side patch (`commit-fallback` in quickfix-kilo) is a band-aid. The right fix is either per-repo (layer 1) or hermes-managed (layer 3), and either way needs to be tracked centrally so the workaround can eventually be retired.
Mark 2026-07-31 05:01:51 +00:00
Sign in to join this conversation.
No description provided.