diff --git a/release_automation/docs/repository-setup.md b/release_automation/docs/repository-setup.md
index 5c388f7..acc2504 100644
--- a/release_automation/docs/repository-setup.md
+++ b/release_automation/docs/repository-setup.md
@@ -1,6 +1,6 @@
# Repository Setup for Release Automation
-**Last Updated**: 2026-04-19
+**Last Updated**: 2026-10-05
## Overview
@@ -8,9 +8,9 @@ API repositories that adopt the CAMARA release automation need specific reposito
This document defines the required configuration for each API repository. It serves as the **specification** that the automated onboarding tooling implements — repository administrators do not need to apply or verify this configuration manually.
-**Automated application**: The `campaign-release-automation-onboarding` campaign in [`camaraproject/project-administration`](https://github.com/camaraproject/project-administration) applies the full configuration to API repositories. It installs both the release-automation caller workflow and the CAMARA Validation caller workflow side-by-side, sets up the CHANGELOG directory structure, and uses a stable reconciliation branch so repeated runs update the same PR rather than creating new ones. A separate admin script (`apply-release-rulesets.sh`) applies the repository rulesets. Both support dry-run / plan modes and phased rollout — test repositories first, then volunteering repos, then all.
+**Automated application**: The `campaign-release-automation-onboarding` campaign in [`camaraproject/project-administration`](https://github.com/camaraproject/project-administration) applies the full configuration to API repositories. It installs both the release-automation caller workflow and the CAMARA Validation caller workflow side-by-side, sets up the CHANGELOG directory structure, and uses a stable reconciliation branch so repeated runs update the same PR rather than creating new ones. The repository rulesets are declared in project-administration and applied with its [repository-config](https://github.com/camaraproject/project-administration/blob/main/workflows/repository-config/README.md) tool. Both support dry-run / plan modes and phased rollout — test repositories first, then volunteering repos, then all.
-**New repositories**: After rollout, the configuration will also be applied to `Template_API_Repository` ([camaraproject/tooling#82](https://github.com/camaraproject/tooling/issues/82)), so that newly created API repositories inherit it automatically.
+**New repositories**: After rollout, the configuration will also be applied to `Template_API_Repository` ([camaraproject/tooling#82](https://github.com/camaraproject/tooling/issues/82)), so that newly created API repositories inherit it automatically. Rulesets are not copied from the template: the repository creation workflow applies the declared rulesets of the `api-repository` ruleset class.
### What the workflow manages internally
@@ -24,7 +24,7 @@ This document defines the required configuration for each API repository. It ser
| Item | Purpose | Section |
|------|---------|---------|
-| Repository ruleset | Branch protection for snapshot branches | [Ruleset](#repository-ruleset) |
+| Repository rulesets | Protection for snapshot and pointer branches and release tags | [Rulesets](#repository-rulesets) |
| CODEOWNERS file | Codeowner assignment for `/publish-release` authorization | [CODEOWNERS](#codeowners-requirements) |
| Release-automation caller workflow | Entry point that connects the repo to the release automation | [Caller Workflows](#caller-workflows) |
| CAMARA Validation caller workflow | Entry point that connects the repo to the validation framework | [Caller Workflows](#caller-workflows) |
@@ -36,13 +36,16 @@ This document defines the required configuration for each API repository. It ser
## Repository Rulesets
-Three rulesets protect branches managed by the release automation:
+Four rulesets protect branches and tags managed by the release automation:
1. **Snapshot branch protection** — protects `release-snapshot/**` branches with branch protection rules and PR review requirements
2. **Release pointer branch protection** — protects `release/**` pointer branches (fully immutable)
3. **Pre-release pointer branch protection** — protects `pre-release/**` pointer branches (immutable but deletable by codeowners)
+4. **Release tag protection** — protects `r*` release tags (only the automation creates them)
-The `camara-release-automation` GitHub App is the bypass actor for all rulesets, allowing the workflow to create and manage these branches while humans are governed by protection rules.
+The `camara-release-automation` GitHub App is the bypass actor for all rulesets, allowing the workflow to create and manage these branches and tags while humans are governed by protection rules.
+
+**Declared configuration**: the exact ruleset payloads are maintained in [`config/rulesets/`](https://github.com/camaraproject/project-administration/tree/main/config/rulesets) in project-administration, attached to the `api-repository` ruleset class in [`config/ruleset-classes.yaml`](https://github.com/camaraproject/project-administration/blob/main/config/ruleset-classes.yaml). The tables below describe them; the files are authoritative. Actor IDs are listed in the [repository-config README](https://github.com/camaraproject/project-administration/blob/main/workflows/repository-config/README.md#bypass-actors).
No ruleset is needed for `release-review/**` branches — codeowners push CHANGELOG review fixes directly to these branches, and the workflow handles creation and cleanup. Validation rule P-012 restricts what may change there to CHANGELOG.md / CHANGELOG/ — README.md is a mechanical change committed with the snapshot, not editable here.
@@ -69,70 +72,9 @@ No ruleset is needed for `release-review/**` branches — codeowners push CHANGE
The dual review gate ensures both API codeowners and Release Management reviewers must approve before a Release PR can be merged:
- The `*` CODEOWNERS pattern assigns API codeowners as reviewers
-- The ruleset's `required_reviewers` field auto-requests the `release-management_reviewers` team
-
-
-GitHub API payload for programmatic application
-
-```json
-{
- "name": "release-snapshot-protection",
- "target": "branch",
- "enforcement": "active",
- "conditions": {
- "ref_name": {
- "include": ["refs/heads/release-snapshot/**"],
- "exclude": []
- }
- },
- "rules": [
- { "type": "deletion" },
- { "type": "non_fast_forward" },
- { "type": "creation" },
- {
- "type": "pull_request",
- "parameters": {
- "required_approving_review_count": 2,
- "dismiss_stale_reviews_on_push": true,
- "required_reviewers": [
- {
- "minimum_approvals": 1,
- "file_patterns": ["*"],
- "reviewer": {
- "id": 13109132,
- "type": "Team"
- }
- }
- ],
- "require_code_owner_review": true,
- "require_last_push_approval": false,
- "required_review_thread_resolution": false,
- "allowed_merge_methods": ["merge", "squash", "rebase"]
- }
- }
- ],
- "bypass_actors": [
- {
- "actor_id": null,
- "actor_type": "OrganizationAdmin",
- "bypass_mode": "always"
- },
- {
- "actor_id": 2865881,
- "actor_type": "Integration",
- "bypass_mode": "always"
- }
- ]
-}
-```
+- The ruleset's `required_reviewers` field auto-requests the `release-management_reviewers` team (a beta feature in the GitHub Rulesets API)
-Notes:
-- `actor_id: 2865881` is the `camara-release-automation` GitHub App ID
-- `reviewer.id: 13109132` is the `release-management_reviewers` team ID
-- The `required_reviewers` field is a beta feature in the GitHub Rulesets API
-- The canonical ruleset is maintained in `Template_API_Repository` — the JSON above matches it
-
-
+Declared payload: [`release-snapshot-protection.json`](https://github.com/camaraproject/project-administration/blob/main/config/rulesets/release-snapshot-protection.json)
### Release Pointer Branch Protection
@@ -162,96 +104,26 @@ Rules: restrict creations, restrict deletions, restrict updates, block force pus
Rules: restrict creations, restrict updates, block force pushes. **No deletion rule** — codeowners can delete older pre-release pointers to manage the branch list as pre-releases accumulate during a release cycle.
-
-GitHub API payloads
-
-```json
-{
- "name": "release-pointer-protection",
- "target": "branch",
- "enforcement": "active",
- "conditions": {
- "ref_name": {
- "include": ["refs/heads/release/**"],
- "exclude": []
- }
- },
- "rules": [
- { "type": "creation" },
- { "type": "deletion" },
- { "type": "update" },
- { "type": "non_fast_forward" }
- ],
- "bypass_actors": [
- {
- "actor_id": null,
- "actor_type": "OrganizationAdmin",
- "bypass_mode": "always"
- },
- {
- "actor_id": 2865881,
- "actor_type": "Integration",
- "bypass_mode": "always"
- }
- ]
-}
-```
+The `update` rule prevents any commits to pointer branches — they must stay at the tag commit.
-```json
-{
- "name": "pre-release-pointer-protection",
- "target": "branch",
- "enforcement": "active",
- "conditions": {
- "ref_name": {
- "include": ["refs/heads/pre-release/**"],
- "exclude": []
- }
- },
- "rules": [
- { "type": "creation" },
- { "type": "update" },
- { "type": "non_fast_forward" }
- ],
- "bypass_actors": [
- {
- "actor_id": null,
- "actor_type": "OrganizationAdmin",
- "bypass_mode": "always"
- },
- {
- "actor_id": 2865881,
- "actor_type": "Integration",
- "bypass_mode": "always"
- }
- ]
-}
-```
-
-Notes:
-- `actor_id: 2865881` is the `camara-release-automation` GitHub App ID
-- The `update` rule prevents any commits to pointer branches — they must stay at the tag commit
+Declared payloads: [`release-pointer-protection.json`](https://github.com/camaraproject/project-administration/blob/main/config/rulesets/release-pointer-protection.json), [`pre-release-pointer-protection.json`](https://github.com/camaraproject/project-administration/blob/main/config/rulesets/pre-release-pointer-protection.json)
-
+### Release Tag Protection
-### Applying rulesets programmatically
+| Property | Value |
+|----------|-------|
+| **Name** | `release-tag-protection` |
+| **Enforcement** | Active |
+| **Target** | Include tags matching: `r*` |
+| **Bypass actors** | `camara-release-automation` GitHub App (always), Organization admins (always) |
-The GitHub Rulesets API is **not idempotent** — calling `POST` twice creates duplicate rulesets. The admin script in `project-administration` uses a check-then-create/update pattern:
+Rules: restrict creations, restrict deletions, restrict updates, block force pushes. Release tags are created by the automation at publication; a manual tag or release publish is refused.
-```bash
-# List existing rulesets
-existing=$(gh api repos/{owner}/{repo}/rulesets --jq '.[].name')
+Declared payload: [`release-tag-protection.json`](https://github.com/camaraproject/project-administration/blob/main/config/rulesets/release-tag-protection.json)
-# Check if it exists, then create or update
-if echo "$existing" | grep -q "release-snapshot-protection"; then
- id=$(gh api repos/{owner}/{repo}/rulesets --jq '.[] | select(.name == "release-snapshot-protection") | .id')
- gh api -X PUT repos/{owner}/{repo}/rulesets/$id --input payload.json
-else
- gh api -X POST repos/{owner}/{repo}/rulesets --input payload.json
-fi
-```
+### Applying rulesets
-See `project-administration/scripts/apply-release-rulesets.sh` for the full script.
+The [repository-config](https://github.com/camaraproject/project-administration/blob/main/workflows/repository-config/README.md) tool in project-administration compares the declared rulesets with each repository (`plan`, scheduled weekly) and brings named repositories to the declared state (`apply`). Rulesets are matched by name, so repeated runs do not create duplicates.
---
@@ -472,6 +344,8 @@ Use this checklist to verify that a repository is correctly configured for relea
### Rulesets
+`plan --repos ` of the repository-config tool reports any difference from the declared rulesets; the items below are what it checks.
+
- [ ] Ruleset `release-snapshot-protection` exists and is **active**
- Target: `release-snapshot/**`
- Branch protection: restrict creations, deletions, block force pushes
@@ -486,6 +360,10 @@ Use this checklist to verify that a repository is correctly configured for relea
- Target: `pre-release/**`
- Rules: restrict creations, updates, block force pushes (no deletion rule)
- Bypass: `camara-release-automation` GitHub App, Organization admins
+- [ ] Ruleset `release-tag-protection` exists and is **active**
+ - Target: tags `r*`
+ - Rules: restrict creations, deletions, updates, block force pushes
+ - Bypass: `camara-release-automation` GitHub App, Organization admins
### CODEOWNERS
diff --git a/release_automation/docs/technical-architecture.md b/release_automation/docs/technical-architecture.md
index 2db011d..1041f68 100644
--- a/release_automation/docs/technical-architecture.md
+++ b/release_automation/docs/technical-architecture.md
@@ -898,7 +898,7 @@ Permission checks use the GitHub API (`GET /repos/{owner}/{repo}/collaborators/{
- At least one **Release Management reviewer** approval required
- Enforced via repository rulesets on `release-snapshot/*` branches
-Three repository rulesets enforce these protections. GitHub Actions is configured as a bypass actor to allow workflow operations while blocking direct human access. See [repository-setup.md](repository-setup.md#repository-rulesets) for exact ruleset definitions, API payloads, and the verification checklist.
+Repository rulesets enforce these protections. GitHub Actions is configured as a bypass actor to allow workflow operations while blocking direct human access. See [repository-setup.md](repository-setup.md#repository-rulesets) for the ruleset definitions, links to the declared payloads, and the verification checklist.
### 7.3 Secrets