exlab_wizard.api.schemas#
msgspec.Struct types for the ExLab-Wizard cache files.
This module is the single source of truth for the on-disk JSON schemas
documented in design spec §11.3 (creation.json), §11.4 (readme_fields.json),
§11.4.1 (equipment.json), §11.4.2 (test_runs.json), and §13.4 (ingest.json).
Every cache reader and writer in the codebase round-trips its bytes through
these Struct types via msgspec.json.decode(blob, type=...) /
msgspec.json.encode(obj); schema validation happens during decode in
one pass.
Design choices that affect every Struct in this module:
frozen=False– a few fields onCreationJson(sync_status,validation_overrides) are mutated in place byCreationWriterafter the initial write (Backend Spec §4.4.5; §11.3 “mutated in place” note).omit_defaults=True– field values that equal their declared default are omitted from the encoded JSON. Keeps writes compact and matches the on-disk shape shown in spec §11.3 (no nullable-but-null fields, no empty arrays for unused sub-blocks).forbid_unknown_fields=False– unknown fields are silently preserved on round-trip via the writer’sraw_extrasmechanism incache/creation_writer.py. This is required by §11.9.3 writer policy rule 2: a v0.7 writer mutating a file written by a v0.8 writer MUST NOT drop the v0.8 fields it doesn’t recognize.
The validation_overrides field is typed as list[dict[str, Any]]
rather than list[OverrideEntry | TombstoneEntry] because msgspec
struct unions require an explicit tag (tag or tag_field) and the
discriminator the spec mandates – the boolean revoked flag – cannot
be used as a tag (msgspec tags must be strings or ints). Helpers
override_entry_to_dict, tombstone_entry_to_dict, and
parse_validation_override_entry bridge the wire form (dict) and the
typed form (Struct) for callers that want either representation.
Functions
|
Serialize an |
Inspect |
|
|
Serialize a |
Classes
|
Top-level |
|
|
|
|
|
LIMS-side project identity captured at creation time. |
|
Orchestrator-mode metadata. |
|
Operator-recorded validation override entry. |
|
Resolved on-disk paths captured at creation time. |
|
Per-plugin invocation record written into |
|
Plugin worker isolation telemetry. |
|
|
|
Template provenance captured at creation time. |
|
|
|
Tombstone entry that revokes a prior override by |
- class exlab_wizard.api.schemas.CreationJson(schema_version: str, created_at: str, created_by: str, level: ~exlab_wizard.constants.enums.CreationLevel, run_kind: ~exlab_wizard.constants.enums.RunKind, lims_project: ~exlab_wizard.api.schemas.LimsProjectBlock, template: ~exlab_wizard.api.schemas.TemplateBlock, variables: dict[str, ~typing.Any], paths: ~exlab_wizard.api.schemas.PathsBlock, plugins_applied: list[~exlab_wizard.api.schemas.PluginApplied] = <factory>, orchestrator: ~exlab_wizard.api.schemas.OrchestratorBlock | None = None, sync_status: ~exlab_wizard.constants.enums.SyncStatus = SyncStatus.PENDING, validation_overrides: list[dict[str, ~typing.Any]] = <factory>)[source]#
Bases:
StructTop-level
creation.jsonpayload at schema version 1.8.Reading:
msgspec.json.decode(blob, type=CreationJson)validates every required field and rejects type errors in one pass. Unknown fields are silently ignored at the Struct boundary; the writer re-serializes them viacache/creation_writer.py’s extras pass so forward-compat is preserved.Writing: every write goes through
CacheWriter; directmsgspec.json.encode(payload)calls are reserved for tests and for the writer’s tempfile pass.- level: CreationLevel#
- lims_project: LimsProjectBlock#
- orchestrator: OrchestratorBlock | None#
- paths: PathsBlock#
- plugins_applied: list[PluginApplied]#
- run_kind: RunKind#
- sync_status: SyncStatus#
- template: TemplateBlock#
- class exlab_wizard.api.schemas.EquipmentJson(schema_version: str, id: str, label: str, configured_local_root: str, configured_nas_root: str, first_seen_at: str, last_modified_at: str)[source]#
Bases:
Structequipment.jsonat schema version 1.0. Spec §11.4.1.
- class exlab_wizard.api.schemas.IngestJson(schema_version: str, project_name: str, equipment_id: str, run_kind: RunKind, run_path: str, transport: OrchestratorTransportType, current_state: IngestState, history: list[dict[str, ~typing.Any]]=<factory>)[source]#
Bases:
Structingest.jsonorchestrator staging record at schema version 1.1. Spec §13.4.Written by the orchestrator only (not by equipment workstations). The
historylist is append-only per §13: lifecycle transitions are recorded, never overwritten.current_statemirrors the most recent history entry’sstatefor fast read-without-walk access.History entries are loose dicts because the optional fields per state (
files_received/bytes_receivedoncomplete;nas_path/checksum_fileonsync_verified) make a strict type a nuisance. The state-machine validation is performed by the writer.- current_state: IngestState#
- run_kind: RunKind#
- transport: OrchestratorTransportType#
- class exlab_wizard.api.schemas.LimsProjectBlock(uid: str, short_id: str, name_at_creation: str, source: LIMSProjectSource = LIMSProjectSource.LIVE, cache_freshness_at_use: str | None = None)[source]#
Bases:
StructLIMS-side project identity captured at creation time. Spec §11.3.
Required on project- and run-level
creation.jsonfiles at schema version >= 1.5.sourceandcache_freshness_at_usewere added in 1.8; on a 1.7 file they are absent and read as"live"/Noneper the migration policy in §11.9.2.- source: LIMSProjectSource#
- class exlab_wizard.api.schemas.OrchestratorBlock(enabled: bool, host: str, label: str)[source]#
Bases:
StructOrchestrator-mode metadata. Spec §11.3 / §13.
Absent (not null) at the parent-struct level when the wizard is running in single-equipment mode –
CreationJson.orchestratorisNonein that case andomit_defaults=Truekeeps the field out of the encoded JSON entirely.
- class exlab_wizard.api.schemas.OverrideEntry(id: str, problem_class: str, operator: str, recorded_at: str, reason: str, revoked: bool = False, expires_at: str | None = None)[source]#
Bases:
StructOperator-recorded validation override entry. Spec §11.3.
revokedisFalseon every override entry. The default is kept for ergonomic construction; the writer helperoverride_entry_to_dictensures the field is always present on the wire (the spec requires it on every entry).
- class exlab_wizard.api.schemas.PathsBlock(local: str, nas: str)[source]#
Bases:
StructResolved on-disk paths captured at creation time. Spec §11.3.
- class exlab_wizard.api.schemas.PluginApplied(plugin: str, version: str, files_affected: list[str], status: PluginStatus, isolation: PluginIsolation | None = None)[source]#
Bases:
StructPer-plugin invocation record written into
plugins_applied.Spec §6.2.4 / §11.3.
isolationwas added in schema version 1.3; older readers ignore it and older writers treat its absence as a no-op.- isolation: PluginIsolation | None#
- status: PluginStatus#
- class exlab_wizard.api.schemas.PluginIsolation(duration_ms: int, exit_code: int, peak_memory_mb: int)[source]#
Bases:
StructPlugin worker isolation telemetry. Spec §6.2.4 / §11.3 (added 1.3).
- class exlab_wizard.api.schemas.ReadmeFieldsJson(schema_version: str, generated_at: str, core_fields: dict[str, str], system_fields: dict[str, ~typing.Any], template_fields: dict[str, ~typing.Any] = <factory>, config_fields: dict[str, ~typing.Any] = <factory>, custom_fields: list[dict[str, str]] = <factory>)[source]#
Bases:
Structreadme_fields.jsonat schema version 1.1. Spec §11.4.
- class exlab_wizard.api.schemas.TemplateBlock(name: str, version: str, source_path: str, run_scope: RunScope | None = None)[source]#
Bases:
StructTemplate provenance captured at creation time. Spec §11.3.
- class exlab_wizard.api.schemas.TestRunsJson(schema_version: str, created_at: str, project: str, equipment: str, run_kind: RunKind = RunKind.TEST)[source]#
Bases:
Structtest_runs.jsonmarker at schema version 1.0. Spec §11.4.2.Filename retained from v0.5 for backward compatibility even though the parent folder was renamed to
TestRuns/in v0.6.- run_kind: RunKind#
- class exlab_wizard.api.schemas.TombstoneEntry(id: str, revokes: str, operator: str, recorded_at: str, reason: str, revoked: bool = True)[source]#
Bases:
StructTombstone entry that revokes a prior override by
id. Spec §11.3.revokedisTrueon every tombstone. The default is kept for ergonomic construction; the writer helpertombstone_entry_to_dictensures the field is always present on the wire.
- exlab_wizard.api.schemas.override_entry_to_dict(entry)[source]#
Serialize an
OverrideEntryto a wire-form dict.Uses
msgspec.structs.asdictso thatrevoked(which equals its default ofFalseon every override) is included. Spec §11.3 requires the field on every entry.- Parameters:
entry (
OverrideEntry)- Return type:
- exlab_wizard.api.schemas.parse_validation_override_entry(entry)[source]#
Inspect
entry["revoked"]andentry["revokes"]to decide which Struct shape to convert into.The spec says tombstones carry
revoked: TrueAND arevokespointer; overrides carryrevoked: FalseAND aproblem_class. Userevokedfirst,revokesas a tiebreaker for old files (pre-1.6) whererevokesmay have been the only discriminator.- Parameters:
- Return type: