Extractor Format
schemas/protocol.schema.json
Source file coverage
Path:
schemas/protocol.schema.json
Lines:
696
Non-empty lines:
696
Non-empty lines covered with requirements:
183 / 696 (26.3%)
Functions:
0
Functions covered by requirements:
0 / 0 (0.0%)
1
{
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
  "$id": "urn:syntol:extractor-format:schema:protocol:2",
4
  "title": "Extractor protocol",
5
  "description": "Hardware-independent laboratory protocol for a magnetic-particle extractor",
6
  "$comment": "Semantic validation must additionally check uniqueness of local IDs; existence and type of every local and catalog reference; referenced definition revision; availability of the referenced labware at each step, including replacements on a shared seat; plate and sleeve membership in the selected device model; step preconditions; component volume totals; and parameter ranges from device and labware definitions.",
7
  "type": "object",
8
  "required": [
9
    "schema",
10
    "revision",
11
    "uid",
12
    "title",
13
    "description",
14
    "created_at",
15
    "modified_at",
16
    "device",
17
    "required_capabilities",
18
    "components",
19
    "labware",
20
    "steps",
21
    "heater_final_state"
22
  ],
23
  "properties": {
24
    "schema": {
25
      "type": "object",
26
      "required": [ "name", "revision" ],
27
      "properties": {
28
        "name": {
29
          "const": "syntol.extractor.protocol"
30
        },
31
        "revision": {
32
          "const": 2
33
        }
34
      },
35
      "additionalProperties": false
36
    },
37
    "revision": {
38
      "description": "Revision of this particular protocol",
39
      "type": "integer",
40
      "minimum": 1
41
    },
42
    "uid": {
43
      "description": "Stable global identifier of the protocol across revisions",
44
      "$ref": "#/$defs/uuid_v4"
45
    },
46
    "title": {
47
      "type": "string",
48
      "maxLength": 200,
49
      "minLength": 1
50
    },
51
    "description": {
52
      "type": "string",
53
      "maxLength": 2000,
54
      "minLength": 1
55
    },
56
    "created_at": {
57
      "type": "string",
58
      "format": "date-time"
59
    },
60
    "modified_at": {
61
      "type": "string",
62
      "format": "date-time"
63
    },
64
    "instructions": {
65
      "type": "object",
66
      "properties": {
67
        "before_run": {
68
          "type": "string",
69
          "minLength": 1
70
        },
71
        "after_run": {
72
          "type": "string",
73
          "minLength": 1
74
        }
75
      },
76
      "additionalProperties": false
77
    },
78
    "device": {
79
      "description": "Reference to the compatible device-model definition, not to a device instance or serial number",
80
      "$ref": "#/$defs/definition_reference"
81
    },
82
    "required_capabilities": {
83
      "type": "array",
84
      "minItems": 1,
85
      "uniqueItems": true,
86
      "items": {
87
        "$ref": "#/$defs/device_capability"
88
      }
89
    },
90
    "components": {
91
      "description": "Definitions of the typical well-content components used by this protocol",
92
      "type": "array",
93
      "minItems": 1,
94
      "items": {
95
        "$ref": "#/$defs/component"
96
      }
97
    },
98
    "labware": {
99
      "description": "Protocol-local labware instances and their layout",
100
      "type": "array",
101
      "minItems": 1,
102
      "items": {
103
        "oneOf": [
104
          {
105
            "$ref": "#/$defs/plate_instance"
106
          },
107
          {
108
            "$ref": "#/$defs/sleeve_comb_instance"
109
          }
110
        ]
111
      }
112
    },
113
    "steps": {
114
      "description": "Ordered protocol steps",
115
      "type": "array",
116
      "minItems": 1,
117
      "items": {
118
        "oneOf": [
119
          {
120
            "$ref": "#/$defs/collect_sleeves_step"
121
          },
122
          {
123
            "$ref": "#/$defs/release_sleeves_step"
124
          },
125
          {
126
            "$ref": "#/$defs/set_temperature_step"
127
          },
128
          {
129
            "$ref": "#/$defs/wait_temperature_step"
130
          },
131
          {
132
            "$ref": "#/$defs/mix_step"
133
          },
134
          {
135
            "$ref": "#/$defs/collect_beads_step"
136
          },
137
          {
138
            "$ref": "#/$defs/release_beads_step"
139
          }
140
        ]
141
      }
142
    },
143
    "heater_final_state": {
144
      "description": "Required state of all heaters after the protocol finishes or aborts",
145
      "const": "off"
146
    },
147
    "extensions": {
148
      "$ref": "#/$defs/extensions"
149
    }
150
  },
151
  "additionalProperties": false,
152
  "$defs": {
153
    "local_id": {
154
      "type": "string",
155
      "pattern": "^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$",
156
      "maxLength": 100
157
    },
158
    "uuid_v4": {
159
      "type": "string",
160
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
161
      "format": "uuid"
162
    },
163
    "definition_reference": {
164
      "type": "object",
165
      "required": [ "uid", "revision" ],
166
      "properties": {
167
        "uid": {
168
          "$ref": "#/$defs/uuid_v4"
169
        },
170
        "revision": {
171
          "type": "integer",
172
          "minimum": 1
173
        }
174
      },
175
      "additionalProperties": false
176
    },
177
    "extensions": {
178
      "description": "Namespaced non-standard metadata ignored unless explicitly supported",
179
      "type": "object",
180
      "propertyNames": {
181
        "pattern": "^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)+$"
182
      },
183
      "additionalProperties": true
184
    },
185
    "device_capability": {
186
      "enum": [
187
        "motion_absolute",
188
        "homing",
189
        "mixing_time",
190
        "mixing_cycles",
191
        "motion_configuration",
192
        "temperature_control",
193
        "temperature_wait",
194
        "sleeves_detection",
195
        "offsets_configurations",
196
        "plate_detection",
197
        "uv_sterilization",
198
        "status_leds",
199
        "notify_beeper",
200
        "safety_interlocks",
201
        "power_management"
202
      ]
203
    },
204
    "component": {
205
      "type": "object",
206
      "required": [ "id", "title" ],
207
      "properties": {
208
        "id": {
209
          "$ref": "#/$defs/uuid_v4"
210
        },
211
        "title": {
212
          "type": "string",
213
          "maxLength": 200,
214
          "minLength": 1
215
        },
216
        "role": {
217
          "description": "Optional protocol-local classification used for reporting and editing",
218
          "$ref": "#/$defs/local_id"
219
        },
220
        "color": {
221
          "description": "Editor-only hint; it has no execution semantics",
222
          "type": "string",
223
          "pattern": "^#[0-9A-Fa-f]{6}$"
224
        },
225
        "extensions": {
226
          "$ref": "#/$defs/extensions"
227
        }
228
      },
229
      "additionalProperties": false
230
    },
231
    "well_component": {
232
      "type": "object",
233
      "required": [ "component_id", "volume_ul" ],
234
      "properties": {
235
        "component_id": {
236
          "$ref": "#/$defs/uuid_v4"
237
        },
238
        "volume_ul": {
239
          "description": "Volume in one typical well, in microliters",
240
          "type": "number",
241
          "exclusiveMinimum": 0
242
        }
243
      },
244
      "additionalProperties": false
245
    },
246
    "plate_purpose": {
247
      "enum": [
248
        "sample_input",
249
        "lysis",
250
        "wash",
251
        "elution",
252
        "waste",
253
        "intermediate"
254
      ]
255
    },
256
    "plate_instance": {
257
      "type": "object",
258
      "required": [
259
        "id",
260
        "kind",
261
        "title",
262
        "purpose",
263
        "seat",
264
        "model",
265
        "well_content"
266
      ],
267
      "properties": {
268
        "id": {
269
          "$ref": "#/$defs/local_id"
270
        },
271
        "kind": {
272
          "const": "plate"
273
        },
274
        "title": {
275
          "type": "string",
276
          "maxLength": 200,
277
          "minLength": 1
278
        },
279
        "purpose": {
280
          "$ref": "#/$defs/plate_purpose"
281
        },
282
        "seat": {
283
          "type": "integer",
284
          "minimum": 1
285
        },
286
        "model": {
287
          "$ref": "#/$defs/definition_reference"
288
        },
289
        "well_content": {
290
          "description": "Composition of one typical working well; it does not imply that every well is occupied",
291
          "type": "array",
292
          "minItems": 1,
293
          "items": {
294
            "$ref": "#/$defs/well_component"
295
          }
296
        },
297
        "extensions": {
298
          "$ref": "#/$defs/extensions"
299
        }
300
      },
301
      "additionalProperties": false
302
    },
303
    "sleeve_comb_instance": {
304
      "type": "object",
305
      "required": [ "id", "kind", "title", "purpose", "seat", "model" ],
306
      "properties": {
307
        "id": {
308
          "$ref": "#/$defs/local_id"
309
        },
310
        "kind": {
311
          "const": "sleeve_comb"
312
        },
313
        "title": {
314
          "type": "string",
315
          "maxLength": 200,
316
          "minLength": 1
317
        },
318
        "purpose": {
319
          "const": "sleeve_storage"
320
        },
321
        "seat": {
322
          "type": "integer",
323
          "minimum": 1
324
        },
325
        "model": {
326
          "$ref": "#/$defs/definition_reference"
327
        },
328
        "extensions": {
329
          "$ref": "#/$defs/extensions"
330
        }
331
      },
332
      "additionalProperties": false
333
    },
334
    "error_policy": {
335
      "enum": [ "abort", "operator_decision" ]
336
    },
337
    "position": {
338
      "oneOf": [
339
        {
340
          "type": "object",
341
          "required": [ "reference" ],
342
          "properties": {
343
            "reference": {
344
              "enum": [ "above_liquid", "liquid_surface", "liquid_middle" ]
345
            }
346
          },
347
          "additionalProperties": false
348
        },
349
        {
350
          "type": "object",
351
          "required": [ "reference", "offset_mm" ],
352
          "properties": {
353
            "reference": {
354
              "const": "well_bottom_offset"
355
            },
356
            "offset_mm": {
357
              "type": "number",
358
              "minimum": 0
359
            }
360
          },
361
          "additionalProperties": false
362
        }
363
      ]
364
    },
365
    "collect_sleeves_step": {
366
      "type": "object",
367
      "required": [ "id", "kind", "title", "labware_id", "on_error" ],
368
      "properties": {
369
        "id": {
370
          "$ref": "#/$defs/uuid_v4"
371
        },
372
        "kind": {
373
          "const": "collect_sleeves"
374
        },
375
        "title": {
376
          "type": "string",
377
          "maxLength": 200,
378
          "minLength": 1
379
        },
380
        "labware_id": {
381
          "$ref": "#/$defs/local_id"
382
        },
383
        "on_error": {
384
          "$ref": "#/$defs/error_policy"
385
        },
386
        "extensions": {
387
          "$ref": "#/$defs/extensions"
388
        }
389
      },
390
      "additionalProperties": false
391
    },
392
    "release_sleeves_step": {
393
      "type": "object",
394
      "required": [ "id", "kind", "title", "labware_id", "on_error" ],
395
      "properties": {
396
        "id": {
397
          "$ref": "#/$defs/uuid_v4"
398
        },
399
        "kind": {
400
          "const": "release_sleeves"
401
        },
402
        "title": {
403
          "type": "string",
404
          "maxLength": 200,
405
          "minLength": 1
406
        },
407
        "labware_id": {
408
          "$ref": "#/$defs/local_id"
409
        },
410
        "on_error": {
411
          "$ref": "#/$defs/error_policy"
412
        },
413
        "extensions": {
414
          "$ref": "#/$defs/extensions"
415
        }
416
      },
417
      "additionalProperties": false
418
    },
419
    "set_temperature_step": {
420
      "type": "object",
421
      "required": [
422
        "id",
423
        "kind",
424
        "title",
425
        "labware_id",
426
        "target_celsius",
427
        "on_error"
428
      ],
429
      "properties": {
430
        "id": {
431
          "$ref": "#/$defs/uuid_v4"
432
        },
433
        "kind": {
434
          "const": "set_temperature"
435
        },
436
        "title": {
437
          "type": "string",
438
          "maxLength": 200,
439
          "minLength": 1
440
        },
441
        "labware_id": {
442
          "$ref": "#/$defs/local_id"
443
        },
444
        "target_celsius": {
445
          "type": "number"
446
        },
447
        "on_error": {
448
          "$ref": "#/$defs/error_policy"
449
        },
450
        "extensions": {
451
          "$ref": "#/$defs/extensions"
452
        }
453
      },
454
      "additionalProperties": false
455
    },
456
    "wait_temperature_step": {
457
      "type": "object",
458
      "required": [
459
        "id",
460
        "kind",
461
        "title",
462
        "labware_id",
463
        "target_celsius",
464
        "tolerance_celsius",
465
        "timeout_s",
466
        "on_error"
467
      ],
468
      "properties": {
469
        "id": {
470
          "$ref": "#/$defs/uuid_v4"
471
        },
472
        "kind": {
473
          "const": "wait_temperature"
474
        },
475
        "title": {
476
          "type": "string",
477
          "maxLength": 200,
478
          "minLength": 1
479
        },
480
        "labware_id": {
481
          "$ref": "#/$defs/local_id"
482
        },
483
        "target_celsius": {
484
          "type": "number"
485
        },
486
        "tolerance_celsius": {
487
          "type": "number",
488
          "exclusiveMinimum": 0
489
        },
490
        "timeout_s": {
491
          "type": "number",
492
          "exclusiveMinimum": 0
493
        },
494
        "on_error": {
495
          "$ref": "#/$defs/error_policy"
496
        },
497
        "extensions": {
498
          "$ref": "#/$defs/extensions"
499
        }
500
      },
501
      "additionalProperties": false
502
    },
503
    "mix_purpose": {
504
      "enum": [ "lysis", "wash", "drying", "elution", "custom" ]
505
    },
506
    "mix_stage": {
507
      "type": "object",
508
      "oneOf": [
509
        {
510
          "not": {
511
            "required": [ "cycles" ]
512
          },
513
          "required": [ "duration_s" ]
514
        },
515
        {
516
          "not": {
517
            "required": [ "duration_s" ]
518
          },
519
          "required": [ "cycles" ]
520
        }
521
      ],
522
      "required": [ "start_position", "amplitude_mm", "speed_mm_min" ],
523
      "properties": {
524
        "start_position": {
525
          "$ref": "#/$defs/position"
526
        },
527
        "amplitude_mm": {
528
          "type": "number",
529
          "exclusiveMinimum": 0
530
        },
531
        "speed_mm_min": {
532
          "type": "number",
533
          "exclusiveMinimum": 0
534
        },
535
        "duration_s": {
536
          "type": "number",
537
          "exclusiveMinimum": 0
538
        },
539
        "cycles": {
540
          "type": "integer",
541
          "minimum": 1
542
        },
543
        "pause": {
544
          "type": "object",
545
          "required": [ "position", "duration_s" ],
546
          "properties": {
547
            "position": {
548
              "$ref": "#/$defs/position"
549
            },
550
            "duration_s": {
551
              "type": "number",
552
              "minimum": 0
553
            }
554
          },
555
          "additionalProperties": false
556
        }
557
      },
558
      "additionalProperties": false
559
    },
560
    "mix_step": {
561
      "type": "object",
562
      "required": [
563
        "id",
564
        "kind",
565
        "title",
566
        "purpose",
567
        "labware_id",
568
        "repeat_count",
569
        "stages",
570
        "on_error"
571
      ],
572
      "properties": {
573
        "id": {
574
          "$ref": "#/$defs/uuid_v4"
575
        },
576
        "kind": {
577
          "const": "mix"
578
        },
579
        "title": {
580
          "type": "string",
581
          "maxLength": 200,
582
          "minLength": 1
583
        },
584
        "purpose": {
585
          "$ref": "#/$defs/mix_purpose"
586
        },
587
        "labware_id": {
588
          "$ref": "#/$defs/local_id"
589
        },
590
        "repeat_count": {
591
          "type": "integer",
592
          "minimum": 1
593
        },
594
        "stages": {
595
          "type": "array",
596
          "minItems": 1,
597
          "items": {
598
            "$ref": "#/$defs/mix_stage"
599
          }
600
        },
601
        "on_error": {
602
          "$ref": "#/$defs/error_policy"
603
        },
604
        "extensions": {
605
          "$ref": "#/$defs/extensions"
606
        }
607
      },
608
      "additionalProperties": false
609
    },
610
    "collect_beads_step": {
611
      "type": "object",
612
      "required": [
613
        "id",
614
        "kind",
615
        "title",
616
        "labware_id",
617
        "collection_cycles",
618
        "hold_duration_s",
619
        "on_error"
620
      ],
621
      "properties": {
622
        "id": {
623
          "$ref": "#/$defs/uuid_v4"
624
        },
625
        "kind": {
626
          "const": "collect_beads"
627
        },
628
        "title": {
629
          "type": "string",
630
          "maxLength": 200,
631
          "minLength": 1
632
        },
633
        "labware_id": {
634
          "$ref": "#/$defs/local_id"
635
        },
636
        "collection_cycles": {
637
          "type": "integer",
638
          "minimum": 1
639
        },
640
        "hold_duration_s": {
641
          "type": "number",
642
          "exclusiveMinimum": 0
643
        },
644
        "on_error": {
645
          "$ref": "#/$defs/error_policy"
646
        },
647
        "extensions": {
648
          "$ref": "#/$defs/extensions"
649
        }
650
      },
651
      "additionalProperties": false
652
    },
653
    "release_beads_step": {
654
      "type": "object",
655
      "required": [
656
        "id",
657
        "kind",
658
        "title",
659
        "labware_id",
660
        "start_position",
661
        "duration_s",
662
        "on_error"
663
      ],
664
      "properties": {
665
        "id": {
666
          "$ref": "#/$defs/uuid_v4"
667
        },
668
        "kind": {
669
          "const": "release_beads"
670
        },
671
        "title": {
672
          "type": "string",
673
          "maxLength": 200,
674
          "minLength": 1
675
        },
676
        "labware_id": {
677
          "$ref": "#/$defs/local_id"
678
        },
679
        "start_position": {
680
          "$ref": "#/$defs/position"
681
        },
682
        "duration_s": {
683
          "type": "number",
684
          "exclusiveMinimum": 0
685
        },
686
        "on_error": {
687
          "$ref": "#/$defs/error_policy"
688
        },
689
        "extensions": {
690
          "$ref": "#/$defs/extensions"
691
        }
692
      },
693
      "additionalProperties": false
694
    }
695
  }
696
}