HOCO B2B
Team tasksImad

MOB-02 — Core package — HTTP client, interceptors, errors, storage, logging

hoco_core: flavor config, dio with auth/locale/app-version/Idempotency-Key interceptors, error-envelope mapping, secure storage, connectivity, Sentry.

FieldDetails
Task codeMOB-02
PhaseP0 — Foundations (cadrage, UX/UI, architecture & contract)
Product scopeV1
StatusPlanned
StartMon 19 Oct 2026
DueMon 19 Oct 2026
ResponsibleImad — Mobile developer (Flutter)
Co-responsible—
Effort1 person-day(s) planned · 4 from scratch · size S
Area · appMobile foundation · mobile
Depends onMOB-01
UnblocksMOB-03
Milestones—

Summary

hoco_core: flavor config, dio with auth/locale/app-version/Idempotency-Key interceptors, error-envelope mapping, secure storage, connectivity, Sentry.

Description

Every feature in both apps must send the same headers, authenticate the same way and turn the server's error envelope into the same typed failures, so this logic lives once in hoco_core. Scope: flavor config, a dio factory with interceptors for Authorization: Bearer, Accept-Language, X-App-Version, X-App-Platform, X-Device-Id and Idempotency-Key, mapping of { error: { code, message, details, requestId } } to a sealed ApiFailure, secure session storage, connectivity, logging and Sentry. Feature repositories and generated clients are out of scope (MOB-03). It needs the error-code registry and auth contract from Anis (SRV-03, SRV-05); nothing from Akram. MOB-03 plugs the generated client onto this dio instance, MOB-05 listens to its 426/503 streams, and MOB-09 reuses its idempotency helpers for the outbox.

Subtasks

#SubtaskOwner
1FlavorConfig and Env (apiBaseUrl, sentryDsn, flavor, contract version) loaded from dart-define, exposed as a Riverpod providerImad
2HocoDio factory: base URL …/v1, connect 10 s / receive 30 s timeouts, retry only for GETs and for mutations that carry an Idempotency-Key (3 attempts, exponential backoff, on network errors, 502, 503, 504)Imad
3ClientHeadersInterceptor: Accept-Language (fr default, ar, en), X-App-Version from package_info_plus, X-App-Platform android|ios, X-Device-Id (UUIDv7 generated once and kept in secure storage)Imad
4SessionStore on flutter_secure_storage (sessionToken, expiresAt, userType) and AuthInterceptor adding the bearer token; 401 UNAUTHENTICATED clears the store and emits AuthEvent.signedOutImad
5Idempotency helpers: UUIDv7 generator and a request option that pins one key per user action so retries reuse it; Idempotent-Replayed: true treated as successImad
6Sealed ApiFailure (network, timeout, unauthenticated, forbidden(code), notFound, conflict(code), businessRule(code, details), validation(fieldErrors), rateLimited(retryAfter), upgradeRequired(storeUrl), maintenance(until), server) mapped from the error envelope with requestId keptImad
7App-wide event streams for 426 UPGRADE_REQUIRED and 503 MAINTENANCE consumed by the router (MOB-05)Imad
8ConnectivityService: connectivity_plus plus a reachability probe on GET /app-config, exposed as a StreamProvider used by offline banners and the outboxImad
9Sentry (sentry_flutter + sentry_dio): one project per app, environment = flavor, release = version+build, requestId tag, beforeSend scrubbing Authorization, tokens, phone numbers and OTPsImad
10Logger with redaction; verbose HTTP logging only in the dev flavorImad

Deliverables

  • packages/hoco_core/lib/src/config/, http/, errors/, storage/, connectivity/, observability/
  • packages/hoco_core/test/ with interceptor and error-mapping tests
  • Sentry projects hoco-reseller and hoco-delegate with dev/staging/prod environments

Acceptance criteria

  • Every request carries Accept-Language, X-App-Version, X-App-Platform and X-Device-Id; authenticated requests carry the bearer token (AUTH-06)
  • X-Device-Id stays the same across restarts and changes only after reinstall
  • A 401 UNAUTHENTICATED clears the stored session and routes to sign-in; a 426 UPGRADE_REQUIRED raises the forced-update event with details.storeUrl
  • Envelope codes such as ORDER_MIN_AMOUNT_NOT_MET or GPS_OUT_OF_TOLERANCE reach the UI as typed failures with the localised message and requestId
  • A retried mutation reuses its Idempotency-Key; 409 IDEMPOTENCY_KEY_REUSED is surfaced and never retried; 409 IDEMPOTENCY_IN_PROGRESS is retried after 1 s (SYN-05)
  • No session token, OTP or phone number appears in Sentry events or logs (NFR-08)

Requirements covered

RequirementTitleRelease
AUTH-06Sessions: expiry, multi-device, listing and revocation, sign-outV1
NFR-08Logging of sensitive actions and technical errors; monitoring and alertingV1
SYN-05Sync queue handling retries, duplicates (idempotency), errors and conflictsV1

API surface

Test measures

  • Unit tests with http_mock_adapter for each interceptor and each ApiFailure class
  • Unit test that a POST without Idempotency-Key is never auto-retried
  • Unit test of the Sentry beforeSend scrubber on a captured request with Authorization and phone fields

Risks

  • Secure-storage keys lost on some Android OEMs after backup/restore — disable backup and treat unreadable storage as signed out instead of crashing

KPI

  • Delivered by Mon 19 Oct 2026 within 1 person-day(s).
  • Every acceptance criterion above demonstrated before hand-off; linked requirements' acceptance criteria pass.
  • Zero open critical defects, permission gaps or data-integrity regressions at sign-off.

Generated from content/plan/tasks.json by scripts/generate-team-tasks.mjs. Dates are computed by the scheduler (Sunday–Thursday weeks, holidays excluded) — change the plan, not this page.

On this page