Files
headquarter/openspec/changes/repo-restructure/explore.md
T
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

23 KiB

Repo Restructure — Exploration Report

Project: Headquarter (full-stack workspace platform) Date: 2026-06-02 Scope: Comprehensive codebase audit for structural refactoring


1. Directory Structure

Root Layout

/workspace
├── apps/
│   ├── web/          # React 18 + Vite frontend
│   └── api/          # Python FastAPI + SQLAlchemy backend
├── e2e/              # Playwright tests
├── docs/             # (not heavily populated)
└── openspec/         # OpenSpec changes

Frontend (apps/web/src/)

src/
├── api/              # 13 API modules (~1,200 LOC total)
│   ├── client.ts
│   ├── dashboard.ts
│   ├── git_repositories.ts
│   ├── profile.ts
│   ├── projects.ts
│   ├── sessions.ts
│   ├── settings.ts
│   ├── ssh_keys.ts
│   ├── terminal.ts
│   ├── tool_configs.ts
│   ├── tool_types.ts
│   ├── config_folders.ts
│   └── config_profiles.ts
├── components/       # 16 components (~2,100 LOC total)
│   ├── app-shell.tsx
│   ├── code-editor.tsx
│   ├── commit-dialog.tsx
│   ├── commit-panel.tsx
│   ├── file-editor.tsx
│   ├── git-toolbar.tsx
│   ├── icon.tsx
│   ├── instance-list.tsx
│   ├── merge-dialog.tsx
│   ├── protected-route.tsx
│   ├── protected-route.test.tsx
│   ├── repository-create-dialog.tsx
│   ├── settings-tab-layout.tsx
│   ├── syntax-highlighter.tsx
│   ├── workspace-header.tsx
│   └── repositories-settings-tab.tsx
├── hooks/            # 1 hook
│   └── use-theme.ts
├── pages/            # 15 pages (~3,500 LOC total)
│   ├── dashboard.tsx
│   ├── git-history.tsx
│   ├── git-repositories.tsx
│   ├── placeholder.tsx
│   ├── profile.tsx
│   ├── project-settings.tsx
│   ├── projects.tsx
│   ├── repo-workspace.tsx
│   ├── settings.tsx
│   ├── ssh-keys.tsx
│   ├── terminal.tsx
│   ├── tool-configs.tsx
│   ├── tool-types.tsx
│   └── tool-workshop.tsx
├── state/            # 2 context providers
│   ├── auth.tsx
│   └── sessions.tsx
├── types/            # 2 type modules
│   └── terminal.ts
├── utils/            # 3 utilities
│   ├── icons.ts
│   ├── language.ts
│   └── terminal-protocol.ts
├── styles.css        # 1 massive stylesheet (2,844 lines)
├── router.tsx        # Route definitions
├── main.tsx          # Entry point
└── types.ts          # Shared domain types

Backend (apps/api/)

apps/api/
├── src/
│   ├── main.py                 # App entry point (~287 lines)
│   ├── config.py               # Pydantic settings (~128 lines)
│   ├── database.py             # SQLAlchemy setup (~114 lines)
│   ├── logging_config.py       # Middleware & logging (~92 lines)
│   ├── auth/
│   │   ├── session.py
│   │   └── dependencies.py
│   ├── api/                    # 15 routers
│   │   ├── auth.py
│   │   ├── config_folders.py
│   │   ├── config_profiles.py
│   │   ├── dashboard.py
│   │   ├── git_repositories.py  # ~900+ lines
│   │   ├── health.py
│   │   ├── instance_proxy.py
│   │   ├── projects.py
│   │   ├── ssh_keys.py
│   │   ├── terminal.py
│   │   ├── tool_configs.py
│   │   ├── tool_instances.py    # ~1,463 lines — CRITICAL
│   │   ├── tool_types.py
│   │   ├── user_config.py
│   │   └── users.py
│   ├── models/                 # SQLAlchemy models
│   ├── services/               # Business logic
│   │   ├── docker.py           # ~457+ lines
│   │   ├── docker_build.py
│   │   ├── git_control.py
│   │   ├── git_files.py
│   │   ├── git_history.py
│   │   ├── git_url_parser.py
│   │   ├── profile_resolver.py
│   │   └── readiness_probe.py
│   ├── utils/                  # Additional utilities
│   └── scripts/
│       └── seed.py
├── alembic/versions/           # 14+ migrations
└── tests/
    ├── conftest.py
    ├── unit/
    └── integration/

