--- stepsCompleted: - step-01-validate-prerequisites - step-02-design-epics - step-03-create-stories inputDocuments: - _bmad-output/planning-artifacts/prd.md - _bmad-output/planning-artifacts/architecture.md - _bmad-output/planning-artifacts/ux-design-specification.md --- # Campaign_Tracker App - Epic Breakdown ## Overview This document provides the complete epic and story breakdown for Campaign_Tracker App, decomposing the requirements from the PRD, UX Design, and Architecture into implementable stories. ## Requirements Inventory ### Functional Requirements FR1: Client services staff can create and maintain municipality account profiles linked to existing jurisdiction identifiers. FR2: Client services staff can store and update municipality operational mailing and delivery addresses for election services. FR3: Client services staff can manage municipality service-contact records, including primary and secondary contacts. FR4: Client services staff can view municipality-specific service defaults from prior election cycles. FR5: Client services staff can create a new election-cycle job for a municipality without altering legacy Access tables. FR6: Client services staff can define election-cycle key dates for data files, proofing, pickups, deliveries, and mail activities. FR7: Client services staff can copy configurable planning defaults from the municipality's prior election-cycle job. FR8: Operations users can mark required planning fields complete and see readiness status for each election-cycle job. FR9: Client services staff can configure addressing service for an election-cycle job, including quantities and dependency fields. FR10: Client services staff can configure envelope service options including purple and blue envelope scenarios. FR11: Client services staff can configure office-copy requirements and related production details. FR12: Client services staff can configure sorting service options including quantity, class, permit/meter, and daily-sort flags. FR13: Users can record transportation-relevant service events tied to configured services. FR14: Operations users can schedule and update milestone dates for all configured service events. FR15: Operations users can view milestone timelines by municipality and by date across active election-cycle jobs. FR16: Operations users can detect missing or conflicting required milestones before execution cutoffs. FR17: Operations users can reassign milestone ownership and due dates across teams. FR18: Production users can update job status at defined process checkpoints. FR19: Support and operations users can log exceptions, blockers, and resolution notes against an election-cycle job. FR20: Users can record status transitions with timestamped history for key operational events. FR21: Users can view current and historical process state for a municipality's election-cycle jobs. FR22: Users can generate a transportation report grouped by date and sorted by county and municipality context. FR23: Users can generate a sorting report by municipality for all jobs with sorting enabled. FR24: Users can generate a sorting report by mail date for all jobs with sorting enabled. FR25: Users can export operational reports for downstream consumption in spreadsheet-compatible formats. FR26: Users can filter reports by date ranges, municipality, county, and service-status criteria. FR27: Administrators can manage user roles and permissions for client services, production, transportation, support, and admin functions. FR28: Administrators can manage extension-layer reference values used by planning and status workflows. FR29: Administrators can define required-field rules for election-cycle readiness checks. FR30: Administrators can configure notification/escalation rules for missed or at-risk milestones. FR31: System users can link extension records to existing legacy identifiers (ID, JCode/JurisCode, KitID) for unified workflows. FR32: Administrators can run compatibility validation checks that confirm legacy-table schemas remain unchanged. FR33: Support users can trace report values back to source records across legacy and extension datasets. FR34: Authorized users can correct extension-layer data issues without direct edits to immutable legacy tables. ### NonFunctional Requirements NFR1: 95% of authenticated page loads for core workflows complete in 2 seconds or less under normal operating load. NFR2: 95% of create/update operations for election-cycle records complete with user confirmation in 1 second or less. NFR3: Transportation and sorting report generation completes in 30 seconds or less for standard daily/weekly filters. NFR4: 100% of application traffic is encrypted in transit using TLS 1.2+ and verified by quarterly transport-security scans. NFR5: 100% of sensitive stored operational data is encrypted at rest with platform-managed AES-256 controls, verified by monthly configuration audits. NFR6: 100% of privileged operations enforce role-based authorization checks, and unauthorized requests are blocked and logged. NFR7: Security-relevant actions (authentication events and permission-sensitive updates) are logged within 5 seconds and retained for at least 365 days. NFR8: The system supports at least 10x growth in active election-cycle job volume without requiring legacy schema changes. NFR9: The system supports at least 150 concurrent operational users with p95 response time under 2 seconds and error rate under 1% during peak-period load testing. NFR10: MVP screens for forms, tables, and reports meet WCAG 2.1 AA-aligned criteria relevant to keyboard access, labeling, contrast, and focus visibility. NFR11: 100% of critical workflows are operable by keyboard-only interaction and validated against an accessibility test checklist in each release. NFR12: Legacy Access tables are treated as immutable dependencies, and release gates fail on any unauthorized schema delta against the approved baseline. NFR13: Extension-table joins to legacy identifiers maintain at least 99.9% referential consistency, verified by nightly integrity checks and pre-release validation runs. NFR14: Exported report datasets preserve required field parity for existing operational consumers. NFR15: Planned system availability target is 99.5% monthly during operational hours. NFR16: Every key milestone/status update is stored with audit history sufficient for operational reconstruction. NFR17: Recovery procedures ensure no committed extension-record updates are lost for the previous 24 hours of operations. ### Additional Requirements - **Starter Template (Epic 1, Story 1)**: ASP.NET Core 10 Web API + Vite React TypeScript. Initialization commands: `dotnet new sln -n campaign-tracker`, `dotnet new webapi -n Campaign_Tracker.Server -f net10.0 --use-controllers`, `dotnet sln add`, `npm create vite@latest campaign-tracker-client -- --template react-ts`. - Anti-corruption data access layer required between modern services and immutable legacy Access structures. - Separate operational write path (extension tables) from report read path (join/materialized/query layer) to reduce coupling. - Join-key integrity (ID, JCode/JurisCode, KitID) is a platform concern requiring scheduled validation jobs and release-gate checks. - Pre-commit validation orchestration as a shared capability: required-field checks, dependency checks, and policy checks before any sensitive state transition. - Parity governance workflow: run legacy and modern reports in parallel during transition; define parity acceptance thresholds per report type; require parity evidence as a formal cutover gate. - Compliance evidence model: generate and bind evidence artifacts (security events, data residency, accessibility) to release gates, not post-release documentation. - Discrepancy triage workflow with owner assignment, resolution status tracking, and closure audit trail. - Performance strategy under peak election windows: query shaping, caching strategy, and export throughput optimization for concurrent report generation. ### UX Design Requirements UX-DR1: Implement Ant Design v5 as base component library with custom token configuration — colorPrimary (#1F4E79), teal secondary (#0F766E), interactive accent (#2563EB), semantic status palette (success/warning/error/info/overdue), compact density profile, and Public Sans / IBM Plex Sans typography stack. UX-DR2: Build tri-pane workspace shell as primary application layout — left context/navigation pane (municipality and saved views), center dense editable operations grid, right risk queue + inspector/provenance panel; collapsible secondary pane at 1280–1599px. UX-DR3: Implement MunicipalityCycleWorkspaceGrid custom component — sticky header, column controls, inline editable cells, row status chips, provenance quick-view trigger; states: default, sorted, filtered, inline-editing, validation-error, loading, empty; keyboard row/cell navigation with ARIA sort state. UX-DR4: Implement SafeCommitRail custom component — pre-commit dependency/policy gate; anatomy: validation summary, blocking reasons, corrective links, reason-code selector, commit action; variants: inline rail (grid), drawer rail (detail mode); commit disabled until all checks and reason codes satisfied. UX-DR5: Implement CutoffRiskQueuePanel custom component — persistent urgency queue; anatomy: prioritized cards, countdown/due state, severity badges, owner, quick-open action; always-visible in right pane; reorders deterministically after updates; severity announced with text labels not color alone. UX-DR6: Implement BlockerResolutionDrawer custom component — in-context recovery workspace; anatomy: blocker details, dependency links, assignment, due-by edit, resolution note, save controls; writes exception history on save; returns user to previous context. UX-DR7: Implement DispatchRunBuilder custom component — date range controls, filter set, results grouped by date/county/municipality, quantity notes, export actions; next-day quick mode and 7-day lookahead mode; deep-links to corrective records on data gaps. UX-DR8: Implement ProvenanceTimelinePanel custom component — timestamped events, actor, reason code, changed fields, source link; compact inline preview and full audit view variants; semantic timeline structure for screen readers. UX-DR9: Implement keyboard-first interaction across all operational surfaces: visible 2px focus indicators, logical tab order in grids/forms/drawers/modals, standardized focus management for modal/drawer open/close flows. UX-DR10: Implement role-based workspace entry — distinct default views and saved filter presets for Client Services, Production, Transportation, Support, and Admin roles. UX-DR11: Implement "Next Best Action" prompt system per record to reduce decision latency in operations views. UX-DR12: Implement accessible status semantics — pair all status colors with text labels and icons; WCAG 2.2 AA minimum contrast (4.5:1 normal text, 3:1 large text/UI components); respect reduced-motion preferences. UX-DR13: Implement deterministic filtering/sorting with always-visible active filter chips, saved presets per user role/workspace, and predictable default sort (urgency then due date). UX-DR14: Implement progressive required-field validation — validate inline during edit; hard-block validation at publish/commit checkpoints only; show inline field-level errors plus top-level summary for multi-error states. UX-DR15: Implement desktop-first responsive strategy — minimum supported width 1280px; full tri-pane at 1600px+; compact tri-pane with collapsible secondary at 1280–1599px; reduced read mode with support notice below 1280px. ### FR Coverage Map FR1: Epic 1 - Municipality account profile creation and management FR2: Epic 1 - Municipality operational mailing/delivery addresses FR3: Epic 1 - Municipality service-contact records FR4: Epic 1 - Prior-cycle service defaults FR5: Epic 2 - Election-cycle job creation FR6: Epic 2 - Election-cycle key dates definition FR7: Epic 2 - Prior-cycle planning defaults copy FR8: Epic 2 - Required-field readiness status FR9: Epic 3 - Addressing service configuration FR10: Epic 3 - Purple/blue envelope service configuration FR11: Epic 3 - Office-copy requirements FR12: Epic 3 - Sorting service configuration FR13: Epic 3 - Transportation-relevant service events FR14: Epic 4 - Milestone scheduling and updates FR15: Epic 4 - Milestone timeline views FR16: Epic 4 - Missing/conflicting milestone detection FR17: Epic 4 - Milestone ownership reassignment FR18: Epic 5 - Job status updates at checkpoints FR19: Epic 5 - Exception, blocker, and resolution logging FR20: Epic 5 - Timestamped status transition history FR21: Epic 5 - Current and historical process state view FR22: Epic 5 - Transportation report (early municipality proof point) FR23: Epic 6 - Sorting report by municipality FR24: Epic 6 - Sorting report by mail date FR25: Epic 6 - Report export (spreadsheet-compatible) FR26: Epic 6 - Report filtering by date/municipality/county/status FR27: Epic 1 - Role and permission management foundation FR28: Epic 1 (seed/engine) + Epic 6 (admin UI) - Extension-layer reference value management FR29: Epic 1 (seed/engine) + Epic 6 (admin UI) - Required-field rules for readiness checks FR30: Epic 1 (seed/engine) + Epic 6 (admin UI) - Notification/escalation rule configuration FR31: Epic 1 - Legacy identifier linking (ID, JCode, KitID) FR32: Epic 1 - Legacy schema compatibility validation FR33: Epic 6 - Report-to-source record traceability FR34: Epic 6 - Extension-layer data correction FR35: Epic 2 - Spreadsheet import and column mapping (Story 2.6) FR36: Epic 3 - Proof/contact workflow status handling (Story 3.2 additions) ## Epic List ### Epic 1: Foundation, Auth, Municipality Profiles & Admin Seed Users can authenticate and access their role-specific workspace. Municipality account profiles, addresses, contacts, and service defaults are immediately available linked to legacy jurisdiction identifiers. Seed data makes the system operationally functional from day one. Audit logging runs as a shared infrastructure-level service across all epics. **FRs covered:** FR1, FR2, FR3, FR4, FR27, FR28 (seed+engine), FR29 (seed+engine), FR30 (seed+engine), FR31, FR32 **Architecture:** ASP.NET Core 10 + Vite React TS starter template initialization, anti-corruption data access layer, RBAC setup, legacy join-key integrity validation, compatibility check release gates, NFR7 shared audit logging infrastructure **UX:** Ant Design v5 token setup (UX-DR1), tri-pane workspace shell (UX-DR2), role-based workspace entry (UX-DR10), keyboard-first foundation (UX-DR9), desktop-first responsive strategy (UX-DR15) **Implementation note:** FR1–FR4 are CRUD-weight stories; FR27–FR32 carry infrastructure weight. Stories can be developed in parallel within the epic by splitting backend and frontend tracks. **Adoption target:** Epic 2 must begin within 6 weeks of Epic 1 delivery to preserve coordinator adoption momentum. ### Epic 2: Election-Cycle Job Creation & Planning Client services staff can create election-cycle jobs, define key dates, apply prior-cycle defaults, and track required-field readiness status to production-ready publication — with a kanban entry-point that assigns municipalities to cycle lanes (multiple concurrent cycle support included). **FRs covered:** FR5, FR6, FR7, FR8 **UX:** Municipality-to-Cycle Kanban Entry Point (UX-DR16) — municipality cards in multiple cycle lanes; SafeCommitRail component (UX-DR4) built as reusable module for Epic 3 and Epic 5 consumption; progressive required-field validation (UX-DR14) **Metric:** Epics 2+3 together are the primary delivery point for the 40% election job setup-time reduction KPI (PRD Success Criteria) ### Epic 3: Service Configuration Client services staff can configure all election services on a job: addressing (quantities and dependency fields), purple and blue envelope scenarios, office-copy requirements, sorting options (quantity, class, permit/meter, daily-sort flags), and transportation-relevant service events. **FRs covered:** FR9, FR10, FR11, FR12, FR13 **UX:** MunicipalityCycleWorkspaceGrid (UX-DR3) — inline editable cells, row status chips, deterministic filtering; SafeCommitRail reused from Epic 2 **Metric:** Epics 2+3 together = 40% setup-time reduction measurement checkpoint; post-Epic 3 coordinator survey recommended ### Epic 4: Milestone Scheduling & Timeline Management Operations teams can schedule and update milestone dates for all configured service events, view milestone timelines by municipality and date across active jobs, detect missing or conflicting required milestones before execution cutoffs, and reassign milestone ownership and due dates across teams. **FRs covered:** FR14, FR15, FR16, FR17 **UX:** CutoffRiskQueuePanel (UX-DR5) — persistent urgency queue, always-visible in right pane; Next Best Action prompts (UX-DR11); deterministic filtering/sorting with saved presets (UX-DR13) ### Epic 5: Production Tracking, Exception Management, Audit History & Transportation Report Production teams can update job statuses at checkpoints, log exceptions and blockers with resolution notes, record timestamped status transitions, and view complete process history. Transportation coordinators receive the first external-facing report — a date-sorted transportation dispatch report grouped by county and municipality — giving municipality clients an early proof point before the full reporting suite. **FRs covered:** FR18, FR19, FR20, FR21, FR22 **UX:** ProvenanceTimelinePanel (UX-DR8) — timestamped events, actor, reason code, changed fields; BlockerResolutionDrawer (UX-DR6) — in-context recovery workspace; accessible status semantics (UX-DR12) **External proof point:** FR22 transportation report provides municipality clients visible evidence of modernization before Epic 6 ### Epic 6: Full Reporting Suite, Admin Management UIs & Governance All remaining operational reports with deterministic filtering and spreadsheet-compatible export. Admin management UIs for reference values, required-field rules, and escalation rules (completing FR28/FR29/FR30 from seed-only to fully configurable). Support analysts get report-to-source traceability and extension-layer data correction tools. Parity governance closes with a defined sunset criteria, named owner, and municipality sign-off before legacy report deprecation. **FRs covered:** FR23, FR24, FR25, FR26, FR33, FR34 **Admin UIs:** FR28 reference value management UI, FR29 required-field rules management UI, FR30 escalation rule configuration UI **UX:** DispatchRunBuilder (UX-DR7); deterministic filtering with saved presets (UX-DR13) **Parity governance:** Must include sunset trigger threshold, named operational owner, and municipality client sign-off criteria before legacy report stack is deprecated --- ## Epic 1: Foundation, Auth, Municipality Profiles & Admin Seed Users can authenticate and access their role-specific workspace. Municipality account profiles are immediately available linked to legacy jurisdiction identifiers. Seed data makes the system operationally functional. Audit logging runs as a shared infrastructure-level service across all epics. ### Story 1.1: Project Initialization & Solution Scaffold As a developer, I want the solution scaffolded with ASP.NET Core 10 Web API and Vite React TypeScript, So that the team has a working, runnable baseline with the correct project structure for all subsequent stories. **Acceptance Criteria:** **Given** the initialization commands are run (`dotnet new sln`, `dotnet new webapi`, `npm create vite@latest --template react-ts`) **When** the solution builds **Then** both Campaign_Tracker.Server and campaign-tracker-client compile without errors **Given** the solution is running locally **When** the Vite dev server starts **Then** the React app loads at the configured local URL with no console errors **Given** the server is running **When** GET /health is called **Then** it returns 200 OK with a status payload **Given** the solution structure is created **When** reviewed against the architecture decision **Then** the .sln file, server project folder, and client project folder exist at expected paths and .gitignore excludes node_modules, bin, obj, and .env files --- ### Story 1.2: Workspace Shell & Ant Design Foundation As any authenticated user, I want a consistent tri-pane application layout with the Ant Design visual system applied, So that I have a predictable, accessible, and keyboard-navigable operational environment from day one. **Acceptance Criteria:** **Given** the app loads after authentication **When** an authenticated user enters the application **Then** the tri-pane layout renders with a left navigation pane, center content area, and collapsible right panel **Given** the Ant Design token configuration is applied **When** the UI renders **Then** colorPrimary is #1F4E79, teal secondary is #0F766E, semantic status colors (success/warning/error/info/overdue) match the UX spec, and compact density profile is active (UX-DR1) **Given** a user interacts with any focusable element **When** tabbing through the interface **Then** visible 2px focus indicators are present on all interactive elements with logical tab order (UX-DR9) **Given** the viewport is 1280–1599px **When** the layout renders **Then** the right panel is collapsible and the tri-pane adapts to compact mode (UX-DR15) **Given** the viewport is below 1280px **When** the layout renders **Then** a reduced read mode with a support notice is displayed and full editing is not available **Given** any status indicator renders **When** viewed **Then** status is conveyed with both color and a text label or icon — never color alone (UX-DR12) --- ### Story 1.3: Keycloak Realm Configuration & OIDC Integration As any team member, I want to authenticate using Keycloak single sign-on via OpenID Connect, So that I can securely access the application with my organizational credentials. **Acceptance Criteria:** **Given** a user navigates to the application while unauthenticated **When** they load any protected route **Then** they are redirected to the Keycloak login page for the configured realm **Given** a user enters valid Keycloak credentials **When** authentication succeeds **Then** they receive a JWT access token, are redirected to their role-specific workspace, and the authentication event is logged to the audit service within 5 seconds (NFR7) **Given** a user's access token expires **When** they make an authenticated request **Then** the refresh token flow silently renews the session or redirects to login if the refresh token is also expired **Given** an invalid or expired token is presented to the API **When** the request is processed **Then** the API returns 401 Unauthorized and the failed authentication attempt is captured in the audit log **Given** the application is deployed **When** traffic is inspected **Then** all communication is over TLS 1.2+ (NFR4) and sensitive token data is not exposed in URLs or logs --- ### Story 1.4: Keycloak Role Mapping & Application Authorization As a developer, I want Keycloak roles mapped to application permission policies enforced on API routes and frontend features, So that each user sees only the capabilities appropriate to their operational role. **Acceptance Criteria:** **Given** a Keycloak user has the ClientServices role **When** they authenticate and navigate **Then** they can access municipality profile and election-cycle creation routes and cannot access admin-only or production-only routes **Given** a Keycloak user has the Admin role **When** they authenticate **Then** they can access all application routes including admin-sensitive functions **Given** a user without a recognized application role authenticates **When** they access any protected route **Then** they receive 403 Forbidden and the unauthorized access attempt is logged with actor identity **Given** a privileged operation is performed **When** it completes **Then** the authorization check result, actor, and resource are captured by the audit logging service within 5 seconds (NFR6, NFR7) **And** roles ClientServices, Production, Transportation, Support, and Admin are managed entirely in Keycloak Admin Console — FR27 is satisfied without a custom role management UI --- ### Story 1.5: Shared Audit Logging Infrastructure As a system, I want all security-relevant and operational events captured by a shared logging service, So that audit history is uniformly available across all application features from day one without per-feature implementation. **Acceptance Criteria:** **Given** any security-relevant action occurs (auth event, permission check, privileged update) **When** the action completes **Then** the event is written to the audit log within 5 seconds including actor identity, timestamp (UTC), action type, resource identifier, and outcome (NFR7) **Given** audit records are persisted **When** the retention policy is evaluated **Then** records are retained for at least 365 days and are not purgeable by standard application operations (NFR7) **Given** any application feature calls the audit logging service **When** the call is made **Then** it succeeds using the shared service contract — the calling feature does not implement its own audit persistence **Given** an audit record is written **When** retrieved for review **Then** it is append-only — no update or delete operations are available on audit records **Given** the audit service is unavailable **When** an auditable action occurs **Then** the action is blocked or queued — auditable operations must not silently proceed without capture --- ### Story 1.6: Legacy Anti-Corruption Data Access Layer As a developer, I want a dedicated read-only data access layer for legacy Access-derived entities using fixed join keys, So that legacy data is available to all features without any risk of schema mutation or direct table coupling. **Acceptance Criteria:** **Given** a feature requests municipality or jurisdiction data **When** the anti-corruption layer is called **Then** it returns data joined by ID, JCode/JurisCode, or KitID without modifying any legacy table structure or content (NFR12) **Given** the anti-corruption layer executes any query **When** the operation is inspected **Then** only SELECT operations are permitted — INSERT, UPDATE, and DELETE on legacy entities are blocked at the layer boundary **Given** legacy data is returned through the layer **When** mapped to the application domain **Then** it is converted to strongly-typed domain model objects before being returned to any calling feature **Given** any application code outside the layer attempts to query legacy tables directly **When** reviewed in code **Then** no such direct access exists — the anti-corruption layer is the sole access point for legacy data --- ### Story 1.7: Legacy Schema Compatibility Validation Gate As an administrator, I want to run a compatibility check that confirms legacy table schemas are unchanged against the approved baseline, So that every release can be gated on legacy integrity before deployment. **Acceptance Criteria:** **Given** the compatibility check is triggered **When** it runs against the live database **Then** it compares all legacy table structures (column names, types, constraints) against the approved schema baseline stored at initialization **Given** a structural change is detected on any legacy table **When** the check completes **Then** it returns a failure status with a detailed report identifying the affected table, column, and change type (NFR12) **Given** no schema drift is detected **When** the check completes **Then** it returns a pass confirmation with timestamp, number of tables verified, and zero drift count **Given** the check is integrated into the release pipeline **When** it fails **Then** the release is blocked automatically and a failure report is surfaced to the administrator **Given** an administrator is logged in **When** they navigate to the compatibility check feature **Then** they can trigger the check manually and view the most recent check history with timestamps --- ### Story 1.8: Legacy Identifier Linking for Extension Records As 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:** **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 **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 **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 **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) --- ### Story 1.9: Seed System Reference Values & Rule Defaults As a developer, I want the system seeded with default reference values, required-field rules, and escalation rule defaults, So that Epics 2–5 are immediately functional without requiring administrator configuration before use. **Acceptance Criteria:** **Given** the application initializes for the first time **When** the seed operation runs **Then** operational status sets, service template defaults, and extension-layer reference values are populated in the database **Given** the seed runs **When** required-field rules are evaluated **Then** default readiness fields for election-cycle jobs are defined and evaluable by Epic 2's readiness status feature (FR29) **Given** the seed runs **When** escalation rule defaults are checked **Then** at least one default rule exists covering overdue milestone alert scenarios (FR30) **Given** the seed operation is run multiple times **When** it completes **Then** no duplicate records are created — the operation is fully idempotent **Given** Epic 6 admin UIs update reference values or rules **When** the changes are saved **Then** they persist independently of the seed — rerunning the seed does not overwrite admin-managed values --- ### Story 1.10: Municipality Account Profile As a client services staff member, I want to create and maintain municipality account profiles linked to legacy jurisdiction identifiers, So that permanent municipality data is managed in the extension layer without modifying legacy Access tables. **Acceptance Criteria:** **Given** a client services user navigates to municipality management **When** they create a new municipality profile **Then** it is saved to the extension layer with a required link to a valid legacy jurisdiction identifier (ID/JCode) **Given** a municipality profile is created **When** the profile is loaded **Then** the anti-corruption layer resolves the legacy join and displays combined extension and legacy data together in the workspace grid **Given** a profile field is updated **When** saved **Then** the change is recorded in the audit log with actor identity and timestamp **Given** a user attempts to create a profile without a valid legacy jurisdiction identifier **When** the form is submitted **Then** the save is rejected with a clear validation message identifying the required legacy link **And** no INSERT, UPDATE, or DELETE operations are performed on legacy Access tables at any point --- ### Story 1.11: Municipality Operational Addresses As a client services staff member, I want to store and update municipality operational mailing and delivery addresses, So that election services reference current address information without depending on legacy records. **Acceptance Criteria:** **Given** a municipality profile exists **When** a client services user adds an address **Then** they can specify mailing or delivery address type, and the record is saved to the extension address table linked to the municipality profile **Given** an address is updated **When** saved **Then** the previous address is preserved in history, the new address is marked as current, and the change is logged with actor and timestamp **Given** an address record is saved **When** retrieved **Then** it includes address type, street, city, state, zip, and effective date fields **Given** a municipality profile is viewed **When** addresses are displayed **Then** mailing and delivery addresses are shown with clear type labels and current/historical status --- ### Story 1.12: Municipality Service Contacts As a client services staff member, I want to manage municipality service-contact records including primary and secondary contacts, So that the right people can be reached during election operations without consulting legacy records. **Acceptance Criteria:** **Given** a municipality profile exists **When** a client services user adds a contact **Then** they can designate it as primary or secondary contact and save name, role/title, phone, and email **Given** a municipality has both a primary and secondary contact **When** the profile is viewed **Then** both contacts are displayed with clear primary/secondary designation labels **Given** a contact is updated or removed **When** the change is saved **Then** it is recorded in the audit log with actor identity and timestamp **Given** a user attempts to save a contact without required fields (name, contact type) **When** the form is submitted **Then** the save is rejected with inline validation errors identifying the missing fields --- ### Story 1.13: Municipality Prior-Cycle Service Defaults View As a client services staff member, I want to view municipality-specific service defaults from prior election cycles as a read-only reference, So that I can reference proven configurations when setting up new election-cycle jobs in Epic 2. **Acceptance Criteria:** **Given** a municipality has at least one completed election-cycle job **When** a client services user views the prior-cycle defaults panel **Then** the most recent cycle's service configurations are displayed as read-only reference data **Given** a municipality has multiple prior cycles **When** the defaults panel is viewed **Then** the most recent cycle is shown by default with a cycle selector control to navigate older cycles **Given** no prior cycles exist for a municipality **When** defaults are requested **Then** the system displays a clear "No prior cycle defaults available" state with guidance to create the first cycle **And** all data in this view is read-only — the apply-to-new-job action is out of scope for this story and delivered in Epic 2 (Story 2.4) --- ## Epic 2: Election-Cycle Job Creation & Planning Client services staff can create election-cycle jobs, define key dates, apply prior-cycle defaults, and track required-field readiness through to production-ready publication — with a kanban entry point showing municipalities across cycle lanes. ### Story 2.1: Municipality-to-Cycle Kanban Entry Point As a client services staff member, I want a kanban board showing municipalities as cards organized by election cycle lanes, So that I can see at a glance which municipalities are assigned to which cycles and initiate new cycle jobs from a familiar planning view. **Acceptance Criteria:** **Given** a client services user navigates to the election cycle workspace **When** the kanban loads **Then** municipalities with active cycle jobs appear as cards in their respective cycle lane columns, and municipalities with no active jobs appear in an "Unassigned" lane **Given** a municipality has jobs in multiple concurrent election cycles **When** the kanban renders **Then** the municipality card appears in each relevant cycle lane independently (UX-DR16 multi-lane support) **Given** a user views a municipality card **When** displayed in a cycle lane **Then** the card shows municipality name, jurisdiction code, cycle job status badge, and a quick-open action **Given** a cycle lane contains many municipality cards **When** the user scrolls within the lane **Then** the lane column header remains visible and performance is maintained **Given** a user interacts with the kanban via keyboard **When** navigating cards and lanes **Then** all card actions are reachable without a mouse and focus indicators are visible (UX-DR9) --- ### Story 2.2: Create Election-Cycle Job As a client services staff member, I want to create a new election-cycle job for a municipality from the kanban board, So that the municipality is assigned to an election cycle without altering any legacy Access tables. **Acceptance Criteria:** **Given** a municipality card is in the Unassigned lane **When** a client services user initiates job creation **Then** they can select an existing cycle or name a new cycle, and a new election-cycle extension record is created linked to the municipality's legacy identifier (ID/JCode) **Given** an election-cycle job is created **When** saved **Then** it is stored in the extension table with the municipality's legacy identifier as a required join key and the municipality card moves to the selected cycle lane on the kanban **Given** an election-cycle job is created **When** the job record is inspected **Then** it includes municipality reference, cycle name, creation actor, creation timestamp, and a status of "In Setup" **Given** a job creation is attempted without selecting or naming a cycle **When** the form is submitted **Then** the save is rejected with a clear inline validation message **And** no INSERT, UPDATE, or DELETE operations are performed on legacy Access tables at any point --- ### Story 2.3: Election-Cycle Key Dates As a client services staff member, I want to define election-cycle key dates for data files, proofing, pickups, deliveries, and mail activities, So that the election timeline is established and visible to all operational teams. **Acceptance Criteria:** **Given** an election-cycle job exists **When** a client services user opens the key dates section **Then** they can enter dates for: data file receipt, proofing, customer envelope pickup, customer envelope delivery, purple envelope pickup, purple envelope delivery, blue envelope pickup, blue envelope delivery, sort dates, mail dates, and tray delivery **Given** a key date is entered and saved **When** the job is reloaded **Then** all entered dates persist and are visible in the job's date summary view **Given** a key date entry would create a logical conflict (e.g., mail date before sort date) **When** the conflicting date is entered **Then** an inline warning identifies the conflict with specific field names — the save is not blocked but the warning is persistent until resolved **Given** a key date is updated **When** saved **Then** the change is recorded in the audit log with actor identity and timestamp **Given** a user enters dates via keyboard **When** using the date picker controls **Then** dates are enterable via keyboard without requiring mouse interaction (UX-DR9) --- ### Story 2.4: Prior-Cycle Defaults Application As a client services staff member, I want to copy configurable planning defaults from a municipality's prior election-cycle job into a new cycle, So that I can apply proven configurations without re-entering all details from scratch. **Acceptance Criteria:** **Given** an election-cycle job exists and the municipality has at least one prior completed cycle **When** a client services user selects "Apply Prior-Cycle Defaults" **Then** the prior cycle's service configurations and key date offsets are pre-populated into the new job's fields **Given** prior-cycle defaults are applied **When** pre-populated fields are displayed **Then** each inherited field is visibly marked as "Inherited from [cycle name/year]" to distinguish it from manually entered data **Given** a user applies defaults and then manually edits an inherited field **When** the edit is saved **Then** the "Inherited" marker is removed from that field and it is treated as a manually entered value going forward **Given** a municipality has multiple prior cycles **When** applying defaults **Then** the user can select which prior cycle to use as the source from a list ordered most-recent first **Given** a municipality has no prior cycles **When** the defaults action is viewed **Then** the option is disabled with a clear "No prior cycle available" message — no error state is triggered --- ### Story 2.5: Election-Cycle Readiness Status & Publication As a client services staff member, I want to see required-field readiness status for my election-cycle job and publish it to production readiness through a pre-commit validation rail, So that I can confirm the plan is complete and provide the team with a verified production-ready job. **Acceptance Criteria:** **Given** an election-cycle job has missing required fields (as defined by seeded rules from Story 1.9) **When** a client services user views readiness status **Then** each missing required field is identified by name with a jump-link that navigates directly to the field **Given** all required fields are complete **When** readiness status is evaluated **Then** the job shows a "Ready to Publish" state and the Publish action becomes enabled **Given** a user initiates the Publish action **When** the SafeCommitRail activates **Then** it runs dependency and policy checks and displays: overall pass/fail status, a list of any blocking reasons with one-click corrective action links, and a reason-code selector where audit policy requires it (UX-DR4) **Given** the SafeCommitRail shows all checks passed **When** the user confirms the publish with a required reason code **Then** the job status transitions to "Production Ready" and actor, timestamp, and reason code are captured in the audit log **Given** the SafeCommitRail shows one or more blocking issues **When** displayed **Then** the Publish confirm button remains disabled until all blocking issues are resolved **Given** the publish action succeeds **When** the confirmation is shown **Then** a "Next Best Action" prompt guides the user to the recommended following step (e.g., configure services) (UX-DR11) **And** the SafeCommitRail is built as a reusable shared module — it is independently consumable by Epic 3 service configuration commits and Epic 5 production status commits without duplication or modification --- ### Story 2.6: Spreadsheet Import & Column Mapping As a client services or operations user, I want to import approved election-cycle spreadsheet data and map it to the extension model, So that key schedule and service fields can be staged and reviewed without manual re-entry. **Acceptance Criteria:** **Given** an approved import file is provided **When** the import workflow starts **Then** the system parses required columns and validates expected template headers before staging data **Given** staged rows include jurisdiction references **When** mapping runs **Then** each row is matched to legacy-linked identifiers (`ID`, `JCode`/`JurisCode`, `KitID` where applicable) or flagged as unresolved **Given** a staged row fails validation **When** the review screen is displayed **Then** deterministic error output identifies the row, failing field, and corrective action needed before publish **Given** a staged row passes validation **When** it is included in publish **Then** provenance metadata is recorded: source file identifier, source row reference, import timestamp, and importing user --- ## Epic 3: Service Configuration Client services staff can configure all election services on a job — addressing, envelopes, sorting, office copies, and transportation events — with inline editing in the operational workspace grid and SafeCommitRail validation on commits. ### Story 3.1: Addressing Service Configuration As a client services staff member, I want to configure addressing service for an election-cycle job including quantities and dependency fields, So that the addressing workflow has all required production parameters before execution begins. **Acceptance Criteria:** **Given** an election-cycle job exists **When** a client services user opens the addressing service section **Then** they can enter addressing quantity, addressing type, dependency fields, and addressing-specific notes **Given** an addressing field is edited inline in the workspace grid **When** the cell is activated **Then** it enters edit mode with explicit commit and cancel controls visible and keyboard-accessible (UX-DR3) **Given** addressing configuration is ready to commit **When** the user saves via the SafeCommitRail **Then** dependency and policy checks run, the configuration is stored against the election-cycle job on pass, and actor and timestamp are captured in the audit log **Given** the MunicipalityCycleWorkspaceGrid renders a service configuration row **When** displayed **Then** a status chip shows current configuration completeness state (e.g., incomplete, configured, confirmed) **And** the MunicipalityCycleWorkspaceGrid is built as a reusable shared component with: sticky column header, column visibility controls, inline editable cells, row status chips, and provenance quick-view trigger — consumable across all service configuration stories in this epic (UX-DR3) --- ### Story 3.2: Envelope Service Configuration — Purple & Blue As a client services staff member, I want to configure purple and blue envelope service options with quantities and scenario details, So that production teams know exactly what envelope materials and quantities to prepare for the election. **Acceptance Criteria:** **Given** an election-cycle job exists **When** a client services user opens envelope service configuration **Then** purple envelope and blue envelope scenarios are presented as independently configurable sections each with quantity and service-specific option fields **Given** both envelope types are configured **When** saved via SafeCommitRail **Then** purple and blue configurations are stored as separate service records against the election-cycle job **Given** a purple or blue envelope scenario is not applicable to this election **When** the user marks it as "Not Used" **Then** the service is recorded as Not Used, no quantity validation is required, and no downstream transportation events are generated for that envelope type **Given** envelope configuration is committed **When** confirmed **Then** actor, timestamp, and any reason code are captured in the audit log **Given** envelope proof workflow fields are tracked for the election-cycle job **When** the user updates proof status values **Then** `Proof Sent` and `Proof Approved` values are persisted with actor/timestamp provenance and surfaced to downstream reporting --- ### Story 3.3: Office-Copy Service Configuration As a client services staff member, I want to configure office-copy requirements and related production details for an election-cycle job, So that office copy preparation is tracked and visible alongside other election services. **Acceptance Criteria:** **Given** an election-cycle job exists **When** a client services user opens office-copy configuration **Then** they can enter office copy quantity, production details, and any special handling requirements **Given** office-copy configuration is saved **When** committed via SafeCommitRail **Then** the configuration is stored against the election-cycle job and appears in the service summary view **Given** office copies are not required for this election **When** the user marks the service as "Not Required" **Then** it is recorded as Not Required with no quantity validation triggered and the service is clearly labelled as excluded in the service summary **Given** office-copy configuration is committed **When** confirmed **Then** actor, timestamp, and reason code are captured in the audit log --- ### Story 3.4: Sorting Service Configuration As a client services staff member, I want to configure sorting service options including quantity, mail class, permit or meter designation, and daily-sort flags, So that the sort team has all parameters needed to plan and execute sorting operations. **Acceptance Criteria:** **Given** an election-cycle job exists **When** a client services user opens sorting service configuration **Then** they can enter: sort quantity, mail class, permit or meter designation, and a daily-sort flag toggle **Given** sorting configuration is saved via SafeCommitRail **When** committed **Then** all sort parameters are stored against the election-cycle job and the sort service appears as configured in the service summary **Given** the daily-sort flag is enabled **When** configuration is saved **Then** the sort service is marked for daily sort operations and this flag is visible in Epic 4 milestone scheduling for sort milestone generation **Given** sorting is not applicable to this election **When** the user marks it as "Not Used" **Then** sorting is excluded from service summary and no sort milestones are generated in Epic 4 **Given** sorting configuration is committed **When** confirmed **Then** actor, timestamp, and reason code are captured in the audit log **Given** sorting configuration includes permit/meter and mail-date-sensitive settings **When** the user commits the configuration **Then** deterministic validation checks ensure daily-sort and mail-date dependencies are coherent before save is allowed --- ### Story 3.5: Transportation Service Events As a client services staff member, I want to record transportation-relevant service events tied to configured election services, So that transportation coordinators have accurate event data to build dispatch plans in Epic 5. **Acceptance Criteria:** **Given** at least one service is configured on an election-cycle job (addressing, envelopes, sorting, or office copies) **When** a client services user opens transportation events **Then** they can record pickup and delivery events linked to each configured service type **Given** a transportation event is created **When** saved **Then** it stores: event type (pickup or delivery), service reference, expected date, quantity, and pickup/delivery location **Given** a transportation event is linked to a service configuration **When** that service configuration is subsequently updated **Then** related transportation events are flagged as "Needs Review" rather than silently deleted or auto-updated **Given** multiple services are configured **When** the transportation events list is viewed **Then** events from all service types are consolidated in a single list sortable by date and filterable by service type **Given** a transportation event is saved **When** confirmed **Then** actor and timestamp are captured in the audit log and the event is available to Epic 5 report generation --- ## Epic 4: Milestone Scheduling & Timeline Management Operations teams can schedule and update milestone dates across all configured services, view timelines by municipality and date, detect missing or conflicting milestones before cutoffs, and assign or reassign ownership — with a persistent risk queue surfacing deadline pressure in real time. ### Story 4.1: Schedule & Update Milestone Dates As an operations user, I want to schedule and update milestone dates for all configured service events on an election-cycle job, So that each service has a date-driven execution plan tied to the election timeline. **Acceptance Criteria:** **Given** an election-cycle job has configured services **When** an operations user opens milestone scheduling **Then** milestone date fields are presented for each active service type (addressing, envelopes, sorting, office copies, transportation events) **Given** a service was marked "Not Used" in Epic 3 **When** milestones are displayed **Then** that service's milestone fields are excluded from the scheduling view entirely **Given** the daily-sort flag is set on a sorting service (Story 3.4) **When** sort milestones are generated **Then** daily sort milestone entries are created for each day within the configured sort date range **Given** a milestone date is entered and saved **When** the job is reloaded **Then** the date persists and is visible in the milestone timeline view **Given** a milestone date is updated **When** saved **Then** the change is captured in the audit log with actor identity and timestamp --- ### Story 4.2: Milestone Timeline Views & CutoffRiskQueuePanel As an operations user, I want to view milestone timelines by municipality and by date across active election-cycle jobs with at-risk and overdue items surfaced in a persistent right-pane risk queue, So that I can see the full operational schedule and identify where urgent attention is needed before cutoffs. **Acceptance Criteria:** **Given** active election-cycle jobs have scheduled milestones **When** an operations user opens the timeline view **Then** milestones are displayed organized by municipality with date-sorted order and filterable by date range, county, and municipality **Given** the timeline view is filtered **When** two users apply identical filters **Then** both see the same deterministic sort order — no per-user result variation (UX-DR13) **Given** multiple municipalities share milestones on the same date **When** viewed in date mode **Then** milestones are grouped by date then sorted by county then municipality name **Given** the CutoffRiskQueuePanel renders in the right pane **When** it loads **Then** it displays prioritized urgency cards each showing: milestone name, job reference, due date countdown, severity badge (on-track/at-risk/overdue/critical), and assigned owner name (UX-DR5) **Given** a milestone transitions to at-risk or overdue status **When** the queue re-evaluates **Then** it reorders with the most urgent items at the top and each severity badge is accompanied by a text label — never color alone (UX-DR12) **Given** a user selects a card in the risk queue **When** clicked or activated via keyboard **Then** the center pane navigates to the relevant job and milestone without losing queue context **And** the CutoffRiskQueuePanel is built as a reusable shared component — consumable by Epic 5 production tracking without modification --- ### Story 4.3: Missing & Conflicting Milestone Detection As an operations user, I want the system to detect missing required milestones and logically conflicting date sequences before execution cutoffs, So that I can resolve gaps before they block production operations. **Acceptance Criteria:** **Given** an election-cycle job has required milestones not yet scheduled **When** an operations user views the job or runs detection **Then** each missing required milestone is identified by name with its service context displayed **Given** two milestone dates create a logical conflict (e.g., delivery date before pickup date, mail date before sort date) **When** the conflict is detected **Then** both conflicting milestones are flagged with a plain-language explanation identifying the conflict type and the affected dates **Given** a missing or conflicting milestone is flagged **When** the flag is displayed **Then** a one-click jump link navigates directly to the scheduling field for that specific milestone **Given** all required milestones are scheduled without conflicts **When** the job is evaluated **Then** it displays a "Milestones Complete" status and this clears any related items from the CutoffRiskQueuePanel **Given** a user resolves flagged issues and re-evaluates **When** detection re-runs **Then** resolved items are cleared from the detection list and a Next Best Action prompt guides the user to the recommended following step (UX-DR11) --- ### Story 4.4: Milestone Ownership & Reassignment As an operations user, I want to assign and reassign milestone ownership and due dates across teams, So that each milestone has a clear accountable owner responsible for its timely completion. **Acceptance Criteria:** **Given** a milestone exists on an election-cycle job **When** an operations user opens milestone details **Then** they can assign an owner from the list of active application users and set or update the milestone due date **Given** a milestone is reassigned to a new owner **When** saved **Then** the previous owner, new owner, reassigning actor identity, and timestamp are all captured in the audit log **Given** a milestone due date is updated **When** saved **Then** the CutoffRiskQueuePanel re-evaluates the milestone's urgency and reorders the queue to reflect the new due date **Given** a user is assigned as milestone owner **When** they access their role workspace **Then** their assigned milestones appear in their workspace prioritized by due date with severity indicators **Given** a milestone is reassigned to a new owner **When** they next view their workspace **Then** newly assigned milestones are visually distinguished (e.g., "New Assignment" badge) until acknowledged --- ## Epic 5: Production Tracking, Exception Management, Audit History & Transportation Report Production teams can update job statuses, log and resolve exceptions, view complete timestamped status history, and see current and historical process state. Transportation coordinators receive the first external-facing report — a date-sorted dispatch report — as an early municipality proof point. ### Story 5.1: Production Status Updates at Checkpoints As a production user, I want to update election-cycle job status at defined process checkpoints, So that the team has an accurate current view of where each job stands in the production workflow. **Acceptance Criteria:** **Given** an election-cycle job is in production **When** a production user opens the status panel **Then** available status transitions from the current checkpoint are presented (e.g., In Setup → Configured → Scheduled → In Production → Complete) — only valid next states are selectable **Given** a status update is initiated **When** the SafeCommitRail activates (reused from Story 2.5) **Then** it validates the transition is permitted from the current status and all required fields for the new status are present before enabling the commit action **Given** a valid status transition passes all SafeCommitRail checks **When** the user confirms with a reason code **Then** the new status is saved with actor identity, timestamp, and reason code captured in the audit log **Given** an invalid status transition is attempted (e.g., skipping a required checkpoint) **When** the SafeCommitRail evaluates **Then** the commit is blocked with a plain-language explanation identifying the constraint and the required preceding step **Given** a status update is committed **When** confirmed **Then** the change is immediately reflected in the CutoffRiskQueuePanel and milestone timeline views --- ### Story 5.2: Exception & Blocker Logging As a support or operations user, I want to log exceptions, blockers, and resolution notes against an election-cycle job, So that issues are tracked with accountability and visible to all relevant team members without leaving the operations workspace. **Acceptance Criteria:** **Given** an election-cycle job exists **When** a user creates an exception record **Then** they can enter: exception type, description, blocking impact, assigned owner, and due-by timestamp **Given** an exception is logged with blocking impact **When** saved **Then** it appears in the CutoffRiskQueuePanel with a severity badge matching its impact level (reused from Epic 4, UX-DR5) **Given** an open exception passes its due-by timestamp without resolution **When** the CutoffRiskQueuePanel evaluates urgency **Then** the exception's severity escalates automatically and the updated priority is reflected immediately in the queue ordering **Given** an exception is resolved **When** the resolution note is saved **Then** the exception status transitions to Resolved with resolution note, resolving actor identity, and timestamp captured in the audit log **And** exception records are available as input to the BlockerResolutionDrawer in Story 5.3 for in-context resolution workflows --- ### Story 5.3: Blocked-Job Resolution with BlockerResolutionDrawer As a production or operations user, I want to resolve blocked election-cycle jobs through an in-context recovery drawer without losing my current workspace position, So that I can address blockers quickly and maintain operational momentum under deadline pressure. **Acceptance Criteria:** **Given** a blocked job or open exception exists **When** a user opens the BlockerResolutionDrawer from the risk queue, a blocked status indicator, or a validation failure **Then** a right-side drawer opens in context — the user's current workspace view remains visible and no navigation occurs **Given** the BlockerResolutionDrawer is open **When** displayed **Then** it shows: blocker details, dependency links to affected milestones, assignee field, due-by date control, resolution note field, and save and escalate actions (UX-DR6) **Given** a user updates assignment and due-by date and saves **When** committed **Then** the exception history is updated with the new values, actor identity, and timestamp, and the drawer closes returning keyboard focus to the previous context **Given** a blocker cannot be resolved in the available time **When** the user selects Escalate **Then** the exception's severity escalates in the CutoffRiskQueuePanel and the escalation event is appended to the exception audit trail **Given** the drawer has unsaved changes **When** the user attempts to close it **Then** a confirmation prompt prevents accidental data loss before dismissal **And** the BlockerResolutionDrawer is built as a reusable shared component with standard and escalation mode variants — consumable across all operational views without modification (UX-DR6) --- ### Story 5.4: Status Transition History & ProvenanceTimelinePanel As any operational user, I want to view a complete timestamped history of status transitions and key operational events on an election-cycle job, So that I can trace what changed, when, by whom, and why at any point in the job lifecycle. **Acceptance Criteria:** **Given** any status transition or key operational event occurs on an election-cycle job **When** committed **Then** previous value, new value, actor identity, timestamp, event type, and reason code are appended to the immutable event history **Given** the ProvenanceTimelinePanel is opened for a job **When** loaded **Then** it displays a chronological timeline of events each showing: actor, timestamp, event type, changed field, previous value, new value, and reason code (UX-DR8) **Given** a user filters the timeline by event type (status change, exception logged, milestone updated) **When** the filter is applied **Then** the timeline updates deterministically showing only matching events in the same chronological order **Given** the timeline renders event type indicators **When** displayed **Then** each event type is conveyed with both a text label and an icon — never color alone (UX-DR12) **And** the ProvenanceTimelinePanel is built as a reusable shared component with compact inline preview and full audit view variants — consumable by Epic 6 traceability features without modification (UX-DR8) --- ### Story 5.5: Current & Historical Process State View As any operational user, I want to view current and historical process state for a municipality's election-cycle jobs, So that I can understand where each job stands today and how it progressed through the full production lifecycle. **Acceptance Criteria:** **Given** a municipality has one or more election-cycle jobs **When** a user opens the process state view **Then** the current job displays: status, milestone completion summary, count of open exceptions, and last-updated timestamp **Given** the current job has open blockers **When** the process state view is displayed **Then** a BlockerResolutionDrawer quick-launch action is available directly from the view for immediate in-context resolution (reused from Story 5.3) **Given** prior completed election-cycle jobs exist for the municipality **When** the user accesses job history **Then** historical jobs are listed with final status, completion date, and a link to their full ProvenanceTimelinePanel audit view **Given** a user navigates to a historical job's provenance timeline **When** the timeline loads **Then** the complete event history for that cycle is displayed in chronological order with all actor, timestamp, and reason code data intact --- ### Story 5.6: Transportation Report — Date-Sorted Dispatch As a transportation coordinator, I want to generate a date-sorted transportation report grouped by county and municipality, So that I can prepare accurate next-day and week-lookahead dispatch runs tied directly to election job milestones. **Acceptance Criteria:** **Given** active election-cycle jobs have transportation events and milestones scheduled **When** a transportation coordinator generates the report **Then** pickup and delivery events are aggregated from extension records joined to legacy identifiers and sorted by date then county then municipality (FR22) **Given** the report is generated **When** displayed **Then** each row shows: event date, county, municipality name, event type (pickup or delivery), service type, quantity, and location **Given** a filter is applied (date range, county, municipality, or service status) **When** the report refreshes **Then** only matching events are shown in the same deterministic sort order for all users applying identical filters (UX-DR13) **Given** a report row has missing critical event data **When** detected during generation **Then** the row is flagged with a data-gap indicator and a direct deep-link to the source job record for correction **Given** the report is ready for dispatch planning **When** the user exports **Then** a spreadsheet-compatible file (CSV or XLSX-equivalent) is generated preserving all visible columns and sort order (FR25) **And** report generation completes in 30 seconds or less for standard daily and weekly date range filters under normal operating load (NFR3) --- ## Epic 6: Full Reporting Suite, Admin Management UIs & Governance All remaining operational reports with deterministic filtering and export. Admin management UIs complete the FR28/FR29/FR30 configuration layer from seed-only to fully managed. Support analysts get report-to-source traceability and data correction tools. Parity governance closes the brownfield migration with a defined sunset gate. ### Story 6.1: Sorting Report by Municipality As an operations user, I want to generate a sorting report organized by municipality for all election-cycle jobs with sorting enabled, So that the sort team can review all sorting requirements per municipality in a single operational view. **Acceptance Criteria:** **Given** active election-cycle jobs have sorting services configured and enabled **When** a user generates the sorting report by municipality **Then** all jobs with sorting enabled are aggregated and displayed grouped by municipality name **Given** the report is generated **When** displayed **Then** each row shows: municipality name, jurisdiction code, sort quantity, mail class, permit or meter designation, daily-sort flag, and current job status **Given** filters are applied (date range, county, service status) **When** the report refreshes **Then** only matching jobs are shown in deterministic municipality-sorted order **Given** the report is exported **When** downloaded **Then** a spreadsheet-compatible file is generated preserving all visible columns, sort order, and active filter context (FR25) **And** report generation completes in 30 seconds or less for standard filter ranges (NFR3) --- ### Story 6.2: Sorting Report by Mail Date As an operations user, I want to generate a sorting report organized chronologically by mail date for all election-cycle jobs with sorting enabled, So that the sort team can plan operations across all active municipalities in date-priority order. **Acceptance Criteria:** **Given** active election-cycle jobs have sorting enabled and mail dates scheduled **When** a user generates the sorting report by mail date **Then** jobs are displayed sorted by mail date ascending then by municipality name within each date group **Given** the report is generated **When** displayed **Then** each row shows: mail date, municipality name, sort quantity, mail class, permit or meter designation, and daily-sort flag **Given** multiple municipalities share the same mail date **When** grouped **Then** they appear together under the shared date header in municipality-sorted order **Given** the report is exported **When** downloaded **Then** a spreadsheet-compatible file is generated preserving all columns, grouping, and sort order (FR25) **And** report generation completes in 30 seconds or less for standard filter ranges (NFR3) --- ### Story 6.3: Report Filtering, Saved Presets & Unified Export As an operations user, I want to apply consistent filters across all operational reports and save presets for recurring use, So that I can reproduce the same report view instantly without re-entering filter criteria each session. **Acceptance Criteria:** **Given** any operational report is open (transportation, sorting by municipality, sorting by mail date) **When** a user applies filters (date range, municipality, county, service status) **Then** only matching records are shown in deterministic sort order identical for all users with the same active filters (UX-DR13, FR26) **Given** a user configures a useful filter set **When** they save it as a named preset **Then** the preset is stored per user role and available from a preset selector dropdown in future sessions **Given** a saved preset is selected **When** applied **Then** all filter values are restored exactly and the report regenerates with the same deterministic output **Given** any report is ready for export **When** the user triggers export **Then** a spreadsheet-compatible file (CSV or XLSX-equivalent) is generated preserving column labels, sort order, grouping, and all applied filter values (FR25) **And** exported files preserve required field parity for existing operational consumers — column names and data types match the legacy report output schema where mandated (NFR14) --- ### Story 6.4: Report-to-Source Traceability As a support analyst, I want to trace any report value back to its source records across legacy and extension datasets, So that I can investigate and resolve report discrepancies with full visibility into how the value was produced. **Acceptance Criteria:** **Given** a report row or field value is under investigation **When** a support analyst activates the trace action for that row **Then** the system displays the source extension record(s) and legacy join record(s) that produced the value, including join path and field-level mapping (FR33) **Given** the trace is opened **When** displayed using the ProvenanceTimelinePanel (reused from Story 5.4) **Then** source record identifiers, field values, last-updated actor, timestamp, and join keys are all visible in the audit view **Given** a discrepancy exists between a report output value and its source record **When** the trace is reviewed **Then** the analyst can identify the exact field, record, and value mismatch causing the discrepancy **Given** a trace is run on any operational report **When** the result is shown **Then** traceability is available for transportation reports and all sorting reports — not limited to a single report type **Given** a traced value originated from spreadsheet import **When** trace details are expanded **Then** source file identifier, source row reference, import timestamp, and importing user are visible in the provenance panel --- ### Story 6.5: Extension-Layer Data Correction As an authorized support or admin user, I want to correct erroneous extension-layer data with a mandatory reason and full audit trail, So that data issues can be resolved with accountability while preserving immutable legacy schema integrity. **Acceptance Criteria:** **Given** an extension record contains erroneous data **When** an authorized user (Support or Admin role) opens the correction workflow **Then** they can edit the extension record fields with a mandatory correction reason field that must be completed before saving **Given** a correction is submitted **When** saved **Then** the corrected values, previous values, correction reason, actor identity, and timestamp are all captured in the audit log as an immutable correction event (FR34) **Given** the correction workflow displays a record that joins to legacy data **When** legacy fields are shown **Then** they are rendered read-only with no edit affordance — no modification path to legacy tables exists in this workflow (NFR12) **Given** a user without the Support or Admin role attempts to access the correction workflow **When** they navigate to it **Then** they receive 403 Forbidden and the unauthorized access attempt is captured in the audit log (NFR6) **Given** a correction saves successfully **When** the corrected record is used in a subsequent report **Then** the report reflects the corrected value and the correction event is visible via report-to-source traceability (Story 6.4) --- ### Story 6.6: Admin Configuration UIs — Reference Values, Required-Field Rules & Escalation Rules As an administrator, I want management interfaces for extension-layer reference values, required-field rules, and escalation rule configurations, So that operational defaults established in Epic 1 can be updated as business needs evolve without requiring code changes. **Acceptance Criteria:** **Given** an administrator navigates to reference value management **When** the UI loads **Then** they can view, add, edit, and deactivate status sets, service template defaults, and other extension-layer reference values across all operational domains (FR28) **Given** a reference value is updated **When** saved **Then** the change is immediately available to all operational workflows and captured in the audit log with actor and timestamp **Given** an administrator navigates to required-field rules management **When** the UI loads **Then** they can view and modify which fields are required for election-cycle readiness checks and set effective dates for rule changes (FR29) **Given** an administrator navigates to escalation rule configuration **When** the UI loads **Then** they can configure notification triggers, escalation thresholds, due-date offsets, and recipient rules for missed or at-risk milestones (FR30) **Given** any admin configuration change is about to be committed **When** the SafeCommitRail activates (reused from Story 2.5) **Then** it validates the change does not break active workflows or produce orphaned records before enabling the save action --- ### Story 6.7: Parity Governance & Legacy Report Sunset As an operations administrator and parity governance owner, I want a structured parity validation workflow with a defined sunset checklist, So that the legacy report stack can be formally decommissioned only after proven equivalence is documented and signed off. **Acceptance Criteria:** **Given** modern reports are generating output alongside legacy reports **When** a parity validation run is initiated **Then** modern and legacy report outputs are compared field-by-field across a defined validation set of active election jobs and a comparison report is produced **Given** a parity comparison report is generated **When** discrepancies are found **Then** the report identifies: affected report type, field name, legacy value, modern value, discrepancy severity, and an assignable owner field for triage **Given** a discrepancy is assigned to an owner **When** the owner resolves it and updates closure status **Then** the resolution is recorded with actor identity, timestamp, and resolution notes in the parity audit trail **Given** parity validation shows zero critical discrepancies across the full validation set **When** reviewed by the named parity governance owner **Then** the sunset criteria checklist becomes available for formal sign-off review **Given** all sunset criteria are satisfied (zero critical discrepancies, named parity owner sign-off, municipality client acknowledgment) **When** the final gate is passed and recorded **Then** the legacy report stack is eligible for decommission and the gate completion event is captured with all sign-off actors and timestamps **And** the named parity governance owner is a required system configuration field — parity validation cannot be initiated until this field is populated --- ### Story 6.8: Source Reconciliation & Data Quality Queue As a support analyst or operations owner, I want a reconciliation queue for imported source data versus report output, So that data mismatches are triaged with clear ownership before they impact operational decisions. **Acceptance Criteria:** **Given** imported spreadsheet-origin records and report outputs exist **When** a reconciliation run is executed **Then** mismatches are surfaced with report type, affected field, source value, system value, and severity **Given** a mismatch is identified **When** triage begins **Then** an owner can be assigned with due date, status, and resolution notes tracked in an auditable trail **Given** a reconciliation issue is resolved **When** the resolution is saved **Then** closure metadata (actor, timestamp, reason) is captured and linked to report traceability records **Given** unresolved high-severity mismatches remain **When** operational export is attempted for affected scope **Then** the system displays a blocking warning with a direct link to the reconciliation queue