Files
Developer ee1fa6bee5 refactor: centralize types and extract seed data (Task 1.1)
- Create types/ directory with centralized domain types:
  session, tool-instance, tool-type, git-repository, config-folder,
  tool-config, project, user, api-response
- Remove inline type definitions from API modules;
  re-export from types/ for backward compatibility
- Update state/sessions.tsx to import Session from types/session.ts
- Update all consumer components/pages to import from types/
- Extract seed_builtin_tool_types from main.py to
  seeds/builtin_tool_types.py
- Create types/index.ts barrel export

Quality gates: tsc (pass), eslint (pass), Python syntax (pass)
2026-06-02 18:56:54 +00:00

30 KiB

Design: Repository Restructuring and Modularization

Overview

This design document defines the exact target file layout, import patterns, barrel export structure, and per-phase migration mechanics for the repo restructuring. Every old file is mapped to its new location. All decisions from the spec are implemented concretely.

Key decisions:

  • CSS Modules for component-scoped styles
  • Flat api/ backend structure (no versioning yet)
  • Feature components at components/features/{domain}/
  • Barrel exports for components/ui/, components/features/{domain}/, types/
  • Merge each phase to main immediately

1. Target Directory Structure

1.1 Frontend (apps/web/src/)

src/
├── api/                              # API clients — NO types, NO barrel exports
│   ├── client.ts
│   ├── config-folders.ts             # renamed: config_folders.ts → kebab-case
│   ├── config-profiles.ts
│   ├── dashboard.ts
│   ├── git-repositories.ts
│   ├── profile.ts
│   ├── projects.ts
│   ├── sessions.ts
│   ├── settings.ts
│   ├── ssh-keys.ts
│   ├── tool-configs.ts
│   ├── tool-types.ts
│   └── user-config.ts
│
├── components/
│   ├── layout/                       # App-level layout
│   │   ├── AppShell.tsx              # renamed: app-shell.tsx
│   │   ├── AppShell.module.css
│   │   ├── Navigation.tsx
│   │   ├── Navigation.module.css
│   │   ├── UserChip.tsx
│   │   └── index.ts                  # barrel: export { AppShell, Navigation }
│   │
│   ├── ui/                           # Primitive UI components
│   │   ├── Button.tsx
│   │   ├── Button.module.css
│   │   ├── Card.tsx
│   │   ├── Card.module.css
│   │   ├── Dialog.tsx
│   │   ├── Dialog.module.css
│   │   ├── Input.tsx
│   │   ├── Input.module.css
│   │   ├── LoadingState.tsx
│   │   ├── ErrorState.tsx
│   │   ├── StatusBadge.tsx
│   │   └── index.ts                  # barrel
│   │
│   └── features/                     # Domain-specific components
│       ├── git/
│       │   ├── FileBrowser.tsx       # extracted from repo-workspace.tsx
│       │   ├── FileBrowser.module.css
│       │   ├── GitToolbar.tsx        # renamed: git-toolbar.tsx
│       │   ├── GitToolbar.module.css
│       │   ├── CommitPanel.tsx
│       │   ├── CommitPanel.module.css
│       │   ├── CommitDialog.tsx
│       │   ├── CommitDialog.module.css
│       │   ├── MergeDialog.tsx
│       │   ├── MergeDialog.module.css
│       │   ├── FileEditor.tsx        # renamed: file-editor.tsx
│       │   ├── FileEditor.module.css
│       │   ├── SyntaxHighlighter.tsx
│       │   └── index.ts              # barrel
│       │
│       ├── project/
│       │   ├── ProjectCard.tsx
│       │   ├── ProjectCard.module.css
│       │   ├── ProjectList.tsx
│       │   ├── CreateProjectDialog.tsx
│       │   ├── DeleteConfirmDialog.tsx
│       │   ├── RepositoryCard.tsx
│       │   ├── RepositoryCreateDialog.tsx
│       │   ├── RepositoriesSettingsTab.tsx
│       │   └── index.ts              # barrel
│       │
│       ├── session/
│       │   ├── InstanceList.tsx      # renamed: instance-list.tsx
│       │   ├── InstanceList.module.css
│       │   ├── InstanceCard.tsx
│       │   ├── CreateInstanceDialog.tsx
│       │   ├── SessionCard.tsx
│       │   ├── SessionList.tsx
│       │   ├── CreateSessionForm.tsx
│       │   └── index.ts              # barrel
│       │
│       ├── settings/
│       │   ├── SettingsTabLayout.tsx
│       │   ├── GeneralSettingsTab.tsx
│       │   └── index.ts              # barrel
│       │
│       ├── terminal/
│       │   ├── TerminalComponent.tsx # renamed: terminal.tsx
│       │   ├── TerminalComponent.module.css
│       │   └── index.ts
│       │
│       └── workspace/
│           ├── WorkspaceHeader.tsx
│           └── index.ts
│
├── hooks/
│   ├── use-theme.ts
│   ├── use-auth.ts                   # extracted from state/auth.tsx? No — keep in state/
│   ├── use-api-query.ts              # NEW: reusable data fetching
│   ├── use-local-storage.ts          # NEW
│   └── use-debounce.ts               # NEW: extracted from use-terminal-connection
│
├── pages/                            # Route entry points ONLY
│   ├── DashboardPage.tsx             # renamed: dashboard.tsx
│   ├── DashboardPage.module.css
│   ├── GitHistoryPage.tsx            # renamed: git-history.tsx
│   ├── GitRepositoriesPage.tsx       # renamed: git-repositories.tsx
│   ├── ProfilePage.tsx               # renamed: profile.tsx
│   ├── ProjectSettingsPage.tsx       # renamed: project-settings.tsx
│   ├── ProjectsPage.tsx              # renamed: projects.tsx
│   ├── RepoWorkspacePage.tsx         # renamed: repo-workspace.tsx
│   ├── SessionsPage.tsx              # renamed: sessions.tsx
│   ├── SettingsPage.tsx              # renamed: settings.tsx
│   ├── SshKeysPage.tsx               # renamed: ssh-keys.tsx
│   ├── TerminalPage.tsx              # renamed: terminal.tsx
│   ├── ToolConfigsPage.tsx           # renamed: tool-configs.tsx
│   ├── ToolTypesPage.tsx             # renamed: tool-types.tsx
│   ├── ToolWorkshopPage.tsx          # renamed: tool-workshop.tsx
│   └── PlaceholderPage.tsx           # renamed: placeholder.tsx
│
├── router.tsx                        # unchanged
│
├── state/
│   ├── auth.tsx                      # keep — context is state layer
│   └── sessions.tsx                  # keep — imports from types/session.ts
│
├── styles/
│   ├── tokens.css                    # CSS variables / design tokens
│   ├── global.css                    # reset, body, shell layout grid
│   ├── utilities.css                 # .truncate, .stack, .row, etc.
│   ├── pages/
│   │   ├── sessions.css              # page-specific layout only
│   │   ├── repo-workspace.css
│   │   └── tool-workshop.css
│   └── syntax-highlight.css          # Prism.js overrides
│
├── types/                            # ALL domain types centralized
│   ├── index.ts                      # barrel: re-exports all
│   ├── api-response.ts               # generic ApiResponse<T>, PaginatedResponse<T>
│   ├── config-folder.ts
│   ├── config-profile.ts
│   ├── git-repository.ts
│   ├── project.ts
│   ├── session.ts                    # canonical Session definition
│   ├── ssh-key.ts
│   ├── terminal.ts                   # merged from types/terminal.ts
│   ├── tool-config.ts
│   ├── tool-instance.ts              # canonical ToolInstance definition
│   ├── tool-type.ts
│   ├── user.ts
│   └── user-config.ts
│
├── utils/
│   ├── icons.ts
│   ├── language.ts
│   └── terminal-protocol.ts
│
├── main.tsx                          # import entry point for styles
└── test/
    └── setup.ts