2. File Sizes — Files Over 200 Lines

🔴 CRITICAL — Over 400 Lines (Must Split)

File Lines Issue
apps/web/src/styles.css 2,844 Single stylesheet for entire app; mixes layout, components, pages, syntax highlighting, and themes
apps/api/src/api/tool_instances.py 1,463 Monolithic router: CRUD, Docker orchestration, tunneling, proxying, config resolution, readiness probes
apps/api/src/api/git_repositories.py ~900+ Combined file browsing, Git control (branch/commit/merge/push/pull), URL parsing, history
apps/api/src/services/docker.py ~457+ Docker compose, container management, tunneling, config folder staging all in one

🟡 WARNING — Over 200 Lines (Should Split)

File Lines Issue
apps/web/src/pages/tool-workshop.tsx ~700+ 3-tab admin page with inline forms for tool types, configs, AND folders
apps/web/src/pages/sessions.tsx ~668 Sessions page with create form, active/recent lists, inline confirmations
apps/web/src/pages/repo-workspace.tsx ~394 Page + FileBrowser component + mixed data loading
apps/web/src/components/instance-list.tsx ~388 Instance CRUD + health checks + create dialog
apps/web/src/pages/tool-types.tsx ~380 Tool types list + create/edit dialog inline
apps/web/src/pages/tool-configs.tsx ~354 Tool configs list + create/edit dialog inline
apps/web/src/hooks/use-terminal-connection.ts ~439 WS lifecycle, ping-pong, reconnection, local echo, resize debouncing
apps/web/src/pages/dashboard.tsx ~338 Summary cards, session lists, quick-create form, recent sessions
apps/web/src/components/terminal.tsx ~309 Terminal chrome + xterm lifecycle + resize observer
apps/web/src/pages/git-history.tsx ~233 Commit list + detail panel with inline formatting
apps/web/src/api/git_repositories.ts ~245 API functions + types (reasonable, but types should move)
apps/web/src/pages/projects.tsx ~206 List + create dialog + delete confirmation
apps/api/src/main.py ~287 Router registration + startup logic + seeding + error handlers
apps/api/src/api/config_profiles.py ~877 Config profiles CRUD + complex resolution logic
apps/api/src/api/tool_types.py ~616 Tool types CRUD + compose/dockerfile validation
apps/api/src/api/config_folders.py ~372 Config folders CRUD

3. Frontend Module Analysis

Components (16 files, ~2,100 LOC, avg ~131 LOC)

Too large:

  • git-toolbar.tsx (~268) — mixes git ops, branch creation form, merge dialog trigger, status summary
  • file-editor.tsx (~241) — view/edit/commit workflow
  • instance-list.tsx (~388) — instance CRUD + health + create dialog
  • terminal.tsx (~309) — terminal chrome + xterm lifecycle

Well-sized:

  • workspace-header.tsx (~48)
  • protected-route.tsx (~19)
  • icon.tsx (~165)

Pages (15 files, ~3,500 LOC, avg ~233 LOC)

All pages are too large. Every page mixes:

  • Data fetching (useEffect + API calls)
  • Local state management (useState for forms, dialogs, loading)
  • UI rendering (JSX)

Worst offenders:

  • tool-workshop.tsx (~700) — 3 completely different admin interfaces in one file
  • sessions.tsx (~668) — create form + active/recent lists + confirmations
  • repo-workspace.tsx (~394) — contains FileBrowser component inline
  • dashboard.tsx (~338) — summary, active sessions, projects list, quick-create form

Hooks (3 files)

  • use-theme.ts (~23) — fine
  • use-terminal-connection.ts (~439) — too large; mixes WS lifecycle, ping-pong, reconnection, echo, resize

API Modules (13 files, ~1,200 LOC)

  • Well-organized by domain
  • Inconsistency: Some define types inline (api/sessions.ts defines ToolInstance, Session), others in separate types.ts
  • api/client.ts — centralized Axios instance with auth interceptor. Good pattern.

State/Context (2 files)

  • auth.tsx (~63) — well-sized
  • sessions.tsx (~44) — well-sized

Styles (1 file, 2,844 lines) — CRITICAL

styles.css is the biggest problem in the frontend. It contains:

  • CSS variables / design tokens
  • Global resets
  • Layout (shell, nav, content grid)
  • Page styles (home, settings, git-history, repo-workspace)
  • Component styles (cards, buttons, dialogs, forms, file-tree, editor)
  • Syntax highlighting overrides
  • Responsive media queries scattered throughout

Types

  • src/types.ts — core domain types (SessionUser, Project)
  • src/types/terminal.ts — terminal-specific WebSocket protocol types
  • Problem: API modules also export their own types (ToolInstance, Session, GitRepository, etc.) causing duplication and confusion. Session is defined in BOTH api/sessions.ts and state/sessions.tsx.

Utils

  • icons.ts (~180) — icon name mapping
  • language.ts (~90) — file extension → language detection
  • terminal-protocol.ts (~76) — WS message encoding/decoding + type guards

Router

  • router.tsx (~58) — clean and readable

Tests

  • components/protected-route.test.tsx (~49)
  • api/tool_types.test.ts (~227)
  • api/config_folders.test.ts (~131)
  • pages/dashboard.test.tsx (~81)
  • pages/projects.test.tsx (~174) — failing tests (React Router context issue)
  • pages/tool-workshop.test.tsx (~527)
  • hooks/use-terminal-connection.test.ts (~339)
  • Massive gaps: No tests for most pages, hooks, state providers, or components

4. Backend Module Analysis

Entry Points

  • src/main.py (~287) — FastAPI app setup, CORS, middleware, exception handlers, startup events, seeding, router mounting
  • Problem: Seed data (builtin tool types) is hardcoded here (~100 lines of compose templates). Should be in seeds/ or services/seed_data.py.

Routers/Endpoints (15 files)

Organization: One router per domain — good structure in theory, but files are too large.

tool_instances.py (1,463 lines) — The worst offender. Contains:

  • Pydantic request/response models
  • Helper functions: _modify_compose_file, _apply_resolved_profile, _get_user, _get_owned_project, _sanitize_name, _generate_instance_name
  • Endpoints: create, list, get, start, stop, restart, delete, logs, recreate-tunnel, health-check, proxy
  • Inline Docker orchestration logic (should be in services)
  • Inline config resolution (should use service layer)

git_repositories.py (~900+ lines) — Contains:

  • Repository CRUD
  • File browsing endpoints
  • Git control endpoints (branch, checkout, commit, fetch, pull, push, merge)
  • URL parsing endpoint

config_profiles.py (~877 lines) — Contains:

  • Config profile CRUD
  • Complex profile resolution logic
  • Config folder/application logic

Models

  • Located in src/models/ — one file per entity
  • Clean separation, well-sized

Services/Business Logic

  • docker.py (~456) — Docker compose, container ops, tunneling, config file staging. Too large.
  • docker_build.py (~69) — Image building
  • git_control.py (~295) — Git operations
  • git_files.py (~439) — File tree, read, write
  • git_history.py (~382) — Commit history, graph, diff
  • git_url_parser.py (~228) — URL parsing and validation
  • profile_resolver.py (~251) — Config profile resolution
  • readiness_probe.py (~66) — Container health probes
  • terminal_manager.py (~193) — Terminal session lifecycle
  • terminal_session.py (~162) — Individual terminal session handling

Database/ORM

  • database.py (~116) — Engine, session factory, init with alembic subprocess
  • config.py (~143) — Pydantic settings with env var resolution
  • Alembic migrations in alembic/versions/ — 14+ migration files

5. Coupling and Dependency Patterns

Frontend High-Coupling Files

repo-workspace.tsx imports from:

  • react-router-dom (params, search params)
  • ../api/client (direct apiClient usage)
  • ../api/git_repositories
  • ../components/commit-panel
  • ../components/file-editor
  • ../components/git-toolbar
  • ../components/instance-list
  • ../components/workspace-header
  • ../api/tool_types

dashboard.tsx imports from:

  • ../api/dashboard, ../api/sessions, ../api/projects, ../api/git_repositories, ../api/tool_types, ../api/settings
  • ../types, ../components/icon

tool-workshop.tsx imports from:

  • ../api/tool_types, ../api/tool_configs, ../api/config_folders
  • Manages 3 separate entity forms with ~20 useState variables each

Circular Dependencies

  • No obvious circular imports detected, but Session type is duplicated between api/sessions.ts and state/sessions.tsx, creating conceptual circularity.

Business Logic Mixed with UI

  • Every page component contains API calls directly in useEffect
  • Form validation logic is inline in page components
  • repo-workspace.tsx defines FileBrowser as an inner component — cannot be tested or reused independently

API Call Patterns

  • Mostly centralized in api/ modules — good
  • Exception: repo-workspace.tsx, file-editor.tsx, project-settings.tsx use apiClient directly instead of domain API modules
  • Exception: app-shell.tsx calls getUserSessions() directly

6. Naming Inconsistencies

File Naming Conventions

Location Convention Examples Issues
pages/ mostly kebab-case git-history.tsx, repo-workspace.tsx projects.tsx, profile.tsx, settings.tsx, dashboard.tsx are NOT kebab-case
components/ kebab-case app-shell.tsx, protected-route.tsx repositories-settings-tab.tsx (long but consistent)
api/ snake_case tool_configs.ts, git_repositories.ts Mixes with frontend convention
utils/ kebab-case terminal-protocol.ts Good
hooks/ camelCase useTheme.ts would be standard, but file is use-theme.ts Actually kebab-case, which is fine but inconsistent with React convention
Backend routers snake_case tool_instances.py, git_repositories.py Consistent within backend
Backend services snake_case docker.py, profile_resolver.py Consistent

Component vs File Naming

  • Component ProtectedRoute → file protected-route.tsx
  • Component AppShell → file app-shell.tsx
  • Component GitHistoryPage → file git-history.tsx (should be GitHistoryPage in git-history-page.tsx OR component renamed to GitHistory)
  • Component RepoWorkspace → file repo-workspace.tsx (same issue)
  • Page components use Page suffix inconsistently: ProjectsPage, GitHistoryPage, but RepoWorkspace has no Page suffix

Function/Variable Naming

  • Frontend: camelCase consistently
  • Backend: snake_case consistently
  • API types: Backend uses snake_case fields; frontend types mirror this (default_ssh_key_id, tool_type_name). Good for API alignment.

7. Quality Signals

TODO/FIXME Comments

  • Only 2 TODOs found:
    • apps/api/src/utils/git_history.py:188-189: # TODO: extract committer separately (appears twice)

This is surprisingly low — suggests either good maintenance or lack of inline documentation.

Dead Code / Unused Exports

  • dashboard.tsx exports HomePage as DashboardPage — dual naming is confusing
  • src/types.ts exports SessionPayload which is only used in auth context
  • Several CSS classes in styles.css may be unused (hard to verify without build analysis)

Duplicate Logic

  • Backend auth checks: _get_user() and _get_owned_project() are duplicated in nearly every router file (tool_instances.py, git_repositories.py, ssh_keys.py, etc.)
  • Frontend loading/error patterns: Identical status: "loading" | "ready" | "error" state + retry button pattern copied in ~8 page components
  • Frontend form dialogs: Create/edit/delete confirmation pattern repeated in projects.tsx, tool-types.tsx, tool-configs.tsx, ssh-keys.tsx

Test Coverage Gaps

  • Frontend: 7 test files, but many pages and components untested
  • Backend: Unit tests for git_url_parser.py, migration_metadata.py, profile_resolver.py, readiness_probe.py, docker_build.py, terminal_manager.py, terminal_session.py; integration tests via conftest.py
  • E2E tests only cover login flow (e2e/tests/login.spec.ts)

Recommendations

Target Directory Structure

Frontend (apps/web/src/)

src/
├── api/                    # Keep — centralized API layer
│   ├── client.ts
│   ├── __mocks__/          # Add: mock API responses for tests
│   └── {domain}/           # Group by domain
│       ├── index.ts        # Re-exports
│       ├── types.ts        # Domain types ONLY
│       └── api.ts          # API functions
├── components/             # Generic UI components
│   ├── ui/                 # Primitive components (Button, Card, Dialog, Input)
│   ├── layout/             # AppShell, Navigation, Header
│   └── features/           # Domain-specific components
│       ├── git/
│       ├── project/
│       ├── session/
│       └── settings/
├── hooks/                  # Custom hooks
│   ├── use-theme.ts
│   ├── use-auth.ts         # Extract from state/auth.tsx?
│   └── use-api-query.ts    # NEW: reusable data fetching
├── pages/                  # Route entry points ONLY
│   ├── dashboard/
│   │   └── page.tsx
│   ├── projects/
│   │   ├── page.tsx
│   │   ├── project-list.tsx
│   │   └── create-project-dialog.tsx
│   └── ...
├── state/                  # Keep contexts
├── styles/
│   ├── tokens.css          # CSS variables only
│   ├── global.css          # Resets + base styles
│   ├── components/         # Component styles
│   └── pages/              # Page-specific styles
├── types/                  # Centralize ALL shared types
│   └── index.ts
└── utils/

Backend (apps/api/src/)

src/
├── main.py                 # Router mounting + middleware ONLY
├── config.py
├── database.py
├── logging_config.py
├── auth/
├── api/
│   └── v1/                 # Versioned routes
│       ├── __init__.py
│       ├── auth.py
│       ├── projects/
│       │   ├── __init__.py
│       │   ├── router.py
│       │   └── dependencies.py
│       ├── repositories/
│       │   ├── __init__.py
│       │   ├── router.py     # CRUD only
│       │   ├── files.py      # File browsing
│       │   └── git.py        # Git control operations
│       ├── instances/
│       │   ├── __init__.py
│       │   ├── router.py     # CRUD + lifecycle
│       │   ├── compose.py    # Compose file generation
│       │   ├── tunnel.py     # Cloudflare tunnel ops
│       │   └── proxy.py      # HTTP proxy
│       └── ...
├── models/
├── schemas/                # NEW: Pydantic schemas separate from routers
├── services/
│   ├── docker/
│   │   ├── __init__.py
│   │   ├── compose.py      # Extract from docker.py
│   │   ├── container.py    # Container lifecycle
│   │   ├── tunnel.py       # Cloudflare tunneling
│   │   └── config.py       # Config file staging
│   └── git/
│       ├── control.py
│       ├── files.py
│       └── history.py
├── seeds/                  # NEW: Seed data
│   └── builtin_tool_types.py
└── tests/

Files That MUST Be Split

  1. apps/web/src/styles.css → Split into 5-8 files by concern
  2. apps/api/src/api/tool_instances.py → Split into router + compose service + tunnel service + proxy service
  3. apps/api/src/api/git_repositories.py → Split into repository CRUD router + file router + git control router
  4. apps/api/src/services/docker.py → Split into compose, container, tunnel, config staging modules
  5. apps/web/src/pages/tool-workshop.tsx → Split into 3 page tabs or feature components
  6. apps/web/src/pages/repo-workspace.tsx → Extract FileBrowser to components/features/git/file-browser.tsx
  7. apps/web/src/pages/sessions.tsx → Extract create form, active list, recent list
  8. apps/web/src/hooks/use-terminal-connection.ts → Extract WS manager, echo handler, resize debouncer

Naming Convention to Standardize On

Layer Convention Example
React components (files) PascalCase matching component GitHistoryPage.tsx
React hooks (files) camelCase useTheme.ts
Utility modules kebab-case terminal-protocol.ts
API modules kebab-case tool-configs.ts
Backend routers snake_case tool_instances.py
Backend services snake_case profile_resolver.py
CSS modules kebab-case matching component git-history-page.module.css

Order of Migration (First → Last)

Phase 1: Safe Foundations (low risk)

  1. Extract shared types to src/types/index.ts (remove duplication)
  2. Create src/hooks/use-api-query.ts for reusable data fetching
  3. Extract FileBrowser from repo-workspace.tsx
  4. Move seed data from main.py to seeds/builtin_tool_types.py

Phase 2: Style System (medium risk, high reward) 5. Split styles.css into design tokens + component modules 6. Introduce CSS modules or Tailwind utility extraction for component styles

Phase 3: Backend Decomposition (medium risk) 7. Extract _get_user and _get_owned_project to auth/dependencies.py or api/dependencies.py 8. Split tool_instances.py into router + services 9. Split git_repositories.py into CRUD + files + git control routers 10. Split services/docker.py into focused modules

Phase 4: Frontend Page Decomposition (higher risk — touches UX) 11. Split tool-workshop.tsx into feature components 12. Split dashboard.tsx into summary/session/project sections 13. Split sessions.tsx into create-form + lists 14. Split settings.tsx — move GeneralSettingsTab to its own file

Phase 5: Testing & Polish 15. Add tests for extracted components 16. Add backend integration tests for refactored routers