exlab_wizard.controller.creation#

Creation controller. Backend Spec §4.4.1, §4.7, §4.8.

Composes the validator, template engine, plugin host, cache writers, README generator, and NAS sync client into the §4.7 state machine, driving each session from SessionState.PENDING through to SessionState.DONE (or SessionState.FAILED / SessionState.ABORTED on failure / cancel).

Validation gate (UI-spec §2). Before transitioning out of SessionState.VALIDATING the controller enforces the mandatory core-field set:

  • label non-empty after trim, ≤ 100 chars.

  • operator non-empty after trim; allowlisted when config.operators.allowlist is non-empty.

  • objective non-empty after trim, ≤ 2000 chars.

  • equipment_id is in config.equipment.

  • For runs, project_short_id matches PROJECT_SHORT_ID_PATTERN.

  • Template-required field ids and config-required field ids are all present in readme_extra.

ReadmeGenerator and NASSyncClient are injected as Protocols so this phase ships before Phase 8 / Phase 10 land their concrete implementations. NoOpReadmeGenerator writes a minimal README.md that the post-validate pass can scan; NoOpNASSync is a true no-op (the sync queue is built in Phase 10).

Classes

CreationController(*, config, validator, ...)

Drives the §4.7 state machine end-to-end.

NASSyncProtocol(*args, **kwargs)

The NAS sync surface the controller depends on.

NoOpNASSync()

No-op stand-in for NASSyncClient until Phase 10 lands.

NoOpReadmeGenerator()

Minimal README generator used until Phase 8 lands the real one.

ProjectCreateRequest(equipment_id, ...[, ...])

Inputs for CreationController.create_project().

ReadmeContext(label, operator, objective, ...)

Inputs handed to the README generator.

ReadmeGeneratorProtocol(*args, **kwargs)

The README generator surface the controller depends on.

RunCreateRequest(equipment_id, ...[, ...])

Inputs for CreationController.create_run().

SessionHandle(session_id, state, ...)

Snapshot of session state.

class exlab_wizard.controller.creation.CreationController(*, config, validator, template_engine, plugin_host, cache_creation, cache_equipment, cache_log=None, readme_generator=None, nas_sync=None, session_store=None)[source]#

Bases: object

Drives the §4.7 state machine end-to-end.

Composes the validator (Phase 4), template engine (Phase 5), plugin host (Phase 6), cache writers (Phase 3), README generator (Phase 8 – NoOp until then), and NAS sync client (Phase 10 – NoOp until then).

Parameters:
async cancel(session_id, *, discard_files=False)[source]#

Abort an in-flight session.

Pushes a None onto the resume queue (so an INPUT_REQUIRED session wakes immediately), cancels the pipeline task, and runs the cleanup hook. discard_files deletes the partially-created directory; otherwise the directory is left in place as an orphan (Backend Spec §4.7 / §4.8).

Parameters:
  • session_id (str)

  • discard_files (bool)

Return type:

None

async create_project(req)[source]#

Open a project-creation session and start the pipeline.

The session is registered with the store immediately and the pipeline runs as a background asyncio task; the returned SessionHandle reflects the post-VALIDATING state. Failures from the validation gate transition the session to FAILED synchronously before returning – so the caller can detect them on the very first response.

Parameters:

req (ProjectCreateRequest)

Return type:

SessionHandle

async create_run(req)[source]#

Open a run-creation session and start the pipeline.

Parameters:

req (RunCreateRequest)

Return type:

SessionHandle

async resume(session_id, extra_inputs)[source]#

Supply extra_inputs after a PluginInputRequired prompt.

Pushes the payload onto the session’s resume queue; the suspended pipeline wakes, re-spawns the trigger plugin’s worker with the new inputs, and continues. Backend Spec §4.7 / §6.4.

Parameters:
Return type:

SessionHandle

property session_store: SessionStore#

Expose the in-memory session store for the API surface.

async status(session_id)[source]#

Return a snapshot SessionHandle for session_id.

Parameters:

session_id (str)

Return type:

SessionHandle

async subscribe(session_id)[source]#

Yield WebSocket-event dicts for the named session.

Wraps the session’s event_queue. The iterator terminates when the session reaches a terminal state and the queue drains.

Parameters:

session_id (str)

Return type:

AsyncIterator[dict[str, Any]]

class exlab_wizard.controller.creation.NASSyncProtocol(*args, **kwargs)[source]#

Bases: Protocol

The NAS sync surface the controller depends on. Phase 10.

async enqueue(run_path)[source]#
Parameters:

run_path (Path)

Return type:

None

class exlab_wizard.controller.creation.NoOpNASSync[source]#

Bases: object

No-op stand-in for NASSyncClient until Phase 10 lands.

async enqueue(run_path)[source]#
Parameters:

run_path (Path)

Return type:

None

class exlab_wizard.controller.creation.NoOpReadmeGenerator[source]#

Bases: object

Minimal README generator used until Phase 8 lands the real one.

Writes a tiny README.md containing only the core fields so the post-validate pass has something to scan.

async generate(dst, ctx)[source]#
Parameters:
Return type:

Path

class exlab_wizard.controller.creation.ProjectCreateRequest(equipment_id, template_path, lims_project, variables, label, operator, objective, readme_extra=<factory>)[source]#

Bases: object

Inputs for CreationController.create_project().

Backend Spec §4.6.1 / UI-spec §3.1. lims_project mirrors the §11.3 lims_project block (uid, short_id, name_at_creation, source).

Parameters:
equipment_id: str#
label: str#
lims_project: dict[str, Any]#
objective: str#
operator: str#
readme_extra: dict[str, Any]#
template_path: Path#
variables: dict[str, Any]#
class exlab_wizard.controller.creation.ReadmeContext(label, operator, objective, equipment_id, project_short_id, run_kind, variables, template, extra_fields=<factory>)[source]#

Bases: object

Inputs handed to the README generator. Phase 8 owns the canonical type; this lightweight stand-in lets Phase 7 ship before Phase 8 lands.

Parameters:
equipment_id: str#
extra_fields: dict[str, Any]#
label: str#
objective: str#
operator: str#
project_short_id: str#
run_kind: str#
template: ResolvedTemplate#
variables: dict[str, Any]#
class exlab_wizard.controller.creation.ReadmeGeneratorProtocol(*args, **kwargs)[source]#

Bases: Protocol

The README generator surface the controller depends on. Phase 8.

async generate(dst, ctx)[source]#
Parameters:
Return type:

Path

class exlab_wizard.controller.creation.RunCreateRequest(equipment_id, project_short_id, template_path, run_kind, variables, label, operator, objective, readme_extra=<factory>, run_date=None, lims_project=<factory>)[source]#

Bases: object

Inputs for CreationController.create_run().

Backend Spec §4.6.1 / UI-spec §3.2 / §3.3. run_kind is the core mode flag and is immutable mid-session per UI-spec §3.3.

Parameters:
equipment_id: str#
label: str#
lims_project: dict[str, Any]#
objective: str#
operator: str#
project_short_id: str#
readme_extra: dict[str, Any]#
run_date: datetime | None = None#
run_kind: RunKind#
template_path: Path#
variables: dict[str, Any]#
class exlab_wizard.controller.creation.SessionHandle(session_id, state, current_phase, next_action)[source]#

Bases: object

Snapshot of session state. Backend Spec §4.4.1.

Parameters:
current_phase: Phase | None#
next_action: str#
session_id: str#
state: SessionState#