# Frontend Application Foundation This repository is not treated as a minimal React project template and it is not an independent general-purpose SDK. Its architectural role is: > Frontend Application Foundation = Starter / Composition Skeleton + Reusable Capability Platform The starter side owns bootstrap, routing, providers, project conventions and a removable reference feature. The capability side owns reusable technical problems such as HTTP execution, Server State, authentication boundaries, IndexedDB/OPFS, Cache Storage, realtime, browser RPC, transfer and diagnostics. The cost of a sophisticated capability is acceptable only when product features do not have to understand that internal sophistication. ## Hybrid architecture Platform code is horizontal: - `application`: generic application inputs/policies/ports - `contracts`: genuinely cross-capability shared vocabulary and registries - `adapters`: reusable capability runtimes - `presentation`: generic UI/routing/query integration - `bootstrap`: concrete composition Product business code is vertical: - `features//domain` - `features//application` - `features//contracts` - `features//adapters` - `features//presentation` The dependency model is: Domain / Use case | | owns v Business port ^ | implements / binds | Feature-owned adapter binding | | generic type + mapper/codec + policy v Reusable capability runtime | v Browser / network / native API A use case never imports a platform adapter. Generic binding happens in the feature adapter/composition seam. ## Capability-specific typed bindings Do not introduce one universal `Repository` abstraction for HTTP, storage, realtime and transfer. Their lifecycle and failure semantics differ. A reusable capability boundary is composed from: - generic input/output types, - feature-owned mapper or codec, - feature-selected policy, - one capability-specific runtime. The HTTP reference path is the first concrete example. `src/adapters/http/feature-http-binding.ts` owns transport/outcome normalization. The reference feature contributes only: - operation ID, - route ID, - exact request input type, - exact success value type, - wire-to-domain mapper. The feature gateway therefore does not reimplement timeout, cancellation, transport failure, authentication failure or contract-violation projection. Storage, realtime and transfer may gain their own typed binders only after actual feature repetition demonstrates the need. They must not be forced through the HTTP abstraction. ## Custom adapter escape hatch A product feature uses a reusable capability when the capability preserves the business requirement. If a platform contract would require changing or weakening the business model, the feature implements a custom outbound adapter behind the same application port. The architecture boundary remains stable; platform reuse is optional. ## Feature installation A feature owns its contract, runtime and adapter contributions. Central installed catalogs are aggregation points only: - `installed-product-manifest.ts`: which product features are compiled/selected - `installed-feature-contracts.ts`: contract aggregation - `installed-feature-runtimes.tsx`: runtime contribution aggregation - `installed-feature-adapters.ts`: application-input contribution aggregation Feature-specific composition belongs under the feature itself. Central catalogs must not grow feature-specific branching logic. Adapter contributions declare the platform capabilities they consume through `needs`. The generic contribution seam in `src/features/feature-adapter-contribution.ts` derives the context from that list, so an HTTP-only feature cannot reach IndexedDB and an IndexedDB-only feature does not receive the HTTP executor. It also binds `featureId -> ApplicationFeatureInputs[featureId]` at the contribution definition site instead of recovering that relationship with a final catalog cast. A contribution becomes composable only through `defineFeatureAdapterContribution()`. That factory adds the private contribution brand required by `composeFeatureAdapterInputs()`; a raw object with the same visible fields is rejected by TypeScript and checked again at runtime. The negative type fixture `invalid-direct-feature-composition.ts` is part of FE-GATE-003 so this authority cannot be bypassed by calling the composer directly. For IndexedDB-backed features, `createIndexedDbRepositoryProvider()` is the composition helper that maps feature-owned repository identities to typed `IndexedDbRepositoryPort` instances. It does not create a universal storage repository or move dataset/migration/lifecycle policy into the feature. The repository now has two executable contribution shapes: - Reference feature: `needs: ["http"]` - Local Draft feature: `needs: ["indexedDb"]` Local Draft is a compiled architecture consumer used to prove the second capability shape; it is not added to the default product manifest. Its browser-capability test composes the feature over the real `createIndexedDbRuntime` and native IndexedDB, proving that this seam is not limited to an in-memory contract fixture. ## Presentation consumer surface `ApplicationProvider` remains the composition root for presentation, but new consumers should not navigate a root `ApplicationApi` service locator. Use the narrow hooks in `src/presentation/providers/application-provider.tsx`: - `useApplicationSession` - `useApplicationPreferences` - `useApplicationDiagnostics` - `useApplicationRuntime` - `useApplicationRecovery` - `useApplicationFeature` `useApplication` exists only as a deprecated compatibility escape hatch. ## Canonical imports New feature code should use capability public entry points rather than deep runtime modules. For HTTP the canonical path is `src/adapters/http/index.ts`. Compatibility re-exports may exist during a migration window, but they must be marked as compatibility/deprecated paths and should not expand into an unbounded public barrel. ## Policy ownership Duplication is judged by ownership, not by syntax percentage. Small local validators can remain duplicated when locality improves auditing. Business or concurrency policy must have one owner. For example the reference create mutation keeps definition ID, idempotency requirement, duplicate policy and invalidation policy in one feature-owned definition and binds only `scope` and `execute` per usage site. ## Runtime decomposition rule Large runtime files are not split by line count. Extract a boundary when it has its own state machine, lifecycle owner, failure model or compensation/recovery responsibility. Candidate seams include: - connection/open/upgrade lifecycle, - transaction ownership, - migration state machine, - reconnect/backoff and heartbeat, - subscription ownership, - retry/deadline/cancellation ownership, - settlement/reconciliation/cleanup. A cohesive 2,000-line state machine can remain together. A 300-line file with multiple lifecycle owners is a better extraction candidate. ### Current runtime boundary audit The current large-runtime inventory was reviewed using that rule. - IndexedDB remains large, but connection, transaction, migration, maintenance and failure translation already have separate owners/modules. - resumable upload already separates runtime policy, checkpoint persistence, HTTP control-plane transport, part execution, cancellation and mutation locking. - realtime already separates reconnect policy/coordinator, event codec/consumer and stream coordination. - OPFS is separated into browser runtime, journal, byte-store, policy and worker protocol/runtime responsibilities. - HTTP V3 still owned retry eligibility/backoff inside the execution state machine, so that responsibility moved to `src/adapters/http/http-retry-lifecycle.ts`. No other runtime is split merely because of its line count. ## Consumer quality metrics Before adding another abstraction, implement or model multiple real feature uses and measure: - feature-owned adapter LOC, - repeated platform glue, - number of platform-internal types exposed to the feature, - central catalog edits, - files changed for one normal query/command, - whether native browser/network APIs leak into the feature. The target feature-development path is: 1. domain type and invariant, 2. use case, 3. port, 4. transport/storage schema plus mapper/codec, 5. capability binding, 6. presentation controller/page. A product feature should not need to know the retry scheduler, abort ownership, effect-certainty machinery, transaction leases, reconnect coordinator, OPFS journal or provider lifecycle. The executable REST and IndexedDB consumer baselines are recorded in [`capability-consumer-experience.md`](./capability-consumer-experience.md). Contract placement and the global-vs-owner-local audit are recorded in [`contract-ownership.md`](./contract-ownership.md). ## Verification paths Product-development verification, capability verification and release assurance are intentionally separate. See [`docs/testing/taxonomy.md`](../testing/taxonomy.md). Host-level CI-runner tests belong to `tests/system`, not `tests/unit`. Reusable capability consumer contracts belong to `tests/contract`.