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
30 changes: 26 additions & 4 deletions .agents/skills/cut-release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ Execute this workflow for: "cut release", "ship vX.Y.Z", "push tag and monitor r

- Repository: `.`
- Tag source branch: `main` only.
- No pre-release branch creation.
- No pre-release PR creation.
- Branch and PR flow is used only for hotfixes after failed checks.
- Allow one short-lived release-prep branch and PR only for finalized changelog publication before tagging.
- Branch and PR flow is otherwise used only for hotfixes after failed checks.
- Deterministic changelog finalization is allowed only through the release helper scripts below.
- Default posture: when a release or post-release blocker is actionable from repo code, workflow config, docs, or install-path behavior, fix it in-loop and continue instead of stopping at the first failure.

Expand Down Expand Up @@ -43,6 +42,7 @@ Execute this workflow for: "cut release", "ship vX.Y.Z", "push tag and monitor r

- Tag must always be created and pushed from `main`.
- `main` must be fast-forward synced with `origin/main` before each tag push.
- Do not bypass branch protection on `main`; use the release-prep PR path when changelog finalization changes files.
- No force-push to tags.
- No destructive git commands.
- No commit amend unless explicitly requested.
Expand All @@ -66,7 +66,29 @@ Execute this workflow for: "cut release", "ship vX.Y.Z", "push tag and monitor r
6. Finalize the changelog for the resolved version before any tag checks:
- `python3 scripts/finalize_release_changelog.py --release-version <version> --json`
- `python3 scripts/validate_release_changelog.py --release-version <version> --json`
- if `CHANGELOG.md` changes, review the diff, commit the finalized changelog on `main`, and continue from that committed release-prep state before tagging
- if `CHANGELOG.md` changes:
- create release-prep branch from current `main`:
- `git checkout -b codex/release-prep-<version>`
- commit only the finalized changelog there:
- `git add CHANGELOG.md`
- `git commit -m "chore: finalize changelog for <version>"`
- push branch:
- `git push -u origin codex/release-prep-<version>`
- open release-prep PR using EOF body:
- `gh pr create --base main --head codex/release-prep-<version> --title "chore: finalize changelog for <version>" --body-file - <<'EOF'`
- include: problem, root cause, fix, validation
- `EOF`
- monitor PR CI to green (`CI_TIMEOUT_MIN`)
- required Wrkr PR checks: `fast-lane`, `scan-contract`, `wave-sequence`, `windows-smoke`
- also monitor `CodeQL` when present
- use non-interactive watch or polling such as:
- `gh pr checks <number> --watch --interval 10`
- merge the release-prep PR after green:
- `gh pr merge <number> --rebase --delete-branch`
- sync `main` to the merged commit before continuing:
- `git checkout main`
- `git pull --ff-only origin main`
- rerun `python3 scripts/validate_release_changelog.py --release-version <version> --json` on merged `main`
7. Ensure target tag does not already exist locally or remotely.
8. Ensure release prerequisites are available:
- `gh auth status`
Expand Down
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and

### Changed

- (none yet)
- [semver:patch] Release prep now lands finalized changelog updates through a short-lived release-prep PR before tagging when `main` is protected.

### Deprecated

Expand All @@ -33,8 +33,8 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
## Changelog maintenance process

