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

14 KiB
Raw Permalink Blame History

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: 100200). 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: 100200 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 <LoadingState message="Loading sessions..." /> instead of inline JSX

Scenario: Error state

  • GIVEN a page fails to load data
  • WHEN the UI renders
  • THEN it uses <ErrorState message="Failed to load" onRetry={loadData} /> 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.cssstyles/ + .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.