You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
External adopters are now using apexyard. Every commit on upstream/main is (potentially) pulled into their fork when they run /update. Under the current trunk-based model, main receives daily work-in-progress — half-finished skills, in-flight hook experiments, docs refactors mid-iteration. This was fine when the framework had one user; it's not fine now.
Fork users deserve upstream/main to mean "the state a reasonable adopter should live on." That's a release-branch promise, not a trunk promise.
Proposed model: "release-cut" (gitflow-lite)
Two long-lived branches, no release or hotfix branches:
dev ────●──●──●──●──●──●──────●──●──────●──●────── (daily work)
\ / /
\ / /
main ──────●────────────────●──────────●────────────── (released only, tagged)
v1.1.0 v1.2.0 v1.2.1
dev — every feature/fix/chore PR targets here. All existing PR hooks + review gates apply unchanged.
main — only receives merges from dev, via release PRs. Every merge to main is tagged with a semver. Direct pushes blocked (already enforced by block-main-push.sh).
Release = dev → main merge + git tag vX.Y.Z + git push --tags.
Explicitly NOT adopting from full git flow:
No release/* branches (no stabilisation window needed for a docs+hooks framework).
No hotfix/* branches (no multi-version support; forks always run latest).
No support/* branches.
See the rationale in the AgDR draft at the end of this ticket.
Scope
Framework only — NOT managed projects
This is the critical scoping call. apexyard-the-framework has downstream consumers (forks); it needs the release-branch promise. Managed projects under governance (per the registry) have no downstream consumers — they stay trunk-based with main as working. Contributors must not cargo-cult the dev/main pattern into project templates.
Changes required
Repo policy:
Create dev branch from current main HEAD.
Set dev as the default branch for PRs (GitHub repo setting + .github/PULL_REQUEST_TEMPLATE.md target).
Branch protection on main:
Only merges from dev accepted (GitHub "restrict who can push" or a merge-gate check).
Required status checks: all existing + a "tag present" check on the merge commit.
Branch protection on dev:
Same as today's main protection (required checks, review markers, CEO approval, design review).
Hooks:
block-main-push.sh — expand to block direct commits/pushes to bothmain and dev. Today it blocks main; we need dev too.
validate-pr-create.sh — default base is now dev; warn if a PR targets main but the author isn't on dev-HEAD (likely an accidental miss-target).
check-upstream-drift.sh — no change (already tag-based; works identically).
pre-push-gate.sh (from apexyard#111) — no change.
CI:
Every workflow trigger that says on: push: [main] → add dev.
Every workflow that says on: pull_request: [main] → switch to [dev] as primary; keep main for the release-PR case.
Skills:
New /release skill:
Reads current dev HEAD, diffs against main.
Proposes a semver bump based on conventional commit types in the diff (breaking → major, feat → minor, fix/chore/docs → patch). Author can override.
Generates a changelog draft from commit messages (type+subject), grouped by type.
Creates the release PR: release/vX.Y.Z branch from dev, PR from there to main, title release: vX.Y.Z.
After the release PR merges: tags the merge commit, pushes the tag.
Optionally: opens a GitHub Release with the changelog body.
/update skill — no change (already pulls upstream/main, which is now strictly release-tagged).
Docs:
CLAUDE.md — new section clarifying the two-branch model, with the explicit "framework only, not managed projects" rule.
docs/multi-project.md — reiterate that managed projects stay trunk-based.
A first release cut through the new flow lands cleanly (dogfood).
Risks / Dependencies
Risk of contributor confusion. Short-term: PRs get opened against main by force of habit, get rejected, author is annoyed. Mitigation: clear banner on the repo, .github/PULL_REQUEST_TEMPLATE.md with a heading that names the target, migration-week grace period where hooks warn-only.
Risk of cargo-culting into managed projects. Mitigation: docs must be loud about framework-only. Add a SessionStart check (or at least a doc warning) if a managed-project registry entry has a dev branch.
Risk of stale release cadence. If dev → main cadence slips, adopters get stale updates. Mitigation: target monthly cuts unless nothing material has landed.
Risk of the /release skill auto-tagging wrong version. Mitigation: always show the proposed bump for confirmation; never auto-tag without the author's explicit OK.
Two-branch strategy: daily work on dev, tagged releases on main. No explicit release or hotfix branches.
Trunk-based
Single long-lived branch (usually main); features merge directly. What managed projects use.
Semver
Semantic versioning: MAJOR.MINOR.PATCH where MAJOR = breaking, MINOR = backward-compatible feature, PATCH = backward-compatible fix.
AgDR draft (to commit as docs/agdr/AgDR-NNNN-release-branch-model.md during implementation)
---id: AgDR-NNNNtimestamp: YYYY-MM-DDTHH:MM:SSZagent: claudemodel: claude-opus-4-7trigger: user-promptstatus: executed---# Adopt release-cut branch model (dev + main + tags) for apexyard> In the context of external adopters forking apexyard and pulling from `upstream/main`,> facing the problem that daily work-in-progress was polluting what downstream users see as "the framework",> I decided to move daily work to a `dev` branch and reserve `main` for tagged releases (dev → main merge + semver tag),> to achieve a predictable, adopter-safe upstream without the ceremony of full git flow,> accepting that contributors need to learn a slightly different target branch and that release cadence must be curated.## Context- apexyard started as a single-user framework; `main` was both the working branch and the consumed surface.
- External adopters now fork the repo and sync via `/update` (which pulls `upstream/main`). Every commit on main reaches them immediately.
- Drift detection is already tag-based (apexyard v1.1.0), which means the infrastructure for release semantics exists; the policy does not.
- The model must stay lightweight — apexyard is docs + hooks + skills, not a compiled artefact needing stabilisation windows.
## Options Considered| Option | Pros | Cons ||--------|------|------||**A. Stay trunk-based (status quo)**| Zero change cost; one branch to reason about | Adopters pull WIP; no release promise ||**B. Trunk + tags only (forks pin to tags)**| Minimal policy change |`/update` still pulls `main` HEAD unless we rewrite it to pull latest tag; adopters who don't pin still see WIP ||**C. Release-cut (dev + main + tags) — CHOSEN**| Clean promise: main = released; light ceremony; compatible with existing tag-based drift | Contributors retarget PRs; release cadence must be curated; slight onboarding friction ||**D. Full git flow (main + develop + release/* + hotfix/* + support/*)**| Handles multi-version maintenance, stabilisation, emergency patches | Heavy for a framework with one supported version and no compilation; most modern teams don't need it; overkill |## Decision
Chosen: **Option C (release-cut)**, because it gives external adopters the release promise they need without the ceremony they don't. Full git flow's extra branch types (`release/*`, `hotfix/*`, `support/*`) solve problems apexyard doesn't have — there's no stabilisation window, no multi-version maintenance, and no compilation step that benefits from a soak period.
## Consequences**Kept:**- All existing hooks (block-main-push, validate-branch-name, validate-pr-create, merge-gate hooks) — they apply to `dev` PRs as-is.
- Tag-based drift detection — continues to work unchanged.
-`/update` skill — no behavioural change; semantic upgrade (pulls release-only content).
**Added:**-`dev` branch as the daily-work trunk; default branch for PRs.
- Release-PR flow (`dev` → `main` + semver tag).
-`/release` skill to standardise the cut.
- Branch protection on `main` restricting merges to release-PRs only.
**Dropped:**- Rolling-HEAD semantics on `main`. Adopters can no longer casually pull unreviewed WIP.
**Non-consequences (explicitly):**- Managed projects under apexyard governance do NOT adopt this pattern. They stay trunk-based because they have no downstream consumers.
- No hotfix/support branches. If multi-version maintenance becomes a need, revisit.
## Artifacts- Implementing ticket: this issue.
- Release skill: `.claude/skills/release/SKILL.md` (added in implementation).
- Release-process doc: `docs/release-process.md` (added in implementation).
Driver
External adopters are now using apexyard. Every commit on
upstream/mainis (potentially) pulled into their fork when they run/update. Under the current trunk-based model,mainreceives daily work-in-progress — half-finished skills, in-flight hook experiments, docs refactors mid-iteration. This was fine when the framework had one user; it's not fine now.Fork users deserve
upstream/mainto mean "the state a reasonable adopter should live on." That's a release-branch promise, not a trunk promise.Proposed model: "release-cut" (gitflow-lite)
Two long-lived branches, no release or hotfix branches:
dev— every feature/fix/chore PR targets here. All existing PR hooks + review gates apply unchanged.main— only receives merges fromdev, via release PRs. Every merge tomainis tagged with a semver. Direct pushes blocked (already enforced byblock-main-push.sh).dev→mainmerge +git tag vX.Y.Z+git push --tags.Explicitly NOT adopting from full git flow:
release/*branches (no stabilisation window needed for a docs+hooks framework).hotfix/*branches (no multi-version support; forks always run latest).support/*branches.See the rationale in the AgDR draft at the end of this ticket.
Scope
Framework only — NOT managed projects
This is the critical scoping call. apexyard-the-framework has downstream consumers (forks); it needs the release-branch promise. Managed projects under governance (per the registry) have no downstream consumers — they stay trunk-based with
mainas working. Contributors must not cargo-cult the dev/main pattern into project templates.Changes required
Repo policy:
devbranch from currentmainHEAD.devas the default branch for PRs (GitHub repo setting +.github/PULL_REQUEST_TEMPLATE.mdtarget).main:devaccepted (GitHub "restrict who can push" or a merge-gate check).dev:mainprotection (required checks, review markers, CEO approval, design review).Hooks:
block-main-push.sh— expand to block direct commits/pushes to bothmainanddev. Today it blocksmain; we needdevtoo.validate-pr-create.sh— default base is nowdev; warn if a PR targetsmainbut the author isn't ondev-HEAD (likely an accidental miss-target).check-upstream-drift.sh— no change (already tag-based; works identically).pre-push-gate.sh(from apexyard#111) — no change.CI:
on: push: [main]→ adddev.on: pull_request: [main]→ switch to[dev]as primary; keepmainfor the release-PR case.Skills:
/releaseskill:devHEAD, diffs againstmain.release/vX.Y.Zbranch fromdev, PR from there tomain, titlerelease: vX.Y.Z./updateskill — no change (already pullsupstream/main, which is now strictly release-tagged).Docs:
CLAUDE.md— new section clarifying the two-branch model, with the explicit "framework only, not managed projects" rule.docs/multi-project.md— reiterate that managed projects stay trunk-based.docs/getting-started.md— contributor flow updated (PRs targetdev).docs/release-process.md(new) — how to cut a release, when, and the semver policy.docs/agdr/capturing the decision (draft below).Migration:
maintodevbefore the cutover. Document the one-liner:gh pr edit N --base dev.Acceptance Criteria
devbranch exists, set as default.mainbranch protection restricts merges to release-PRs fromdevonly.block-main-push.shblocks direct push/commit to bothmainanddev.validate-pr-create.shwarns on misdirected base.devpush +dev-targeting PRs./releaseskill exists and produces a valid release PR fromdevHEAD./releaseskill tags the merged commit with semver and pushes the tag.dev(zero PRs orphaned onmain).multi-project.md,getting-started.mdupdated.docs/release-process.mdexists.docs/agdr/.Risks / Dependencies
mainby force of habit, get rejected, author is annoyed. Mitigation: clear banner on the repo,.github/PULL_REQUEST_TEMPLATE.mdwith a heading that names the target, migration-week grace period where hooks warn-only.devbranch.dev→maincadence slips, adopters get stale updates. Mitigation: target monthly cuts unless nothing material has landed./releaseskill auto-tagging wrong version. Mitigation: always show the proposed bump for confirmation; never auto-tag without the author's explicit OK.Glossary
dev, tagged releases onmain. No explicit release or hotfix branches.main); features merge directly. What managed projects use.MAJOR.MINOR.PATCHwhere MAJOR = breaking, MINOR = backward-compatible feature, PATCH = backward-compatible fix.AgDR draft (to commit as
docs/agdr/AgDR-NNNN-release-branch-model.mdduring implementation)