@dyno-cloud/schema 0.0.0-stage → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/json/budget.schema.json +239 -0
  2. package/json/catalogue.schema.json +2217 -0
  3. package/json/compare.schema.json +823 -0
  4. package/json/devices.json +399 -0
  5. package/json/doctor.schema.json +144 -0
  6. package/json/driver-message.schema.json +912 -0
  7. package/json/dyno-toml.schema.json +80 -0
  8. package/json/exposure-checkpoint.schema.json +97 -0
  9. package/json/games.schema.json +66 -0
  10. package/json/library-bucket.schema.json +22 -0
  11. package/json/library.schema.json +19 -0
  12. package/json/measures.json +407 -0
  13. package/json/probe-event.schema.json +465 -0
  14. package/json/result.schema.json +2140 -0
  15. package/json/run-request.schema.json +161 -0
  16. package/json/unclean-exit.schema.json +99 -0
  17. package/package.json +22 -4
  18. package/ts/AssetChange.ts +8 -0
  19. package/ts/AssetChangeKind.ts +6 -0
  20. package/ts/AssignedRole.ts +7 -0
  21. package/ts/AudioState.ts +6 -0
  22. package/ts/BrowserEngine.ts +6 -0
  23. package/ts/BudgetId.ts +6 -0
  24. package/ts/BudgetLimit.ts +8 -0
  25. package/ts/BudgetRef.ts +15 -0
  26. package/ts/BudgetVerdict.ts +22 -0
  27. package/ts/BuildLink.ts +6 -0
  28. package/ts/BuildManifest.ts +17 -0
  29. package/ts/CatalogueEntry.ts +7 -0
  30. package/ts/CatalogueResult.ts +22 -0
  31. package/ts/CheckResult.ts +16 -0
  32. package/ts/CompareDeterministic.ts +42 -0
  33. package/ts/CompareResult.ts +12 -0
  34. package/ts/CompareRun.ts +17 -0
  35. package/ts/ContextKind.ts +6 -0
  36. package/ts/Covers.ts +6 -0
  37. package/ts/Deterministic.ts +35 -0
  38. package/ts/DeviceClass.ts +6 -0
  39. package/ts/DeviceEntry.ts +25 -0
  40. package/ts/DeviceKind.ts +6 -0
  41. package/ts/DoctorCheck.ts +16 -0
  42. package/ts/DoctorCheckId.ts +6 -0
  43. package/ts/DoctorReport.ts +20 -0
  44. package/ts/DriverInfo.ts +10 -0
  45. package/ts/DriverMessage.ts +37 -0
  46. package/ts/Environment.ts +13 -0
  47. package/ts/EnvironmentBrowser.ts +23 -0
  48. package/ts/EnvironmentDevice.ts +27 -0
  49. package/ts/EnvironmentOs.ts +12 -0
  50. package/ts/Evidence.ts +11 -0
  51. package/ts/ExposureCheckpoint.ts +9 -0
  52. package/ts/ExposureSample.ts +15 -0
  53. package/ts/FileKind.ts +6 -0
  54. package/ts/Finding.ts +29 -0
  55. package/ts/Fix.ts +22 -0
  56. package/ts/FixAction.ts +7 -0
  57. package/ts/FrameState.ts +6 -0
  58. package/ts/GameHookKind.ts +6 -0
  59. package/ts/HostProfile.ts +47 -0
  60. package/ts/Inputs.ts +34 -0
  61. package/ts/Library.ts +6 -0
  62. package/ts/LibraryBucket.ts +6 -0
  63. package/ts/LiveFrame.ts +19 -0
  64. package/ts/Machine.ts +14 -0
  65. package/ts/ManifestFile.ts +12 -0
  66. package/ts/Measure.ts +50 -0
  67. package/ts/MeasureChange.ts +14 -0
  68. package/ts/MeasureEntry.ts +13 -0
  69. package/ts/MeasureId.ts +6 -0
  70. package/ts/MeasureItem.ts +15 -0
  71. package/ts/MeasureStatus.ts +6 -0
  72. package/ts/MeasureValue.ts +7 -0
  73. package/ts/MemorySample.ts +24 -0
  74. package/ts/MessageDirection.ts +6 -0
  75. package/ts/Method.ts +6 -0
  76. package/ts/OsName.ts +6 -0
  77. package/ts/ProbeEvent.ts +48 -0
  78. package/ts/ProcessMemory.ts +22 -0
  79. package/ts/ProcessRole.ts +6 -0
  80. package/ts/ProfileRef.ts +6 -0
  81. package/ts/Quantity.ts +7 -0
  82. package/ts/RelPath.ts +9 -0
  83. package/ts/Release.ts +12 -0
  84. package/ts/ResultRef.ts +15 -0
  85. package/ts/RuleId.ts +6 -0
  86. package/ts/RunCoverage.ts +8 -0
  87. package/ts/RunInfo.ts +56 -0
  88. package/ts/RunMark.ts +6 -0
  89. package/ts/RunRequest.ts +24 -0
  90. package/ts/Scenario.ts +6 -0
  91. package/ts/Scope.ts +12 -0
  92. package/ts/Severity.ts +6 -0
  93. package/ts/Shard.ts +17 -0
  94. package/ts/Source.ts +6 -0
  95. package/ts/SourceKind.ts +7 -0
  96. package/ts/Statistic.ts +7 -0
  97. package/ts/Status.ts +9 -0
  98. package/ts/Target.ts +15 -0
  99. package/ts/Tier.ts +9 -0
  100. package/ts/TierReport.ts +26 -0
  101. package/ts/Timeline.ts +21 -0
  102. package/ts/TimelineMark.ts +11 -0
  103. package/ts/TimelineMethod.ts +6 -0
  104. package/ts/TimelineProcess.ts +22 -0
  105. package/ts/ToolBuild.ts +14 -0
  106. package/ts/Uncertainty.ts +19 -0
  107. package/ts/UncertaintyKind.ts +6 -0
  108. package/ts/UncleanExit.ts +16 -0
  109. package/ts/Unit.ts +6 -0
  110. package/ts/Verdict.ts +6 -0
  111. package/ts/VerdictChange.ts +12 -0
  112. package/ts/Versions.ts +22 -0
  113. package/ts/Viewport.ts +6 -0
  114. package/ts/index.ts +97 -0
  115. package/README.md +0 -3
