You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
pre-push-gate.sh today reminds the user to run lint / typecheck / test / build before git push — prints the checklist and exits 0. The rule it enforces (from .claude/rules/pr-workflow.md) is explicitly labelled a HARD STOP — "Never push without running CI checks locally." A reminder is not a hard stop. Observed failure mode: the gate prints, the agent continues, CI fails on the PR, cycle wastes time.
Scope
Upgrade pre-push-gate.sh from advisory to blocking:
Read the project's pre-push command set from config (new field, default ships the canonical set):
# .claude/project-config.jsonpre_push:
commands:
- name: lintrun: "npm run lint"
- name: typecheckrun: "npm run typecheck"
- name: testrun: "npm run test"
- name: buildrun: "npm run build"skip_marker: "<!-- pre-push: skip -->"# for emergency hotfix paths
Execute each command in sequence. Capture stdout/stderr.
On any non-zero: exit 2 with a concise summary naming the failing command + a pointer to the full output log.
Escape hatch: the skip marker, written into the most recent commit message, lets a push through. Use for true emergencies — leaves a grep-able trace.
Skip condition (silent, no block): no .claude/project-config.json and no package.json — treat as a non-runnable repo (e.g. docs-only).
Performance: run commands in parallel where safe (lint + typecheck are independent; test + build typically are). Configurable.
Acceptance Criteria
Hook runs configured commands on git push and blocks (exit 2) on any red.
Hook reads the command list from project config; ships a sensible default.
Failing command name + last 20 lines of output included in the block message.
Skip marker in the HEAD commit bypasses the gate (and prints a warning that it was bypassed).
docs/rule-audit.md updates the hook's entry from "advisory" to "blocking".
Fixtures in .claude/hooks/tests/ cover pass, fail, skip, and no-config paths.
Risks / Dependencies
Slow commands (build) extend push time from seconds to minutes. Mitigation: parallel execution; option to disable build via config.
Command failures that aren't the developer's fault (flaky tests, transient network) cause false blocks. Mitigation: skip marker escape hatch, clear.
Hook executes arbitrary commands from config — careful about executable paths, quoting, set -e behaviour. Lean on shellcheck; add a test that malformed config fails loudly rather than silently.
Driver
pre-push-gate.shtoday reminds the user to run lint / typecheck / test / build beforegit push— prints the checklist and exits 0. The rule it enforces (from.claude/rules/pr-workflow.md) is explicitly labelled a HARD STOP — "Never push without running CI checks locally." A reminder is not a hard stop. Observed failure mode: the gate prints, the agent continues, CI fails on the PR, cycle wastes time.Scope
Upgrade
pre-push-gate.shfrom advisory to blocking:Read the project's pre-push command set from config (new field, default ships the canonical set):
Execute each command in sequence. Capture stdout/stderr.
On any non-zero: exit 2 with a concise summary naming the failing command + a pointer to the full output log.
Escape hatch: the skip marker, written into the most recent commit message, lets a push through. Use for true emergencies — leaves a grep-able trace.
Skip condition (silent, no block): no
.claude/project-config.jsonand nopackage.json— treat as a non-runnable repo (e.g. docs-only).Performance: run commands in parallel where safe (lint + typecheck are independent; test + build typically are). Configurable.
Acceptance Criteria
git pushand blocks (exit 2) on any red.docs/rule-audit.mdupdates the hook's entry from "advisory" to "blocking"..claude/hooks/tests/cover pass, fail, skip, and no-config paths.Risks / Dependencies
buildvia config.set -ebehaviour. Lean on shellcheck; add a test that malformed config fails loudly rather than silently.Glossary