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.FileSyncRecord(synced_signature: tuple[int, int] | None = None, verified_at: str | None = None, keep_local: bool = False, verified_sha256: str | None = None)[source]#
Bases:
StructPer-file sync record stored under
sync_state.json’sfilesmap.One record per run-relative POSIX path. These records are freely mutated in place by
SyncStateWriteras a file is synced, re-modified, and re-synced.synced_signature– the(st_size, st_mtime_ns)captured at the last successful sync.Nonemeans the file has never synced. A file whose current signature differs is “modified since sync” and becomes eligible again once it re-settles.verified_at– the ISO-8601 timestamp at which the file’s SHA-256 was confirmed against the NAS copy.Noneuntil verified; a file counts toward theSYNCEDrollup only once this is set.keep_local– whenTruethe file still syncs to the NAS but is excluded from cleanup deletion.verified_sha256– the SHA-256 hex digest of the local bytes captured at sync time. Slot A of the 2026-05-26 rclone-only migration:rclone check --download --combinedconfirms the NAS copy matches the local source, and we record the local SHA here so the offline-audit affordance lost by droppingchecksums.sha256is preserved without any wire cost.Nonefor records that pre-date the migration or for files synced via the legacy verify path; reads must tolerate its absence.
- 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, equipment_label: str | None = None)[source]#
Bases:
StructOrchestrator-mode metadata. Spec §11.3 / §13 / Redesign Spec §3.3.
Always present on a v1
creation.json; the redesign collapses the single-equipment / orchestrator distinction into a per-equipment sync role and the orchestrator block always carries the producing device’s identity.The
equipment_labelfield travels with the push so the receiving orchestrator can auto-discover received equipment (Redesign Spec §3.3) without a per-equipment registry of its own. It is optional for forward-compat with creation.json files written by earlier writers. The former completeness-signal relay fields were removed by the operator-free quiescence-sync redesign (2026-05-21).
- 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.SyncStateJson(schema_version: str, cleared_at: str | None = None, files: dict[str, ~exlab_wizard.cache.sync_state_schema.FileSyncRecord]=<factory>)[source]#
Bases:
Structsync_state.jsonper-run sync-state record at schema version 1.0.Written by the orchestrator only. A freely-mutable current-state map (not append-only):
filesmaps a run-relative POSIX path to itsFileSyncRecord.cleared_atis set once the run’s staging copy has been cleaned up. The run-levelSYNCING/SYNCED/CLEAREDrollup is derived on read from this payload, never persisted as such.- files: dict[str, FileSyncRecord]#
- class exlab_wizard.api.schemas.TemplateBlock(name: str, version: str, source_path: str, run_scope: RunScope | None = None, provenance_path: str = '')[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: