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
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.
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.
Problem
Releasing currently hand-commits to
main(release: promote dev to main), which makesmainanddevdiverge permanently, and thepackage.jsonversion 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 todev).This is not a missing-tool problem. zeroshot already runs semantic-release correctly:
.releaserc.jsonreleases frommain,@semantic-release/gitcommitschore(release): ${version} [skip ci]back tomain,@semantic-release/npmpublishes, andrelease.ymlruns it on CI success. The drift comes entirely from the branch topology around it — a long-liveddevbranch plus a manualdev → mainpromotion flow. semantic-release commits the version bump only tomain, that commit never flows back todev, and the manual promotions pile up one-directionally.Evidence (as of 2026-07-13)
maincarries 6 commitsdevdoesn't have — all are release bookkeeping, no product work:devis ~59 commits ahead with the actual work. Neither branch can fast-forward to the other.package.jsonsay6.4.0; npmlatestis6.6.0. Thechore(release)bump commit lives only onmain, sodev!= npm.resume --detachruns agents in the caller and leaves zombie clusters #637) is merged, verified, and stuck ondevwith no clean path to release without dragging 59 unrelated commits or cherry-pick gymnastics.Fix: go trunk-based on
main, delete thedev → mainpromotion flowAlign with how
the-open-engine/agentsreleases (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 nodevbranch: PRs merge straight tomain, the release job runs on push tomain, and semantic-release's[skip ci]bump commit lands on the same trunk everyone branches from — so nothing can drift.branches: ["main"], git+npm+github plugins stay. No Changesets — semantic-release is the correct single-package tool and it's already wired.dev.mainis the trunk: every PR targetsmain,mainis always releasable. This removes the divergence at the source.If a staging branch is genuinely wanted
Keep
devas integration only if there's a real QA-staging window, and enforce mechanically:mainis fast-forward-only fromdev(never direct commits tomain; a failed--ff-onlyis a hard error), plus an auto back-mergemain -> devPR after each release so thechore(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:
commitlint(@commitlint/config-conventional) on PR/push/merge queue, so the version signal can't be malformed.main/tags, instead of the defaultGITHUB_TOKEN.mainstill equals the tested SHA before releasing; zeroshot's currentworkflow_runtrigger can race. (agents checks this both before and after minting the release token.)workflow_dispatchrecovery path to rebuild a missing release from a protected tag.One-time reconciliation (before adopting)
Merge
main's 6 release commits intodev(or rebase somainis an ancestor ofdev), land the currently-stuck work, tag that as the new baseline, then delete the promotion flow and retiredev.Acceptance
release: promote dev to mainstyle commits created going forward.mainis the single trunk;package.jsonversion on the trunk matches npmlatestat all times.devis kept at all, it cannot diverge frommainwithout a hard CI failure.