1.2 Backend (apps/api/src/)

src/
├── main.py                           # router mounting + middleware ONLY (target: <100 lines)
├── config.py                         # unchanged
├── database.py                       # unchanged
├── logging_config.py                 # unchanged
│
├── auth/
│   ├── __init__.py
│   ├── cookies.py
│   ├── dependencies.py               # shared: get_current_user, get_owned_project
│   ├── oidc.py
│   └── session.py
│
├── api/                              # flat — no v1/ yet
│   ├── __init__.py
│   ├── auth.py                       # ~200 lines (target)
│   ├── config_folders.py             # ~200 lines (target)
│   ├── config_profiles.py            # ~250 lines (target) — CRUD only
│   ├── dashboard.py                  # ~65 lines (unchanged)
│   ├── git_repositories.py           # ~250 lines (target) — CRUD only
│   ├── health.py                     # ~150 lines (unchanged)
│   ├── instance_proxy.py             # ~125 lines (unchanged)
│   ├── projects.py                   # ~200 lines (target)
│   ├── ssh_keys.py                   # ~170 lines (target)
│   ├── terminal.py                   # ~158 lines (unchanged)
│   ├── tool_configs.py               # ~200 lines (target)
│   ├── tool_instances.py             # ~250 lines (target) — CRUD + lifecycle endpoints only
│   ├── tool_types.py                 # ~250 lines (target)
│   ├── user_config.py                # ~121 lines (unchanged)
│   └── users.py                      # ~156 lines (unchanged)
│
├── models/                           # unchanged — already well-organized
│
├── schemas/                          # NEW: Pydantic request/response schemas
│   ├── __init__.py
│   ├── config_folder.py
│   ├── config_profile.py
│   ├── git_repository.py
│   ├── project.py
│   ├── ssh_key.py
│   ├── tool_config.py
│   ├── tool_instance.py
│   ├── tool_type.py
│   ├── user.py
│   └── user_config.py
│
├── services/
│   ├── __init__.py
│   ├── docker/
│   │   ├── __init__.py
│   │   ├── compose.py                # compose file generation (≤300 lines)
│   │   ├── container.py              # container lifecycle (≤300 lines)
│   │   ├── tunnel.py                 # Cloudflare tunnel (≤200 lines)
│   │   └── config_staging.py         # config folder file writing (≤200 lines)
│   ├── docker_build.py               # unchanged (~69 lines)
│   ├── git/
│   │   ├── __init__.py
│   │   ├── control.py                # renamed: git_control.py
│   │   ├── files.py                  # renamed: git_files.py
│   │   └── history.py                # renamed: git_history.py
│   ├── profile_resolver.py           # unchanged (~251 lines)
│   ├── readiness_probe.py            # unchanged (~66 lines)
│   ├── terminal_manager.py           # unchanged (~193 lines)
│   └── terminal_session.py           # unchanged (~162 lines)
│
├── seeds/
│   ├── __init__.py
│   └── builtin_tool_types.py         # extracted from main.py
│
├── utils/
│   ├── git_url_parser.py             # unchanged
│   └── ...                           # keep existing
│
└── scripts/
    └── seed.py                       # unchanged

