dev-b4push

Set up before-push validation (b4push) in a project: scripts/run-b4push.sh, the package.json entry, a project-level b4push skill, and optionally a mirroring GitHub Actions CI workflow. Use when the us

SKILL.md

GitHub

dev-b4push

Deliverables: scripts/run-b4push.sh, a b4push package.json script, .claude/skills/b4push/SKILL.md, and (optional) .github/workflows/ci.yml.

1. Pick the steps

Read package.json (and pnpm-workspace.yaml, any doc/ / docs/ / website/ sub-package, playwright/cypress config, generate-* scripts). Use the project's package manager. Include only what exists:

Step Typical command Include when
Workspace tests pnpm --filter "@scope/*" test workspace packages have tests
App unit tests pnpm --filter app-name test:unit app has unit tests
Code quality pnpm check / pnpm lint && pnpm format always
TypeScript pnpm typecheck TypeScript project
Build pnpm build build script exists
Doc quality / build cd doc && pnpm check / pnpm build doc site exists
E2E start production server + playwright e2e tests exist

If the full run exceeds ~5 min (e2e), go dual-track: a quick script plus a full one.

2. scripts/run-b4push.sh

The script must run every step even after a failure (collect into FAILURES, report at the end, exit 1 if any) and isolate each step in a subshell.

#!/usr/bin/env bash
set -euo pipefail

START_TIME=$(date +%s)
FAILURES=()
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

step() { echo ""; echo "━━━━━━━━━━━━━━━━━━━━━━━━"; echo "▶ $1"; echo "━━━━━━━━━━━━━━━━━━━━━━━━"; }
pass() { echo "✅ $1"; }
fail() { echo "❌ $1"; FAILURES+=("$1"); }

# Machine-wide queue for heavy steps, shared by every agent session on this machine
# (owner's ~/.claude or ~/.codex). Absent on CI and on teammates' machines → runs directly.
heavy() {
  local g="${HEAVY_GUARD:-}"
  [ -n "$g" ] || for c in "$HOME/.claude/scripts/heavy-guard.sh" "$HOME/.codex/scripts/heavy-guard.sh"; do
    [ -x "$c" ] && { g="$c"; break; }
  done
  if [ -n "$g" ] && [ -z "${CI:-}" ]; then "$g" -- "$@"; else "$@"; fi
}

step "Step 1/N: Code quality"
if (cd "$ROOT_DIR" && pnpm check); then pass "Code quality"; else fail "Code quality"; fi

# ...more steps, same shape. Heavy ones (build + full test, e2e, cargo --workspace) go through heavy():
step "Step N/N: E2E"
if (cd "$ROOT_DIR" && heavy pnpm test:e2e); then pass "E2E"; else fail "E2E"; fi

DURATION=$(( $(date +%s) - START_TIME ))
echo ""; echo "SUMMARY (${DURATION}s)"
if [ ${#FAILURES[@]} -eq 0 ]; then
  echo "✅ All checks passed! Safe to push."
else
  echo "❌ ${#FAILURES[@]} check(s) failed:"
  for f in "${FAILURES[@]}"; do echo "   - $f"; done
  exit 1
fi

Wrap heavy steps in heavy, leave light ones bare. Several agent sessions running b4push at once starve the machine of memory and turn suites red for non-code reasons; heavy-guard.sh queues them machine-wide and gates on available memory. Putting the call in the script — not in a skill's prose — means every caller is serialized without having to remember a rule, and the fail-open lookup keeps the repo usable for anyone without the guard. Do NOT wrap the whole script from outside or wrap lint / type-check / unit steps: a queued lint is wasted wall-clock. Never tune the suite itself (timeouts, workers) to fit one machine.

chmod +x scripts/run-b4push.sh, then add "b4push": "./scripts/run-b4push.sh" to package.json scripts.

3. Project skill .claude/skills/b4push/SKILL.md

Uppercase SKILL.md is required — lowercase skill.md causes git dual-tracking / clone collisions on case-insensitive filesystems.

---
name: b4push
description: >-
  Run comprehensive pre-push validation covering [steps]. Use when: (1) Completing a PR
  or feature implementation, (2) Before pushing significant changes, (3) After large refactors,
  (4) User says 'b4push', 'before push', 'check everything', or 'ready to push'.
user-invocable: true
allowed-tools:
  - Bash
---

# Before Push Check

Run `pnpm b4push` from the project root (`scripts/run-b4push.sh`):

1. [step list]

Takes ~[duration]. All steps must pass.

## On failure

Identify the failed step, auto-fix what you can (`pnpm check:fix`, `pnpm lint:fix`, same under `doc/`), re-run `pnpm b4push`, report the final status.

Heavy steps print a `heavy-guard: verdict=...` line. Exit 75 = queued too long, the step never ran. `ENV_SUSPECT` = failed while the machine was starved: rerun once; still red with no assertion / type / lint error in the output → defer that step to CI under a `deferred-verification` issue and report it as deferred, not passed. `FAIL` = real, fix it.

4. CI workflow (optional, GitHub, no CI yet)

.github/workflows/ci.yml mirrors the b4push steps one-to-one, each as its own run step so failures are identifiable.

name: CI
on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches: [main]
  push:
    branches: [main]
concurrency:
  group: ci-${{ github.head_ref || github.ref }}
  cancel-in-progress: true
jobs:
  checks:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with:
          version: 10
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: pnpm install --frozen-lockfile
      # one step per b4push step

No cache: on setup-node — a fresh install from the npm CDN beats cache restore (see /gh-actions-wisdom rule 5).

5. Verify

Run pnpm b4push and fix whatever it surfaces.

Reference setups

  • message (zudomessages): 4 steps (workspace tests, app tests, doc checks, doc build), ~1-2 min
  • mdx-formatter: 6 steps (quality, build, test, doc data, doc quality, doc build), ~40s
  • zmod: 9 steps including e2e against a production server, ~3-4 min
  • zpanels: dual-track (quick ~3-5 min, full with e2e ~10-15 min)