DATA_COLLECTION_PIPELINE.md 6.6 KB

Field-to-Model: Correction Data Pipeline

Status: Discussion draft — no implementation started, no code changes made. Scope: Mobile app → dedicated ingestion server. Retraining itself is out of scope (see Out of scope).

Why this exists

The app already lets a harvester correct the AI's grading — change a class, remove a false detection, or draw a box around a bunch the model missed. That correction is never destructive: the AI's original call is kept, and the human's edit sits alongside it (see the history / corrections tables in lib/services/database_helper.dart).

That pairing — what the model said next to what was actually true — is exactly the labeled data needed to train the next generation of the model. Today it's trapped in a local SQLite database on each harvester's phone and goes nowhere. This document proposes a pipeline to collect it centrally, without adding a single step to the harvester's workflow.

Agreed principles

Three calls have already been made in discussion; everything downstream follows from them.

  1. A dedicated, standalone ingestion server. Not bolted onto server-desktop or any operational service. If it's slow or down, nothing a harvester relies on in the field is affected — the data simply queues on-device until it's reachable again.
  2. Fully silent, background sync. No "sync now" button, no progress bar, no prompt. The harvester's job is to scan and correct; getting that data to us is entirely the app's problem, running only when connectivity allows.
  3. The app tracks its own upload state. Every record knows locally whether it's been sent. No handshake with the server is needed to know what's still pending — a local flag is the source of truth, which makes the whole thing resumable after any interruption.

System architecture

The phone is the only place data is created. Everything after that is designed so a slow or offline server never blocks a harvester from scanning.

flowchart LR
  H(["Harvester"]) --> P["Phone: scan + correct"]
  P --> L[("Local SQLite\nscans + corrections\n+ upload state")]
  L -- "background sync\nWi-Fi / connectivity trigger" --> S["Dedicated Ingestion Server"]
  S --> R[("Raw Store\nimages + AI result + correction")]
  R --> T["Retraining Pipeline\n(offline, periodic — future work)"]
  T --> M["New Model Version"]
  M -.-> P

The dashed link back to the phone is deliberately out of scope for this document — how a new model version reaches devices is a separate rollout question for later.

Sync flow, step by step

The harvester only ever sees the left-most lane. Everything else happens whenever the phone next has connectivity, with no user-visible state.

sequenceDiagram
  participant H as Harvester
  participant App as App
  participant DB as Local DB
  participant Sync as Background Sync
  participant Srv as Ingestion Server

  H->>App: Scan bunch, correct a grade
  App->>DB: Save record (uploadedAt = none)
  Note right of DB: Sits queued.<br/>Nothing for the harvester to do.
  loop Whenever the OS wakes it
    Sync->>DB: Which records are unsynced?
    alt connectivity available
      Sync->>Srv: Upload batch (image + AI result + correction)
      Srv-->>Sync: Acknowledged
      Sync->>DB: Mark uploadedAt = now
    else no connectivity
      Sync->>Sync: Do nothing, try again next wake
    end
  end

One edge case worth naming: a harvester can correct a scan after it already uploaded — reviewing a batch a few days later, say. So "synced" needs to track the correction separately from the original scan, or a late correction silently never makes it off the device.

What actually gets sent

Per scan, the payload pairs the model's original call with whatever the harvester changed it to — that pairing is the entire point of the exercise.

Field Where it comes from Why it matters for training
image Camera capture The actual training input
ai_detections[] On-device inference The model's original boxes, classes, confidence — what it got right or wrong
human_correction[] Harvester review The corrected boxes/classes, or a removed false positive — the actual label
model_version App build Which model produced the AI call, so improvement can be measured generation over generation
captured_at App Chronological + seasonal context for the dataset
device_id App Coarse debugging / dedup, not tied to a personal identity
uploaded_at Sync engine, local only Never sent — this is the idempotency marker that makes sync resumable

Open decisions

Three things still need a call before this can move from brainstorm to plan.

Question Leaning Trade-off
Wi-Fi only, or any connection? Wi-Fi only to start Slower data arrival, but no surprise mobile-data cost for a harvester who never asked for this Open
Upload every scan, or only corrected ones? Every reviewed scan More storage, but "the AI was right, no correction needed" is itself a useful training signal, not noise Open
Silent upload, or an opt-in toggle? Depends on device ownership Opt-in adds friction and drop-off; silent upload only sits right if these are company-managed devices Open
Dedicated, standalone server Keeps ingestion downtime from ever touching the app's core detection features Decided
Fully background, no harvester-facing sync UI Zero added burden on the harvester's workflow Decided

Risks & mitigations

  • Ingestion server is down or slow. No effect on scanning or correcting — records simply stay queued locally and upload on the next successful sync. This is the entire reason it's a separate server.
  • Bandwidth cost on a harvester's personal data plan. Addressed by the Wi-Fi-only decision above, once confirmed.
  • A correction made after the scan already synced gets missed. Track correction-sync state separately from scan-sync state, so an edit made days later still gets picked up on the next background pass.
  • Raw storage grows indefinitely. Not a phone-side concern, but worth flagging for the server design — periodic archival into a proper training dataset, rather than an ever-growing inbox.

Out of scope

The app's responsibility ends at reliably getting corrected data to the server. Aggregating uploads into a training dataset, running retraining, evaluating a new model, and distributing it back to devices are separate initiatives — worth planning, but deliberately not decided here.


Companion presentation version (with rendered diagrams) prepared as a shareable artifact for stakeholder review.