Files
2026-05-27 23:29:53 +01:00

136 lines
9.4 KiB
Markdown

## Context
`packages/` currently holds 41 sibling folders at depth 2 (`packages/<plugin>/<pkg>/`). 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/<pkg>` 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/<category>/<plugin>/<pkg>/`
**Chosen:** keep the existing `<plugin>/<pkg>/` pair and add `<category>/` above it.
**Alternatives:**
- *Drop the plugin level* (`packages/<category>/<pkg>/`): 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/<plugin> packages/<category>/<plugin>` (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/<plugin>/<pkg>` 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/<category>/` 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/<category>/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.