2. Barrel Export Patterns

2.1 Frontend Barrels

components/ui/index.ts:

export { Button } from "./Button";
export { Card } from "./Card";
export { Dialog } from "./Dialog";
export { Input } from "./Input";
export { LoadingState } from "./LoadingState";
export { ErrorState } from "./ErrorState";
export { StatusBadge } from "./StatusBadge";

components/features/git/index.ts:

export { FileBrowser } from "./FileBrowser";
export { GitToolbar } from "./GitToolbar";
export { CommitPanel } from "./CommitPanel";
export { CommitDialog } from "./CommitDialog";
export { MergeDialog } from "./MergeDialog";
export { FileEditor } from "./FileEditor";
export { SyntaxHighlighter } from "./SyntaxHighlighter";

types/index.ts:

export type { ApiResponse, PaginatedResponse } from "./api-response";
export type { ConfigFolder } from "./config-folder";
export type { ConfigProfile } from "./config-profile";
export type { GitRepository } from "./git-repository";
export type { Project } from "./project";
export type { Session } from "./session";
export type { SshKey } from "./ssh-key";
export type { TerminalConnectionState, ClientControlMessage, ServerControlMessage } from "./terminal";
export type { ToolConfig } from "./tool-config";
export type { ToolInstance } from "./tool-instance";
export type { ToolType } from "./tool-type";
export type { User } from "./user";
export type { UserConfig } from "./user-config";

