{
"id": "org.cannadb.growEntry",
"defs": {
"main": {
"key": "tid",
"type": "record",
"record": {
"type": "object",
"required": [
"plant",
"createdAt"
],
"properties": {
"grow": {
"type": "string",
"format": "at-uri",
"description": "Optional denormalized AT-URI reference to the plant's `org.cannadb.grow`. Lets the AppView assemble a whole-grow timeline and gate on the grow's `listed` preference without resolving every plant first. The plant ref is canonical; this is a cache for query efficiency and ref resilience."
},
"kind": {
"type": "string",
"description": "What kind of event this is. Open vocabulary — clients should accept unknown values and fall back to generic display, and may offer user-defined kinds. \"stage\" marks a lifecycle transition and is the one kind that gives `stage` meaning.",
"knownValues": [
"stage",
"watering",
"feeding",
"note",
"measurement",
"training",
"transplant",
"topping",
"defoliation",
"flush",
"pest",
"disease",
"treatment",
"harvest"
]
},
"note": {
"type": "string",
"maxLength": 100000,
"description": "Markdown-formatted free-form note — the catch-all for anything the structured fields don't capture (measurement context, LST/defoliation details, observations, photos commentary). Deliberately unstructured.",
"maxGraphemes": 10000
},
"plant": {
"type": "string",
"format": "at-uri",
"description": "AT-URI reference to the `org.cannadb.plant` this event belongs to. (Consumers should validate the ref resolves to an `org.cannadb.plant` record.)"
},
"stage": {
"type": "string",
"description": "The lifecycle stage this entry marks a transition into. Set on stage transitions (typically with `kind` \"stage\"). The AppView treats the most recent entry carrying a `stage` as the plant's current stage, and the full ordered set as its lifecycle history. Display ordering of stages is an AppView concern. Open vocabulary.",
"knownValues": [
"germinating",
"seedling",
"vegetative",
"flowering",
"drying",
"curing",
"harvested",
"dead"
]
},
"createdAt": {
"type": "string",
"format": "datetime",
"description": "Client-declared timestamp when this entry record was created."
},
"occurredAt": {
"type": "string",
"format": "datetime",
"description": "When the event actually happened, if different from `createdAt` (e.g., logging yesterday's feeding today). Consumers default to `createdAt` when absent. This is the field timelines sort on."
},
"measurements": {
"type": "array",
"items": {
"ref": "#measurement",
"type": "ref"
},
"description": "Structured readings recorded with this event (height, pH, EC/PPM, runoff pH, water volume, …). Optional — most entries carry none. A feeding entry might carry pH and EC; a check-up entry might carry height. Mirrors how journaling tools attach measurements to an activity."
}
}
},
"description": "One append-only event on a plant's timeline. A single record type unifies what other journals split into status changes, activities, measurements, and notes — because they are all the same shape (a timestamped thing that happened to a plant) and the distinguishing detail is an open `kind` vocabulary. Stage transitions (kind \"stage\") are how lifecycle is tracked: the AppView reads the latest stage entry as the plant's current stage and the full set as its lifecycle history. Activities (watering, feeding, training, …) optionally carry structured `measurements`. Freeform observation is the `note` field — deliberately loose so growers can record measurements prose, LST details, and anything else without the schema being prescriptive. Photos are NOT embedded here; they are separate `org.cannadb.growPhoto` records that may link back to an entry. Publishing this record IS the claim — contextualized by the publisher DID at display time."
},
"measurement": {
"type": "object",
"required": [
"metric",
"value"
],
"properties": {
"unit": {
"type": "string",
"description": "Unit of `value`. Open vocabulary; required to interpret the scaled integer. Consumers should accept unknown units and fall back to displaying the unit string as written.",
"knownValues": [
"cm",
"in",
"ph",
"mS_cm",
"ppm",
"ml",
"l",
"gal",
"celsius",
"fahrenheit",
"percent"
]
},
"value": {
"type": "integer",
"description": "The measured value in hundredths of `unit` (e.g., 625 = 6.25). Scaled integer because atproto has no float type."
},
"metric": {
"type": "string",
"description": "What was measured. Open vocabulary — consumers accept unknown metrics and may offer user-defined ones.",
"knownValues": [
"height",
"ph",
"ec",
"ppm",
"tds",
"runoff_ph",
"water_volume",
"temperature",
"humidity",
"pot_size"
]
}
},
"description": "A single structured reading. Both `metric` and `unit` are open vocabularies. `value` is a scaled integer in hundredths of `unit` (atproto has no float type), matching the scaled-integer convention used for cannabinoid percentages on `org.cannadb.strain`: pH 6.25 = 625 with unit \"ph\"; height 30.5 cm = 3050 with unit \"cm\"; EC 1.85 = 185 with unit \"mS_cm\"."
}
},
"$type": "com.atproto.lexicon.schema",
"lexicon": 1
}