@@ -0,0 +1,2140 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "CheckResult",
4
+ "description": "The output of `dyno check` for one target.",
5
+ "type": "object",
6
+ "properties": {
7
+ "deterministic": {
8
+ "$ref": "#/$defs/Deterministic"
9
+ },
10
+ "result_hash": {
11
+ "description": "Lowercase hex SHA-256 of the RFC 8785 canonical JSON of `deterministic`.",
12
+ "type": "string"
13
+ },
14
+ "run": {
15
+ "$ref": "#/$defs/RunInfo"
16
+ },
17
+ "schema_version": {
18
+ "description": "Read this first. It bumps only on a breaking change.",
19
+ "type": "integer",
20
+ "format": "uint32",
21
+ "minimum": 0
22
+ }
23
+ },
24
+ "required": [
25
+ "schema_version",
26
+ "result_hash",
27
+ "deterministic",
28
+ "run"
29
+ ],
30
+ "$defs": {
31
+ "BrowserEngine": {
32
+ "description": "A browser engine.",
33
+ "oneOf": [
34
+ {
35
+ "description": "Chromium and the browsers built on it.",
36
+ "type": "string",
37
+ "const": "blink"
38
+ },
39
+ {
40
+ "description": "Safari, and every browser on iOS.",
41
+ "type": "string",
42
+ "const": "webkit"
43
+ },
44
+ {
45
+ "description": "Firefox.",
46
+ "type": "string",
47
+ "const": "gecko"
48
+ }
49
+ ]
50
+ },
51
+ "BudgetId": {
52
+ "description": "Every budget a budget file can set. Each id is the key path in the file.",
53
+ "oneOf": [
54
+ {
55
+ "description": "Decoded audio at 48 kHz.",
56
+ "type": "string",
57
+ "const": "audio.decoded_mb"
58
+ },
59
+ {
60
+ "description": "Decoded bytes at 48 kHz that a mono export of every stereo sound would save.",
61
+ "type": "string",
62
+ "const": "audio.stereo_saving_mb"
63
+ },
64
+ {
65
+ "description": "A sprite map's share of time that no sprite covers.",
66
+ "type": "string",
67
+ "const": "audio.silence_share_bp"
68
+ },
69
+ {
70
+ "description": "Decoded bytes at 48 kHz in a sprite map's time that no sprite covers.",
71
+ "type": "string",
72
+ "const": "audio.silence_saving_mb"
73
+ },
74
+ {
75
+ "description": "Decoded textures, one copy.",
76
+ "type": "string",
77
+ "const": "textures.decoded_mb"
78
+ },
79
+ {
80
+ "description": "Antialias on.",
81
+ "type": "string",
82
+ "const": "render.antialias"
83
+ },
84
+ {
85
+ "description": "Resolution uncapped.",
86
+ "type": "string",
87
+ "const": "render.resolution_uncapped"
88
+ },
89
+ {
90
+ "description": "Two or more files with the same SHA-256.",
91
+ "type": "string",
92
+ "const": "files.duplicate"
93
+ }
94
+ ]
95
+ },
96
+ "BudgetLimit": {
97
+ "description": "A budget's limit as a verdict or rule reads it: sizes in bytes, shares in basis points, and\nflags as a severity.",
98
+ "oneOf": [
99
+ {
100
+ "description": "Byte limits. A value above `warn_above` warns, and a value above `fail_above` fails.",
101
+ "type": "object",
102
+ "properties": {
103
+ "fail_above": {
104
+ "type": [
105
+ "integer",
106
+ "null"
107
+ ],
108
+ "format": "uint64",
109
+ "minimum": 0
110
+ },
111
+ "kind": {
112
+ "type": "string",
113
+ "const": "bytes"
114
+ },
115
+ "warn_above": {
116
+ "type": [
117
+ "integer",
118
+ "null"
119
+ ],
120
+ "format": "uint64",
121
+ "minimum": 0
122
+ }
123
+ },
124
+ "required": [
125
+ "kind"
126
+ ]
127
+ },
128
+ {
129
+ "description": "The verdict when the flag is on.",
130
+ "type": "object",
131
+ "properties": {
132
+ "kind": {
133
+ "type": "string",
134
+ "const": "flag"
135
+ },
136
+ "when_on": {
137
+ "$ref": "#/$defs/Severity"
138
+ }
139
+ },
140
+ "required": [
141
+ "kind",
142
+ "when_on"
143
+ ]
144
+ },
145
+ {
146
+ "description": "Share limits in basis points. A value above `warn_above` warns, and a value above\n`fail_above` fails.",
147
+ "type": "object",
148
+ "properties": {
149
+ "fail_above": {
150
+ "type": [
151
+ "integer",
152
+ "null"
153
+ ],
154
+ "format": "uint64",
155
+ "minimum": 0
156
+ },
157
+ "kind": {
158
+ "type": "string",
159
+ "const": "basis_points"
160
+ },
161
+ "warn_above": {
162
+ "type": [
163
+ "integer",
164
+ "null"
165
+ ],
166
+ "format": "uint64",
167
+ "minimum": 0
168
+ }
169
+ },
170
+ "required": [
171
+ "kind"
172
+ ]
173
+ }
174
+ ]
175
+ },
176
+ "BudgetRef": {
177
+ "description": "Which budget file a result used.",
178
+ "type": "object",
179
+ "properties": {
180
+ "name": {
181
+ "description": "The file's `name`, if it has one.",
182
+ "type": [
183
+ "string",
184
+ "null"
185
+ ]
186
+ },
187
+ "sha256": {
188
+ "description": "Lowercase hex SHA-256 of the parsed budgets as canonical JSON. The same budgets in TOML\nand JSON give the same hash, and comments do not change it.",
189
+ "type": "string"
190
+ }
191
+ },
192
+ "required": [
193
+ "sha256"
194
+ ]
195
+ },
196
+ "BudgetVerdict": {
197
+ "description": "The verdict of one budget. Only budgets that `dyno-score` checks get one, never a rule's\nthreshold.",
198
+ "type": "object",
199
+ "properties": {
200
+ "actual": {
201
+ "description": "The measure's value. Null when it has no usable value: none, a status other than measured\nor estimated, or a bad number.",
202
+ "anyOf": [
203
+ {
204
+ "$ref": "#/$defs/MeasureValue"
205
+ },
206
+ {
207
+ "type": "null"
208
+ }
209
+ ]
210
+ },
211
+ "budget": {
212
+ "$ref": "#/$defs/BudgetId"
213
+ },
214
+ "limit": {
215
+ "$ref": "#/$defs/BudgetLimit"
216
+ },
217
+ "measure": {
218
+ "$ref": "#/$defs/MeasureId"
219
+ },
220
+ "verdict": {
221
+ "description": "A partial value, one whose scope has a `not_covered` entry, is a floor. It gives a verdict\nonly when the full value could not give a worse one. Otherwise the verdict is incomplete.",
222
+ "$ref": "#/$defs/Verdict"
223
+ }
224
+ },
225
+ "required": [
226
+ "budget",
227
+ "measure",
228
+ "verdict",
229
+ "limit"
230
+ ]
231
+ },
232
+ "BuildLink": {
233
+ "description": "How a run's evidence ties to the build it names.",
234
+ "oneOf": [
235
+ {
236
+ "description": "The check had no build.",
237
+ "type": "string",
238
+ "const": "none"
239
+ },
240
+ {
241
+ "description": "A static scan of the build, and no browser run.",
242
+ "type": "string",
243
+ "const": "scanned"
244
+ },
245
+ {
246
+ "description": "The browser loaded the build that dyno served (`--driver serve` or `--browser`).",
247
+ "type": "string",
248
+ "const": "served"
249
+ },
250
+ {
251
+ "description": "A build and a separate launch URL. dyno did not check that the URL serves that build.",
252
+ "type": "string",
253
+ "const": "asserted"
254
+ }
255
+ ]
256
+ },
257
+ "BuildManifest": {
258
+ "description": "Every supplied file of one build. `compare` diffs two manifests to find suspect assets.",
259
+ "type": "object",
260
+ "properties": {
261
+ "build_id": {
262
+ "description": "Lowercase hex SHA-256 over every file's path and SHA-256, sorted by path, so a rename\nchanges it. It is the join key for lab and field rows, and does not cover external URLs.\nNull until the inventory is implemented. docs/design/static-scan.md gives the exact bytes.",
263
+ "type": [
264
+ "string",
265
+ "null"
266
+ ]
267
+ },
268
+ "files": {
269
+ "description": "Sorted by path.",
270
+ "type": "array",
271
+ "items": {
272
+ "$ref": "#/$defs/ManifestFile"
273
+ }
274
+ }
275
+ },
276
+ "required": [
277
+ "files"
278
+ ]
279
+ },
280
+ "Covers": {
281
+ "description": "The set of things a value covers.",
282
+ "oneOf": [
283
+ {
284
+ "description": "Every file in the build. An inventory, not an upper bound on memory.",
285
+ "type": "string",
286
+ "const": "supplied_files"
287
+ },
288
+ {
289
+ "description": "One load of the game in the harness page, until idle.",
290
+ "type": "string",
291
+ "const": "load_to_idle"
292
+ },
293
+ {
294
+ "description": "Spins in a demo-only session.",
295
+ "type": "string",
296
+ "const": "demo_spins"
297
+ },
298
+ {
299
+ "description": "Fresh-process runs on the reference iPhone.",
300
+ "type": "string",
301
+ "const": "fresh_device_runs"
302
+ },
303
+ {
304
+ "description": "Whatever the model's static and lab inputs cover.",
305
+ "type": "string",
306
+ "const": "model_inputs"
307
+ }
308
+ ]
309
+ },
310
+ "Deterministic": {
311
+ "description": "Everything the same build, tool version and budget file give on any machine.",
312
+ "type": "object",
313
+ "properties": {
314
+ "build": {
315
+ "description": "The build's files and id. Null when the check had no build folder, or the inventory has\nnot listed it yet.",
316
+ "anyOf": [
317
+ {
318
+ "$ref": "#/$defs/BuildManifest"
319
+ },
320
+ {
321
+ "type": "null"
322
+ }
323
+ ]
324
+ },
325
+ "findings": {
326
+ "description": "Static rule findings. Sorted by rule id, then by canonical JSON.",
327
+ "type": "array",
328
+ "items": {
329
+ "$ref": "#/$defs/Finding"
330
+ }
331
+ },
332
+ "inputs": {
333
+ "$ref": "#/$defs/Inputs"
334
+ },
335
+ "measures": {
336
+ "description": "Static measures. Sorted by id.",
337
+ "type": "array",
338
+ "items": {
339
+ "$ref": "#/$defs/Measure"
340
+ }
341
+ },
342
+ "run_coverage": {
343
+ "$ref": "#/$defs/RunCoverage"
344
+ },
345
+ "status": {
346
+ "description": "The overall status. The exit code comes from it alone.",
347
+ "$ref": "#/$defs/Status"
348
+ },
349
+ "verdicts": {
350
+ "description": "One verdict per budget the budget file sets. Sorted by budget id.",
351
+ "type": "array",
352
+ "items": {
353
+ "$ref": "#/$defs/BudgetVerdict"
354
+ }
355
+ },
356
+ "versions": {
357
+ "$ref": "#/$defs/Versions"
358
+ }
359
+ },
360
+ "required": [
361
+ "versions",
362
+ "inputs",
363
+ "status",
364
+ "run_coverage",
365
+ "measures",
366
+ "verdicts",
367
+ "findings"
368
+ ]
369
+ },
370
+ "DeviceClass": {
371
+ "description": "The phone class a tier is for.",
372
+ "oneOf": [
373
+ {
374
+ "description": "The Phase 1 reference: an iPhone with 6 GB of memory.",
375
+ "type": "string",
376
+ "const": "reference_iphone"
377
+ }
378
+ ]
379
+ },
380
+ "DeviceKind": {
381
+ "description": "A kind of device.",
382
+ "type": "string",
383
+ "enum": [
384
+ "phone",
385
+ "tablet",
386
+ "desktop"
387
+ ]
388
+ },
389
+ "DriverInfo": {
390
+ "description": "The browser driver behind a run: the Rust CDP driver, or a fallback subprocess.",
391
+ "type": "object",
392
+ "properties": {
393
+ "browser": {
394
+ "description": "The browser and its version, such as \"Chromium 140.0.7339.0\".",
395
+ "type": [
396
+ "string",
397
+ "null"
398
+ ]
399
+ },
400
+ "name": {
401
+ "type": "string"
402
+ },
403
+ "version": {
404
+ "type": "string"
405
+ }
406
+ },
407
+ "required": [
408
+ "name",
409
+ "version"
410
+ ]
411
+ },
412
+ "Environment": {
413
+ "description": "What ran a check. A field is null when the driver does not say.",
414
+ "type": "object",
415
+ "properties": {
416
+ "browser": {
417
+ "anyOf": [
418
+ {
419
+ "$ref": "#/$defs/EnvironmentBrowser"
420
+ },
421
+ {
422
+ "type": "null"
423
+ }
424
+ ]
425
+ },
426
+ "device": {
427
+ "description": "Null for a desktop browser.",
428
+ "anyOf": [
429
+ {
430
+ "$ref": "#/$defs/EnvironmentDevice"
431
+ },
432
+ {
433
+ "type": "null"
434
+ }
435
+ ]
436
+ },
437
+ "os": {
438
+ "anyOf": [
439
+ {
440
+ "$ref": "#/$defs/EnvironmentOs"
441
+ },
442
+ {
443
+ "type": "null"
444
+ }
445
+ ]
446
+ }
447
+ }
448
+ },
449
+ "EnvironmentBrowser": {
450
+ "description": "The browser that loaded the game.",
451
+ "type": "object",
452
+ "properties": {
453
+ "engine": {
454
+ "description": "Null when dyno does not know the browser's engine.",
455
+ "anyOf": [
456
+ {
457
+ "$ref": "#/$defs/BrowserEngine"
458
+ },
459
+ {
460
+ "type": "null"
461
+ }
462
+ ]
463
+ },
464
+ "headless": {
465
+ "description": "Whether the browser ran with no window. Null when the driver does not say.",
466
+ "type": [
467
+ "boolean",
468
+ "null"
469
+ ]
470
+ },
471
+ "name": {
472
+ "description": "Such as \"Chromium\" or \"Safari\".",
473
+ "type": "string"
474
+ },
475
+ "version": {
476
+ "description": "Such as \"152.0.7977.82\". Null when the driver does not say.",
477
+ "type": [
478
+ "string",
479
+ "null"
480
+ ]
481
+ }
482
+ },
483
+ "required": [
484
+ "name"
485
+ ]
486
+ },
487
+ "EnvironmentDevice": {
488
+ "description": "The device the browser ran on.",
489
+ "type": "object",
490
+ "properties": {
491
+ "key": {
492
+ "description": "The device catalogue's key, such as \"iphone-14-pro-max\": the model name in lower case,\nwith one `-` for each run of other characters. A model the catalogue does not list keeps\nits own key. Null when the model is not known, as from a user agent, which names only\n\"iPhone\".",
493
+ "type": [
494
+ "string",
495
+ "null"
496
+ ]
497
+ },
498
+ "kind": {
499
+ "$ref": "#/$defs/DeviceKind"
500
+ },
501
+ "memory_bytes": {
502
+ "description": "The device's memory, from a device run's trace. Null otherwise.",
503
+ "type": [
504
+ "integer",
505
+ "null"
506
+ ],
507
+ "format": "uint64",
508
+ "minimum": 0
509
+ },
510
+ "name": {
511
+ "description": "The model as the trace names it, such as \"iPhone 14 Pro Max\", or the kind of device a user\nagent names, such as \"iPhone\". Never the device's own name, which is often its owner's.",
512
+ "type": "string"
513
+ },
514
+ "vendor": {
515
+ "description": "Such as \"Apple\".",
516
+ "type": "string"
517
+ }
518
+ },
519
+ "required": [
520
+ "name",
521
+ "vendor",
522
+ "kind"
523
+ ]
524
+ },
525
+ "EnvironmentOs": {
526
+ "description": "The operating system the browser ran on.",
527
+ "type": "object",
528
+ "properties": {
529
+ "name": {
530
+ "$ref": "#/$defs/OsName"
531
+ },
532
+ "version": {
533
+ "description": "Such as \"26.6.2\". Null when the source does not give it. A user agent's OS version is\nfrozen, as Safari on iOS 26 says iOS 18.6, so dyno never reads it from there.",
534
+ "type": [
535
+ "string",
536
+ "null"
537
+ ]
538
+ }
539
+ },
540
+ "required": [
541
+ "name"
542
+ ]
543
+ },
544
+ "Evidence": {
545
+ "description": "One piece of evidence for a value.",
546
+ "type": "object",
547
+ "properties": {
548
+ "detail": {
549
+ "description": "A short plain-English note, such as the files read.",
550
+ "type": "string"
551
+ },
552
+ "method": {
553
+ "$ref": "#/$defs/Method"
554
+ }
555
+ },
556
+ "required": [
557
+ "method",
558
+ "detail"
559
+ ]
560
+ },
561
+ "FileKind": {
562
+ "description": "The bucket a file counts in, for bytes by type.",
563
+ "oneOf": [
564
+ {
565
+ "type": "string",
566
+ "enum": [
567
+ "js",
568
+ "css",
569
+ "html",
570
+ "image",
571
+ "audio",
572
+ "video",
573
+ "font",
574
+ "other"
575
+ ]
576
+ },
577
+ {
578
+ "description": "GPU texture containers such as KTX2.",
579
+ "type": "string",
580
+ "const": "texture"
581
+ },
582
+ {
583
+ "description": "JSON, atlases, sprite maps and other data files.",
584
+ "type": "string",
585
+ "const": "data"
586
+ }
587
+ ]
588
+ },
589
+ "Finding": {
590
+ "description": "The outcome of one rule, with fixes that clear it.\n\nA static finding counts toward the status. A lab finding is in the run part, so it does not.",
591
+ "type": "object",
592
+ "properties": {
593
+ "fixes": {
594
+ "description": "Fixes that clear the finding. Sorted by path, then action, then bytes saved, then the\nrest of the fix.",
595
+ "type": "array",
596
+ "items": {
597
+ "$ref": "#/$defs/Fix"
598
+ }
599
+ },
600
+ "measures": {
601
+ "description": "The measures the rule read. Sorted by id.",
602
+ "type": "array",
603
+ "items": {
604
+ "$ref": "#/$defs/MeasureId"
605
+ }
606
+ },
607
+ "phase": {
608
+ "description": "The plan phase that builds this rule: 1 or 2.",
609
+ "type": "integer",
610
+ "format": "uint8",
611
+ "maximum": 255,
612
+ "minimum": 0
613
+ },
614
+ "reason": {
615
+ "description": "Why the verdict is incomplete. Null otherwise.",
616
+ "type": [
617
+ "string",
618
+ "null"
619
+ ]
620
+ },
621
+ "rule": {
622
+ "$ref": "#/$defs/RuleId"
623
+ },
624
+ "verdict": {
625
+ "$ref": "#/$defs/Verdict"
626
+ }
627
+ },
628
+ "required": [
629
+ "rule",
630
+ "verdict",
631
+ "phase",
632
+ "measures",
633
+ "fixes"
634
+ ]
635
+ },
636
+ "Fix": {
637
+ "description": "One change that clears a finding, and what it saves.",
638
+ "type": "object",
639
+ "properties": {
640
+ "action": {
641
+ "$ref": "#/$defs/FixAction"
642
+ },
643
+ "path": {
644
+ "description": "The file to change. Null when the fix is in the bundle's code or config.",
645
+ "anyOf": [
646
+ {
647
+ "$ref": "#/$defs/RelPath"
648
+ },
649
+ {
650
+ "type": "null"
651
+ }
652
+ ]
653
+ },
654
+ "saves_bytes": {
655
+ "description": "Estimated bytes the fix saves, by the same arithmetic as the measure.",
656
+ "type": "integer",
657
+ "format": "uint64",
658
+ "minimum": 0
659
+ },
660
+ "saves_decoded_bytes": {
661
+ "description": "Decoded bytes the fix also saves, when `saves_bytes` counts download bytes. Removing a\ncopy saves its file's bytes, and the decoded bytes of an image or sound the game loads\nonce per copy. Null for other fixes, whose `saves_bytes` are decoded bytes already.",
662
+ "type": [
663
+ "integer",
664
+ "null"
665
+ ],
666
+ "format": "uint64",
667
+ "minimum": 0
668
+ }
669
+ },
670
+ "required": [
671
+ "action",
672
+ "saves_bytes"
673
+ ]
674
+ },
675
+ "FixAction": {
676
+ "description": "What to change. Declared in the alphabetical order of the JSON names, so fixes for the same\npath sort by name.",
677
+ "oneOf": [
678
+ {
679
+ "description": "Cap the renderer's resolution below the device pixel ratio.",
680
+ "type": "string",
681
+ "const": "cap_resolution"
682
+ },
683
+ {
684
+ "description": "Mix a stereo file down to mono.",
685
+ "type": "string",
686
+ "const": "downmix_to_mono"
687
+ },
688
+ {
689
+ "description": "Keep the one copy the game loads of a set of identical files, and delete the others. The\npath names the set by its first path, when the build does not show which copy loads.",
690
+ "type": "string",
691
+ "const": "keep_one_copy"
692
+ },
693
+ {
694
+ "description": "Delete a file that repeats another one.",
695
+ "type": "string",
696
+ "const": "remove_duplicate"
697
+ },
698
+ {
699
+ "description": "Play audio through a 24 kHz context, or resample the file to 24 kHz.",
700
+ "type": "string",
701
+ "const": "resample_to24k"
702
+ },
703
+ {
704
+ "description": "Use smaller or GPU-compressed textures.",
705
+ "type": "string",
706
+ "const": "shrink_textures"
707
+ },
708
+ {
709
+ "description": "Cut the silence out of a sprite map.",
710
+ "type": "string",
711
+ "const": "trim_sprite_silence"
712
+ },
713
+ {
714
+ "description": "Turn antialias off.",
715
+ "type": "string",
716
+ "const": "turn_off_antialias"
717
+ }
718
+ ]
719
+ },
720
+ "Inputs": {
721
+ "description": "What the check was asked to do. Paths and URLs are in `run.target`.",
722
+ "type": "object",
723
+ "properties": {
724
+ "budget": {
725
+ "description": "Null when no budget file was given.",
726
+ "anyOf": [
727
+ {
728
+ "$ref": "#/$defs/BudgetRef"
729
+ },
730
+ {
731
+ "type": "null"
732
+ }
733
+ ]
734
+ },
735
+ "build": {
736
+ "description": "Whether a build folder or a .zip of one was given.",
737
+ "type": "boolean"
738
+ },
739
+ "device": {
740
+ "description": "Whether `--device` asked for an iPhone run (Phase 2).",
741
+ "type": "boolean"
742
+ },
743
+ "device_runs": {
744
+ "description": "The runs of the game, and of the empty build, that `--runs` asked for with `--device`.\nLeft out of the JSON when not given, so the hash of a check without it does not change.",
745
+ "type": [
746
+ "integer",
747
+ "null"
748
+ ],
749
+ "format": "uint32",
750
+ "minimum": 0
751
+ },
752
+ "host_profile": {
753
+ "$ref": "#/$defs/ProfileRef"
754
+ },
755
+ "launch_url": {
756
+ "description": "Whether a launch URL was given.",
757
+ "type": "boolean"
758
+ },
759
+ "scenario": {
760
+ "$ref": "#/$defs/Scenario"
761
+ },
762
+ "spins": {
763
+ "description": "Spins asked for with `--spins` (Phase 2). Null when none.",
764
+ "type": [
765
+ "integer",
766
+ "null"
767
+ ],
768
+ "format": "uint32",
769
+ "minimum": 0
770
+ }
771
+ },
772
+ "required": [
773
+ "build",
774
+ "launch_url",
775
+ "host_profile",
776
+ "scenario",
777
+ "device"
778
+ ]
779
+ },
780
+ "Machine": {
781
+ "description": "The machine dyno ran on. No host name or user name.",
782
+ "type": "object",
783
+ "properties": {
784
+ "arch": {
785
+ "description": "\"aarch64\" or \"x86_64\".",
786
+ "type": "string"
787
+ },
788
+ "cpus": {
789
+ "type": "integer",
790
+ "format": "uint32",
791
+ "minimum": 0
792
+ },
793
+ "os": {
794
+ "description": "\"macos\", \"linux\" or \"windows\".",
795
+ "type": "string"
796
+ }
797
+ },
798
+ "required": [
799
+ "os",
800
+ "arch",
801
+ "cpus"
802
+ ]
803
+ },
804
+ "ManifestFile": {
805
+ "description": "One supplied file.",
806
+ "type": "object",
807
+ "properties": {
808
+ "bytes": {
809
+ "type": "integer",
810
+ "format": "uint64",
811
+ "minimum": 0
812
+ },
813
+ "kind": {
814
+ "$ref": "#/$defs/FileKind"
815
+ },
816
+ "path": {
817
+ "$ref": "#/$defs/RelPath"
818
+ },
819
+ "sha256": {
820
+ "description": "Lowercase hex SHA-256 of the file's bytes.",
821
+ "type": "string"
822
+ }
823
+ },
824
+ "required": [
825
+ "path",
826
+ "bytes",
827
+ "sha256",
828
+ "kind"
829
+ ]
830
+ },
831
+ "Measure": {
832
+ "description": "One measure in a result.",
833
+ "type": "object",
834
+ "properties": {
835
+ "evidence": {
836
+ "description": "How the value was found. Sorted.",
837
+ "type": "array",
838
+ "items": {
839
+ "$ref": "#/$defs/Evidence"
840
+ }
841
+ },
842
+ "id": {
843
+ "$ref": "#/$defs/MeasureId"
844
+ },
845
+ "items": {
846
+ "description": "A breakdown of the value, such as one item per file. Sorted by key, then value.",
847
+ "type": "array",
848
+ "items": {
849
+ "$ref": "#/$defs/MeasureItem"
850
+ }
851
+ },
852
+ "phase": {
853
+ "description": "The plan phase that builds this measure: 1 or 2.",
854
+ "type": "integer",
855
+ "format": "uint8",
856
+ "maximum": 255,
857
+ "minimum": 0
858
+ },
859
+ "reason": {
860
+ "description": "Why the status is missing, unsupported or invalid. Null otherwise.",
861
+ "type": [
862
+ "string",
863
+ "null"
864
+ ]
865
+ },
866
+ "required": {
867
+ "description": "Whether the result needs this measure. A required measure that is missing or invalid\nstops the result from passing. Phase 2 measures are required only when a flag asks for\nthem.",
868
+ "type": "boolean"
869
+ },
870
+ "scope": {
871
+ "description": "What the value covers, and what it does not.",
872
+ "$ref": "#/$defs/Scope"
873
+ },
874
+ "source": {
875
+ "$ref": "#/$defs/Source"
876
+ },
877
+ "status": {
878
+ "$ref": "#/$defs/MeasureStatus"
879
+ },
880
+ "uncertainty": {
881
+ "description": "How close the value is to the truth.",
882
+ "$ref": "#/$defs/Uncertainty"
883
+ },
884
+ "unit": {
885
+ "$ref": "#/$defs/Unit"
886
+ },
887
+ "value": {
888
+ "description": "The value when the status is measured or estimated. An invalid measure may keep the value\nthat failed its check. Null otherwise.",
889
+ "anyOf": [
890
+ {
891
+ "$ref": "#/$defs/MeasureValue"
892
+ },
893
+ {
894
+ "type": "null"
895
+ }
896
+ ]
897
+ }
898
+ },
899
+ "required": [
900
+ "id",
901
+ "status",
902
+ "source",
903
+ "phase",
904
+ "required",
905
+ "unit",
906
+ "items",
907
+ "evidence",
908
+ "scope",
909
+ "uncertainty"
910
+ ]
911
+ },
912
+ "MeasureId": {
913
+ "description": "Every measure that Phases 1 and 2 name.",
914
+ "oneOf": [
915
+ {
916
+ "description": "Number of supplied files.",
917
+ "type": "string",
918
+ "const": "static.inventory.files"
919
+ },
920
+ {
921
+ "description": "Bytes of every supplied file, with one item per file kind.",
922
+ "type": "string",
923
+ "const": "static.bytes.by_type"
924
+ },
925
+ {
926
+ "description": "Gzip bytes of the text files (js, css, html, data), with one item per file kind.",
927
+ "type": "string",
928
+ "const": "static.bytes.compressed"
929
+ },
930
+ {
931
+ "description": "Bytes in files that repeat another file byte for byte (same SHA-256), one item per copy.",
932
+ "type": "string",
933
+ "const": "static.bytes.duplicates"
934
+ },
935
+ {
936
+ "description": "Decoded texture bytes of all supplied images, one copy: width x height x 4, plus the mip\nlevels the engine or the file makes. The uncertainty gives the range up to two copies.",
937
+ "type": "string",
938
+ "const": "static.textures.decoded"
939
+ },
940
+ {
941
+ "description": "Decoded audio bytes at 48 kHz: seconds x channels x 48,000 x 4, one item per sound\ngroup.",
942
+ "type": "string",
943
+ "const": "static.audio.decoded_48k"
944
+ },
945
+ {
946
+ "description": "Decoded audio bytes at 24 kHz, one item per sound group.",
947
+ "type": "string",
948
+ "const": "static.audio.decoded_24k"
949
+ },
950
+ {
951
+ "description": "Number of stereo sound groups, one item per group's representative file.",
952
+ "type": "string",
953
+ "const": "static.audio.stereo_files"
954
+ },
955
+ {
956
+ "description": "Number of stereo sound groups whose two channels match once decoded, so a mono export\nsounds the same. One item per decoded stereo group, with what a mono export does to it:\n`safe_to_make_mono`, `changes_the_sound` or `cancels`.",
957
+ "type": "string",
958
+ "const": "static.audio.stereo_mono_safe"
959
+ },
960
+ {
961
+ "description": "Largest share of silence in a Howler sprite map, one item per sprite map.",
962
+ "type": "string",
963
+ "const": "static.audio.sprite_silence"
964
+ },
965
+ {
966
+ "description": "Share of the decoded time of every sound group that is silent, in 10 ms windows at or\nbelow -60 dBFS. One item per decoded group.",
967
+ "type": "string",
968
+ "const": "static.audio.silence_by_decode"
969
+ },
970
+ {
971
+ "description": "Whether the bundle turns antialias on.",
972
+ "type": "string",
973
+ "const": "static.render.antialias"
974
+ },
975
+ {
976
+ "description": "Whether the bundle renders at a resolution above 2, the cap that a fix suggests.",
977
+ "type": "string",
978
+ "const": "static.render.resolution_uncapped"
979
+ },
980
+ {
981
+ "description": "The renderer the bundle asks for: webgl, webgl2, webgpu or canvas2d.",
982
+ "type": "string",
983
+ "const": "static.render.backend"
984
+ },
985
+ {
986
+ "description": "Canvas bytes: one WebGL context plus the backing store at the bundle's resolution.",
987
+ "type": "string",
988
+ "const": "static.render.canvas"
989
+ },
990
+ {
991
+ "description": "Number of libraries found in the bundle, one item per library with its version, keyed\n`<bucket>.<library>` such as `rendering.pixi` (`library::Library`).",
992
+ "type": "string",
993
+ "const": "static.bundle.engines"
994
+ },
995
+ {
996
+ "description": "Number of URLs in the bundle that point outside the build, one item per URL.\nThe build id does not cover them.",
997
+ "type": "string",
998
+ "const": "static.bundle.external_urls"
999
+ },
1000
+ {
1001
+ "description": "JavaScript bytes per source module, from source maps.",
1002
+ "type": "string",
1003
+ "const": "static.bundle.source_map"
1004
+ },
1005
+ {
1006
+ "description": "Time from navigation to the game frame's first animation frame.",
1007
+ "type": "string",
1008
+ "const": "lab.time.first_frame"
1009
+ },
1010
+ {
1011
+ "description": "Time from navigation to the host profile's ready message, such as `reels:ready`.",
1012
+ "type": "string",
1013
+ "const": "lab.time.ready"
1014
+ },
1015
+ {
1016
+ "description": "Bytes the game frame loaded by idle, one item per file kind.",
1017
+ "type": "string",
1018
+ "const": "lab.bytes.loaded_by_idle"
1019
+ },
1020
+ {
1021
+ "description": "Bytes the game frame loaded before its playable hook. Unsupported when the game sends no\nplayable hook. The host's ready message is not that signal.",
1022
+ "type": "string",
1023
+ "const": "lab.bytes.to_playable"
1024
+ },
1025
+ {
1026
+ "description": "Audio bytes decoded by idle, at the audio context's rate: length x channels x 4.",
1027
+ "type": "string",
1028
+ "const": "lab.audio.decoded"
1029
+ },
1030
+ {
1031
+ "description": "The audio context's sample rate.",
1032
+ "type": "string",
1033
+ "const": "lab.audio.context_rate"
1034
+ },
1035
+ {
1036
+ "description": "Texture bytes uploaded by idle, re-uploads included. One item per source path, such as\n`/assets/a.png`. An ImageBitmap, a `blob:` object URL or a response made from a\nfetched file keeps that file's path. Uploads with no http source are `empty` (no\npixels), `canvas` (a canvas the game drew) or `other` (data, `data:` URLs, or a field\nprobe that does not tell). A path always starts with `/`, so it never meets these.",
1037
+ "type": "string",
1038
+ "const": "lab.textures.uploaded"
1039
+ },
1040
+ {
1041
+ "description": "Texture bytes the GPU holds at idle: the last upload of each level of each texture the\ngame did not delete.",
1042
+ "type": "string",
1043
+ "const": "lab.textures.resident"
1044
+ },
1045
+ {
1046
+ "description": "Number of canvas rendering contexts the game created.",
1047
+ "type": "string",
1048
+ "const": "lab.canvas.contexts"
1049
+ },
1050
+ {
1051
+ "description": "Canvas buffer bytes from the contexts' sizes, DPR and antialias.",
1052
+ "type": "string",
1053
+ "const": "lab.canvas.bytes"
1054
+ },
1055
+ {
1056
+ "description": "JavaScript heap in use at idle. Chromium's heap, not the iPhone's.",
1057
+ "type": "string",
1058
+ "const": "lab.js_heap"
1059
+ },
1060
+ {
1061
+ "description": "Whether an audio context ran while the frame was inactive.",
1062
+ "type": "string",
1063
+ "const": "lab.inactive.audio_running"
1064
+ },
1065
+ {
1066
+ "description": "Frames per second the game rendered while inactive.",
1067
+ "type": "string",
1068
+ "const": "lab.inactive.frame_rate"
1069
+ },
1070
+ {
1071
+ "description": "Audio decode and texture upload bytes before activation. A diagnostic, never a fail.",
1072
+ "type": "string",
1073
+ "const": "lab.inactive.allocated"
1074
+ },
1075
+ {
1076
+ "description": "The game frame's device pixel ratio, in thousandths. The run is invalid unless it is 3\n(3,000).",
1077
+ "type": "string",
1078
+ "const": "lab.self_check.frame_dpr"
1079
+ },
1080
+ {
1081
+ "description": "Requests the game frame made. The run is invalid when it made none.",
1082
+ "type": "string",
1083
+ "const": "lab.self_check.frame_requests"
1084
+ },
1085
+ {
1086
+ "description": "The WebGL renderer string. CI runners often fall back to a software renderer.",
1087
+ "type": "string",
1088
+ "const": "lab.self_check.webgl_renderer"
1089
+ },
1090
+ {
1091
+ "description": "Modelled iPhone footprint while active: decoded audio at the context rate, textures,\ncanvas buffers, decoded images that are not textures, and k times the JS heap, plus a\nfixed b. The model fit sets k and b.",
1092
+ "type": "string",
1093
+ "const": "model.footprint"
1094
+ },
1095
+ {
1096
+ "description": "Modelled iPhone footprint while inactive, for the preloadable tier.",
1097
+ "type": "string",
1098
+ "const": "model.footprint_inactive"
1099
+ },
1100
+ {
1101
+ "description": "Spins played in the demo-only session.",
1102
+ "type": "string",
1103
+ "const": "play.spins"
1104
+ },
1105
+ {
1106
+ "description": "Peak bytes during the spin loop.",
1107
+ "type": "string",
1108
+ "const": "play.peak"
1109
+ },
1110
+ {
1111
+ "description": "Memory growth per 100 spins (Theil-Sen slope). Negative when memory shrinks.",
1112
+ "type": "string",
1113
+ "const": "play.growth_per_100_spins"
1114
+ },
1115
+ {
1116
+ "description": "Peak bytes during a bonus round.",
1117
+ "type": "string",
1118
+ "const": "play.bonus_peak"
1119
+ },
1120
+ {
1121
+ "description": "The `phys_footprint` of the game's process on the iPhone, at the first sample after the\nframe went idle. Its peak in the run window is an item too, for readers from before\n`device.phys_footprint_peak`.",
1122
+ "type": "string",
1123
+ "const": "device.phys_footprint"
1124
+ },
1125
+ {
1126
+ "description": "The largest `phys_footprint` of the game's process on the iPhone in the run window,\nfrom the answer to the page's `hello` to the end of the run, with the slack of a sample\nat each end that places the run on the trace's clock.",
1127
+ "type": "string",
1128
+ "const": "device.phys_footprint_peak"
1129
+ },
1130
+ {
1131
+ "description": "The median `phys_footprint` peak of N runs of the game, minus the median peak of N runs\nof the empty build, on the same phone. Needs `--runs`. A difference, so a game within\nthe noise of the empty page can give a value below 0.",
1132
+ "type": "string",
1133
+ "const": "device.footprint_over_baseline"
1134
+ },
1135
+ {
1136
+ "description": "Runs that the iPhone killed: the game's process left the trace before the run ended, as\na process that iOS kills does. One item per kill.",
1137
+ "type": "string",
1138
+ "const": "device.kills"
1139
+ }
1140
+ ]
1141
+ },
1142
+ "MeasureItem": {
1143
+ "description": "One part of a measure's breakdown. Items sort by key, then value.",
1144
+ "type": "object",
1145
+ "properties": {
1146
+ "key": {
1147
+ "description": "A relative path with '/', a file kind, a sprite name, an engine name or a URL.",
1148
+ "type": "string"
1149
+ },
1150
+ "value": {
1151
+ "description": "In the measure's unit, or text for a fact such as an engine version.",
1152
+ "$ref": "#/$defs/MeasureValue"
1153
+ }
1154
+ },
1155
+ "required": [
1156
+ "key",
1157
+ "value"
1158
+ ]
1159
+ },
1160
+ "MeasureStatus": {
1161
+ "description": "How far to trust a measure. \"Not measured\" is never 0.",
1162
+ "oneOf": [
1163
+ {
1164
+ "description": "Observed directly in a browser run or on a phone.",
1165
+ "type": "string",
1166
+ "const": "measured"
1167
+ },
1168
+ {
1169
+ "description": "Computed from the build's files or from the model. Never a measure of iPhone memory.",
1170
+ "type": "string",
1171
+ "const": "estimated"
1172
+ },
1173
+ {
1174
+ "description": "dyno cannot measure this for this target, for example bytes to playable when the game\nsends no playable hook.",
1175
+ "type": "string",
1176
+ "const": "unsupported"
1177
+ },
1178
+ {
1179
+ "description": "There is no value.",
1180
+ "type": "string",
1181
+ "const": "missing"
1182
+ },
1183
+ {
1184
+ "description": "A value failed a check, for example a frame DPR that is not 3. The value stays, unless it\nwas outside the safe integer range.",
1185
+ "type": "string",
1186
+ "const": "invalid"
1187
+ }
1188
+ ]
1189
+ },
1190
+ "MeasureValue": {
1191
+ "description": "A value. Integers carry every number, so float formatting never changes the output. They are\nsigned, because a change such as memory growth can be negative.",
1192
+ "anyOf": [
1193
+ {
1194
+ "type": "integer",
1195
+ "format": "int64"
1196
+ },
1197
+ {
1198
+ "type": "boolean"
1199
+ },
1200
+ {
1201
+ "type": "string"
1202
+ }
1203
+ ]
1204
+ },
1205
+ "MemorySample": {
1206
+ "description": "One process's memory at one moment.",
1207
+ "type": "object",
1208
+ "properties": {
1209
+ "private_bytes": {
1210
+ "description": "The memory that only this process holds, as the OS counts it for the process (see\n[`TimelineMethod`]).",
1211
+ "type": "integer",
1212
+ "format": "uint64",
1213
+ "minimum": 0
1214
+ },
1215
+ "resident_bytes": {
1216
+ "description": "Resident memory, shared pages included. Null when the method does not read it.",
1217
+ "type": [
1218
+ "integer",
1219
+ "null"
1220
+ ],
1221
+ "format": "uint64",
1222
+ "minimum": 0
1223
+ },
1224
+ "shared_bytes": {
1225
+ "description": "Resident shared memory, such as the buffers the GPU process and the renderers share. Null\nwhen the method does not read it.",
1226
+ "type": [
1227
+ "integer",
1228
+ "null"
1229
+ ],
1230
+ "format": "uint64",
1231
+ "minimum": 0
1232
+ },
1233
+ "t_ms": {
1234
+ "description": "Since the navigation, on the driver's clock.",
1235
+ "type": "integer",
1236
+ "format": "uint64",
1237
+ "minimum": 0
1238
+ }
1239
+ },
1240
+ "required": [
1241
+ "t_ms",
1242
+ "private_bytes"
1243
+ ]
1244
+ },
1245
+ "Method": {
1246
+ "description": "How a value was found. Declared in alphabetical order, so evidence sorts by name.",
1247
+ "oneOf": [
1248
+ {
1249
+ "description": "Decoding a sound's samples.",
1250
+ "type": "string",
1251
+ "const": "audio_decode"
1252
+ },
1253
+ {
1254
+ "description": "A scan of the JavaScript bundle.",
1255
+ "type": "string",
1256
+ "const": "bundle_scan"
1257
+ },
1258
+ {
1259
+ "description": "A Chrome DevTools Protocol reading, such as the JS heap.",
1260
+ "type": "string",
1261
+ "const": "devtools_protocol"
1262
+ },
1263
+ {
1264
+ "description": "A file's SHA-256.",
1265
+ "type": "string",
1266
+ "const": "file_hash"
1267
+ },
1268
+ {
1269
+ "description": "A file's header, such as image dimensions or audio duration.",
1270
+ "type": "string",
1271
+ "const": "file_header"
1272
+ },
1273
+ {
1274
+ "description": "A file's size.",
1275
+ "type": "string",
1276
+ "const": "file_size"
1277
+ },
1278
+ {
1279
+ "description": "A postMessage between the game frame and the host page.",
1280
+ "type": "string",
1281
+ "const": "host_message"
1282
+ },
1283
+ {
1284
+ "description": "The fitted memory model.",
1285
+ "type": "string",
1286
+ "const": "model"
1287
+ },
1288
+ {
1289
+ "description": "Network events on the game frame's own session.",
1290
+ "type": "string",
1291
+ "const": "network"
1292
+ },
1293
+ {
1294
+ "description": "An event from the in-page probe.",
1295
+ "type": "string",
1296
+ "const": "probe"
1297
+ },
1298
+ {
1299
+ "description": "A Howler sprite map.",
1300
+ "type": "string",
1301
+ "const": "sprite_map"
1302
+ },
1303
+ {
1304
+ "description": "The iPhone syslog.",
1305
+ "type": "string",
1306
+ "const": "syslog"
1307
+ },
1308
+ {
1309
+ "description": "The iPhone's sysmontap samples, which Instruments' Activity Monitor reads too.",
1310
+ "type": "string",
1311
+ "const": "sysmontap"
1312
+ },
1313
+ {
1314
+ "description": "An xctrace recording on the iPhone. Device runs of older dyno versions have it.",
1315
+ "type": "string",
1316
+ "const": "xctrace"
1317
+ }
1318
+ ]
1319
+ },
1320
+ "OsName": {
1321
+ "description": "An operating system.",
1322
+ "type": "string",
1323
+ "enum": [
1324
+ "linux",
1325
+ "macos",
1326
+ "windows",
1327
+ "ios",
1328
+ "ipados",
1329
+ "android"
1330
+ ]
1331
+ },
1332
+ "ProcessRole": {
1333
+ "description": "The part a process plays in the run, as far as the driver can tell.",
1334
+ "oneOf": [
1335
+ {
1336
+ "description": "It runs the game frame.",
1337
+ "type": "string",
1338
+ "const": "game"
1339
+ },
1340
+ {
1341
+ "description": "It runs the harness page.",
1342
+ "type": "string",
1343
+ "const": "page"
1344
+ },
1345
+ {
1346
+ "description": "The GPU process.",
1347
+ "type": "string",
1348
+ "const": "gpu"
1349
+ },
1350
+ {
1351
+ "description": "The browser's main process.",
1352
+ "type": "string",
1353
+ "const": "browser"
1354
+ },
1355
+ {
1356
+ "description": "Any other process, such as a spare renderer or the network service.",
1357
+ "type": "string",
1358
+ "const": "other"
1359
+ }
1360
+ ]
1361
+ },
1362
+ "ProfileRef": {
1363
+ "description": "Which host profile, and which version of it, a result used.",
1364
+ "type": "object",
1365
+ "properties": {
1366
+ "name": {
1367
+ "type": "string"
1368
+ },
1369
+ "version": {
1370
+ "type": "integer",
1371
+ "format": "uint32",
1372
+ "minimum": 0
1373
+ }
1374
+ },
1375
+ "required": [
1376
+ "name",
1377
+ "version"
1378
+ ]
1379
+ },
1380
+ "RelPath": {
1381
+ "description": "A path relative to the build root, with '/' between parts on every platform.\n\nConstruction normalizes the separators, so the same file gives the same bytes in a result on\nmacOS, Linux and Windows.",
1382
+ "type": "string"
1383
+ },
1384
+ "Release": {
1385
+ "description": "Labels the caller gives. Outside the hash, so two callers who name a release differently\nstill get the same hash.",
1386
+ "type": "object",
1387
+ "properties": {
1388
+ "game": {
1389
+ "type": [
1390
+ "string",
1391
+ "null"
1392
+ ]
1393
+ },
1394
+ "game_id": {
1395
+ "description": "The game's id on the dyno site, a UUID: from `--game-id`, or from the `dyno.toml` entry\nfor the build. Null when neither gives one.",
1396
+ "type": [
1397
+ "string",
1398
+ "null"
1399
+ ],
1400
+ "default": null
1401
+ },
1402
+ "version": {
1403
+ "type": [
1404
+ "string",
1405
+ "null"
1406
+ ]
1407
+ }
1408
+ }
1409
+ },
1410
+ "RuleId": {
1411
+ "description": "Every rule that Phases 1 and 2 name. Field reports reuse these ids.",
1412
+ "oneOf": [
1413
+ {
1414
+ "description": "Decoded audio is over the audio budget.",
1415
+ "type": "string",
1416
+ "const": "audio.pcm_over_budget"
1417
+ },
1418
+ {
1419
+ "description": "An audio file is stereo.",
1420
+ "type": "string",
1421
+ "const": "audio.stereo"
1422
+ },
1423
+ {
1424
+ "description": "A Howler sprite map is mostly silence.",
1425
+ "type": "string",
1426
+ "const": "audio.silence_share"
1427
+ },
1428
+ {
1429
+ "description": "The bundle turns antialias on or leaves the resolution uncapped.",
1430
+ "type": "string",
1431
+ "const": "render.antialias_or_resolution"
1432
+ },
1433
+ {
1434
+ "description": "Decoded textures are over the texture budget. Names the top 5 files.",
1435
+ "type": "string",
1436
+ "const": "textures.over_budget"
1437
+ },
1438
+ {
1439
+ "description": "Two or more files have the same SHA-256.",
1440
+ "type": "string",
1441
+ "const": "files.duplicate"
1442
+ },
1443
+ {
1444
+ "description": "A supplied file that nothing references. Needs the asset graph.",
1445
+ "type": "string",
1446
+ "const": "files.unreferenced"
1447
+ },
1448
+ {
1449
+ "description": "Flags a ready message that came after the host profile's limit (5 s for Reels), or\nnever came.",
1450
+ "type": "string",
1451
+ "const": "contract.ready_in_time"
1452
+ },
1453
+ {
1454
+ "description": "Flags an audio context that ran before activation.",
1455
+ "type": "string",
1456
+ "const": "contract.no_audio_before_active"
1457
+ },
1458
+ {
1459
+ "description": "Flags rendering that was not throttled while inactive: more than 1 frame a second.",
1460
+ "type": "string",
1461
+ "const": "contract.throttled_while_inactive"
1462
+ },
1463
+ {
1464
+ "description": "Flags audio decode or texture upload work before activation. Never a fail.",
1465
+ "type": "string",
1466
+ "const": "diagnostic.alloc_before_active"
1467
+ }
1468
+ ]
1469
+ },
1470
+ "RunCoverage": {
1471
+ "description": "Whether the browser and device runs that a check asked for gave every required measure.\n\nIt sits in the deterministic part, so the status and exit code never read the run part.",
1472
+ "oneOf": [
1473
+ {
1474
+ "description": "The check asked for no browser or device run.",
1475
+ "type": "string",
1476
+ "const": "not_requested"
1477
+ },
1478
+ {
1479
+ "description": "Every required run measure is present, and every run measure fits the check.",
1480
+ "type": "string",
1481
+ "const": "complete"
1482
+ },
1483
+ {
1484
+ "description": "A required run measure is missing or absent from the result.",
1485
+ "type": "string",
1486
+ "const": "missing"
1487
+ },
1488
+ {
1489
+ "description": "A required run measure is invalid, or a run measure does not fit the check. Invalid wins\nover missing.",
1490
+ "type": "string",
1491
+ "const": "invalid"
1492
+ }
1493
+ ]
1494
+ },
1495
+ "RunInfo": {
1496
+ "description": "What differs between machines and runs. The hash excludes it.\n\nA field that a later dyno added is missing from a result that an earlier one wrote, so a\nreader treats a missing or null field as not known.",
1497
+ "type": "object",
1498
+ "properties": {
1499
+ "build_link": {
1500
+ "description": "How the run's evidence ties to the build.",
1501
+ "anyOf": [
1502
+ {
1503
+ "$ref": "#/$defs/BuildLink"
1504
+ },
1505
+ {
1506
+ "type": "null"
1507
+ }
1508
+ ],
1509
+ "default": null
1510
+ },
1511
+ "driver": {
1512
+ "description": "The browser driver. Null when there was no browser run.",
1513
+ "anyOf": [
1514
+ {
1515
+ "$ref": "#/$defs/DriverInfo"
1516
+ },
1517
+ {
1518
+ "type": "null"
1519
+ }
1520
+ ]
1521
+ },
1522
+ "duration_ms": {
1523
+ "type": "integer",
1524
+ "format": "uint64",
1525
+ "minimum": 0
1526
+ },
1527
+ "environment": {
1528
+ "description": "What actually ran: the browser, the OS and the device. Null when there was no browser or\ndevice run, or when the run failed before the browser said what it was.",
1529
+ "anyOf": [
1530
+ {
1531
+ "$ref": "#/$defs/Environment"
1532
+ },
1533
+ {
1534
+ "type": "null"
1535
+ }
1536
+ ],
1537
+ "default": null
1538
+ },
1539
+ "findings": {
1540
+ "description": "Lab rule findings, such as the host contract checks. Sorted by rule id, then by the\nfinding's canonical JSON.",
1541
+ "type": "array",
1542
+ "items": {
1543
+ "$ref": "#/$defs/Finding"
1544
+ }
1545
+ },
1546
+ "machine": {
1547
+ "$ref": "#/$defs/Machine"
1548
+ },
1549
+ "measures": {
1550
+ "description": "Browser, device and model measures. Sorted by id.",
1551
+ "type": "array",
1552
+ "items": {
1553
+ "$ref": "#/$defs/Measure"
1554
+ }
1555
+ },
1556
+ "release": {
1557
+ "$ref": "#/$defs/Release"
1558
+ },
1559
+ "run_id": {
1560
+ "description": "A UUID v4 that dyno makes when the check starts. Each game of a games file gets its own,\nand an upload sends it again when it is retried, so the site keeps one run.",
1561
+ "type": [
1562
+ "string",
1563
+ "null"
1564
+ ],
1565
+ "default": null
1566
+ },
1567
+ "started_at": {
1568
+ "description": "RFC 3339 UTC time the check started.",
1569
+ "type": "string"
1570
+ },
1571
+ "target": {
1572
+ "$ref": "#/$defs/Target"
1573
+ },
1574
+ "tier": {
1575
+ "$ref": "#/$defs/TierReport"
1576
+ },
1577
+ "timeline": {
1578
+ "description": "The memory of each browser process through the browser run, with the run's steps. Null\nwhen the driver read none, such as on a machine without a sampler.",
1579
+ "anyOf": [
1580
+ {
1581
+ "$ref": "#/$defs/Timeline"
1582
+ },
1583
+ {
1584
+ "type": "null"
1585
+ }
1586
+ ],
1587
+ "default": null
1588
+ },
1589
+ "tool_build": {
1590
+ "$ref": "#/$defs/ToolBuild"
1591
+ }
1592
+ },
1593
+ "required": [
1594
+ "started_at",
1595
+ "duration_ms",
1596
+ "machine",
1597
+ "tool_build",
1598
+ "target",
1599
+ "release",
1600
+ "measures",
1601
+ "findings",
1602
+ "tier"
1603
+ ]
1604
+ },
1605
+ "RunMark": {
1606
+ "description": "The steps of a run. They sort in the order they are declared.",
1607
+ "oneOf": [
1608
+ {
1609
+ "description": "The harness page started loading the game frame.",
1610
+ "type": "string",
1611
+ "const": "navigated"
1612
+ },
1613
+ {
1614
+ "description": "The game frame went idle.",
1615
+ "type": "string",
1616
+ "const": "idle"
1617
+ },
1618
+ {
1619
+ "description": "The harness made the game active, such as by sending `reels:active` `true`.",
1620
+ "type": "string",
1621
+ "const": "activated"
1622
+ },
1623
+ {
1624
+ "description": "The harness made the game inactive.",
1625
+ "type": "string",
1626
+ "const": "deactivated"
1627
+ },
1628
+ {
1629
+ "description": "The game frame committed a new document, as a script redirect does. Probe events after\nit are on the new document's clock.",
1630
+ "type": "string",
1631
+ "const": "frame_document"
1632
+ },
1633
+ {
1634
+ "description": "The driver forced a garbage collection and sent a critical memory pressure signal, after\nthe active watch. The memory after it is what the game holds once garbage and caches go.",
1635
+ "type": "string",
1636
+ "const": "purged"
1637
+ },
1638
+ {
1639
+ "description": "The phone's kernel killed the game's process: jetsam did, or it went over a fatal memory\nlimit.",
1640
+ "type": "string",
1641
+ "const": "killed"
1642
+ },
1643
+ {
1644
+ "description": "The game's process went over a memory limit that does not kill it, such as iOS's soft\nlimit.",
1645
+ "type": "string",
1646
+ "const": "over_memory_limit"
1647
+ }
1648
+ ]
1649
+ },
1650
+ "Scenario": {
1651
+ "description": "The harness scenario of a browser run.",
1652
+ "oneOf": [
1653
+ {
1654
+ "description": "The game alone in a fresh harness page (Phase 1).",
1655
+ "type": "string",
1656
+ "const": "alone"
1657
+ },
1658
+ {
1659
+ "description": "The harness page after 10 earlier games (Phase 2).",
1660
+ "type": "string",
1661
+ "const": "aged"
1662
+ },
1663
+ {
1664
+ "description": "One game active while the next one preloads (Phase 2).",
1665
+ "type": "string",
1666
+ "const": "overlap"
1667
+ }
1668
+ ]
1669
+ },
1670
+ "Scope": {
1671
+ "description": "What a value covers, and what it does not.",
1672
+ "type": "object",
1673
+ "properties": {
1674
+ "covers": {
1675
+ "$ref": "#/$defs/Covers"
1676
+ },
1677
+ "not_covered": {
1678
+ "description": "Plain-English parts the value leaves out, such as \"a.ogg: did not decode\". Sorted. Any\nentry makes the value partial, so a budget reads it as a floor.",
1679
+ "type": "array",
1680
+ "items": {
1681
+ "type": "string"
1682
+ }
1683
+ }
1684
+ },
1685
+ "required": [
1686
+ "covers",
1687
+ "not_covered"
1688
+ ]
1689
+ },
1690
+ "Severity": {
1691
+ "description": "The verdict a flag gives when it is on.",
1692
+ "type": "string",
1693
+ "enum": [
1694
+ "allow",
1695
+ "warn",
1696
+ "fail"
1697
+ ]
1698
+ },
1699
+ "Source": {
1700
+ "description": "Where a value comes from.",
1701
+ "oneOf": [
1702
+ {
1703
+ "description": "Arithmetic on the build's files. The same on every machine.",
1704
+ "type": "string",
1705
+ "const": "static"
1706
+ },
1707
+ {
1708
+ "description": "A Chromium run in the harness page.",
1709
+ "type": "string",
1710
+ "const": "lab"
1711
+ },
1712
+ {
1713
+ "description": "A run on a real iPhone.",
1714
+ "type": "string",
1715
+ "const": "device"
1716
+ },
1717
+ {
1718
+ "description": "The fitted memory model in `dyno-score`.",
1719
+ "type": "string",
1720
+ "const": "model"
1721
+ },
1722
+ {
1723
+ "description": "The injected field probe or the host script.",
1724
+ "type": "string",
1725
+ "const": "field"
1726
+ }
1727
+ ]
1728
+ },
1729
+ "Status": {
1730
+ "description": "The overall status of a result. The exit code comes from it alone.\n\nWhen several statuses apply, the worst wins. From worst to best: fail, invalid, incomplete,\nwarn, pass. The variants below are not declared in that order.",
1731
+ "oneOf": [
1732
+ {
1733
+ "description": "None of the other statuses applies.",
1734
+ "type": "string",
1735
+ "const": "pass"
1736
+ },
1737
+ {
1738
+ "description": "A budget or a static rule warns. Nothing fails.",
1739
+ "type": "string",
1740
+ "const": "warn"
1741
+ },
1742
+ {
1743
+ "description": "A budget or a static rule fails. Nothing offsets a fail.",
1744
+ "type": "string",
1745
+ "const": "fail"
1746
+ },
1747
+ {
1748
+ "description": "A required measure is missing or absent from the result, a budget could not be checked, or\na static rule could not be checked because a value from the build is missing. Never a pass.",
1749
+ "type": "string",
1750
+ "const": "incomplete"
1751
+ },
1752
+ {
1753
+ "description": "A required measure failed its check, such as a run whose frame DPR was not 3, or a measure\nin the result does not fit the check.",
1754
+ "type": "string",
1755
+ "const": "invalid"
1756
+ }
1757
+ ]
1758
+ },
1759
+ "Target": {
1760
+ "description": "The target as given on the command line.",
1761
+ "type": "object",
1762
+ "properties": {
1763
+ "build": {
1764
+ "description": "The build folder or .zip path as given. Null when none.",
1765
+ "type": [
1766
+ "string",
1767
+ "null"
1768
+ ]
1769
+ },
1770
+ "launch_url": {
1771
+ "description": "The launch URL without its user info, query string or fragment, which can carry passwords\nand session ids. Null when none.",
1772
+ "type": [
1773
+ "string",
1774
+ "null"
1775
+ ]
1776
+ }
1777
+ }
1778
+ },
1779
+ "Tier": {
1780
+ "description": "A provisional tier. The worst gate sets it.\n\nA line is a modelled footprint limit on the reference iPhone. The proposed lines are 500 MB\nwhile active and 250 MB while inactive. The phone calibration sets them.",
1781
+ "oneOf": [
1782
+ {
1783
+ "description": "Within the line while active, minus the headroom for the worst held-out underprediction.",
1784
+ "type": "string",
1785
+ "const": "playable"
1786
+ },
1787
+ {
1788
+ "description": "Over the line, or killed in 2 of 3 fresh runs of the game alone.",
1789
+ "type": "string",
1790
+ "const": "blocked"
1791
+ },
1792
+ {
1793
+ "description": "Within the inactive line too. Reserved for Phase 2. Phase 1 never gives it.",
1794
+ "type": "string",
1795
+ "const": "preloadable"
1796
+ }
1797
+ ]
1798
+ },
1799
+ "TierReport": {
1800
+ "description": "The provisional Reels tier. Report-only: it decides nothing in Phase 1.",
1801
+ "type": "object",
1802
+ "properties": {
1803
+ "device_class": {
1804
+ "$ref": "#/$defs/DeviceClass"
1805
+ },
1806
+ "phase": {
1807
+ "description": "The plan phase that builds the tier.",
1808
+ "type": "integer",
1809
+ "format": "uint8",
1810
+ "maximum": 255,
1811
+ "minimum": 0
1812
+ },
1813
+ "reason": {
1814
+ "description": "Why there is no tier. Null otherwise.",
1815
+ "type": [
1816
+ "string",
1817
+ "null"
1818
+ ]
1819
+ },
1820
+ "status": {
1821
+ "description": "Estimated once the model is calibrated. Unsupported with no launch URL, because the\ntotal needs a JS heap reading.",
1822
+ "$ref": "#/$defs/MeasureStatus"
1823
+ },
1824
+ "tier": {
1825
+ "description": "Null unless the status is estimated.",
1826
+ "anyOf": [
1827
+ {
1828
+ "$ref": "#/$defs/Tier"
1829
+ },
1830
+ {
1831
+ "type": "null"
1832
+ }
1833
+ ]
1834
+ }
1835
+ },
1836
+ "required": [
1837
+ "status",
1838
+ "device_class",
1839
+ "phase"
1840
+ ]
1841
+ },
1842
+ "Timeline": {
1843
+ "description": "The memory of each browser process through one run.",
1844
+ "type": "object",
1845
+ "properties": {
1846
+ "every_ms": {
1847
+ "description": "The time between samples that the sampler asked for. A busy machine can make it longer.",
1848
+ "type": "integer",
1849
+ "format": "uint32",
1850
+ "minimum": 0
1851
+ },
1852
+ "marks": {
1853
+ "description": "The run's steps, on the samples' clock. Sorted by time, then step.",
1854
+ "type": "array",
1855
+ "items": {
1856
+ "$ref": "#/$defs/TimelineMark"
1857
+ }
1858
+ },
1859
+ "method": {
1860
+ "$ref": "#/$defs/TimelineMethod"
1861
+ },
1862
+ "processes": {
1863
+ "description": "Every process the sampler saw. Sorted by pid.",
1864
+ "type": "array",
1865
+ "items": {
1866
+ "$ref": "#/$defs/TimelineProcess"
1867
+ }
1868
+ }
1869
+ },
1870
+ "required": [
1871
+ "method",
1872
+ "every_ms",
1873
+ "marks",
1874
+ "processes"
1875
+ ]
1876
+ },
1877
+ "TimelineMark": {
1878
+ "description": "One step of the run, on the timeline's clock.",
1879
+ "type": "object",
1880
+ "properties": {
1881
+ "mark": {
1882
+ "$ref": "#/$defs/RunMark"
1883
+ },
1884
+ "t_ms": {
1885
+ "description": "Since the navigation, on the driver's clock.",
1886
+ "type": "integer",
1887
+ "format": "uint64",
1888
+ "minimum": 0
1889
+ }
1890
+ },
1891
+ "required": [
1892
+ "mark",
1893
+ "t_ms"
1894
+ ]
1895
+ },
1896
+ "TimelineMethod": {
1897
+ "description": "How a timeline's samples were read.",
1898
+ "oneOf": [
1899
+ {
1900
+ "description": "Linux's `/proc/<pid>/status`: `private_bytes` is `RssAnon` plus `VmSwap`, the private\nmemory footprint that Chromium reports on Linux.",
1901
+ "type": "string",
1902
+ "const": "proc"
1903
+ },
1904
+ {
1905
+ "description": "An Activity Monitor trace that xctrace exported, such as a device cloud's: `private_bytes`\nis the physical footprint that jetsam reads.",
1906
+ "type": "string",
1907
+ "const": "xctrace"
1908
+ },
1909
+ {
1910
+ "description": "The iPhone's sysmontap service, which `--device` reads: `private_bytes` is the physical\nfootprint that jetsam reads.",
1911
+ "type": "string",
1912
+ "const": "sysmontap"
1913
+ }
1914
+ ]
1915
+ },
1916
+ "TimelineProcess": {
1917
+ "description": "One process of the browser, and its memory through the run.",
1918
+ "type": "object",
1919
+ "properties": {
1920
+ "kind": {
1921
+ "description": "What the browser calls the process, such as \"renderer\", \"gpu-process\" or\n\"utility:network.mojom.NetworkService\", or its name, such as\n\"com.apple.WebKit.WebContent\".",
1922
+ "type": "string"
1923
+ },
1924
+ "pid": {
1925
+ "description": "The OS process id. It only tells processes apart within this run.",
1926
+ "type": "integer",
1927
+ "format": "uint32",
1928
+ "minimum": 0
1929
+ },
1930
+ "role": {
1931
+ "$ref": "#/$defs/ProcessRole"
1932
+ },
1933
+ "samples": {
1934
+ "description": "Sorted by time.",
1935
+ "type": "array",
1936
+ "items": {
1937
+ "$ref": "#/$defs/MemorySample"
1938
+ }
1939
+ }
1940
+ },
1941
+ "required": [
1942
+ "pid",
1943
+ "role",
1944
+ "kind",
1945
+ "samples"
1946
+ ]
1947
+ },
1948
+ "ToolBuild": {
1949
+ "description": "The dyno binary that made the result.",
1950
+ "type": "object",
1951
+ "properties": {
1952
+ "git_sha": {
1953
+ "description": "The commit the binary was built from. Null for a local build.",
1954
+ "type": [
1955
+ "string",
1956
+ "null"
1957
+ ]
1958
+ },
1959
+ "profile": {
1960
+ "description": "\"debug\" or \"release\".",
1961
+ "type": "string"
1962
+ },
1963
+ "version": {
1964
+ "type": "string"
1965
+ }
1966
+ },
1967
+ "required": [
1968
+ "version",
1969
+ "profile"
1970
+ ]
1971
+ },
1972
+ "Uncertainty": {
1973
+ "description": "How close a value is to the truth.",
1974
+ "type": "object",
1975
+ "properties": {
1976
+ "high": {
1977
+ "description": "The highest likely value, in the measure's unit.",
1978
+ "type": [
1979
+ "integer",
1980
+ "null"
1981
+ ],
1982
+ "format": "int64"
1983
+ },
1984
+ "kind": {
1985
+ "$ref": "#/$defs/UncertaintyKind"
1986
+ },
1987
+ "low": {
1988
+ "description": "The lowest likely value, in the measure's unit.",
1989
+ "type": [
1990
+ "integer",
1991
+ "null"
1992
+ ],
1993
+ "format": "int64"
1994
+ },
1995
+ "note": {
1996
+ "description": "A short plain-English note, such as \"0 to 23% below the decoded size\".",
1997
+ "type": [
1998
+ "string",
1999
+ "null"
2000
+ ]
2001
+ }
2002
+ },
2003
+ "required": [
2004
+ "kind"
2005
+ ]
2006
+ },
2007
+ "UncertaintyKind": {
2008
+ "description": "The kind of uncertainty.",
2009
+ "oneOf": [
2010
+ {
2011
+ "description": "Exact for what it covers, such as bytes on disk.",
2012
+ "type": "string",
2013
+ "const": "exact"
2014
+ },
2015
+ {
2016
+ "description": "The true value is at or above the value.",
2017
+ "type": "string",
2018
+ "const": "floor"
2019
+ },
2020
+ {
2021
+ "description": "The true value is between low and high.",
2022
+ "type": "string",
2023
+ "const": "range"
2024
+ },
2025
+ {
2026
+ "description": "The model's published error applies.",
2027
+ "type": "string",
2028
+ "const": "model_error"
2029
+ },
2030
+ {
2031
+ "description": "Not known, for example because the value is missing.",
2032
+ "type": "string",
2033
+ "const": "unknown"
2034
+ }
2035
+ ]
2036
+ },
2037
+ "Unit": {
2038
+ "description": "The unit of a value. Sizes are always integer bytes. Only a change in size can be negative.",
2039
+ "oneOf": [
2040
+ {
2041
+ "description": "A size.",
2042
+ "type": "string",
2043
+ "const": "bytes"
2044
+ },
2045
+ {
2046
+ "description": "A change in size, such as growth per 100 spins. It is negative when memory shrinks.",
2047
+ "type": "string",
2048
+ "const": "bytes_change"
2049
+ },
2050
+ {
2051
+ "description": "Milliseconds.",
2052
+ "type": "string",
2053
+ "const": "ms"
2054
+ },
2055
+ {
2056
+ "description": "A number of things.",
2057
+ "type": "string",
2058
+ "const": "count"
2059
+ },
2060
+ {
2061
+ "description": "Times a second, such as a frame rate or an audio sample rate.",
2062
+ "type": "string",
2063
+ "const": "hertz"
2064
+ },
2065
+ {
2066
+ "description": "Hundredths of a percent: 10,000 is 100%.",
2067
+ "type": "string",
2068
+ "const": "basis_points"
2069
+ },
2070
+ {
2071
+ "description": "Thousandths: a device pixel ratio of 3 is 3,000.",
2072
+ "type": "string",
2073
+ "const": "milli"
2074
+ },
2075
+ {
2076
+ "description": "True or false.",
2077
+ "type": "string",
2078
+ "const": "flag"
2079
+ },
2080
+ {
2081
+ "description": "A string, such as a renderer name.",
2082
+ "type": "string",
2083
+ "const": "text"
2084
+ }
2085
+ ]
2086
+ },
2087
+ "Verdict": {
2088
+ "description": "The outcome of one budget, rule or doctor check.",
2089
+ "oneOf": [
2090
+ {
2091
+ "type": "string",
2092
+ "enum": [
2093
+ "pass",
2094
+ "warn",
2095
+ "fail"
2096
+ ]
2097
+ },
2098
+ {
2099
+ "description": "It could not be checked, because the inputs it needs are missing, invalid or partial.",
2100
+ "type": "string",
2101
+ "const": "incomplete"
2102
+ }
2103
+ ]
2104
+ },
2105
+ "Versions": {
2106
+ "description": "The versions of everything that shapes a result.",
2107
+ "type": "object",
2108
+ "properties": {
2109
+ "model": {
2110
+ "description": "The fitted memory model's version. Null until the phone calibration sets one.",
2111
+ "type": [
2112
+ "string",
2113
+ "null"
2114
+ ]
2115
+ },
2116
+ "schema": {
2117
+ "description": "The schema version. `CheckResult::new` sets it.",
2118
+ "type": "integer",
2119
+ "format": "uint32",
2120
+ "minimum": 0
2121
+ },
2122
+ "scoring": {
2123
+ "description": "The version of the budget and rule logic in `dyno-score`.",
2124
+ "type": "integer",
2125
+ "format": "uint32",
2126
+ "minimum": 0
2127
+ },
2128
+ "tool": {
2129
+ "description": "The dyno version, such as \"0.1.0\".",
2130
+ "type": "string"
2131
+ }
2132
+ },
2133
+ "required": [
2134
+ "schema",
2135
+ "tool",
2136
+ "scoring"
2137
+ ]
2138
+ }
2139
+ }
2140
+ }