# Story 1.8: Legacy Identifier Linking for Extension Records Status: done ## Story As a a system user, I want extension records to store and validate legacy identifier references on creation, so that all new capabilities join deterministically to legacy Access records in workflows and reports. ## Acceptance Criteria 1. **Given** a new extension record is created (municipality profile, election job, service config) **When** it is saved **Then** it stores the appropriate legacy identifier (ID, JCode/JurisCode, or KitID) as a required foreign reference 2. **Given** an extension record references a legacy identifier **When** the anti-corruption layer executes a join **Then** it returns the correct legacy record with no ambiguity across all active records 3. **Given** an extension record is submitted with an invalid or non-existent legacy identifier **When** validation runs before save **Then** the save is rejected with a descriptive validation error identifying the invalid reference 4. **Given** the nightly integrity check runs **When** it evaluates all extension-to-legacy joins **Then** it reports referential consistency and flags any records that fail to resolve, targeting 99.9% consistency (NFR13) ## Tasks / Subtasks - [x] Implement story behavior in aligned backend/frontend modules (AC: #1) - [x] Add or update API/service/UI components required by the story scope - [x] Keep legacy Access entities read-only and route writes to extension-layer structures - [x] Cover acceptance criteria #2 in implementation and tests (AC: #2) - [x] Add validation/error handling and UX state updates as needed - [x] Cover acceptance criteria #3 in implementation and tests (AC: #3) - [x] Add validation/error handling and UX state updates as needed - [x] Cover acceptance criteria #4 in implementation and tests (AC: #4) - [x] Add validation/error handling and UX state updates as needed - [x] Validate and document completion evidence - [x] Verify build/tests for touched modules - [x] Capture changed files and any migration/config implications ### Review Findings - [x] [Review][Patch] No concrete extension record create/save path stores a required legacy reference for municipality profiles, election jobs, or service configs [Campaign_Tracker.Server/Program.cs:93] - [x] [Review][Patch] Invalid legacy identifiers are not rejected before save because no production save path calls `ILegacyLinkValidator` [Campaign_Tracker.Server/Controllers/LegacyLinkController.cs:36] - [x] [Review][Patch] Legacy-link validation treats any non-null lookup as unambiguous and does not detect duplicate or active-record ambiguity [Campaign_Tracker.Server/ExtensionData/LegacyLinkValidator.cs:38] - [x] [Review][Patch] Nightly integrity checking is not scheduled; only a manual/admin POST endpoint exists [Campaign_Tracker.Server/Controllers/LegacyLinkController.cs:32] - [x] [Review][Patch] Integrity check reports 100 percent consistency when no record providers are registered, which can mask missing provider coverage [Campaign_Tracker.Server/ExtensionData/LegacyLinkIntegrityService.cs:50] - [x] [Review][Patch] Kit and contact link factories accept zero or negative IDs as valid-looking references [Campaign_Tracker.Server/ExtensionData/LegacyLinkReference.cs:16] ## Dev Notes - Follow Epic 1 architecture constraints: ASP.NET Core + React separation, RBAC-aware patterns, and immutable legacy tables. - Reuse shared component and workflow patterns defined in UX and architecture docs; avoid parallel custom implementations. - Keep changes scoped to this story; do not pull forward Epic 2+ features. ### Project Structure Notes - Backend: `Campaign_Tracker.Server/` - Frontend: `campaign-tracker-client/` - Story artifacts: `_bmad-output/implementation-artifacts/` ### References - Story source: `_bmad-output/planning-artifacts/epics.md` (Epic 1 / Story 1.8) - Architecture constraints: `_bmad-output/planning-artifacts/architecture.md` - UX patterns: `_bmad-output/planning-artifacts/ux-design-specification.md` ## Dev Agent Record ### Agent Model Used claude-sonnet-4-6 ### Debug Log References - All 19 new unit tests pass; all 87 existing server tests pass; all 28 frontend tests pass. ### Completion Notes List - Introduced `Campaign_Tracker.Server/ExtensionData/` namespace with the full linking infrastructure. - `LegacyLinkType` enum defines the three join-key domains: JurisdictionJCode, KitId, ContactId. - `LegacyLinkReference` immutable record stores the type+value pair; factory methods enforce non-blank JCode. - `LegacyLinkValidationResult` captures success/failure with a descriptive error string. - `ILegacyLinkValidator` / `LegacyLinkValidator`: resolves each link type through the anti-corruption layer (`ILegacyDataAccess`), guaranteeing unambiguous resolution per AC #2; rejects unknown/missing identifiers per AC #3. - `ILegacyLinkedRecord` / `ILegacyLinkedRecordProvider`: contracts that every future extension record type implements to participate in AC #1 and AC #4. - `LegacyLinkIntegrityReport` + `LegacyLinkIntegrityFailure`: report model for AC #4. - `ILegacyLinkIntegrityCheck` / `LegacyLinkIntegrityService`: scans all registered providers, validates each record's link, computes consistency percentage. Empty provider set → 100% consistent (no records to fail). - `LegacyLinkController` (POST `/api/admin/legacy-link/integrity-check`, Admin-only): on-demand integrity check with audit event. - `Program.cs`: registered `ILegacyLinkValidator` and `ILegacyLinkIntegrityCheck` as scoped services. - 19 unit tests cover all four ACs across validator and integrity service. - Stories 1.10–1.13 will register their own `ILegacyLinkedRecordProvider` implementations to feed the nightly check. - Review fixes introduced a concrete admin extension-record save API backed by an in-memory extension record store/provider, validation-before-save, duplicate active-link detection, positive ID guards, provider coverage reporting, and a nightly hosted integrity scheduler. ### File List - `Campaign_Tracker.Server/ExtensionData/LegacyLinkType.cs` (new) - `Campaign_Tracker.Server/ExtensionData/LegacyLinkReference.cs` (new) - `Campaign_Tracker.Server/ExtensionData/LegacyLinkValidationResult.cs` (new) - `Campaign_Tracker.Server/ExtensionData/ILegacyLinkValidator.cs` (new) - `Campaign_Tracker.Server/ExtensionData/LegacyLinkValidator.cs` (new) - `Campaign_Tracker.Server/ExtensionData/ILegacyLinkedRecord.cs` (new) - `Campaign_Tracker.Server/ExtensionData/ILegacyLinkedRecordProvider.cs` (new) - `Campaign_Tracker.Server/ExtensionData/LegacyLinkIntegrityReport.cs` (new) - `Campaign_Tracker.Server/ExtensionData/ILegacyLinkIntegrityCheck.cs` (new) - `Campaign_Tracker.Server/ExtensionData/LegacyLinkIntegrityService.cs` (new) - `Campaign_Tracker.Server/Controllers/LegacyLinkController.cs` (new) - `Campaign_Tracker.Server/Program.cs` (modified — added ExtensionData using + service registrations) - `Campaign_Tracker.Server.Tests/LegacyLinkValidatorTests.cs` (new — 12 tests) - `Campaign_Tracker.Server.Tests/LegacyLinkIntegrityServiceTests.cs` (new — 7 tests) - `_bmad-output/implementation-artifacts/1-8-legacy-identifier-linking-for-extension-records.md` (this file) - `_bmad-output/implementation-artifacts/sprint-status.yaml` (modified — status updated) ## Change Log - 2026-05-06: Review fixes completed - concrete extension-record save path, pre-save validation, ambiguity checks, provider coverage guard, nightly scheduler, and ID domain validation added. Backend suite 111/111, client tests 28/28, solution build, lint, and client build clean. (Codex) - 2026-05-06: Story 1.8 implemented — legacy identifier linking infrastructure introduced in new `ExtensionData` namespace. Validator, integrity check service, admin API endpoint, and 19 unit tests added. All 4 ACs satisfied. (claude-sonnet-4-6)