# Spec: Repository Restructuring and Modularization ## Overview Restructure the Headquarter monorepo into a modular, maintainable architecture where every source file has a single responsibility and stays within 300 lines (target: 100–200). No behavior changes. No new features. Pure structural reorganization with standardized naming conventions. **Scope:** Frontend (`apps/web/src/`) and backend (`apps/api/src/`) **Non-goals:** Technology swaps, feature additions, database migrations, CI/CD changes **Target file size:** 100–200 lines; hard limit 300 lines --- ## Naming Conventions (MUST follow) | Layer | File Naming | Component/Function Naming | Example | |-------|------------|--------------------------|---------| | React page components | PascalCase matching exported name | `GitHistoryPage` | `GitHistoryPage.tsx` | | React feature components | PascalCase matching exported name | `FileBrowser` | `FileBrowser.tsx` + `FileBrowser.module.css` | | React UI primitives | PascalCase matching exported name | `Button`, `Dialog` | `Button.tsx` + `Button.module.css` | | React hooks | camelCase | `useTheme`, `useApiQuery` | `useTheme.ts` | | Frontend API modules | kebab-case | — | `tool-configs.ts` | | Frontend utilities | kebab-case | camelCase functions | `terminal-protocol.ts` | | Frontend types | kebab-case | PascalCase interfaces | `session.ts` | | CSS modules | kebab-case matching component | — | `file-browser.module.css` | | Backend routers | snake_case | snake_case handlers | `tool_instances.py` | | Backend services | snake_case | snake_case functions | `docker_compose.py` | | Backend models | snake_case | PascalCase classes | `tool_instance.py` | | Backend tests | snake_case prefixed with `test_` | — | `test_tool_instances.py` | **CSS Modules rule:** Every React component with significant styling gets its own `.module.css` file. Global styles live in `styles/` and only contain resets, tokens, and layout foundations. --- ## Acceptance Criteria ### AC-1: No Monolithic Files Remain **GIVEN** the codebase after restructuring **WHEN** we count lines in every `.ts`, `.tsx`, `.py`, and `.css` file under `src/` **THEN** no file exceeds 300 lines **AND** the average file size under each domain directory is under 200 lines **Test:** Run `find src -type f | xargs wc -l | sort -rn` — verify top result ≤ 300. ### AC-2: Types Are Centralized and Deduplicated **GIVEN** a domain type such as `Session` or `ToolInstance` **WHEN** a developer searches for its definition **THEN** exactly one definition exists under `types/` **AND** `api/sessions.ts` and `state/sessions.tsx` both import from `types/session.ts` **AND** no API module defines types inline **Test:** Search for `interface Session` — expect 1 result. Search for `interface ToolInstance` — expect 1 result. ### AC-3: Styles Are Modular **GIVEN** the frontend build **WHEN** `styles.css` is checked **THEN** it does not exist (deleted) **AND** global styles live in `styles/global.css` (resets + tokens + layout) **AND** component styles live in `.module.css` files co-located with components **AND** page styles live in `styles/pages/{page-name}.css` for page-specific layout only **AND** syntax highlighting styles live in `styles/syntax-highlight.css` **Test:** `test -f src/styles.css` fails. `find src/styles -name "*.css" | wc -l` ≥ 5. ### AC-4: Backend Routers Contain Only HTTP Concerns **GIVEN** any router file under `api/` **WHEN** reading its contents **THEN** it contains only: route definitions, dependency injection, request/response models, and thin handler functions **AND** no Docker CLI calls, no Git subprocess calls, no file I/O, no compose file mutation **AND** all business logic delegates to `services/` modules **Test:** `grep -n "subprocess\|docker\|compose\|os\." apps/api/src/api/*.py` returns zero matches. ### AC-5: Backend Services Are Focused **GIVEN** the `services/` directory **WHEN** listing files **THEN** each service module has a single responsibility (e.g., container lifecycle, tunnel management, compose generation) **AND** `services/docker.py` does not exist (split into focused modules) **Test:** `test -f apps/api/src/services/docker.py` fails. Each `.py` in `services/` is ≤ 300 lines. ### AC-6: Inline Components Are Extracted **GIVEN** any page component **WHEN** reading its JSX **THEN** no inner component definitions exist (no `const FileBrowser = () => ...` inside a page) **AND** all extracted components are importable and testable independently **Test:** `grep -rn "const [A-Z].*=.*=>" apps/web/src/pages/` returns zero results. ### AC-7: Naming Is Consistent **GIVEN** any source file **WHEN** checking its name against the naming table above **THEN** it follows the convention for its layer **AND** every exported React component matches its file name (case-insensitive) **Test:** Script checks that every `.tsx` file's default/named export matches its basename. ### AC-8: All Quality Gates Pass **GIVEN** any phase of the refactor **WHEN** running the quality gates **THEN** `npm run typecheck` (frontend) passes with zero errors **AND** `npm run lint` (frontend) passes with zero warnings **AND** `pytest` (backend) passes with zero failures **AND** no visual regressions are introduced **Test:** Run all gates after each phase. No failures. ### AC-9: Barrel Exports for Stable Boundaries **GIVEN** `components/ui/`, `components/features/{domain}/`, or `types/` **WHEN** importing from those directories **THEN** an `index.ts` barrel export exists **AND** consumers import from the directory, not individual files **AND** one-off utilities and API modules do NOT have barrel exports **Test:** `test -f src/components/ui/index.ts` passes. `test -f src/utils/index.ts` fails. --- ## Requirements ### REQ-1: Frontend Type System The system SHALL centralize all shared domain types under `apps/web/src/types/`. **Rationale:** Prevents drift between API types, state types, and component prop types. #### Scenario: Centralizing Session types - **GIVEN** `Session` is defined in `api/sessions.ts` and `state/sessions.tsx` - **WHEN** the refactor is applied - **THEN** a single `types/session.ts` defines the canonical `Session` interface - **AND** both `api/sessions.ts` and `state/sessions.tsx` import from it - **AND** `api/sessions.ts` no longer exports a `Session` type #### Scenario: API modules lose inline types - **GIVEN** `api/tool_types.ts` defines `ToolType` inline - **WHEN** the refactor is applied - **THEN** `types/tool-type.ts` defines `ToolType` - **AND** `api/tool_types.ts` imports and re-exports it ### REQ-2: Frontend Style Modules The system SHALL use CSS Modules for component-scoped styles. **Rationale:** Eliminates global CSS specificity wars; makes component styles discoverable and deletable. #### Scenario: Component with styles - **GIVEN** `FileBrowser` has custom styles - **WHEN** a developer looks for its styles - **THEN** they find `components/features/git/file-browser/FileBrowser.module.css` - **AND** the module contains only `.fileBrowser` and child selectors - **AND** no global class names leak outside the component #### Scenario: Global styles remain minimal - **GIVEN** `styles/global.css` exists - **WHEN** reading it - **THEN** it contains only: CSS variables, `* { box-sizing }`, `body` reset, and shell layout grid - **AND** it does not contain component-specific rules (cards, buttons, dialogs, etc.) ### REQ-3: Backend Router Separation The system SHALL separate HTTP routing from business logic. **Rationale:** Routers should be thin and testable; business logic should be reusable and independently testable. #### Scenario: Creating a tool instance - **GIVEN** a `POST /instances` request - **WHEN** the router handles it - **THEN** it validates the request body with a Pydantic schema - **AND** it calls `services.instances.create_instance(...)` - **AND** it returns the response - **AND** it does not call `docker compose up`, modify files, or manage tunnels #### Scenario: Starting a tool instance - **GIVEN** a `POST /instances/{id}/start` request - **WHEN** the router handles it - **THEN** it calls `services.instances.lifecycle.start_instance(...)` - **AND** it does not contain subprocess calls ### REQ-4: Backend Service Focus The system SHALL split `services/docker.py` into single-responsibility modules. **Rationale:** Docker operations span compose, containers, tunnels, and config staging — too many concerns for one file. #### Scenario: Service decomposition - **GIVEN** the old `services/docker.py` - **WHEN** the refactor is applied - **THEN** the following modules exist: - `services/docker/compose.py` — compose file generation and modification - `services/docker/container.py` — container lifecycle (create, start, stop, remove) - `services/docker/tunnel.py` — Cloudflare tunnel management - `services/docker/config.py` — config folder staging and file writing - **AND** each module is ≤ 300 lines - **AND** `services/docker.py` does not exist ### REQ-5: Page Component Decomposition The system SHALL split page components into route entry points and feature sub-components. **Rationale:** Pages should orchestrate data and routing, not contain inline UI implementations. #### Scenario: Repo workspace page - **GIVEN** the old `pages/repo-workspace.tsx` - **WHEN** the refactor is applied - **THEN** `pages/repo-workspace/page.tsx` contains only: data loading, layout, and sub-component composition - **AND** `components/features/git/file-browser/FileBrowser.tsx` contains the file tree UI - **AND** `components/features/git/commit-panel/CommitPanel.tsx` contains the commit form - **AND** each extracted component is independently importable #### Scenario: Tool workshop page - **GIVEN** the old `pages/tool-workshop.tsx` - **WHEN** the refactor is applied - **THEN** it is split into: - `pages/tool-workshop/page.tsx` — tab navigation and layout - `components/features/tool-workshop/ToolTypesTab.tsx` - `components/features/tool-workshop/ToolConfigsTab.tsx` - `components/features/tool-workshop/ConfigFoldersTab.tsx` - **AND** each tab component manages its own form state ### REQ-6: Inline Component Extraction The system SHALL not contain inner component definitions. **Rationale:** Inner components cannot be tested independently, cause re-creation on every render, and hide complexity. #### Scenario: No inner components in pages - **GIVEN** any file under `pages/` - **WHEN** searching for `const [A-Z]` followed by a component body - **THEN** zero matches are found - **AND** all previously inner components are moved to `components/` ### REQ-7: Reusable Loading/Error Patterns The system SHALL extract repeated loading/error/retry UI into shared components. **Rationale:** ~8 pages copy the same `status: "loading" | "ready" | "error"` pattern with identical retry buttons. #### Scenario: Loading state - **GIVEN** a page is loading data - **WHEN** the UI renders - **THEN** it uses `` instead of inline JSX #### Scenario: Error state - **GIVEN** a page fails to load data - **WHEN** the UI renders - **THEN** it uses `` instead of inline JSX ### REQ-8: Barrel Exports at Stable Boundaries The system SHALL provide `index.ts` barrel exports for stable module boundaries. **Rationale:** Cleaner imports; encapsulates internal file structure. #### Scenario: Importing UI primitives - **GIVEN** a developer needs `Button` and `Dialog` - **WHEN** they write the import - **THEN** they write `import { Button, Dialog } from "@/components/ui"` - **AND** not `import { Button } from "@/components/ui/button/button"` #### Scenario: No barrel for utilities - **GIVEN** a developer needs `terminal-protocol` utilities - **WHEN** they write the import - **THEN** they write `import { encodeControlMessage } from "@/utils/terminal-protocol"` - **AND** `utils/index.ts` does not exist --- ## API / Protocol Changes None. This is a pure reorganization refactor. All HTTP endpoints, WebSocket protocols, and database schemas remain unchanged. --- ## Dependencies No new dependencies required. Existing toolchain: - Frontend: React 18, Vite, TypeScript, ESLint, Vitest - Backend: FastAPI, SQLAlchemy, Alembic, pytest **Optional consideration:** If CSS Modules are adopted (per proposal), Vite has built-in support — no new dependency needed. --- ## Non-Functional Requirements - **Build time:** No regression in `npm run build` or `vite build` duration - **Bundle size:** No increase in output bundle size - **Test runtime:** No regression in `npm run test` or `pytest` duration - **Developer experience:** File discovery time (time to locate a component/service) must decrease --- ## Migration Order | Phase | Concern | Files Touched | Est. Lines | |-------|---------|--------------|------------| | 1 | Types, seeds, shared deps | `types/`, `main.py`, `repo-workspace.tsx` | ~350 | | 2 | Style system | `styles.css` → `styles/` + `.module.css` | ~2,750 | | 3a | Backend shared deps | `auth/dependencies.py`, router helpers | ~400 | | 3b | `tool_instances` split | `api/tool_instances.py` → router + services | ~800 | | 3c | `git_repositories` + `config_profiles` split | Routers + services | ~700 | | 4a | `tool-workshop` split | Page + feature components | ~500 | | 4b | Pages split | `sessions.tsx`, `dashboard.tsx`, `repo-workspace.tsx` | ~600 | | 4c | Naming consistency | Rename files/components | ~350 | | 5 | Tests + docs | Backfill tests, naming doc | ~350 | --- ## Open Questions (Resolved) | # | Question | Resolution | |---|----------|------------| | 1 | CSS approach | **CSS Modules** — each component gets its own `.module.css` | | 2 | Backend API versioning | **Keep flat `api/`** — version when v2 is actually needed | | 3 | Feature components location | **`components/features/{domain}/`** | | 4 | Barrel exports | **Yes for stable boundaries** (`components/ui/`, `components/features/{domain}/`, `types/`); **no for one-off utilities and API modules** | | 5 | Branching strategy | **Merge each phase to `main` immediately** | --- *Spec prepared for SDD design phase. Next: technical design with exact file layout and import patterns.*