Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,12 +463,12 @@ Conventional commits drive semantic-release. `feat:` triggers a minor bump, `fix

**Default branch for PRs is `dev`.** Feature work, fixes, and refactors all land there via normal PRs. `main` is updated only through the release workflows — never PR a feature branch directly into `main`.

**Branch lineage invariant:** after every release reconciles, `main` is an ancestor of `dev`, so every tag on `main` is reachable from `dev`. The `Release: Semantic Version` workflow keeps this invariant in two ways depending on the promotion source:
**Branch lineage invariant:** `main` becomes an ancestor of `dev` at **each minor/major promotion** (every tag on `main` is then reachable from `dev`). Current-line hotfixes intentionally let `main` diverge from `dev` until the next promotion folds them back in. `dev` is **never rewritten** — the `Release: Semantic Version` workflow reconciles per promotion source:

- **dev → main promotion** (minor/major): main fast-forwards to the dev SHA, semantic-release appends a `chore(release):` commit on top, then dev fast-forwards to absorb that commit. No history rewrites on either branch.
- **hotfix-staging → main promotion** (current-line patch): main fast-forwards to the hotfix-staging SHA, semantic-release appends the `chore(release):` commit, then dev is **rebase-reconciled** onto the new main. `git rebase` drops dev's originals of the cherry-picked fixes (patch-id match) and replays any unique dev work on top. This is the only place the workflow force-pushes (`--force-with-lease`) — it is intentional and load-bearing.
- **dev → main promotion** (minor/major): if interim hotfixes have diverged `main`, the workflow first **merges `main` into `dev`** (a single ancestry-only merge commit; the merge tree equals `dev`'s, with version-bump files resolved to `dev`, and any non-`dev`-sourced divergence hard-fails before pushing). That merge is a fast-forward push of `dev` (**no force** — the App's PR-bypass authorizes it). Then `main` FFs to the merge commit, semantic-release appends `chore(release):`, and `dev` FFs to absorb it. A best-effort step dedups the new release's notes of the carried-over hotfix entries.
- **hotfix-staging → main promotion** (current-line patch): `main` fast-forwards to the hotfix-staging SHA and semantic-release appends `chore(release):`. **`dev` is not touched** — it is reconciled at the next minor/major promotion via the merge above. No rebase, no force-push of `dev`.

After a hotfix release, open PRs targeting `dev` are auto-rebased by the `Auto-rebase open PRs` workflow (a thin wrapper around `peter-evans/rebase@v3`). PRs from forks need "Allow edits by maintainers" enabled or the action silently skips them; drafts and PRs labeled `no-auto-rebase` are also excluded. The workflow's job summary reports the rebased count and lists the buckets PRs can fall into; conflict-skipped PRs need a manual `git rebase origin/dev` by the author.
**Prerequisite:** the release App (`community-shaders-release-bot`) must be in the **"Allow specified actors to bypass required pull requests"** list for **both `main` and `dev`** — the app token alone cannot bypass the PR requirement, so a missing entry fails the FF push with `GH006: Changes must be made through a pull request`.

**Patch flow (current line _or_ older line, same staging mechanism):**

Expand All @@ -477,17 +477,17 @@ After a hotfix release, open PRs targeting `dev` are auto-rebased by the `Auto-r
3. PR checks build a `vX.Y.Z-prNNNN` prerelease for verification.
4. Merge the candidate PR.
5. Cut the release:
- **Current line** (`main` is on `X.Y`): dispatch **Release: Semantic Version** on `main` with `ff_target = <hotfix/X.Y.x tip SHA>`.
- **Current line** (`main` is on `X.Y`): dispatch **Release: Semantic Version** on `main` with `ff_target = <hotfix-staging branch tip SHA>` — **not** the `hotfix/X.Y.x` tip, which is a merge commit that `main`'s branch protection rejects. Use the second parent of the merge commit: `git rev-parse origin/hotfix/X.Y.x^2`. `dev` is left untouched and is reconciled at the next minor/major promotion.
- **Older line** (`main` has shipped a newer minor/major): dispatch **Release: Semantic Version** on `hotfix/X.Y.x` with `ff_target` empty.

**Minor/major release flow:**

1. Cut RCs from `dev`: dispatch **Release: Semantic Version** on `dev`, `ff_target` empty → `vX.Y.Z-rc.N`.
2. When ready, dispatch **Release: Semantic Version** on `main` with `ff_target = <dev SHA>` (typically the latest RC's SHA). The workflow FFs `main`, runs semantic-release to cut stable, then FFs `dev` to absorb the `chore(release):` commit.
2. When ready, dispatch **Release: Semantic Version** on `main` with `ff_target = <dev SHA>` (typically the latest RC's SHA). If interim hotfixes have diverged `main`, the workflow first merges `main` into `dev` (ancestry-only, no force) and retargets to that merge commit; it then FFs `main`, runs semantic-release to cut stable, dedups the notes, and FFs `dev` to absorb the `chore(release):` commit.

**Things agents should not do without explicit user direction:**

- Force-push or rebase `main`, `dev`, or any `hotfix/*` branch. (The release workflow's rebase-reconcile of `dev` after a hotfix-staging promotion is the one sanctioned exception; humans should not replicate it manually unless the workflow's remediation block explicitly instructs them to.)
- Force-push or rebase `main`, `dev`, or any `hotfix/*` branch. (The release workflow reconciles `dev` only via fast-forward and ancestry-only merge commits — it never rewrites `dev`.)
- Manually create tags matching `v*` (semantic-release owns these).
- Bump `CMakeLists.txt`'s `VERSION` field outside the release workflow.
- PR a feature branch directly into `main`.
Expand Down
Loading
Loading