Internal discussion draft — no implementation started

Field-to-Model: a correction‑data pipeline for PalmOilAI

Every grade a harvester corrects in the field is a labeled training example. Right now it never leaves the phone. This document proposes how it gets to us — quietly, reliably, and without asking the harvester to do anything.

Scope — mobile app → dedicated ingestion server Status — brainstorm, pending decisions marked below Owner — palm_oil_mobile
01

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.

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.

02

Agreed principles

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

03

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.

figure 1 — data path from bunch to model
flowchart LR
  classDef device fill:#00000000,stroke-width:1px;
  classDef server fill:#00000000,stroke-width:1px;
  classDef future stroke-dasharray: 4 3;

  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

  class P,L device
  class S,R server
  class T,M future

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.

04

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.

figure 2 — from a correction to an uploaded record
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.
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.

05

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.

FieldWhere it comes fromWhy it matters for training
imageCamera captureThe actual training input
ai_detections[]On-device inferenceThe model's original boxes, classes, confidence — what it got right or wrong
human_correction[]Harvester reviewThe corrected boxes/classes, or a removed false positive — the actual label
model_versionApp buildWhich model produced the AI call, so improvement can be measured generation over generation
captured_atAppChronological + seasonal context for the dataset
device_idAppCoarse debugging / dedup, not tied to a personal identity
uploaded_atSync engine, local onlyNever sent — this is the idempotency marker that makes sync resumable
06

Open decisions

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

QuestionLeaningTrade-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 Decided Keeps ingestion downtime from ever touching the app's core detection features Decided
Fully background, no harvester-facing sync UI Decided Zero added burden on the harvester's workflow Decided
07

Risks & mitigations

08

Out of scope

Where this document stops

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.