From 3954dfe206df68b70edadaa56b7f5496dd70e97e Mon Sep 17 00:00:00 2001 From: xhwSkhizein Date: Wed, 15 Apr 2026 00:01:57 +0800 Subject: [PATCH] Add local hooks protecting main branch --- .githooks/pre-commit | 9 +++++ .githooks/pre-push | 10 ++++++ AGENTS.md | 6 ++++ ...-14-main-branch-local-protection-design.md | 36 +++++++++++++++++++ 4 files changed, 61 insertions(+) create mode 100755 .githooks/pre-commit create mode 100755 .githooks/pre-push create mode 100644 docs/superpowers/specs/2026-04-14-main-branch-local-protection-design.md diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..c9a4654 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,9 @@ +#!/usr/bin/env sh + +branch="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)" + +if [ "$branch" = "main" ]; then + echo "Direct commits to main are disabled in this repository." + echo "Create a feature branch first, then commit there." + exit 1 +fi diff --git a/.githooks/pre-push b/.githooks/pre-push new file mode 100755 index 0000000..acc493c --- /dev/null +++ b/.githooks/pre-push @@ -0,0 +1,10 @@ +#!/usr/bin/env sh + +while read -r local_ref local_oid remote_ref remote_oid +do + if [ "$remote_ref" = "refs/heads/main" ]; then + echo "Direct pushes to main are disabled in this repository." + echo "Push a feature branch and open a pull request instead." + exit 1 + fi +done diff --git a/AGENTS.md b/AGENTS.md index a496d1d..e58f5a6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -163,6 +163,8 @@ the implementation, and where should a change land first. `skills/browser-cli-delivery/SKILL.md` - Tests for behavior and contracts: `tests/unit/*`, `tests/integration/*` +- Repo-local git branch protection hooks: + `.githooks/pre-commit`, `.githooks/pre-push` ## Common Navigation Paths @@ -215,6 +217,8 @@ the implementation, and where should a change land first. -> inspect `src/browser_cli/extension/session.py` and `tests/unit/test_extension_transport.py`. - If a change touches architecture or public product contracts: inspect `scripts/guards/architecture.py`, `scripts/guards/product_contracts.py`, and `scripts/guards/docs_sync.py` before making the change final. +- If repo-local branch protection appears inactive in a clone: + inspect `.githooks/pre-commit`, `.githooks/pre-push`, and `git config --local core.hooksPath`; the hooks are repo-owned but activation is clone-local. ## Architectural Boundaries @@ -242,6 +246,8 @@ public interactive commands. - Top-level parser registration lives in `src/browser_cli/cli/main.py`. `read`, `doctor`, `paths`, `task`, `automation`, `status`, and lifecycle `reload` are hand-wired there; the rest come from `get_action_specs()`. - `browser-cli install-skills` installs the packaged Browser CLI skills into `~/.agents/skills` by default and `--target` overrides the destination root. +- Repo-local contributor branch protection lives under `.githooks/`; activate it + per clone with `git config core.hooksPath .githooks`. - Public daemon-backed actions should be added through `ActionSpec`, not by manually bolting ad hoc parsers into `main.py`. - The lifecycle command `browser-cli reload` and the page action `browser-cli page-reload` are intentionally different surfaces. Do not collapse them. - Public daemon commands return JSON payloads. Preserve `ok/data/meta` shape and machine-readable error codes. diff --git a/docs/superpowers/specs/2026-04-14-main-branch-local-protection-design.md b/docs/superpowers/specs/2026-04-14-main-branch-local-protection-design.md new file mode 100644 index 0000000..0c88c07 --- /dev/null +++ b/docs/superpowers/specs/2026-04-14-main-branch-local-protection-design.md @@ -0,0 +1,36 @@ +# Main Branch Local Protection Design + +## Goal + +Prevent accidental direct `git commit` and `git push` operations on `main` in a +way that is lightweight, repository-owned, and easy to understand. + +## Chosen Approach + +Use repo-local git hooks under `.githooks/` and activate them in each clone with +`git config core.hooksPath .githooks`. + +This keeps the policy versioned with the repository without introducing new +runtime dependencies or changing the Python product surface. + +## Behavior + +- `.githooks/pre-commit` rejects commits when the current branch is `main`. +- `.githooks/pre-push` rejects any push whose destination ref is + `refs/heads/main`. +- Detached HEAD states are not blocked by the commit hook. +- The hooks are advisory local protection and can still be bypassed with normal + git escape hatches such as `--no-verify`; remote branch protection remains the + authoritative enforcement layer. + +## Documentation Impact + +Record the hook location and activation requirement in `AGENTS.md` so future +agents know where the local policy lives and why it may appear inactive in a new +clone. + +## Validation + +- Mark both hook files executable. +- Set `core.hooksPath` to `.githooks` in the current clone. +- Run repository lint, tests, and guards after the change.