FLX-ENG-RFC-009 — Repository Consolidation & Cutover Plan¶
| Field | Value |
|---|---|
| RFC ID | FLX-ENG-RFC-009 |
| Status | Active — transition in progress |
| Author | Arun Singh, Senior Distinguished Engineer / Architect (Consulting) |
| Reviewers | Raja Choudhary (Founder), Rahul Bahuguna (Eng Lead), Tushar Garg |
| Scope | Migration of all active development from Distribution-Management-Server-Layered (legacy) to dms_ci_cd_test (canonical); sync agent operation; branch standardisation; decommission sequence |
| Supersedes | Ad-hoc dual-repo workflow; non-RFC-compliant branch names |
| Related | RFC FLX-ENG-RFC-001 §3.2.2 (branching model), Issue #54 (dev branch retirement) |
| GitHub Workflow | .github/workflows/repo-sync.yml |
| Sync Script | scripts/sync/repo_sync.py |
TL;DR — The decision¶
A single automated sync agent mirrors every developer commit from the legacy repo into the canonical repo every 15 minutes, normalising branch names to RFC §3.2.2 standard, enforcing DCO sign-off, and resolving trivial conflicts automatically. When all developers confirm they have switched to the canonical repo, a single --cutover trigger archives the legacy repo, retiring it permanently.
Developers do not need to change anything during the transition period.
After cutover, one git remote set-url command is all that's needed.
1. Problem¶
Two GitHub repositories exist for the same product:
| Repository | Role | State |
|---|---|---|
Distribution-Management-Server-Layered | Legacy — where developers currently commit | Active, 6 branches, non-RFC names |
dms_ci_cd_test | Canonical — has new CI gates, RFC docs, DORA setup | Active, only main branch |
Running two active repos creates:
- Split history — defects fixed in one repo silently absent from the other
- CI gate bypass — developers on the legacy repo miss all 10 quality gates (Epic #6)
- DORA metric distortion — CodePulse measures only the canonical repo; legacy PRs don't count
- Branch naming debt — legacy branches are
21-bagging-flow, notfeature/21-bagging-flow; RFC §3.2.2 is violated before anyone even writes a line of new code - DCO gaps — Windows domain author
RAHUL-BAHUGUNA\RAHULdoes not produce a validSigned-off-bytrailer
2. Design Principles¶
- Zero developer disruption during transition. Developers keep committing to the legacy repo. The sync agent handles the rest.
- Every commit reaches canonical. No work is lost, dropped, or silently skipped. SQLite state and patch-id deduplication guarantee exactly-once delivery.
- Branch names are normalised on import, not after. Legacy feature branches arrive in canonical already named
feature/<id>-<slug>. - Long-lived development branches are retired, not migrated.
devandprodare archived as timestamped tags and their unique commits are merged tomainfirst. - CI/docs files always take target precedence. The 10 quality gates, RFC docs, and MkDocs config committed to canonical are never overwritten by a legacy push.
- Cutover is a single command, not a manual procedure. All complexity is front-loaded into the sync agent; cutover is atomic and reversible (the archive tag preserves all history).
3. Repositories¶
| Property | Legacy (source) | Canonical (target) |
|---|---|---|
| URL | github.com/Flexli-Technologies/Distribution-Management-Server-Layered | github.com/Flexli-Technologies/dms_ci_cd_test |
| Branches | main, dev, prod, 21-bagging-flow, 22-sorterprofileendpoint-dlink-archivalworker, 27-meesho-server-comm-testing, 29-manual-sortation-endpoints-and-sorter-profile-endpoint | main |
| CI gates | None | 10 automated gates (Epic #6, RFC-005) |
| RFC docs | None | docs/rfcs/FLX-ENG-RFC-001 through RFC-009 |
| DORA tracking | None (CodePulse not configured) | Active (CodePulse, EPIC-2) |
| Status after cutover | Archived (read-only) | Canonical — all development |
4. Branch Mapping — Legacy → RFC Standard¶
RFC FLX-ENG-RFC-001 §3.2.2 and GitHub Issue #54 define the allowed branch taxonomy:
main sole long-lived branch
feature/<id>-<slug> short-lived, ≤ 1 sprint
release/<major.minor> release train branch
hotfix/<id> emergency fix
Current legacy branches¶
| Legacy name | RFC target name | Disposition | Reason |
|---|---|---|---|
main | main | Mirror | Already correct |
dev | (none) | Retire | Long-lived dev branch banned (RFC §3.2.2). Unique commits merged to main first, then archive/dev-YYYYMMDD tag created. |
prod | (none) | Archive | Production tracking branch; archive/prod-YYYYMMDD tag preserves history. Not needed in trunk-based model. |
21-bagging-flow | feature/21-bagging-flow | Sync | Issue-number prefix extracted; feature/ added |
22-sorterprofileendpoint-dlink-archivalworker | feature/22-sorterprofile-dlink-archival | Sync | Slug truncated to 40 chars |
27-meesho-server-comm-testing | feature/27-meesho-server-comm-testing | Sync | RFC-compliant after adding feature/ |
29-manual-sortation-endpoints-and-sorter-profile-endpoint | feature/29-manual-sortation-endpoints | Sync | Slug truncated |
Mapping algorithm (deterministic)¶
input branch name
│
├─ == "main" → target: main, SKIP (mirror)
├─ in {dev, develop} → RETIRE (archive tag, merge-to-main)
├─ in {prod, production} → ARCHIVE (tag only)
├─ already RFC-compliant → SKIP (mirror as-is)
├─ starts with feature/ → re-slug inner part
├─ starts with release/ → normalise version string
├─ starts with hotfix/ → normalise issue ID
├─ matches \d+-.* → prefix with feature/
└─ anything else → feature/0-<slugified> (fallback, warns)
5. Sync Agent Architecture¶
┌─────────────────────────────────────────────────────────────────┐
│ GitHub Actions (*/15 * * * * or workflow_dispatch) │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ SyncEngine.sync_once() │ │
│ │ │ │
│ │ 1. git fetch --all --prune (source) │ │
│ │ 2. git fetch --all --prune (target) │ │
│ │ 3. for each source branch: │ │
│ │ a. BranchMapper → disposition + target name │ │
│ │ b. if RETIRE → _retire_branch() │ │
│ │ c. if ARCHIVE → _archive_branch() │ │
│ │ d. if SYNC → _mirror_branch() │ │
│ │ │ │
│ │ _mirror_branch(): │ │
│ │ • check tip SHA vs last_synced in SQLite │ │
│ │ • collect commits ahead: git log <last>..<tip> │ │
│ │ • deduplicate via git patch-id --stable │ │
│ │ • for each new commit: │ │
│ │ – resolve author (Windows domain → real identity) │ │
│ │ – git cherry-pick onto target branch │ │
│ │ – on conflict: try auto-resolve │ │
│ │ · whitespace → keep target │ │
│ │ · .github/ docs/ → keep target │ │
│ │ · else → conflict branch + GH issue │ │
│ │ – enforce DCO (Signed-off-by) │ │
│ │ • git push origin <target-branch> │ │
│ │ • update SQLite: tip_sha, last_synced │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ SQLite /tmp/dms-sync/sync_state.db (cached between runs) │
└─────────────────────────────────────────────────────────────────┘
Memory and compute profile¶
| Operation | Memory bound | Why |
|---|---|---|
git fetch | O(delta) | Only transmits new objects |
| Commit traversal | O(500) patch-ids max | Capped to bound dedup cost |
| Cherry-pick | O(diff size) | Per-commit, not whole branch |
| SQLite | < 1 MB typical | Keyed by SHA (fixed-size rows) |
| Total | < 50 MB RSS | Suitable for GitHub Actions free tier |
6. DCO — Developer Certificate of Origin¶
Every commit reaching the canonical repo must carry:
This certifies the developer has the right to contribute the code (see DCO 1.1).
Enforcement in the sync agent¶
- After cherry-picking each commit onto the target branch, the agent checks the commit message for a
Signed-off-by:trailer. - If absent, it amends HEAD in-place using
git commit --amendwith the resolved author identity. - The original commit SHA is recorded in SQLite (
synced_commits.sha) so future runs do not re-process it.
Developer responsibility¶
Going forward, every PR to either repo (during transition) or to canonical (after cutover) must include a sign-off. Two ways to do this:
# Option A: Always sign-off automatically
git config --global commit.gpgsign false
git config --global format.signoff true
# Option B: Per commit
git commit -s -m "feat: your commit message"
# Option C: Amend last commit if you forgot
git commit --amend -s --no-edit
7. Author Identity Map¶
Legacy commits from Rahul use a Windows domain account name. The sync agent remaps these before writing to canonical:
| Git config value (legacy) | Canonical identity | |
|---|---|---|
RAHUL-BAHUGUNA\RAHUL | Rahul Bahuguna | rahul@flexli.in |
RAHUL-BAHUGUNA/RAHUL | Rahul Bahuguna | rahul@flexli.in |
Tushar Garg | Tushar Garg | tushar@flexli.in |
Action for Rahul: Fix your local Git identity permanently so new commits on any repo use the correct name:
Verify: git config --global --list | grep user
8. Conflict Resolution Policy¶
Auto-resolved (no human needed)¶
| Conflict type | Resolution | Rationale |
|---|---|---|
| Whitespace-only diff | Keep target | Formatting is enforced by dotnet format gate |
.github/workflows/ conflict | Keep target | CI gates must not be overwritten by legacy |
docs/ and docs/rfcs/ conflict | Keep target | RFC docs are canonical in the new repo |
mkdocs.yml, Makefile, requirements-docs.txt | Keep target | Docs infra set up in canonical |
| File deleted on source, exists on target | Keep target | Assume target added something new |
Escalated to human (conflict branch + GitHub issue)¶
Any conflict in application source code (src/, DistributionServerUnitTest/, docker/) that cannot be trivially resolved triggers:
- A
conflict/sync-<branch>-<sha>branch is pushed to canonical with the conflicting changes - A GitHub issue is opened in canonical labelled
P1-highandsync-conflict - The issue body contains the exact
git checkoutandgit mergecommands to resolve it - The sync agent stops processing further commits on that branch until the issue is closed
Resolution checklist for the assigned developer¶
# 1. Fetch and checkout the conflict branch
git fetch origin
git checkout conflict/sync-feature-21-bagging-flow-abc12345
# 2. Merge into the target branch
git checkout feature/21-bagging-flow
git merge conflict/sync-feature-21-bagging-flow-abc12345
# 3. Resolve conflicts in your editor, then:
git add <resolved-files>
git commit -s -m "fix: resolve sync conflict on feature/21-bagging-flow
Closes #<issue-number>"
# 4. Push and close the issue
git push origin feature/21-bagging-flow
gh issue close <issue-number>
9. Step-by-Step Process¶
Phase 1 — Pre-transition (complete these before enabling the workflow)¶
Step 1.1 — Create the sync secret
In dms_ci_cd_test → Settings → Secrets and variables → Actions → New repository secret:
The PAT needs these scopes: - repo — read/write both repos - workflow — update GitHub Actions workflows - admin:org — archive the source repo at cutover (or ask Raja to do this step)
Step 1.2 — Fix developer Git identities
Each developer runs:
# Rahul
git config --global user.name "Rahul Bahuguna"
git config --global user.email "rahul@flexli.in"
# Tushar
git config --global user.name "Tushar Garg"
git config --global user.email "tushar@flexli.in"
Step 1.3 — Enable the GitHub Actions workflow
Step 1.4 — Verify first sync run
Check: - [ ] All legacy branches appear in canonical with feature/ prefix - [ ] dev branch is tagged archive/dev-<date> and deleted from source - [ ] prod branch is tagged archive/prod-<date> and deleted from source - [ ] Each feature branch's commits are in canonical with correct author names - [ ] No commits are missing Signed-off-by in canonical
Phase 2 — Steady state (developers work normally, sync runs every 15 min)¶
Developer workflow during transition (no changes needed):
# Developers continue working in the legacy repo as usual
cd Distribution-Management-Server-Layered/
# Create feature branches (the sync agent will prefix feature/ if missing)
git checkout -b 42-new-endpoint
# ... work ...
git commit -s -m "feat: add new sorting endpoint (#42)"
git push origin 42-new-endpoint
# Open PR on legacy repo
# Every 15 minutes, the sync agent picks up new commits
# and pushes feature/42-new-endpoint to dms_ci_cd_test
Monitoring syncs:
# Check sync logs (CLI)
gh run list --repo Flexli-Technologies/dms_ci_cd_test --workflow=repo-sync.yml
# View specific run log
gh run view <run-id> --repo Flexli-Technologies/dms_ci_cd_test --log
# Check for conflict issues
gh issue list --repo Flexli-Technologies/dms_ci_cd_test --label P1-high
Phase 3 — Cutover (when all developers are ready to switch)¶
Step 3.1 — Confirm cutover readiness checklist
- All open PRs on the legacy repo are merged or closed
- No commits pushed to legacy in the last 24 hours (sync has caught up)
- All developers have confirmed they have read this document
- All conflict issues on canonical are resolved
- Raja has approved the cutover date
Step 3.2 — Trigger cutover
Or via CLI:
What the cutover run does (automated, ~5 minutes):
- Final sync of all remaining source branches
- Archive every remaining source branch as
archive/<branch>-final-<date>tag - Add decommission notice to source
README.mdwith redirect URL - Push the notice commit to source
main - Call GitHub API to archive the source repo (sets it read-only)
- Update canonical repo description and homepage URL
Step 3.3 — Every developer migrates their local clone
# One-time command — takes 10 seconds
git remote set-url origin https://github.com/Flexli-Technologies/dms_ci_cd_test.git
git fetch origin
git branch --set-upstream-to=origin/main main
# Verify
git remote -v
# origin https://github.com/Flexli-Technologies/dms_ci_cd_test.git (fetch)
# origin https://github.com/Flexli-Technologies/dms_ci_cd_test.git (push)
Step 3.4 — Verify post-cutover state
-
Distribution-Management-Server-Layeredshows "Archived" badge on GitHub - Source README shows the decommission notice with link to canonical
-
git pushto source fails with "Repository is archived" - All canonical branches visible:
main,feature/21-bagging-flow, etc. - CI gates run on first PR opened in canonical
Phase 4 — Post-cutover (all work in canonical)¶
New developer workflow in canonical:
# Create feature branch (RFC §3.2.2 standard names required from day 1)
git checkout -b feature/53-net8-port main
# ... work ...
git commit -s -m "feat: port DMS to .NET 8 LTS (#53)"
git push origin feature/53-net8-port
# Open PR → all 10 CI gates run automatically
gh pr create --title "feat: port DMS to .NET 8 LTS" \
--body "Closes #53\n\n## Summary\n..." \
--base main
Branch naming quick reference (issue #54):
# Feature work
git checkout -b feature/<github-issue-id>-<2-5-word-slug>
# Example: feature/53-net8-port, feature/60-openfeature-sdk
# Release
git checkout -b release/1.2 # from main, after sprint freeze
# Hotfix
git checkout -b hotfix/99 # from main, merged back to main + release/*
10. Decommission Artifacts¶
After cutover, these artifacts provide permanent proof of the migration:
| Artifact | Location | Contains |
|---|---|---|
| Archive tags | Source repo (archived) | archive/dev-YYYYMMDD, archive/prod-YYYYMMDD, archive/*-final-YYYYMMDD |
| Sync state DB | GitHub Actions cache / download artifact | Full audit of every commit synced: SHA, branch, timestamp |
| Sync run logs | GitHub Actions → Artifacts (30-day retention) | Per-run output with branch results |
| Conflict issues | dms_ci_cd_test → Issues (labelled sync-conflict) | Any conflicts that required manual resolution |
| Decommission notice | Source README.md (archived) | Redirect link to canonical |
11. Rollback Plan¶
The sync is non-destructive to the legacy repo (it only pushes to canonical). If the sync agent causes problems:
# Stop the scheduler
# dms_ci_cd_test → Actions → DMS Repository Sync → Disable workflow
# If cutover was triggered but needs to be reversed
gh api --method PATCH repos/Flexli-Technologies/Distribution-Management-Server-Layered \
-f archived=false
# (requires admin:org scope)
Legacy branches retired during sync can be restored from their archive tags:
12. Success Criteria¶
- All commits from
Distribution-Management-Server-Layeredare present indms_ci_cd_testwith original author attribution - Every commit in canonical carries
Signed-off-by(verifiable viagit log --grep="Signed-off-by") - All feature branches have RFC-compliant names in canonical
-
devandprodare retired (archive tags exist, branches deleted) - Source repo is archived on GitHub (read-only)
- First PR in canonical runs all 10 CI gates successfully
- No developer commit is missing from canonical (diff:
git log canonical/main ^legacy/mainis empty) - CodePulse DORA data collection active in canonical (EPIC-2 US-2.1)