2.2 Backend Barrels

services/docker/__init__.py:

from .compose import generate_compose, modify_compose
from .container import create_container, start_container, stop_container, remove_container
from .tunnel import create_tunnel, recreate_tunnel, check_tunnel_health
from .config_staging import stage_config_files

__all__ = [
    "generate_compose", "modify_compose",
    "create_container", "start_container", "stop_container", "remove_container",
    "create_tunnel", "recreate_tunnel", "check_tunnel_health",
    "stage_config_files",
]

services/git/__init__.py:

from .control import branch, checkout, commit, fetch, pull, push, merge
from .files import list_files, read_file, write_file
from .history import get_history, get_commit_detail, get_diff

__all__ = [
    "branch", "checkout", "commit", "fetch", "pull", "push", "merge",
    "list_files", "read_file", "write_file",
    "get_history", "get_commit_detail", "get_diff",
]

3. Import Pattern Examples

3.1 Frontend Imports (After Refactor)

Page component — orchestration only:

// pages/RepoWorkspacePage.tsx
import { useParams, useSearchParams } from "react-router-dom";
import { WorkspaceHeader } from "@/components/features/workspace";
import { FileBrowser, GitToolbar, CommitPanel } from "@/components/features/git";
import { InstanceList } from "@/components/features/session";
import { FileEditor } from "@/components/features/git";
import { useApiQuery } from "@/hooks/use-api-query";
import type { Project, GitRepository } from "@/types";

Feature component — self-contained:

// components/features/git/FileBrowser.tsx
import { useCallback, useEffect, useState } from "react";
import { Icon } from "@/components/ui";
import { apiClient } from "@/api/client";
import type { FileTreeEntry, GitStatus } from "@/types";
import styles from "./FileBrowser.module.css";

API module — pure functions, no types:

// api/git-repositories.ts
import { apiClient } from "./client";
import type { GitRepository, GitStatus, FileTreeEntry } from "@/types";

export async function listRepositories(projectId: string): Promise<GitRepository[]> { ... }

3.2 Backend Imports (After Refactor)

Router — thin, delegates to services:

# api/tool_instances.py
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession

from ..auth.dependencies import get_current_user, get_owned_project
from ..database import get_db
from ..models import User, Project
from ..schemas.tool_instance import CreateInstanceRequest, InstanceResponse
from ..services.docker import container, tunnel, compose
from ..services.profile_resolver import resolve_profile

router = APIRouter(prefix="/projects/{project_id}/repositories/{repo_id}/instances")

@router.post("", response_model=InstanceResponse)
async def create_instance(
    project_id: str,
    repo_id: str,
    request: CreateInstanceRequest,
    user: User = Depends(get_current_user),
    project: Project = Depends(get_owned_project),
    db: AsyncSession = Depends(get_db),
):
    compose_content = compose.generate_compose(...)
    await container.create_container(...)
    return InstanceResponse(...)

Service — pure business logic:

# services/docker/container.py
import subprocess
from pathlib import Path

from .compose import generate_compose
from .config_staging import stage_config_files

def create_container(instance_id: str, project_id: str, compose_path: Path) -> dict:
    stage_config_files(instance_id)
    result = subprocess.run(
        ["docker", "compose", "-f", str(compose_path), "up", "-d"],
        capture_output=True,
        text=True,
    )
    ...