1. Update `## [Unreleased]` in every PR that changes user-visible behavior, contracts, or governance process.
2. Before release tagging, run `python3 scripts/finalize_release_changelog.py --json` to promote releasable `Unreleased` entries into a dated versioned section and commit that changelog update in the same release-prep commit that will be tagged.
3. Validate the prepared release changelog with `python3 scripts/validate_release_changelog.py --release-version vX.Y.Z --json` on that release-prep commit before or during the tag workflow.
2. Before release tagging, run `python3 scripts/finalize_release_changelog.py --json` to promote releasable `Unreleased` entries into a dated versioned section and publish that change through a short-lived release-prep PR.
3. Validate the prepared release changelog with `python3 scripts/validate_release_changelog.py --release-version vX.Y.Z --json` on merged `main` before or during the tag workflow.
4. Keep entries concise and operator-facing: what changed, why it matters, and any migration/action notes.
5. Link release notes and tag artifacts to the finalized changelog section.

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ This path is sufficient for most CLI/runtime changes and does not require Node.
6. If docs are touched, follow [`docs/map.md`](docs/map.md) and run docs validation bundle.
7. For user-visible changes, update [`CHANGELOG.md`](CHANGELOG.md) under `Unreleased`.
Public contract wording changes in `README.md`, command help, `docs/`, `product/`, or docs-site projections count even when JSON, exit codes, and schemas stay unchanged.
Maintainers finalize `Unreleased` into a versioned section immediately before tagging with `python3 scripts/finalize_release_changelog.py --json` and commit that release-prep changelog update before creating the tag.
Maintainers finalize `Unreleased` into a versioned section immediately before tagging with `python3 scripts/finalize_release_changelog.py --json`, publish that change through a short-lived release-prep PR, merge it to `main`, and only then create the tag from merged `main`.
8. For `product/` or `.agents/skills/` changes, confirm policy conformance per [`docs/governance/content-visibility.md`](docs/governance/content-visibility.md).

