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
9 changes: 9 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,12 @@ intentionally **mutable** and are an accepted, documented exception because they
Compliance audits must therefore **not** flag `@pr-review/stable` / `@dev-lead/stable` (or other channel
tags) on first-party callers as "unpinned actions" — they are the sanctioned version-selection mechanism
(see the initiative analysis §5.1: `docs/initiatives/agentic-release-strategy.md`).

**feature-ideation (cross-repo).** The `feature-ideation` agent is released through the same model with
the channel set `{next, ring0, ring1, stable}`, so its `feature-ideation/<channel>` pins are the **same
sanctioned mutable-ref exception** and must not be flagged as unpinned. Unlike `pr-review`/`dev-lead`
whose reusables live in this repo, `feature-ideation`'s reusable lives in **`petry-projects/.github`**
(this repo holds only the thin caller stub), so its release/channel tags are cut against that public
repo — and the protective ruleset bounding `feature-ideation/**` channel tags is therefore created
**there**, not on this repo (an untracked prerequisite in the public repo). See
[`docs/release/versioning.md`](./docs/release/versioning.md) "Cross-repo: feature-ideation".
28 changes: 25 additions & 3 deletions docs/release/runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ Operational procedures for the per-agent channel-tag release model (initiative
[`versioning.md`](./versioning.md) and the
[initiative analysis](../initiatives/agentic-release-strategy.md) §5.1, §7.

**Agents covered:** `pr-review`, `dev-lead`. Both live in this repo; callers
(consumers + this repo's own self-host caller) pin the moving channel tag
`@<agent>/stable` and thread `agent_ref: <agent>/stable`.
**Agents covered:** `pr-review`, `dev-lead`, `feature-ideation`. `pr-review` and
`dev-lead` live in this repo; callers (consumers + this repo's own self-host
caller) pin the moving channel tag `@<agent>/stable` and thread
`agent_ref: <agent>/stable`. `feature-ideation`'s reusable lives in
`petry-projects/.github`, so its release/channel tags are cut against **that**
repo, not this repo's `origin` (see "Cross-repo: feature-ideation" in
[`versioning.md`](./versioning.md)). `cut-release.sh` recognizes it and previews
its tags via `--dry-run`, but **live cross-repo pushes are not wired yet** — the
push target is an open question, so non-dry-run cuts for `feature-ideation` are
refused for now.

**Two tag kinds per agent:**

Expand Down Expand Up @@ -48,6 +55,11 @@ scripts/cut-release.sh pr-review 1.6.0 --ref origin/main --dry-run

# Cut the immutable tag only (no promotion):
scripts/cut-release.sh pr-review 1.6.0 --ref origin/main --push

# feature-ideation is cross-repo (reusable in petry-projects/.github): dry-run
# previews the tag names, but live --push is refused until the cross-repo target
# is decided (see "Cross-repo: feature-ideation" in versioning.md):
scripts/cut-release.sh feature-ideation 1.4.0 --channel ring0 --dry-run
```

- Pick the version per semver (see `versioning.md#semantic-versioning`): bugfix →
Expand Down Expand Up @@ -91,6 +103,13 @@ git push origin pr-review/stable --force
> `--channel-only` flag exists (follow-up), use the direct `git tag -f` above.
> Channel tags are lightweight (point straight at the commit); the immutable
> `vX.Y.Z` tags are annotated.
>
> **feature-ideation is cross-repo.** Its `feature-ideation/<channel>` tags live
> on `petry-projects/.github`, so a promotion is a tag move on **that** repo
> (`git push <petry-projects/.github remote> feature-ideation/<channel> --force`),
> not this repo's `origin`. The exact remote/target for the automated path is an
> open question — `cut-release.sh` refuses a live `feature-ideation` cut until it
> is resolved; preview with `--dry-run`.

Then **verify** (§4).

Expand Down Expand Up @@ -165,6 +184,9 @@ git push origin pr-review/stable --force

- The immutable `vX.Y.Z` tags are the rollback targets — pick the last release
known healthy (`git tag -l 'pr-review/v*' | sort -V`).
- For `feature-ideation`, the same reverse-tag-move applies but against
`petry-projects/.github` (where its `feature-ideation/v*` and channel tags
live), not this repo — see the cross-repo note in §2b.
- Every caller picks up the rolled-back version on its **next** run with no
change on their side. In-flight runs already started on the bad version finish;
new runs use the restored one.
Expand Down
42 changes: 40 additions & 2 deletions docs/release/versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ together, so a version is a single repo commit that contains a known-good combin
|---|---|---|
| `pr-review` | `.github/workflows/pr-review.yml` | `scripts/review-one-pr.sh`, `scripts/review-batch.sh`, `scripts/post-pr-review.sh`, `scripts/engine.sh`, `scripts/lib/*` |
| `dev-lead` | `.github/workflows/dev-lead-reusable.yml` | `scripts/dev-lead-*.sh`, `scripts/engine.sh`, `scripts/lib/*` |
| `feature-ideation` | `petry-projects/.github` → `.github/workflows/feature-ideation-reusable.yml` (**cross-repo**) | reusable-owned (lives in the public repo; this repo holds only the thin caller `.github/workflows/feature-ideation.yml`) |

Because both agents live in one repo, a release tag points at a whole-repo commit; the tag *name*
scopes it to one agent so the two can be released and promoted independently.
`pr-review` and `dev-lead` both live in this repo, so a release tag points at a whole-repo commit; the
tag *name* scopes it to one agent so the two can be released and promoted independently.

`feature-ideation` is the exception: its reusable lives in **`petry-projects/.github`** (this repo holds
only the thin caller stub), so a `feature-ideation` "release" is a commit on that public repo, and its
release/channel tags must be cut **against `petry-projects/.github`, not this repo's `origin`**. See
[Cross-repo: feature-ideation](#cross-repo-feature-ideation) below.

## Tag scheme

Expand Down Expand Up @@ -69,6 +75,12 @@ may sit at the same commit between releases; they diverge while a candidate is b
rejects `@main` and frozen `@vX.Y.Z`/`@<sha>` — callers must pin a *moving* channel). pr-review still
uses `stable` only; its ring channels are pending under #499.

`feature-ideation` uses the full per-ring channel set — `{next, ring0, ring1, stable}` — so it can be
promoted through the same canary → ring → stable model. Its tags follow the identical name scheme
(`feature-ideation/vX.Y.Z`, `feature-ideation/next`, `feature-ideation/ring0`,
`feature-ideation/ring1`, `feature-ideation/stable`) but are cut against `petry-projects/.github`
(see [Cross-repo: feature-ideation](#cross-repo-feature-ideation)).

### Semantic versioning

- **MAJOR** — breaking change to the caller contract (workflow inputs/secrets, required permissions,
Expand Down Expand Up @@ -102,6 +114,28 @@ The first release was cut from the production `main` at the time of issue #496:
version. The health-gated promotion added in Phase 2 (#501) is what makes *future* promotions to
`stable` genuinely validated before they become production.

## Cross-repo: feature-ideation

Everything above assumes the agent's reusable workflow lives **in this repo**, so a release tag is a
whole-repo commit here and tags are cut against this repo's `origin`. That holds for `pr-review` and
`dev-lead`. It does **not** hold for `feature-ideation`:

- `feature-ideation`'s reusable lives in **`petry-projects/.github`**
(`.github/workflows/feature-ideation-reusable.yml`); this repo carries only the thin caller stub
`.github/workflows/feature-ideation.yml`.
- Therefore a `feature-ideation` release is a commit on **`petry-projects/.github`**, and its
`feature-ideation/vX.Y.Z` immutable + `feature-ideation/<channel>` tags must be cut **against that
repo**, not this repo's `origin`.
- `scripts/cut-release.sh` already recognizes `feature-ideation` and formats its tag names, and its
`--dry-run` prints the intended immutable + channel tags. **Live cross-repo pushes are not wired
yet** — the push target mechanism (a `--repo`/remote argument vs. dispatching against the public
repo) is an open question reserved for a human decision, so the script refuses a non-dry-run cut for
`feature-ideation` (see the `TODO(#872)` in `cut-release.sh`). Use `--dry-run` to preview tag names
until the target is decided.
- Because `feature-ideation`'s channel tags live on `petry-projects/.github`, the protective ruleset
that bounds them (the mutable-ref exception) is created **there**, not on this repo — see
[`AGENTS.md`](../../AGENTS.md) "Release channel tags & the mutable-ref exception".

## Cutting / moving tags

Use `scripts/cut-release.sh` (tested in `tests/test_cut_release.bats`) rather than ad-hoc `git tag`:
Expand All @@ -115,6 +149,10 @@ scripts/cut-release.sh pr-review 1.1.0 --channel stable --push

# Preview without touching anything:
scripts/cut-release.sh pr-review 1.1.0 --channel stable --dry-run

# feature-ideation: dry-run only for now (cross-repo target unresolved — see above).
# Prints feature-ideation/v1.4.0 and feature-ideation/ring0 without mutating anything:
scripts/cut-release.sh feature-ideation 1.4.0 --channel ring0 --dry-run
```

The promote/rollback **runbook** (when to move `stable`, how to roll back, verify, gotchas) lives in
Expand Down
20 changes: 17 additions & 3 deletions scripts/cut-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# cut-release.sh <agent> <version> [--ref <ref>] [--channel <name>]
# [--push] [--dry-run]
#
# <agent> pr-review | dev-lead
# <agent> pr-review | dev-lead | feature-ideation
# <version> semantic version without the leading v, e.g. 1.2.0
# --ref commit/ref to tag (default: origin/main)
# --channel also move <agent>/<name> (e.g. stable) to the new release
Expand All @@ -32,7 +32,7 @@ set -euo pipefail
# valid_agent <agent> — return 0 iff agent is a known agent name.
valid_agent() {
case "$1" in
pr-review | dev-lead) return 0 ;;
pr-review | dev-lead | feature-ideation) return 0 ;;
*) return 1 ;;
esac
}
Expand Down Expand Up @@ -75,7 +75,7 @@ main() {
done

if ! valid_agent "$agent"; then
echo "::error::unknown agent '$agent' (expected: pr-review | dev-lead)" >&2
echo "::error::unknown agent '$agent' (expected: pr-review | dev-lead | feature-ideation)" >&2
return 2
fi
if ! validate_version "$version"; then
Expand All @@ -95,10 +95,24 @@ main() {
[ -n "$channel" ] && { chan="$(channel_ref "$agent" "$channel")"; echo "channel tag: $chan -> $rel"; }

if [ "$dry" = true ]; then
if [ "$agent" = "feature-ideation" ]; then
echo "::warning::cross-repo placeholder: the SHA above ($sha) was resolved from petry-projects/.github-private. feature-ideation's canonical commits and tags live in petry-projects/.github — this SHA is a local placeholder only."
fi
echo "(dry-run) no tags created or pushed."
return 0
fi

# TODO(#872): feature-ideation's reusable lives in petry-projects/.github, so its
# release/channel tags must be cut against THAT repo — but main() pushes to this
# repo's `origin`. The cross-repo push target (a --repo/remote arg vs. dispatching
# against the public repo) is an open question reserved for a human decision. Until
# it is wired, only --dry-run is supported for feature-ideation; refuse live cuts so
# tags are never written to the wrong remote.
if [ "$agent" = "feature-ideation" ]; then
echo "::error::live cut/push for 'feature-ideation' is not wired yet — its tags belong on petry-projects/.github (cross-repo target is an open question; see TODO). Use --dry-run for now." >&2
return 1
fi
Comment thread
don-petry marked this conversation as resolved.

if git rev-parse -q --verify "refs/tags/$rel" >/dev/null; then
echo "::error::release tag '$rel' already exists — immutable tags are never overwritten." >&2
return 1
Expand Down
15 changes: 15 additions & 0 deletions tests/test_cut_release.bats
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ setup() {
[ "$status" -eq 0 ]
}

@test "valid_agent: feature-ideation is accepted" {
run valid_agent "feature-ideation"
[ "$status" -eq 0 ]
}

@test "valid_agent: unknown agent is rejected" {
run valid_agent "ci-failure-analyst"
[ "$status" -ne 0 ]
Expand Down Expand Up @@ -82,6 +87,11 @@ setup() {
[ "$output" = "dev-lead/v2.3.1" ]
}

@test "release_ref: feature-ideation variant" {
run release_ref "feature-ideation" "1.4.0"
[ "$output" = "feature-ideation/v1.4.0" ]
}

@test "channel_ref: agent-scoped channel name" {
run channel_ref "pr-review" "stable"
[ "$output" = "pr-review/stable" ]
Expand All @@ -91,3 +101,8 @@ setup() {
run channel_ref "dev-lead" "next"
[ "$output" = "dev-lead/next" ]
}

@test "channel_ref: feature-ideation ring0 channel" {
run channel_ref "feature-ideation" "ring0"
[ "$output" = "feature-ideation/ring0" ]
}
Loading