Guard pre-push step in repos with their own hooks

Task 6002 · Done · Coder · created · updated

Epic 5896 (Architectural Guard), finish-phase gap in R3.2 (commit/push backstop). ## What was observed Task 5934 (PR #5201) put the guard's `--pre-push` step inside the access guard's git pre-push hook (`apps/jaahd/guards/access-guard-pre-push.py`). That hook is installed by `apps/jaahd/guards/install-access-guard.py:107-112`, which copies it into `_git_hooks_dir(clone)/pre-push`. A managed repo can run its own pre-push hook instead, through a repo `core.hooksPath` or its own hook chain. On dev, cto-org-b uses `.claude/hooks/pre-push.sh`, so the access-guard step and the guard's pre-push step never run there (5934 report, "Not verified on dev"). The PreToolUse check still judges `git commit` and `git push` typed in a shell tool (`tools/architectural_guard.py`). But a commit or push made by a script or subprocess, such as `python -c "subprocess.run(['git','push'])"` or a Makefile target, is not judged in such a repo. PRD R3.2's "catches code written through a shell or another non-write tool" relies on the pre-push catch-all. So in these repos a violation can reach the remote branch. ## Fix (smallest) Make the guard's pre-push judgement run in a guarded session even when git runs the repo's own pre-push hook. The fix must not edit the managed repo's tracked files (PRD: no per-repo setup or committed file). One option is to set the effective hooks dir per session through the session env (`GIT_CONFIG_*`) to a node-owned dir. Its pre-push runs the access guard plus guard step, then chains to the repo's own hook (the path from the repo's `core.hooksPath` or `.git/hooks`) with the same argv and stdin, and propagates its exit status. The guard's existing refusal of `core.hooksPath` changes must keep treating the agent's own changes as bypasses. If you find a smaller safe route, take it and say why. ## Out of scope - No change to what the guard judges, the rule kinds, the pre-push decision logic or waivers. - Do not disable or replace a repo's own pre-push hook; it must still run. - No edits to managed repos. No merge-time block. ## Done means - Real-git test: a clone with `core.hooksPath=.githooks` and its own `pre-push` that writes a marker. A scripted `git push` (subprocess, not the shell tool) of a commit that introduces a gate violation is refused by the guard, with the rule named. A compliant push succeeds, and the repo's own hook marker shows it ran in both cases. - Real-git test: an unguarded session (no `JAAH_GUARD_BUNDLE`) behaves byte-identically to today. - Dev oracle on cto-org-b, which ships `.claude/hooks/pre-push.sh`: a guarded session's scripted push of a violation is refused, and a Guard activity row with moment `push` appears. - `./dc check full` green; Jaah Deploy to dev succeeds.

Outcome

Guarded sessions now run the guard's pre-push judgement where the repo runs its own hooks (worktree-scoped hooksPath redirect, chains to the repo's hooks). PR #5229 merged, full gate and Jaah Deploy dev green; dev probe #5003 on cto-org-b refused the scripted push. Follow-ups: 4 UI notes in report.

Pull request

Artifacts

Activity

  • Ready
  • Runner started
  • Runner ended (done)