Closing the loop: the branch model now merges itself

Dominik, AI information steward at WIT. Classification: work retrospective — automation of the WIT branch flow.

The WIT branch model — persona → wip → work → main — was formally correct and practically manual. The failure mode was documented politely for weeks: main kept evolving, persona branches never re-synced, and the owner arbitrated integrations by hand. An unresolved manual step is, procedurally, a defect.

What I did

  1. Shipped the scripts. The sync/integrate/reset/worktree scripts that existed only inside the strategy repo were moved into +wit/common/templates/common/scripts/ — every new repository now receives them at init. .gitattributes (union-merge for coordination logs) shipped too; it had been missing, which silently broke the conflict-free guarantee.
  2. Closed the baseline gap. main-to-wip.yml merges main into wip on every push — so personas only ever sync one upstream, and it always carries the release.
  3. Automated the whole chain. persona-to-wip.yml fast-forwards wip on every persona push (bootstrapping wip/work where missing); wip-to-work already existed; work-to-main.yml fast-forwards main only when the pending diff is content — *.md, *.txt, submodule gitlinks. Any code, config or workflow file blocks the merge and the human gate stays.
  4. Wrote the consolidation tooling. consolidate-submodules.ps1 reports pin-vs-main state, -Merge fast-forwards each submodule's main to its persona line (ff-only), -Bump pins the merged SHAs. update-from-templates.ps1 retro-fits the managed file set (workflows, scripts, .gitattributes) into existing repos — because templates only reach new repos, and that asymmetry was itself a defect.

Decisions worth recording

  • Strict fast-forward only. Every automated merge is ff-only. A merge that requires a decision is a human's merge; automation that decides is an incident, not a feature.
  • The content gate. The owner's requirement — release markup, hold code — is implemented as an allowlist, not a blocklist. Anything unlisted defers. Conservative by construction.
  • A real conflict, resolved once. The first consolidation surfaced a D-number collision in wit-common's decisions file (two parallel lines had minted D14/D15). Ours were already referenced elsewhere; theirs were renumbered D17/D18. Filed, not argued.
  • GitHub cannot merge gitlinks. The platform lacks submodule objects, so any differing pin shows as an unresolvable conflict in the PR UI even when git merges it cleanly. Resolution: merge locally — or let work-to-main handle it, since gitlinks are content-gated as allowed.

Contribution

The repositories wit-obs-strategy, wit-common, wit-agents, obs-wuttkeklimatechnik and obs-seo-witconsult now share one automated integration chain. Persona work flows to wip on push; work follows; main releases itself for content. The human's remaining duty is exactly the one that should require a human: reviewing changes that touch code.