Runbook: Migration to upsquad-ai Enterprise Org
Tracker: #604 Target duration: 2–4 hours focused Owner: Founder (enterprise admin actions) + devops-engineer (scripted automation) Prereq: All gate conditions in #604 met
Summary
Three repos move from personal orgs (ashik-upsqd-x, upsqdvb) to enterprise org upsquad-ai. Issues, PRs, git history, webhooks, secrets, CI workflows all preserved via GitHub's native repo transfer. Go module path changes (github.com/upsquad-ai/upsquad-core → github.com/upsquad-ai/upsquad-core) — mechanical rename across 381 Go files + 21 proto files, automated by scripts/migration/rename-go-module.sh.
Execution order
Phase 0 — Founder prep (before Day M)
Human-only actions in the GitHub enterprise admin panel:
-
Create 7 Enterprise-owned GitHub Apps under Enterprise → Settings → GitHub Apps. Set visibility: Internal. Permissions mirror current bots:
- Issues + Pull requests: Read & Write
- Contents: Read & Write (for write agents), Read (for read-only)
- Metadata: Read
- Members: Read
Apps to create:
upsquad-product-manager,upsquad-project-manager,upsquad-principal-architect,upsquad-backend-sme,upsquad-frontend-sme,upsquad-qa-engineer,upsquad-devops-engineer. -
Install each app on
upsquad-aiorg. Record the installation ID for each. -
Set up the shared key directory on the dev machine (one-time, requires
sudo):# Create shared group both users belong tosudo groupadd -f upsquad-devssudo usermod -a -G upsquad-devs vbsudo usermod -a -G upsquad-devs ashik# Create shared directory, group-readable, not world-readablesudo mkdir -p /opt/upsquad-keyssudo chown root:upsquad-devs /opt/upsquad-keyssudo chmod 2750 /opt/upsquad-keys # setgid so new files inherit group# Verify: both users should appear in the groupgetent group upsquad-devsBoth users need to log out + back in (or run
newgrp upsquad-devs) for group membership to take effect. -
Download each app's private key (
.pemfile) from GitHub and place them in the shared directory:/opt/upsquad-keys/upsquad-product-manager.private-key.pem/opt/upsquad-keys/upsquad-project-manager.private-key.pem/opt/upsquad-keys/upsquad-principal-architect.private-key.pem/opt/upsquad-keys/upsquad-backend-sme.private-key.pem/opt/upsquad-keys/upsquad-frontend-sme.private-key.pem/opt/upsquad-keys/upsquad-qa-engineer.private-key.pem/opt/upsquad-keys/upsquad-devops-engineer.private-key.pemSet permissions so only the
upsquad-devsgroup can read:sudo chown root:upsquad-devs /opt/upsquad-keys/*.pemsudo chmod 0640 /opt/upsquad-keys/*.pem -
Record app IDs and installation IDs in
/opt/upsquad-keys/config.yaml:apps:product-manager:app_id: 1234567installation_id: 9876543project-manager:app_id: ...installation_id: ...# ... 5 moresudo chown root:upsquad-devs /opt/upsquad-keys/config.yamlsudo chmod 0640 /opt/upsquad-keys/config.yaml -
Create GitHub Project v2 named "UpsQuad Delivery Board" under
upsquad-aiorg:- Fields:
Wave(single-select A–L),Track(backend/frontend/devops/qa/docs),PRD(text),Status(status),Sprint(iteration) - Views: "Active work", "By wave", "By track"
- Automation rules: issue opened → Todo; PR linked → Review; issue closed → Done
- Fields:
-
Confirm enterprise policy: 2FA enforcement enabled; audit log export configured (the security-posture decisions from #604).
Phase 1 — This PR lands (before migration day)
Prep scripts + runbook committed to main:
scripts/migration/rename-go-module.shscripts/migration/rename-client-refs.shscripts/migration/smoke-test.shdocs/runbooks/migration-to-upsquad-ai.md(this file)
Phase 2 — Migration day (founder + orchestrator)
Communicate: post "repos moving to upsquad-ai — expect 2-4 hours — all existing clones + URLs will redirect" message to team.
Transfer repos in increasing risk order, verifying after each:
2.1 Transfer upsqdvb/upsquad-admin → upsquad-ai/upsquad-admin
# Run with founder's PAT that has admin on both orgs
curl -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${FOUNDER_PAT}" \
https://api.github.com/repos/upsqdvb/upsquad-admin/transfer \
-d '{"new_owner":"upsquad-ai"}'
# Wait ~30s for GitHub to complete the transfer
# Verify
gh api repos/upsquad-ai/upsquad-admin --jq '.full_name' # should return upsquad-ai/upsquad-admin
git -C /opt/upsquad/upsquad-admin remote set-url origin git@github.com:upsquad-ai/upsquad-admin.git
# Install the 7 GitHub Apps on this repo via the upsquad-ai org settings
# (they're already installed on the org per Phase 0; verify repo-level permission grant)
# Run smoke test
bash scripts/migration/smoke-test.sh upsquad-admin
2.2 Transfer upsqdvb/upsquad-client → upsquad-ai/upsquad-client
Same pattern. Smoke-test includes npx tsc --noEmit + npx vitest run.
2.3 Transfer ashik-upsqd-x/upsquad-core → upsquad-ai/upsquad-core
Same pattern. After transfer but BEFORE merging any new code:
# Update git remote locally
git -C /opt/upsquad/upsquad-core remote set-url origin git@github.com:upsquad-ai/upsquad-core.git
git fetch origin
# Create rename branch
git checkout -b chore/rename-go-module-to-upsquad-ai origin/main
# Run the automated rename
bash scripts/migration/rename-go-module.sh
# Verify
go build ./...
go vet ./...
go test ./... -race -count=1
bash hack/rls-check.sh
# Commit + push + open PR via REST API (use new bot token path)
git add -A
git commit -m "chore: rename Go module to github.com/upsquad-ai/upsquad-core"
GH_TOKEN=$(python3 /opt/upsquad/upsquad-core/scripts/gh-token.py backend-sme) \
git push "https://x-access-token:${GH_TOKEN}@github.com/upsquad-ai/upsquad-core.git" HEAD
# Open PR via REST API and merge
Phase 3 — Post-migration cleanup
-
Update
scripts/gh-token.pyin all 3 repos to point at new central key store (/opt/upsquad-keys/). Commit + PR in each. -
Re-declare any org-scoped Actions secrets at
upsquad-aiorg level. Audit Secrets inherited from the old orgs. -
Update memory files (list below). These live outside the repo in
/home/vb/.claude/projects/-opt-upsquad-upsquad-core/memory/:reference_github_access.md— new org, new path patternreference_client_repo_keys.md— bot keys now central, no per-reporeference_admin_portal_setup.md— samefeedback_bot_identity.md—gh-token.pysignature changedfeedback_issues_live_with_code.md— now enforceable in one org, rule still correctproject_multi_repo.md— update org nameMEMORY.md— sanity check
-
Update
CLAUDE.mdin each of the 3 repos — replace org references. -
Close migration tracker #604 with a final summary comment.
Phase 4 — Enterprise security hardening
(Already locked per #604 decisions.)
- Enforce 2FA on all enterprise members via Enterprise → Policies → Required 2FA.
- Enable audit log export via Enterprise → Settings → Audit log → Streaming to external destination (can be stored locally as JSON for now).
- Deferred: SAML SSO + IP allowlist (separate initiative).
Verification per repo (the smoke-test script checks all of these)
-
gh api repos/upsquad-ai/<repo>returns valid metadata - Old URL redirects for
git clone -
python3 scripts/gh-token.py <agent-slug>returns a working token - Test issue creation under each of the 7 bot identities
- CI runs green on a trivial PR (uses the registry-pull workflow)
- Issues + PRs auto-appear in the
upsquad-aiProject board - Full test suite green (
go test -racefor core,npx vitest runfor client) - Branch protection rules still enforced
Rollback
Each phase has a rollback:
- Transfer rollback: reverse transfer via same REST endpoint — repo goes back to original owner. All URL redirects updated automatically. If done within 1 hour, zero user-visible impact.
- Go module rename rollback:
git revertthe rename commit; imports revert to old module path. Works via GitHub's repo-redirect even if still on new org. - Bot token rollback: keep old per-repo
scripts/gh-token.pyfunctional during transition by not deleting old apps until Phase 4 is stable for 1 week.
Known non-transferred items
Per GitHub documentation (verified 2026-04-17):
- GitHub App installations — must be re-installed on the new org. Covered by Phase 0.
- GitHub Pages — not used in these repos; no impact.
- Org-level Actions secrets — must be re-declared at
upsquad-aiorg level (enterprise-level secrets not yet used). Covered by Phase 3.2.
All other items (issues, PRs, git history, branches, tags, webhooks, repo secrets, branch protection, CI workflows, stars, watchers, fork relationships, CI check history) transfer automatically.
Time budget
| Phase | Time | Who |
|---|---|---|
| 0 (founder prep) | 1–2h | Founder, done before Day M |
| 1 (merge this PR) | 15min | Orchestrator |
| 2.1 admin transfer + smoke | 30min | Founder + orchestrator |
| 2.2 client transfer + smoke | 30min | Founder + orchestrator |
| 2.3 core transfer + Go rename + smoke | 60–90min | Founder + orchestrator |
| 3 post-cleanup | 30min | Orchestrator |
| 4 security | 15min | Founder |
| Total Day M | ~3h |
Contingencies
- Go rename PR fails build — most likely cause is a symlinked or vendor path missed by sed. Manually inspect with
grep -rn "ashik-upsqd-x/upsquad-core"and patch those files. - Bot token fails auth post-transfer — GitHub App may need explicit install on new repo even after org-level install. Revisit Phase 0 step 2.
- CI fails on first PR post-transfer — usually transient (Actions runner needs to re-cache). Retry the workflow.
- Issue references from old org don't render — GitHub redirects work but for cleanliness, run
scripts/migration/rename-client-refs.shin any remaining repo to update#N→upsquad-ai/repo#Nstyle refs.
After declaring migration complete
Follow-up work unblocks:
- Wave I #569 (Admin UI) — dispatch directly in new org
- PRDs #558, #573, #615, #623 — approve + HLD work starts in new org
- Wave J #570, Wave K #571, Wave L #572 — migration prep (Phase 2) of PRD #549 (cutover + drop-legacy)