Files
Uni-Lab-OS/plan/2026-05-21_01_peptide_reset_four_checkbox_plan.md
2026-05-21 19:53:58 +08:00

14 KiB
Raw Blame History

Peptide Four-Checkbox Reset Plan

Date: 2026-05-21 16:30 Status: Proposal only / not executed

Scope

This plan replaces 2026-05-21_1556_peptide_reset_sirna_reference_plan.md for Peptide reset work.

User direction captured here:

  • take_out is unnecessary for Peptide reset.
  • Do not add a material-cache refresh checkbox.
  • Change reset to four checkbox-controlled operations:
    • 调度器复位
    • 订单状态复位
    • 库位复位
    • 仪器复位
  • The first three checkboxes default to checked.
  • The fourth checkbox, 仪器复位 / reset_devices, defaults to unchecked.
  • Replace the current public reset action with:
    • reset_auto: normal ILab action node. This is the renamed/replaced version of the current reset implementation.
    • reset_manual: manual-confirm action node with a physical cleanup confirmation message.

Evidence Summary

Current Peptide source:

  • Reset action code is currently in unilabos/devices/workstation/bioyond_studio/peptide_station/peptide_station.py.
  • Current Peptide reset selects scheduler_reset, reset_order_status, and reset_location, and passes ids to order/location resets.
  • BioyondV1RPC.reset_devices() already calls /api/lims/device/reset-devices with only apiKey and requestTime.
  • BioyondV1RPC.scheduler_reset() already calls /api/lims/scheduler/reset with only apiKey and requestTime.
  • BioyondV1RPC.reset_order_status(order_id) and reset_location(location_id) currently send data, but live probes showed that omitted data succeeds.

Live Peptide no-data reset probes using temp_benyao/peptide/peptide_station_config.example.json:

  • POST /api/lims/order/reset-order-status with request keys ["apiKey", "requestTime"] returned HTTP 200 and code=1.
  • POST /api/lims/scheduler/reset with request keys ["apiKey", "requestTime"] returned HTTP 200 and code=1.
  • POST /api/lims/storage/reset-location with request keys ["apiKey", "requestTime"] returned HTTP 200 and code=1.
  • reset-devices was not live-probed in this session, but the current RPC wrapper already sends no data.

Raw findings:

  • temp_benyao/peptide/_findings/2026-05-21_1613_reset_order_status_no_data_live.md
  • temp_benyao/peptide/_findings/2026-05-21_1615_remaining_resets_no_data_live.md

Proposed Public Actions

reset_auto

Normal action node. This is the auto/no-manual-confirm path. It replaces the current public reset action; do not leave a second public reset action unless a later compatibility request explicitly asks for an alias.

Checkbox schema rule:

  • Use plain bool annotations in the action signature.
  • Do not use Annotated[bool, Field(...)] for these checkbox params in this implementation plan.
  • The current AST registry schema path does not unwrap Annotated[...]; plain bool is required so generated JSON Schema marks the fields as boolean and the renderer can show checkboxes.
  • Put human-facing labels/descriptions in the method docstring or action description. If field-level Field(description=...) metadata is required later, add registry Annotated support and a schema test as a separate change.

Decorator shape:

@action(
    always_free=True,
    goal_default={
        "reset_scheduler": True,
        "reset_order_status": True,
        "reset_location": True,
        "reset_devices": False,
    },
    description="自动复位调度器/订单状态/库位,可选仪器复位",
)
def reset_auto(
    self,
    reset_scheduler: bool = True,
    reset_order_status: bool = True,
    reset_location: bool = True,
    reset_devices: bool = False,
    **kwargs: Any,
) -> Dict[str, Any]:
    """自动复位调度器/订单状态/库位,可选仪器复位。

    Args:
        reset_scheduler[调度器复位]: 调用 /api/lims/scheduler/reset默认勾选。
        reset_order_status[订单状态复位]: 调用 /api/lims/order/reset-order-status默认勾选。
        reset_location[库位复位]: 调用 /api/lims/storage/reset-location默认勾选。
        reset_devices[仪器复位]: 调用 /api/lims/device/reset-devices默认不勾选。
    """
    ...

Implementation notes:

  • Use real plain-bool parameters, not hidden **kwargs and not Annotated, so the action renderer can expose four checkboxes.
  • Rename/replace the existing reset action as reset_auto; the implementation should not keep the old id-shaped reset action as another public path by default.
  • Keep the three routine reset defaults checked.
  • Keep reset_devices unchecked because it can be broader and more disruptive.
  • Do not require or resolve order ids or location ids.
  • Do not call take_out.
  • Do not call refresh_material_cache.

reset_manual

Manual-confirm node. It should show the operator a physical cleanup warning, then execute the same reset helper as reset_auto after the operator confirms.

