# Feature-Space Alignment Check

> **TLDR:** A repeatable audit run per feature space (FTDT, Login, Jumpseat, Documents, …)
> that scores the feature against the eight GAP principles (work items 2105–2112 under
> Feature 2104) **and** the N-Tier layer rules. Output is a per-GAP verdict table split
> **Blazor Hybrid / new N-Tier code vs legacy XAML head code**, with file:line evidence and
> a remediation list ordered by effort. **Verdicts and remediation priority weight the
> Blazor Hybrid column** — the XAML surface is end-of-life and its column is informational
> (fix legacy only when cheap, beta-stabilizing, or correctness-protecting). Records go to
> the Feature 2104 discussion; remediation gets an umbrella work item (child of 2104).
> First run: FTDT, 2026-07-07 → AB#2215.

## Purpose

The N-Tier refactor (Epic 2081) and the gap analysis (Feature 2104) define how code
should be shaped. This check measures one **feature space** at a time against both, so
alignment work can be scoped, ordered, and tracked per feature instead of as one
unbounded cleanup. Run it before investing new work in a feature area, after a large
merge touching the area, or when scheduling remediation.

## Step 1 — Define the surface

Inventory every file the feature owns, and classify each as **NEW** (post-refactor:
Application services/ports, Presentation ViewModels, Blazor components/pages) or
**LEGACY** (head ViewModels/pages, helpers, BusinessLogic). The split is what makes the
verdicts actionable: the **Blazor Hybrid implementation is the compliance target** — the
legacy XAML surface will be abandoned, so its column is reported for completeness and
acted on only where an item is cheap, improves beta stability, or protects correctness
while XAML pages remain user-reachable (e.g. a diverged rules fork). Typical buckets:

| Bucket | Where to look |
|---|---|
| Head ViewModels / pages | `ALPAMobile/ViewModels/`, `ALPAMobile/Pages/` |
| Head helpers / shims / adapters | `ALPAMobile/Helpers/`, `ALPAMobile/Extensions/`, `ALPAMobile/Services/Platform/` |
| Legacy business logic | `ALPAMobile/BusinessLogic/` |
| Application services + ports | `ALPAMobile.Application/Services/<feature>/`, `ALPAMobile.Application/Abstractions/` |
| Domain models / interfaces | `ALPAMobile.Domain/` |
| Presentation ViewModels | `ALPAMobile.Presentation/ViewModels/` |
| Blazor UI | `ALPAMobile/Components/`, `ALPAMobile.HybridUi/` |

## Step 2 — GAP principle sweep

Score each GAP for the feature. What to actually look for:

| GAP | Work item | Check |
|---|---|---|
| 1 — Constructor injection | 2105 | Grep `GetService`, `Handler?.MauiContext`, `AppServices.` in the feature's ViewModels. Count hits; note which are documented transitional shims. |
| 2 — Toolkit commands | 2106 | Count `new Command(` vs `RelayCommand`/`AsyncRelayCommand` per file. Blazor VMs driven by `@onclick` method calls are exempt (no commands). |
| 3 — ObservableObject base | 2107 | Trace the VM base chain; flag `BindableBase`. Passive POCO projection VMs (no INPC at all) are a deliberate pattern — report as PARTIAL, not a violation. |
| 4 — Rules out of the head | 2108 | Business rules belong in Application (engines/services) with models and interfaces in Domain, per `N-TIER-ARCHITECTURE.md`. Flag rule code under `ALPAMobile/BusinessLogic/` and — critically — **diff any duplicated copies**: a diverged fork means legacy and new paths can compute different results. |
| 5 — Narrow ports | 2109 | Flag injection of `IDataManager` or fat `IRestService`; consumers should take role-specific ports (`I<Feature>Queries`, service ports). Note the used-surface ratio when a fat interface is injected. |
| 6 — VM-level validation | 2110 | Look for `INotifyDataErrorInfo` / `IValidationRule` / `ObservableValidator`. Throw-and-alert validation (validator throws, VM catches → `DisplayAlert`) is NON-COMPLIANT. |
| 7 — Navigation abstraction | 2111 | Count `Shell.Current` per VM (navigation **and** `DisplayAlertAsync`). Call out the injected-but-bypassed case — `INavigationService` in the constructor while calls still hit the static — it converts mechanically and cheaply. |
| 8 — Settings behind interfaces | 2112 | Flag concrete `AppProperties` injection. Feature-scoped settings ports (e.g. `IFTDTBetaSettings`) satisfy the principle — arguably better than generic `ISettingsService` (ISP). |

