medium priority medium complexity integration pending frontend specialist Tier 3

Acceptance Criteria

HierarchyStructureValidator exposes a public validate(operation: HierarchyOperation) method that can be called directly from the admin portal BLoC/Cubit layer without routing through HierarchyService
validate() returns a structured ValidationResult object with fields: isValid (bool), errors (List<ValidationError>), warnings (List<ValidationWarning>), and affectedNodeCount (int)
ValidationError objects include errorCode (String), affectedNodeId (String), humanReadableMessage (String), and suggestedFix (String?)
Admin portal UI displays inline error banners above the submit button when ValidationResult.isValid == false, listing all errors in a numbered list
Validation runs on debounced input (300ms delay) so the user sees errors as they configure a reparent operation without waiting for form submission
ValidationResult is returned within 200ms for subtrees of up to 200 nodes
The submit button is disabled (not just visually greyed) when any ValidationError is present; warnings allow submission with a confirmation dialog
Circular hierarchy detection error surfaces as a distinct error code CIRCULAR_REFERENCE with a clear human-readable explanation
NHF-specific constraint: reparenting a node that would place a member in more than 5 chapters triggers a MAX_CHAPTER_MEMBERSHIP_EXCEEDED warning (not error) with count of affected members
Unit tests cover: valid reparent, circular reference, max-depth exceeded, cross-organisation reparent attempt, and NHF multi-chapter warning scenario

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
Supabase PostgreSQL 15 (read-only hierarchy queries for validation)
Supabase Edge Functions (optional: server-side subtree depth check)
data models
assignment
contact_chapter
contact
performance requirements
validate() must complete within 200ms for subtrees up to 200 nodes (local validation logic only)
Remote subtree-depth queries must be cached per session to avoid repeated Supabase round-trips during debounced validation
Debounce interval of 300ms on UI input changes before triggering validate()
security requirements
Validator must not accept node IDs from outside the authenticated user's organisation scope — verify against JWT unit_ids claim
ValidationResult must never include data about nodes the user cannot read; filter error messages to omit names of out-of-scope nodes
All Supabase queries inside validator must run under the user's JWT (not service role) so RLS enforces data access automatically
ui components
ValidationErrorBanner widget (inline, dismissible per session, red accent)
ValidationWarningBanner widget (inline, yellow accent, with 'Proceed anyway' CTA)
SubmitButton with disabled state driven by ValidationResult.isValid
ConfirmationDialog for proceeding despite warnings

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Inject HierarchyStructureValidator as a dependency into the admin portal's ReparentNodeCubit (or equivalent BLoC) rather than calling it from the widget tree directly — this keeps validation logic testable without Flutter widget setup. Use a StreamTransformer with debounceTime(Duration(milliseconds: 300)) on the reparent-target input stream before piping to validate(). For the NHF multi-chapter check, query contact_chapter grouped by contact_id filtered to peer mentors in the affected subtree; this is the most expensive check and should be guarded behind a feature flag per organisation. Return ValidationResult as an immutable value object (use freezed or manual const constructor) so BLoC states can be compared with == for efficient rebuilds.

Do not throw exceptions from validate() — always return a ValidationResult, even for internal errors, so the UI layer has a single response type to handle.

Testing Requirements

Unit tests (flutter_test): validate() with valid reparent returns isValid=true and empty errors list; circular reference detection returns CIRCULAR_REFERENCE error code; max-depth exceeded returns MAX_DEPTH_EXCEEDED; cross-organisation reparent returns CROSS_ORGANISATION_VIOLATION. Widget tests: inline error banner renders when BLoC state contains ValidationResult with errors; submit button is disabled in error state; warning banner renders 'Proceed anyway' button; debounce prevents validate() being called more than once per 300ms burst. Integration test: full reparent flow triggers validation, user sees error, corrects input, error clears, submit becomes enabled. Target 90% line coverage on HierarchyStructureValidator class.

Component
Hierarchy Structure Validator
infrastructure medium
Epic Risks (4)
high impact medium prob security

Injecting all unit assignment IDs into JWT claims for users assigned to many units (up to 5 for NHF peer mentors, many more for national coordinators) may exceed JWT size limits, causing authentication failures.

Mitigation & Contingency

Mitigation: Store unit IDs in a Supabase session variable or a dedicated Postgres function rather than embedding them directly in the JWT payload. Use set_config('app.unit_ids', ...) within RLS helper functions querying the assignments table at policy evaluation time.

Contingency: Fall back to querying the unit_assignments table directly within RLS policies using the authenticated user ID, accepting a small per-query overhead in exchange for removing the JWT size constraint.

medium impact medium prob technical

Rendering 1,400+ nodes in a recursive Flutter tree widget may cause jank or memory pressure on lower-end devices used by field peer mentors, degrading the admin experience.

Mitigation & Contingency

Mitigation: Implement lazy tree expansion — only the root level is rendered on initial load. Child nodes are rendered on demand when the parent is expanded. Use const constructors and ListView.builder for all node lists to minimize rebuild scope.

Contingency: Add a search/filter bar that scopes the visible tree to matching nodes, reducing the visible node count. Provide a 'flat list' fallback view for administrators who prefer searching over browsing the tree.

medium impact medium prob scope

Requirements for what constitutes a valid hierarchy structure may expand during NHF sign-off (e.g., mandatory coordinator assignments per chapter, minimum member counts per region), requiring repeated validator redesign.

Mitigation & Contingency

Mitigation: Design the validator as a pluggable rule engine where each check is a discrete, independently testable function. New rules can be added without changing the core validation orchestration. Surface all rules in a configuration table per organization.

Contingency: Defer non-blocking validation rules to warning-level feedback rather than hard blocks, allowing structural changes to proceed while flagging potential issues for admin review.

high impact low prob integration

Deploying RLS policy migrations to a shared Supabase project used by multiple organizations simultaneously could lock tables or interrupt active sessions, causing downtime during production migration.

Mitigation & Contingency

Mitigation: Write all RLS policies as CREATE POLICY IF NOT EXISTS statements. Schedule migrations during off-peak hours. Use Supabase's migration preview environment to validate policies against production data shapes before applying.

Contingency: Prepare rollback migration scripts for every RLS policy. If a migration causes issues, execute the rollback immediately and re-test the policy logic in staging before reattempting.