Branches & Merges: The Governance Spine
Four domains sharing "one change, one notification, explicit approval" turned out to be only half the story — they didn't share how a change actually got to that notification. Env value edits applied immediately and fanned out live. API and validation edits sat as a hidden per-service draft, swept up by a central Publish button. DB table/column proposals used a third mechanism entirely, a boolean flag an admin flipped. Three inconsistent staging models, one governance promise. We replaced all three with one.
Everything stages on a Branch now
A Branch is scoped to the whole workspace, not to any one domain — an env value, a DB column, an API route, and a validation rule can all ride the same branch, staged and reviewed as one unit. Picking or creating a branch happens once, from a selector in the topbar; every governed-edit form after that just stages its change against whichever branch is active.
One review, then the same per-service checklist as before
Submitting a branch creates exactly one notification — a single Owner/Admin approve/reject on the whole branch, the same shape as a pull request review. The reviewer isn't deciding blind: the branch page shows a per-domain change summary, an impact matrix previewing exactly which services would be notified, a readiness score that docks points for conflicts and breaking changes, and a comment thread where a reviewer can formally request changes. Approving merges every staged change into its real master row and then runs the exact same per-domain trigger the product always used, so the part everyone already trusted — each individually affected service still gets its own approve/reject on the value itself — didn't go away. It just moved to fire from the merge instead of from the original edit.
Conflicts are caught, not silently overwritten
Every staged change snapshots the entity's value at the moment it was staged. If something else changes that same entity before your branch merges, the snapshot won't match reality anymore and the merge is blocked — a one-click rebase re-snapshots against current state so you can see exactly what moved before deciding to proceed, instead of one edit silently clobbering another. The branch page also warns you earlier than that: it lists the other open branches touching the same entities, so a collision is visible while both branches are still drafts.
And merges are undoable
A merged branch can be reverted: OneEnv creates a new draft branch with before/after swapped for every update-type change, linked back to the branch it undoes, and it goes through the standard submit → review → merge path like any other change — an undo is a governed decision too, not a backdoor. Structural creates/deletes are skipped and reported rather than guessed at.
What this replaced
| Before — three mechanisms | Now — one Branch |
|---|---|
| Env value edits applied immediately, fanned out live | Staged as a branch_change, only applied when the branch merges |
| API/validation edits: a hidden per-service draft, swept up by a central Publish button | Staged the same way as everything else on your active branch |
| DB table/column proposals: a pending-promotion flag an admin flipped | A branch_change with the row marked private until merge |
A merged branch is also the permanent record of what changed — there's no separate versioned snapshot table to keep in sync with it. What's deliberately unchanged: a service's own local override still stages then publishes on its own, no branch involved — that was never a shared-value governance decision, just a service opting itself out. And plain entity creation, renames, and archives stay exactly as silent and immediate as they always were; only the specific edits that other people rely on are branch-gated.