Actual manual-confirm decorator pattern in this repo:

  • Use @action(node_type=NodeType.MANUAL_CONFIRM).
  • Set always_free=True.
  • Add placeholder_keys={"assignee_user_ids": "unilabos_manual_confirm"}.
  • Include timeout_seconds: int and assignee_user_ids: list[str].
  • Add goal_default for timeout_seconds and assignee_user_ids.
  • Manual-confirm actions are normally side-effect-light, but existing Peptide start_experiment is already a MANUAL_CONFIRM action that performs scheduler start after the operator gate, so a reset-after-confirm pattern is compatible with current Peptide style.

Proposed confirmation text:

请确认G3、CEM、Tecan、撕膜机、封膜机、打标机、旋转堆栈上下料位、3个转台等位置的物料已清理完毕
请开门检查冰箱、IDOT、酶标仪、离心机、LCMS内部没有遗留物料。

Decorator/function shape:

RESET_MANUAL_CONFIRM_MESSAGE = (
    "请确认G3、CEM、Tecan、撕膜机、封膜机、打标机、旋转堆栈上下料位、3个转台等位置的物料已清理完毕\n"
    "请开门检查冰箱、IDOT、酶标仪、离心机、LCMS内部没有遗留物料。"
)

@action(
    always_free=True,
    node_type=NodeType.MANUAL_CONFIRM,
    placeholder_keys={"assignee_user_ids": "unilabos_manual_confirm"},
    goal_default={
        "reset_scheduler": True,
        "reset_order_status": True,
        "reset_location": True,
        "reset_devices": False,
        "physical_cleanup_confirmed": False,
        "timeout_seconds": 3600,
        "assignee_user_ids": [],
    },
    feedback_interval=300,
    description=RESET_MANUAL_CONFIRM_MESSAGE,
)
def reset_manual(
    self,
    reset_scheduler: bool = True,
    reset_order_status: bool = True,
    reset_location: bool = True,
    reset_devices: bool = False,
    physical_cleanup_confirmed: bool = False,
    timeout_seconds: int = 3600,
    assignee_user_ids: Optional[List[str]] = None,
    **kwargs: Any,
) -> Dict[str, Any]:
    """人工确认物理清理后执行复位。

    Args:
        reset_scheduler[调度器复位]: 调用 /api/lims/scheduler/reset默认勾选。
        reset_order_status[订单状态复位]: 调用 /api/lims/order/reset-order-status默认勾选。
        reset_location[库位复位]: 调用 /api/lims/storage/reset-location默认勾选。
        reset_devices[仪器复位]: 调用 /api/lims/device/reset-devices默认不勾选。
        physical_cleanup_confirmed[物理清理确认]: 确认清理提示中的物料检查已经完成,默认不勾选。
    """
    ...

Execution rule:

  • If physical_cleanup_confirmed is false, return a blocked result and do not call any reset API.
  • If it is true, call the same internal helper as reset_auto.
  • Return confirmation_message in the result payload so call logs preserve the exact operator instruction text.

Renderer caveat:

  • description should carry the warning in generated action metadata.
  • physical_cleanup_confirmed must remain a plain bool so it renders as a checkbox.
  • The cleanup warning should be carried by the action description and the docstring param description. Do not rely on Field(description=...) unless registry Annotated support has been implemented and tested.
  • If the current frontend does not show action descriptions or docstring field descriptions reliably, add a read-only string parameter such as confirmation_message: str = RESET_MANUAL_CONFIRM_MESSAGE with goal_default, or use a handle-based display only after renderer behavior is verified.

Shared Internal Helper

Both public actions should delegate to one helper, for example:

def _execute_reset_operations(
    self,
    *,
    reset_scheduler: bool,
    reset_order_status: bool,
    reset_location: bool,
    reset_devices: bool,
) -> Dict[str, Any]:
    ...

Call order:

  1. scheduler_reset
  2. reset_order_status
  3. reset_location
  4. reset_devices

Result shape:

{
    "selected_operations": [
        {"key": "reset_scheduler", "label": "调度器复位", "selected": True},
        {"key": "reset_order_status", "label": "订单状态复位", "selected": True},
        {"key": "reset_location", "label": "库位复位", "selected": True},
        {"key": "reset_devices", "label": "仪器复位", "selected": False},
    ],
    "executed_calls": [
        {"operation": "scheduler_reset", "endpoint": "/api/lims/scheduler/reset", "result": {"code": 1}},
    ],
    "skipped_operations": [
        {"operation": "reset_devices", "reason": "checkbox_disabled"},
    ],
    "warnings": [],
}

Failure handling:

  • Execute selected operations sequentially and record each result.
  • If an operation returns non-1 code, add a warning and continue unless the caller later requests fail-fast.
  • If an RPC method raises, catch it, record an error entry, and continue to the next selected operation unless fail-fast is introduced.

RPC Wrapper Adjustment

Adjust the two id-shaped wrappers to no-data calls:

  • BioyondV1RPC.reset_order_status() should no longer require order_id.
  • BioyondV1RPC.reset_location() should no longer require location_id.

Current no-data wrappers already exist:

  • scheduler_reset()
  • reset_devices()

Suggested RPC signatures:

def scheduler_reset(self) -> int: ...
def reset_order_status(self) -> int: ...
def reset_location(self) -> int: ...
def reset_devices(self) -> int: ...

Compatibility option:

def reset_order_status(self, order_id: Optional[str] = None) -> int:
    del order_id
    ...

def reset_location(self, location_id: Optional[str] = None) -> int:
    del location_id
    ...

This keeps older code from crashing while making the actual wire request no-data.

Adjusted Runtime API Schemas

These are the schemas Peptide reset code should target at runtime after the live no-data probes. They intentionally omit data, even though OpenAPI models nullable data for these endpoints.

All four requests use:

{
  "apiKey": "string",
  "requestTime": "date-time"
}

No data field should be sent by default.

All four responses use:

{
  "code": 1,
  "message": "",
  "timestamp": 0
}

调度器复位

Endpoint:

POST /api/lims/scheduler/reset

Adjusted request:

{
  "apiKey": "B10B5995",
  "requestTime": "2026-05-21T08:15:16.494Z"
}

Live response:

{
  "code": 1,
  "message": "",
  "timestamp": 1779351316072
}

Notes:

  • OpenAPI says data is nullable int32.
  • Live Peptide accepted omitted data.

订单状态复位

Endpoint:

POST /api/lims/order/reset-order-status

Adjusted request:

{
  "apiKey": "B10B5995",
  "requestTime": "2026-05-21T08:13:34.750Z"
}

Live response:

{
  "code": 1,
  "message": "",
  "timestamp": 1779351214422
}

Notes:

  • OpenAPI says data is nullable string.
  • Live Peptide accepted omitted data.
  • Do not model this as order-id scoped unless Bioyond confirms backend behavior.

库位复位

Endpoint:

POST /api/lims/storage/reset-location

Adjusted request:

{
  "apiKey": "B10B5995",
  "requestTime": "2026-05-21T08:15:18.924Z"
}

Live response:

{
  "code": 1,
  "message": "",
  "timestamp": 1779351318565
}

Notes:

  • OpenAPI says data is nullable string.
  • Live Peptide accepted omitted data.
  • Do not model this as location-id scoped unless Bioyond confirms backend behavior.

仪器复位

Endpoint:

POST /api/lims/device/reset-devices

Adjusted request:

{
  "apiKey": "B10B5995",
  "requestTime": "date-time"
}

Expected response shape:

{
  "code": 1,
  "message": "",
  "timestamp": 0
}

Notes:

  • OpenAPI says data is nullable string.
  • Current BioyondV1RPC.reset_devices() already sends no data.
  • This endpoint was not live-probed in the no-data reset session.
  • Keep checkbox default unchecked.

Tests To Add Before Implementation

  1. reset_auto is not NodeType.MANUAL_CONFIRM.
  2. reset_manual has node_type=NodeType.MANUAL_CONFIRM.
  3. reset_manual metadata includes:
    • always_free=True
    • placeholder_keys={"assignee_user_ids": "unilabos_manual_confirm"}
    • timeout_seconds=3600
    • assignee_user_ids=[]
    • physical_cleanup_confirmed=False
  4. Both reset actions expose four real boolean params:
    • reset_scheduler
    • reset_order_status
    • reset_location
    • reset_devices
  5. The generated registry schema marks those reset params as JSON Schema type: boolean, not object or string, so the frontend can render checkboxes.
  6. reset_auto replaces the current public reset action. Unless a later compatibility request adds an alias, no old id-shaped public reset action remains.
  7. Goal defaults are:
    • first three reset checkboxes True
    • reset_devices=False
  8. reset_manual(..., physical_cleanup_confirmed=False) does not call any RPC reset method.
  9. reset_auto() with defaults calls:
    • scheduler_reset()
    • reset_order_status()
    • reset_location()
    • not reset_devices()
  10. reset_auto(reset_devices=True) also calls reset_devices().
  11. reset_order_status() and reset_location() RPC wrappers send no data key.
  12. No reset path calls take_out.
  13. No reset path calls refresh_material_cache.

Non-Goals

  • Do not implement take_out in reset.
  • Do not refresh material_cache from reset.
  • Do not resolve order ids or location ids for reset.
  • Do not add Project/cache/browser cleanup routes.
  • Do not make reset_devices default-on.
  • Do not execute this plan during planning.