org.cannadb.strain
Schema Diff
+2 -2
Compatibility Analysis
Backward Compatible
No changes detected.
1
1
{
2
2
"id": "org.cannadb.strain",
3
3
"defs": {
4
4
"main": {
5
5
"key": "tid",
6
6
"type": "record",
7
7
"record": {
8
8
"type": "object",
9
9
"required": [
10
10
"name",
11
11
"kind",
12
12
"createdAt"
13
13
],
14
14
"properties": {
15
15
"kind": {
16
16
"type": "string",
17
17
"description": "Level of specificity. \"strain\" = broad genetic identity; \"cut\" = a specific phenotype, clone, or keeper pheno of a strain. Open vocabulary — clients should accept unknown values gracefully and fall back to generic display.",
18
18
"knownValues": [
19
19
"strain",
20
20
"cut"
21
21
]
22
22
},
23
23
"name": {
24
24
"type": "string",
25
25
"description": "Primary common name of the strain or cut, e.g., \"Wedding Cake\" or \"Forum cut of GSC\"."
26
26
},
27
27
"yield": {
28
28
"ref": "#yieldRange",
29
29
"type": "ref",
30
30
"description": "Claimed yield as a min/max integer range plus a `unit` enum so the integer is unambiguous. Single-value quotes set min == max. The unit is required to interpret the integer correctly — a lexicon convention because breeders quote yield in mutually incompatible units (per-plant vs per-m², grams vs ounces) and an unlabeled integer is unsafe."
31
31
},
32
32
"height": {
33
33
"ref": "#heightRange",
34
34
"type": "ref",
35
35
"description": "Plant height at maturity in centimeters, expressed as a min/max range. Conventional basis is indoor mature (the breeder's typical quote). Outdoor mature heights run substantially larger; outdoor-quoted values should be flagged in `cultivationNotes` rather than smashed into this field."
36
36
},
37
37
"holder": {
38
38
"type": "string",
39
39
"format": "at-uri",
40
40
"description": "For cut records: AT-URI reference (typically a DID) to the holder of this specific cut. `holderName` is the display fallback."
41
41
},
42
42
"breeder": {
43
43
"type": "string",
44
44
"format": "at-uri",
45
45
"description": "AT-URI reference to an `org.cannadb.breeder` record. Canonical when present and resolvable; `breederName` is the display fallback. (The lexicon spec does not enforce collection constraints on at-uri; consumers should validate the ref resolves to an `org.cannadb.breeder` record.)"
46
46
},
47
47
"effects": {
48
48
"type": "array",
49
49
"items": {
50
50
"type": "string",
51
51
"description": "A single recreational effect descriptor."
52
52
},
53
53
"description": "Claimed recreational effect descriptors. Open vocabulary — see `flavors` for rationale. Distinct from `medicinalEffects`, which is a parallel vocabulary for therapeutic claims."
54
54
},
55
55
"flavors": {
56
56
"type": "array",
57
57
"items": {
58
58
"type": "string",
59
59
"description": "A single flavor descriptor."
60
60
},
61
61
"description": "Claimed flavor profile descriptors. Open vocabulary — the lexicon does not enforce a closed set; the AppView normalizes near-duplicates at index time and may ship a curated suggestion list for autocomplete."
62
62
},
63
63
"parents": {
64
64
"type": "array",
65
65
"items": {
66
66
"type": "string",
67
67
"format": "at-uri",
68
68
"description": "AT-URI reference to a parent `org.cannadb.strain` record."
69
69
},
70
70
"description": "Genetic ancestry — AT-URI references to parent `org.cannadb.strain` records (typically 1–2 entries). `parentNames` provides display fallbacks; `parentBreederNames` provides per-parent breeder attribution as a parallel array. (The lexicon spec does not enforce collection constraints on at-uri; consumers should validate refs resolve to `org.cannadb.strain` records.)"
71
71
},
72
72
"cbdRange": {
73
73
"ref": "#cannabinoidRange",
74
74
"type": "ref",
75
75
"description": "Claimed CBD percentage range. Values are integers in hundredths of a percent (e.g., 75 = 0.75%). Optional; either bound may be omitted if only one is known."
76
76
},
77
77
"terpenes": {
78
78
"type": "array",
79
79
"items": {
80
80
"type": "string",
81
81
"description": "A single terpene name."
82
82
},
83
83
"description": "Claimed dominant terpenes. Open vocabulary — see `flavors` for rationale."
84
84
},
85
85
"thcRange": {
86
86
"ref": "#cannabinoidRange",
87
87
"type": "ref",
88
88
"description": "Claimed THC percentage range. Values are integers in hundredths of a percent (e.g., 2450 = 24.50%). Optional; either bound may be omitted if only one is known."
89
89
},
90
90
"createdAt": {
91
91
"type": "string",
92
92
"format": "datetime",
93
93
"description": "Client-declared timestamp when this record was originally created."
94
94
},
95
95
"cycleTime": {
96
96
"ref": "#dayRange",
97
97
"type": "ref",
98
98
"description": "Cycle time in days, expressed as a min/max range. Basis derives from `autoflower`: full-run (germination → harvest) for autoflowers, flower-only (start of 12/12 → harvest) for photoperiods. Single-value quotes set min == max. The basis is not stored separately because it derives from `autoflower`; documenting the convention here prevents downstream tools from misinterpreting the integer."
99
99
},
100
100
"sourceUrl": {
101
101
"type": "string",
102
102
"format": "uri",
103
103
"description": "Optional URL pointing at the canonical breeder/origin write-up for this strain — typically the breeder's product page or a strain-specific writeup. Distinct from the breeder record's `website` (the breeder's home page); this is the strain-specific page. No link-rot guarantee; informational only."
104
104
},
105
105
"versionOf": {
106
106
"type": "array",
107
107
"items": {
108
108
"type": "string",
109
109
"format": "at-uri",
110
110
"description": "AT-URI reference to an `org.cannadb.strain` record this record claims a derivative or equivalence relationship with."
111
111
},
112
112
"description": "Derivative or claimed relationships — AT-URI references to `org.cannadb.strain` records this record is sold as, claims to be, or is a take on. Distinct from `parents` (genetic ancestry): a pure relabel has `versionOf` but no `parents`; a derivative breeding take may have both. Includes the autoflower-take-on-a-photo pattern (an autoflower variant of \"the same strain\" is a `versionOf` claim even though its `parents` may differ because a ruderalis line was crossed in)."
113
113
},
114
114
"autoflower": {
115
115
"type": "boolean",
116
116
"description": "Whether this strain or cut is autoflowering (i.e., has ruderalis influence). Orthogonal to indica/sativa lean. Also conditions the basis of `cycleTime`: full-run (germination → harvest) for autoflowers, flower-only for photoperiods."
117
117
},
118
118
"generation": {
119
119
"type": "string",
120
120
"description": "Free-form generation marker for the strain's own generation (e.g., \"F7\", \"BX3\", \"S1\", \"Selection\"). Free-form because the vocabulary is open and breeders write whatever shorthand fits. Parent strains' generations stay inline in `parentNames` strings."
121
121
},
122
122
"holderName": {
123
123
"type": "string",
124
124
"description": "Display fallback for the cut holder."
125
125
},
126
126
"originYear": {
127
127
"type": "integer",
128
128
"description": "Year the strain or cut was first introduced."
129
129
},
130
130
"alsoKnownAs": {
131
131
"type": "array",
132
132
"items": {
133
133
"type": "string",
134
134
"description": "An alternate display name for this strain or cut."
135
135
},
136
136
"description": "Alternate names this strain or cut is known by, e.g., [\"Triangle Mints #23\"]."
137
137
},
138
138
"breederName": {
139
139
"type": "string",
140
140
"description": "Display fallback for the breeder name. Used when no `breeder` ref is available, when the ref does not resolve, or for breeders without atproto presence."
141
141
},
142
142
"cbdDominant": {
143
143
"type": "boolean",
144
144
"description": "Whether this is a CBD-dominant strain. Orthogonal to the indica/sativa axis (a strain can be both indica-leaning and CBD-dominant). Distinct from `cbdRange` (which is the quantitative CBD percentage); `cbdDominant` is the category signal — cannabis culture treats CBD-dominant strains as a distinct shopping category, not just a high-CBD variant."
145
145
},
146
146
"description": {
147
147
"type": "string",
148
148
"maxLength": 100000,
149
149
"description": "Markdown-formatted identity narrative — what this strain is, where it came from, the breeder's intent, the character. Distinct from `cultivationNotes`, which is for cultivators. Capped at 10000 graphemes (100000 bytes).",
150
150
"maxGraphemes": 10000
151
151
},
152
152
"growthOdour": {
153
153
"type": "string",
154
154
"description": "Claimed growth odour intensity. Practical for stealth growers. `knownValues` for forward-compat.",
155
155
"knownValues": [
156
156
"low",
157
157
"medium",
158
158
"high"
159
159
]
160
160
},
161
161
"parentNames": {
162
162
"type": "array",
163
163
"items": {
164
164
"type": "string",
165
165
"description": "Display name of a parent strain."
166
166
},
167
167
"description": "Display fallbacks for parent strains. Used when refs are absent or unresolved. Phenotype designations, generation markers, and uncertainty markers may be embedded inline in the string (e.g., \"Triangle Mints #23 [pheno: keeper] (F3)\") — clients render them as written."
168
168
},
169
169
"thcCbdRatio": {
170
170
"type": "string",
171
171
"description": "Claimed THC:CBD ratio expressed as a string, e.g., \"1:1\", \"20:1\"."
172
172
},
173
173
"indicaSativa": {
174
174
"type": "integer",
175
175
"maximum": 100,
176
176
"minimum": 0,
177
177
"description": "Indica/sativa lean on a 0–100 scale, where 0 is pure indica and 100 is pure sativa. Pure landraces sit at the extremes; modern hybrids fall between."
178
178
},
179
179
"originRegion": {
180
180
"type": "string",
181
181
"description": "Region of geographic origin, e.g., \"Hindu Kush\", \"California\"."
182
182
},
183
183
"primaryImage": {
184
184
"type": "blob",
185
185
"accept": [
186
186
"image/png",
187
187
"image/jpeg",
188
188
"image/webp"
189
189
],
190
-
"maxSize": 1000000,
191
-
"description": "One representative image of the strain or cut. Accepts common web image formats; capped at 1 MB to keep PDS storage burden modest. Higher-resolution photography belongs in review or grow records."
190
+
"maxSize": 2000000,
191
+
"description": "One representative image of the strain or cut. Accepts common web image formats; capped at 2 MB to keep PDS storage burden modest. Higher-resolution photography belongs in review or grow records."
192
192
},
193
193
"growDifficulty": {
194
194
"type": "string",
195
195
"description": "Claimed grow difficulty. `knownValues` for forward-compat — clients should accept unknown values and fall back to generic display.",
196
196
"knownValues": [
197
197
"easy",
198
198
"intermediate",
199
199
"hard"
200
200
]
201
201
},
202
202
"versionOfNames": {
203
203
"type": "array",
204
204
"items": {
205
205
"type": "string",
206
206
"description": "Display name of a strain this record is a version of."
207
207
},
208
208
"description": "Display fallbacks for `versionOf` references."
209
209
},
210
210
"characteristics": {
211
211
"type": "array",
212
212
"items": {
213
213
"type": "string",
214
214
"description": "A single plant characteristic descriptor."
215
215
},
216
216
"description": "Plant trait descriptors orthogonal to consumption experience. Open vocabulary; parallel to `effects`/`medicinalEffects`/`flavors` but for properties of the plant rather than the experience of consuming it. Examples: \"mold resistant\", \"frost tolerant\", \"high flower-to-leaf ratio\", \"drought tolerant\". The AppView normalizes near-duplicates at index time."
217
217
},
218
218
"cultivationNotes": {
219
219
"type": "string",
220
220
"maxLength": 100000,
221
221
"description": "Markdown-formatted cultivation guidance from the publisher — overflow for context that doesn't fit the structured `cycleTime`, `height`, `yield`, `growDifficulty`, `growthOdour` fields. Things like indoor/outdoor recommendations, environmental sensitivities, training notes, and run-specific quirks belong here. Capped at 10000 graphemes (100000 bytes).",
222
222
"maxGraphemes": 10000
223
223
},
224
224
"medicinalEffects": {
225
225
"type": "array",
226
226
"items": {
227
227
"type": "string",
228
228
"description": "A single medicinal effect descriptor."
229
229
},
230
230
"description": "Claimed medical/therapeutic effect descriptors. Open vocabulary; parallel to `effects` but for medicinal/therapeutic claims distinct from recreational. Examples: \"insomnia relief\", \"appetite stimulation\", \"anxiety relief\". The AppView normalizes near-duplicates at index time."
231
231
},
232
232
"seedAvailability": {
233
233
"type": "array",
234
234
"items": {
235
235
"type": "string",
236
236
"description": "A single seed variant identifier.",
237
237
"knownValues": [
238
238
"regular",
239
239
"feminized",
240
240
"autoflower",
241
241
"clone_only"
242
242
]
243
243
},
244
244
"description": "What variants exist for this strain name. Multi-valued because a strain often ships in multiple variants. Open vocabulary via `knownValues` so additive expansions (e.g., a future \"feminized_autoflower\" composite value) don't require a lexicon revision."
245
245
},
246
246
"shortDescription": {
247
247
"type": "string",
248
248
"maxLength": 2000,
249
249
"description": "Single-line plain-text summary used in card meta lines, search-result excerpts, and social-share previews. Markdown is disallowed; this is a one-line summary distinct from the full markdown `description`. Capped at 250 graphemes (2000 bytes) — enforced by the lexicon to keep card meta lines and share previews short.",
250
250
"maxGraphemes": 250
251
251
},
252
252
"parentBreederNames": {
253
253
"type": "array",
254
254
"items": {
255
255
"type": "string",
256
256
"description": "Display name of the breeder of the parent at the same index in `parentNames`. Empty string when unknown."
257
257
},
258
258
"description": "Optional parallel array to `parentNames` capturing per-parent breeder attribution (e.g., [\"Karma Genetics\", \"\"] when the first parent's breeder is known and the second's is not). Index-aligned with `parentNames`: position N in this array attributes the breeder of the parent at position N in `parentNames`. Empty strings or missing trailing entries indicate unknown attribution. Forward-compat note: phenotype designations, generation markers, and uncertainty markers stay inline in the parent name string until evidence shows they need their own structure."
259
259
}
260
260
}
261
261
},
262
262
"description": "Cannabis genetic identity. The same record type covers two levels of specificity, distinguished by the `kind` field: \"strain\" denotes a broad genetic identity (e.g., \"Wedding Cake\"); \"cut\" denotes a specific phenotype, clone, or keeper pheno (e.g., \"Forum cut of GSC\"). Captures intrinsic identity, structured cultivation profile, and the publisher's contextualized field assertions; subjective experience and measured outcomes live in separate record types (`org.cannadb.review`, future `org.cannadb.grow`) that reference this one. Publishing this record IS the claim — every field below is implicitly contextualized by the publisher's identity, computed by consumers from the publisher DID at display time."
263
263
},
264
264
"dayRange": {
265
265
"type": "object",
266
266
"properties": {
267
267
"max": {
268
268
"type": "integer",
269
269
"maximum": 365,
270
270
"minimum": 0,
271
271
"description": "Upper bound in days."
272
272
},
273
273
"min": {
274
274
"type": "integer",
275
275
"maximum": 365,
276
276
"minimum": 0,
277
277
"description": "Lower bound in days."
278
278
}
279
279
},
280
280
"description": "A min/max integer range expressed in whole days. Both bounds independently optional; single-value quotes set min == max. Atproto's data model has no float type, so all numeric fields use integers; days are a natural integer unit and need no further scaling."
281
281
},
282
282
"yieldRange": {
283
283
"type": "object",
284
284
"properties": {
285
285
"max": {
286
286
"type": "integer",
287
287
"minimum": 0,
288
288
"description": "Upper bound, in the unit specified by `unit`."
289
289
},
290
290
"min": {
291
291
"type": "integer",
292
292
"minimum": 0,
293
293
"description": "Lower bound, in the unit specified by `unit`."
294
294
},
295
295
"unit": {
296
296
"type": "string",
297
297
"description": "Unit of `min`/`max`. `g_per_plant` = grams per plant; `g_per_m2` = grams per square meter; `oz_per_plant` = ounces per plant; `oz_per_m2` = ounces per square meter. `knownValues` rather than a closed enum so additive unit expansions don't require a lexicon revision.",
298
298
"knownValues": [
299
299
"g_per_plant",
300
300
"g_per_m2",
301
301
"oz_per_plant",
302
302
"oz_per_m2"
303
303
]
304
304
}
305
305
},
306
306
"description": "A min/max integer range plus a `unit` enum so the integer is unambiguous. Single-value quotes set min == max. The unit is what makes the integer interpretable — breeders quote yield in mutually incompatible units (per-plant vs per-m², grams vs ounces), so consumers must read `unit` before interpreting `min`/`max`."
307
307
},
308
308
"heightRange": {
309
309
"type": "object",
310
310
"properties": {
311
311
"max": {
312
312
"type": "integer",
313
313
"maximum": 2000,
314
314
"minimum": 0,
315
315
"description": "Upper bound in centimeters."
316
316
},
317
317
"min": {
318
318
"type": "integer",
319
319
"maximum": 2000,
320
320
"minimum": 0,
321
321
"description": "Lower bound in centimeters."
322
322
}
323
323
},
324
324
"description": "A min/max integer range expressed in whole centimeters. Both bounds independently optional; single-value quotes set min == max. Centimeters are a natural integer unit and need no further scaling."
325
325
},
326
326
"cannabinoidRange": {
327
327
"type": "object",
328
328
"properties": {
329
329
"max": {
330
330
"type": "integer",
331
331
"maximum": 10000,
332
332
"minimum": 0,
333
333
"description": "Upper bound, in hundredths of a percent (e.g., 2700 = 27.00%)."
334
334
},
335
335
"min": {
336
336
"type": "integer",
337
337
"maximum": 10000,
338
338
"minimum": 0,
339
339
"description": "Lower bound, in hundredths of a percent (e.g., 2000 = 20.00%)."
340
340
}
341
341
},
342
342
"description": "A min/max integer range for a cannabinoid percentage, expressed in hundredths of a percent (e.g., 2450 = 24.50%). Both bounds are independently optional; either may be omitted when only one is known."
343
343
}
344
344
},
345
345
"$type": "com.atproto.lexicon.schema",
346
346
"lexicon": 1
347
347
}