feat(hooks): track + auto-install the repo-dev pre-commit gate (Closes #15) - #16
Merged
Merged
Conversation
…#15) The repo's own dev pre-commit gate (ruff/pytest/mypy) was an UNTRACKED, hand-placed .git/hooks/pre-commit, so a fresh clone / another machine got NO gate — only the `3d init` tracked template, which is a DIFFERENT hook (it gates a user's .scad project via `3d check`, not this repo's Python dev). Add a tracked source + an idempotent installer so every contributor gets it: - scripts/hooks/pre-commit — TRACKED dev gate: runs `3d test` (ruff -> pytest -> mypy, the canonical gate via tests/run_gate.py, so it never drifts). Fails CLOSED if ./bin/3d is missing (a gate that silently passes is worse than none). - scripts/install-dev-hooks.sh — copies it into the COMMON git hooks dir (`git rev-parse --git-common-dir`, correct for linked worktrees + submodules, not shadowed by a global core.hooksPath). Idempotent; backs up a differing pre-existing hook to .bak (never clobbers an existing .bak); repairs a cleared exec bit even on the up-to-date path; warns if a foreign core.hooksPath would bypass the gate. Preserves an existing inline global-hooks-dispatcher prefix (only a well-formed `... || exit $?` block) so a repo whose LOCAL core.hooksPath=.git/hooks bypasses the composer keeps its secret-scan. - README — a Contributing section documenting the install one-liner, the working-tree (not staged-snapshot) semantics, fail-closed behavior, and how it coexists with the `3d init` user-facing .scad template. Distinct from `3d init`'s assets/templates/pre-commit: that gates a user's .scad project; this gates this repo's own Python source. They live in different repos and never collide. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQNpHiYWBHHQyMJP34BdHH
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d4d15990a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
alex-mextner
added a commit
that referenced
this pull request
Jun 23, 2026
…oses #15) Codex CI review (PR #16) flagged a real worktree trap: a RELATIVE core.hooksPath=.git/hooks works in the main checkout but is silently bypassed in a linked worktree — `.git` is a FILE there, so the relative path resolves to nothing and commits skip the gate. Verified empirically. install-dev-hooks.sh now: - treats ONLY the absolute common hooks dir as safe (silent); a relative .git/hooks gets a soft note in a plain checkout and a HARD warning when a linked worktree actually exists (git worktree list > 1); - normalizes a trailing slash before the absolute-equality check, so `…/.git/hooks/` no longer triggers a spurious foreign-dispatcher warning; - prints the exact `git config core.hooksPath '<abs>'` remediation. README documents the relative-vs-absolute core.hooksPath caveat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQNpHiYWBHHQyMJP34BdHH
…oses #15) Codex CI review (PR #16) flagged a real worktree trap: a RELATIVE core.hooksPath=.git/hooks works in the main checkout but is silently bypassed in a linked worktree — `.git` is a FILE there, so the relative path resolves to nothing and commits skip the gate. Verified empirically. install-dev-hooks.sh now: - treats ONLY the absolute common hooks dir as safe (silent); a relative .git/hooks gets a soft note in a plain checkout and a HARD warning when a linked worktree actually exists (git worktree list > 1); - normalizes a trailing slash before the absolute-equality check, so `…/.git/hooks/` no longer triggers a spurious foreign-dispatcher warning; - prints the exact `git config core.hooksPath '<abs>'` remediation. README documents the relative-vs-absolute core.hooksPath caveat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQNpHiYWBHHQyMJP34BdHH
alex-mextner
force-pushed
the
feat/tracked-dev-precommit-hook
branch
from
June 23, 2026 05:13
66af2ca to
b404346
Compare
Ships the tracked repo-dev pre-commit gate + installer (PR #16). Patch bump: contributor tooling + docs, no change to the installable 3d CLI's behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQNpHiYWBHHQyMJP34BdHH
uv updated the project's own version entry in uv.lock to match the pyproject bump; commit it so the lockfile doesn't drift from pyproject. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQNpHiYWBHHQyMJP34BdHH
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The repo's own dev pre-commit gate (ruff/pytest/mypy) was an untracked, hand-placed
.git/hooks/pre-commit. A fresh clone or another machine got no dev gate — only the3d inittracked template, which is a different hook (it gates a user's.scadproject via3d check, not this repo's Python dev). Every contributor (incl. the CTO on another machine) silently lacked the gate until hand-placing it.What this does
scripts/hooks/pre-commit(tracked) — the repo-dev gate. Runs3d test(ruff → pytest → mypy viatests/run_gate.py, the canonical gate, so it never drifts from3d test). Fails closed if./bin/3dis missing — a gate that silently passes when it can't run its checks is worse than no gate.scripts/install-dev-hooks.sh(tracked) — idempotent installer. Targets the common git hooks dir (git rev-parse --git-common-dir, so it's correct in linked worktrees + submodules and isn't shadowed by a globalcore.hooksPath). Backs up a differing pre-existing hook to.bak(never clobbers an existing.bak), repairs a cleared exec bit even on the up-to-date path, and warns if a foreigncore.hooksPath(husky/lefthook) would bypass the gate. Preserves an existing inline global-hooks-dispatcher prefix (only a well-formed… || exit $?block) so a repo whose localcore.hooksPath=.git/hooksbypasses the composer keeps its secret-scan running.3d inittemplate.Coexistence with
3d init3d init'sassets/templates/pre-commitgates a user's.scadproject via3d check; this gate gates this repo's own Python source. Different hooks, different repos — they never collide.Verification
3d testgreen (ruff + 3230 passed, 6 skipped + mypy clean) — and the commit on this PR was itself gated by the installed hook (proof the gate fires on a real commit)..git/hooks, fires from the worktree), gate blocks on3d testfailure,.bakbackup + no-clobber, exec-bit repair, dispatcher-prefix preservation (well-formed only; a malformed block falls back to a pure body, never pastes a tail), and the CTO's exact current-machine config (warning stays silent, his inline dispatcher preserved).Closes #15
🤖 Generated with Claude Code