Skip to content

feat(hooks): track + auto-install the repo-dev pre-commit gate (Closes #15) - #16

Merged
alex-mextner merged 4 commits into
mainfrom
feat/tracked-dev-precommit-hook
Jun 23, 2026
Merged

feat(hooks): track + auto-install the repo-dev pre-commit gate (Closes #15)#16
alex-mextner merged 4 commits into
mainfrom
feat/tracked-dev-precommit-hook

Conversation

@alex-mextner

Copy link
Copy Markdown
Owner

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 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). 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. Runs 3d test (ruff → pytest → mypy via tests/run_gate.py, the canonical gate, so it never drifts from 3d test). Fails closed if ./bin/3d is 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 global core.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 foreign core.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 local core.hooksPath=.git/hooks bypasses the composer keeps its secret-scan running.
  • README — a Contributing section: the install one-liner, working-tree (not staged-snapshot) semantics, fail-closed behavior, and how it coexists with the 3d init template.

Coexistence with 3d init

3d init's assets/templates/pre-commit gates a user's .scad project via 3d check; this gate gates this repo's own Python source. Different hooks, different repos — they never collide.

Verification

  • 3d test green (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).
  • Sandbox-verified: fresh-clone install, idempotent re-run, linked-worktree targeting (lands in common .git/hooks, fires from the worktree), gate blocks on 3d test failure, .bak backup + 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).
  • shellcheck clean on both scripts.

Closes #15

🤖 Generated with Claude Code

…#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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread scripts/install-dev-hooks.sh Outdated
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
alex-mextner force-pushed the feat/tracked-dev-precommit-hook branch from 66af2ca to b404346 Compare June 23, 2026 05:13
alex-mextner and others added 2 commits June 23, 2026 07:21
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
@alex-mextner
alex-mextner merged commit 6ed1598 into main Jun 23, 2026
8 checks passed
@alex-mextner
alex-mextner deleted the feat/tracked-dev-precommit-hook branch June 23, 2026 05:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Track + auto-install the repo-dev pre-commit hook (ruff/pytest/mypy)

1 participant