Files
ui/_docs/02_document/module-layout.md
T
Oleksandr Bezdieniezhnykh c368f60853 [AZ-511] classColors carve-out to src/class-colors/ (closes F3)
Move src/features/annotations/classColors.ts to its own component directory
src/class-colors/ with a proper barrel; update the 4 consumer imports to go
through the barrel; remove the F3-pending exemption from STC-ARCH-01 and from
the architecture test fixture; clean up the 5 coupled doc/script touchpoints.
Closes baseline finding F3 and retires the 5-coupled-places carry-over surface
logged in LESSONS.md 2026-05-12.

- Add `class-colors` to scripts/check-arch-imports.mjs COMPONENT_DIRS so deep
  imports past the new barrel are caught symmetric to every other component.
- Replace the architecture test "exemption WORKS" fixture with the stronger
  "deep import into class-colors NOW FAILS" assertion (Risk 4 mitigation).
- module-layout.md: Layout Rules + Per-Component Mapping (11_class-colors,
  06_annotations, 03_shared-ui) + Verification Needed #1 + shared/class-colors
  block all updated to reflect the new home.
- 11_class-colors/description.md: Caveats §7 + Module Inventory updated.
- architecture_compliance_baseline.md: F3 marked CLOSED with full pre-resolution
  context preserved (mirrors AZ-485/F4 + AZ-486/F7 pattern); F4 carry-forward
  exemption note retired.
- 04_verification_log.md: open questions #1 + #8 marked RESOLVED.
- Build passes with no circular-import warnings (AC-4); fast suite 231/13
  skipped green (AC-5); static profile green (AC-3 — zero exemptions remain).

Batch report: _docs/03_implementation/batch_14_cycle3_report.md

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 03:08:36 +03:00

21 KiB
Raw Blame History

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 56).

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.tsuseDebounce
    • src/hooks/useResizablePanel.tsuseResizablePanel
    • 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.tsxHeader
    • HelpModal.tsxHelpModal
    • ConfirmDialog.tsxConfirmDialog
    • DetectionClasses.tsxDetectionClasses
    • FlightContext.tsxFlightProvider, 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)
    • CanvasEditoralso 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, 08_admin, 09_settings 0, 1, 2, 3
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)

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-colorsRESOLVED 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 anywhereresolved 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 callsitesresolved 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.).

  1. 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).

  2. 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.

  3. 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?

  4. 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.

  5. Test layout is undefined (no tests exist). When Steps 56 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)