mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 20:11:13 +00:00
[AZ-485] Add Public API barrels + STC-ARCH-01 (F4 close)
Closes architecture baseline finding F4. Every component now exposes its Public API through `src/<component>/index.ts`; cross-component imports go through the barrel. `scripts/check-arch-imports.mjs` plus `STC-ARCH-01` in the static profile enforce the rule; tests in `tests/architecture_imports.test.ts` cover AC-4/AC-5 + 2 exemption cases. One F3-pending exemption (`classColors`) is documented in 5 places (barrel, consumer, script, doc, test) to avoid a circular import. Phase B cycle 1 batch 1 of 2 (epic AZ-447). Batch 2 is AZ-486 (endpoint builders) — blocked on this commit landing. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
# Public API barrels per component + deep-import migration
|
||||
|
||||
**Task**: AZ-485_refactor_public_api_barrels
|
||||
**Name**: Add Public API barrels and migrate cross-component imports
|
||||
**Description**: Introduce `index.ts` barrels for every component, narrow each component's Public API to the symbols listed in `module-layout.md`, replace every cross-component deep import with a barrel import, and add a static check that flags future deep imports. Closes architecture baseline finding **F4**.
|
||||
**Complexity**: 5 points
|
||||
**Dependencies**: None
|
||||
**Component**: cross-cutting (00–10) — coordinated edit across `src/api/`, `src/auth/`, `src/components/`, `src/features/**/`, `src/hooks/`, `src/i18n/`, `src/App.tsx`, plus every test importer
|
||||
**Tracker**: AZ-485
|
||||
**Epic**: AZ-447
|
||||
|
||||
## Problem
|
||||
|
||||
`_docs/02_document/architecture_compliance_baseline.md` Finding **F4** (High / Architecture): no component currently exposes a barrel `index.ts` (the sole barrel today is `src/types/index.ts`, owned by `00_foundation`). Cross-component imports use file-name granularity (`import { api } from '../api/client'`, `import { useFlight } from '../components/FlightContext'`, …). Consequence:
|
||||
|
||||
1. There is no enforceable Public API surface — every internal file is de-facto public.
|
||||
2. Any internal split / rename inside a component is a breaking change to ~10 importers.
|
||||
3. Phase 7 architecture compliance ("Public API respect") cannot fail in this codebase because everything is public.
|
||||
4. The next time `module-layout.md` flags a Public-API drift, no static gate exists to catch it.
|
||||
|
||||
`module-layout.md` Layout Rules #3 records the same observation and lists this as a Step 4 testability candidate; Step 4 deferred it to Phase B (`_autodev_state.md::step_2_baseline_routing: per-finding-recommended`).
|
||||
|
||||
## Outcome
|
||||
|
||||
- Every component listed in `module-layout.md`'s "Per-Component Mapping" exposes its Public API through a barrel `index.ts` at the component root (10 new files; `src/types/index.ts` is unchanged).
|
||||
- Every cross-component import in `src/**` and `tests/**` resolves through a component barrel — no remaining deep imports of another component's internal files. `mission-planner/**` is exempt (untouched per F1's deferred convergence plan).
|
||||
- A static check (added to `scripts/run-tests.sh`) fails the static profile if any new `src/**` or `tests/**` file imports a non-barrel path from another component.
|
||||
- `_docs/02_document/module-layout.md` Layout Rules #3 is rewritten to describe the post-change state ("Each component exposes its Public API via `src/<component>/index.ts`. Cross-component imports MUST use the barrel. The static gate `STC-ARCH-01` enforces this.").
|
||||
- All existing fast + static profiles remain green after the migration.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
- Create 10 new barrels (`src/api/index.ts`, `src/auth/index.ts`, `src/components/index.ts`, `src/features/{login,flights,annotations,dataset,admin,settings}/index.ts`, `src/hooks/index.ts`, `src/i18n/index.ts`). Each barrel re-exports ONLY the symbols listed for that component in `module-layout.md`'s "Per-Component Mapping" → "Public API (de-facto)".
|
||||
- Replace every cross-component deep import (~30 sites across `src/App.tsx`, every feature page that imports from another component, and every `tests/**` and colocated `*.test.tsx` that imports a production symbol from another component) with a barrel import.
|
||||
- Add a new static check `STC-ARCH-01` to `scripts/run-tests.sh` that fails the static profile if any `src/**` or `tests/**` file (excluding the barrel itself, `mission-planner/**`, and intra-component imports) imports a non-barrel path from a different component.
|
||||
- Update `_docs/02_document/module-layout.md` Layout Rules #3 to reflect the post-change state and add `STC-ARCH-01` to the Static Checks inventory (if such a list exists; otherwise document inline).
|
||||
|
||||
### Excluded
|
||||
- `src/types/index.ts` is already a barrel — left unchanged.
|
||||
- `mission-planner/**` — untouched (F1's deferred convergence plan; will be deleted in the final Phase B port cycle per the baseline).
|
||||
- F2 (`07_dataset → 06_annotations` cross-feature edge for `CanvasEditor`) — `CanvasEditor` STAYS in the `06_annotations` barrel's Public API list (the cross-feature edge is grandfathered in `module-layout.md` and is closed by F2, not F4).
|
||||
- F3 (`classColors.ts` physical/logical owner split) — the file remains physically under `src/features/annotations/`; F4 lists it in the `06_annotations` barrel for now. Physical move is F3's own task.
|
||||
- New runtime behavior — this is a structural refactor only.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: Every component has a barrel exposing only its Public API**
|
||||
Given the post-change repo,
|
||||
When `src/<component>/index.ts` is read for every component listed in `module-layout.md`,
|
||||
Then the file exists, every named export matches a symbol in that component's "Public API (de-facto)" line in `module-layout.md`, and no internal-only file's symbol is re-exported.
|
||||
|
||||
**AC-2: No cross-component deep imports remain in production code**
|
||||
Given the post-change repo,
|
||||
When `ripgrep "^(import|export).*from\s+['\"]\.\.\/[a-z][^'\"]*\/[A-Za-z][^'\"]+['\"]" src/` is run, excluding intra-component paths (paths that resolve to the same component's owned directory),
|
||||
Then no match is found.
|
||||
|
||||
**AC-3: No cross-component deep imports remain in tests**
|
||||
Given the post-change repo,
|
||||
When the same ripgrep is run across `tests/**`, `e2e/**`, and colocated `**/*.test.{ts,tsx}` files,
|
||||
Then no match is found OUTSIDE the documented testability exemptions in `module-layout.md` "Blackbox Tests" entry (test infrastructure may import testability accessors like `setToken`, `setNavigateToLogin`, `AuthProvider`, and i18n directly per the existing exemption — those continue to use barrel paths now that the barrels re-export them).
|
||||
|
||||
**AC-4: Static gate STC-ARCH-01 fails on a newly-introduced deep import**
|
||||
Given the post-change static profile,
|
||||
When a synthetic test file is added that imports `'../api/client'` instead of `'../api'` (or equivalent for another component),
|
||||
Then `bash scripts/run-tests.sh --static` exits non-zero with `STC-ARCH-01` named in the failure line.
|
||||
|
||||
**AC-5: Static gate STC-ARCH-01 passes on the migrated codebase**
|
||||
Given the post-change repo,
|
||||
When `bash scripts/run-tests.sh --static` runs,
|
||||
Then it exits zero and the static report shows `STC-ARCH-01` as PASS.
|
||||
|
||||
**AC-6: Fast profile remains green**
|
||||
Given the post-change repo,
|
||||
When `bash scripts/run-tests.sh --fast` runs,
|
||||
Then it reports the same PASS / SKIP / FAIL counts as the pre-change baseline (163 PASS / 13 SKIP / 0 FAIL per `_docs/03_implementation/test_run_report.md`), with zero new failures and zero regressions in skip-classification.
|
||||
|
||||
**AC-7: module-layout.md reflects the new convention**
|
||||
Given the post-change repo,
|
||||
When `_docs/02_document/module-layout.md` Layout Rules #3 is read,
|
||||
Then it states "Each component exposes its Public API via `src/<component>/index.ts`. Cross-component imports MUST use the barrel. The static gate `STC-ARCH-01` enforces this." and the Verification Needed item referencing the missing barrels is removed (or marked closed by this task's tracker ID).
|
||||
|
||||
## Non-Functional Requirements
|
||||
|
||||
**Performance**
|
||||
- Initial JS bundle gzipped size MUST remain ≤ 2 MB (existing `STC-PERF01`). Barrel re-exports tree-shake under Vite's production rollup, so no regression expected.
|
||||
|
||||
**Compatibility**
|
||||
- No runtime behavior change. The fast + e2e suites are the contract; both stay green.
|
||||
|
||||
**Maintainability**
|
||||
- Future internal renames inside a component MUST not require import-path edits outside that component (validated by AC-2/AC-3 + STC-ARCH-01).
|
||||
|
||||
## Unit Tests
|
||||
|
||||
| AC Ref | What to Test | Required Outcome |
|
||||
|--------|--------------|------------------|
|
||||
| AC-1 | Each barrel file re-exports only documented symbols | Re-export list matches `module-layout.md`'s Public API line for that component (test reads both files and compares) |
|
||||
| AC-4 | Synthetic deep-import detection | `STC-ARCH-01` fails when a fixture file with a deep import is added |
|
||||
| AC-5 | Static check on the real codebase | `STC-ARCH-01` passes |
|
||||
|
||||
## Blackbox Tests
|
||||
|
||||
| AC Ref | Initial Data/Conditions | What to Test | Expected Behavior | NFR References |
|
||||
|--------|-------------------------|--------------|-------------------|----------------|
|
||||
| AC-2 | Repo after migration | `ripgrep` for cross-component deep imports in `src/` | Zero matches | Maintainability |
|
||||
| AC-3 | Repo after migration | `ripgrep` for cross-component deep imports in `tests/`, `e2e/`, colocated tests | Zero matches outside documented exemptions | Maintainability |
|
||||
| AC-6 | Fast profile | `scripts/run-tests.sh --fast` | 163 PASS / 13 SKIP / 0 FAIL (matches `_docs/03_implementation/test_run_report.md`) | Compat |
|
||||
|
||||
## Constraints
|
||||
|
||||
- All 10 barrels + import migration + static check MUST land in ONE commit to keep mid-state green (partial migration breaks the static gate on intermediate commits). If commit size is impractical, split per-component but always under one PR atomic to merge.
|
||||
- The barrel files are OWNED by each respective component (e.g. `src/api/index.ts` is OWNED by `01_api-transport` tasks); the static check addition to `scripts/run-tests.sh` is OWNED by `Blackbox Tests` per `module-layout.md`.
|
||||
- No new dependencies. The static check uses `ripgrep` (already used elsewhere in `scripts/run-tests.sh`).
|
||||
- `mission-planner/**` MUST be untouched.
|
||||
|
||||
## Risks & Mitigation
|
||||
|
||||
**Risk 1: A cohort import-path edit misses a transitive import path → fast suite goes red on TypeScript "module not found"**
|
||||
- *Risk*: ~30 import statements across many files; mechanical edit can miss one.
|
||||
- *Mitigation*: Run `bun tsc -b --noEmit` (or the project's `lint:tests` script) after every per-component batch; commit only when type-check is green. AC-6 (full fast profile) is the final gate.
|
||||
|
||||
**Risk 2: Vite tree-shaking regression — barrel re-exports drag in optional sub-modules**
|
||||
- *Risk*: A barrel that re-exports rarely-used symbols can defeat tree-shaking and inflate the bundle.
|
||||
- *Mitigation*: STC-PERF01 already caps gzipped bundle at 2 MB and runs in the static profile. The migration must keep that gate green. If bundle regresses, split the barrel into eager + lazy re-export blocks per Vite's recommendations.
|
||||
|
||||
**Risk 3: `CanvasEditor` cross-feature edge (F2) confuses the static check**
|
||||
- *Risk*: `src/features/dataset/DatasetPage.tsx` legitimately imports `CanvasEditor` from `src/features/annotations/`. STC-ARCH-01 must allow this when it goes through the `06_annotations` barrel but flag the legacy direct path.
|
||||
- *Mitigation*: After migration, the dataset import becomes `import { CanvasEditor } from '../annotations'` — passes the barrel-path check. F2's eventual `CanvasEditor` lift is independent of this task.
|
||||
|
||||
## Contract
|
||||
|
||||
This task produces the Public API contract for 10 components — the barrel re-export lists ARE the contract. The contract surface for each component is documented inline at `_docs/02_document/module-layout.md` "Per-Component Mapping" → "Public API (de-facto)"; this task does not create a new contract file but DOES update Layout Rules #3 to declare the barrel files as the canonical Public API surface (no longer "de-facto").
|
||||
Reference in New Issue
Block a user