critical priority medium complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

PeriodSelectionBloc class is defined and extends Bloc<PeriodSelectionEvent, PeriodSelectionState>
All 4 state classes are defined: PeriodSelectionInitial, PeriodSelectionLoading, PeriodSelectionLoaded, PeriodSelectionError — each extends PeriodSelectionState and uses Equatable
All 5 event classes are defined: LoadPresets, SelectPreset, ChangeCustomRange, FetchRecordCount, ConfirmPeriod — each extends PeriodSelectionEvent and uses Equatable
PeriodSelectionLoaded carries a List<PeriodPreset> presets, PeriodPreset? selectedPreset, DateTimeRange? customRange, int? recordCount, bool isCountLoading, bool showIncompleteWarning
PeriodSelectionError carries a String errorMessage and optionally the previous loaded state for partial recovery
All 5 event handlers are registered in the constructor via on<EventType>(handler)
Constructor accepts injected PeriodPresetService, PeriodRecordCountService, and ReportPeriodValidator — all as required named parameters
Each event handler stub emits at minimum the correct state type (e.g., LoadPresets emits PeriodSelectionLoading)
Bloc compiles without errors and passes flutter analyze with zero warnings
Unit test file exists with a test group covering state equality and event equality using Equatable

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
Riverpod
Equatable
data models
PeriodPreset
DateTimeRange
PeriodSelectionState
PeriodSelectionEvent
performance requirements
Bloc instantiation must complete in under 5ms
State transitions must not hold references that prevent GC of large payload objects
security requirements
No sensitive date-range or record-count data logged outside of verbose/debug mode
Service dependencies injected — no static singletons that could leak state between test runs

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Integration Task

Handles integration between different epics or system components. Requires coordination across multiple development streams.

Implementation Notes

Use sealed classes or abstract base with Equatable for states and events — this enables exhaustive pattern matching in BlocBuilder/BlocListener and prevents missed states. Follow the standard BLoC folder convention: period_selection_bloc.dart, period_selection_event.dart, period_selection_state.dart in lib/features/bufdir_report/bloc/. Inject services via constructor — never use GetIt or static providers inside the bloc itself; the Riverpod provider that creates this bloc will supply dependencies. Keep event handler method bodies empty stubs returning early (emit nothing or emit initial) until subsequent tasks fill them in — this allows the file to compile and be tested immediately.

Use copyWith on PeriodSelectionLoaded to make incremental state updates readable and safe.

Testing Requirements

Unit tests using flutter_test and bloc_test package. Test groups: (1) state equality — two instances of PeriodSelectionLoaded with identical fields must be equal via Equatable; (2) event equality — same for all event classes; (3) bloc instantiation — confirm bloc is created without throwing when mock services are injected; (4) initial state — verify bloc.state is PeriodSelectionInitial on creation. Minimum 90% line coverage on the bloc file. No widget or integration tests needed at this stage.

Component
Period Selection BLoC
infrastructure low
Epic Risks (3)
medium impact medium prob technical

The record count query is asynchronous and may take up to 1 second on a slow connection. If the BLoC does not manage loading states carefully, the UI may show a stale count or the confirm button may be briefly enabled during the loading transition, allowing premature submission.

Mitigation & Contingency

Mitigation: Define explicit BLoC states: PeriodSelectionLoading, PeriodSelectionCountLoaded, PeriodSelectionValidationError, and PeriodSelectionReady. The confirm button must only be enabled in PeriodSelectionReady. Add a debounce of 300ms on period-change events before triggering the count query to prevent excessive calls.

Contingency: If debouncing is insufficient to prevent UX degradation on slow connections, add an optimistic loading skeleton to the RecordCountBanner that clearly communicates a pending state, and keep the confirm button disabled until the count resolves.

high impact low prob integration

The confirmedReportPeriodProvider Riverpod contract between this feature and the Bufdir Export feature may not be defined yet, causing integration failures when the downstream export screen attempts to read the provider.

Mitigation & Contingency

Mitigation: Define the confirmedReportPeriodProvider as a StateProvider<DateTimeRange?> in a shared providers file at the start of this epic, before any screen code is written. Communicate the provider contract to the Bufdir Export feature team so both sides align on the same provider reference.

Contingency: If the export feature consumes the period through a different mechanism (e.g., navigation arguments), add a thin adapter that writes the confirmed period to both the Riverpod provider and the navigation argument, ensuring backward compatibility.

medium impact medium prob technical

Flutter's live region support for screen readers is inconsistent across platforms (iOS VoiceOver vs Android TalkBack), and the record count banner must be announced on every change — a pattern that has historically required platform-channel workarounds.

Mitigation & Contingency

Mitigation: Test the live region announcement on both iOS (VoiceOver) and Android (TalkBack) early in implementation using the accessibility_test harness. Reference the project's existing live-region-announcer component (664-accessibility-live-region-announcer) for a proven implementation pattern.

Contingency: If native live region support is insufficient, wrap the record count text in a Semantics widget with a programmatically updated label string, and trigger a SemanticsService.announce call on each count update as a platform-agnostic fallback.