# drop-monorepo-refactor-plan

# Plan: Drop Monorepo Refactor (src/ → apps/+packages/)

**MC:** #10051
**Owner:** John (orchestrator) + CodeCraft + Proveo + Skillforge
**Estimated total:** ~15h CodeCraft + 2h validation + 1h docs = **~18h fleet time**
**Strategy:** 4-PR staged migration (NOT atomic) per Petter's research
**Research:** `/tmp/drop-monorepo-research-10051.md` (347 lines, Petter Graff)

---

## Research Summary

Drop monorepo currently uses npm workspaces under `src/` (3 registered: `src/shared`, `src/drop-api`, `src/drop-app`; `src/drop-mobile` is on separate release cycle, NOT in workspace config but referenced in 3 mobile workflows). Migrating to `apps/`+`packages/` requires touching **80+ hardcoded paths** across 8 GitHub Actions workflows, 2 root Dockerfiles, buildspec.yml (just merged in PR #3), 2 docker-compose files, and root package.json lint-staged.

### Top 3 production risks
1. `Dockerfile.drop-app:59,67` — `WORKDIR /app/src/drop-app` baked into Next.js standalone runner stage. Silent build success / runtime fail on App Runner cold start.
2. `ci.yml:49-53` paths-filter must update atomically with rename. Wrong order = silent CI skip = unreviewed code reaches main.
3. `Dockerfile.api` uses raw source-copy to `/shared/` (not workspace symlinks) — structurally inconsistent with `Dockerfile.drop-app`. Audit separately.

### Why multi-PR (NOT atomic)
- Each PR is reviewable, revertable, testable independently
- CI workflow update (PR 3) is highest-risk single change → its own PR
- Pre-Finanstilsynet: every CI/security-gate change deserves explicit review
- 4 conflict-able PRs > 1 monster PR with 80+ touched paths

---

## Objective

Restructure Drop monorepo to industry-standard Turborepo/pnpm convention (`apps/` for deployables, `packages/` for shared libs) WITHOUT introducing behavior changes, downtime, or production deploys breaking. Land in 4 staged PRs over 2-3 days, with staging validation before prod merge.

---

## Team Orchestration

### Team Members

| ID | Name | Role | Agent Type |
|----|------|------|------------|
| B1 | codecraft-pkg | PR 1: workspace config + packages/ rename | codecraft |
| B2 | codecraft-apps | PR 2: apps/ rename + Dockerfiles + buildspec | codecraft |
| B3 | codecraft-ci | PR 3: 8 GitHub workflow files update | codecraft |
| B4 | codecraft-cleanup | PR 4: delete old src/ + final validation | codecraft |
| V1 | proveo-staging | Staging validation pre-PR-4 merge | proveo |
| V2 | proveo-prod | Production validation post-PR-4 merge | proveo |
| V3 | securion-fintech | Pre-Finanstilsynet security audit | securion |
| D1 | skillforge-docs | BookStack page + memory entries | skillforge |
| R1 | gemini-reviewer | Review every PR before merge | gemini-reviewer |

### Step-by-Step Tasks

---

#### Phase 1 — Internal packages rename (low risk, isolated)

**Task 1: PR 1 — Rename `src/shared` → `packages/shared`**
- Owner: B1 (codecraft)
- BlockedBy: none (research deliverable already done)
- Branch: `feat/monorepo-pr1-packages-shared`
- Scope:
  - `git mv src/shared packages/shared` (preserves history)
  - Root `package.json` workspaces array: `"src/shared"` → `"packages/shared"`
  - `npm install` regenerate lockfile (in Linux container per ZAKON LOCKFILE PORTABILITY)
  - Verify `apps/drop-app/node_modules/@drop/shared` symlink resolves (post-PR-2; for PR 1 verify root `node_modules/@drop/shared` resolves)
  - Update lint-staged glob if `src/shared/` referenced
- Acceptance:
  - [ ] `npm install --ignore-scripts --no-audit` exit 0
  - [ ] `cd src/drop-app && npx tsc --noEmit` exit 0 (shared resolves via root workspace)
  - [ ] `cd src/drop-api && npx tsc --noEmit` exit 0
  - [ ] CI Quality Gate REQUIRED green on PR
  - [ ] No file outside `packages/shared/` and `package.json` + `package-lock.json` modified

**Task 2: Gemini review PR 1**
- Owner: R1
- BlockedBy: 1
- Acceptance: VERDICT: APPROVE

**Task 3: Squash merge PR 1 to main**
- Owner: John
- BlockedBy: 2
- Post-merge: verify main CI green

---

#### Phase 2 — Apps rename + Docker (highest production risk)

**Task 4: PR 2 — Rename `src/drop-*` → `apps/drop-*` + Dockerfile + buildspec updates**
- Owner: B2 (codecraft)
- BlockedBy: 3 (PR 1 merged)
- Branch: `feat/monorepo-pr2-apps-docker`
- Scope:
  - `git mv src/drop-app apps/drop-app`, `src/drop-api apps/drop-api`, `src/drop-mobile apps/drop-mobile`
  - Update root `package.json` workspaces: `"src/drop-api"` → `"apps/drop-api"`, `"src/drop-app"` → `"apps/drop-app"`
  - Update lint-staged globs (3 patterns) + `cd src/drop-*` commands
  - Update `Dockerfile.drop-app`: ALL `src/drop-app` → `apps/drop-app`, `src/shared` → `packages/shared`. **CRITICAL: both `WORKDIR /app/src/drop-app` lines (59 + 67) → `/app/apps/drop-app`**
  - Update `Dockerfile.api`: `src/drop-api` → `apps/drop-api`, `src/shared` → `packages/shared` AND `/monorepo/src/shared/` → `/monorepo/packages/shared/` (the absolute-path hack)
  - Update `buildspec.yml`: 7 occurrences of `src/drop-app` → `apps/drop-app`, `src/shared` → `packages/shared`
  - Update `docker-compose.yml` + `docker-compose.dev.yml` build contexts + bind mounts
  - Verify `next.config.ts` `outputFileTracingRoot: ../../` still resolves to repo root from `apps/drop-app/` (it does — same depth)
  - Local Docker build MANDATORY (ZAKON LOCAL DOCKER BUILD): `docker buildx build --platform linux/amd64 -f Dockerfile.drop-app .` exit 0
- Acceptance:
  - [ ] `npm install --ignore-scripts --no-audit` exit 0
  - [ ] `cd apps/drop-app && npx eslint .` 0 errors
  - [ ] `cd apps/drop-app && npx tsc --noEmit` exit 0
  - [ ] `docker buildx build --platform linux/amd64 -f Dockerfile.drop-app .` exit 0 (LOCAL evidence required)
  - [ ] `docker buildx build --platform linux/amd64 -f Dockerfile.api .` exit 0
  - [ ] CI Quality Gate REQUIRED green on PR
  - [ ] No CI workflow file modified in this PR (saved for PR 3)
  - [ ] No `src/` directory deleted yet (saved for PR 4)

**Task 5: Gemini review PR 2**
- Owner: R1
- BlockedBy: 4
- Acceptance: VERDICT: APPROVE
- Special review focus: Dockerfile WORKDIR lines, Dockerfile.api absolute path, lockfile linux-x64 variants

**Task 6: Squash merge PR 2 to main — CAUTION**
- Owner: John
- BlockedBy: 5
- Note: This DOES trigger `deploy.yml` auto-deploy. Production App Runner will build with new paths. Monitor `aws apprunner describe-service` until RUNNING status. If deploy fails, immediate rollback via CF CNAME flip + previous SHA redeploy per DEPLOY-MAP.md.
- Pre-merge mandatory: external `curl -sI https://app.getdrop.no` shows current production health
- Post-merge mandatory: external `curl -sI https://app.getdrop.no` shows healthy within 10min of CF CNAME flip

---

#### Phase 3 — CI workflows update (silent skip risk)

**Task 7: PR 3 — Update 8 GitHub Actions workflows**
- Owner: B3 (codecraft)
- BlockedBy: 6 (PR 2 merged + production verified RUNNING)
- Branch: `feat/monorepo-pr3-ci-workflows`
- Scope:
  - `ci.yml`: paths-filter (5 lines), working-directory, cache-dependency-path, cd commands, artifact paths
  - `deploy.yml`: docker build context path
  - `deploy-staging.yml`: paths, working-directory, cache-dependency-path, playwright report path
  - `hotfix.yml`: working-directory x2, cache-dependency-path
  - `deploy-aws.yml`: working-directory, docker build context
  - `mobile-ci.yml`: paths-filter, working-directory x3, cache-dependency-path, artifact path
  - `mobile-deploy.yml`: paths-filter, working-directory, git add path
  - `mobile-release.yml`: working-directory, cache-dependency-path, VERSION_FILE path, git add path
- Acceptance:
  - [ ] All 8 workflow files modified, no other code changes
  - [ ] CI Quality Gate REQUIRED green on PR
  - [ ] `gh workflow view "CI — Quality Gate"` runs against new paths
  - [ ] `mobile-ci.yml` triggers on `apps/drop-mobile/**` paths-filter when mobile code changed (test by including 1-line mobile change in PR)

**Task 8: Gemini review PR 3**
- Owner: R1
- BlockedBy: 7
- Acceptance: VERDICT: APPROVE
- Special review focus: paths-filter glob accuracy (silent CI skip is the danger)

**Task 9: Squash merge PR 3**
- Owner: John
- BlockedBy: 8
- Note: deploy-staging.yml manual workflow_dispatch test BEFORE merge, against PR HEAD SHA, to confirm staging path resolution

---

#### Phase 4 — Cleanup + production validation

**Task 10: PR 4 — Delete legacy `src/` directory + ALAI memory updates**
- Owner: B4 (codecraft)
- BlockedBy: 9 (PR 3 merged + main CI green)
- Branch: `feat/monorepo-pr4-cleanup`
- Scope:
  - `git rm -r src/` (now empty after PR 1 + PR 2 moves)
  - Update `BUILD-BLUEPRINT.md` repo structure section + build commands
  - Update `DEPLOY-MAP.md` paths
  - Update `RUNBOOK.md` if any path references
  - Update `project/architecture/*.md` references
  - Update `~/.claude/projects/-Users-makinja/memory/MEMORY-products.md` Drop entry
  - Update any `~/.claude/projects/-Users-makinja/memory/project_drop_*.md` referring to old paths
- Acceptance:
  - [ ] `find . -path "./src" -type d` empty
  - [ ] `grep -r "src/drop-" --include="*.md" --include="*.yml" --include="*.json"` 0 hits in repo (excluding old git history)
  - [ ] CI Quality Gate REQUIRED green on PR

**Task 11: Proveo validation — STAGING (pre-merge)**
- Owner: V1 (proveo)
- BlockedBy: 10 (PR 4 ready, but NOT merged yet)
- Action: trigger `deploy-staging.yml` workflow_dispatch against PR 4 head SHA
- Acceptance:
  - [ ] Staging App Runner reaches RUNNING state within 15min
  - [ ] `curl -s https://staging.getdrop.no/api/health | jq .status` == "ok"
  - [ ] Playwright smoke test (login + dashboard + send-money flow) passes
  - [ ] No new Sentry errors in staging post-deploy
  - [ ] Evidence written to `/tmp/proveo-monorepo-staging-10051.json`

**Task 12: Gemini review PR 4**
- Owner: R1
- BlockedBy: 11
- Acceptance: VERDICT: APPROVE

**Task 13: Securion fintech security audit (pre-prod merge)**
- Owner: V3 (securion / Parisa Tabriz)
- BlockedBy: 11
- Scope: monorepo path rename security implications — secrets accidentally exposed via different paths, .gitignore coverage, JWT/auth boundary integrity, OWASP Top 10 quick check
- Acceptance:
  - [ ] No secret leaks introduced by path changes
  - [ ] Auth middleware paths still correct
  - [ ] No new attack surface introduced
  - [ ] Report at `/tmp/securion-monorepo-10051.md`

**Task 14: Squash merge PR 4 to main**
- Owner: John
- BlockedBy: 12, 13
- Triggers: `deploy.yml` auto-deploy to production
- Pre-merge mandatory: external curl baseline, gh run list main shows green
- Post-merge mandatory: monitor App Runner deploy

**Task 15: Proveo validation — PRODUCTION**
- Owner: V2 (proveo)
- BlockedBy: 14
- Action: post-deploy verification per DEPLOY-MAP.md ZAKON PI2 protocol
- Acceptance:
  - [ ] `curl -s https://app.getdrop.no/api/health | jq .status` == "ok"
  - [ ] Playwright screenshot of dashboard (live data load)
  - [ ] No CI deploy.yml errors
  - [ ] No Sentry error spike in 30min post-deploy
  - [ ] App Runner blue/green flip verified
  - [ ] Evidence at `/tmp/proveo-monorepo-prod-10051.json` + screenshot

**Task 16: Skillforge documentation**
- Owner: D1 (skillforge)
- BlockedBy: 15
- Scope:
  - BookStack page "Drop Monorepo Architecture" — apps/+packages/ structure, deploy paths, conventions
  - Memory entry `project_drop_monorepo_refactor_2026-XX-XX.md` with lessons learned + path migration patterns
  - Update `~/system/rules/zakon-local-docker-build.md` if monorepo-specific gotchas surfaced
- Acceptance:
  - [ ] BookStack page exists and synced
  - [ ] Memory entry created
  - [ ] MEMORY.md index updated

---

## Risk Register

| Risk | Probability | Impact | Mitigation |
|---|---|---|---|
| Dockerfile WORKDIR baked path miss (E1) | MED | HIGH (prod cold start fail) | Mandatory local Docker build in PR 2 |
| paths-filter wrong = silent CI skip (E4) | LOW | CRITICAL (unreviewed code in main) | PR 3 has explicit paths-filter glob review by R1 |
| Lockfile platform variants missing (E6) | MED | MED (CodeBuild fail) | Regenerate in Linux container per ZAKON LOCKFILE PORTABILITY |
| @drop/shared symlink resolution fails (E5) | MED | HIGH (build fail) | PR 1 isolated test before PR 2 starts |
| Sentry source maps un-symbolicate (E8) | LOW | LOW (debug noise only) | Verify post-deploy Sentry dashboard |
| Dockerfile.api absolute path break (E3) | MED | HIGH (drop-api fail) | Local docker build evidence MANDATORY in PR 2 |
| AWS App Runner deploy fails post-merge | LOW | CRITICAL (prod outage) | CF CNAME rollback < 10s + previous SHA redeploy |
| backend/ legacy dir confusion (E9) | LOW | LOW | Document as legacy, do not rename in this MC |
| Mobile workflow paths-filter coupling (E7) | LOW | MED | Tested in PR 3 with 1-line mobile change |

---

## Rollback Plan (per PR)

| PR | Rollback action |
|---|---|
| PR 1 | `git revert <merge-commit>` — pure rename, no behavior change. < 5min recovery. |
| PR 2 | `git revert <merge-commit>` + ECR previous-SHA redeploy via `apprunner start-deployment` + CF CNAME flip. < 15min recovery. |
| PR 3 | `git revert <merge-commit>` — workflows revert. CI may briefly run against wrong paths during revert window (acceptable). < 5min. |
| PR 4 | `git revert <merge-commit>` — restores src/ from history. Same as PR 2 if production-affecting deploy triggers. |

---

## Validation Checkpoints (cumulative across phases)

After EACH PR merge to main:
1. `gh run list --branch main --limit 1` last run = SUCCESS for "CI — Quality Gate"
2. `curl -sI https://app.getdrop.no` returns HTTP 200
3. `aws apprunner describe-service --service-arn <drop-web-arn>` Status: RUNNING
4. No new Sentry errors in 15min after deploy
5. `git log --oneline main` shows clean linear history (squash merges)

After PR 4 (final cleanup):
6. `find /Users/makinja/ALAI/products/Drop -type d -name "src"` returns 0 results
7. Repo size reduced (lockfile + node_modules can re-regenerate in fresh clone)
8. Skillforge BookStack page accessible at https://docs.alai.no

---

## Estimate of Total Time

| Phase | CodeCraft hours | Calendar days |
|---|---|---|
| PR 1 (packages/) | 1.5h | Day 1 |
| PR 2 (apps/ + Docker) | 5h | Day 1-2 |
| PR 3 (CI workflows) | 2.5h | Day 2 |
| PR 4 (cleanup) | 2h | Day 2-3 |
| Proveo + Securion validation | 2h | Day 2-3 |
| Skillforge docs | 1h | Day 3 |
| **Total** | **~14h** | **3 days** |

Add 30% buffer for failure recovery iterations = **~18h fleet, 3 calendar days**.

---

## Required Follow-ups

1. `backend/` legacy directory at repo root — confirm dead code in separate MC, then either rename or delete. NOT in scope of this MC.
2. `mobile-release.yml` cross-references `src/drop-app/src/config/app-versions.json` from within mobile workflow — clarify ownership boundary in followup.
3. Migration to true Turborepo (turbo.json) or pnpm — separate MC. This refactor only renames paths, does NOT change tooling.
4. Sentry post-deploy verification — if source maps un-symbolicate, separate MC for Sentry config update.

---

## Per ZAKON PLAN compliance

- [x] **Validation task** — Tasks 11 (Proveo staging) + 15 (Proveo prod) — end-to-end Playwright + curl + Sentry monitoring (NOT dry-run)
- [x] **Documentation task** — Task 16 (Skillforge) — BookStack page + memory entry

---

## Approval

Plan ready for CEO review. To execute:

```
/build-plan ~/system/specs/drop-monorepo-refactor-plan.md
```

Or dispatch first phase manually:
```
/mehanik "Drop monorepo PR 1: packages/shared rename + workspace config" /Users/makinja/ALAI/products/Drop <new_MC_id>
```