games.atmosphere.define

atmosphere.games

Schema Diff

+200 -5

From

CID
bafyreifg6gjn2um...
Indexed At
2026-07-17 14:46 UTC
View this version

To

CID
bafyreia6xhfyhiq...
Indexed At
2026-07-18 02:21 UTC
View this version

Compatibility Analysis

Breaking Changes Detected

5 breaking changes, 61 non-breaking changes.

Breaking Changes (5)
  • RequiredEdgeAdded RequiredEdgeAdded { vertex_id: "games.atmosphere.define#referenceDisplay", src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.key", kind: "prop", name: Some("key") }
  • RequiredEdgeAdded RequiredEdgeAdded { vertex_id: "games.atmosphere.define#referenceDisplay", src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.path", kind: "prop", name: Some("path") }
  • RequiredEdgeAdded RequiredEdgeAdded { vertex_id: "games.atmosphere.define#referenceDisplay", src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.label", kind: "prop", name: Some("label") }
  • RequiredEdgeAdded RequiredEdgeAdded { vertex_id: "games.atmosphere.define#reference", src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.role", kind: "prop", name: Some("role") }
  • RequiredEdgeAdded RequiredEdgeAdded { vertex_id: "games.atmosphere.define#reference", src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.nsid", kind: "prop", name: Some("nsid") }
Non-Breaking Changes (61)
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.acquire" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.authority" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.display" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.display:items" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.filters" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.filters:items" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.label" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.nsid" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.path" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.purpose" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.rkey" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.role" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#reference.verify" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.aggregate" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.divideBy" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.format" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.key" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.label" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.maximumFractionDigits" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.multiplyBy" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.path" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.priority" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.unit" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define:body.legacyIds" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define:body.legacyIds:items" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define:body.migratedFromAtUri" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define:body.references" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define:body.references:items" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define:body.replaces" }
  • AddedVertex AddedVertex { vertex_id: "games.atmosphere.define:body.replaces:items" }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.acquire", kind: "prop", name: Some("acquire") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.authority", kind: "prop", name: Some("authority") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.display", kind: "prop", name: Some("display") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.filters", kind: "prop", name: Some("filters") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.label", kind: "prop", name: Some("label") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.path", kind: "prop", name: Some("path") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.purpose", kind: "prop", name: Some("purpose") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.rkey", kind: "prop", name: Some("rkey") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.verify", kind: "prop", name: Some("verify") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference.display", tgt: "games.atmosphere.define#reference.display:items", kind: "items", name: None }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference.display:items", tgt: "games.atmosphere.define#referenceDisplay", kind: "ref", name: None }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference.filters", tgt: "games.atmosphere.define#reference.filters:items", kind: "items", name: None }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference.filters:items", tgt: "games.atmosphere.define#filter", kind: "ref", name: None }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#reference.verify", tgt: "games.atmosphere.define#rule", kind: "ref", name: None }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.aggregate", kind: "prop", name: Some("aggregate") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.divideBy", kind: "prop", name: Some("divideBy") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.format", kind: "prop", name: Some("format") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.maximumFractionDigits", kind: "prop", name: Some("maximumFractionDigits") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.multiplyBy", kind: "prop", name: Some("multiplyBy") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.priority", kind: "prop", name: Some("priority") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.unit", kind: "prop", name: Some("unit") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.legacyIds", kind: "prop", name: Some("legacyIds") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.migratedFromAtUri", kind: "prop", name: Some("migratedFromAtUri") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.references", kind: "prop", name: Some("references") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.replaces", kind: "prop", name: Some("replaces") }
  • AddedEdge AddedEdge { src: "games.atmosphere.define:body.legacyIds", tgt: "games.atmosphere.define:body.legacyIds:items", kind: "items", name: None }
  • AddedEdge AddedEdge { src: "games.atmosphere.define:body.references", tgt: "games.atmosphere.define:body.references:items", kind: "items", name: None }
  • AddedEdge AddedEdge { src: "games.atmosphere.define:body.references:items", tgt: "games.atmosphere.define#reference", kind: "ref", name: None }
  • AddedEdge AddedEdge { src: "games.atmosphere.define:body.replaces", tgt: "games.atmosphere.define:body.replaces:items", kind: "items", name: None }

Migration Guidance

Added Elements

  • AddedVertex { vertex_id: "games.atmosphere.define#reference" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.acquire" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.authority" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.display" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.display:items" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.filters" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.filters:items" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.label" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.nsid" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.path" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.purpose" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.rkey" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.role" }
  • AddedVertex { vertex_id: "games.atmosphere.define#reference.verify" }
  • AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay" }
  • AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.aggregate" }
  • AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.divideBy" }
  • AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.format" }
  • AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.key" }
  • AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.label" }
  • AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.maximumFractionDigits" }
  • AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.multiplyBy" }
  • AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.path" }
  • AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.priority" }
  • AddedVertex { vertex_id: "games.atmosphere.define#referenceDisplay.unit" }
  • AddedVertex { vertex_id: "games.atmosphere.define:body.legacyIds" }
  • AddedVertex { vertex_id: "games.atmosphere.define:body.legacyIds:items" }
  • AddedVertex { vertex_id: "games.atmosphere.define:body.migratedFromAtUri" }
  • AddedVertex { vertex_id: "games.atmosphere.define:body.references" }
  • AddedVertex { vertex_id: "games.atmosphere.define:body.references:items" }
  • AddedVertex { vertex_id: "games.atmosphere.define:body.replaces" }
  • AddedVertex { vertex_id: "games.atmosphere.define:body.replaces:items" }

Additional Notes

  • Breaking: RequiredEdgeAdded { vertex_id: "games.atmosphere.define#referenceDisplay", src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.key", kind: "prop", name: Some("key") }
  • Breaking: RequiredEdgeAdded { vertex_id: "games.atmosphere.define#referenceDisplay", src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.path", kind: "prop", name: Some("path") }
  • Breaking: RequiredEdgeAdded { vertex_id: "games.atmosphere.define#referenceDisplay", src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.label", kind: "prop", name: Some("label") }
  • Breaking: RequiredEdgeAdded { vertex_id: "games.atmosphere.define#reference", src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.role", kind: "prop", name: Some("role") }
  • Breaking: RequiredEdgeAdded { vertex_id: "games.atmosphere.define#reference", src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.nsid", kind: "prop", name: Some("nsid") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.acquire", kind: "prop", name: Some("acquire") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.authority", kind: "prop", name: Some("authority") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.display", kind: "prop", name: Some("display") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.filters", kind: "prop", name: Some("filters") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.label", kind: "prop", name: Some("label") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.path", kind: "prop", name: Some("path") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.purpose", kind: "prop", name: Some("purpose") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.rkey", kind: "prop", name: Some("rkey") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference", tgt: "games.atmosphere.define#reference.verify", kind: "prop", name: Some("verify") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference.display", tgt: "games.atmosphere.define#reference.display:items", kind: "items", name: None }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference.display:items", tgt: "games.atmosphere.define#referenceDisplay", kind: "ref", name: None }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference.filters", tgt: "games.atmosphere.define#reference.filters:items", kind: "items", name: None }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference.filters:items", tgt: "games.atmosphere.define#filter", kind: "ref", name: None }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#reference.verify", tgt: "games.atmosphere.define#rule", kind: "ref", name: None }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.aggregate", kind: "prop", name: Some("aggregate") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.divideBy", kind: "prop", name: Some("divideBy") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.format", kind: "prop", name: Some("format") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.maximumFractionDigits", kind: "prop", name: Some("maximumFractionDigits") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.multiplyBy", kind: "prop", name: Some("multiplyBy") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.priority", kind: "prop", name: Some("priority") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define#referenceDisplay", tgt: "games.atmosphere.define#referenceDisplay.unit", kind: "prop", name: Some("unit") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.legacyIds", kind: "prop", name: Some("legacyIds") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.migratedFromAtUri", kind: "prop", name: Some("migratedFromAtUri") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.references", kind: "prop", name: Some("references") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define:body", tgt: "games.atmosphere.define:body.replaces", kind: "prop", name: Some("replaces") }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define:body.legacyIds", tgt: "games.atmosphere.define:body.legacyIds:items", kind: "items", name: None }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define:body.references", tgt: "games.atmosphere.define:body.references:items", kind: "items", name: None }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define:body.references:items", tgt: "games.atmosphere.define#reference", kind: "ref", name: None }
  • Non-breaking: AddedEdge { src: "games.atmosphere.define:body.replaces", tgt: "games.atmosphere.define:body.replaces:items", kind: "items", 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 129
          "cover": {
130 130
            "type": "blob",
131 131
            "accept": [
132 132
              "image/png",
133 133
              "image/jpeg",
134 134
              "image/webp"
135 135
            ],
136 136
            "maxSize": 4000000,
137 137
            "description": "Primary cover image. Portals display this as the card thumbnail. Recommended ratio 16:9."
138 138
          },
139 139
          "media": {
140 140
            "type": "array",
141 141
            "items": {
142 142
              "ref": "#mediaItem",
143 143
              "type": "ref"
144 144
            },
145 145
            "maxLength": 24,
146 146
            "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 147
          },
148 148
          "status": {
149 149
            "type": "string",
150 150
            "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 151
            "knownValues": [
152 152
              "live",
153 153
              "beta",
154 154
              "coming",
155 155
              "archived"
156 156
            ]
157 157
          },
158 158
          "catalog": {
159 159
            "type": "array",
160 160
            "items": {
161 161
              "ref": "#catalogRef",
162 162
              "type": "ref"
163 163
            },
164 164
            "maxLength": 8,
165 165
            "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 166
          },
167 167
          "tagline": {
168 168
            "type": "string",
169 169
            "maxLength": 200,
170 170
            "description": "One-line summary shown beneath the title in card views."
171 171
          },
172 172
          "category": {
173 173
            "type": "string",
174 174
            "maxLength": 30,
175 175
            "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 176
            "knownValues": [
177 177
              "browser",
178 178
              "download",
179 179
              "physical",
180 180
              "other"
181 181
            ]
182 182
          },
183 183
          "includes": {
184 184
            "type": "array",
185 185
            "items": {
186 186
              "ref": "#lexiconRef",
187 187
              "type": "ref"
188 188
            },
189 189
            "maxLength": 24,
190 -
            "description": "Records this game can use when present but never needs in order to run. Portals may show these as optional interoperability, import, cosmetic, or bonus features. They never gate play."
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 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."
200 +
          },
192 201
          "requires": {
193 202
            "type": "array",
194 203
            "items": {
195 204
              "ref": "#rule",
196 205
              "type": "ref"
197 206
            },
198 207
            "maxLength": 16,
199 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."
200 209
          },
201 210
          "trailers": {
202 211
            "type": "array",
203 212
            "items": {
204 213
              "ref": "#video",
205 214
              "type": "ref"
206 215
            },
207 216
            "maxLength": 8,
208 217
            "description": "Video trailers and gameplay clips. Hosted externally (YouTube, Vimeo, Twitch) or linked directly so portals can embed or link them."
209 218
          },
210 219
          "createdAt": {
211 220
            "type": "string",
212 221
            "format": "datetime"
213 222
          },
214 223
          "developer": {
215 224
            "ref": "#party",
216 225
            "type": "ref",
217 226
            "description": "The studio or individual who built the experience. Prefer a DID when available so the developer is verifiable on the network."
218 227
          },
228 +
          "legacyIds": {
229 +
            "type": "array",
230 +
            "items": {
231 +
              "type": "string",
232 +
              "maxLength": 80
233 +
            },
234 +
            "maxLength": 12,
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 +
          },
219 237
          "publisher": {
220 238
            "type": "string",
221 239
            "format": "at-uri",
222 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."
223 241
          },
224 242
          "updatedAt": {
225 243
            "type": "string",
226 244
            "format": "datetime"
227 245
          },
246 +
          "references": {
247 +
            "type": "array",
248 +
            "items": {
249 +
              "ref": "#reference",
250 +
              "type": "ref"
251 +
            },
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[]."
254 +
          },
228 255
          "releasedAt": {
229 256
            "type": "string",
230 257
            "format": "datetime",
231 258
            "description": "When this experience became publicly available."
232 259
          },
233 260
          "description": {
234 261
            "type": "string",
235 262
            "maxLength": 2000,
236 263
            "description": "Full description of the experience. Plain text; portals may render line breaks."
237 264
          },
238 265
          "statNamespace": {
239 266
            "type": "string",
240 267
            "maxLength": 80,
241 -
            "description": "Collection NSID under which this experience writes player activity. Portals count unique DIDs holding records here to derive player counts, no telemetry required."
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."
242 274
          }
243 275
        }
244 276
      },
245 277
      "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."
246 278
    },
247 279
    "rule": {
248 280
      "type": "object",
249 281
      "required": [
250 282
        "check"
251 283
      ],
252 284
      "properties": {
253 285
        "op": {
254 286
          "type": "string",
255 287
          "description": "For field: comparison operator.",
256 288
          "knownValues": [
257 289
            "exists",
258 290
            "notExists",
259 291
            "isViewer",
260 292
            "eq",
261 293
            "neq",
262 294
            "gt",
263 295
            "gte",
264 296
            "lt",
265 297
            "lte"
266 298
          ]
267 299
        },
268 300
        "nsid": {
269 301
          "type": "string",
270 302
          "format": "nsid",
271 303
          "description": "Collection read by record, field, matches, or vouch."
272 304
        },
273 305
        "rkey": {
274 306
          "type": "string",
275 307
          "maxLength": 50,
276 308
          "description": "For field: record key, default 'self'; 'any' scans the collection."
277 309
        },
278 310
        "rule": {
279 311
          "ref": "#rule",
280 312
          "type": "ref",
281 313
          "description": "For not: nested rule to negate."
282 314
        },
283 315
        "check": {
284 316
          "type": "string",
285 317
          "description": "Rule discriminator. Known values are open for forward compatibility; an unknown value must not pass.",
286 318
          "knownValues": [
287 319
            "record",
288 320
            "field",
289 321
            "matches",
290 322
            "vouch",
291 323
            "all",
292 324
            "any",
293 325
            "not"
294 326
          ]
295 327
        },
296 328
        "field": {
297 329
          "type": "string",
298 330
          "maxLength": 200,
299 331
          "description": "For field: dot-path to test."
300 332
        },
301 333
        "label": {
302 334
          "type": "string",
303 335
          "maxLength": 120,
304 336
          "description": "Human-readable requirement."
305 337
        },
306 338
        "rules": {
307 339
          "type": "array",
308 340
          "items": {
309 341
            "ref": "#rule",
310 342
            "type": "ref"
311 343
          },
312 344
          "maxLength": 16,
313 345
          "minLength": 1,
314 346
          "description": "For all or any: nested rules."
315 347
        },
316 348
        "acquire": {
317 349
          "type": "string",
318 350
          "format": "uri",
319 351
          "description": "URL where a player can fulfil this requirement."
320 352
        },
321 353
        "filters": {
322 354
          "type": "array",
323 355
          "items": {
324 356
            "ref": "#filter",
325 357
            "type": "ref"
326 358
          },
327 359
          "maxLength": 8,
328 360
          "minLength": 1,
329 361
          "description": "For matches or vouch: conditions that must hold on one record."
330 362
        },
331 363
        "authority": {
332 364
          "type": "string",
333 365
          "format": "did",
334 366
          "description": "For vouch: stable DID of the record issuer."
335 367
        },
336 368
        "valueString": {
337 369
          "type": "string",
338 370
          "maxLength": 1000,
339 371
          "description": "String comparison value."
340 372
        },
341 373
        "valueBoolean": {
342 374
          "type": "boolean",
343 375
          "description": "Boolean comparison value."
344 376
        },
345 377
        "valueInteger": {
346 378
          "type": "integer",
347 379
          "description": "Integer comparison value."
348 380
        }
349 381
      },
350 382
      "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."
351 383
    },
352 384
    "field": {
353 385
      "type": "object",
354 386
      "required": [
355 387
        "check",
356 388
        "nsid",
357 389
        "field",
358 390
        "op"
359 391
      ],
360 392
      "properties": {
361 393
        "op": {
362 394
          "type": "string",
363 395
          "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.",
364 396
          "knownValues": [
365 397
            "exists",
366 398
            "notExists",
367 399
            "isViewer",
368 400
            "eq",
369 401
            "neq",
370 402
            "gt",
371 403
            "gte",
372 404
            "lt",
373 405
            "lte"
374 406
          ]
375 407
        },
376 408
        "nsid": {
377 409
          "type": "string",
378 410
          "format": "nsid"
379 411
        },
380 412
        "rkey": {
381 413
          "type": "string",
382 414
          "maxLength": 50,
383 415
          "description": "Record key. Defaults to 'self'. Use 'any' to pass if any record in the collection satisfies the condition."
384 416
        },
385 417
        "check": {
386 418
          "type": "string",
387 419
          "const": "field"
388 420
        },
389 421
        "field": {
390 422
          "type": "string",
391 423
          "maxLength": 200,
392 424
          "description": "Dot-path to the property to evaluate (e.g. 'score.total', 'level'). Array traversal is not supported."
393 425
        },
394 426
        "label": {
395 427
          "type": "string",
396 428
          "maxLength": 120
397 429
        },
398 430
        "acquire": {
399 431
          "type": "string",
400 432
          "format": "uri"
401 433
        },
402 434
        "valueString": {
403 435
          "type": "string",
404 436
          "maxLength": 1000,
405 437
          "description": "String comparison value. For value-taking operators, set exactly one of valueString, valueInteger, or valueBoolean."
406 438
        },
407 439
        "valueBoolean": {
408 440
          "type": "boolean",
409 441
          "description": "Boolean comparison value."
410 442
        },
411 443
        "valueInteger": {
412 444
          "type": "integer",
413 445
          "description": "Integer comparison value. Use for numeric gt/gte/lt/lte checks."
414 446
        }
415 447
      },
416 448
      "description": "Fetches a record from the player's repository and tests one field. Covers required fields, score thresholds, flags, and enum matches."
417 449
    },
418 450
    "party": {
419 451
      "type": "object",
420 452
      "required": [
421 453
        "name"
422 454
      ],
423 455
      "properties": {
424 456
        "did": {
425 457
          "type": "string",
426 458
          "format": "did",
427 -
          "description": "AT Protocol DID of this entity's account, if they have one."
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."
428 460
        },
429 461
        "url": {
430 462
          "type": "string",
431 463
          "format": "uri",
432 464
          "description": "Website for this entity."
433 465
        },
434 466
        "name": {
435 467
          "type": "string",
436 468
          "maxLength": 100,
437 469
          "description": "Display name of the entity."
438 470
        }
439 471
      },
440 472
      "description": "A named entity (developer, publisher, studio) associated with the experience."
441 473
    },
442 474
    "video": {
443 475
      "type": "object",
444 476
      "required": [
445 477
        "title"
446 478
      ],
447 479
      "properties": {
448 480
        "url": {
449 481
          "type": "string",
450 482
          "format": "uri",
451 483
          "description": "Full URL to the video, used when no platform/videoId is given."
452 484
        },
453 485
        "title": {
454 486
          "type": "string",
455 487
          "maxLength": 120,
456 488
          "description": "Label for the clip (e.g. 'Launch Trailer')."
457 489
        },
458 490
        "videoId": {
459 491
          "type": "string",
460 492
          "maxLength": 120,
461 493
          "description": "Platform video id (e.g. a YouTube id). Combined with 'platform' to build an embed."
462 494
        },
463 495
        "platform": {
464 496
          "type": "string",
465 497
          "description": "Where the video lives. 'direct' means the url points straight at a video file or page.",
466 498
          "knownValues": [
467 499
            "youtube",
468 500
            "vimeo",
469 501
            "twitch",
470 502
            "direct"
471 503
          ]
472 504
        },
473 505
        "thumbnail": {
474 506
          "type": "blob",
475 507
          "accept": [
476 508
            "image/png",
477 509
            "image/jpeg",
478 510
            "image/webp"
479 511
          ],
480 512
          "maxSize": 2000000,
481 513
          "description": "Optional still frame shown before the video plays."
482 514
        }
483 515
      },
484 516
      "description": "A trailer or gameplay clip. Provide a platform + videoId (preferred, so portals embed natively) or a direct url."
485 517
    },
486 518
    "vouch": {
487 519
      "type": "object",
488 520
      "required": [
489 521
        "check",
490 522
        "authority",
491 523
        "nsid"
492 524
      ],
493 525
      "properties": {
494 526
        "nsid": {
495 527
          "type": "string",
496 528
          "format": "nsid",
497 529
          "description": "The kind of record to look for in that account's repo (e.g. equipment.rpg.give)."
498 530
        },
499 531
        "check": {
500 532
          "type": "string",
501 533
          "const": "vouch"
502 534
        },
503 535
        "label": {
504 536
          "type": "string",
505 537
          "maxLength": 120
506 538
        },
507 539
        "acquire": {
508 540
          "type": "string",
509 541
          "format": "uri"
510 542
        },
511 543
        "filters": {
512 544
          "type": "array",
513 545
          "items": {
514 546
            "ref": "#filter",
515 547
            "type": "ref"
516 548
          },
517 549
          "maxLength": 8,
518 550
          "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."
519 551
        },
520 552
        "authority": {
521 553
          "type": "string",
522 554
          "format": "did",
523 555
          "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."
524 556
        }
525 557
      },
526 558
      "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.'"
527 559
    },
528 560
    "filter": {
529 561
      "type": "object",
530 562
      "required": [
531 563
        "field",
532 564
        "op"
533 565
      ],
534 566
      "properties": {
535 567
        "op": {
536 568
          "type": "string",
537 569
          "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.",
538 570
          "knownValues": [
539 571
            "exists",
540 572
            "notExists",
541 573
            "isViewer",
542 574
            "eq",
543 575
            "neq",
544 576
            "gt",
545 577
            "gte",
546 578
            "lt",
547 579
            "lte"
548 580
          ]
549 581
        },
550 582
        "field": {
551 583
          "type": "string",
552 584
          "maxLength": 200,
553 585
          "description": "Dot-path to the field."
554 586
        },
555 587
        "valueString": {
556 588
          "type": "string",
557 589
          "maxLength": 1000,
558 590
          "description": "String comparison value. For value-taking operators, set exactly one typed value property."
559 591
        },
560 592
        "valueBoolean": {
561 593
          "type": "boolean",
562 594
          "description": "Boolean comparison value."
563 595
        },
564 596
        "valueInteger": {
565 597
          "type": "integer",
566 598
          "description": "Integer comparison value. Use for numeric gt/gte/lt/lte checks."
567 599
        }
568 600
      },
569 601
      "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."
570 602
    },
571 603
    "record": {
572 604
      "type": "object",
573 605
      "required": [
574 606
        "check",
575 607
        "nsid"
576 608
      ],
577 609
      "properties": {
578 610
        "nsid": {
579 611
          "type": "string",
580 612
          "format": "nsid",
581 613
          "description": "Collection to check on the player's PDS (e.g. 'actor.rpg.sprite')."
582 614
        },
583 615
        "check": {
584 616
          "type": "string",
585 617
          "const": "record"
586 618
        },
587 619
        "label": {
588 620
          "type": "string",
589 621
          "maxLength": 120,
590 622
          "description": "Human-readable requirement shown when gated (e.g. 'An rpg.actor sprite')."
591 623
        },
592 624
        "acquire": {
593 625
          "type": "string",
594 626
          "format": "uri",
595 627
          "description": "URL to send players who fail, so they can fulfil the requirement."
596 628
        }
597 629
      },
598 630
      "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?'"
599 631
    },
600 632
    "matches": {
601 633
      "type": "object",
602 634
      "required": [
603 635
        "check",
604 636
        "nsid",
605 637
        "filters"
606 638
      ],
607 639
      "properties": {
608 640
        "nsid": {
609 641
          "type": "string",
610 642
          "format": "nsid"
611 643
        },
612 644
        "check": {
613 645
          "type": "string",
614 646
          "const": "matches"
615 647
        },
616 648
        "label": {
617 649
          "type": "string",
618 650
          "maxLength": 120
619 651
        },
620 652
        "acquire": {
621 653
          "type": "string",
622 654
          "format": "uri"
623 655
        },
624 656
        "filters": {
625 657
          "type": "array",
626 658
          "items": {
627 659
            "ref": "#filter",
628 660
            "type": "ref"
629 661
          },
630 662
          "maxLength": 8,
631 663
          "minLength": 1,
632 664
          "description": "All filters must match on the same record (AND semantics within one record)."
633 665
        }
634 666
      },
635 667
      "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'."
636 668
    },
637 669
    "mediaItem": {
638 670
      "type": "object",
639 671
      "required": [
640 672
        "image"
641 673
      ],
642 674
      "properties": {
643 675
        "alt": {
644 676
          "type": "string",
645 677
          "maxLength": 300,
646 678
          "description": "Accessibility caption describing the image."
647 679
        },
648 680
        "type": {
649 681
          "type": "string",
650 682
          "description": "What kind of image this is, so portals can place it (e.g. 'banner' across the top, 'screenshot' in a gallery).",
651 683
          "knownValues": [
652 684
            "screenshot",
653 685
            "banner",
654 686
            "artwork",
655 687
            "poster",
656 688
            "background",
657 689
            "logo",
658 690
            "thumbnail"
659 691
          ]
660 692
        },
661 693
        "image": {
662 694
          "type": "blob",
663 695
          "accept": [
664 696
            "image/png",
665 697
            "image/jpeg",
666 698
            "image/webp",
667 699
            "image/gif"
668 700
          ],
669 701
          "maxSize": 8000000,
670 702
          "description": "The image blob, stored on the developer's PDS."
671 703
        },
672 704
        "width": {
673 705
          "type": "integer",
674 706
          "description": "Pixel width, if known, lets portals reserve layout space before the blob loads."
675 707
        },
676 708
        "height": {
677 709
          "type": "integer",
678 710
          "description": "Pixel height, if known."
679 711
        }
680 712
      },
681 713
      "description": "One image in the experience's media gallery."
682 714
    },
715 +
    "reference": {
716 +
      "type": "object",
717 +
      "required": [
718 +
        "role",
719 +
        "nsid"
720 +
      ],
721 +
      "properties": {
722 +
        "nsid": {
723 +
          "type": "string",
724 +
          "format": "nsid",
725 +
          "description": "Collection to read."
726 +
        },
727 +
        "path": {
728 +
          "type": "string",
729 +
          "maxLength": 200,
730 +
          "description": "Optional dot-path to the relevant payload within the record, e.g. 'clunscannon' inside actor.rpg.stats/self."
731 +
        },
732 +
        "rkey": {
733 +
          "type": "string",
734 +
          "maxLength": 50,
735 +
          "description": "Record key to read from a player's repo. Defaults to 'self'; use 'any' when a portal should scan the collection."
736 +
        },
737 +
        "role": {
738 +
          "type": "string",
739 +
          "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 +
          "knownValues": [
741 +
            "activity",
742 +
            "stats",
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 reference."
759 +
        },
760 +
        "verify": {
761 +
          "ref": "#rule",
762 +
          "type": "ref",
763 +
          "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 +
        },
765 +
        "acquire": {
766 +
          "type": "string",
767 +
          "format": "uri",
768 +
          "description": "Optional URL where a player can create 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": "Why a portal or game reads this data."
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": "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[]."
800 +
    },
683 801
    "catalogRef": {
684 802
      "type": "object",
685 803
      "required": [
686 804
        "uri"
687 805
      ],
688 806
      "properties": {
689 807
        "uri": {
690 808
          "type": "string",
691 809
          "format": "at-uri",
692 810
          "description": "AT-URI of the external catalog record (e.g. at://did:web:gamesgamesgamesgames.games/games.gamesgamesgamesgames.game/3mgjih2bzdp2l)."
693 811
        },
694 812
        "name": {
695 813
          "type": "string",
696 814
          "maxLength": 100,
697 815
          "description": "Display name of the referenced entry, so portals can show it without resolving the record."
698 816
        },
699 817
        "borrow": {
700 818
          "type": "array",
701 819
          "items": {
702 820
            "type": "string",
703 821
            "knownValues": [
704 822
              "media",
705 823
              "trailers",
706 824
              "genres",
707 825
              "platforms",
708 826
              "releases",
709 827
              "ratings",
710 828
              "description",
711 829
              "summary"
712 830
            ]
713 831
          },
714 832
          "maxLength": 8,
715 833
          "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."
716 834
        },
717 835
        "source": {
718 836
          "type": "string",
719 837
          "maxLength": 60,
720 838
          "description": "Short label for the catalog this points at (e.g. 'pentaract')."
721 839
        }
722 840
      },
723 841
      "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."
724 842
    },
725 843
    "lexiconRef": {
726 844
      "type": "object",
727 845
      "required": [
728 846
        "nsid"
729 847
      ],
730 848
      "properties": {
731 849
        "nsid": {
732 850
          "type": "string",
733 851
          "format": "nsid"
734 852
        },
735 853
        "verify": {
736 854
          "ref": "#rule",
737 855
          "type": "ref",
738 856
          "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."
739 857
        },
740 858
        "acquire": {
741 859
          "type": "string",
742 860
          "format": "uri",
743 861
          "description": "Optional URL where a player can obtain this record."
744 862
        },
745 863
        "purpose": {
746 864
          "type": "string",
747 865
          "maxLength": 200,
748 -
          "description": "How this lexicon enriches the experience when present. Shown to players."
866 +
          "description": "The player-visible benefit this record unlocks or enriches when present. Shown to players."
867 +
        }
868 +
      },
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."
870 +
    },
871 +
    "referenceDisplay": {
872 +
      "type": "object",
873 +
      "required": [
874 +
        "key",
875 +
        "label",
876 +
        "path"
877 +
      ],
878 +
      "properties": {
879 +
        "key": {
880 +
          "type": "string",
881 +
          "maxLength": 80,
882 +
          "description": "Stable metric key within this definition, e.g. 'bestDistance'."
883 +
        },
884 +
        "path": {
885 +
          "type": "string",
886 +
          "maxLength": 200,
887 +
          "description": "Dot-path to the value, relative to the reference path when set."
888 +
        },
889 +
        "unit": {
890 +
          "type": "string",
891 +
          "maxLength": 20,
892 +
          "description": "Unit suffix such as 'm', 'XP', or 'pts'."
893 +
        },
894 +
        "label": {
895 +
          "type": "string",
896 +
          "maxLength": 80,
897 +
          "description": "Short label shown before or beside the value."
898 +
        },
899 +
        "format": {
900 +
          "type": "string",
901 +
          "description": "Presentation hint for the value.",
902 +
          "knownValues": [
903 +
            "string",
904 +
            "integer",
905 +
            "number",
906 +
            "distance",
907 +
            "percent",
908 +
            "duration",
909 +
            "datetime",
910 +
            "boolean"
911 +
          ]
912 +
        },
913 +
        "divideBy": {
914 +
          "type": "integer",
915 +
          "minimum": 1,
916 +
          "description": "Integer divisor applied before display, e.g. divideBy 10 for decimeters to meters."
917 +
        },
918 +
        "priority": {
919 +
          "type": "integer",
920 +
          "description": "Lower numbers display first."
921 +
        },
922 +
        "aggregate": {
923 +
          "type": "string",
924 +
          "description": "How to reduce multiple records. Defaults to 'first' for fixed rkey records and 'count' for collection scans.",
925 +
          "knownValues": [
926 +
            "first",
927 +
            "count",
928 +
            "max",
929 +
            "min",
930 +
            "sum",
931 +
            "latest",
932 +
            "exists"
933 +
          ]
934 +
        },
935 +
        "multiplyBy": {
936 +
          "type": "integer",
937 +
          "description": "Integer multiplier applied before display. Use with divideBy for rational scaling."
938 +
        },
939 +
        "maximumFractionDigits": {
940 +
          "type": "integer",
941 +
          "maximum": 6,
942 +
          "minimum": 0,
943 +
          "description": "Decimal precision for numeric display."
749 944
        }
750 945
      },
751 -
      "description": "A record collection this game can use when present, never a requirement. 'nsid' names the optional player-facing record; 'verify' may inspect that record or a separate issuer-controlled grant collection."
946 +
      "description": "A declarative display metric derived from a referenced record or collection. Portals may ignore unsupported formats or aggregates."
752 947
    }
753 948
  },
754 949
  "$type": "com.atproto.lexicon.schema",
755 950
  "lexicon": 1,
756 951
  "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."
757 952
}

Compare Other Versions

Lexicon Garden

@