4. CSS Modules Migration Strategy

4.1 How It Works

Vite has built-in CSS Modules support. Naming a file {name}.module.css makes Vite:

  1. Scope all class names to that component
  2. Export a mapping object from the import
import styles from "./Button.module.css";

// In JSX:
<button className={styles.primary}>Click</button>
// → renders as: <button class="Button_primary__a3f7b">Click</button>

4.2 Migration Mechanics

Step 1: Extract component styles from styles.css For each component, find its CSS rules in styles.css and move them to {Component}.module.css.

Example — FileBrowser:

/* components/features/git/FileBrowser.module.css */
.fileBrowser { padding: 0.5rem; overflow: auto; }
.treeEntry { display: block; padding: 0.375rem 0.5rem; ... }
.treeDirectory { font-weight: 500; }
/* etc. */

Step 2: Convert global class names to camelCase in the module Original: .file-tree, .tree-entry, .tree-directory Module: .fileBrowser, .treeEntry, .treeDirectory

Step 3: Update component to import the module

import styles from "./FileBrowser.module.css";

// Before: <div className="file-tree">
// After:  <div className={styles.fileBrowser}>

4.3 Global Styles That Stay Global

These rules remain in styles/global.css or styles/utilities.css:

/* styles/global.css */
:root { /* CSS variables */ }
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); }

