## Context `packages/` currently holds 41 sibling folders at depth 2 (`packages///`). Three plugins (`geo`, `i18n`, `r3f`) collide with same-named packages they contain, producing `packages/geo/geo/`, `packages/i18n/i18n/`, `packages/r3f/r3f/`. A 4th catch-all (`platform/`) groups unrelated packages, and `STATUS.md` admits an implicit `unknown/` bucket. The user wrote `PROPOSED_STRUCTURE.md` with 18 stable category names; this design turns that into an executable migration. The user's first-draft mechanic was a `packages_temp/` swap. That breaks Git rename detection for files whose containing directory is recreated mid-operation. `git mv` per plugin folder is strictly better — same end-state, full rename detection, single atomic commit. Constraints: no public surface change, no version bump, no code edits in the rename commit, `genesis` branch only. ## Goals / Non-Goals **Goals:** - One canonical home for every package, enforceable by a CI gate. - Eliminate same-name nesting (`geo/geo` etc.) and the `platform/` catch-all. - Preserve `git log --follow` history across the move. - Update every workspace-glob, turbo, vitest, tsup, biome, and script reference in lockstep so `bun install && bunx turbo build` works immediately after the commit. - Refresh `MANIFEST.md`, `CLAUDE.md`, `STATUS.md`, `assignment.yml` to describe the 3-level layout. **Non-Goals:** - Renaming any `@suaveplan/` import path. Only filesystem paths change. - Splitting or merging packages. Code is byte-identical post-move. - Touching archived OpenSpec changes' content (paths quoted inside archives stay frozen). - Adopting forward-looking categories for packages that do not yet exist (e.g. `video`, `store`, `logistics`, `gtap`, `vatdash` named in `PROPOSED_STRUCTURE.md`); those land when those packages land. - Bumping any package version. ## Decisions ### 1. Three-level layout: `packages////` **Chosen:** keep the existing `//` pair and add `/` above it. **Alternatives:** - *Drop the plugin level* (`packages///`): cleaner tree but loses the suaveplan-skills plugin grouping that `assignment.yml` depends on, and forces a flat list of ~270 package folders inside large categories like `react/` and `games/`. - *Flatten same-name pairs* (hoist `geo/geo/` → `geo/`): solves nesting but creates an inconsistent depth (some packages at 2 levels, others at 3) that breaks every workspace glob. Three-level is the only option that keeps glob-depth uniform AND preserves the plugin grouping. ### 2. `git mv` per plugin folder, one commit **Chosen:** for each of the 41 plugin folders, run `git mv packages/ packages//` (creating the category directory first). Squash into a single rename commit. **Alternatives:** - *`packages_temp/` swap* (user's draft): breaks rename detection on files whose directory is recreated; reviewers see add+delete, not rename. Rejected. - *Script-driven from `assignment.yml`*: still uses `git mv` internally, but the script becomes a single point of failure. We do the moves by hand from a checklist for auditability; the script lives in `tasks.md` as a documented helper. ### 3. Path-shape CI gate **Chosen:** add a small script (`scripts/lint/check-package-paths.ts`) that walks `packages/`, asserts every `package.json` is at depth 3 (category/plugin/pkg), and asserts the category segment is in the canonical 18-name list. Wire it into `turbo lint`. **Alternatives:** - *Enforce only via `package.json` workspaces glob*: Bun silently ignores unmatched folders, so a stray two-level package becomes invisible rather than an error. Rejected. - *No gate*: relies on convention drift; the original two-level layout is itself evidence that convention without enforcement decays. ### 4. Canonical 18-category list from `PROPOSED_STRUCTURE.md` **Chosen:** adopt the 18 categories as the closed vocabulary. `assignment.yml` maps every current plugin to exactly one. Adding a 19th requires a new OpenSpec change that modifies the `monorepo-layout` spec. **Alternatives:** - *Open vocabulary*: invites the same drift that produced `platform/` and `unknown/`. Rejected. ### 5. Plugin-to-category assignments (binding) From `PROPOSED_STRUCTURE.md`, the 41 existing plugin folders map as follows: | Category | Plugin folders moving in | |---|---| | `foundation/` | `core`, `data`, `random`, `timing`, `bitpacker`, `barcode`, `flow` | | `persistence/` | `database`, `storage`, `cache` | | `network/` | `network`, `drive`, `events` | | `communication/` | `messaging`, `emails` | | `api/` | `api` | | `auth/` | `auth` | | `security/` | `security`, `crypto` | | `monitor/` | `monitor` | | `media/` | `audio`, `audio-fx`, `audio-ir`, `image`, `fonts` | | `react/` | `react`, `forms` | | `r3f/` | `r3f` | | `i18n/` | `i18n` | | `geo/` | `geo` | | `finance/` | `finance`, `payment` | | `commerce/` | `booking`, `contacts` | | `testing/` | `testing` | | `tooling/` | `tooling`, `docs`, `docker` | | `games/` | `games` | The `platform/` plugin folder is split: its contents are inspected and each child plugin moves to the category that fits (most candidates: `foundation/`, `api/`, `communication/`, `monitor/`). The exact per-package decomposition is in `tasks.md` step 3. ### 6. Workspace-glob change **Chosen:** root `package.json` workspaces glob becomes `["packages/*/*/*"]` (single line). No transitional dual-glob. **Alternatives:** - *Dual glob* (`packages/*/*` + `packages/*/*/*`) during transition: encourages partial migrations and delays the path-shape gate. Rejected — we move everything in one commit, no transition window. ## Risks / Trade-offs - **Risk:** turbo cache misses for the whole repo on the first build after the move. **Mitigation:** acceptable one-time cost; cache rebuilds in one `bunx turbo build` run. Run it locally before pushing. - **Risk:** a hard-coded `packages//` path is missed in a vitest config, tsup config, or doc snippet, breaking that package's gate. **Mitigation:** `tasks.md` includes a `rg -n 'packages/[a-z][a-z0-9-]+/[a-z][a-z0-9-]+'` sweep across the repo to find every literal occurrence before commit, plus a green `bunx turbo lint typecheck test` gate post-move. - **Risk:** archived OpenSpec changes quote old paths; rewriting them violates the archive's frozen-content rule. **Mitigation:** archives are explicitly out of scope; readers see the old paths in archived deltas and the new paths in current docs. `CLAUDE.md` notes this. - **Risk:** IDEs and `find` shortcuts that assume 2-level depth break for users. **Mitigation:** announce the change in the commit message; the path-shape CI gate makes the new convention discoverable. - **Trade-off:** small categories (`auth/`, `monitor/`, `i18n/`, `geo/`, `r3f/`, `testing/`, `games/`) each hold a single plugin folder. Accepted because uniform depth beats variable depth — every glob, every script, every readme can assume 3 levels. - **Trade-off:** `git mv` 41 folders is mechanical and tedious. Acceptable for a one-shot reorg; `tasks.md` lists every command verbatim. ## Migration Plan 1. **Branch:** `chore/restructure-packages-by-category` off `genesis`. 2. **Pre-flight:** clean working tree in every worktree (`git worktree list`); turbo cache cleared; `MANIFEST.md` snapshot saved for post-move diff. 3. **Move:** run the 41 `git mv` commands from `tasks.md` step 4 in order. Create each `packages//` directory just before the first `git mv` into it. 4. **Decompose `platform/`:** per-package, choose target category, `git mv`. Remove the empty `platform/` directory. 5. **Update configs:** root `package.json` workspaces glob; `turbo.json`; any `tsconfig*.json` paths; `scripts/folder-reorg/generate-manifest.ts`; `scripts/folder-reorg/assignment.yml`; `scripts/publish.ts`; `scripts/lint/check-test-runner-imports.ts`; any `vitest.config.*` / `vite.config.*` / `tsup.config.*` that hard-codes `packages/`. 6. **Add path-shape gate:** create `scripts/lint/check-package-paths.ts` and wire it into the root `lint` script. 7. **Regenerate manifest:** `bun scripts/folder-reorg/generate-manifest.ts`. Diff against the pre-move snapshot — only path columns and the category column should change. 8. **Update docs:** `CLAUDE.md` Layout section, `STATUS.md`, `PROPOSED_STRUCTURE.md` (mark as adopted). 9. **Verify:** `bun install && bunx turbo lint typecheck test` green end-to-end. `git log --follow` spot-check on three packages (one from `geo/`, one from `core/`, one from the old `platform/`). 10. **Commit:** single squash commit titled `chore(repo): restructure packages under 18 categories`. 11. **Rollback:** if step 9 fails, `git reset --hard origin/genesis` (with explicit user approval — see CLAUDE.md git-safety rules). No partial restructure is shipped. ## Open Questions - **Q1: Does the `platform/` decomposition need its own follow-up OpenSpec change?** Tentative answer: no — its packages are catalogued in `tasks.md` step 3 and the per-package category assignment is mechanical given the 18-category vocabulary. Promote to a follow-up only if any platform sub-package's category is genuinely contested. - **Q2: Do we add a `packages//README.md` per category?** Tentative answer: no for now (CLAUDE.md rule: one README at package root only). Revisit if scanning a category's plugins becomes painful. - **Q3: Do we re-fold archived OpenSpec changes' quoted paths?** Tentative answer: no — archives stay frozen; that is the whole point of the archive.