{
"id": "org.cannadb.plant",
"defs": {
"main": {
"key": "tid",
"type": "record",
"record": {
"type": "object",
"required": [
"grow",
"createdAt"
],
"properties": {
"grow": {
"type": "string",
"format": "at-uri",
"description": "AT-URI reference to the `org.cannadb.grow` this plant belongs to. The grow is expected to be published by the same DID. (The lexicon spec does not enforce collection constraints on at-uri; consumers should validate the ref resolves to an `org.cannadb.grow` record.)"
},
"label": {
"type": "string",
"maxLength": 2000,
"description": "Short label distinguishing this plant within the grow, e.g., \"WC #1\". Optional — the AppView falls back to the strain name plus an index.",
"maxGraphemes": 200
},
"strain": {
"type": "string",
"format": "at-uri",
"description": "AT-URI reference to the `org.cannadb.strain` record (any `kind`) this plant is a grow of. Canonical when present and resolvable; `strainName` is the display fallback. This is the seam to the strain library — the same ref that an Isley import keys on. (The lexicon spec does not enforce collection constraints on at-uri; consumers should validate the ref resolves to an `org.cannadb.strain` record.)"
},
"harvest": {
"ref": "#harvest",
"type": "ref",
"description": "Measured harvest outcomes for this plant, recorded once the run completes. Optional and entirely additive — a plant in veg has no harvest object. The AppView aggregates these across grows of the same strain."
},
"isClone": {
"type": "boolean",
"description": "Whether this plant is a clone (cut) rather than grown from seed. When true, `parentPlant` may reference the plant it was cut from."
},
"createdAt": {
"type": "string",
"format": "datetime",
"description": "Client-declared timestamp when this plant record was originally created."
},
"startedAt": {
"type": "string",
"format": "datetime",
"description": "When this plant's life began for the grower (germination or the date the clone/seed was planted). Optional."
},
"strainName": {
"type": "string",
"description": "Display fallback for the strain name. Used when no `strain` ref is available, when the ref does not resolve, or for bag-seed / unknown genetics with no atproto presence."
},
"parentPlant": {
"type": "string",
"format": "at-uri",
"description": "For clones: AT-URI reference to the `org.cannadb.plant` this plant was cut from. `parentPlantName` is the display fallback. (Consumers should validate the ref resolves to an `org.cannadb.plant` record.)"
},
"parentPlantName": {
"type": "string",
"description": "Display fallback for the parent plant when `parentPlant` is absent or unresolved."
}
}
},
"description": "A single plant within a grow. The plant is the lifecycle unit: it references the `org.cannadb.grow` it belongs to and (optionally) the `org.cannadb.strain` it is. Its lifecycle stage is NOT stored as a mutable field here — stage transitions are append-only `org.cannadb.growEntry` events (kind \"stage\"), and the AppView computes the current stage from the latest such event. Measured harvest outcomes, once known, are recorded in the optional `harvest` object so the AppView can aggregate real cultivation data across many grows of the same strain (\"growers' average flowering time / yield\"). Publishing this record IS the claim — every field is contextualized by the publisher DID at display time."
},
"harvest": {
"type": "object",
"properties": {
"notes": {
"type": "string",
"maxLength": 50000,
"description": "Markdown-formatted free-form harvest notes — trim/dry/cure method, smell, observations.",
"maxGraphemes": 5000
},
"dryWeightG": {
"type": "integer",
"minimum": 0,
"description": "Final dry/cured weight in hundredths of a gram (e.g., 12500 = 125.00 g). This is the figure the AppView aggregates into per-strain yield rails."
},
"wetWeightG": {
"type": "integer",
"minimum": 0,
"description": "Wet (fresh-cut) weight in hundredths of a gram (e.g., 50000 = 500.00 g)."
},
"harvestedAt": {
"type": "string",
"format": "datetime",
"description": "When the plant was harvested (cut down)."
},
"measuredCbd": {
"type": "integer",
"maximum": 10000,
"minimum": 0,
"description": "Lab-tested CBD percentage in hundredths of a percent, if available."
},
"measuredThc": {
"type": "integer",
"maximum": 10000,
"minimum": 0,
"description": "Lab-tested THC percentage in hundredths of a percent (e.g., 2450 = 24.50%), if available. Distinct from the strain record's breeder-claimed `thcRange`."
},
"floweringDays": {
"type": "integer",
"maximum": 365,
"minimum": 0,
"description": "Actual flowering duration in days (start of 12/12 — or, for autoflowers, the full run — to harvest). The AppView aggregates this into a growers' average flowering time alongside the breeder-claimed `cycleTime` range on the strain."
}
},
"description": "Measured outcomes of a completed plant. All fields optional. Decimal quantities use the scaled-integer convention from `org.cannadb.strain` (atproto has no float type): weights are in hundredths of a gram, cannabinoid percentages in hundredths of a percent."
}
},
"$type": "com.atproto.lexicon.schema",
"lexicon": 1
}