bigpowers / rules
danielvm-git/bigpowers/.cursor/rules/release-branch.mdc
Make the merge/PR/keep/discard decision for a feature branch, verify coverage gates, create the PR with gh, and clean up the worktree. Use when a feature is done and ready to ship, or when user says \"release\", \"merge\", or \"open a PR\".
Cursor rule240 starsChanged 30 days ago
- Commits and pushes
--- description: "Make the merge/PR/keep/discard decision for a feature branch, verify coverage gates, create the PR with gh, and clean up the worktree. Use when a feature is done and ready to ship, or when user says \"release\", \"merge\", or \"open a PR\"." alwaysApply: false --- <!-- story: e38s07 --> <!-- story: e45s15 --> <!-- story: e45s32 --> <!-- story: e45s39 --> <!-- story: e82s02 --> <!-- story: e20s02 --> # Release Branch > **HARD GATE** — Do NOT merge or release if tests fail or if coverage gates are not met. If the branch is red, return to `develop-tdd` to fix regressions or add missing tests before proceeding. Finalize a completed feature branch: verify coverage gates, integrate onto `main`, and clean up the worktree. ## Additional modes - `--hotfix`: Cherry-pick to main + tag. Skip PR in solo. - `--squash-state`: Squash `chore(state):` commits before merge. ## Integrate mode Read `specs/state.yaml` key `workflow_mode` (`team-pr` | `solo-git`). Fall back to `profiles/solo-git.md`. | Mode | When | Ship path | |------|------|-----------| | **solo-local** | `workflow_mode: solo-git` | Auto: `scripts/land-branch.sh` if present, else fallback (Step 5) | | **team-pr** | `workflow_mode: team-pr` (default) | `gh pr create` → `gh pr merge --squash` | If unsure, prefer **solo-local**. Also read `state.yaml` `vcs.kind`: Git follows the procedures below; Jujutsu uses workspaces/bookmarks and must not call Git-only landing scripts. ## Process > **Timing:** `bash scripts/bp-timing.sh start release-branch` at invocation; `bash scripts/bp-timing.sh end release-branch` before handoff. ### 1. Final verification ```bash <full test command> && <typecheck command> && <lint command> git log main...HEAD --oneline | grep -vE "^[a-f0-9]+ (feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?!?: .+$" && echo "❌ Non-conventional commits found" || echo "✅ Commits verified" # Block AI agent attribution (P1) git log main...HEAD --format="%B" | grep -qiE 'co[- ]authored[- ]by' && echo "❌ Co-authored-by footer found — blocked" || echo "✅ No AI attribution" ``` - [ ] All tests pass, no type errors, no lint violations, all commits follow Conventional Commits - [ ] **NO `Co-authored-by` or `Co-Authored-By`** in any commit body — P1 rule (CONVENTIONS.md § Git Attribution). `land-branch.sh` blocks the merge if found. ### 2. Coverage check - [ ] Overall coverage ≥ 80%; business logic coverage ≥ 95% ### 2a. Security gate - [ ] `specs/security/REVIEW.md` exists and is fresh (matches current branch diff) - [ ] No unresolved HIGH findings with confidence ≥ 8 (or all documented in `specs/security/EXCEPTIONS.md` with sign-off rationale) If REVIEW.md is missing or stale → run `security-review` inline. Findings block the merge unless documented in EXCEPTIONS.md. ### 2b. Traceability gate Run `gate-trace` before merge. FAIL blocks merge; CONCERNS requires explicit override in `specs/state.yaml` (`traceability_override: CONCERNS accepted, reason: <explanation>`). WAIVED if no matrix available. > **Adversarial refute framing (e45s32):** The final pre-merge check is **refute, not rubber-stamp**. Before declaring ready, actively try to disprove traceability completeness — missing story tags, absent verify evidence, stale security review. Only proceed when refutation fails. ### 3. Diff review - [ ] All commits intentional, no secrets, CONVENTIONS.md compliance ### 4. Decision Options: **Release (solo-local)** / **Open PR** / **Keep branch** / **Discard** ### 5. Integrate Run `commit-message` first. Git solo-local uses `land-branch.sh`. Jujutsu team mode explicitly advances and pushes a bookmark; `-m` is mandatory for commit/describe operations: ```bash # Git solo-local bash scripts/land-branch.sh <task-slug> "feat(scope): description" # Jujutsu team PR jj describe -m "feat(scope): description" jj bookmark set <task-slug> -r @ jj git push -b <task-slug> ``` Jujutsu solo-local landing is unsupported: run `bp_require_vcs_operation "$PWD" land-branch` and stop with its remediation instead of invoking the Git backend. ### 6. Create PR (team-pr only) Create the pull request with a **literal provenance marker** in the body so agent-generated PRs are identifiable and do not rot silently: ```markdown <!-- bigpowers-provenance: agent-generated --> ``` Place the marker on its own line immediately after the `## Summary` heading. Then merge via `gh`: ```bash gh pr create --title "..." --body "$(cat <<'EOF' ## Summary <!-- bigpowers-provenance: agent-generated --> - ... ## Test plan - [ ] ... EOF )" gh pr merge --squash --delete-branch ``` `semantic-release` auto-detects the commit, bumps SemVer, tags the repo, generates release notes. ### 7a. Archive completed epic capsule > **HARD GATE** — When all epic stories are done (all `done` in `execution-status.yaml`), archive the capsule: ```bash mv specs/epics/eNN-slug specs/epics/archive/ ``` ### 7b. CI verification & agent lock release (e39s02) > **HARD GATE** — Do NOT declare success until CI completes. **Three-independent-facts** (e45s15): commit landed, workflow green, registry visible — see [REFERENCE.md](REFERENCE.md#three-independent-facts-release). ```bash bash scripts/wait-for-ci.sh --timeout 600 --interval 30 ``` - [ ] CI passes; `release.ci_verified: true` in state.yaml - On failure: `handoff.next_skill = fix-bug` ### 8. Clean up & return Git: prune worktree, delete branch, return to main. Jujutsu: `jj workspace forget <workspace>` only after integration; do not delete its bookmark implicitly. Cycle-time: see [REFERENCE.md](REFERENCE.md#cycle-time). Report: "Branch released." ## Verify → verify: `command -v gh >/dev/null 2>&1 && test -f specs/state.yaml && test -d skills/verify-work` --- # Release Branch — Reference ## Navigation | Lines | Section | |-------|---------| | 1 | Title | | 3–22 | Navigation | | 23–31 | PR body template (team-pr mode) | | 32–35 | Summary | | 36–41 | Verify | | 42–47 | specs/ artifacts | | 48–58 | Worktree cleanup details | | 59–81 | Cycle-time recording | | 82–100 | Why not story_start minus story_end? | | 101–121 | CI verification | | 122–127 | Solo-local fallback detail | | 128–134 | Handoff | | 135–159 | Reference block 1 | # Release Branch — Reference ## PR body template (team-pr mode) ```bash PR_TITLE="<type>(<scope>): <description>" echo "$PR_TITLE" | grep -vE "^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?!?: .+$" && echo "❌ ERROR: PR Title must follow Conventional Commits" gh pr create \ --title "$PR_TITLE" \ --body "$(cat <<'EOF' ## Summary - [What this PR does] - [Key decisions made] ## Verify - [ ] All tests pass - [ ] Coverage gates met (≥80% overall, ≥95% business logic) - [ ] CONVENTIONS.md compliance verified - [ ] PR Title follows Conventional Commits (for automated release) ## specs/ artifacts - [List any specs/ files produced or updated] EOF )" ``` ## Worktree cleanup details ```bash # From the main repo root git worktree prune git worktree remove ../<branch-name> 2>/dev/null || true git branch -d <branch-name> ``` If `git worktree remove` fails due to uncommitted changes, ask: "There are uncommitted changes in the worktree. Force remove? (y/n)". If yes: `git worktree remove -f ../<branch-name>`. ## Cycle-time recording After landing the branch, record delivery metrics using the git-derived, additive script (replaces hand-arithmetic): ```bash bash scripts/record-cycle-time.sh append \ --story <story_id> --bcps <bcps> \ --range "$(git merge-base main HEAD)..HEAD" \ --file specs/metrics/cycle-times.yaml ``` This appends a row to the cycle-times ledger with two separated metrics: - **effort_hours** — ADDITIVE. Idle-stripped estimated effort from git commit history (git-hours model: 120-min session threshold, 120-min first-commit pad). Sums exactly to whole-repo effort. NO hand-arithmetic, NO wall-clock includes. - **lead_time_minutes** — calendar latency from first commit to merge. Median-aggregated across stories; NEVER summed. The script also runs an additivity self-check: Σ(story effort) == whole-repo effort within rounding tolerance. ### Why not story_start minus story_end? The previous hand-arithmetic approach (survey-context writes `story_start`, release-branch writes `story_end`, agent hand-computes `cycle_minutes`) was retired because: 1. It was **agent-self-reported** — trivially fabricated or mis-subtracted. 2. Wall-clock included **overnight/weekend/UAT gaps** — calendar latency, not coding effort. 3. The `bcp_per_hour` metric was **computationally meaningless** (velocity derived from a latency measurement). The new approach derives effort from commit history (objective, reproducible) and lead time from first commit → merge (honest calendar latency). See `docs/references/bcp.md` for BCP sizing context and `scripts/record-cycle-time.sh` for the full algorithm. --- ## CI verification The CI polling logic has been extracted to `scripts/wait-for-ci.sh`. See the script's `--help` for usage. Step 7b of the main SKILL.md invokes it directly: ```bash bash scripts/wait-for-ci.sh --timeout 600 --interval 30 ``` **Exit codes:** - **0** — all workflows green. Set `release.ci_verified: true` in state.yaml. - **1** — at least one workflow failed. Prints failure URLs. Set `handoff.next_skill = fix-bug`. - **2** — timeout. CI did not complete. Retry or investigate. - **0 with warning** — `gh` CLI not available, git-only fallback confirmed push landed but CI status unverified. The script handles: auto-discovery of all workflows for the current branch/commit, polling until completion, success/failure/timeout exit codes, and git-only fallback when `gh` CLI is unavailable. --- ## Solo-local fallback detail The fallback sequence (Path B above) handles the "remote has moved" case with `git pull --rebase`. Use when `scripts/land-branch.sh` is absent. **Acceptance:** When fallback runs, main is updated, feature branch is deleted locally, and output states `"used fallback merge (land-branch.sh not found)"`. ## Handoff Gate: READY -> next: survey-context Writes: state.yaml handoff.next_skill = survey-context --- ## Reference block 1 ```bash # Fallback: manual squash-merge when land-branch.sh is absent FEATURE_BRANCH=<task-slug> DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo main) # Ensure we're on the feature branch if [ "$(git branch --show-current)" != "$FEATURE_BRANCH" ]; then git checkout "$FEATURE_BRANCH" fi # Checkout default branch and update git checkout "$DEFAULT_BRANCH" git pull --rebase origin "$DEFAULT_BRANCH" 2>/dev/null || git pull origin "$DEFAULT_BRANCH" # Squash-merge the feature branch git merge --no-ff "$FEATURE_BRANCH" -m "<conventional-commit-message>" # Push git push origin "$DEFAULT_BRANCH" # Clean up local feature branch git branch -d "$FEATURE_BRANCH" ```
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
Posts are public.Sign in to post
No one has posted yet. Be the first.

