fix(scripts/githooks): prevent agents from bypassing git hooks (#22825)

Agents hit short shell timeouts on `git commit` (~13s) before
`make pre-commit` finishes (~20s warm), then disable hooks via
`git config core.hooksPath /dev/null`. This bypasses all local checks
and, because it writes to shared `.git/config`, silently disables hooks
for every other worktree too.

Add explicit timing guidance to AGENTS.md, and write worktree-scoped
`core.hooksPath` in post-checkout, pre-commit, and pre-push hooks to
make the bypass ineffective.
This commit is contained in:
Mathias Fredriksson
2026-03-09 12:51:44 +02:00
committed by GitHub
parent a48e4a43e2
commit dd34e3d3c2
4 changed files with 45 additions and 9 deletions
+24 -9
View File
@@ -105,22 +105,37 @@ app, err := api.Database.GetOAuth2ProviderAppByClientID(ctx, clientID)
### Full workflows available in imported WORKFLOWS.md
### Git Hooks (MANDATORY)
### Git Hooks (MANDATORY - DO NOT SKIP)
Before your first commit, ensure the git hooks are installed.
Two hooks run automatically:
**You MUST install and use the git hooks. NEVER bypass them with
`--no-verify`. Skipping hooks wastes CI cycles and is unacceptable.**
- **pre-commit**: `make pre-commit` (gen, fmt, lint, typos, build).
Fast checks that catch most CI failures.
- **pre-push**: `make pre-push` (full CI suite including tests).
Runs before pushing to catch everything CI would.
Wait for them to complete, do not skip or bypass them.
The first run will be slow as caches warm up. Consecutive runs are
**significantly faster** (often 10x) thanks to Go build cache,
generated file timestamps, and warm node_modules. This is NOT a
reason to skip them. Wait for hooks to complete before proceeding,
no matter how long they take.
```sh
git config core.hooksPath scripts/githooks
```
Two hooks run automatically:
- **pre-commit**: `make pre-commit` (gen, fmt, lint, typos, build).
Fast checks that catch most CI failures. Allow at least 5 minutes.
- **pre-push**: `make pre-push` (full CI suite including tests).
Runs before pushing to catch everything CI would. Allow at least
15 minutes (race tests are slow without cache).
`git commit` and `git push` will appear to hang while hooks run.
This is normal. Do not interrupt, retry, or reduce the timeout.
NEVER run `git config core.hooksPath` to change or disable hooks.
If a hook fails, fix the issue and retry. Do not work around the
failure by skipping the hook.
### Git Workflow
When working on existing PRs, check out the branch first:
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
# Shield this worktree against shared config hooksPath poisoning.
# Worktree-scoped config overrides the shared .git/config, so even if
# another worktree runs `git config core.hooksPath /dev/null`, this
# worktree continues to use the correct hooks.
#
# This hook runs on `git worktree add` and `git checkout`/`git switch`.
# Only needed in linked worktrees where shared config can be poisoned
# by another worktree. Skipped in the main checkout to avoid errors
# when extensions.worktreeConfig is not set (e.g. fresh clones).
if [[ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ]]; then
git config --worktree core.hooksPath scripts/githooks
fi
+4
View File
@@ -16,4 +16,8 @@ set -euo pipefail
cd "$(git rev-parse --show-toplevel)"
unset GIT_DIR
# In linked worktrees, set worktree-scoped hooksPath to override shared config.
if [[ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ]]; then
git config --worktree core.hooksPath scripts/githooks
fi
exec make pre-commit
+4
View File
@@ -19,4 +19,8 @@ set -euo pipefail
cd "$(git rev-parse --show-toplevel)"
unset GIT_DIR
# In linked worktrees, set worktree-scoped hooksPath to override shared config.
if [[ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ]]; then
git config --worktree core.hooksPath scripts/githooks
fi
exec make pre-push