Document current to target sync architecture gap audit
This commit is contained in:
parent
663da1e409
commit
eded81345e
|
|
@ -0,0 +1,85 @@
|
|||
# MotorSignal — Current Architecture → Target Architecture Gap Audit
|
||||
|
||||
**Date:** September 1, 2026
|
||||
**Scope:** SyncOutbox, SyncModels, SyncProvider, R2UploadService, Firebase sync tests, Phase 2 discovery/implementation evidence, Firestore rules, account/auth integration.
|
||||
|
||||
## Executive finding
|
||||
|
||||
The repository is already substantially aligned with the Phase 2 offline-first design. The correct action is hardening and integration, not replacing the sync architecture.
|
||||
|
||||
The authoritative data path remains:
|
||||
|
||||
`Sensors → local Drift Schema 5 → durable sync outbox → provider → Firestore metadata + R2 telemetry bundle`
|
||||
|
||||
Cloud availability must never gate recording or local ride persistence.
|
||||
|
||||
## Current → Target gap list
|
||||
|
||||
| Area | Current | Target | Gap | Action |
|
||||
|---|---|---|---|---|
|
||||
| Local ownership | Drift Schema 5 remains authoritative | Same | None | Preserve |
|
||||
| Outbox persistence | SharedPreferences JSON manifest | Crash-resilient durable outbox | Interrupted `UPLOADING` could survive forever; concurrent saves could race | **Implemented:** requeue in-flight items on load and serialize manifest writes |
|
||||
| Outbox capacity | Fixed 500 item limit | Bounded without data loss | Oldest items could previously be silently discarded | **Implemented:** evict terminal items only; refuse to discard pending work |
|
||||
| Outbox identity | Local ride + provider composite | Stable idempotency identity | Legacy key format was less explicit | **Implemented:** `providerId:localRideId` plus explicit `idempotencyKey` |
|
||||
| Sync model parsing | JSON parsing was permissive | Defensive persisted-state parsing | Invalid IDs/dates could poison queue | **Implemented:** validate identity, timestamps and attempt count |
|
||||
| Provider abstraction | Upload-only contract in active code | Provider-neutral sync lifecycle | Download/header/delete are future capabilities, not yet required by current consumer flow | **Deferred:** add when multi-device restore is implemented; do not fake unused APIs |
|
||||
| SyncManager | Central queue/retry/dispatch exists | Same + auth/network recovery | Auth-required work did not automatically resume after login | **Implemented:** `retryAuthRequired()` and auth-state listener |
|
||||
| Crash recovery | Upload state persisted | Restart must resume safely | `UPLOADING` had no restart recovery | **Implemented** |
|
||||
| Firebase provider | Firestore + R2 upload implemented | Same | R2 happens before Firestore metadata; retry can repeat upload | **Accepted for now:** deterministic ride/object identity makes retries idempotent; Firestore remains final metadata confirmation |
|
||||
| R2 gateway | Worker + Firebase ID token | Authenticated UID-scoped object storage | Client accepted unsafe ride IDs and unverified worker object keys | **Implemented:** token/empty bundle/path validation and response object-key validation |
|
||||
| R2 HTTP handling | 401/403 and generic retry | Explicit transient/permanent classes | 408/429/5xx need SyncManager retry semantics | **Implemented:** transient responses fall through to retryable result |
|
||||
| R2 response verification | Successful non-JSON response could be treated as success | Verify cloud acknowledgement | False-positive success possible | **Implemented:** successful response must contain a valid user-scoped object key |
|
||||
| SHA-256 | Client computes and sends SHA-256 | Worker authoritative verification | Client-only validation would duplicate trust logic and break existing mocks | **Implemented:** require non-empty hash; worker remains authoritative verifier |
|
||||
| Firebase rules | User profile + rides + ride descendants | Explicit ownership for current/future user-owned collections | No explicit motorcycles/trips/friends/events coverage; recursive ride wildcard was broader than necessary | **Implemented:** explicit user-owned collections and deny-by-default fallback |
|
||||
| Auth integration | Firebase auth abstraction and Riverpod state already exist | Auth should drive cloud identity, not recording | Auth-required sync did not automatically rearm | **Implemented:** SyncManager listens for authenticated transitions |
|
||||
| Account UI | Auth foundation exists | Account should expose cloud/sync state and profile lifecycle | Full account/profile/cloud UX remains a product/UI phase | **Deferred:** no speculative UI changes in stabilization |
|
||||
| Firebase sync tests | Strong Phase 2B/2D matrix already exists | Regression coverage for restart/corruption/security | Missing explicit outbox/R2 hardening cases | **Implemented:** new `sync_architecture_hardening_test.dart` |
|
||||
| Phase reports | Existing Phase 2 discovery provides architecture baseline; exact `MOTORSIGNAL_PHASE_2B/2D/2E_IMPLEMENTATION_REPORT.md` filenames were not present at the audited branch paths | Implementation evidence should match code | Documentation naming/status is not authoritative enough to use as a gate | **Implemented:** this audit records code as source of truth |
|
||||
|
||||
## Intentionally not implemented
|
||||
|
||||
1. **No Drift Schema 5 migration.** The current outbox design deliberately stays outside the frozen telemetry database.
|
||||
2. **No Firestore per-sample telemetry writes.** High-frequency data remains bundled into R2.
|
||||
3. **No speculative multi-device download/delete API.** Those operations need the final restore/conflict model before being exposed in the core contract.
|
||||
4. **No billing/Pro gating in the sync engine.** Entitlement must remain separate from identity and storage ownership.
|
||||
5. **No broad Firestore public/group-write rules.** Social/group collections require explicit membership and field-level authorization before enabling writes.
|
||||
6. **No account UI redesign.** The auth layer is already decoupled; the remaining work is UX and cloud-status presentation, not identity plumbing.
|
||||
|
||||
## Resulting target architecture
|
||||
|
||||
```text
|
||||
┌─────────────────────────────┐
|
||||
│ Account / Auth / Profile UI │
|
||||
└──────────────┬──────────────┘
|
||||
│ AuthState
|
||||
▼
|
||||
Sensors → Drift Schema 5 → SyncOutbox → SyncManager
|
||||
│
|
||||
┌───────────┴───────────┐
|
||||
▼ ▼
|
||||
Firebase Provider GitHub Provider
|
||||
│
|
||||
┌──────┴──────┐
|
||||
▼ ▼
|
||||
Firestore R2 Worker
|
||||
metadata telemetry.zip
|
||||
│ │
|
||||
└──────┬──────┘
|
||||
▼
|
||||
durable cloud backup
|
||||
```
|
||||
|
||||
### Operational guarantees after this audit
|
||||
|
||||
- Riding remains fully offline/local-first.
|
||||
- A process death during upload does not strand a ride in `UPLOADING`.
|
||||
- Concurrent outbox mutations are serialized.
|
||||
- Pending rides are never silently discarded to satisfy capacity.
|
||||
- Authentication recovery automatically resumes cloud work after login.
|
||||
- R2 object paths cannot be supplied through path traversal-style ride IDs.
|
||||
- A nominal HTTP success without a valid worker object key is not accepted as sync success.
|
||||
- Firestore access is UID-scoped and deny-by-default outside explicitly supported collections.
|
||||
|
||||
## Remaining next-phase work
|
||||
|
||||
The next cloud milestone is **restore/multi-device synchronization**, followed by profile/motorcycle cloud documents, conflict handling, explicit account deletion/export flows, and then entitlement-aware cloud limits. Those should be implemented against this hardened foundation rather than replacing it.
|
||||
Loading…
Reference in New Issue