Issue/PR templates:
Expand Down
2 changes: 1 addition & 1 deletion docs/map.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Edit canonical documentation in repository markdown first (`README.md` + `docs/`
README first-screen or quickstart changes should also update the affected docs-site LLM projection files (`docs-site/public/llms.txt`, `docs-site/public/llm/*.md`) in the same change.
If the Wrkr README uses the landing-page Variant B contract, install and OSS trust/support details may live in canonical docs (`docs/install/*`, `docs/README.md`, `docs/trust/*`) instead of the README footer.
Public contract wording changes should update `CHANGELOG.md` under `Unreleased` in the same change, even when runtime JSON, exit-code, and schema contracts stay unchanged.
Maintainers should finalize `Unreleased` with `python3 scripts/finalize_release_changelog.py --json` before cutting a release tag and commit that prepared changelog update so the tag points at the finalized versioned section.
Maintainers should finalize `Unreleased` with `python3 scripts/finalize_release_changelog.py --json` before cutting a release tag, land that prepared changelog update through a release-prep PR, and tag the merged `main` commit so the tag points at the finalized versioned section.

## Required validation bundle

Expand Down
23 changes: 19 additions & 4 deletions docs/trust/changelog-and-release-versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ Instead, maintainers and implementation agents stage operator-facing release not
1. Planning decides whether each story needs a changelog entry.
2. Implementation updates `CHANGELOG.md` `## [Unreleased]` for stories that require it.
3. Release prep resolves the next version from `Unreleased`.
4. Release prep finalizes the changelog into a dated versioned section.
5. The finalized changelog change is committed before tagging.
4. Release prep finalizes the changelog into a dated versioned section on a short-lived release-prep branch.
5. That release-prep PR is merged to `main`.
6. The tag-triggered release workflow validates that the tagged commit's changelog matches the tag.
7. `Unreleased` is empty again, so the next release only considers new changes.

Expand Down Expand Up @@ -146,7 +146,22 @@ Validate the prepared release version:
python3 scripts/validate_release_changelog.py --release-version vX.Y.Z --json
```

Then commit the finalized changelog update on the release-prep commit before creating the tag.
Then publish that finalized changelog update through a release-prep PR before creating the tag:

```bash
git checkout -b codex/release-prep-vX.Y.Z
git add CHANGELOG.md
git commit -m "chore: finalize changelog for vX.Y.Z"
git push -u origin codex/release-prep-vX.Y.Z
gh pr create --base main --head codex/release-prep-vX.Y.Z --title "chore: finalize changelog for vX.Y.Z" --body-file - <<'EOF'
...
EOF
gh pr checks <number> --watch --interval 10
gh pr merge <number> --rebase --delete-branch
git checkout main
git pull --ff-only origin main
python3 scripts/validate_release_changelog.py --release-version vX.Y.Z --json
```

### 4. After finalization

Expand Down Expand Up @@ -262,7 +277,7 @@ It now requires:
- resolving the version from changelog state
- finalizing the changelog
- validating the prepared version
- committing that changelog-prep state before tagging
- landing that changelog-prep state through a release-prep PR before tagging

### `.agents/skills/adhoc-plan/SKILL.md` and `.agents/skills/backlog-plan/SKILL.md`

Expand Down
2 changes: 1 addition & 1 deletion docs/trust/release-integrity.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ python3 scripts/finalize_release_changelog.py --json
python3 scripts/validate_release_changelog.py --release-version v1.0.0 --json
```

The finalizer promotes releasable `Unreleased` entries into `## [vX.Y.Z] - YYYY-MM-DD`, adds a hidden semver hint for CI validation, and resets `Unreleased` to the canonical empty template so the next release only considers new entries. Commit that changelog update before creating the tag; the tag workflow validates the changelog content from the tagged commit itself.
The finalizer promotes releasable `Unreleased` entries into `## [vX.Y.Z] - YYYY-MM-DD`, adds a hidden semver hint for CI validation, and resets `Unreleased` to the canonical empty template so the next release only considers new entries. Publish that changelog update through a short-lived release-prep PR before creating the tag; the tag workflow validates the changelog content from the tagged commit itself.

For the full changelog ownership model, planning/implementation handoff, and file/script reference, see [`docs/trust/changelog-and-release-versioning.md`](changelog-and-release-versioning.md).

Expand Down
26 changes: 24 additions & 2 deletions testinfra/hygiene/release_version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@ func TestCutReleaseSkillReferencesDeterministicResolver(t *testing.T) {
"python3 scripts/resolve_release_version.py --json",
"python3 scripts/finalize_release_changelog.py --release-version <version> --json",
"python3 scripts/validate_release_changelog.py --release-version <version> --json",
"release-prep PR",
"git checkout -b codex/release-prep-<version>",
"gh pr create --base main --head codex/release-prep-<version>",
"gh pr merge",
"[semver:major]",
"[semver:minor]",
"[semver:patch]",
Expand Down Expand Up @@ -450,13 +454,31 @@ func fixtureChangelog(entries map[string][]string) string {
"## Changelog maintenance process",
"",
"1. Update `## [Unreleased]` in every PR that changes user-visible behavior, contracts, or governance process.",
"2. Before release tagging, run `python3 scripts/finalize_release_changelog.py --json` to promote releasable `Unreleased` entries into a dated versioned section.",
"3. Validate the prepared release changelog with `python3 scripts/validate_release_changelog.py --release-version vX.Y.Z --json` before or during the tag workflow.",
"2. Before release tagging, run `python3 scripts/finalize_release_changelog.py --json` to promote releasable `Unreleased` entries into a dated versioned section and land that change through a release-prep PR.",
"3. Validate the prepared release changelog with `python3 scripts/validate_release_changelog.py --release-version vX.Y.Z --json` on merged main before or during the tag workflow.",
)

return strings.Join(lines, "\n")
}

func TestReleaseDocsReferenceReleasePrepPRFlow(t *testing.T) {
t.Parallel()

repoRoot := mustFindRepoRoot(t)
for _, relPath := range []string{
"CHANGELOG.md",
"CONTRIBUTING.md",
"docs/map.md",
"docs/trust/changelog-and-release-versioning.md",
"docs/trust/release-integrity.md",
} {
content := mustReadFile(t, filepath.Join(repoRoot, relPath))
if !strings.Contains(content, "release-prep PR") {
t.Fatalf("expected %s to mention release-prep PR flow", relPath)
}
}
}

func addUnreleasedEntry(t *testing.T, repoRoot string, section string, entry string) {
t.Helper()

Expand Down
Loading