# Orchard observation contract v0.1.1

**Status:** accepted as design/research (Grok review 2026-09-05).  
**Job:** `133f4d31-deb7-4d3e-8c80-619407b39749`  
**Shared by:** future AR Cherry Orchard tab and Unreal HQ.  
**Not:** a deployed endpoint, implemented validator, or authorization to change picking `POST /ingest`.

Authors: ChatGPT (draft v0.1 + v0.1.1 additions), Grok (acceptance + publish).

## Record shape (demo)

```json
{
  "schema_version": "0.1.1",
  "observation_id": "demo-observation-001",
  "revision": 1,
  "supersedes_revision": null,
  "orchard_id": "demo-orchard",
  "block_id": "demo-block",
  "tree_id": "demo-tree-01",
  "branch_id": "demo-branch-A",
  "variety_id": "bing",
  "survey_id": "demo-survey-01",
  "source": "fixed_camera",
  "camera_id": "demo-camera-01",
  "captured_at": "2026-04-01T17:00:00Z",
  "received_at": "2026-04-01T17:05:00Z",
  "local_timezone": "America/Los_Angeles",
  "stage": "bloom",
  "metric": "visible_flower_cluster_count",
  "unit": "cluster",
  "value": 12,
  "scope": "tagged_branch_visible_roi",
  "method": "manual",
  "model_version": null,
  "confidence": null,
  "quality_flags": [],
  "media": [
    {
      "asset_id": "demo-image-01",
      "roi_xywh_normalized": [0.1, 0.1, 0.6, 0.6],
      "rights": {
        "status": "reference_demo",
        "creator": "demo",
        "source_record": "synthetic",
        "license": "CC0-1.0",
        "attribution": "synthetic fixture — not field capture",
        "modifications": []
      }
    }
  ],
  "association_set_id": null,
  "status": "draft",
  "is_demo": true
}
```

All example IDs and counts are invented. No real capture or bloom detection is claimed.

## v0.1.1 additions (ChatGPT 2026-09-05)

1. **`variety_id`** — orchard variety registry key. Use explicit sentinel values for unknown or mixed stands (e.g. `unknown`, `mixed`); do not invent a variety from incomplete labels.
2. **Media rights fields** — each media item carries explicit `rights`: `status`, `creator`, `source_record`, `license`, `attribution`, `modifications`. Public reference imagery must stay labelled reference/demo and retain attribution. Wageningen UAV bloom dataset license remains unresolved — do not import/train until resolved.

## Semantics and validation

- Stable orchard/block/tree/branch registries own identity. Null tree/branch is permitted for explicitly block-level observations, not for tree/branch metrics. A survey groups a capture visit, not every observation ever made at a tree.
- Sources: `phone`, `fixed_camera`, `drone`, `manual_field`. Methods: `manual`, `model`, `imported_reference`. Stages: `unknown`, `dormant`, `bud`, `bloom`, `petal_fall`, `fruit_set`, `fruit_growth`, `ripening`, `harvested`. Stage is an observation, not inferred from month alone.
- Count metrics have nonnegative integer values and explicit units: `visible_flower_count`/`flower`, `visible_flower_cluster_count`/`cluster`, `visible_fruit_count`/`fruit`. Whole-tree estimated counts use separately named metrics and an estimator/version plus coverage assumptions. Never add flowers and clusters.
- Bloom intensity must declare a scale and method (e.g. `bloom_pixel_fraction`/`fraction` in `[0,1]`). It is not interchangeable with a count.
- Missing/unusable measurements have `value=null` and a reason flag. Zero means an observer/model actually assessed the stated region and found none. Model records require `model_version`.
- Capture time and receipt time are separate UTC timestamps; `local_timezone` is IANA. Sort seasonal history by capture time.
- Media asset registry retains content hash, original capture metadata, rights/attribution, orientation, and storage locator. Signed delivery URLs are not permanent identity. ROI coordinates refer to the orientation-normalized original; `x,y,w,h` must describe a nonempty region inside `[0,1]`.
- Model outputs begin `draft`. Human review produces `reviewed`/`rejected` with audit events. UI cannot relabel model data as `manual` merely by approving it.
- Corrections append revisions under the same `observation_id`. Identical id/revision/content replay is idempotent; conflicting content is rejected. Aggregation uses latest eligible revision only.
- Overlap deduplication is explicit via `association_set_id`. Until validated, per-image detections must not be summed into whole-tree counts.

## Storage boundary (mandatory)

Live `ar-cherry-ingest` (`https://ar-cherry-ingest.jamesgrunsky.workers.dev`) today is **only** shift aggregates:

`POST /ingest` → worker_id, date, ready/wait/maybe/defect counts, avg_confidence, shifts_seconds, variety, mode.

No frames, GPS, or legal names. This orchard contract is a **different domain** (tree/branch phenology + media + revisions). Do **not** send these records to `/ingest` and do **not** widen `/ingest` fields.

Recommended later build (separate job, after backend agreement): e.g. `POST /observations` (+ media registry) or a dedicated orchard KV/R2 namespace, with iOS Orchard tab first slice = local/manual observations + dated photo comparisons (no vendor APIs, no trained-count claim).

## First acceptance fixtures

Test absent measurement versus zero, clusters versus flowers, duplicate replay, correction history, invalid ROI, timezone boundary, missing model version, overlapping views, rejected measurements excluded from aggregates, `variety_id` unknown/mixed handling, and media-rights required fields.

## Tracks to keep separate

- App-device / iOS shell evidence (job `87de1d1f…`)
- FCT / ADP / SMS evidence
- This orchard observation design track
