games.atmosphere.define
Schema Diff
+105 -19
Compatibility Analysis
Breaking Changes Detected
8 breaking changes, 33 non-breaking changes.
Breaking Changes (8)
- RequiredEdgeAdded RequiredEdgeAdded { vertex_id: "games.atmosphere.define#recordUse", src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.role", kind: "prop", name: Some("role") }
- RequiredEdgeAdded RequiredEdgeAdded { vertex_id: "games.atmosphere.define#recordUse", src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.nsid", kind: "prop", name: Some("nsid") }
- RemovedVertex RemovedVertex { vertex_id: "games.atmosphere.define:body.migratedFromAtUri" }
- RemovedVertex RemovedVertex { vertex_id: "games.atmosphere.define:body.replaces" }
- RemovedVertex RemovedVertex { vertex_id: "games.atmosphere.define:body.replaces:items" }
- RemovedEdge RemovedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.migratedFromAtUri", kind: "prop", name: Some("migratedFromAtUri") }
- RemovedEdge RemovedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.replaces", kind: "prop", name: Some("replaces") }
- RemovedEdge RemovedEdge { src: "games.atmosphere.define:body.replaces", tgt: "games.atmosphere.define:body.replaces:items", kind: "items", name: None }
Non-Breaking Changes (33)
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.acquire" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.authority" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.display" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.display:items" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.filters" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.filters:items" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.label" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.nsid" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.path" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.purpose" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.rkey" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.role" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#recordUse.verify" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define:body.uses" }
- AddedVertex AddedVertex { vertex_id: "games.atmosphere.define:body.uses:items" }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.acquire", kind: "prop", name: Some("acquire") }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.authority", kind: "prop", name: Some("authority") }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.display", kind: "prop", name: Some("display") }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.filters", kind: "prop", name: Some("filters") }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.label", kind: "prop", name: Some("label") }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.path", kind: "prop", name: Some("path") }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.purpose", kind: "prop", name: Some("purpose") }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.rkey", kind: "prop", name: Some("rkey") }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.verify", kind: "prop", name: Some("verify") }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse.display", tgt: "games.atmosphere.define#recordUse.display:items", kind: "items", name: None }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse.display:items", tgt: "games.atmosphere.define#referenceDisplay", kind: "ref", name: None }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse.filters", tgt: "games.atmosphere.define#recordUse.filters:items", kind: "items", name: None }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse.filters:items", tgt: "games.atmosphere.define#filter", kind: "ref", name: None }
- AddedEdge AddedEdge { src: "games.atmosphere.define#recordUse.verify", tgt: "games.atmosphere.define#rule", kind: "ref", name: None }
- AddedEdge AddedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.uses", kind: "prop", name: Some("uses") }
- AddedEdge AddedEdge { src: "games.atmosphere.define:body.uses", tgt: "games.atmosphere.define:body.uses:items", kind: "items", name: None }
- AddedEdge AddedEdge { src: "games.atmosphere.define:body.uses:items", tgt: "games.atmosphere.define#recordUse", kind: "ref", name: None }
Migration Guidance
Removed Elements
RemovedVertex { vertex_id: "games.atmosphere.define:body.migratedFromAtUri" }RemovedVertex { vertex_id: "games.atmosphere.define:body.replaces" }RemovedVertex { vertex_id: "games.atmosphere.define:body.replaces:items" }
Added Elements
AddedVertex { vertex_id: "games.atmosphere.define#recordUse" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.acquire" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.authority" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.display" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.display:items" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.filters" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.filters:items" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.label" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.nsid" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.path" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.purpose" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.rkey" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.role" }AddedVertex { vertex_id: "games.atmosphere.define#recordUse.verify" }AddedVertex { vertex_id: "games.atmosphere.define:body.uses" }AddedVertex { vertex_id: "games.atmosphere.define:body.uses:items" }
Additional Notes
- Breaking: RequiredEdgeAdded { vertex_id: "games.atmosphere.define#recordUse", src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.role", kind: "prop", name: Some("role") }
- Breaking: RequiredEdgeAdded { vertex_id: "games.atmosphere.define#recordUse", src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.nsid", kind: "prop", name: Some("nsid") }
- Breaking: RemovedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.migratedFromAtUri", kind: "prop", name: Some("migratedFromAtUri") }
- Breaking: RemovedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.replaces", kind: "prop", name: Some("replaces") }
- Breaking: RemovedEdge { src: "games.atmosphere.define:body.replaces", tgt: "games.atmosphere.define:body.replaces:items", kind: "items", name: None }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.acquire", kind: "prop", name: Some("acquire") }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.authority", kind: "prop", name: Some("authority") }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.display", kind: "prop", name: Some("display") }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.filters", kind: "prop", name: Some("filters") }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.label", kind: "prop", name: Some("label") }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.path", kind: "prop", name: Some("path") }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.purpose", kind: "prop", name: Some("purpose") }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.rkey", kind: "prop", name: Some("rkey") }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse", tgt: "games.atmosphere.define#recordUse.verify", kind: "prop", name: Some("verify") }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse.display", tgt: "games.atmosphere.define#recordUse.display:items", kind: "items", name: None }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse.display:items", tgt: "games.atmosphere.define#referenceDisplay", kind: "ref", name: None }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse.filters", tgt: "games.atmosphere.define#recordUse.filters:items", kind: "items", name: None }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse.filters:items", tgt: "games.atmosphere.define#filter", kind: "ref", name: None }
- Non-breaking: AddedEdge { src: "games.atmosphere.define#recordUse.verify", tgt: "games.atmosphere.define#rule", kind: "ref", name: None }
- Non-breaking: AddedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.uses", kind: "prop", name: Some("uses") }
- Non-breaking: AddedEdge { src: "games.atmosphere.define:body.uses", tgt: "games.atmosphere.define:body.uses:items", kind: "items", name: None }
- Non-breaking: AddedEdge { src: "games.atmosphere.define:body.uses:items", tgt: "games.atmosphere.define#recordUse", kind: "ref", name: None }
1
1
{
2
2
"id": "games.atmosphere.define",
3
3
"defs": {
4
4
"all": {
5
5
"type": "object",
6
6
"required": [
7
7
"check",
8
8
"rules"
9
9
],
10
10
"properties": {
11
11
"check": {
12
12
"type": "string",
13
13
"const": "all"
14
14
},
15
15
"label": {
16
16
"type": "string",
17
17
"maxLength": 120
18
18
},
19
19
"rules": {
20
20
"type": "array",
21
21
"items": {
22
22
"ref": "#rule",
23
23
"type": "ref"
24
24
},
25
25
"maxLength": 16,
26
26
"minLength": 1
27
27
},
28
28
"acquire": {
29
29
"type": "string",
30
30
"format": "uri"
31
31
}
32
32
},
33
33
"description": "Logical AND. Passes only when every sub-rule passes."
34
34
},
35
35
"any": {
36
36
"type": "object",
37
37
"required": [
38
38
"check",
39
39
"rules"
40
40
],
41
41
"properties": {
42
42
"check": {
43
43
"type": "string",
44
44
"const": "any"
45
45
},
46
46
"label": {
47
47
"type": "string",
48
48
"maxLength": 120
49
49
},
50
50
"rules": {
51
51
"type": "array",
52
52
"items": {
53
53
"ref": "#rule",
54
54
"type": "ref"
55
55
},
56
56
"maxLength": 16,
57
57
"minLength": 1
58
58
},
59
59
"acquire": {
60
60
"type": "string",
61
61
"format": "uri"
62
62
}
63
63
},
64
64
"description": "Logical OR. Passes when at least one sub-rule passes, e.g. 'holds a sheet in any of these systems'."
65
65
},
66
66
"not": {
67
67
"type": "object",
68
68
"required": [
69
69
"check",
70
70
"rule"
71
71
],
72
72
"properties": {
73
73
"rule": {
74
74
"ref": "#rule",
75
75
"type": "ref"
76
76
},
77
77
"check": {
78
78
"type": "string",
79
79
"const": "not"
80
80
},
81
81
"label": {
82
82
"type": "string",
83
83
"maxLength": 120
84
84
}
85
85
},
86
86
"description": "Logical NOT. Passes only when the sub-rule fails. Use for exclusion gates: 'has not already claimed this reward'."
87
87
},
88
88
"main": {
89
89
"key": "any",
90
90
"type": "record",
91
91
"record": {
92
92
"type": "object",
93
93
"required": [
94
94
"name",
95
95
"url",
96
96
"publisher",
97
97
"category",
98
98
"createdAt"
99
99
],
100
100
"properties": {
101
101
"url": {
102
102
"type": "string",
103
103
"format": "uri",
104
104
"description": "URL at which the game is played, downloaded, or accessed. This is a launch location, not the record's identity."
105
105
},
106
106
"icon": {
107
107
"type": "blob",
108
108
"accept": [
109
109
"image/png",
110
110
"image/webp"
111
111
],
112
112
"maxSize": 512000,
113
113
"description": "Square icon for compact display contexts. Recommended 256x256 px."
114
114
},
115
115
"name": {
116
116
"type": "string",
117
117
"maxLength": 100,
118
118
"description": "Display name of the experience as shown in portal listings."
119
119
},
120
120
"tags": {
121
121
"type": "array",
122
122
"items": {
123
123
"type": "string",
124
124
"maxLength": 32
125
125
},
126
126
"maxLength": 12,
127
127
"description": "Freeform tags for search and discovery (e.g. 'arcade', 'pixel art', 'multiplayer')."
128
128
},
129
+
"uses": {
130
+
"type": "array",
131
+
"items": {
132
+
"ref": "#recordUse",
133
+
"type": "ref"
134
+
},
135
+
"maxLength": 32,
136
+
"description": "Non-required records this game uses, understands, produces, validates, or wants portals to read. Use requires[] only for hard gates. Use uses[] for everything else: optional gameplay perks, character imports, cosmetics, bonuses, interoperability, stats, saves, activity, sessions, replays, leaderboards, labels, grants, validation, inventory, or profile context. The role and purpose fields explain whether the use is a player-facing benefit, a game-authored record, or a portal-readable display surface."
137
+
},
129
138
"cover": {
130
139
"type": "blob",
131
140
"accept": [
132
141
"image/png",
133
142
"image/jpeg",
134
143
"image/webp"
135
144
],
136
145
"maxSize": 4000000,
137
146
"description": "Primary cover image. Portals display this as the card thumbnail. Recommended ratio 16:9."
138
147
},
139
148
"media": {
140
149
"type": "array",
141
150
"items": {
142
151
"ref": "#mediaItem",
143
152
"type": "ref"
144
153
},
145
154
"maxLength": 24,
146
155
"description": "Extended media gallery, screenshots, banners, key art, and posters a portal can show on a detail page. 'cover' and 'icon' above stay the primary card images; this is the deeper set, mirroring the rich galleries dedicated game catalogs keep."
147
156
},
148
157
"status": {
149
158
"type": "string",
150
159
"description": "Current availability. Known values: 'live', 'beta', 'coming' (announced but not yet playable), 'archived' (no longer available). Open set; portals may badge or filter on these.",
151
160
"knownValues": [
152
161
"live",
153
162
"beta",
154
163
"coming",
155
164
"archived"
156
165
]
157
166
},
158
167
"catalog": {
159
168
"type": "array",
160
169
"items": {
161
170
"ref": "#catalogRef",
162
171
"type": "ref"
163
172
},
164
173
"maxLength": 8,
165
174
"description": "Optional links to records in other game catalogs that describe this same title, most often a Pentaract (games.gamesgamesgamesgames.game) entry. This is how a definition borrows rich metadata a dedicated catalog already maintains, genres, platforms, screenshots, release dates, ratings, instead of duplicating it here. Purely supplementary: evaluation never reads these, and the experience is complete without them. Cooperate, don't re-enter."
166
175
},
167
176
"tagline": {
168
177
"type": "string",
169
178
"maxLength": 200,
170
179
"description": "One-line summary shown beneath the title in card views."
171
180
},
172
181
"category": {
173
182
"type": "string",
174
183
"maxLength": 30,
175
184
"description": "Broad type of experience, used for portal filtering and display. Known values: 'browser' (plays in a web browser), 'download' (downloaded and run locally), 'physical' (tabletop or in-person), 'other'. This is an open set, portals should tolerate values they do not recognise.",
176
185
"knownValues": [
177
186
"browser",
178
187
"download",
179
188
"physical",
180
189
"other"
181
190
]
182
191
},
183
192
"includes": {
184
193
"type": "array",
185
194
"items": {
186
195
"ref": "#lexiconRef",
187
196
"type": "ref"
188
197
},
189
198
"maxLength": 24,
190
-
"description": "Optional player-facing records that change or enrich the experience when present. Use includes[] when the game itself can do something extra for the player: import a character, unlock a cosmetic, grant a bonus, recognise an item, or enable an interoperability perk. An include may have verify logic so portals can badge whether that optional benefit is available. Includes never gate basic play; put hard gates in requires[]. Do not use includes[] merely to tell portals how to read stats, saves, activity, or leaderboard data; use references[] for that."
191
-
},
192
-
"replaces": {
193
-
"type": "array",
194
-
"items": {
195
-
"type": "string",
196
-
"format": "at-uri"
197
-
},
198
-
"maxLength": 12,
199
-
"description": "Definition records this record intentionally supersedes. Used for ownership claims, rekeys, and mergers without encoding platform-specific migration language. A portal may trust a claim only when the replaced source definition names this record's repo DID as developer.did."
199
+
"description": "Deprecated compatibility field. Use uses[] instead. Older records used includes[] for optional player-facing records that change or enrich gameplay when present."
200
200
},
201
201
"requires": {
202
202
"type": "array",
203
203
"items": {
204
204
"ref": "#rule",
205
205
"type": "ref"
206
206
},
207
207
"maxLength": 16,
208
208
"description": "Hard playability requirements. Every rule must pass before the game can be played as declared. Do not put recommendations, bonuses, or merely supported records here. Portals surface failures as actionable prompts using each rule's 'acquire' link."
209
209
},
210
210
"trailers": {
211
211
"type": "array",
212
212
"items": {
213
213
"ref": "#video",
214
214
"type": "ref"
215
215
},
216
216
"maxLength": 8,
217
217
"description": "Video trailers and gameplay clips. Hosted externally (YouTube, Vimeo, Twitch) or linked directly so portals can embed or link them."
218
218
},
219
219
"createdAt": {
220
220
"type": "string",
221
221
"format": "datetime"
222
222
},
223
223
"developer": {
224
224
"ref": "#party",
225
225
"type": "ref",
226
226
"description": "The studio or individual who built the experience. Prefer a DID when available so the developer is verifiable on the network."
227
227
},
228
228
"legacyIds": {
229
229
"type": "array",
230
230
"items": {
231
231
"type": "string",
232
232
"maxLength": 80
233
233
},
234
234
"maxLength": 12,
235
235
"description": "Stable identifiers used by older catalogues, score readers, or inventory integrations for this same game. These are compatibility aliases, not canonical identity."
236
236
},
237
237
"publisher": {
238
238
"type": "string",
239
239
"format": "at-uri",
240
240
"description": "Stable AT-URI identifying the account that publishes and maintains this definition, normally at:// followed by the publishing DID. It must name the same repository that contains this record; portals should reject a mismatch."
241
241
},
242
242
"updatedAt": {
243
243
"type": "string",
244
244
"format": "datetime"
245
245
},
246
246
"references": {
247
247
"type": "array",
248
248
"items": {
249
249
"ref": "#reference",
250
250
"type": "ref"
251
251
},
252
252
"maxLength": 24,
253
-
"description": "Non-gating records that describe where this game's portable data lives and how portals or tools may read it. Use references[] for stats, saves, activity, sessions, replays, leaderboard entries, validation records, labels, grants, profiles, or other context that summarizes play. A reference is a reading recipe, not a player benefit: it may carry display metrics and an optional verify rule for trusted presentation, but it does not by itself say the game uses that record during play. Put hard access rules in requires[] and optional gameplay/perk integrations in includes[]."
253
+
"description": "Deprecated compatibility field. Use uses[] instead. Older records used references[] for non-gating stat, save, activity, replay, leaderboard, validation, label, grant, profile, or portal-readable context."
254
254
},
255
255
"releasedAt": {
256
256
"type": "string",
257
257
"format": "datetime",
258
258
"description": "When this experience became publicly available."
259
259
},
260
260
"description": {
261
261
"type": "string",
262
262
"maxLength": 2000,
263
263
"description": "Full description of the experience. Plain text; portals may render line breaks."
264
264
},
265
265
"statNamespace": {
266
266
"type": "string",
267
267
"maxLength": 80,
268
268
"description": "Deprecated compatibility hint. Use references[] with role 'activity' or 'stats' to describe player activity records and display metrics."
269
-
},
270
-
"migratedFromAtUri": {
271
-
"type": "string",
272
-
"format": "at-uri",
273
-
"description": "Deprecated compatibility field. Use 'replaces' for clean definition lineage. Exact URI of a previous definition republished into this repository during an ownership claim. Portals must accept this lineage only when the source definition names this repository DID as developer.did; lineage by itself is not proof of ownership."
274
269
}
275
270
}
276
271
},
277
272
"description": "Published to the publisher's own repository, one record per game. Use a stable, readable record key and update that same record in place. The record's at:// URI is its canonical identity; 'url' is an access location and may change. A portal must not merge definitions solely because their URLs match."
278
273
},
279
274
"rule": {
280
275
"type": "object",
281
276
"required": [
282
277
"check"
283
278
],
284
279
"properties": {
285
280
"op": {
286
281
"type": "string",
287
282
"description": "For field: comparison operator.",
288
283
"knownValues": [
289
284
"exists",
290
285
"notExists",
291
286
"isViewer",
292
287
"eq",
293
288
"neq",
294
289
"gt",
295
290
"gte",
296
291
"lt",
297
292
"lte"
298
293
]
299
294
},
300
295
"nsid": {
301
296
"type": "string",
302
297
"format": "nsid",
303
298
"description": "Collection read by record, field, matches, or vouch."
304
299
},
305
300
"rkey": {
306
301
"type": "string",
307
302
"maxLength": 50,
308
303
"description": "For field: record key, default 'self'; 'any' scans the collection."
309
304
},
310
305
"rule": {
311
306
"ref": "#rule",
312
307
"type": "ref",
313
308
"description": "For not: nested rule to negate."
314
309
},
315
310
"check": {
316
311
"type": "string",
317
312
"description": "Rule discriminator. Known values are open for forward compatibility; an unknown value must not pass.",
318
313
"knownValues": [
319
314
"record",
320
315
"field",
321
316
"matches",
322
317
"vouch",
323
318
"all",
324
319
"any",
325
320
"not"
326
321
]
327
322
},
328
323
"field": {
329
324
"type": "string",
330
325
"maxLength": 200,
331
326
"description": "For field: dot-path to test."
332
327
},
333
328
"label": {
334
329
"type": "string",
335
330
"maxLength": 120,
336
331
"description": "Human-readable requirement."
337
332
},
338
333
"rules": {
339
334
"type": "array",
340
335
"items": {
341
336
"ref": "#rule",
342
337
"type": "ref"
343
338
},
344
339
"maxLength": 16,
345
340
"minLength": 1,
346
341
"description": "For all or any: nested rules."
347
342
},
348
343
"acquire": {
349
344
"type": "string",
350
345
"format": "uri",
351
346
"description": "URL where a player can fulfil this requirement."
352
347
},
353
348
"filters": {
354
349
"type": "array",
355
350
"items": {
356
351
"ref": "#filter",
357
352
"type": "ref"
358
353
},
359
354
"maxLength": 8,
360
355
"minLength": 1,
361
356
"description": "For matches or vouch: conditions that must hold on one record."
362
357
},
363
358
"authority": {
364
359
"type": "string",
365
360
"format": "did",
366
361
"description": "For vouch: stable DID of the record issuer."
367
362
},
368
363
"valueString": {
369
364
"type": "string",
370
365
"maxLength": 1000,
371
366
"description": "String comparison value."
372
367
},
373
368
"valueBoolean": {
374
369
"type": "boolean",
375
370
"description": "Boolean comparison value."
376
371
},
377
372
"valueInteger": {
378
373
"type": "integer",
379
374
"description": "Integer comparison value."
380
375
}
381
376
},
382
377
"description": "One recursive playability rule, discriminated by 'check'. Lexicon does not permit a union as a top-level definition, so this object carries the fields used by all seven variants. Evaluators enforce each variant's required fields: record(check,nsid); field(check,nsid,field,op); matches(check,nsid,filters); vouch(check,authority,nsid); all/any(check,rules); not(check,rule). Unknown check values evaluate as unknown, never as pass."
383
378
},
384
379
"field": {
385
380
"type": "object",
386
381
"required": [
387
382
"check",
388
383
"nsid",
389
384
"field",
390
385
"op"
391
386
],
392
387
"properties": {
393
388
"op": {
394
389
"type": "string",
395
390
"description": "How to test the field. exists/notExists check presence; eq/neq/gt/gte/lt/lte compare to a value; isViewer matches the signed-in player by their account ID (DID) and takes no value.",
396
391
"knownValues": [
397
392
"exists",
398
393
"notExists",
399
394
"isViewer",
400
395
"eq",
401
396
"neq",
402
397
"gt",
403
398
"gte",
404
399
"lt",
405
400
"lte"
406
401
]
407
402
},
408
403
"nsid": {
409
404
"type": "string",
410
405
"format": "nsid"
411
406
},
412
407
"rkey": {
413
408
"type": "string",
414
409
"maxLength": 50,
415
410
"description": "Record key. Defaults to 'self'. Use 'any' to pass if any record in the collection satisfies the condition."
416
411
},
417
412
"check": {
418
413
"type": "string",
419
414
"const": "field"
420
415
},
421
416
"field": {
422
417
"type": "string",
423
418
"maxLength": 200,
424
419
"description": "Dot-path to the property to evaluate (e.g. 'score.total', 'level'). Array traversal is not supported."
425
420
},
426
421
"label": {
427
422
"type": "string",
428
423
"maxLength": 120
429
424
},
430
425
"acquire": {
431
426
"type": "string",
432
427
"format": "uri"
433
428
},
434
429
"valueString": {
435
430
"type": "string",
436
431
"maxLength": 1000,
437
432
"description": "String comparison value. For value-taking operators, set exactly one of valueString, valueInteger, or valueBoolean."
438
433
},
439
434
"valueBoolean": {
440
435
"type": "boolean",
441
436
"description": "Boolean comparison value."
442
437
},
443
438
"valueInteger": {
444
439
"type": "integer",
445
440
"description": "Integer comparison value. Use for numeric gt/gte/lt/lte checks."
446
441
}
447
442
},
448
443
"description": "Fetches a record from the player's repository and tests one field. Covers required fields, score thresholds, flags, and enum matches."
449
444
},
450
445
"party": {
451
446
"type": "object",
452
447
"required": [
453
448
"name"
454
449
],
455
450
"properties": {
456
451
"did": {
457
452
"type": "string",
458
453
"format": "did",
459
-
"description": "AT Protocol DID of this entity's account, if they have one. On an atmosphere.games-managed provisional definition, this is also the sole DID eligible to claim it by republishing with exact replaces[] lineage."
454
+
"description": "AT Protocol DID of this entity's account, if they have one. On an atmosphere.games-managed provisional definition, this is also the DID expected to publish the canonical developer-owned definition."
460
455
},
461
456
"url": {
462
457
"type": "string",
463
458
"format": "uri",
464
459
"description": "Website for this entity."
465
460
},
466
461
"name": {
467
462
"type": "string",
468
463
"maxLength": 100,
469
464
"description": "Display name of the entity."
470
465
}
471
466
},
472
467
"description": "A named entity (developer, publisher, studio) associated with the experience."
473
468
},
474
469
"video": {
475
470
"type": "object",
476
471
"required": [
477
472
"title"
478
473
],
479
474
"properties": {
480
475
"url": {
481
476
"type": "string",
482
477
"format": "uri",
483
478
"description": "Full URL to the video, used when no platform/videoId is given."
484
479
},
485
480
"title": {
486
481
"type": "string",
487
482
"maxLength": 120,
488
483
"description": "Label for the clip (e.g. 'Launch Trailer')."
489
484
},
490
485
"videoId": {
491
486
"type": "string",
492
487
"maxLength": 120,
493
488
"description": "Platform video id (e.g. a YouTube id). Combined with 'platform' to build an embed."
494
489
},
495
490
"platform": {
496
491
"type": "string",
497
492
"description": "Where the video lives. 'direct' means the url points straight at a video file or page.",
498
493
"knownValues": [
499
494
"youtube",
500
495
"vimeo",
501
496
"twitch",
502
497
"direct"
503
498
]
504
499
},
505
500
"thumbnail": {
506
501
"type": "blob",
507
502
"accept": [
508
503
"image/png",
509
504
"image/jpeg",
510
505
"image/webp"
511
506
],
512
507
"maxSize": 2000000,
513
508
"description": "Optional still frame shown before the video plays."
514
509
}
515
510
},
516
511
"description": "A trailer or gameplay clip. Provide a platform + videoId (preferred, so portals embed natively) or a direct url."
517
512
},
518
513
"vouch": {
519
514
"type": "object",
520
515
"required": [
521
516
"check",
522
517
"authority",
523
518
"nsid"
524
519
],
525
520
"properties": {
526
521
"nsid": {
527
522
"type": "string",
528
523
"format": "nsid",
529
524
"description": "The kind of record to look for in that account's repo (e.g. equipment.rpg.give)."
530
525
},
531
526
"check": {
532
527
"type": "string",
533
528
"const": "vouch"
534
529
},
535
530
"label": {
536
531
"type": "string",
537
532
"maxLength": 120
538
533
},
539
534
"acquire": {
540
535
"type": "string",
541
536
"format": "uri"
542
537
},
543
538
"filters": {
544
539
"type": "array",
545
540
"items": {
546
541
"ref": "#filter",
547
542
"type": "ref"
548
543
},
549
544
"maxLength": 8,
550
545
"description": "Conditions the record must meet, written exactly like a matches rule. Put the 'isViewer' operator on the field that names the player (e.g. recipient) to require the record reference the signed-in player; add value conditions (e.g. item equals jet_pack) to pin a specific grant. With no filters, any record of this kind on the authority's repo passes."
551
546
},
552
547
"authority": {
553
548
"type": "string",
554
549
"format": "did",
555
550
"description": "Stable DID of the account that issues the record (for example an item provider, guild, or tournament organiser). Handles are not accepted because they are mutable. A player can never be their own authority."
556
551
}
557
552
},
558
553
"description": "Passes when a trusted account's repo holds a record that meets the given conditions, optionally one that references the signed-in player. Because the record lives on the issuer's repo, not the player's, it cannot be forged. Use it for item grants, awards, memberships, approvals, licences, or any record one account publishes about another. Example: 'games.protoimsg.app has an equipment.rpg.give whose recipient is this player and whose item is jet_pack.'"
559
554
},
560
555
"filter": {
561
556
"type": "object",
562
557
"required": [
563
558
"field",
564
559
"op"
565
560
],
566
561
"properties": {
567
562
"op": {
568
563
"type": "string",
569
564
"description": "How to test the field. exists/notExists check presence; eq/neq/gt/gte/lt/lte compare to a value; isViewer matches the signed-in player by their account ID (DID) and takes no value.",
570
565
"knownValues": [
571
566
"exists",
572
567
"notExists",
573
568
"isViewer",
574
569
"eq",
575
570
"neq",
576
571
"gt",
577
572
"gte",
578
573
"lt",
579
574
"lte"
580
575
]
581
576
},
582
577
"field": {
583
578
"type": "string",
584
579
"maxLength": 200,
585
580
"description": "Dot-path to the field."
586
581
},
587
582
"valueString": {
588
583
"type": "string",
589
584
"maxLength": 1000,
590
585
"description": "String comparison value. For value-taking operators, set exactly one typed value property."
591
586
},
592
587
"valueBoolean": {
593
588
"type": "boolean",
594
589
"description": "Boolean comparison value."
595
590
},
596
591
"valueInteger": {
597
592
"type": "integer",
598
593
"description": "Integer comparison value. Use for numeric gt/gte/lt/lte checks."
599
594
}
600
595
},
601
596
"description": "A single condition on a record's contents, used by matches and vouch. Most operators compare the field to a value. The special 'isViewer' operator takes no value and passes when the field identifies the signed-in player by their account ID (DID). It accepts the DID directly or an at:// URI whose repo is the player, and never matches on a handle, since handles can change. This is how a static definition points at whoever is reading it."
602
597
},
603
598
"record": {
604
599
"type": "object",
605
600
"required": [
606
601
"check",
607
602
"nsid"
608
603
],
609
604
"properties": {
610
605
"nsid": {
611
606
"type": "string",
612
607
"format": "nsid",
613
608
"description": "Collection to check on the player's PDS (e.g. 'actor.rpg.sprite')."
614
609
},
615
610
"check": {
616
611
"type": "string",
617
612
"const": "record"
618
613
},
619
614
"label": {
620
615
"type": "string",
621
616
"maxLength": 120,
622
617
"description": "Human-readable requirement shown when gated (e.g. 'An rpg.actor sprite')."
623
618
},
624
619
"acquire": {
625
620
"type": "string",
626
621
"format": "uri",
627
622
"description": "URL to send players who fail, so they can fulfil the requirement."
628
623
}
629
624
},
630
625
"description": "Passes if the player's PDS holds at least one record in the given collection. The simplest gate: 'does this person participate in this lexicon at all?'"
631
626
},
632
627
"matches": {
633
628
"type": "object",
634
629
"required": [
635
630
"check",
636
631
"nsid",
637
632
"filters"
638
633
],
639
634
"properties": {
640
635
"nsid": {
641
636
"type": "string",
642
637
"format": "nsid"
643
638
},
644
639
"check": {
645
640
"type": "string",
646
641
"const": "matches"
647
642
},
648
643
"label": {
649
644
"type": "string",
650
645
"maxLength": 120
651
646
},
652
647
"acquire": {
653
648
"type": "string",
654
649
"format": "uri"
655
650
},
656
651
"filters": {
657
652
"type": "array",
658
653
"items": {
659
654
"ref": "#filter",
660
655
"type": "ref"
661
656
},
662
657
"maxLength": 8,
663
658
"minLength": 1,
664
659
"description": "All filters must match on the same record (AND semantics within one record)."
665
660
}
666
661
},
667
662
"description": "Scans the player's collection and passes if any single record satisfies all filters simultaneously. Use to find a record by content rather than by key, e.g. 'holds item X', 'has membership tier Y'."
668
663
},
669
664
"mediaItem": {
670
665
"type": "object",
671
666
"required": [
672
667
"image"
673
668
],
674
669
"properties": {
675
670
"alt": {
676
671
"type": "string",
677
672
"maxLength": 300,
678
673
"description": "Accessibility caption describing the image."
679
674
},
680
675
"type": {
681
676
"type": "string",
682
677
"description": "What kind of image this is, so portals can place it (e.g. 'banner' across the top, 'screenshot' in a gallery).",
683
678
"knownValues": [
684
679
"screenshot",
685
680
"banner",
686
681
"artwork",
687
682
"poster",
688
683
"background",
689
684
"logo",
690
685
"thumbnail"
691
686
]
692
687
},
693
688
"image": {
694
689
"type": "blob",
695
690
"accept": [
696
691
"image/png",
697
692
"image/jpeg",
698
693
"image/webp",
699
694
"image/gif"
700
695
],
701
696
"maxSize": 8000000,
702
697
"description": "The image blob, stored on the developer's PDS."
703
698
},
704
699
"width": {
705
700
"type": "integer",
706
701
"description": "Pixel width, if known, lets portals reserve layout space before the blob loads."
707
702
},
708
703
"height": {
709
704
"type": "integer",
710
705
"description": "Pixel height, if known."
711
706
}
712
707
},
713
708
"description": "One image in the experience's media gallery."
714
709
},
710
+
"recordUse": {
711
+
"type": "object",
712
+
"required": [
713
+
"role",
714
+
"nsid"
715
+
],
716
+
"properties": {
717
+
"nsid": {
718
+
"type": "string",
719
+
"format": "nsid",
720
+
"description": "Collection to read or use."
721
+
},
722
+
"path": {
723
+
"type": "string",
724
+
"maxLength": 200,
725
+
"description": "Optional dot-path to the relevant payload within the record, e.g. 'data' inside actor.rpg.stats/clunscannon."
726
+
},
727
+
"rkey": {
728
+
"type": "string",
729
+
"maxLength": 50,
730
+
"description": "Record key to read from a player's repo. Defaults to 'self'; use 'any' when a portal should scan the collection."
731
+
},
732
+
"role": {
733
+
"type": "string",
734
+
"description": "How this record is used. Player-benefit roles such as perk, character, cosmetic, bonus, and interoperability describe optional game behavior. Data roles such as stats, activity, leaderboard, save, replay, session, profile, inventory, grant, validation, and label describe records portals/tools may read or display.",
735
+
"knownValues": [
736
+
"perk",
737
+
"character",
738
+
"cosmetic",
739
+
"bonus",
740
+
"interoperability",
741
+
"stats",
742
+
"activity",
743
+
"leaderboard",
744
+
"save",
745
+
"replay",
746
+
"session",
747
+
"profile",
748
+
"inventory",
749
+
"grant",
750
+
"validation",
751
+
"label",
752
+
"other"
753
+
]
754
+
},
755
+
"label": {
756
+
"type": "string",
757
+
"maxLength": 120,
758
+
"description": "Human label for this use."
759
+
},
760
+
"verify": {
761
+
"ref": "#rule",
762
+
"type": "ref",
763
+
"description": "Optional rule that detects, authenticates, or validates this use. For player-benefit roles, it badges whether the benefit is available. For data roles, it badges whether the displayed record is trusted."
764
+
},
765
+
"acquire": {
766
+
"type": "string",
767
+
"format": "uri",
768
+
"description": "Optional URL where a player can create, obtain, or manage this record."
769
+
},
770
+
"display": {
771
+
"type": "array",
772
+
"items": {
773
+
"ref": "#referenceDisplay",
774
+
"type": "ref"
775
+
},
776
+
"maxLength": 8,
777
+
"description": "Small metrics a portal may render on cards or profiles."
778
+
},
779
+
"filters": {
780
+
"type": "array",
781
+
"items": {
782
+
"ref": "#filter",
783
+
"type": "ref"
784
+
},
785
+
"maxLength": 8,
786
+
"description": "Optional filters used when rkey is 'any' or authority is set. Conditions must match on one record."
787
+
},
788
+
"purpose": {
789
+
"type": "string",
790
+
"maxLength": 300,
791
+
"description": "Plain-language reason this record matters. This should say whether it unlocks a benefit, imports player data, stores progress, validates a score, or powers portal display."
792
+
},
793
+
"authority": {
794
+
"type": "string",
795
+
"format": "did",
796
+
"description": "When the referenced record lives in a trusted authority repo instead of the player's repo, this DID names that repo."
797
+
}
798
+
},
799
+
"description": "One non-required AT Protocol record relationship for this game. This single shape covers optional gameplay benefits and portal-readable data. Examples: role 'perk' for a Jetpack item that changes play; role 'character' for a sprite the game can import; role 'stats' for a score record portals can display; role 'validation' for an authority record that confirms a stat. Hard gates still belong in requires[]."
800
+
},
715
801
"reference": {
716
802
"type": "object",
717
803
"required": [
718
804
"role",
719
805
"nsid"
720
806
],
721
807
"properties": {
722
808
"nsid": {
723
809
"type": "string",
724
810
"format": "nsid",
725
811
"description": "Collection to read."
726
812
},
727
813
"path": {
728
814
"type": "string",
729
815
"maxLength": 200,
730
816
"description": "Optional dot-path to the relevant payload within the record, e.g. 'clunscannon' inside actor.rpg.stats/self."
731
817
},
732
818
"rkey": {
733
819
"type": "string",
734
820
"maxLength": 50,
735
821
"description": "Record key to read from a player's repo. Defaults to 'self'; use 'any' when a portal should scan the collection."
736
822
},
737
823
"role": {
738
824
"type": "string",
739
825
"description": "Why this collection is relevant. 'stats' and 'leaderboard' are for card summaries; 'activity' for player counts/recent activity; 'validation' for trusted attestations such as actor.rpg.master.",
740
826
"knownValues": [
741
827
"activity",
742
828
"stats",
743
829
"leaderboard",
744
830
"save",
745
831
"replay",
746
832
"session",
747
833
"profile",
748
834
"inventory",
749
835
"grant",
750
836
"validation",
751
837
"label",
752
838
"other"
753
839
]
754
840
},
755
841
"label": {
756
842
"type": "string",
757
843
"maxLength": 120,
758
844
"description": "Human label for this reference."
759
845
},
760
846
"verify": {
761
847
"ref": "#rule",
762
848
"type": "ref",
763
849
"description": "Optional rule that validates this reference before displaying it as trusted. Example: an actor.rpg.master vouch for a player's actor.rpg.stats system."
764
850
},
765
851
"acquire": {
766
852
"type": "string",
767
853
"format": "uri",
768
854
"description": "Optional URL where a player can create or manage this record."
769
855
},
770
856
"display": {
771
857
"type": "array",
772
858
"items": {
773
859
"ref": "#referenceDisplay",
774
860
"type": "ref"
775
861
},
776
862
"maxLength": 8,
777
863
"description": "Small metrics a portal may render on cards or profiles."
778
864
},
779
865
"filters": {
780
866
"type": "array",
781
867
"items": {
782
868
"ref": "#filter",
783
869
"type": "ref"
784
870
},
785
871
"maxLength": 8,
786
872
"description": "Optional filters used when rkey is 'any' or authority is set. Conditions must match on one record."
787
873
},
788
874
"purpose": {
789
875
"type": "string",
790
876
"maxLength": 300,
791
877
"description": "Why a portal or game reads this data."
792
878
},
793
879
"authority": {
794
880
"type": "string",
795
881
"format": "did",
796
882
"description": "When the referenced record lives in a trusted authority repo instead of the player's repo, this DID names that repo."
797
883
}
798
884
},
799
-
"description": "A non-gating record surface relevant to stats, saves, leaderboards, activity, sessions, replays, validation, labels, grants, inventory, profile enrichment, or other portal-readable context. A reference is a recipe for reading and presenting records. It is not an eligibility rule and not a promise that the game gives the player a benefit for owning that record. Put play gates in requires[] and optional gameplay integrations in includes[]."
885
+
"description": "Deprecated compatibility shape for references[]. Use recordUse in uses[]. A non-gating record surface relevant to stats, saves, leaderboards, activity, sessions, replays, validation, labels, grants, inventory, profile enrichment, or other portal-readable context."
800
886
},
801
887
"catalogRef": {
802
888
"type": "object",
803
889
"required": [
804
890
"uri"
805
891
],
806
892
"properties": {
807
893
"uri": {
808
894
"type": "string",
809
895
"format": "at-uri",
810
896
"description": "AT-URI of the external catalog record (e.g. at://did:web:gamesgamesgamesgames.games/games.gamesgamesgamesgames.game/3mgjih2bzdp2l)."
811
897
},
812
898
"name": {
813
899
"type": "string",
814
900
"maxLength": 100,
815
901
"description": "Display name of the referenced entry, so portals can show it without resolving the record."
816
902
},
817
903
"borrow": {
818
904
"type": "array",
819
905
"items": {
820
906
"type": "string",
821
907
"knownValues": [
822
908
"media",
823
909
"trailers",
824
910
"genres",
825
911
"platforms",
826
912
"releases",
827
913
"ratings",
828
914
"description",
829
915
"summary"
830
916
]
831
917
},
832
918
"maxLength": 8,
833
919
"description": "Advisory hints naming which parts of the referenced record a portal may display alongside this experience (e.g. pull screenshots and genres from Pentaract rather than duplicating them here). Display guidance only, never affects eligibility."
834
920
},
835
921
"source": {
836
922
"type": "string",
837
923
"maxLength": 60,
838
924
"description": "Short label for the catalog this points at (e.g. 'pentaract')."
839
925
}
840
926
},
841
927
"description": "A pointer to a record in another catalog that already describes this title in depth, most usefully a games.gamesgamesgamesgames.game record on The Pentaract, which carries genres, platforms, release dates, age ratings, and a full media gallery. The definition stays self-contained (eligibility never depends on it); the reference simply lets a portal fetch that richer metadata and show it alongside, so we build on existing game databases instead of re-entering everything."
842
928
},
843
929
"lexiconRef": {
844
930
"type": "object",
845
931
"required": [
846
932
"nsid"
847
933
],
848
934
"properties": {
849
935
"nsid": {
850
936
"type": "string",
851
937
"format": "nsid"
852
938
},
853
939
"verify": {
854
940
"ref": "#rule",
855
941
"type": "ref",
856
942
"description": "Optional rule that detects or authenticates this feature, typically a player-record match or a vouch in an issuer's separate grant collection. Portals may badge the feature when it passes. It never gates access."
857
943
},
858
944
"acquire": {
859
945
"type": "string",
860
946
"format": "uri",
861
947
"description": "Optional URL where a player can obtain this record."
862
948
},
863
949
"purpose": {
864
950
"type": "string",
865
951
"maxLength": 200,
866
952
"description": "The player-visible benefit this record unlocks or enriches when present. Shown to players."
867
953
}
868
954
},
869
-
"description": "An optional player-facing record collection this game can use when present. This is about gameplay behavior or player benefit, not portal analytics. Example: an rpg.actor sprite can appear in the game, or an equipment item can unlock a bonus. 'verify' may inspect the player's record or an issuer-controlled grant collection so portals can badge whether the benefit is available."
955
+
"description": "Deprecated compatibility shape for includes[]. Use recordUse in uses[]. An optional player-facing record collection this game can use when present."
870
956
},
871
957
"referenceDisplay": {
872
958
"type": "object",
873
959
"required": [
874
960
"key",
875
961
"label",
876
962
"path"
877
963
],
878
964
"properties": {
879
965
"key": {
880
966
"type": "string",
881
967
"maxLength": 80,
882
968
"description": "Stable metric key within this definition, e.g. 'bestDistance'."
883
969
},
884
970
"path": {
885
971
"type": "string",
886
972
"maxLength": 200,
887
973
"description": "Dot-path to the value, relative to the reference path when set."
888
974
},
889
975
"unit": {
890
976
"type": "string",
891
977
"maxLength": 20,
892
978
"description": "Unit suffix such as 'm', 'XP', or 'pts'."
893
979
},
894
980
"label": {
895
981
"type": "string",
896
982
"maxLength": 80,
897
983
"description": "Short label shown before or beside the value."
898
984
},
899
985
"format": {
900
986
"type": "string",
901
987
"description": "Presentation hint for the value.",
902
988
"knownValues": [
903
989
"string",
904
990
"integer",
905
991
"number",
906
992
"distance",
907
993
"percent",
908
994
"duration",
909
995
"datetime",
910
996
"boolean"
911
997
]
912
998
},
913
999
"divideBy": {
914
1000
"type": "integer",
915
1001
"minimum": 1,
916
1002
"description": "Integer divisor applied before display, e.g. divideBy 10 for decimeters to meters."
917
1003
},
918
1004
"priority": {
919
1005
"type": "integer",
920
1006
"description": "Lower numbers display first."
921
1007
},
922
1008
"aggregate": {
923
1009
"type": "string",
924
1010
"description": "How to reduce multiple records. Defaults to 'first' for fixed rkey records and 'count' for collection scans.",
925
1011
"knownValues": [
926
1012
"first",
927
1013
"count",
928
1014
"max",
929
1015
"min",
930
1016
"sum",
931
1017
"latest",
932
1018
"exists"
933
1019
]
934
1020
},
935
1021
"multiplyBy": {
936
1022
"type": "integer",
937
1023
"description": "Integer multiplier applied before display. Use with divideBy for rational scaling."
938
1024
},
939
1025
"maximumFractionDigits": {
940
1026
"type": "integer",
941
1027
"maximum": 6,
942
1028
"minimum": 0,
943
1029
"description": "Decimal precision for numeric display."
944
1030
}
945
1031
},
946
1032
"description": "A declarative display metric derived from a referenced record or collection. Portals may ignore unsupported formats or aggregates."
947
1033
}
948
1034
},
949
1035
"$type": "com.atproto.lexicon.schema",
950
1036
"lexicon": 1,
951
1037
"description": "An AT Protocol game definition: a publisher's self-contained declaration of a playable experience, its presentation, the records a player must hold to play, and records the game can optionally use. Evaluators resolve requirements through AT Protocol repository reads; no game server or central eligibility database is required."
952
1038
}