exlab_wizard.orchestrator#

Orchestrator-mode runtime. Backend Spec §12, §13.

This package implements the orchestrator-mode features that activate when a config.orchestrator.staging_root is configured or any nas-mode equipment exists:

  • QuiescenceSyncPoller – background polling task that discovers every run pending NAS sync (orchestrator-staged and nas-mode) and enqueues a run once it has at least one quiescent file. It is the single operator-free auto-sync trigger (operator-free per-file NAS sync design, 2026-05-21), superseding the retired sentinel/manifest StagingWatcher.

  • list_staged_runs() – read-side query that backs the Staging UI panel and the GET /staging endpoint.

class exlab_wizard.orchestrator.QuiescenceSyncPoller(*, config, nas_sync, sync_state_writer)[source]#

Bases: object

Polls every run pending NAS sync and enqueues runs with eligible files.

Constructor dependencies are a Config, a NASSyncClient-shaped sync client, and a SyncStateWriter used (read-only) to skip files already synced at their current (st_size, st_mtime_ns) signature.

The start/stop/loop lifecycle mirrors the retired StagingWatcher; poll_once is exposed so tests can drive the poller synchronously.

Parameters:
apply_config(config)[source]#

Swap the cached config in place so a live settings save applies.

poll_once re-reads quiescence_minutes, the equipment list, the staging root, and the ignore globs from self._config every sweep, and _loop() re-reads poll_interval_seconds each iteration – so reassigning the config here makes every sync setting take effect on the next sweep without a tray relaunch.

Parameters:

config (Config)

Return type:

None

async poll_once(*, now_monotonic=None)[source]#

Run one discovery + quiescence + enqueue sweep.

now_monotonic is injectable so tests can drive the settle window with a controllable clock; production callers leave it None and the poller reads time.monotonic().

Returns the list of run paths enqueued on this sweep (in discovery order) so tests can assert exactly which runs fired.

Parameters:

now_monotonic (float | None)

Return type:

list[Path]

async start()[source]#

Start the background polling task. Idempotent.

Returns immediately; the task runs until stop() is called or the surrounding event loop tears down.

Return type:

None

async stop()[source]#

Cancel the background task and wait for it to exit. Idempotent.

Return type:

None

class exlab_wizard.orchestrator.StagedRunSummary(path, current_state, equipment_id, project_name, run_kind, file_count, byte_total, elapsed_seconds_since_last_activity, last_activity_at)[source]#

Bases: object

One row in the orchestrator’s staging panel.

Backend Spec §13.8:

  • path – absolute filesystem path of the run leaf directory.

  • current_state – the run’s derived sync_state.json rollup (RunSyncState value: "syncing" / "synced" / "cleared").

  • equipment_id – the equipment segment of the run path.

  • project_name – the LIMS project short id (parent dir).

  • run_kind"experimental" or "test".

  • file_count / byte_total – size of the staged data.

  • elapsed_seconds_since_last_activity – seconds between now_utc and the run directory’s mtime.

  • last_activity_at – ISO-8601 string of the directory mtime.

Parameters:
  • path (str)

  • current_state (str)

  • equipment_id (str)

  • project_name (str)

  • run_kind (str)

  • file_count (int)

  • byte_total (int)

  • elapsed_seconds_since_last_activity (int)

  • last_activity_at (str)

byte_total: int#
current_state: str#
elapsed_seconds_since_last_activity: int#
equipment_id: str#
file_count: int#
last_activity_at: str#
path: str#
project_name: str#
run_kind: str#
exlab_wizard.orchestrator.list_staged_runs(*, config, staging_root=None, now_utc=None, sync_state_writer=None)[source]#

Enumerate every staged run with its derived lifecycle state.

staging_root defaults to config.orchestrator.staging_root. Returns an empty list when staging_root is unset / missing.

current_state is the derived sync_state.json rollup (RunSyncState): "syncing" / "synced" / "cleared". sync_state_writer is used to read each run’s per-file record; a default SyncStateWriter is constructed when the caller omits it. A run without a sync_state.json (no sync activity yet) rolls up to "syncing".

This function is synchronous: it reads sync_state.json via the writer’s blocking SyncStateWriter.read_sync() so sync NiceGUI page handlers can call it without an event loop.

Sort order: most recent directory mtime first.

Parameters:
Return type:

list[StagedRunSummary]

Modules

quiescence_poller

Operator-free, per-file quiescence-driven NAS sync poller.

staging_clear

Operator-facing staging-clear helper.

staging_query

Read-only enumeration of runs pending NAS sync.