/* Shell layout — used by AppShell only */
.shell { min-height: 100vh; display: flex; flex-direction: column; }
.shell-body { display: grid; grid-template-columns: 230px 1fr; }
/* styles/utilities.css */
.stack { display: flex; flex-direction: column; gap: 1rem; }
.row { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

4.4 Page-Specific Layout Styles

Some pages need layout rules that don't belong to any single component:

/* styles/pages/repo-workspace.css */
.repo-workspace { display: flex; flex-direction: column; height: calc(100vh - 60px); }
.workspace-layout { display: flex; flex: 1; overflow: hidden; }
.workspace-sidebar { width: 280px; min-width: 280px; ... }

These are imported by the page component:

import "@/styles/pages/repo-workspace.css";

5. Per-Phase Migration Mechanics

Phase 1: Safe Foundations

Goal: Low-risk extractions that establish the new patterns without touching many files.

Action Old Location New Location Technique
Extract FileBrowser pages/repo-workspace.tsx (inline) components/features/git/FileBrowser.tsx Cut-paste + import rewrite
Create types/session.ts api/sessions.ts + state/sessions.tsx types/session.ts Extract shared interface
Create types/tool-instance.ts api/sessions.ts types/tool-instance.ts Extract interface
Create types/tool-type.ts api/tool-types.ts types/tool-type.ts Extract interface
Create types/git-repository.ts api/git-repositories.ts types/git-repository.ts Extract interface
Create types/project.ts types.ts + scattered types/project.ts Extract from types.ts
Create types/user.ts types.ts + api/auth.ts types/user.ts Extract from types.ts
Create types/api-response.ts Nowhere (new) types/api-response.ts New file for generic types
Update api/sessions.ts inline types imports from types/ Import rewrite
Update state/sessions.tsx inline Session imports from types/session.ts Import rewrite
Move seed data main.py (hardcoded) seeds/builtin_tool_types.py Cut-paste + import
Extract auth deps Duplicated in routers auth/dependencies.py + api/ imports Cut-paste + import rewrite
Create barrel types/ types/index.ts New file

Quality gate: tsc --noEmit, pytest, verify repo-workspace.tsx still works

Phase 2: Style System

Goal: Replace styles.css with modular styles. This is the largest diff but lowest risk (no JS logic changes).

Action Old New Technique
Create styles/tokens.css styles.css (variables section) New file Extract :root and [data-theme="dark"]
Create styles/global.css styles.css (reset + layout) New file Extract *, body, .shell-*
Create styles/utilities.css styles.css (utility classes) New file Extract .stack, .row, .truncate, etc.
Create styles/syntax-highlight.css styles.css (Prism overrides) New file Extract all code[class*="language-"] rules
Create component .module.css files styles.css (component sections) Per-component files Extract .terminal-*, .git-toolbar, .file-editor, etc.
Create page layout CSS files styles.css (page sections) styles/pages/*.css Extract .repo-workspace, .sessions-page, etc.
Delete styles.css styles.css git rm
Update main.tsx imports styles.css imports styles/global.css, styles/tokens.css, etc. Edit import
Update components use global class names import .module.css and use styles.className Edit JSX + add CSS file

Migration order within Phase 2:

  1. Extract tokens + global + utilities + syntax-highlight (safe, no component changes)
  2. Extract component styles one domain at a time: terminal → git → session → settings
  3. Extract page layout styles
  4. Delete styles.css
  5. Run full visual check

Quality gate: npm run build succeeds, npm run lint passes, manual visual verification of all pages

Phase 3a: Backend Shared Dependencies

Goal: Extract duplicated auth helpers so later router splits don't duplicate them.

Action Old New Technique
Extract get_current_user api/tool_instances.py, api/git_repositories.py, etc. auth/dependencies.py Find all _get_user functions, unify, move
Extract get_owned_project Same routers auth/dependencies.py Same
Extract get_owned_repository Same routers auth/dependencies.py Same
Update router imports inline helper from ..auth.dependencies import get_current_user Import rewrite

Quality gate: pytest passes, all integration tests pass

Phase 3b: tool_instances.py Decomposition

Goal: Split the 1,463-line monster into router + services + schemas.

Action Old New Technique
Create schemas Inline Pydantic models in router schemas/tool_instance.py Extract CreateInstanceRequest, InstanceResponse, etc.
Extract compose logic tool_instances.py _modify_compose_file services/docker/compose.py Cut-paste + tests
Extract container lifecycle tool_instances.py start/stop/restart services/docker/container.py Cut-paste
Extract tunnel logic tool_instances.py recreate-tunnel, health services/docker/tunnel.py Cut-paste
Extract config staging tool_instances.py config folder writing services/docker/config_staging.py Cut-paste
Extract instance name gen tool_instances.py _generate_instance_name services/docker/compose.py or new services/instances/naming.py Cut-paste
Slim router 1,463 lines ~250 lines (endpoints + thin handlers) Delete moved code, add imports

Quality gate: pytest, especially integration tests for tool instances

Phase 3c: git_repositories.py + config_profiles.py Decomposition

Action Old New Technique
Create schemas/git_repository.py Inline in router New file Extract
Create schemas/config_profile.py Inline in router New file Extract
Extract file browsing endpoints git_repositories.py api/git_files.py (or keep in router but delegate) Move endpoint handlers
Extract git control endpoints git_repositories.py Keep in router but delegate to services/git/control.py Thin handlers
Extract config profile resolution config_profiles.py services/profile_resolver.py (already exists, use it more) Refactor to use existing service
Slim routers 900 + 877 lines ~250 lines each Delete moved code

Quality gate: pytest, git-related integration tests

Phase 4a: tool-workshop Page Split

Goal: Split the 700-line page into tab components.

Action Old New Technique
Extract ToolTypesTab pages/tool-workshop.tsx (inline state + JSX) components/features/tool-workshop/ToolTypesTab.tsx Cut-paste
Extract ToolConfigsTab Same components/features/tool-workshop/ToolConfigsTab.tsx Cut-paste
Extract ConfigFoldersTab Same components/features/tool-workshop/ConfigFoldersTab.tsx Cut-paste
Slim page ~700 lines ~100 lines (tab switcher + layout) Compose tabs
Create barrel components/features/tool-workshop/index.ts New

Quality gate: tsc, eslint, manual test of all 3 tabs

Phase 4b: Pages Split

Action Old New Technique
Extract SessionList, SessionCard, CreateSessionForm pages/sessions.tsx components/features/session/ Cut-paste
Extract DashboardSummary, QuickActions pages/dashboard.tsx components/features/dashboard/ Cut-paste
Rename pages dashboard.tsx DashboardPage.tsx git mv
Rename pages git-history.tsx GitHistoryPage.tsx git mv
Rename pages repo-workspace.tsx RepoWorkspacePage.tsx git mv
etc. all pages PascalCase matching component git mv

Quality gate: tsc, eslint, router still resolves all routes

Phase 4c: Naming Consistency

Action Old New Technique
Rename component files app-shell.tsx AppShell.tsx git mv
Rename component files git-toolbar.tsx GitToolbar.tsx git mv
Rename component files file-editor.tsx FileEditor.tsx git mv
Rename component files instance-list.tsx InstanceList.tsx git mv
Rename component files terminal.tsx TerminalComponent.tsx git mv
Rename API files tool_types.ts tool-types.ts git mv
Rename API files git_repositories.ts git-repositories.ts git mv
Rename API files config_folders.ts config-folders.ts git mv
Update all imports old paths new paths IDE refactor / sed
Update router old page paths new page paths Edit router.tsx

Quality gate: tsc, eslint, all tests pass

Phase 5: Tests + Docs

Action Description
Add tests for FileBrowser Basic render + interaction tests
Add tests for LoadingState, ErrorState Render tests
Add tests for extracted tabs ToolTypesTab, ToolConfigsTab, ConfigFoldersTab
Write docs/development/naming.md Document all naming conventions
Dead code cleanup Remove unused CSS classes, unused exports
Final quality gate Full tsc, eslint, pytest, build, visual check

6. Risk Mitigation by Phase

Phase 1 (Safe Foundations)

  • Risk: Type extraction breaks consumers
  • Mitigation: Update ALL consumers in the same commit; run tsc before commit

Phase 2 (Style System)

  • Risk: Visual regressions from CSS split
  • Mitigation: Keep original styles.css until all extractions are verified; delete only at phase end

Phase 3 (Backend Decomposition)

  • Risk: Endpoint behavior changes during router slimming
  • Mitigation: Pure cut-paste with zero logic changes; integration tests verify behavior

Phase 4 (Frontend Pages)

  • Risk: Router breaks from file renames
  • Mitigation: Update router.tsx in the same commit as renames; git mv preserves history

Phase 5 (Tests + Docs)

  • Risk: Low — additive only

7. Tooling Recommendations

Import Rewriting

Use VS Code / Vite path aliases to minimize import churn:

// tsconfig.json (already configured)
"paths": {
  "@/*": ["src/*"]
}

Automated Refactoring

  • File moves: git mv (preserves git history)
  • Import updates: VS Code "Move to new file" or find-replace with path patterns
  • Dead CSS detection: purgecss or manual grep — run after Phase 2

Verification Scripts

# File size check
find apps/web/src apps/api/src -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.py" -o -name "*.css" \) -exec sh -c 'lines=$(wc -l < "$1"); if [ "$lines" -gt 300 ]; then echo "OVERSIZED ($lines): $1"; fi' _ {} \;

# Inner component check
grep -rn "const [A-Z].*=" apps/web/src/pages/ || echo "No inner components found"

# CSS module check
find apps/web/src/components -name "*.module.css" | wc -l

# Barrel export check
test -f apps/web/src/types/index.ts && echo "types barrel exists"
test -f apps/web/src/components/ui/index.ts && echo "ui barrel exists"

8. Definition of Done (Per Phase)

Each phase is done when:

  1. All files in the phase are ≤ 300 lines
  2. tsc --noEmit passes
  3. eslint passes
  4. pytest passes (backend phases) or vitest run passes (frontend phases)
  5. No visual regressions (frontend phases)
  6. Commit uses git mv for moves (preserves history)
  7. Commit message references this SDD change: refactor: phase N — description

Design prepared for SDD tasks phase. Next: break into reviewable implementation tasks with line-count forecasts.