# 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.*