Define AggregationState BLoC contracts
epic-bufdir-data-aggregation-ui-task-001 — Define the BLoC state classes and events that both UI components will consume: AggregationInitial, AggregationInProgress (with current pipeline stage enum: fetching, categorising, deduplicating, distributing), AggregationWarning (deduplication anomaly count), AggregationFailed (error message, retry callback), and AggregationComplete (summary metrics payload). Establish the shared data models for pipeline stage tracking and summary metrics.
Acceptance Criteria
Technical Requirements
Implementation Notes
Place all BLoC contracts in lib/features/bufdir/aggregation/bloc/. Use the standard BLoC pattern: separate files for events (aggregation_event.dart), states (aggregation_state.dart), and the bloc itself (aggregation_bloc.dart), all re-exported from aggregation_bloc.dart. Use the freezed package if the project already uses it for union types — it eliminates boilerplate for sealed state hierarchies. If not, use abstract base classes with Equatable.
The AggregationSummaryData type referenced in AggregationComplete should be defined in task-004 but declare it as a placeholder class now (with a TODO comment) to unblock dependent tasks. Keep the onRetry callback in AggregationFailed typed as VoidCallback (from Flutter foundation) — do not use Function() to keep it testable with mock callbacks.
Testing Requirements
Unit tests using flutter_test. Test file: test/features/bufdir/aggregation/bloc/aggregation_state_test.dart. Test cases: (1) each state class instantiates without error; (2) two instances of the same state with identical fields are equal via Equatable; (3) two instances with different fields are not equal; (4) AggregationInProgress with stage=fetching is not equal to AggregationInProgress with stage=categorising; (5) AggregationFailed.onRetry is callable. Achieve 100% line coverage on all state and event classes.
For NHF with 1,400 local chapters, rendering a geographic breakdown as a flat list in the AggregationSummaryWidget would produce an unscrollable wall of data that is unusable on a mobile screen. Coordinators need to verify geographic completeness before export, but the raw chapter list is too long to review.
Mitigation & Contingency
Mitigation: Design the geographic distribution section as a collapsible two-level hierarchy: regions are shown expanded by default with their aggregate count, individual chapters are collapsed under each region and expandable on tap. Show only non-zero regions by default with a 'show empty regions' toggle.
Contingency: If the hierarchical display is too complex to implement before the submission deadline, fall back to a region-only summary view with a total count of active chapters per region and a note that chapter-level detail is available in the full export file.
The aggregation BLoC must handle a multi-stage async pipeline with partial success states, warning accumulation across stages, and retry logic for individual failed stages. If the state model is too simplistic (e.g., loading/success/error), warning states and partial results will be lost on retry, confusing coordinators who see warnings disappear and reappear.
Mitigation & Contingency
Mitigation: Model the BLoC state as a rich sealed class hierarchy: AggregationIdle, AggregationRunning(stage, completedStages, accumulatedWarnings), AggregationComplete(result, warnings), AggregationFailed(failedStage, accumulatedWarnings, error). Accumulated warnings persist across retry attempts so coordinators see the full warning history.
Contingency: If state management complexity causes repeated bugs near the deadline, simplify to a single AggregationResult value object that captures success, warnings, and error as nullable fields, and drive both UI components from that single object rather than a live BLoC stream.