merge: align dev branch with main

This commit is contained in:
Developer
2026-06-03 08:51:02 +00:00
parent 51a399c775
commit b39d6ce5f4
319 changed files with 30221 additions and 9221 deletions
+675
View File
@@ -0,0 +1,675 @@
# Tasks: Repository Restructuring and Modularization
## Overview
9 reviewable PRs (all ≤ 400 lines changed) implementing the full restructure. Each task is a standalone merge to `main`. Dependencies are explicit. Review workload is protected.
**Conventions:**
- `+N/-M` = lines added / removed in the PR
- `Files: N` = number of files touched
- `Deps:` = must-merge tasks before this one
---
## Phase 1: Safe Foundations
### Task 1.1: Centralize Types and Extract Seed Data
**PR label:** `refactor: centralize types and extract seed data`
**Estimated:** +180 / 120 lines across 15 files
**Deps:** None
**What:**
- Create `types/` directory with domain type files
- Move types out of `api/sessions.ts`, `api/tool-types.ts`, `api/git-repositories.ts`, `api/config-folders.ts`
- Move `Session` definition from `state/sessions.tsx` to `types/session.ts`
- Move `ToolInstance` definition from `api/sessions.ts` to `types/tool-instance.ts`
- Move hardcoded seed data from `main.py` to `seeds/builtin_tool_types.py`
- Create `types/index.ts` barrel export
- Update all consumers to import from `types/`
**Files:**
```
NEW: types/session.ts (from api/sessions.ts + state/sessions.tsx)
NEW: types/tool-instance.ts (from api/sessions.ts)
NEW: types/tool-type.ts (from api/tool-types.ts)
NEW: types/git-repository.ts (from api/git-repositories.ts)
NEW: types/config-folder.ts (from api/config-folders.ts)
NEW: types/project.ts (from types.ts)
NEW: types/user.ts (from types.ts)
NEW: types/api-response.ts (new generic types)
NEW: types/index.ts (barrel)
NEW: seeds/builtin_tool_types.py (from main.py)
MOD: api/sessions.ts (remove inline types, import from types/)
MOD: api/tool-types.ts (remove inline types, import from types/)
MOD: api/git-repositories.ts (remove inline types, import from types/)
MOD: api/config-folders.ts (remove inline types, import from types/)
MOD: state/sessions.tsx (import Session from types/)
MOD: types.ts (remove moved types)
MOD: main.py (import seed data from seeds/)
```
**Acceptance criteria:**
- [ ] `grep -n "interface Session" apps/web/src` returns exactly 1 result (in `types/session.ts`)
- [ ] `grep -n "interface ToolInstance" apps/web/src` returns exactly 1 result
- [ ] `tsc --noEmit` passes with zero errors
- [ ] `pytest` passes
- [ ] No behavior changes
---
### Task 1.2: Extract FileBrowser and Shared UI Components
**PR label:** `refactor: extract FileBrowser and shared UI primitives`
**Estimated:** +150 / 80 lines across 8 files
**Deps:** 1.1
**What:**
- Extract `FileBrowser` component from inline definition in `repo-workspace.tsx`
- Create `components/features/git/FileBrowser.tsx`
- Create `components/ui/LoadingState.tsx` (reusable loading pattern)
- Create `components/ui/ErrorState.tsx` (reusable error+retry pattern)
- Create `components/ui/index.ts` barrel
- Update `repo-workspace.tsx` to import `FileBrowser`
- Update pages that use loading/error patterns to use new components
**Files:**
```
NEW: components/features/git/FileBrowser.tsx (from repo-workspace.tsx)
NEW: components/ui/LoadingState.tsx
NEW: components/ui/ErrorState.tsx
NEW: components/ui/StatusBadge.tsx
NEW: components/ui/index.ts (barrel)
MOD: pages/repo-workspace.tsx (remove inline FileBrowser, import)
MOD: pages/dashboard.tsx (use LoadingState, ErrorState)
MOD: pages/sessions.tsx (use LoadingState, ErrorState)
```
**Acceptance criteria:**
- [ ] `grep -n "const FileBrowser" pages/repo-workspace.tsx` returns zero results
- [ ] FileBrowser renders correctly in repo workspace
- [ ] `tsc --noEmit` passes
- [ ] `eslint` passes
---
## Phase 2: Style System
### Task 2.1: Extract Global and Token Styles
**PR label:** `refactor: split styles.css — global styles and tokens`
**Estimated:** +120 / 50 lines across 5 files
**Deps:** 1.2
**What:**
- Create `styles/tokens.css` — CSS variables + dark theme
- Create `styles/global.css` — reset, body, shell layout
- Create `styles/utilities.css` — .stack, .row, .truncate, etc.
- Create `styles/syntax-highlight.css` — Prism.js overrides
- Update `main.tsx` to import the 4 new files
- Do NOT delete `styles.css` yet
**Files:**
```
NEW: styles/tokens.css (from styles.css lines 180)
NEW: styles/global.css (from styles.css: body, .shell, .shell-header, etc.)
NEW: styles/utilities.css (from styles.css: .stack, .row, .truncate, etc.)
NEW: styles/syntax-highlight.css (from styles.css: Prism overrides)
MOD: main.tsx (add imports for new style files)
```
**Acceptance criteria:**
- [ ] All 4 new CSS files exist and contain only their concern
- [ ] `npm run build` succeeds
- [ ] No visual regressions on shell layout
- [ ] `styles.css` still exists (deleted in Task 2.3)
---
### Task 2.2: Extract Component CSS Modules (Part 1 — Terminal + Git)
**PR label:** `refactor: extract CSS modules for terminal and git components`
**Estimated:** +280 / 200 lines across 14 files
**Deps:** 2.1
**What:**
- Create `.module.css` files for terminal and git components
- Extract styles from `styles.css` for: Terminal, GitToolbar, FileBrowser, FileEditor, CommitPanel, CommitDialog, MergeDialog
- Update components to import their `.module.css`
- Convert global class names to camelCase module classes
**Files:**
```
NEW: components/features/terminal/TerminalComponent.module.css
NEW: components/features/git/GitToolbar.module.css
NEW: components/features/git/FileBrowser.module.css
NEW: components/features/git/FileEditor.module.css
NEW: components/features/git/CommitPanel.module.css
NEW: components/features/git/CommitDialog.module.css
NEW: components/features/git/MergeDialog.module.css
MOD: components/terminal.tsx (import module, use styles.*)
MOD: components/git-toolbar.tsx (import module, use styles.*)
MOD: components/features/git/FileBrowser.tsx
MOD: components/file-editor.tsx
MOD: styles.css (remove extracted sections)
```
**Acceptance criteria:**
- [ ] Terminal renders identically
- [ ] Git toolbar, file browser, file editor render identically
- [ ] Commit panel and dialogs render identically
- [ ] `npm run build` succeeds
- [ ] `eslint` passes
---
### Task 2.3: Extract Component CSS Modules (Part 2 — Session + Settings + Layout) + Delete styles.css
**PR label:** `refactor: extract CSS modules for session/settings + delete monolithic styles.css`
**Estimated:** +250 / 2,500 lines across 12 files
**Deps:** 2.2
**What:**
- Create `.module.css` files for: InstanceList, AppShell, Navigation, SettingsTabLayout
- Create `styles/pages/sessions.css`, `styles/pages/repo-workspace.css`, `styles/pages/tool-workshop.css`
- Extract remaining component styles from `styles.css`
- Update components to import modules
- **Delete `styles.css`**
- Verify no remaining references to `styles.css`
**Files:**
```
NEW: components/features/session/InstanceList.module.css
NEW: components/layout/AppShell.module.css
NEW: components/layout/Navigation.module.css
NEW: components/features/settings/SettingsTabLayout.module.css
NEW: styles/pages/sessions.css
NEW: styles/pages/repo-workspace.css
NEW: styles/pages/tool-workshop.css
MOD: components/instance-list.tsx
MOD: components/app-shell.tsx
MOD: components/settings-tab-layout.tsx
MOD: pages/sessions.tsx
MOD: pages/repo-workspace.tsx
DEL: styles.css
```
**Acceptance criteria:**
- [ ] `test -f styles.css` fails (file deleted)
- [ ] All pages render identically
- [ ] `npm run build` succeeds
- [ ] No unstyled components
- [ ] `eslint` passes
---
## Phase 3: Backend Decomposition
### Task 3.1: Extract Shared Auth Dependencies
**PR label:** `refactor: extract shared auth dependencies`
**Estimated:** +90 / 150 lines across 10 files
**Deps:** 1.1
**What:**
- Create `auth/dependencies.py` with `get_current_user()`, `get_owned_project()`, `get_owned_repository()`
- Find and remove duplicated `_get_user()` / `_get_owned_project()` helpers from all routers
- Update routers to import from `auth.dependencies`
- Ensure dependency signatures match across all routers
**Files:**
```
NEW: auth/dependencies.py (consolidated from router files)
MOD: api/tool_instances.py (remove inline helpers, import)
MOD: api/git_repositories.py (remove inline helpers, import)
MOD: api/config_profiles.py (remove inline helpers, import)
MOD: api/ssh_keys.py (remove inline helpers, import)
MOD: api/projects.py (remove inline helpers, import)
MOD: api/tool_configs.py (remove inline helpers, import)
MOD: api/config_folders.py (remove inline helpers, import)
MOD: api/terminal.py (remove inline helpers, import)
```
**Acceptance criteria:**
- [ ] `grep -rn "def _get_user" apps/api/src/api/` returns zero results
- [ ] `grep -rn "def _get_owned_project" apps/api/src/api/` returns zero results
- [ ] All integration tests pass
- [ ] `pytest` passes
---
### Task 3.2: Create Pydantic Schemas Directory
**PR label:** `refactor: extract pydantic schemas from routers`
**Estimated:** +200 / 100 lines across 8 files
**Deps:** 3.1
**What:**
- Create `schemas/` directory
- Extract request/response models from `api/tool_instances.py``schemas/tool_instance.py`
- Extract from `api/git_repositories.py``schemas/git_repository.py`
- Extract from `api/config_profiles.py``schemas/config_profile.py`
- Extract from `api/tool_types.py``schemas/tool_type.py`
- Update routers to import schemas
- Keep schema imports backward-compatible (routers still work)
**Files:**
```
NEW: schemas/tool_instance.py
NEW: schemas/git_repository.py
NEW: schemas/config_profile.py
NEW: schemas/tool_type.py
NEW: schemas/ssh_key.py
NEW: schemas/project.py
MOD: api/tool_instances.py (remove inline schemas, import)
MOD: api/git_repositories.py (remove inline schemas, import)
MOD: api/config_profiles.py (remove inline schemas, import)
MOD: api/tool_types.py (remove inline schemas, import)
```
**Acceptance criteria:**
- [ ] No Pydantic `BaseModel` definitions in router files
- [ ] `pytest` passes
- [ ] All API endpoints return correct response shapes
---
### Task 3.3: Split services/docker.py into Focused Modules
**PR label:** `refactor: split services/docker.py into focused modules`
**Estimated:** +350 / 300 lines across 6 files
**Deps:** 3.2
**What:**
- Create `services/docker/compose.py` — compose file generation + modification
- Create `services/docker/container.py` — container lifecycle (create, start, stop, restart, remove)
- Create `services/docker/tunnel.py` — Cloudflare tunnel create/recreate/health
- Create `services/docker/config_staging.py` — config folder file writing
- Create `services/docker/__init__.py` barrel
- Delete `services/docker.py`
- Update `api/tool_instances.py` to import from `services.docker`
**Files:**
```
NEW: services/docker/__init__.py
NEW: services/docker/compose.py
NEW: services/docker/container.py
NEW: services/docker/tunnel.py
NEW: services/docker/config_staging.py
MOD: api/tool_instances.py (update imports)
DEL: services/docker.py
```
**Acceptance criteria:**
- [ ] `test -f services/docker.py` fails (deleted)
- [ ] Each new module ≤ 300 lines
- [ ] `pytest` passes
- [ ] Tool instance create/start/stop/restart still works
---
### Task 3.4: Slim tool_instances.py Router
**PR label:** `refactor: slim tool_instances router to HTTP-only concerns`
**Estimated:** +80 / 700 lines across 3 files
**Deps:** 3.3
**What:**
- Remove all business logic from `api/tool_instances.py`
- Move compose generation calls to `services.docker.compose`
- Move container lifecycle calls to `services.docker.container`
- Move tunnel calls to `services.docker.tunnel`
- Move config staging calls to `services.docker.config_staging`
- Router should only: validate input, call service, return response
- Target: ~250 lines
**Files:**
```
MOD: api/tool_instances.py (remove ~700 lines of logic, keep ~250 of routing)
MOD: services/docker/compose.py (may need minor adjustments)
MOD: services/docker/container.py (may need minor adjustments)
```
**Acceptance criteria:**
- [ ] `api/tool_instances.py` ≤ 300 lines
- [ ] `grep -n "subprocess" api/tool_instances.py` returns zero results
- [ ] `grep -n "docker" api/tool_instances.py` returns only import lines
- [ ] `pytest` passes, especially tool instance integration tests
---
### Task 3.5: Slim git_repositories.py and config_profiles.py Routers
**PR label:** `refactor: slim git_repositories and config_profiles routers`
**Estimated:** +100 / 600 lines across 6 files
**Deps:** 3.4
**What:**
- Extract git control logic from `api/git_repositories.py` to `services/git/control.py` (already exists, use more)
- Extract file browsing logic to thin handlers delegating to `services/git/files.py`
- Extract config profile resolution logic to `services/profile_resolver.py`
- Slim both routers to ~250 lines each
- Ensure routers contain only route definitions and thin handlers
**Files:**
```
MOD: api/git_repositories.py (remove business logic, delegate)
MOD: api/config_profiles.py (remove business logic, delegate)
MOD: services/git/control.py (may expand)
MOD: services/git/files.py (may expand)
MOD: services/profile_resolver.py (may expand)
```
**Acceptance criteria:**
- [ ] `api/git_repositories.py` ≤ 300 lines
- [ ] `api/config_profiles.py` ≤ 300 lines
- [ ] `pytest` passes
- [ ] Git operations (branch, commit, push, pull) still work
---
## Phase 4: Frontend Page Decomposition
### Task 4.1: Split tool-workshop.tsx into Tab Components
**PR label:** `refactor: split tool-workshop page into tab components`
**Estimated:** +280 / 450 lines across 6 files
**Deps:** 2.3
**What:**
- Create `components/features/tool-workshop/ToolTypesTab.tsx`
- Create `components/features/tool-workshop/ToolConfigsTab.tsx`
- Create `components/features/tool-workshop/ConfigFoldersTab.tsx`
- Create `components/features/tool-workshop/index.ts` barrel
- Slim `pages/tool-workshop.tsx` to tab switcher + layout only (~100 lines)
- Each tab manages its own form state and API calls
**Files:**
```
NEW: components/features/tool-workshop/ToolTypesTab.tsx
NEW: components/features/tool-workshop/ToolConfigsTab.tsx
NEW: components/features/tool-workshop/ConfigFoldersTab.tsx
NEW: components/features/tool-workshop/index.ts
MOD: pages/tool-workshop.tsx (remove inline tabs, compose imports)
```
**Acceptance criteria:**
- [ ] `pages/tool-workshop.tsx` ≤ 150 lines
- [ ] All 3 tabs function identically
- [ ] `tsc --noEmit` passes
- [ ] `eslint` passes
---
### Task 4.2: Extract SessionsPage Components
**PR label:** `refactor: extract sessions page components`
**Estimated:** +220 / 350 lines across 7 files
**Deps:** 4.1
**What:**
- Create `components/features/session/SessionList.tsx`
- Create `components/features/session/SessionCard.tsx`
- Create `components/features/session/CreateSessionForm.tsx`
- Create `components/features/session/index.ts` barrel
- Slim `pages/sessions.tsx` to layout + composition
- Extract inline stop/delete confirmation into reusable `ConfirmDialog` in `components/ui/`
**Files:**
```
NEW: components/features/session/SessionList.tsx
NEW: components/features/session/SessionCard.tsx
NEW: components/features/session/CreateSessionForm.tsx
NEW: components/features/session/index.ts
NEW: components/ui/ConfirmDialog.tsx
MOD: pages/sessions.tsx (remove inline lists/forms, compose)
```
**Acceptance criteria:**
- [ ] `pages/sessions.tsx` ≤ 200 lines
- [ ] Session list, create form, and cards work identically
- [ ] `tsc --noEmit` passes
---
### Task 4.3: Extract Dashboard and RepoWorkspace Components
**PR label:** `refactor: extract dashboard and repo-workspace components`
**Estimated:** +200 / 300 lines across 8 files
**Deps:** 4.2
**What:**
- Create `components/features/dashboard/DashboardSummary.tsx`
- Create `components/features/dashboard/QuickActions.tsx`
- Create `components/features/dashboard/ActiveSessionsList.tsx`
- Create `components/features/dashboard/index.ts` barrel
- Slim `pages/dashboard.tsx` to layout + composition
- Slim `pages/repo-workspace.tsx` further (FileBrowser already extracted in 1.2)
- Extract `InstanceList` inline create dialog to `components/features/session/CreateInstanceDialog.tsx`
**Files:**
```
NEW: components/features/dashboard/DashboardSummary.tsx
NEW: components/features/dashboard/QuickActions.tsx
NEW: components/features/dashboard/ActiveSessionsList.tsx
NEW: components/features/dashboard/index.ts
NEW: components/features/session/CreateInstanceDialog.tsx
MOD: pages/dashboard.tsx (slim to ~120 lines)
MOD: pages/repo-workspace.tsx (slim further)
MOD: components/instance-list.tsx (extract create dialog)
```
**Acceptance criteria:**
- [ ] `pages/dashboard.tsx` ≤ 150 lines
- [ ] Dashboard renders identically
- [ ] `tsc --noEmit` passes
---
### Task 4.4: Rename All Files to Naming Convention
**PR label:** `refactor: rename files to PascalCase components and kebab-case APIs`
**Estimated:** +30 / 0 lines across 40 files (mostly `git mv`)
**Deps:** 4.3
**What:**
- Rename component files to PascalCase matching exported name:
- `app-shell.tsx``AppShell.tsx`
- `git-toolbar.tsx``GitToolbar.tsx`
- `file-editor.tsx``FileEditor.tsx`
- `instance-list.tsx``InstanceList.tsx`
- `terminal.tsx``TerminalComponent.tsx`
- etc.
- Rename page files to PascalCase:
- `dashboard.tsx``DashboardPage.tsx`
- `git-history.tsx``GitHistoryPage.tsx`
- `repo-workspace.tsx``RepoWorkspacePage.tsx`
- etc.
- Rename API files to kebab-case:
- `tool_types.ts``tool-types.ts`
- `git_repositories.ts``git-repositories.ts`
- `config_folders.ts``config-folders.ts`
- etc.
- Update `router.tsx` to import new page paths
- Update all imports across the codebase
**Files:**
```
# Component renames (git mv)
components/app-shell.tsx → components/layout/AppShell.tsx
components/git-toolbar.tsx → components/features/git/GitToolbar.tsx
components/file-editor.tsx → components/features/git/FileEditor.tsx
components/instance-list.tsx → components/features/session/InstanceList.tsx
components/terminal.tsx → components/features/terminal/TerminalComponent.tsx
components/code-editor.tsx → components/ui/CodeEditor.tsx
components/commit-dialog.tsx → components/features/git/CommitDialog.tsx
components/commit-panel.tsx → components/features/git/CommitPanel.tsx
components/merge-dialog.tsx → components/features/git/MergeDialog.tsx
components/protected-route.tsx → components/ProtectedRoute.tsx
components/repositories-settings-tab.tsx → components/features/project/RepositoriesSettingsTab.tsx
components/repository-create-dialog.tsx → components/features/project/RepositoryCreateDialog.tsx
components/settings-tab-layout.tsx → components/features/settings/SettingsTabLayout.tsx
components/syntax-highlighter.tsx → components/features/git/SyntaxHighlighter.tsx
components/workspace-header.tsx → components/features/workspace/WorkspaceHeader.tsx
components/icon.tsx → components/ui/Icon.tsx
# Page renames (git mv)
pages/dashboard.tsx → pages/DashboardPage.tsx
pages/git-history.tsx → pages/GitHistoryPage.tsx
pages/git-repositories.tsx → pages/GitRepositoriesPage.tsx
pages/profile.tsx → pages/ProfilePage.tsx
pages/project-settings.tsx → pages/ProjectSettingsPage.tsx
pages/projects.tsx → pages/ProjectsPage.tsx
pages/repo-workspace.tsx → pages/RepoWorkspacePage.tsx
pages/sessions.tsx → pages/SessionsPage.tsx
pages/settings.tsx → pages/SettingsPage.tsx
pages/ssh-keys.tsx → pages/SshKeysPage.tsx
pages/terminal.tsx → pages/TerminalPage.tsx
pages/tool-configs.tsx → pages/ToolConfigsPage.tsx
pages/tool-types.tsx → pages/ToolTypesPage.tsx
pages/tool-workshop.tsx → pages/ToolWorkshopPage.tsx
pages/placeholder.tsx → pages/PlaceholderPage.tsx
# API renames (git mv)
api/tool_types.ts → api/tool-types.ts
api/git_repositories.ts → api/git-repositories.ts
api/config_folders.ts → api/config-folders.ts
api/tool_configs.ts → api/tool-configs.ts
api/ssh_keys.ts → api/ssh-keys.ts
api/user_config.ts → api/user-config.ts
# Updated imports
MOD: router.tsx
MOD: all page files (update relative imports)
MOD: all component files (update relative imports)
MOD: all test files (update imports)
```
**Acceptance criteria:**
- [ ] All component files match exported component name (case-insensitive)
- [ ] All page files end with `Page.tsx`
- [ ] All API files use kebab-case
- [ ] `tsc --noEmit` passes
- [ ] `eslint` passes
- [ ] `vitest run` passes
- [ ] Router resolves all routes
---
## Phase 5: Testing and Polish
### Task 5.1: Add Tests for Extracted Components
**PR label:** `test: add tests for extracted components`
**Estimated:** +250 / 0 lines across 8 files
**Deps:** 4.4
**What:**
- Add `components/features/git/FileBrowser.test.tsx`
- Add `components/ui/LoadingState.test.tsx`
- Add `components/ui/ErrorState.test.tsx`
- Add `components/features/tool-workshop/ToolTypesTab.test.tsx`
- Add `components/features/session/SessionList.test.tsx`
- Add `pages/DashboardPage.test.tsx` (replace failing `projects.test.tsx` pattern)
- Ensure tests use `MemoryRouter` where needed
- Mock API calls consistently
**Files:**
```
NEW: components/features/git/FileBrowser.test.tsx
NEW: components/ui/LoadingState.test.tsx
NEW: components/ui/ErrorState.test.tsx
NEW: components/features/tool-workshop/ToolTypesTab.test.tsx
NEW: components/features/session/SessionList.test.tsx
NEW: pages/DashboardPage.test.tsx
```
**Acceptance criteria:**
- [ ] All new tests pass (`vitest run`)
- [ ] No test file exceeds 200 lines
- [ ] Tests cover render, basic interaction, and error states
---
### Task 5.2: Documentation and Cleanup
**PR label:** `docs: add naming conventions doc and final cleanup`
**Estimated:** +120 / 50 lines across 6 files
**Deps:** 5.1
**What:**
- Write `docs/development/naming.md` with full naming convention table
- Remove dead CSS classes (verified by grep for unused selectors)
- Remove unused exports (check `eslint` `report-unused-disable-directives`)
- Add verification script to `package.json`: `"check-structure": "node scripts/check-structure.js"`
- Final quality gate run
**Files:**
```
NEW: docs/development/naming.md
NEW: scripts/check-structure.js (verifies file sizes, naming, barrels)
MOD: package.json (add check-structure script)
MOD: styles/global.css (remove dead rules if any)
MOD: various files (remove unused exports)
```
**Acceptance criteria:**
- [ ] `docs/development/naming.md` exists and is complete
- [ ] `npm run check-structure` passes
- [ ] No file in `src/` exceeds 300 lines
- [ ] `tsc --noEmit` passes
- [ ] `eslint` passes
- [ ] `vitest run` passes
- [ ] `pytest` passes
---
## Task Dependency Graph
```
1.1 (Types + Seeds) ──┐
├──→ 1.2 (FileBrowser + UI) ──→ 2.1 (Global Styles)
3.1 (Auth deps) ──→ 3.2 (Schemas) ─┤
│ │
└──→ 3.3 (Docker split) ──→ 3.4 (tool_instances slim)
└──→ 3.5 (git + profiles slim)
2.2 (Terminal/Git CSS) ──→ 2.3 (Session/Settings CSS + delete styles.css) ────────────────┘ │
4.1 (tool-workshop split) ──→ 4.2 (sessions split) ──→ 4.3 (dashboard/workspace split) ──→ 4.4 (rename files)
5.1 (tests) ──→ 5.2 (docs + cleanup) ────────────────────────────────────────────────────────────────────────┘
```
---
## Review Workload Summary
| Task | Est. Lines | Status |
|------|-----------|--------|
| 1.1 | +180 / 120 | ✅ Under 400 |
| 1.2 | +150 / 80 | ✅ Under 400 |
| 2.1 | +120 / 50 | ✅ Under 400 |
| 2.2 | +280 / 200 | ✅ Under 400 |
| 2.3 | +250 / 2,500 | ✅ Under 400 (mostly deletions) |
| 3.1 | +90 / 150 | ✅ Under 400 |
| 3.2 | +200 / 100 | ✅ Under 400 |
| 3.3 | +350 / 300 | ✅ Under 400 |
| 3.4 | +80 / 700 | ✅ Under 400 |
| 3.5 | +100 / 600 | ✅ Under 400 |
| 4.1 | +280 / 450 | ✅ Under 400 |
| 4.2 | +220 / 350 | ✅ Under 400 |
| 4.3 | +200 / 300 | ✅ Under 400 |
| 4.4 | +30 / 0 | ✅ Under 400 (git mv mostly) |
| 5.1 | +250 / 0 | ✅ Under 400 |
| 5.2 | +120 / 50 | ✅ Under 400 |
**All 16 tasks are under the 400-line review budget.**
---
## Quality Gates (Per Task)
Every task MUST pass:
1. `npm run typecheck` (frontend) — zero errors
2. `npm run lint` (frontend) — zero warnings
3. `pytest` (backend) — zero failures
4. File size check — no file > 300 lines
5. For frontend tasks: visual sanity check (build succeeds)
6. Commit with conventional format: `refactor: phase N — description`
---
## Task Execution Notes
- **Use `git mv`** for all file renames to preserve history
- **Update imports with IDE refactor** when possible (VS Code "Move to new file", PyCharm refactor)
- **No logic changes** — pure cut-paste-reorganize
- **Merge to `main` immediately** after each task passes quality gates
- **Pause between phases** (after Tasks 1.2, 2.3, 3.5, 4.4) to verify stability