Skip to content

feat(hooks): add pre-commit guard for generated hook config sync #266

Description

@Bad3r

Summary

Add a pre-commit guard that keeps the generated hook configuration synchronized whenever hook-related source files change. The guard should refresh the local generated hook state offline, fail the current commit if the refresh changed any active hook files, and otherwise allow the commit to continue.

Problem

Repository hook behavior is generated from Nix sources and then consumed through local files such as .pre-commit-config.yaml, the shared fallback config under the Git hooks directory, and the installed pre-commit / pre-push hook scripts. When source files that define hook behavior change, the commit path should detect whether the generated local hook state needs to be refreshed before accepting the commit.

Proposed Behavior

Implement a file-triggered pre-commit guard with this flow:

  1. Detect hook/devshell-related source changes.
  2. Run an offline sync/evaluation.
  3. If the generated config or shared fallback config changed, fail the commit with a clear message: hooks were refreshed; retry commit.
  4. If nothing changed, continue with the commit.

The hook should be deterministic and offline-only. It must not require network access during normal commit flow.

Trigger Scope

The guard should run when staged changes affect files that can change generated hook behavior, including at minimum:

  • flake.nix
  • flake.lock
  • modules/devshell.nix
  • modules/meta/pre-commit.nix
  • modules/meta/hooks/**
  • modules/development/formatter.nix
  • scripts/hooks/**

Keep the trigger set tight enough that ordinary value-level application changes do not pay the cost of hook sync.

Implementation Outline

Add a custom hook, for example pre-commit-config-sync, wired through modules/meta/pre-commit.nix.

The hook entry should:

  1. Resolve the repository root and Git common directory.

  2. Record checksums for active generated hook files, at minimum:

    • .pre-commit-config.yaml
    • $(git rev-parse --git-path hooks)/pre-commit-config.yaml
    • $(git rev-parse --git-path hooks)/pre-commit
    • $(git rev-parse --git-path hooks)/pre-push
  3. Run:

    nix develop --accept-flake-config --offline -c true
  4. Recompute the same checksums.

  5. If any checksum changed, print a concise failure explaining that hooks were refreshed and the commit should be retried, then exit nonzero.

  6. If all checksums are unchanged, exit zero.

The hook should include enough changed-file output to make the result actionable without producing noisy diffs.

Acceptance Criteria

  • A staged change to hook/devshell-related source files triggers the guard.
  • When offline sync updates .pre-commit-config.yaml or the shared Git hook fallback config, the commit fails with the exact core message: hooks were refreshed; retry commit.
  • When offline sync produces no generated hook changes, the commit proceeds normally.
  • The generated pre-commit config validates after sync.
  • The installed pre-commit and pre-push scripts continue to resolve the worktree-local config first and the shared fallback config second.
  • The guard does not run for unrelated staged files.
  • The guard does not require network access.

Test Plan

  • Run the new hook directly against a hook-related staged file and verify it invokes offline sync.

  • Exercise a case where generated hook files change and confirm the commit aborts with the retry message.

  • Exercise a case where generated hook files do not change and confirm the hook exits zero.

  • Run:

    nix develop --accept-flake-config --offline -c pre-commit validate-config .pre-commit-config.yaml
  • Confirm no vulnix, removed hook, or stale hook entries can remain in the generated active hook list after a refresh.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area(hooks)Pre-commit, pre-push, and other repository hook logic.focus(validation)Build checks, assertions, verification flows, or validation failures.input(git-hooks)Flake input cachix/git-hooks.nix.priority(p3)Normal priority.status(backlog)Accepted work that is intentionally unscheduled.type(enhancement)Net-new capability or intentional improvement.

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions