# Module Layout

**Status**: derived-from-code
**Language**: typescript (React 19 + Vite + Tailwind)
**Layout Convention**: custom (flat-features under `src/`; per-component barrels at `src/<component>/index.ts` since AZ-485)
**Root**: `src/`
**Last Updated**: 2026-05-11

> Authoritative file-ownership map for the React UI workspace. Derived from
> `_docs/02_document/00_discovery.md` (dependency graph) and the Step 2
> component specs at `_docs/02_document/components/`. Consumed by
> `/implement` Step 4 (file ownership), `/code-review` Phase 7
> (architecture violations), and `/refactor` discovery.

## Layout Rules

1. Each component owns ONE OR MORE top-level directories (or top-level files) under `src/`. The mapping is NOT 1:1 — `00_foundation` owns three sibling directories (`src/types/`, `src/hooks/`, `src/i18n/`), `05_flights` spans `src/features/flights/` AND a separate `mission-planner/` port-source root, and `10_app-shell` owns top-level files (`App.tsx`, `main.tsx`, `index.css`, `vite-env.d.ts`).
2. Shared code does **not** live under `src/shared/` today — there is no `shared/` directory. One helper module (`06_annotations/CanvasEditor.tsx`) remains physically misplaced and consumed across components; it is flagged in the `## Verification Needed` block. (`11_class-colors` was lifted to its own component directory `src/class-colors/` by AZ-511 / F3.) A `src/shared/` directory is a Step 4 testability candidate.
3. **Public API per component is the barrel `src/<component>/index.ts`** (AZ-485 / F4). Every component except `10_app-shell` (which is a top-level file collection — `App.tsx`, `main.tsx`, etc., never imported as a unit) exposes its Public API through a root barrel. Cross-component imports MUST go through the barrel — `import { api } from '../api'`, not `from '../api/client'`. The `STC-ARCH-01` static gate (`scripts/check-arch-imports.mjs`, wired into `scripts/run-tests.sh --static-only`) fails the build on cross-component deep imports. Intra-component imports (relative `./`) remain free. **No exemptions today** (the prior F3 carry-over for `features/annotations/classColors` was removed by AZ-511 when the file moved to its own component).
4. Cross-cutting concerns (logging, config, error handling, telemetry): no dedicated infrastructure today. `console.error` / silent catches are the closest thing — recorded in module findings.
5. Tests: there are **zero tests** under `src/`. The only test file is `mission-planner/src/test/jsonImport.test.ts`, which can't run because Jest isn't installed (00_discovery.md §11.5). Test layout is therefore TBD; suggest `src/<component>/__tests__/` per the standard React convention when tests are added (autodev Step 5–6).

## Per-Component Mapping

### Component: `00_foundation`

- **Epic**: TBD (set during autodev Step 4 / Decompose)
- **Directories**: `src/types/`, `src/hooks/`, `src/i18n/`
- **Public API** (no `src/<component>/index.ts` barrel — `00_foundation` spans three sibling directories; the existing `src/types/index.ts` is the type-alias barrel and `src/hooks/` + `src/i18n/` are imported directly per file):
  - `src/types/index.ts` — every exported type alias (`Detection`, `Flight`, `MediaItem`, `User`, etc.)
  - `src/hooks/useDebounce.ts` — `useDebounce`
  - `src/hooks/useResizablePanel.ts` — `useResizablePanel`
  - `src/i18n/i18n.ts` — default export (i18n instance)
- **Internal**: `src/i18n/en.json`, `src/i18n/ua.json` (data; consumed only by `i18n.ts`)
- **Owns** (exclusive write): `src/types/**`, `src/hooks/**`, `src/i18n/**`
- **Imports from**: (none — Layer 0)
- **Consumed by**: every other component

### Component: `11_class-colors`

- **Epic**: AZ-509 (carve-out delivered by AZ-511)
- **Directories**: `src/class-colors/` (lifted from `src/features/annotations/` by AZ-511; see `architecture_compliance_baseline.md` F3 — CLOSED)
- **Public API** (via `src/class-colors/index.ts` barrel): `getClassColor`, `getClassNameFallback`, `getPhotoModeSuffix`, `FALLBACK_CLASS_NAMES`.
- **Internal**: module-private `CLASS_COLORS` constant inside `classColors.ts`.
- **Owns**: `src/class-colors/**`
- **Imports from**: (none — Layer 0/1, no internal imports)
- **Consumed by**: `03_shared-ui` (DetectionClasses), `06_annotations` (CanvasEditor, AnnotationsPage, AnnotationsSidebar)

### Component: `01_api-transport`

- **Epic**: TBD
- **Directory**: `src/api/`
- **Public API** (via `src/api/index.ts` barrel): `api`, `setToken`, `getToken`, `getApiBase`, `setNavigateToLogin`, `createSSE`, `endpoints` (the typed URL-builder object that is the single source of truth for every `/api/<service>/...` path the UI talks to today — AZ-486 / F7; `STC-ARCH-02` enforces it).
- **Internal**: none (every file is externally consumed; the colocated `endpoints.test.ts` IS the wire-contract documentation per `module-layout.md`'s "code-derived documentation" pattern).
- **Owns**: `src/api/**`
- **Imports from**: `00_foundation` (types)
- **Consumed by**: `02_auth`, `03_shared-ui`, every feature page (04, 05, 06, 07, 08, 09)

### Component: `02_auth`

- **Epic**: TBD
- **Directory**: `src/auth/`
- **Public API** (via `src/auth/index.ts` barrel): `AuthProvider`, `useAuth`, `ProtectedRoute`.
- **Internal**: none
- **Owns**: `src/auth/**`
- **Imports from**: `00_foundation`, `01_api-transport`
- **Consumed by**: `03_shared-ui` (Header reads `useAuth`), `04_login`, `10_app-shell` (mounts `AuthProvider` + `ProtectedRoute`)

### Component: `03_shared-ui`

- **Epic**: TBD
- **Directory**: `src/components/`
- **Public API** (via `src/components/index.ts` barrel — all symbols externally consumed):
  - `Header.tsx` → `Header`
  - `HelpModal.tsx` → `HelpModal`
  - `ConfirmDialog.tsx` → `ConfirmDialog`
  - `DetectionClasses.tsx` → `DetectionClasses`
  - `FlightContext.tsx` → `FlightProvider`, `useFlight`
- **Internal**: none — every file in `src/components/` is consumed externally today
- **Owns**: `src/components/**`
- **Imports from**: `00_foundation`, `11_class-colors` (via `src/class-colors/index.ts` barrel since AZ-511), `01_api-transport`, `02_auth`
- **Consumed by**: `10_app-shell` (mounts `Header` + `FlightProvider`), every feature page (consumes `useFlight`, `ConfirmDialog`, `DetectionClasses`)

### Component: `04_login`

- **Epic**: TBD
- **Directory**: `src/features/login/`
- **Public API** (via `src/features/login/index.ts` barrel): `LoginPage`.
- **Internal**: none (single-page component)
- **Owns**: `src/features/login/**`
- **Imports from**: `00_foundation`, `01_api-transport`, `02_auth`
- **Consumed by**: `10_app-shell` (route)

### Component: `05_flights`

- **Epic**: TBD (this is the merged Flights & Mission Planning component)
- **Directories** (TWO physical roots):
  - `src/features/flights/` — deployed target tree (15 modules)
  - `mission-planner/` — port-source, NOT deployed (37 modules under `mission-planner/src/`). Documented inside this component per the user's Step 2 BLOCKING-gate decision (`_docs/02_document/state.json::component_05_flights_merge_2026-05-10`). The port direction is `mission-planner/` → `src/features/flights/`; module-layout treats both trees as owned by this component but only the target tree is in the layering table below.
- **Public API** (target tree, via `src/features/flights/index.ts` barrel): `FlightsPage` (route component). Internal sub-components (`FlightMap`, `FlightParamsPanel`, `FlightListSidebar`, `WaypointList`, `AltitudeChart`, `AltitudeDialog`, `WindEffect`, `MiniMap`, `MapPoint`, `DrawControl`, `JsonEditorDialog`, `mapIcons`, `flightPlanUtils`, `types`) are NOT re-exported through the barrel.
- **Public API** (port-source `mission-planner/`): not consumed at all by `src/` today (separate Vite entrypoint, `main.tsx` of its own). Effectively a private vendored sibling.
- **Internal** (target tree): every file under `src/features/flights/` except `FlightsPage.tsx`
- **Internal** (port-source): every file under `mission-planner/`
- **Owns**: `src/features/flights/**`, `mission-planner/**`
- **Imports from** (target tree): `00_foundation`, `01_api-transport`, `02_auth` (via `ProtectedRoute` from shell), `03_shared-ui` (uses `ConfirmDialog`, `useFlight`)
- **Consumed by**: `10_app-shell` (route)

### Component: `06_annotations`

- **Epic**: TBD
- **Directory**: `src/features/annotations/`
- **Public API** (via `src/features/annotations/index.ts` barrel):
  - `AnnotationsPage` (route component)
  - `CanvasEditor` — **also imported by `07_dataset`** (cross-feature edge, see `architecture_compliance_baseline.md` F2). The barrel re-exports `CanvasEditor` to keep the consumer compliant with STC-ARCH-01 until F2 closes the edge.
- **Internal**: `MediaList.tsx`, `VideoPlayer.tsx`, `AnnotationsSidebar.tsx`
- **Owns**: `src/features/annotations/**`
- **Imports from**: `00_foundation`, `11_class-colors` (via barrel since AZ-511), `01_api-transport`, `03_shared-ui`
- **Consumed by**: `10_app-shell` (route); `07_dataset` (imports `CanvasEditor` directly — see Verification Needed)

### Component: `07_dataset`

- **Epic**: TBD
- **Directory**: `src/features/dataset/`
- **Public API** (via `src/features/dataset/index.ts` barrel): `DatasetPage`.
- **Internal**: none (single-page)
- **Owns**: `src/features/dataset/**`
- **Imports from**: `00_foundation`, `11_class-colors` (only when class-distribution chart is added — not in code yet), `01_api-transport`, `03_shared-ui`, **`06_annotations` (CanvasEditor cross-feature edge)**
- **Consumed by**: `10_app-shell` (route)

### Component: `08_admin`

- **Epic**: TBD
- **Directory**: `src/features/admin/`
- **Public API** (via `src/features/admin/index.ts` barrel): `AdminPage`.
- **Internal**: none (single-page)
- **Owns**: `src/features/admin/**`
- **Imports from**: `00_foundation`, `01_api-transport`, `03_shared-ui`
- **Consumed by**: `10_app-shell` (route)

### Component: `09_settings`

- **Epic**: TBD
- **Directory**: `src/features/settings/`
- **Public API** (via `src/features/settings/index.ts` barrel): `SettingsPage`.
- **Internal**: none (single-page)
- **Owns**: `src/features/settings/**`
- **Imports from**: `00_foundation`, `01_api-transport`, `03_shared-ui`
- **Consumed by**: `10_app-shell` (route)

### Component: `10_app-shell`

- **Epic**: TBD
- **Files** (no dedicated directory): `src/App.tsx`, `src/main.tsx`, `src/index.css`, `src/vite-env.d.ts`
- **Public API**: `main.tsx` is the Vite entrypoint (no symbols are externally imported). `App.tsx` exports `App`. **No barrel** — the component is a top-level file collection, never imported as a unit. STC-ARCH-01's component allowlist intentionally omits `10_app-shell`.
- **Internal**: `index.css` (global Tailwind base + `az-*` design-token CSS variables), `vite-env.d.ts` (type shim)
- **Owns**: `src/App.tsx`, `src/main.tsx`, `src/index.css`, `src/vite-env.d.ts`
- **Imports from**: every other component (it is the composition root)
- **Consumed by**: (none — top of the graph; bundled by Vite)

### Component: `Blackbox Tests` (cross-cutting)

- **Epic**: AZ-455
- **Directories**: `tests/` (fast-profile shared helpers, MSW, fixtures, setup), `e2e/` (Playwright config, suite-e2e docker-compose, stubs, runner, e2e specs, e2e fixtures), plus colocated `*.test.{ts,tsx}` and `*.spec.{ts,tsx}` files under any production component directory.
- **Public API**: none (tests are not consumed by production code).
- **Internal**: every file under owned paths.
- **Owns** (exclusive write):
  - `tests/**`
  - `e2e/**`
  - `**/*.test.{ts,tsx}` and `**/*.spec.{ts,tsx}` — colocated test files (Vitest convention) override every production component's `Owns` glob for that filename pattern only.
  - `vitest.config.ts`, `tsconfig.test.json`
  - `scripts/run-tests.sh`, `scripts/run-performance-tests.sh` (extension only — the files were created in autodev Step 4 as Step 6 placeholders).
  - `package.json` test-scoped sections only: `scripts.test*`, `scripts.lint:tests`, `devDependencies` for runners (`vitest`, `@vitest/*`, `@playwright/test`, `msw`, `@testing-library/*`), and any test-only `peerDependencies` overrides.
  - ESLint test-override blocks (`overrides` entries scoped to `tests/**`, `e2e/**`, `**/*.test.{ts,tsx}`).
- **Imports from**:
  - **Test bodies** (files matching `**/*.test.{ts,tsx}` / `**/*.spec.{ts,tsx}` and any e2e spec under `e2e/tests/`): `00_foundation` only (and only `src/types/index.ts` — typed wire-contract enums per `_docs/02_document/tests/environment.md` § Black-box discipline / `P9`). NEVER any other production component's internal files. The static profile enforces this via ripgrep.
  - **Test infrastructure** (everything else under `tests/**` and `e2e/**` — `setup.ts`, MSW handlers, fixtures, helpers/wrappers, Playwright configs, runner Dockerfiles, stub services): MAY import production accessors from any layer when the accessor was created specifically for testability (e.g. `setToken` / `setNavigateToLogin` on `01_api-transport`'s `client.ts`, `AuthProvider` on `02_auth`, `i18n` on `00_foundation`). These helpers ARE the production-equivalent composition root for tests; black-box discipline applies to what test bodies observe, not to how the test environment is wired. Imports MUST still be public-API entry points (no reaching into internal files of other components).
- **Consumed by**: (none — tests are not part of production runtime).
- **Notes**:
  - Every test task spec under epic AZ-455 carries `**Component**: Blackbox Tests` and resolves its file ownership through this entry.
  - Colocated test files are OWNED by the test task that creates them, even when the parent directory belongs to a production component. The production files in that same directory remain READ-ONLY for the test task (compile-time imports of the production module under test are permitted; modifications are not).
  - Test-related `package.json` edits (devDependencies, test scripts) are OWNED here. Production `dependencies` and non-test `scripts` are FORBIDDEN — those remain owned by the production component whose runtime they affect (typically `10_app-shell`).
  - `mission-planner/` test files (e.g., `mission-planner/src/test/jsonImport.test.ts`) are OWNED here for the same reason; the `mission-planner/**` production glob remains owned by `05_flights`.

## Shared / Cross-Cutting

> No `src/shared/` directory exists today. Two cross-cutting concerns are tracked here as **proposed** shared modules; they require a physical file move scheduled for Step 4 (testability) or Step 8 (refactor).

### shared/class-colors — RESOLVED by AZ-511

The class-colors helper is no longer "proposed shared / physical-misplaced". It moved to its own component directory `src/class-colors/` with a proper barrel; see Per-Component Mapping for `11_class-colors` above. The entry is kept here as a back-pointer for readers following older links.

- **Owner component**: `11_class-colors`
- **Physical location**: `src/class-colors/`
- **Public API**: `src/class-colors/index.ts`
- **Consumed by**: `03_shared-ui/DetectionClasses`, `06_annotations` (CanvasEditor, AnnotationsPage, AnnotationsSidebar)

### shared/canvas-editor (proposed; current physical location: `src/features/annotations/CanvasEditor.tsx`)

- **Owner component**: still `06_annotations` for now (it's the dominant consumer)
- **Purpose**: Bounding-box draw / move / resize layer; reused by Dataset's inline editor.
- **Status**: cross-feature edge (07_dataset imports it). The proper home is a future `src/components/canvas/` directory. Decision deferred to Step 2 architecture baseline scan; the implement skill should treat this as a `READ-ONLY` for `07_dataset` tasks.

## Allowed Dependencies (layering)

Read top-to-bottom; an upper layer may import from a lower layer but NEVER the reverse. Same-layer imports are permitted only for explicit cross-feature edges listed in the table footnotes.

| Layer | Components | May import from |
|-------|------------|-----------------|
| 4. App Shell / Entry | `10_app-shell` | 0, 1, 2, 3 (and any feature in Layer 3) |
| 3. Application / Features | `04_login`, `05_flights`, `06_annotations`, `07_dataset`<sup>†</sup>, `08_admin`, `09_settings` | 0, 1, 2, 3<sup>†</sup> |
| 2. Composition | `02_auth`, `03_shared-ui` | 0, 1 |
| 1. Transport | `01_api-transport` | 0 |
| 0. Foundation / Shared kernel | `00_foundation`, `11_class-colors` | (none) |

<sup>†</sup> `07_dataset` imports `06_annotations/CanvasEditor.tsx` — same-layer cross-feature edge. Permitted today; flagged as a refactor target. The implement skill grants `07_dataset` tasks **READ-ONLY** access to that one file specifically.

Violations of this table are **Architecture** findings in code-review Phase 7 and are High severity.

The `Blackbox Tests` cross-cutting component sits **outside** this table. It imports from `00_foundation` only (specifically `src/types/index.ts` for typed wire-contract enums) and is consumed by no production component. The static-profile ripgrep checks enforce that no test imports from any other production component's internal files.

## Verification Needed

The following inferences could not be made cleanly from code alone. They are surfaced for the user to confirm or override at the Step 2.5 BLOCKING gate.

1. ~~**Physical home of `11_class-colors`**~~ — **RESOLVED by AZ-511 (F3)**. The file moved to `src/class-colors/classColors.ts` with a `src/class-colors/index.ts` barrel; consumers import via the barrel; STC-ARCH-01 has no exemptions. The `06_annotations` owns-glob no longer carves out `classColors.ts`.

2. **Physical home of `CanvasEditor.tsx`**. Same shape: it lives under `06_annotations` and is consumed cross-feature by `07_dataset`. Proposed: `src/components/canvas/CanvasEditor.tsx` (or a new `06b_canvas` component). **Decision needed**: keep the same-layer cross-feature edge, or schedule the lift?

3. ~~No barrel exports anywhere~~ — **resolved by AZ-485 (F4)**. Every component now exposes a `src/<component>/index.ts` barrel; cross-component imports go through it; `STC-ARCH-01` enforces it. The original F3-pending exemption (`classColors`) was closed by AZ-511 — there are no STC-ARCH-01 exemptions today.

3a. ~~Hardcoded `/api/<service>/` URLs scattered across callsites~~ — **resolved by AZ-486 (F7)**. The single source of truth is `src/api/endpoints.ts` (re-exported via the `01_api-transport` barrel from rule #3). Every production callsite of `api.*` and `createSSE()` uses an `endpoints.*` builder; the colocated `src/api/endpoints.test.ts` pins every URL string and serves as the wire-contract documentation. The `STC-ARCH-02` static gate (`scripts/check-arch-imports.mjs --mode=api-literals`, wired into `scripts/run-tests.sh --static-only`) fails the build on any new hardcoded `/api/<service>/` literal under `src/`. Exemptions: `src/api/endpoints.ts` (the contract owner) and any `*.test.ts` / `*.test.tsx` under `src/` (test files are exempt because tests legitimately assert URL strings — MSW handlers, contract tests, etc.).

4. **`mission-planner/` is owned by `05_flights` but lives at the repo root** (not under `src/`). Layout rule #1 says one component owns one or more top-level directories — this satisfies the rule (it owns two: `src/features/flights/` AND `mission-planner/`). Implement-skill consumers must include `mission-planner/**` in `05_flights`'s OWNED glob. **Decision needed**: confirm the implement skill should treat `mission-planner/**` as OWNED by 05_flights (otherwise it's FORBIDDEN by default).

5. **`05_flights` cycle inside the port-source**. `mission-planner/src/flightPlanning/MapView.tsx ↔ MiniMap.tsx` form a circular import (named-handle, see `00_discovery.md` §7 footnote). They were analyzed together in batch MP-B6. The cycle is internal to the component and does not cross component boundaries; flagged here for completeness.

6. **`00_foundation` owns three sibling directories** (`types/`, `hooks/`, `i18n/`). Layout rule #1 permits this. Future option: split into `00a_types`, `00b_hooks`, `00c_i18n` if the directories grow. **Decision needed**: keep the multi-directory component, or split now?

7. **`10_app-shell` owns top-level files instead of a directory** (`src/App.tsx`, `src/main.tsx`, etc.). Layout rule #1 permits this. The implement-skill OWNED glob for app-shell tasks must therefore be the explicit file list, not a directory glob.

8. **Test layout is undefined** (no tests exist). When Steps 5–6 of autodev produce tests, recommended layout is `src/<component-dir>/__tests__/` per React convention; for `05_flights` cross-tree tests, prefer `src/features/flights/__tests__/` (target tree only).

## Layout Conventions (reference)

| Language | Root | Per-component path | Public API file | Test path |
|----------|------|-------------------|-----------------|-----------|
| TypeScript / React | `src/` | `src/<component>/` (this codebase deviates: features under `src/features/<feature>/`, shared chrome under `src/components/`) | `src/<component>/index.ts` (barrel; present for every component except `10_app-shell` — see Layout Rule #3) | `src/<component>/__tests__/` (none exist today) |
