diff --git a/.claude/hooks/block-protected-branch.sh b/.claude/hooks/block-protected-branch.sh index 13868d71..32917ec8 100755 --- a/.claude/hooks/block-protected-branch.sh +++ b/.claude/hooks/block-protected-branch.sh @@ -25,9 +25,9 @@ fi branch=$(git -C "${CLAUDE_PROJECT_DIR:-.}" symbolic-ref --short HEAD 2>/dev/null || true) case "$branch" in master|dev) - echo "Blocked: '$branch' is a protected branch. Do not commit/push/merge directly onto it." >&2 - echo "Start a feature branch first (git switch -c feature/...), move the work there, and open a PR." >&2 - echo "master and dev only advance via a reviewed GitHub PR." >&2 + echo "Blocked: '$branch' is a protected branch. Do not commit/merge directly onto it." >&2 + echo "Start a feature branch first (git switch -c feature/...), move the work there, and open a PR into dev." >&2 + echo "Release is a fast-forward: scripts/promote-dev-to-master.sh" >&2 exit 2 ;; esac diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 05e7a73a..0302be84 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -13,10 +13,11 @@ ## Checklist -- [ ] Tests pass (`pre-commit` fast tests green; full suite runs on `dev → master`) -- [ ] Targeting the right base branch (`dev` for features; `master` only for releases) +- [ ] Tests pass (`pre-commit` fast tests green) +- [ ] Targeting `dev` (feature PRs never target `master`) diff --git a/.husky/pre-push b/.husky/pre-push index 9512a857..d15ccb59 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,8 +1,8 @@ #!/usr/bin/env bash export FORCE_COLOR=1 -# Refuse pushing the local master/dev directly — they only move via merged PRs -# on GitHub. Pushing feature branches (to open a PR) is always fine. +# Refuse pushing origin/dev. origin/master may only fast-forward to origin/dev. +# Pushing feature branches (to open a PR into dev) is always fine. . "$(dirname "$0")/protected-branch-guard.sh" protected_branch_guard push diff --git a/.husky/protected-branch-guard.sh b/.husky/protected-branch-guard.sh index ba4f6655..36359b12 100644 --- a/.husky/protected-branch-guard.sh +++ b/.husky/protected-branch-guard.sh @@ -1,15 +1,21 @@ # Shared guard: refuse direct commits/pushes on protected branches. # Sourced by .husky/pre-commit and .husky/pre-push. # -# All real work happens on feature branches; master and dev are only ever -# updated by merging a reviewed PR on GitHub. See CONTRIBUTING.md. +# All real work happens on feature branches. `dev` only moves via a reviewed +# GitHub PR. `master` only moves by fast-forwarding to current origin/dev +# (scripts/promote-dev-to-master.sh). See workflow/branching.md. # -# Escape hatch (use sparingly, e.g. a hotfix): ALLOW_PROTECTED_COMMIT=1 git commit ... +# Escape hatch (use sparingly, e.g. a hotfix commit): ALLOW_PROTECTED_COMMIT=1 protected_branch_guard() { - local action="$1" # "commit" or "push" - local branch - branch=$(git symbolic-ref --short HEAD 2>/dev/null) + action="$1" # "commit" or "push" + + if [ "$action" = "push" ]; then + protected_branch_push_guard + return + fi + + branch=$(git symbolic-ref --short HEAD 2>/dev/null) || return 0 case "$branch" in master|dev) @@ -19,9 +25,10 @@ protected_branch_guard() { fi echo "" echo "✋ Direct $action on '$branch' is blocked." - echo " master and dev only move via a reviewed GitHub PR." + echo " Work on a feature branch; merge to dev via a reviewed PR." + echo " Release: scripts/promote-dev-to-master.sh (FF origin/dev → master)." echo "" - echo " Start a branch and move your work onto it:" + echo " Start a branch:" echo " git switch -c feature/your-thing" echo "" echo " Override (rare): ALLOW_PROTECTED_COMMIT=1 git $action ..." @@ -30,3 +37,67 @@ protected_branch_guard() { ;; esac } + +# Git feeds pre-push: +protected_branch_push_guard() { + if [ "${ALLOW_PROTECTED_COMMIT:-}" = "1" ]; then + echo "⚠️ ALLOW_PROTECTED_COMMIT=1 set — allowing push." + return 0 + fi + + saw_ref=0 + while read -r _local_ref local_sha remote_ref remote_sha; do + [ -z "${remote_ref:-}" ] && continue + saw_ref=1 + case "$remote_ref" in + refs/heads/dev) + echo "" + echo "✋ Pushing to origin/dev is blocked. Open a PR into dev instead." + echo "" + exit 1 + ;; + refs/heads/master) + expected=$(git rev-parse origin/dev 2>/dev/null) || { + echo "✋ Cannot push master: origin/dev is missing. git fetch origin first." + exit 1 + } + if [ "$local_sha" != "$expected" ]; then + echo "" + echo "✋ origin/master may only fast-forward to current origin/dev." + echo " Use scripts/promote-dev-to-master.sh" + echo " attempted: $local_sha" + echo " origin/dev: $expected" + echo "" + exit 1 + fi + zeros=0000000000000000000000000000000000000000 + if [ "$remote_sha" != "$zeros" ]; then + if ! git merge-base --is-ancestor "$remote_sha" "$local_sha"; then + echo "" + echo "✋ origin/master update is not a fast-forward." + echo " See scripts/promote-dev-to-master.sh (and --reset-master-to-dev" + echo " only when the trees already match)." + echo "" + exit 1 + fi + fi + ;; + esac + done + + if [ "$saw_ref" = 1 ]; then + return 0 + fi + + # Empty stdin (unusual): still refuse a default push from master/dev. + branch=$(git symbolic-ref --short HEAD 2>/dev/null) || return 0 + case "$branch" in + master|dev) + echo "" + echo "✋ Direct push on '$branch' is blocked." + echo " Release: scripts/promote-dev-to-master.sh" + echo "" + exit 1 + ;; + esac +} diff --git a/AGENTS.md b/AGENTS.md index 134724a9..dfe2fee9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,4 +22,4 @@ Avoid `lens_diagnostics mode=full` unless you specifically need a project-wide s Read `workflow/branching.md` before working with branches or creating PRs. Key points: - Always compare to `dev`, not `master` -- Feature PRs target `dev`; `dev → master` is a separate release step +- Feature PRs target `dev`; release is `scripts/promote-dev-to-master.sh` (fast-forward `master` to `dev`), not a GitHub PR diff --git a/scripts/promote-dev-to-master.sh b/scripts/promote-dev-to-master.sh new file mode 100755 index 00000000..e07dc495 --- /dev/null +++ b/scripts/promote-dev-to-master.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# Fast-forward origin/master to origin/dev (release). +# See workflow/branching.md. +set -euo pipefail + +ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +cd "$ROOT" + +RESET=0 +if [ "${1:-}" = "--reset-master-to-dev" ]; then + RESET=1 +fi + +git fetch origin + +dev_sha=$(git rev-parse origin/dev) +master_sha=$(git rev-parse origin/master) + +if [ "$dev_sha" = "$master_sha" ]; then + echo "origin/master already points at origin/dev ($dev_sha)." + exit 0 +fi + +if git merge-base --is-ancestor "$master_sha" "$dev_sha"; then + echo "Fast-forwarding origin/master to origin/dev ($dev_sha)." + git push origin "origin/dev:refs/heads/master" + exit 0 +fi + +echo "Cannot fast-forward: origin/master is not an ancestor of origin/dev." +echo " master: $master_sha" +echo " dev: $dev_sha" + +if git diff --quiet origin/dev origin/master; then + echo "Trees match (leftover GitHub merge commit on master)." + if [ "$RESET" = 1 ]; then + echo "Force-pushing origin/master to origin/dev (same tree)." + git push --force origin "origin/dev:refs/heads/master" + exit 0 + fi + echo "One-time catch-up after old merge-commit promotes:" + echo " 1. Temporarily allow force-pushes on master (GitHub settings or" + echo " allow_force_pushes in scripts/protect-branches.sh), then:" + echo " 2. $0 --reset-master-to-dev" + echo " 3. Re-run scripts/protect-branches.sh so force-push is off again." + exit 1 +fi + +echo "Master has file changes that are not on dev. Land those on dev via a" +echo "feature PR first, then re-run this script." +git log --oneline origin/dev..origin/master +exit 1 diff --git a/scripts/protect-branches.sh b/scripts/protect-branches.sh old mode 100644 new mode 100755 index 6be45f62..9bab9437 --- a/scripts/protect-branches.sh +++ b/scripts/protect-branches.sh @@ -2,7 +2,7 @@ set -euo pipefail REPO="AdamSpitz/commonality" -# Force the PR flow: no direct pushes to master/dev, no force-pushes/deletes, +# Force the PR flow on `dev`: no direct pushes, no force-pushes/deletes, # require the PR to be up to date, and require conversations resolved. # required_approving_review_count is 0 because this is a solo account (you # can't approve your own PR); the review discipline is ENFORCED by the @@ -10,35 +10,23 @@ REPO="AdamSpitz/commonality" # workflow/review-gate.md), plus required_conversation_resolution which blocks # merge until every posted finding is resolved. # -# Review receipts are required on BOTH `dev` and `master`: a PR must carry a -# receipt for its head commit. The one exemption is a dev -> master release, -# which is a rubber-stamp of content already reviewed on the way into dev. -# Hotfix branches may still go straight into master — they just need a receipt, -# so nothing reaches the release branch unreviewed. +# `master` is the release pointer. It does NOT require a pull request: release +# is a fast-forward push of current `origin/dev` onto `master` (see +# scripts/promote-dev-to-master.sh). Force-push and delete stay off, so GitHub +# will reject anything that is not a fast-forward. There is no review-received +# check on `master`; content was already reviewed on the way into `dev`. # -# `strict` (require the PR branch to be up to date with the base) is on for dev -# and OFF for master. Promoting dev -> master leaves a merge commit on master -# that dev lacks, so a strict master would demand a back-merge into dev before -# every single release. -for BRANCH in master dev; do - echo "=== Protecting $BRANCH ===" +# Hotfix branches should still land on `dev` (review gate) and then be +# promoted. A leftover PR into `master` is optional paper trail, not required. - if [ "$BRANCH" = "dev" ]; then - REQUIRED_STATUS_CHECKS='{ +echo "=== Protecting dev ===" +gh api -X PUT "repos/$REPO/branches/dev/protection" \ + --input - <<'JSON' +{ + "required_status_checks": { "strict": true, "contexts": ["review-received"] - }' - else - REQUIRED_STATUS_CHECKS='{ - "strict": false, - "contexts": ["review-received"] - }' - fi - - gh api -X PUT "repos/$REPO/branches/$BRANCH/protection" \ - --input - < master release PR. -gh pr create --base master --head dev --title "Promote dev to master" +# Fast-forward master to current reviewed dev. +scripts/promote-dev-to-master.sh # Everything else happens automatically: # ✅ Contracts deployed to Base Sepolia @@ -113,8 +113,8 @@ If you don't have these yet, see `workflow/github-secrets-guide.md`. After running the setup script: ```bash -# Release an actual reviewed change through the protected branch workflow. -gh pr create --base master --head dev --title "Promote dev to master" +# Release reviewed dev by fast-forwarding master. +scripts/promote-dev-to-master.sh ``` Then watch: diff --git a/workflow/branching.md b/workflow/branching.md index a817d2e7..076ad9ec 100644 --- a/workflow/branching.md +++ b/workflow/branching.md @@ -19,7 +19,8 @@ gh pr merge --auto --merge # 6. queue merge; do NOT wait for GitHub A refuses and tells you to branch — that's the safety net, not an error to fight. - **Feature PRs target `dev`.** GitHub's default branch is `dev`, so a plain `gh pr create` (or telling an LLM "make a PR") bases onto `dev` automatically. - You only ever target `master` for a deliberate `dev → master` release. + Do not open a PR into `master` to release — fast-forward `master` to `dev` + (see below). - **The review is a manual step** you trigger before merging — decide when the branch is ready, run `/code-review`, address findings, then queue auto-merge. - **Do not sit on GitHub Actions.** Lint / build / UI / contract jobs on the PR @@ -29,22 +30,26 @@ gh pr merge --auto --merge # 6. queue merge; do NOT wait for GitHub A ## Overview -All work happens on **feature branches**. The two long-lived branches are never -committed to directly — they only advance by merging a **reviewed GitHub PR**. +All work happens on **feature branches**. You never commit on `dev` or `master`. - **`feature/*`** (also `fix/*`, `chore/*`) — where you actually work -- **`dev`** — integration branch; the review gate lives here -- **`master`** — release branch, auto-deploys to Render. End-user documentation +- **`dev`** — integration branch; the review gate lives here; advances only by + merging a **reviewed GitHub PR** +- **`master`** — release pointer, auto-deploys to Render. End-user documentation links source files at `master` on purpose: it is the code the deployed sites - are running, even though `dev` is GitHub's default branch. + are running, even though `dev` is GitHub's default branch. `master` advances + only by **fast-forwarding to the current `dev` tip** — same commit SHA, no + extra merge commit. ``` -feature/x ──▶ PR ──▶ /code-review ──▶ merge to dev ──▶ PR ──▶ merge to master ──▶ Render deploys - (the mandatory review gate) (rubber-stamp: dev is already reviewed) +feature/x ──▶ PR ──▶ /code-review ──▶ merge to dev ──▶ fast-forward master to dev ──▶ Render deploys + (the mandatory review gate) ``` Because `dev` is gated, promoting `dev → master` is a formality — everything in -`dev` was already reviewed on the way in. +`dev` was already reviewed on the way in. GitHub's "Create a merge commit" is +`--no-ff` and would leave a commit only on `master`; we do not use a PR for +this step. ## The flow @@ -76,8 +81,11 @@ Because `dev` is gated, promoting `dev → master` is a formality — everything Auto-merge lands the PR as soon as `review-received` is green, threads are resolved, and the branch is up to date with `dev` (`strict` is on). Then delete the branch when GitHub does (or after it lands). -6. **Release:** open a PR `dev → master` and merge it. The `pre-merge-commit` - hook still runs the full test suite as the safety net. Render deploys `master`. +6. **Release:** fast-forward `master` to `dev`: + ```bash + scripts/promote-dev-to-master.sh + ``` + Render deploys `master`. No GitHub PR, no back-merge. ### Don't wait on CI @@ -105,46 +113,36 @@ review + `post-review.sh` again before auto-merge can fire. ### Releasing `dev` to `master` -The release path is a GitHub PR from `dev` into `master`: +`master` is a pointer. Release means move it to the same commit as `dev`: ```bash -git fetch origin -gh pr create --base master --head dev --title "Promote dev to master" +scripts/promote-dev-to-master.sh ``` -GitHub's "Create a merge commit" always adds a merge commit **only on -`master`**. That is normal. It does **not** mean the file trees diverged. - -Do **not** treat `git merge-base --is-ancestor origin/master origin/dev` as the -release health check. It fails after every GitHub merge-commit promotion even -when `dev` and `master` have the same tree. Agents that "fixed" that by copying -the `dev` tree onto a `master`-based snapshot commit made the graphs worse -without changing the product. - -The invariant that matters is **trees**, not ancestry: +That is `git push origin origin/dev:refs/heads/master` after a fetch. GitHub +branch protection on `master` does **not** require a PR. Force-push is off, so +the push succeeds only when it is a fast-forward (`origin/master` is already an +ancestor of `origin/dev`). After a successful promote, the SHAs match: ```bash git fetch origin -# After a successful release, these should match: -git diff --quiet origin/dev origin/master - -# Before a release, inspect unique *content* on master, not merge commits: -git log --oneline origin/dev..origin/master -git diff origin/dev origin/master +test "$(git rev-parse origin/dev)" = "$(git rev-parse origin/master)" ``` -If `git diff origin/dev origin/master` is empty, a normal `dev → master` merge -PR is the right promotion. If master has real file changes that are not on -`dev` (a hotfix that was never back-merged), merge `master` into `dev` first -and land that through the usual feature PR + review gate. Then promote. +If `origin/master` has unique *file* changes (a hotfix that never went through +`dev`), land those on `dev` with the usual feature PR + review gate first. Then +promote. Do not open a `dev → master` GitHub PR — that creates a merge commit +only on `master` and breaks the next fast-forward. -After every `dev → master` merge, **back-merge `master` into `dev`** with a -feature PR so `master`'s merge commit is in `dev`'s history. That keeps the -next promote a boring merge instead of a fake snapshot. Do not rewrite -`master` to sit on a `dev` SHA. +**Catch-up from the old merge-commit workflow:** if the trees already match +(`git diff --quiet origin/dev origin/master`) but `master` is not an ancestor +of `dev`, that leftover merge commit has to be dropped once. Temporarily allow +force-push on `master`, run +`scripts/promote-dev-to-master.sh --reset-master-to-dev`, then re-run +`scripts/protect-branches.sh` so force-push is off again. If your local `master` got messy while experimenting, reset it to the -protected remote branch instead of pushing it: +remote instead of pushing it: ```bash git switch master @@ -158,9 +156,10 @@ is driving: | Layer | What it does | Bypassable? | |-------|--------------|-------------| -| GitHub branch protection on `master` & `dev` | No direct pushes, no force-push/delete, PR required, conversations must resolve. `enforce_admins` is on, so it applies to you too. | No — server-side | +| GitHub branch protection on `dev` | No direct pushes, no force-push/delete, PR required, `review-received`, conversations must resolve. `enforce_admins` is on. | No — server-side | +| GitHub branch protection on `master` | No force-push/delete. PR **not** required. Direct fast-forward to `dev` is how release works. | No — server-side | | `.husky/pre-commit` guard | Refuses commits while `HEAD` is `master`/`dev` | `--no-verify` / escape hatch | -| `.husky/pre-push` guard | Refuses pushing local `master`/`dev` | `--no-verify` / escape hatch | +| `.husky/pre-push` guard | Refuses pushing `origin/dev`. `origin/master` only if the new SHA is current `origin/dev` and the update is a fast-forward. | `--no-verify` / escape hatch | | `.claude/hooks/block-protected-branch.sh` | Makes Claude Code / Grok self-correct onto a feature branch instead of erroring. Matches `git commit` / `git push` / `git merge` as subcommands only (not `merge-base`, not the word "merge" in a description). | Agent sugar; husky still enforces | Escape hatch for a genuine hotfix commit (still can't push to protected branch @@ -189,20 +188,22 @@ its own PR — the `review-received` check is what does the enforcing. The full protocol (so other agents can post receipts) is in [`review-gate.md`](review-gate.md). -`master` is **not** gated by the `review-received` check: a `dev → master` -release is a rubber-stamp of content already reviewed on the way into `dev`. The -full test suite still runs — the `pre-merge-commit` hook executes -`automated.test-full` on every merge into `master` and aborts on failure. +`master` is **not** gated by `review-received` and does not require a PR: a +`dev → master` promote is a fast-forward of content already reviewed on the way +into `dev`. The `pre-merge-commit` hook still runs `automated.test-full` if +someone merges into a local `master` checkout; the normal promote path does not +create a merge commit, so that hook does not run. ## Hook reference - **pre-commit** (every commit, any branch): branch guard, then lint + build + `verifier-run automated.test-fast`. Skipped if only `.txt/.md/.gitignore` changed. -- **pre-push** (every push): branch guard against pushing local `master`/`dev`. -- **pre-merge-commit** (merging into `master`): clean-tree check + - `verifier-run automated.test-full` (Docker/Playwright E2E, ~3 min). Aborts the - merge on failure. +- **pre-push** (every push): refuse `origin/dev`; allow `origin/master` only as + a fast-forward to current `origin/dev`. +- **pre-merge-commit** (merging into a local `master` checkout): clean-tree + check + `verifier-run automated.test-full` (Docker/Playwright E2E, ~3 min). + Aborts that merge on failure. Not used by `scripts/promote-dev-to-master.sh`. ## Notes diff --git a/workflow/ci-cd-setup.md b/workflow/ci-cd-setup.md index afa0ee7b..0dfefcbc 100644 --- a/workflow/ci-cd-setup.md +++ b/workflow/ci-cd-setup.md @@ -60,10 +60,10 @@ Log into Render dashboard and verify: ### 3. Test the Setup -Use the next reviewed `dev` → `master` release PR to trigger the deployment workflow: +Use the next reviewed `dev` → `master` fast-forward to trigger the deployment workflow: ```bash -gh pr create --base master --head dev --title "Promote dev to master" +scripts/promote-dev-to-master.sh ``` Watch the Actions tab: https://github.com/AdamSpitz/commonality/actions diff --git a/workflow/github-secrets-guide.md b/workflow/github-secrets-guide.md index 7aaf28f7..af6ea5ea 100644 --- a/workflow/github-secrets-guide.md +++ b/workflow/github-secrets-guide.md @@ -132,7 +132,7 @@ After setting secrets, verify they're correct: # List all secrets (doesn't show values) gh secret list -# Test with the next reviewed dev -> master release PR. +# Test with the next reviewed promote: scripts/promote-dev-to-master.sh ``` Watch the Actions tab: https://github.com/AdamSpitz/commonality/actions diff --git a/workflow/local-development.md b/workflow/local-development.md index 02cce0a7..e60a354c 100644 --- a/workflow/local-development.md +++ b/workflow/local-development.md @@ -2,7 +2,7 @@ ## Coding -**Branch structure:** See [workflow/branching.md](/workflow/branching.md). Briefly: work on feature branches; commits directly on `dev` or `master` are blocked by hooks. Feature-branch commits run the quick suite, and merges into `master` are gated by the full suite. +**Branch structure:** See [workflow/branching.md](/workflow/branching.md). Briefly: work on feature branches; commits directly on `dev` or `master` are blocked by hooks. Feature-branch commits run the quick suite. Release is `scripts/promote-dev-to-master.sh`. ## Building diff --git a/workflow/review-gate.md b/workflow/review-gate.md index 98f85b5c..704a803a 100644 --- a/workflow/review-gate.md +++ b/workflow/review-gate.md @@ -12,25 +12,17 @@ another agent, or a human — whatever you like — without touching CI. | A review actually ran on **this commit** | `review-received` status check (`scripts/review-gate.mjs`) | posting a **receipt** (below) | | Every **finding** is dealt with | GitHub `required_conversation_resolution` | posting findings as review threads and resolving them | -## Why `master` is gated too - -`review-received` is required on `master` as well as `dev`, with **one -exemption**: a `dev -> master` release carries no fresh receipt, because its -content already passed the gate on the way into `dev`. Re-reviewing it would be -pure ceremony. (The lookup couldn't recognise those earlier reviews anyway — it -is per-PR and keyed to the head sha, and merging into `dev` mints a new merge -commit whose sha never carried a receipt.) - -Every other PR into `master` — **a hotfix branch, say** — is treated exactly like -a PR into `dev`: it needs a receipt for its head commit. Without this, a feature -branch could merge straight into the release branch having been reviewed nowhere -at all, which is what happened with #86 and #87. - -Note what this deliberately does **not** do: it doesn't force hotfixes to travel -through `dev`. Requiring that would mean a one-line production fix could only -ship by promoting all of `dev`, unreleased work included. Branch off `master`, -fix, review, merge — the fast path stays open. Just remember to back-merge into -`dev` afterwards, or the two will drift. +## Why this is a `dev` gate, not a `master` gate + +`review-received` is required to merge into **`dev`**. `master` is a release +pointer: it fast-forwards to the current `dev` tip with no PR +(`scripts/promote-dev-to-master.sh`). Re-reviewing that would be ceremony. + +If someone still opens a PR into `master`, the referee treats a `dev -> master` +head as already reviewed and any other head (a leftover hotfix PR) like a `dev` +PR: it needs a receipt. That does **not** replace landing the fix on `dev` and +promoting; GitHub no longer requires a PR to update `master`. Local pre-push +will reject a `master` push whose SHA is not current `origin/dev`. Neither half runs an LLM in CI. The referee is a ~100-line script with no API key; it only inspects the PR's existing reviews. @@ -93,9 +85,10 @@ they exist, are hard-blocked by conversation resolution. - Reviewer receipts / findings: `scripts/post-review.sh`, `/code-review`. - The referee logic: `scripts/review-gate.mjs`. - The CI trigger: `.github/workflows/review-gate.yml`. -- Branch protection (marks `review-received` **required** on both `dev` and - `master`): `scripts/protect-branches.sh` — re-run after changing it. +- Branch protection (`review-received` **required** on `dev` only; `master` is + a fast-forward of `dev`): `scripts/protect-branches.sh` — re-run after + changing it. Note that `.github/workflows/review-gate.yml` checks the referee out from the -**default branch**, not the PR head, so a change to `scripts/review-gate.mjs` -only takes effect once it reaches `master`. +**default branch** (`dev`), not the PR head, so a change to +`scripts/review-gate.mjs` only takes effect once it reaches `dev`. diff --git a/workflow/roles/developer.md b/workflow/roles/developer.md index 3fe44d20..86b4b1ec 100644 --- a/workflow/roles/developer.md +++ b/workflow/roles/developer.md @@ -25,7 +25,7 @@ The project has a verifier workspace in [`/verifier`](/verifier/README.md). If a The project `.envrc` sets `VERIFIER_WORKSPACE=verifier` so `--workspace` is automatic from the repo root. -**Branch structure:** See [workflow/branching.md](/workflow/branching.md). Briefly: work on feature branches; commits directly on `dev` or `master` are blocked by hooks. Feature-branch commits run the quick suite, and merges into `master` are gated by the full suite. +**Branch structure:** See [workflow/branching.md](/workflow/branching.md). Briefly: work on feature branches; commits directly on `dev` or `master` are blocked by hooks. Feature-branch commits run the quick suite. Release is a fast-forward of `master` to `dev` (`scripts/promote-dev-to-master.sh`). ## LSP (Language Server Protocol)