## Step 3 — N-Tier layer checks

The `LayerDependencyRulesTests` suite enforces the hard boundaries (run it — it must be
green), but the check also covers what the tests can't see:

- **Port adherence in the head:** direct MAUI Essentials statics (`MainThread`,
  `Connectivity`, `Preferences`, `Launcher`, `Browser`) in feature files where an
  Application port exists (`IMainThreadDispatcher`, `IConnectivityService`, …). Legal in
  the head, but it blocks the file's migration into Presentation, where MAUI is banned.
  Adapters implementing the ports and composition-root bootstrap are exempt.
- **Transport types above Infrastructure:** exception types (`HttpRequestException`) or
  client types leaking into Presentation/Application. Translate at the port boundary
  (see `ConnectivityException` for the pattern). The arch tests ban `System.Net.Http`
  in Application — a classifier there will fail the suite; the translation must happen
  in the adapter.
- **Factory vs Options discipline:** raw-JSON/DTO→domain mapping only through
  `RawRepresentationFactory<T>` (or confined to mock services); configuration through
  Options — see [Engineering Conventions](ENGINEERING-CONVENTIONS.md).
- **Composition-root discipline:** `[Obsolete]` fat-aggregate registrations stay inside
  `#pragma warning disable CS0618` blocks in the service-collection extensions; scaffold
  routers register as their own concrete types, never rebinding global ports.
- **Blazor surfaces:** no inline `style=` (CSS custom properties carrying data-bound
  values excepted), subsystem-owned class prefixes, interactive controls backed by real
  state — the [Engineering Conventions](ENGINEERING-CONVENTIONS.md) rules.
- **Component placement vs its consumers:** every component a Presentation `.razor` uses
  must live in Presentation too. `RazorComponentResolutionTests` now enforces this, but
  understand *why* it exists: Razor does **not** error on a tag it cannot resolve — it
  emits `<MyComponent>` as an unknown HTML element, so the build stays green while the
  markup silently does nothing. A C# type crossing the same boundary is a build error.
  When a feature's files move between projects, check the tags, not just the compiler.
- **Pages holding policy that could be tested:** routing, dispatch and content-type
  decisions in a `.razor.cs` can only be exercised on a device. If a page is deciding
  *where something goes*, it belongs in an injectable resolver under
  `ALPAMobile.Presentation/Components/Navigation/` that returns a decision the page acts
  on — see `DeepLinkResolver` and `NotificationContentRouter`. The smell: a code-behind
  method with a `switch` over server-supplied strings and no test naming it.

## Step 4 — Evidence and verdicts

- Every finding carries **file:line and a count** ("62 `Shell.Current` across 6 VMs"),
  never just an impression.
- Verdict per GAP and per layer check: **COMPLIANT / PARTIAL / NON-COMPLIANT**, given
  separately for NEW and LEGACY code, plus an overall.
- Distinguish **by-design deferrals** (documented transitional shims, passive projection
  VMs) from plain debt, and say which work item already tracks an item before filing
  anything new.

## Step 5 — Record and remediate

1. Post the full verdict table + evidence as a **discussion comment on Feature 2104**.
2. If remediation is needed, create one **umbrella Issue as a child of Feature 2104**
   listing the items **ordered by effort (low → high)**, referencing existing work items
   instead of duplicating them, and flagging any correctness-relevant item (e.g. a
   diverged rules fork) for priority.
3. Add the delivery to the working task queue so it is visible next to the feature work.

## Completed checks

| Feature space | Date | Record | Remediation |
|---|---|---|---|
| FTDT | 2026-07-07 | Feature 2104 discussion | AB#2215 (7 items) |
| Login | 2026-07-08 | Feature 2104 discussion | AB#2218 (1 item, one layer beneath a clean VM tier) |
| DevDashboard (BlazorHostPage) | 2026-07-08 | Feature 2104 discussion | AB#2222 (1 item) |
| Figma-aligned Home + banner-test variant | 2026-07-08 | Feature 2104 discussion | AB#2223 (1 item) |
