Skip to content

Release pipeline: dev/main divergence + version drift — go trunk-based (semantic-release already in place) #640

Description

@EivMeyer

Problem

Releasing currently hand-commits to main (release: promote dev to main), which makes main and dev diverge permanently, and the package.json version drifts from what's on npm. Two owned-elsewhere invariants are violated: single source of truth (version) and no manual re-sync (promotion never flows back to dev).

This is not a missing-tool problem. zeroshot already runs semantic-release correctly: .releaserc.json releases from main, @semantic-release/git commits chore(release): ${version} [skip ci] back to main, @semantic-release/npm publishes, and release.yml runs it on CI success. The drift comes entirely from the branch topology around it — a long-lived dev branch plus a manual dev → main promotion flow. semantic-release commits the version bump only to main, that commit never flows back to dev, and the manual promotions pile up one-directionally.

Evidence (as of 2026-07-13)

  • main carries 6 commits dev doesn't have — all are release bookkeeping, no product work:
    557c7b9 release: promote dev to main (#632)
    e3111e7 release: publish dev promotion using package release config (#630)
    0e4a7ec release: publish dev promotion without branch-write release artifacts (#627)
    ec2c5d7 release: publish dev promotion with release-aware analyzer (#625)
    c242575 release: promote dev to main (#617)
    ac354df feat: release deterministic e2e and run-plan hardening (#591)
    
  • dev is ~59 commits ahead with the actual work. Neither branch can fast-forward to the other.
  • Both branches' package.json say 6.4.0; npm latest is 6.6.0. The chore(release) bump commit lives only on main, so dev != npm.
  • Motivating case: fix: resume --detach runs agents in caller and leaves zombie clusters #639 (fixes Bug: resume --detach runs agents in the caller and leaves zombie clusters #637) is merged, verified, and stuck on dev with no clean path to release without dragging 59 unrelated commits or cherry-pick gymnastics.

Fix: go trunk-based on main, delete the dev → main promotion flow

Align with how the-open-engine/agents releases (Nx Release + Conventional Commits — same philosophy, single-package equivalent here is semantic-release, which we already have). The agents repo never hits this problem because it is trunk-based with no dev branch: PRs merge straight to main, the release job runs on push to main, and semantic-release's [skip ci] bump commit lands on the same trunk everyone branches from — so nothing can drift.

feature PR ──▶ main (always green, always releasable)
                 │   Conventional Commit messages drive the bump
                 ▼
   CI passes on main ──▶ semantic-release: version commit + tag vX.Y.Z + npm publish
  • Keep semantic-release as-is. branches: ["main"], git+npm+github plugins stay. No Changesets — semantic-release is the correct single-package tool and it's already wired.
  • Retire dev. main is the trunk: every PR targets main, main is always releasable. This removes the divergence at the source.
  • Version bump stays a real tracked commit on the trunk; branch == npm, always.

If a staging branch is genuinely wanted

Keep dev as integration only if there's a real QA-staging window, and enforce mechanically: main is fast-forward-only from dev (never direct commits to main; a failed --ff-only is a hard error), plus an auto back-merge main -> dev PR after each release so the chore(release) bump flows back. Trunk is simpler and preferred.

Optional hardening (adopt from the agents repo)

These are what makes the agents pipeline robust; worth pulling in while we're here:

  • Conventional Commits gate in CI — validate every non-merge commit with commitlint (@commitlint/config-conventional) on PR/push/merge queue, so the version signal can't be malformed.
  • Dedicated repo-scoped GitHub App token as the only bypass actor for protected main/tags, instead of the default GITHUB_TOKEN.
  • Main-didn't-move guard — confirm main still equals the tested SHA before releasing; zeroshot's current workflow_run trigger can race. (agents checks this both before and after minting the release token.)
  • Refuse-to-overwrite immutable tag/artifact guards, and a workflow_dispatch recovery path to rebuild a missing release from a protected tag.

One-time reconciliation (before adopting)

Merge main's 6 release commits into dev (or rebase so main is an ancestor of dev), land the currently-stuck work, tag that as the new baseline, then delete the promotion flow and retire dev.

Acceptance

  • No release: promote dev to main style commits created going forward.
  • main is the single trunk; package.json version on the trunk matches npm latest at all times.
  • A single merged fix can be released without touching unrelated commits.
  • If dev is kept at all, it cannot diverge from main without a hard CI failure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions