The mental model behind RelEasy. For the schema, see configuration.md; for commands, see commands.md.
origin/antalya-26.3: * (stable base branch on origin — you maintain it)
|
feature/antalya-26.3/pr-42: * --- fix (PR → antalya-26.3)
feature/antalya-26.3/pr-99: * --- feat (PR → antalya-26.3)
You maintain the base branch. Each PR you want to port becomes its own port branch carrying the cherry-picked commits, opened as a rebase PR back into the base. RelEasy never creates or rewrites the base.
releasy run does:
- Discover PRs from
pr_sourcesin the session file. - For each PR / group, create
feature/<base>/<id>from the base. - Cherry-pick the PR merge commit(s).
- Push and open a PR into the base (when
push: trueandpr_policy.auto_pr: true).
On conflict: pipeline stops with instructions. Resolve, then
releasy continue, then
releasy run again.
The other commands fit around this loop — see the at-a-glance matrix.
Base branch = target_branch from config, or derived
<project>-<version> (with <version> parsed from --onto).
--onto is a naming label — never resolved as a git ref.
| Type | Pattern | Example |
|---|---|---|
| Base | target_branch or <project>-<version> |
antalya-26.3 |
| Feature | feature/<base>/<id> |
feature/antalya-26.3/s3-disk |
| Origin PR | feature/<base>/pr-<N> |
feature/antalya-26.3/pr-42 |
| External PR | feature/<base>/<owner>-<repo>-pr-<N> |
feature/antalya-26.3/ClickHouse-ClickHouse-pr-12345 |
Each config.yaml has a required name:. That name keys a state file +
lock under ${XDG_STATE_HOME:-~/.local/state}/releasy/ (override with
$RELEASY_STATE_DIR). Different-named projects run truly concurrently;
same-named projects serialize on the lock.
(cd ~/work/antalya-26.3 && releasy run) &
(cd ~/work/antalya-25.8 && releasy run) &
releasy list # see every project on this machineOne
work_dirper project — git itself isn't safe with two processes mutating the same checkout.
Moving a config.yaml trips an ownership check. Fix with
releasy adopt from the new location.
| Path | Purpose | Edited by |
|---|---|---|
config.yaml |
Stable per-project config — origin, target branch, AI, notifications. Schema. | You; scaffolded by releasy new. |
<config-dir>/<name>.session.yaml |
features: + pr_sources:. Schema. Override path with session_file: or --session-file. |
You; mutated by releasy feature *. |
${XDG_STATE_HOME:-~/.local/state}/releasy/<name>.state.yaml |
Pipeline state (phase, branches, statuses, AI cost). | Auto. Not user-editable. |
${XDG_STATE_HOME:-~/.local/state}/releasy/<name>.lock |
POSIX advisory lock. | Auto. Crash leftovers self-reclaim. |
The state file remembers the owning config.yaml's absolute path, so
releasy list can show the back-link and a
moved/copied config trips a clear ownership error.
When AI resolve is disabled or gives up, RelEasy flags the unit for manual review and keeps the pipeline moving. Two flavours:
Singleton (or first PR of a group) — no useful commits yet, so:
abort cherry-pick, delete local port branch, mark Conflict on the board,
do not push, do not open a PR.
Partial group (later PR in a group fails) — earlier picks are valid, so:
abort failed pick, push the branch, open a draft PR labelled
ai-needs-attention with a banner explaining the failure, mark Conflict.
After conflicts:
| You want to… | Run |
|---|---|
| Re-attempt a source PR you just fixed manually | releasy run |
| Mark a manually-resolved port branch as done | releasy continue |
| Resolve target-drift conflicts on an open rebase PR | releasy refresh |
| Drop a port from this run | releasy skip |
| Resync the GitHub Project board | releasy project push |
Status semantics: clean or AI-resolved port with rebase PR open →
needs_review. Pushed branch without PR → branch_created. Needs human →
conflict. AI involvement is signalled by ai_resolved + the ai-resolved
PR label.
For per-PR hints to the resolver, see
ai_context.
Rebase PR title: "<Project> <version>: <subject>" — e.g.
"Antalya 26.3: Token Authentication and Authorization". Version comes
from the base branch; project is title-cased only if all-lowercase
(ClickHouse preserved). A leading <version>[<project>]: prefix on the
source title is stripped first.
Labels: every PR gets releasy (auto-created on first run with
push: true). AI-resolved PRs also get ai_resolve.label (default
ai-resolved). Optional merged_label is stamped on the rebase PR when it
merges and stripped from the source PRs — see
merged_label.
Cross-repo PR URLs are read-only sources for cherry-picks. RelEasy only
ever creates, updates, labels PRs in the origin repo and only ever
pushes branches to that same remote. Enforced at the API helper layer
(create_pull_request, update_pull_request, add_label_to_pr,
ensure_label, force_push) — there's no parameter to point elsewhere.
releasy run prints PRs will be opened against <owner>/<repo> (origin)
on startup so the target is visible. If you see writes to anywhere else,
it's a bug — please report.