Introduces the ExtensionData namespace with the full contract and service
layer that future extension records (municipality profiles, election jobs,
service configs) will use to store and validate their legacy foreign references.
- LegacyLinkType enum (JurisdictionJCode, KitId, ContactId)
- LegacyLinkReference value record with typed factory methods
- ILegacyLinkValidator / LegacyLinkValidator — validates each link type
against the anti-corruption layer; returns descriptive errors on failure
- ILegacyLinkedRecord / ILegacyLinkedRecordProvider — contracts extension
record types implement to participate in integrity checks
- ILegacyLinkIntegrityCheck / LegacyLinkIntegrityService — scans all
registered providers, computes consistency percentage (NFR13 target 99.9%)
- POST /api/admin/legacy-link/integrity-check (Admin-only, audit-logged)
- 19 unit tests covering all four ACs
Stories 1.10-1.13 register ILegacyLinkedRecordProvider implementations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
React StrictMode's double-invocation of useEffect caused the first run to
remove oidcStateStorageKey from sessionStorage before the async code exchange
began. The second run would see the missing state, throw, and display
"Authentication failed" — while the first run completed in the background
(storing tokens and replacing the URL), making a page refresh appear to work.
Introduces a module-scope Promise (pendingCallbackSequence) that covers the
full callback sequence: token exchange, session fetch, and URL navigation. The
second effect run shares the same Promise and awaits its result, so no re-
validation or re-fetching occurs, and only the non-cancelled run sets state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>