diff --git a/docs/plans/custom_registration_205_coordination.md b/docs/plans/custom_registration_205_coordination.md new file mode 100644 index 0000000000..029b0a5d7f --- /dev/null +++ b/docs/plans/custom_registration_205_coordination.md @@ -0,0 +1,30 @@ +Zeno (#205 DTO/service owner): Lovelace's separately assigned #230 checkpoint is +draft https://github.com/OpenHCSDev/openhcs/pull/233 at150346666, normally integrated +with main250fd1ecf / recorded PolyStore1209068. Parent requested direct shared-file +coordination; this note identifies the exact existing-owner changes, not another +catalog implementation. + +Shared `openhcs/agent/dto/functions.py` scope is the custom-registration request, +destination query/response/result and control-message member. Shared +`openhcs/agent/services/function_catalog_service.py` scope is constructor path +policy injection, `register_custom_function`, and read-only +`custom_function_registration_destination`. Function parameter/artifact-selector +metadata and your measurement/runtime contracts are untouched by this task. + +Existing `AgentPathPolicy`, `ExecutionConnectionSpec`, `ProcessIdentity` and +`CustomFunctionManager` own admission, routing, PID incarnation and native path +derivation. No new registry or provider. Both caller and native policy are checked +before evaluation/persistence; public MCP fields cannot supply write authority or +the server identity. Post-dispatch exceptions/receipt mismatch stay uncertain, +with no replay/fallback. The original H002 receipt033 and persisted side effect +are preserved and never reused as a fixture. + +94 affected source tests pass; after normal main integration23 focused cases +pass. A separate transport-module collection remains blocked by its unbuilt +CellProfiler native extension. Actual stdio owned-vs-shared, delayed-response, +compile/execute acceptance is pending coordinator's finite technical slot; no +installed edit/JVM/new runtime has been performed. + +Please flag any in-progress changes to these registration-specific regions or +their constructors before further shared-owner edits; maintain the single +function-catalog authority. Parent retains integration ownership for both PRs. diff --git a/docs/plans/custom_registration_admission_20260929.md b/docs/plans/custom_registration_admission_20260929.md new file mode 100644 index 0000000000..3dd10b7d1f --- /dev/null +++ b/docs/plans/custom_registration_admission_20260929.md @@ -0,0 +1,219 @@ +# Custom-function admission and responsive preparation (#230) + +Implementation owner: Lovelace. Integration/install owner: coordinator. +Draft PR233: https://github.com/OpenHCSDev/openhcs/pull/233 +Source: /home/ts/wt/openhcs-custom-function-admission-20260929. +This checkpoint normally integrates main240/3032958ac at d33128916, following +main238 integration f5dbebb27 (main01d1a8c55c). Recorded PolyStore1209068 is +unchanged. No installed tree is edited. + +Latest source follow-up after4c6fbac0a: destination proof and source dispatch +now use existing connect_existing, never attach-or-start/replacement. Each +reuses the existing operation/control deadline for attachment and the one +control exchange. Eight absent/unresponsive endpoint cases exercise the actual +attach-only method and assert zero start, kill and source/control sends; +two expired-deadline cases reject before attachment. Current focused shard: +**57 passed,25 deselected,7.22s**, process8.04s,peak309668KiB,exit0. +Receipt: tests/runtime_diagnostics/registration_attach_only_20260929/. +Full startup-lock contention boundedness is not claimed: the recorded dependency's +attach-only method has no operation_deadline parameter for that existing lock. +The control/send deadline still prevents delayed lock acquisition from later +dispatching source after budget expiry. No duplicate lock/lifecycle was added. + +## Working source + +Registration requires an explicit reflected ExecutionConnectionSpec port, also +for ephemeral source. Persistence requires the deliberately authored function_name +and caller-intended absolute storage_dir. Existing AgentPathPolicy admits the +directory and Manager.source_path_for_name before client creation. A read-only +native destination request verifies that endpoint's actual Manager-owned store, +exact file, and ProcessIdentity (PID plus creation time), without evaluating source +or starting catalogue preparation. Unsupported or mismatched destinations reject +before source dispatch. The native service repeats caller/native write admission +before evaluation and atomic persistence. Post-write receipt checks are not admission. + +Cold catalogue/kernel warming belongs to the existing native FunctionCatalogPreparation +future, cancellation and supervised child from #238. New typed start/status/cancel +requests and nominal control strategies project that same owner promptly. They add +no process launcher, future store, cache roster or warmup registry. The handle is the +explicit connection plus native process incarnation; stale owners reject. Cancellation +signals that owner without joining inline; status observes terminal cleanup. + +Registration performs one bounded destination/readiness observation under the existing +control deadline. NOT_STARTED, PENDING, CANCELLING, FAILED or CANCELLED rejects before +any source-bearing registration RPC. The native mutation handler likewise refuses +cold source without initiating warming. Only READY permits one _send_control_request. +The previous mutation resend loop and inline catalogue polling were removed in place. +After source dispatch, timeout/error/malformed receipt stays uncertain; no replay, +fallback or timeout inflation. Pending preparation cannot write later on behalf of +a registration call whose observation expired. + +EndpointFunctionCatalogServiceABC composes the catalogue contract with these three +typed preparation operations; ZMQFunctionCatalogService implements it. Capabilities +use the context's typed endpoint_function_catalog dependency, not undeclared methods +on FunctionCatalogServiceABC. Normal factory and raw local MCP contexts share one +endpoint service for catalogue and preparation. Explicit endpoint injection is +supported. Hosted contexts retain explicitly injected native FunctionCatalogService; +compiler/server native catalogues acquire no second future. No NotImplemented stub, +getattr cast, isinstance dispatch, or second function registry was added. + +The canonical custom-function-authoring how-to now explains explicit routing, +controlled-launch Manager destination lookup, start/status/cancel handle fields, +READY-only mutation and precise postdispatch uncertainty. The knowledge manifest +summary/tags track this; document digests remain derived, not duplicated. + +Bundled SKILL.md and first_use/pipeline now point to that same existing responsive +preparation procedure before first cold function search. One nominal context +section shares the existing first-use/pipeline facets; capability names derive +from declarations. No duplicated guide, inventory or biological hints. Actual +default-context/generated MCP read-only calls reach the full canonical guide and +typed preparation capability without any endpoint-client creation. Ten source +checks pass8.20s, peak308356KiB; skill validator passes. Two existing whole-context +bound checks remain failing on unchanged image_analysis_workflow (17387chars), +also17387 with this section excluded; they are explicitly excluded from the +focused passing shard. This is not all-context readiness or installed proof. +Receipt: tests/runtime_diagnostics/registration_onboarding_20260929/. + +## Actual evidence (source, not installed acceptance) + +After normal main240 integration,16 focused attach-only/deadline, real context +composition and single-send cases pass5.53s (process6.41s,peak293380KiB,exit0). +Both existing abi3 extensions built through setup.py build_ext --inplace +--parallel1 with owned temporary objects/lib:2.55s,peak146140KiB,exit0. +Imports prove source _granularity_reconstruct (45440bytes) and _tabular_native +(210808bytes), plus reviewed OpenHCS/PolyStore paths. No installation/dependency +download/JVM. Durable receipts: +tests/runtime_diagnostics/registration_main240_tests_20260929/ and +tests/runtime_diagnostics/registration_main240_build_20260929/. + +Current focused provider-free shard: **47 passed, 25 deselected, 7.32 seconds**; +process elapsed8.13s, peak305992KiB, exit0. Existing shared Python, explicit reviewed +worktree plus eight submodule src paths, pinned shared bundle/downloadfalse, +threads1, plugins/conftest disabled, nonblocking validation lock. +OpenHCS and PolyStore imports resolve in this tree. Two warnings are disabled +async-plugin configuration options, not failed tests. +Receipt: tests/runtime_diagnostics/registration_readiness_20260929/. + +This shard covers all registration/admission cases, five Manager filename-owner +interceptions, generated MCP controls, real default/raw/explicit context construction, +responsive pending start/status/cancel with a controlled future, stale incarnation +rejection, failed preflight zero mutation, native cold-registration rejection, one +source-bearing client RPC on ready/pending/timeout, and #238 readiness/error propagation. +The controlled pending unit future is not the actual 53-second cold kernel job. + +Historical source checkpoints: 94 cases passed10.41s/323352KiB before newer merges; +23 cases passed6.18s/287876KiB after234; five filename cases passed3.02s/183520KiB at +c10bb3f85. These do not substitute for current live or full-suite evidence. + +The standard existing source abi3 extension was built with setup.py build_ext +--inplace --parallel1 and named owned build-temp/build-lib: **1.48s,93004KiB,exit0**. +Import receipt verifies the source binary45440bytes and source/binary hashes: +tests/runtime_diagnostics/registration_native_build_20260929/. +No install, build-dependency download, CellProfiler skip or replacement registry. +Installed225 already contains its own compiled extension; the previous absence +was in this source-only test tree, not a proved installed dependency defect. + +## Preserved failed live predecessor + +At exact81e636ad4, attempt01 used ordinary stdio MCP and three owned headless +synthetic endpoints. Health/context, missing-route, outside/file/ancestor symlink, +foreign store and unsupported-proof checks passed; protected sentinel was unchanged, +foreign/unsupported audits had zero source-bearing registration RPCs. Positive +call11 expired at10.011s and remained uncertain. Native audit showed automatic +mutation resends while cold preparation was pending. Same-handle read-only observation +exposed the unbuilt source extension. Compilation/execution and delayed acceptance +were not reached. This is **failed source-live evidence**, not acceptance. +tests/runtime_diagnostics/registration_live_20260929_attempt01/ retains original +arguments, failure, native control journals, stderr and close-owned disposition. +MCP and all three native handles are terminal; each server returned0, identities +were verified and the lock released. Original H002033 and foreign saved side effect +are preserved and never replayed. No science, shared7777 contact, GUI or JVM. + +Attempt02 at exact4c6fbac0a is also preserved as accepted=false. Ordinary MCP +start returned0.020s;91 preparation-control observations stayed below0.027s. +Stale cancellation rejected; pre-ready native source-bearing register count +was exactly0. The diagnostic's redundant100s preparation sub-budget expired +while the future was pending; this was not a tool timeout or hung MCP. +Same-handle read-only observation subsequently returned READY: native progress +timestamps1790725945.1608434 to1790726055.660598 (~110.50s). The supervised +child was absent; MCP and all three native endpoints were closed by verified +owner identity, returncodes0, runtime_terminal=true, lock released. Source and +all original calls stayed frozen until closure. No mutation, compile or execution +occurred. Slow real cold warming remains a limitation, not an optimisation claim; +parent owns additional warmup diagnosis. +Receipt: tests/runtime_diagnostics/registration_live_20260929_attempt02/. +The next fresh journey removes redundant100s preparation/45s job subcaps, +retaining the existing single240s total journey gate and unchanged10s per-call +observation cap. Cold, compile and execution stage costs are recorded separately. + +## Focused architectural audit + +Scope: changed production declarations, callers and source tests; no full NRA scan +or global proof. Actual catalog witnesses: + +- IMPL-4: endpoint preparation contract now owns all three methods. Default/raw/ + explicit MCP construction cases execute the generated controls; native compiler/ + hosted catalogues remain their own complete family, with no dummy method bodies. +- IMPL-13: start/status/cancel extends the one native future/cancellation/child. + Repeated start coalesces the exact future/thread; failed readiness sends zero + register RPCs. Mutation no longer borrows the discovery retry mechanism. + Registration and destination proof now consume the existing attach-only + lifecycle, not a caller-side replacement implementation. Eight absent/ + unresponsive cases and two expired-budget cases guard the route closure. +- IMPL-12: Manager.source_path_for_name owns save/admission, named reads/deletes/ + updates, require_source and rename destinations. Five interception cases prove + those operations cannot bypass it. Directory enumeration reads real files and + remains enumeration, not a companion filename roster. +- MEMB-2/MEMB-3: control/capability membership derives from existing declarations; + each new request leaf owns its operation/strategy. A new case extends that family, + not an action switch or hand-maintained catalogue. + The shared onboarding section similarly uses existing nominal context facets; + first-use/pipeline reachability tests exercise registry-derived rendering and + typed capability discovery, not duplicated string-kind membership. +- IDEN-8: one handle and destination token reuse ProcessIdentity; changing creation + time while retaining PID rejects before cancellation/evaluation. +- BOUND-1/BOUND-2/BOUND-6: existing typed request/response codecs decode at control/ + MCP boundaries; native Manager and AgentPathPolicy remain path/write authorities. + No second destination resolver or source-code name parser. +- TIME-1/TIME-5: replaced registration polling was deleted in place. Existing + catalogue polling remains read-only discovery, not a legacy mutation path. + Delayed/unsupported fault injection lives only in the diagnostic driver. + +## Remaining live gates and resource bounds + +The diagnostic now freezes the reviewed source SHA and submodule pins, starts three +headless fixture endpoints with native threads1, limits each observation to10s and +the journey to240s, acquires nonblocking validation.lock, and rechecks actual RAM/ +disk plus scratch<80MiB before dispatch. It asserts **zero source-bearing register +RPCs before READY and exactly two total** (initial positive plus controlled delayed), +not merely one persistence marker. The complete journey is registration/discovery/ +compile/execution of an8x8 plus-three synthetic function, escaping-path sentinels, +foreign/unsupported predispatch denials and same-handle uncertain receipt reconciliation. +It also sends one stale-incarnation cancellation request while observing the +owned preparation, then retains that same valid handle until READY. Pending +valid cancellation/terminal cleanup is covered by the controlled source future +test; it is not claimed live from a cancellation attempted after readiness. + +--installed-entrypoint mode requires PYTHONPATH unset, cwd outside SOURCE, actual +OpenHCS/PolyStore imports matching the reviewed installed source, and the ordinary +McpDevServerSpec. It is implemented but not live-proved. Source mode is explicitly +labelled source_live_not_installed. Parent owns final merge/install and installed +acceptance; no frozen installation changes are authorised here. + +Latest actual guard /home19.8GiB,RAM13.1GiB warns about disk plus historical swap. +No new native journey starts while that non-swap warning remains. Attempt02, +the57-case source follow-up, main240 build/shard and onboarding source checks are +terminal and released the lock. Next is the authorised finite cold source-live +journey after a fresh passing non-swap guard/nonblocking lock, then parent review/merge and +installation of this same frozen tree (no extra checkout). No optional hosted +CI wait or installed readiness claim. + +Owned disposable build objects136KiB, filename-test fixtures40KiB and current +source-test fixtures328KiB were removed after retaining receipts. They are reproducible +from the tests/build command; the source in-place extension remains for acceptance. +Original attempt01 synthetic scratch remains identified separately from receipts/UNKNOWN input. + +This draft references #230, #238, #233 and does not claim Closes acceptance yet. +Broad #206/PolyStore16 scope and its26 combined-suite failures remain in their separate +trees/PRs; no ROI profile or preservation pass is invented. Ordinary write admission +is not a Python sandbox or proof against hostile remote servers/filesystem races. diff --git a/docs/plans/custom_registration_source_live_handoff_20260929.md b/docs/plans/custom_registration_source_live_handoff_20260929.md new file mode 100644 index 0000000000..aecf2117c9 --- /dev/null +++ b/docs/plans/custom_registration_source_live_handoff_20260929.md @@ -0,0 +1,93 @@ +# PR233 finite source-live handoff + +Owner transfer: parent runs the next finite source-live journey, reviews/merges, +installs this same frozen tree and then proves installed entrypoint. Lovelace +has not started attempt03. No handles currently live. Do not edit this source +during the journey or after parent adopts it for installation. + +Tree: /home/ts/wt/openhcs-custom-function-admission-20260929 +Draft: https://github.com/OpenHCSDev/openhcs/pull/233 +Frozen commit: the onboarding follow-up SHA reported in the handoff message. +Normal main240 merge d33128916 / main3032958ac is included. All eight recorded +submodules remain unchanged, PolyStore1209068. Both compiled abi3 artifacts are +present in this source tree; no installation occurred. + +Source receipts: + +- registration_onboarding_20260929:10passed,2deselected,8.20s,308356KiB. + Two real-default-context/generated MCP cases reach first_use/pipeline -> + complete canonical guide -> preparation capability discovery without endpoint + contact. Skill validator passes. Initial failures and unchanged image-analysis + context overflow are preserved in scope.md / pytest_initial.txt. +- registration_main240_tests_20260929:16passed,5.53s,293380KiB. +- registration_main240_build_20260929:2.55s,146140KiB,exit0; both source extension + import paths/hashes captured. Their temporary objects/library copies and bounded + test scratch (~936KiB combined) were removed; source artifacts and receipts remain. +- registration_attach_only_20260929:57passed,25deselected,7.22s,309668KiB. + +All receipt directories are under tests/runtime_diagnostics. Original attempts01/02 +remain immutable, with original synthetic scratch retained for same-handle diagnosis. +Attempt02 status was responsive but redundant100s sub-budget ended before actual +READY at110.50s. All its endpoints/MCP/child are terminal; no mutation happened. +No warmup performance improvement or installed acceptance is claimed. + +## Next invocation + +Recheck /home >=20GiB, availableRAM >=8GiB before launch; only historical swap +warning is waived. Latest own guard was /home19.8GiB, so no native run began. +The driver itself rechecks headroom and acquires validation.lock nonblocking; +do not wrap another competing runtime/lock around it. Use a TTY and preserve +the same handle if it pauses after any unexpected disposition. It never replays. +Replace FROZEN_HEAD with the exact reviewed onboarding SHA, not a moving main. + +```sh +cd /home/ts/wt/openhcs-custom-function-admission-20260929 +env OPENHCS_CPU_ONLY=true PYTHONDONTWRITEBYTECODE=1 \ + PYTHONPATH=/home/ts/wt/openhcs-custom-function-admission-20260929:/home/ts/wt/openhcs-custom-function-admission-20260929/external/ObjectState/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/arraybridge/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/metaclass-registry/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/pycodify/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/pyqt-reactive/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/python-introspect/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/zmqruntime/src \ + POLYSTORE_IMAGEJ_CACHE_ROOT=/home/ts/.cache/polystore/imagej \ + POLYSTORE_IMAGEJ_ALLOW_DOWNLOAD=false \ + /home/ts/code/projects/openhcs/.venv/bin/python -B \ + tests/diagnostics/check_custom_registration_live.py \ + --released-runtime-slot --expected-source-sha FROZEN_HEAD \ + --receipts tests/runtime_diagnostics/registration_live_20260929_attempt03 \ + --scratch /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt03 \ + --port 15997 +``` + +Ports15997/15998/15999 must be absent before launch (driver checks). Three headless +owned/foreign/unsupported synthetic fixture servers only, threads1, scratch<80MiB, +no GUI/JVM/download/install/science/shared7777. Total journey240s, each observation10s, +no redundant100s preparation cap. Existing typed start/status/stale-cancel observe +one future; exact audit requires zero source-bearing registration before READY, +exactly two total (initial plus controlled delayed reply), unchanged escaping-path +sentinels, then ordinary register/discover/compile/execute8x8 plus-three and same- +handle uncertainty reconciliation. Added onboarding calls are read-only and precede +first catalogue search. Captures and stage timing persist before disposition. + +On unexpected failure, retain original inputs/handles; inspect the same owner, +then use diagnostic `close-owned` only for supported identity-verified cleanup. +Do not restart/replay or inflate timeouts. On terminal, verify all recorded PIDs +absent and lock released before edits. Acceptance remains source-live, not installed. + +Later installed proof uses this same driver with --installed-entrypoint, PYTHONPATH +unset and cwd outside this source; ordinary McpDevServerSpec validates actual installed +OpenHCS/PolyStore paths. Parent owns that journey after reviewed merge/install. +Multiprocessing/cold hydration profiling is separate follow-up scope, not this gate. + +## Parent-owned fixture correction after attempt03 + +Parent accepted the source handoff at82f265d3a. Attempt03 reached READY, verified +zero pre-ready source RPCs, registered/discovered/described the persisted synthetic +function (registration0.272s), then failed before compile while constructing the +fixture's PipelineConfig: VFSConfig is not its declared LazyVFSConfig field type. +The same declaration requires LazyPathPlanningConfig as well. Parent replaced +both fixture constructors with their actual declared owners, not converters or +production compatibility. A real source-import constructor check passes for +both nested fields. No compile/execution request was sent by this failed attempt. + +Original attempt03 receipt/scratch remain retained with accepted=false. Supported +close-owned completed all three exact native endpoints with returncode0 and +endpoint_terminated=true; client_closed/runtime_terminal=true. Recorded driver, +MCP and native PIDs are absent; six listeners are absent. No recorded request +is replayed. The next independent synthetic journey uses new ports/owned store, +fresh receipt/scratch and the fixture-correction commit as its source freeze. diff --git a/docs/source/development/mcp_knowledge_base_manifest.json b/docs/source/development/mcp_knowledge_base_manifest.json index f6591c2a9e..c14c2906e8 100644 --- a/docs/source/development/mcp_knowledge_base_manifest.json +++ b/docs/source/development/mcp_knowledge_base_manifest.json @@ -822,8 +822,8 @@ "document_id": "openhcs_custom_function_workflow", "section_count": 0, "source_path": "packaging/codex/openhcs/skills/use-openhcs/references/custom-function-authoring.md", - "summary": "Add a missing analysis operation through existing typed custom-function owners: discover contracts, scope catalog and persistence, verify fresh-process imports, compile artifacts, inspect bounded outputs and separate technical reuse from biological recipe acceptance.", - "tags": ["missing analysis operation", "custom function workflow", "typed artifact authoring", "isolated catalog endpoint", "persisted custom import", "registration timeout reconcile", "ColumnarRows", "3D centres"], + "summary": "Add a missing operation through typed custom-function owners: explicit endpoint/name/store admission, responsive native catalogue preparation with an incarnation-bound start/status/cancel handle, single source dispatch only after readiness, postdispatch uncertainty without replay, stable imports, compilation and bounded outputs; separate technical reuse from biological acceptance.", + "tags": ["missing analysis operation", "custom function workflow", "typed artifact authoring", "isolated catalog endpoint", "catalog preparation readiness cancellation", "persisted custom import", "registration timeout reconcile", "ColumnarRows", "3D centres"], "title": "Add a missing analysis operation as a typed custom function" }, { diff --git a/openhcs/agent/capabilities.py b/openhcs/agent/capabilities.py index c25bd1103c..5aaf98279b 100644 --- a/openhcs/agent/capabilities.py +++ b/openhcs/agent/capabilities.py @@ -71,6 +71,8 @@ CustomFunctionRegistrationRequest, CustomFunctionRegistrationResult, FunctionCatalogPage, + FunctionCatalogPreparationHandle, + FunctionCatalogPreparationState, FunctionDetail, FunctionDetailRequest, FunctionSearchRequest, @@ -1891,6 +1893,57 @@ class DescribeFunctionCapability( ) +class StartFunctionCatalogPreparationCapability(FunctionCatalogCapability): + from openhcs.agent.dto.execution_connection import ExecutionConnectionSpec + + name = "openhcs_start_function_catalog_preparation" + cli_command = "start-function-catalog-preparation" + kind = CapabilityKind.TOOL + title = "Start catalog preparation" + description = "Starts/coalesces existing native catalog/kernel preparation at an explicit owned port. Returns promptly with the exact process-incarnation handle; no custom source is submitted. Observe status, then register only once ready." + service = "endpoint_function_catalog" + mutating = True + side_effects = ("prepares_function_catalog", "writes_declared_kernel_caches") + input_contract = ExecutionConnectionSpec + output_contract = FunctionCatalogPreparationState + request_invocation = AgentDataclassRequestServiceInvocation( + service=lambda context: context.endpoint_function_catalog, + method=lambda service, request: service.start_catalog_preparation(request), + ) + + +class GetFunctionCatalogPreparationStatusCapability(FunctionCatalogCapability): + name = "openhcs_get_function_catalog_preparation_status" + cli_command = "get-function-catalog-preparation-status" + kind = CapabilityKind.TOOL + title = "Observe catalog preparation" + description = "Returns the existing preparation future's current state/progress promptly. Use the exact returned connection/process handle; stale owners reject without starting or replacing a runtime." + service = "endpoint_function_catalog" + input_contract = FunctionCatalogPreparationHandle + output_contract = FunctionCatalogPreparationState + request_invocation = AgentDataclassRequestServiceInvocation( + service=lambda context: context.endpoint_function_catalog, + method=lambda service, request: service.catalog_preparation_status(request), + ) + + +class CancelFunctionCatalogPreparationCapability(FunctionCatalogCapability): + name = "openhcs_cancel_function_catalog_preparation" + cli_command = "cancel-function-catalog-preparation" + kind = CapabilityKind.TOOL + title = "Cancel owned catalog preparation" + description = "Signals cancellation of the same incarnation-bound preparation future without blocking for child cleanup. Observe status until terminal; it does not restart preparation or submit custom source." + service = "endpoint_function_catalog" + mutating = True + side_effects = ("cancels_function_catalog_preparation",) + input_contract = FunctionCatalogPreparationHandle + output_contract = FunctionCatalogPreparationState + request_invocation = AgentDataclassRequestServiceInvocation( + service=lambda context: context.endpoint_function_catalog, + method=lambda service, request: service.cancel_catalog_preparation(request), + ) + + class RegisterCustomFunctionCapability(FunctionCatalogCapability): name = "openhcs_register_custom_function" cli_command = "register-custom-function" @@ -1899,7 +1952,11 @@ class RegisterCustomFunctionCapability(FunctionCatalogCapability): description = ( "Validates, registers, and optionally persists custom function Python " "source through CustomFunctionManager, then returns registry function_id " - "values for MCP pipeline authoring." + "values for MCP pipeline authoring. Requires an explicit execution port; " + "persist=true also requires the endpoint's exact storage_dir and function_name " + "under AgentPathPolicy writable roots before dispatch. Start/observe the native " + "catalog preparation handle first; not-ready registration rejects before source dispatch. A transport timeout " + "is uncertain, not proof that no source or registry mutation occurred." ) service = "function_catalog" exposition = FunctionCatalogCapability.exposition.refine( @@ -1909,7 +1966,7 @@ class RegisterCustomFunctionCapability(FunctionCatalogCapability): side_effects = ("writes_custom_function_file", "updates_function_registry") input_contract = CustomFunctionRegistrationRequest output_contract = CustomFunctionRegistrationResult - request_invocation = AgentDataclassRequestServiceInvocation( + request_invocation = AgentFromFieldsServiceInvocation( service=lambda context: context.function_catalog, method=lambda service, request: service.register_custom_function(request), ) diff --git a/openhcs/agent/dto/functions.py b/openhcs/agent/dto/functions.py index f75859463a..9a8ec2df1f 100644 --- a/openhcs/agent/dto/functions.py +++ b/openhcs/agent/dto/functions.py @@ -6,14 +6,19 @@ import json from abc import ABC from collections.abc import Mapping -from dataclasses import dataclass +from dataclasses import dataclass, field, replace from enum import Enum +from pathlib import Path from typing import Any, ClassVar, Generic, Self, TypeVar, cast -from zmqruntime.messages import MessageFields, ResponseType +from zmqruntime.config import TransportMode +from zmqruntime.messages import MessageFields, ProcessIdentity, ResponseType from zmqruntime.startup import EndpointStartupStatus from openhcs.agent.dto.common import SCHEMA_VERSION, AgentResultEnvelope +from openhcs.agent.dto.execution_connection import ExecutionConnectionSpec +from openhcs.agent.exceptions import AgentFacingErrorMixin +from openhcs.agent.path_policy import AgentPathPolicy from openhcs.core.artifacts import ArtifactViewerStreaming from openhcs.core.function_reference import FunctionReference @@ -28,6 +33,10 @@ class FunctionCatalogControlMessageType(str, Enum): READ_DETAIL = "openhcs_function_detail_read" READ_REFERENCE = "openhcs_function_reference_read" REGISTER_CUSTOM = "openhcs_custom_function_register" + CUSTOM_REGISTRATION_DESTINATION = "openhcs_custom_function_registration_destination" + START_PREPARATION = "openhcs_function_catalog_prepare" + READ_PREPARATION = "openhcs_function_catalog_preparation_status" + CANCEL_PREPARATION = "openhcs_function_catalog_preparation_cancel" class FunctionCatalogControlField(str, Enum): @@ -94,6 +103,83 @@ class FunctionSearchRequest(FunctionCatalogControlRequestABC): message_type = FunctionCatalogControlMessageType.SEARCH_CATALOG +@dataclass(frozen=True, slots=True) +class FunctionCatalogPreparationHandle: + """The one preparation owner in this exact execution-server incarnation.""" + + connection: ExecutionConnectionSpec + server_identity: ProcessIdentity + + def require_current_owner(self) -> None: + if self.server_identity != ProcessIdentity.current(): + raise RuntimeError( + "Function catalog preparation owner changed; handle is stale." + ) + + +class FunctionCatalogPreparationOutcome(str, Enum): + NOT_STARTED = "not_started" + PENDING = "pending" + CANCELLING = "cancelling" + READY = "ready" + FAILED = "failed" + CANCELLED = "cancelled" + + @property + def ready(self) -> bool: + return self is self.READY + + @property + def terminal(self) -> bool: + return self in (self.READY, self.FAILED, self.CANCELLED) + + +@dataclass(frozen=True, kw_only=True) +class FunctionCatalogPreparationState(AgentResultEnvelope): + handle: FunctionCatalogPreparationHandle + outcome: FunctionCatalogPreparationOutcome + progress: EndpointStartupStatus + + def require_ready(self) -> None: + if not self.outcome.ready: + raise FunctionCatalogNotReadyError(self) + + def require_handle(self, expected: FunctionCatalogPreparationHandle) -> None: + if self.handle != expected: + raise RuntimeError( + "Function catalog preparation response changed owner/connection." + ) + + +class FunctionCatalogNotReadyError(AgentFacingErrorMixin, RuntimeError): + agent_error_code = "function_catalog_not_ready" + agent_error_hint = "Start/observe the same typed catalog preparation handle; no source was dispatched." + + def __init__(self, state: FunctionCatalogPreparationState) -> None: + self.preparation = state + super().__init__( + f"Catalog preparation {state.handle!r} is {state.outcome.value}: {state.progress.message}. No source was dispatched." + ) + + +@dataclass(frozen=True, slots=True) +class FunctionCatalogPreparationStartRequest(FunctionCatalogControlRequestABC): + connection: ExecutionConnectionSpec + message_type = FunctionCatalogControlMessageType.START_PREPARATION + + +@dataclass(frozen=True, slots=True) +class FunctionCatalogPreparationStatusRequest(FunctionCatalogControlRequestABC): + handle: FunctionCatalogPreparationHandle + message_type = FunctionCatalogControlMessageType.READ_PREPARATION + + +@dataclass(frozen=True, slots=True) +class FunctionCatalogPreparationCancelRequest(FunctionCatalogControlRequestABC): + handle: FunctionCatalogPreparationHandle + message_type = FunctionCatalogControlMessageType.CANCEL_PREPARATION + + @dataclass(frozen=True, slots=True) class FunctionDetailRequest(FunctionIdentity): max_doc_chars: int | None = DEFAULT_FUNCTION_DETAIL_DOC_CHARS @@ -114,9 +200,107 @@ class CustomFunctionRegistrationRequest(FunctionCatalogControlRequestABC): source_code: str persist: bool = True compact_signature: bool = True + connection: ExecutionConnectionSpec = field(default_factory=ExecutionConnectionSpec) + function_name: str | None = None + storage_dir: str | None = None + admission_policy: AgentPathPolicy | None = field(default=None, repr=False, compare=False) + server_identity: ProcessIdentity | None = None message_type = FunctionCatalogControlMessageType.REGISTER_CUSTOM + @classmethod + def from_fields( + cls, *, source_code: str, persist: bool = True, + compact_signature: bool = True, function_name: str | None = None, + storage_dir: str | None = None, host: str = ExecutionConnectionSpec().host, + port: int | None = None, transport_mode: TransportMode | None = None, + persistent: bool = ExecutionConnectionSpec().persistent, + ) -> Self: + """Public boundary: callers cannot supply their own write authority.""" + return cls( + source_code=source_code, persist=persist, compact_signature=compact_signature, + function_name=function_name, storage_dir=storage_dir, + connection=ExecutionConnectionSpec( + host=host, port=port, transport_mode=transport_mode, persistent=persistent, + ), + ) + + def admitted(self, policy: AgentPathPolicy) -> Self: + """Admit the exact destination before endpoint dispatch or source evaluation.""" + from openhcs.processing.custom_functions.manager import CustomFunctionManager + + self.connection.require_port("Custom function registration") + if self.persist: + if self.function_name is None or self.storage_dir is None: + raise ValueError("Persisted registration requires function_name and the endpoint's storage_dir.") + root = Path(self.storage_dir).expanduser() + if not root.is_absolute(): + raise ValueError("Custom function storage_dir must be absolute.") + policy.assert_writable(root) + policy.assert_writable(CustomFunctionManager.source_path_for_name(root, self.function_name)) + return replace(self, admission_policy=policy) + + def require_server_identity(self) -> ProcessIdentity: + """Require the same native owner that supplied the admission destination.""" + actual = ProcessIdentity.current() + if self.server_identity != actual: + raise ValueError( + f"Custom registration selected server {self.server_identity!r}, " + f"but reached {actual!r}; no source was evaluated." + ) + return actual + + +@dataclass(frozen=True, slots=True) +class CustomFunctionRegistrationDestinationRequest(FunctionCatalogControlRequestABC): + """Read the persistence owner's exact path without evaluating source.""" + + function_name: str | None = None + message_type = FunctionCatalogControlMessageType.CUSTOM_REGISTRATION_DESTINATION + + +@dataclass(frozen=True, slots=True) +class CustomFunctionRegistrationDestination: + storage_dir: str + source_file_path: str | None + server_identity: ProcessIdentity = field(default_factory=ProcessIdentity.current) + + def require_request(self, request: CustomFunctionRegistrationRequest) -> None: + """Reject a different native store before sending executable source.""" + if not request.persist: + return + from openhcs.processing.custom_functions.manager import CustomFunctionManager + + expected_root = Path(request.storage_dir).expanduser() + expected_file = CustomFunctionManager.source_path_for_name( + expected_root, request.function_name, + ) + if ( + Path(self.storage_dir).resolve(strict=False) != expected_root.resolve(strict=False) + or self.source_file_path is None + or Path(self.source_file_path).resolve(strict=False) != expected_file.resolve(strict=False) + ): + raise ValueError( + f"Selected endpoint owns custom storage {self.storage_dir}, " + f"source {self.source_file_path}, not requested {expected_file}; " + "no source was dispatched." + ) + + def require_result( + self, request: CustomFunctionRegistrationRequest, + result: CustomFunctionRegistrationResult, + ) -> None: + """Require the returned mutation receipt to identify the admitted owner.""" + if result.server_identity != self.server_identity or result.connection != request.connection: + raise ValueError("Registration returned a different execution owner.") + if result.persisted != request.persist: + raise ValueError("Registration returned a different persistence policy.") + if request.persist and ( + result.storage_dir != self.storage_dir + or result.source_file_paths != (self.source_file_path,) + ): + raise ValueError("Registration returned a different persistence destination.") + class FunctionParameterSource(str, Enum): """Nominal source of one callable parameter in agent-facing docs.""" @@ -390,6 +574,13 @@ def catalog(self) -> FunctionCatalogPage: return self.value +class FunctionCatalogPreparationStateControlResponse( + FunctionCatalogControlResponseBase[FunctionCatalogPreparationState] +): + field = FunctionCatalogControlField.PREPARATION + value_type = FunctionCatalogPreparationState + + class FunctionCatalogPreparationStatus(str, Enum): """Control response state while endpoint catalog preparation is active.""" @@ -463,6 +654,8 @@ class CustomFunctionRegistrationResult(AgentResultEnvelope): source_file_paths: tuple[str, ...] = () functions: tuple[FunctionCatalogEntry, ...] = () next_steps: tuple[str, ...] = () + connection: ExecutionConnectionSpec = field(default_factory=ExecutionConnectionSpec) + server_identity: ProcessIdentity | None = None class CustomFunctionRegistrationControlResponse( @@ -478,6 +671,17 @@ def result(self) -> CustomFunctionRegistrationResult: return self.value +class CustomFunctionRegistrationDestinationControlResponse( + FunctionCatalogControlResponseBase[CustomFunctionRegistrationDestination] +): + field = FunctionCatalogControlField.RESULT + value_type = CustomFunctionRegistrationDestination + + @property + def destination(self) -> CustomFunctionRegistrationDestination: + return self.value + + def catalog_page( *, items: tuple[FunctionCatalogEntry, ...], diff --git a/openhcs/agent/services/endpoint_function_catalog_service.py b/openhcs/agent/services/endpoint_function_catalog_service.py index d8f8a020e5..b7b6f42fff 100644 --- a/openhcs/agent/services/endpoint_function_catalog_service.py +++ b/openhcs/agent/services/endpoint_function_catalog_service.py @@ -4,26 +4,36 @@ import logging import threading +from abc import abstractmethod from collections.abc import Callable, Mapping from concurrent.futures import CancelledError, Future -from dataclasses import dataclass +from dataclasses import dataclass, replace from types import MappingProxyType from typing import TYPE_CHECKING -from zmqruntime import OperationCancellation +from zmqruntime import OperationCancellation, OperationDeadline +from openhcs.agent.dto.execution_connection import ExecutionConnectionSpec from openhcs.agent.dto.functions import ( DEFAULT_FUNCTION_DETAIL_DOC_CHARS, + CustomFunctionRegistrationDestinationRequest, CustomFunctionRegistrationRequest, CustomFunctionRegistrationResult, FunctionCatalogControlRequest, FunctionCatalogEntry, FunctionCatalogPage, + FunctionCatalogPreparationCancelRequest, + FunctionCatalogPreparationHandle, + FunctionCatalogPreparationStartRequest, + FunctionCatalogPreparationState, + FunctionCatalogPreparationStatusRequest, FunctionDetail, FunctionDetailControlRequest, FunctionReferenceControlRequest, FunctionSearchRequest, ) +from openhcs.agent.exceptions import AgentFacingErrorMixin +from openhcs.agent.path_policy import AgentPathPolicy from openhcs.agent.services.function_catalog_service import FunctionCatalogServiceABC from openhcs.runtime.zmq_config import OpenHCSZMQConfig @@ -34,6 +44,19 @@ logger = logging.getLogger(__name__) +class CustomFunctionRegistrationUncertainError(AgentFacingErrorMixin, RuntimeError): + agent_error_code = "custom_function_registration_uncertain" + agent_error_hint = "Preserve this request and endpoint receipt. Do not replay or fall back; persistence may have completed." + + def __init__(self, request: CustomFunctionRegistrationRequest): + super().__init__( + f"Registration observation failed after invoking {request.connection.transport_endpoint()}; " + f"destination={request.storage_dir}, function={request.function_name!r}. " + f"Selected server={request.server_identity!r}. " + "Outcome is uncertain; source or registry mutation may have completed." + ) + + FunctionCatalogClientFactory = Callable[ [OpenHCSZMQConfig], "ZMQExecutionClient", @@ -158,7 +181,29 @@ def cancel_and_join(self) -> None: self.thread.join() -class ZMQFunctionCatalogService(FunctionCatalogServiceABC): +class EndpointFunctionCatalogServiceABC(FunctionCatalogServiceABC): + """Endpoint catalog with responsive access to its native preparation owner.""" + + @abstractmethod + def start_catalog_preparation( + self, connection: ExecutionConnectionSpec + ) -> FunctionCatalogPreparationState: + """Start/coalesce the selected endpoint's existing preparation future.""" + + @abstractmethod + def catalog_preparation_status( + self, handle: FunctionCatalogPreparationHandle + ) -> FunctionCatalogPreparationState: + """Observe the exact native incarnation without starting or waiting.""" + + @abstractmethod + def cancel_catalog_preparation( + self, handle: FunctionCatalogPreparationHandle + ) -> FunctionCatalogPreparationState: + """Signal the exact native preparation owner without joining its child.""" + + +class ZMQFunctionCatalogService(EndpointFunctionCatalogServiceABC): """Use one execution endpoint as the authoring callable-catalog authority.""" def __init__( @@ -166,8 +211,10 @@ def __init__( config_provider: Callable[[], OpenHCSZMQConfig], *, client_factory: FunctionCatalogClientFactory | None = None, + path_policy: AgentPathPolicy | None = None, ) -> None: self._config_provider = config_provider + self._path_policy = path_policy or AgentPathPolicy.from_environment() self._client_factory = client_factory or self._new_client self._client_session: FunctionCatalogClientSession | None = None self._endpoint_state: FunctionCatalogEndpointState | None = None @@ -350,21 +397,106 @@ def register_custom_function( ) -> CustomFunctionRegistrationResult: """Register source at the endpoint and project ephemeral source locally.""" - endpoint = self._config_provider() - result = self._client_for(endpoint).register_custom_function(request) - if not request.persist: - from openhcs.processing.custom_functions.manager import ( - CustomFunctionManager, + request = request.admitted(self._path_policy) + endpoint = self._endpoint_for_connection(request.connection) + deadline = OperationDeadline.after_milliseconds( + endpoint.control_timeout_ms, + operation="custom function registration", + ) + client = self._client_for(endpoint) + destination = client.custom_function_registration_destination( + CustomFunctionRegistrationDestinationRequest( + function_name=request.function_name + ), + operation_deadline=deadline, + ) + destination.require_request(request) + if request.persist: + self._path_policy.assert_writable(destination.storage_dir) + self._path_policy.assert_writable(destination.source_file_path) + request = replace(request, server_identity=destination.server_identity) + self.invalidate() + self._config_provider = lambda: endpoint + # Observe the existing future once, never inline-poll a cold warmup. + handle = FunctionCatalogPreparationHandle( + request.connection, destination.server_identity + ) + state = client.function_catalog_preparation( + FunctionCatalogPreparationStatusRequest(handle), + operation_deadline=deadline, + ) + state.require_handle(handle) + state.require_ready() + deadline.remaining_seconds() + try: + result = client.register_custom_function( + request, operation_deadline=deadline ) + destination.require_result(request, result) + if not request.persist: + from openhcs.processing.custom_functions.manager import ( + CustomFunctionManager, + ) - CustomFunctionManager().register_from_code( - request.source_code, - persist=False, - clear_caches=False, - emit_signal=False, + CustomFunctionManager(create_storage=False).register_from_code( + request.source_code, + persist=False, + clear_caches=False, + emit_signal=False, + ) + self.invalidate() + except Exception as error: + raise CustomFunctionRegistrationUncertainError(request) from error + return result + + def _endpoint_for_connection( + self, connection: ExecutionConnectionSpec + ) -> OpenHCSZMQConfig: + """Use the explicit typed route, never a companion endpoint registry.""" + return replace( + self._config_provider(), + default_port=connection.require_port("Function catalog operation"), + client_host=connection.host, + transport_mode=connection.transport_endpoint().transport_mode, + persistent=connection.persistent, + ) + + def start_catalog_preparation( + self, connection: ExecutionConnectionSpec + ) -> FunctionCatalogPreparationState: + endpoint = self._endpoint_for_connection(connection) + state = self._client_for(endpoint).function_catalog_preparation( + FunctionCatalogPreparationStartRequest(connection), + ) + if state.handle.connection != connection: + raise RuntimeError( + "Catalog preparation response changed the explicit connection." ) self.invalidate() - return result + self._config_provider = lambda: endpoint + return state + + def catalog_preparation_status( + self, handle: FunctionCatalogPreparationHandle + ) -> FunctionCatalogPreparationState: + state = self._client_for( + self._endpoint_for_connection(handle.connection) + ).function_catalog_preparation( + FunctionCatalogPreparationStatusRequest(handle), + ) + state.require_handle(handle) + return state + + def cancel_catalog_preparation( + self, handle: FunctionCatalogPreparationHandle + ) -> FunctionCatalogPreparationState: + state = self._client_for( + self._endpoint_for_connection(handle.connection) + ).function_catalog_preparation( + FunctionCatalogPreparationCancelRequest(handle), + ) + state.require_handle(handle) + return state def invalidate(self) -> None: """Discard the derived page; the next read requests the endpoint again.""" diff --git a/openhcs/agent/services/function_catalog_service.py b/openhcs/agent/services/function_catalog_service.py index 003092ea2a..a375f5066d 100644 --- a/openhcs/agent/services/function_catalog_service.py +++ b/openhcs/agent/services/function_catalog_service.py @@ -8,6 +8,7 @@ from collections.abc import Callable from dataclasses import dataclass from enum import Enum +from pathlib import Path from typing import TYPE_CHECKING from pyqt_reactive.services.parameter_help_service import docstring_info_for_target @@ -25,6 +26,8 @@ DEFAULT_FUNCTION_DETAIL_DOC_CHARS, CellProfilerArtifactBindingSummary, CellProfilerModuleDeclarationSummary, + CustomFunctionRegistrationDestination, + CustomFunctionRegistrationDestinationRequest, CustomFunctionRegistrationRequest, CustomFunctionRegistrationResult, FunctionArtifactSpec, @@ -37,6 +40,7 @@ catalog_page, ) from openhcs.agent.exceptions import AgentFacingErrorMixin +from openhcs.agent.path_policy import AgentPathPolicy from openhcs.core.artifacts import ( ArtifactInputPlan, ArtifactSpec, @@ -559,7 +563,8 @@ def prepare( ) super().prepare(status_callback=status_callback, cancellation=cancellation) - def __init__(self) -> None: + def __init__(self, path_policy: AgentPathPolicy | None = None) -> None: + self._path_policy = path_policy or AgentPathPolicy.from_environment() self._projection_metadata: dict[str, FunctionMetadata] | None = None self._projections: dict[ tuple[SignatureView, SummaryView], @@ -569,9 +574,24 @@ def __init__(self) -> None: def register_custom_function( self, request: CustomFunctionRegistrationRequest ) -> CustomFunctionRegistrationResult: - manager = custom_function_manager.CustomFunctionManager() + request = request.admitted(request.admission_policy or self._path_policy) + # Caller admission cannot enlarge this server's own policy. + request.admitted(self._path_policy) + server_identity = request.require_server_identity() + manager = custom_function_manager.CustomFunctionManager(create_storage=False) + if request.persist and manager.storage_dir.resolve(strict=False) != Path(request.storage_dir).resolve(strict=False): + raise ValueError( + f"Selected endpoint owns custom storage {manager.storage_dir}, " + f"not requested {request.storage_dir}; no source was evaluated." + ) + def admit_write(path: Path) -> Path: + self._path_policy.assert_writable(path) + return request.admission_policy.assert_writable(path) + registered_functions = manager.register_from_code( - request.source_code, persist=request.persist + request.source_code, persist=request.persist, + expected_function_name=request.function_name, + write_admission=admit_write, ) function_ids = self.function_ids_for_callables(tuple(registered_functions)) entries = tuple( @@ -607,6 +627,22 @@ def register_custom_function( for function_id in function_ids ) ), + connection=request.connection, + server_identity=server_identity, + ) + + def custom_function_registration_destination( + self, request: CustomFunctionRegistrationDestinationRequest + ) -> CustomFunctionRegistrationDestination: + """Project the native store without construction, source execution or IO.""" + manager_type = custom_function_manager.CustomFunctionManager + storage_dir = manager_type.default_storage_directory() + return CustomFunctionRegistrationDestination( + storage_dir=str(storage_dir), + source_file_path=( + str(manager_type.source_path_for_name(storage_dir, request.function_name)) + if request.function_name is not None else None + ), ) def search( diff --git a/openhcs/agent/services/llm_context_service.py b/openhcs/agent/services/llm_context_service.py index 4c0ae9fc8b..adcd4f4d19 100644 --- a/openhcs/agent/services/llm_context_service.py +++ b/openhcs/agent/services/llm_context_service.py @@ -111,6 +111,26 @@ def render_authoring_context_sections( return (header, *sections, deepening) +class CatalogPreparationReadinessSection( + AuthoringContextSection, FirstUseWorkflowContext, PipelineAuthoringRulesContext +): + """One readiness entrypoint shared by first-use and pipeline contexts.""" + + section_id = "catalog_preparation_readiness" + + @classmethod + def render(cls, service: AgentAuthoringContextService) -> str: + del service + return f"""=== COLD LOCAL CATALOGUE READINESS === +Before first cold {agent_capabilities.search_functions.name}, discover "catalog preparation" +with {agent_capabilities.search_capabilities.name}. If exposed, follow +`openhcs_custom_function_workflow` / "Register on the intended process owner": +{agent_capabilities.start_function_catalog_preparation.name} on the intended existing endpoint, +then observe that exact handle until READY. Keep cold warming separate from +10-second tool observations; a timeout does not authorise restart/replay or +mutation. If not exposed, report the surface boundary rather than guess a route.""" + + class PipelineSystemModelSection( AuthoringContextSection, PipelineSystemModelContext, diff --git a/openhcs/core/xdg_paths.py b/openhcs/core/xdg_paths.py index 44ae2a5b68..c0618ec0e5 100644 --- a/openhcs/core/xdg_paths.py +++ b/openhcs/core/xdg_paths.py @@ -18,7 +18,7 @@ logger = logging.getLogger(__name__) -def get_openhcs_data_dir() -> Path: +def get_openhcs_data_dir(*, create: bool = True) -> Path: """ Get the OpenHCS data directory following XDG Base Directory Specification. @@ -27,7 +27,8 @@ def get_openhcs_data_dir() -> Path: """ data_home = Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local" / "share")) data_dir = data_home / "openhcs" - data_dir.mkdir(parents=True, exist_ok=True) + if create: + data_dir.mkdir(parents=True, exist_ok=True) return data_dir @@ -263,7 +264,9 @@ def get_config_file_path(filename: str, legacy_filename: Optional[str] = None) - return new_path -def get_data_file_path(filename: str, legacy_filename: Optional[str] = None) -> Path: +def get_data_file_path( + filename: str, legacy_filename: Optional[str] = None, *, create: bool = True +) -> Path: """ Get path for a data file, with automatic migration from legacy location. @@ -274,7 +277,9 @@ def get_data_file_path(filename: str, legacy_filename: Optional[str] = None) -> Returns: Path to the data file in XDG location """ - new_path = get_openhcs_data_dir() / filename + new_path = get_openhcs_data_dir(create=create) / filename + if not create: + return new_path # Check if we need to migrate from legacy location if not new_path.exists(): diff --git a/openhcs/mcp/context.py b/openhcs/mcp/context.py index 30edcc0f6e..26c8fd70ea 100644 --- a/openhcs/mcp/context.py +++ b/openhcs/mcp/context.py @@ -11,6 +11,9 @@ ArchitectureProjectionService, ) from openhcs.agent.services.config_service import ConfigService + from openhcs.agent.services.endpoint_function_catalog_service import ( + EndpointFunctionCatalogServiceABC, + ) from openhcs.agent.services.execution_session_service import ExecutionSessionService from openhcs.agent.services.function_catalog_service import ( FunctionCatalogServiceABC, @@ -26,14 +29,14 @@ from openhcs.agent.services.plate_inspection_service import PlateInspectionService from openhcs.agent.services.plate_streaming_service import PlateStreamingService from openhcs.agent.services.runtime_server_service import RuntimeServerService - from openhcs.agent.services.viewer_endpoint_discovery import ( - ViewerEndpointDiscoveryService, - ) from openhcs.agent.services.selected_plate_service import SelectedPlateService from openhcs.agent.services.synthetic_plate_service import ( SyntheticPlateGenerationService, ) from openhcs.agent.services.ui_bridge_service import UiBridgeService + from openhcs.agent.services.viewer_endpoint_discovery import ( + ViewerEndpointDiscoveryService, + ) from openhcs.agent.services.viewer_window_service import ViewerWindowService @@ -46,6 +49,7 @@ class OpenHCSAgentContext: "_authoring_context_service", "_config_service", "_execution_service", + "_endpoint_function_catalog", "_function_catalog", "_knowledge_base_service", "_object_state_field_help_service", @@ -69,6 +73,7 @@ def __init__( config_service: "ConfigService | None" = None, execution_service: "ExecutionSessionService | None" = None, function_catalog: "FunctionCatalogServiceABC | None" = None, + endpoint_function_catalog: "EndpointFunctionCatalogServiceABC | None" = None, knowledge_base_service: "KnowledgeBaseService | None" = None, object_state_field_help_service: "ObjectStateFieldHelpService | None" = None, pipeline_service: "PipelineAuthoringService | None" = None, @@ -87,6 +92,7 @@ def __init__( self._config_service = config_service self._execution_service = execution_service self._function_catalog = function_catalog + self._endpoint_function_catalog = endpoint_function_catalog self._knowledge_base_service = knowledge_base_service self._object_state_field_help_service = object_state_field_help_service self._pipeline_service = pipeline_service @@ -112,12 +118,23 @@ def viewer_endpoint_discovery_service(self) -> "ViewerEndpointDiscoveryService": @property def function_catalog(self) -> "FunctionCatalogServiceABC": if self._function_catalog is None: - from openhcs.agent.services.function_catalog_service import ( - FunctionCatalogService, + self._function_catalog = self.endpoint_function_catalog + return self._function_catalog + + @property + def endpoint_function_catalog(self) -> "EndpointFunctionCatalogServiceABC": + """Compose one typed endpoint authority for local catalog/control tools.""" + if self._endpoint_function_catalog is None: + from openhcs.agent.services.endpoint_function_catalog_service import ( + ZMQFunctionCatalogService, ) + from openhcs.pyqt_gui.config import load_cached_ui_execution_endpoint_sync - self._function_catalog = FunctionCatalogService() - return self._function_catalog + endpoint = load_cached_ui_execution_endpoint_sync() + self._endpoint_function_catalog = ZMQFunctionCatalogService( + lambda: endpoint, path_policy=self.path_policy, + ) + return self._endpoint_function_catalog @property def config_service(self) -> "ConfigService": @@ -286,12 +303,20 @@ def create_agent_context() -> OpenHCSAgentContext: from openhcs.pyqt_gui.config import load_cached_ui_execution_endpoint_sync endpoint_config = load_cached_ui_execution_endpoint_sync() + path_policy = AgentPathPolicy.from_environment() + catalog = ZMQFunctionCatalogService( + lambda: endpoint_config, path_policy=path_policy, + ) return OpenHCSAgentContext( - function_catalog=ZMQFunctionCatalogService(lambda: endpoint_config) + path_policy=path_policy, + function_catalog=catalog, + endpoint_function_catalog=catalog, ) def create_hosted_agent_context() -> OpenHCSAgentContext: """Create the self-contained catalog authority used by hosted MCP.""" - return OpenHCSAgentContext() + from openhcs.agent.services.function_catalog_service import FunctionCatalogService + + return OpenHCSAgentContext(function_catalog=FunctionCatalogService()) diff --git a/openhcs/mcp/dev_client_commands/knowledge_pipeline.py b/openhcs/mcp/dev_client_commands/knowledge_pipeline.py index 34a981f835..4c9a92af61 100644 --- a/openhcs/mcp/dev_client_commands/knowledge_pipeline.py +++ b/openhcs/mcp/dev_client_commands/knowledge_pipeline.py @@ -4,6 +4,7 @@ import argparse from collections.abc import Mapping +from dataclasses import fields from pathlib import Path from typing import ClassVar @@ -14,7 +15,11 @@ from openhcs.agent.dto.authoring import AuthoringContextRequest from openhcs.agent.dto.common import JsonObject, JsonValue from openhcs.agent.dto.execution import ExecutionConnectionSpec -from openhcs.agent.dto.functions import FunctionDetailRequest, FunctionSearchRequest +from openhcs.agent.dto.functions import ( + CustomFunctionRegistrationRequest, + FunctionDetailRequest, + FunctionSearchRequest, +) from openhcs.agent.dto.pipeline import ( PipelineSourceRenderRequest, PipelineValidationRequest, @@ -28,6 +33,7 @@ McpDevToolCall, McpToolArgumentAuthority, add_pipeline_source_options, + add_request_factory_option, call_mcp_tool, execute_source_session_tool_arguments, execute_source_submit_timeout_seconds, @@ -200,7 +206,7 @@ def configure_parser(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "--no-persist", action="store_true", - help="Register for this MCP process only; do not write to the custom function directory.", + help="Register on the explicitly selected execution endpoint without persisting a source file.", ) parser.add_argument( "--full-signature", @@ -208,6 +214,16 @@ def configure_parser(self, parser: argparse.ArgumentParser) -> None: action="store_false", default=True, ) + for connection_field in fields(ExecutionConnectionSpec): + add_request_factory_option( + parser, CustomFunctionRegistrationRequest.from_fields, + connection_field.name, f"--{connection_field.name.replace('_', '-')}", + ) + for field_name in ("function_name", "storage_dir"): + add_request_factory_option( + parser, CustomFunctionRegistrationRequest.from_fields, + field_name, f"--{field_name.replace('_', '-')}", + ) parser.add_argument( "--json", action="store_true", @@ -221,11 +237,19 @@ def tool_arguments( source_code = args.source_code if source_code is None: source_code = Path(args.source_file).read_text(encoding="utf-8") + connection = ExecutionConnectionSpec(**{ + connection_field.name: vars(args)[connection_field.name] + for connection_field in fields(ExecutionConnectionSpec) + }) + connection.require_port("Custom function registration") return McpToolArgumentAuthority.from_payload( { "source_code": source_code, "persist": not args.no_persist, "compact_signature": args.compact_signature, + "function_name": args.function_name, + "storage_dir": args.storage_dir, + **connection.tool_arguments(), } ) diff --git a/openhcs/processing/custom_functions/manager.py b/openhcs/processing/custom_functions/manager.py index 2587b1da7d..817cbe7737 100644 --- a/openhcs/processing/custom_functions/manager.py +++ b/openhcs/processing/custom_functions/manager.py @@ -85,7 +85,7 @@ class CustomFunctionManager: def require_source(self, expected: CustomFunctionSource) -> None: """Reject a persisted declaration whose actual source bytes changed.""" current = self._snapshot_source( - self.storage_dir / f"{expected.function_name}.py" + self.source_path_for_name(self.storage_dir, expected.function_name) ) if current is None or current.source != expected: raise RuntimeError( @@ -93,10 +93,24 @@ def require_source(self, expected: CustomFunctionSource) -> None: "recompile the pipeline." ) - def __init__(self): + storage_subdirectory = "custom_functions" + + @classmethod + def default_storage_directory(cls) -> Path: + """Resolve the native store without creating or migrating any files.""" + return get_data_file_path(cls.storage_subdirectory, create=False) + + @staticmethod + def source_path_for_name(storage_dir: Path, function_name: str) -> Path: + """Own source naming for both admission and actual persistence.""" + if not function_name.isidentifier() or function_name.startswith("_"): + raise ValidationError("Custom function name must be a public Python identifier.") + return storage_dir / f"{function_name}.py" + + def __init__(self, *, create_storage: bool = True): """Initialize manager and create storage directory if needed.""" - self.storage_dir: Path = get_data_file_path("custom_functions") - if not self.storage_dir.exists(): + self.storage_dir: Path = self.default_storage_directory() + if create_storage and not self.storage_dir.exists(): self.storage_dir.mkdir(parents=True, exist_ok=True) logger.info(f"Created custom functions directory: {self.storage_dir}") @@ -107,6 +121,8 @@ def register_from_code( *, clear_caches: bool = True, emit_signal: bool = True, + expected_function_name: str | None = None, + write_admission: Callable[[Path], Path] | None = None, ) -> list[Callable]: """ Validate and register the single declaration owned by this source. @@ -127,12 +143,26 @@ def register_from_code( ValueError: If no valid functions found RuntimeError: If function registration fails """ + if persist and write_admission is not None: + if expected_function_name is None: + raise ValueError("Write admission requires an explicit custom function name.") + write_admission(self.storage_dir) + write_admission(self.source_path_for_name(self.storage_dir, expected_function_name)) metadata = self._prepare_source(code) + if expected_function_name is not None and metadata.original_name != expected_function_name: + raise ValidationError( + f"Prepared declaration {metadata.original_name!r} does not match " + f"admitted function {expected_function_name!r}." + ) lifetime = CustomFunctionLifetime.from_persist(persist) with CustomFunctionRuntimeRegistry.lifecycle(): CustomFunctionRuntimeRegistry.ensure_can_publish(metadata) if persist: source_path = self.source_path_for_function(metadata.func) + if write_admission is not None: + write_admission(self.storage_dir) + write_admission(source_path) + self.storage_dir.mkdir(parents=True, exist_ok=True) if source_path.exists(): raise ValueError( f"Custom function '{metadata.original_name}' already exists; " @@ -160,7 +190,7 @@ def register_from_code( def source_path_for_function(self, func: Callable) -> Path: """Return the persisted source path used for a registered function.""" - return self.storage_dir / f"{func.__name__}.py" + return self.source_path_for_name(self.storage_dir, func.__name__) def source_revision(self) -> CustomFunctionSourceRevision: """Return a content-derived revision of all persisted declarations.""" @@ -243,7 +273,7 @@ def load_custom_function( Returns: Number of functions registered from the persisted file. """ - file_path: Path = self.storage_dir / f"{func_name}.py" + file_path = self.source_path_for_name(self.storage_dir, func_name) snapshot = self._snapshot_source(file_path) if snapshot is None: return 0 @@ -295,7 +325,7 @@ def delete_custom_function(self, func_name: str) -> bool: Returns: True if function file was deleted, False if not found """ - file_path: Path = self.storage_dir / f"{func_name}.py" + file_path = self.source_path_for_name(self.storage_dir, func_name) with CustomFunctionRuntimeRegistry.lifecycle(): if not file_path.exists(): @@ -362,7 +392,7 @@ def get_function_code(self, func_name: str) -> str: Raises: ValueError: If function file not found """ - file_path: Path = self.storage_dir / f"{func_name}.py" + file_path = self.source_path_for_name(self.storage_dir, func_name) if not file_path.exists(): raise ValueError(f"Custom function '{func_name}' not found") @@ -389,7 +419,7 @@ def update_custom_function(self, old_name: str, new_code: str) -> str: ValidationError: If new code is invalid OSError: If file operations fail """ - old_file_path = self.storage_dir / f"{old_name}.py" + old_file_path = self.source_path_for_name(self.storage_dir, old_name) old_snapshot = self._snapshot_source(old_file_path) if old_snapshot is None: raise ValueError(f"Custom function '{old_name}' not found") @@ -401,7 +431,7 @@ def update_custom_function(self, old_name: str, new_code: str) -> str: else: metadata = self._prepare_source(new_code) new_name = metadata.original_name - new_file_path = self.storage_dir / f"{new_name}.py" + new_file_path = self.source_path_for_name(self.storage_dir, new_name) temp_path = self._write_temporary_source(new_code) try: with CustomFunctionRuntimeRegistry.lifecycle(): @@ -541,7 +571,7 @@ def _save_function_code(self, func_name: str, code: str) -> None: func_name: Name of function (used as filename) code: Python source code """ - file_path = self.storage_dir / f"{func_name}.py" + file_path = self.source_path_for_name(self.storage_dir, func_name) temp_path = self._write_temporary_source(code) try: os.replace(temp_path, file_path) diff --git a/openhcs/runtime/function_catalog_preparation.py b/openhcs/runtime/function_catalog_preparation.py index 6044a13df6..09c780fdfa 100644 --- a/openhcs/runtime/function_catalog_preparation.py +++ b/openhcs/runtime/function_catalog_preparation.py @@ -14,6 +14,11 @@ from zmqruntime.startup import EndpointStartupPhase, EndpointStartupStatus if TYPE_CHECKING: + from openhcs.agent.dto.execution_connection import ExecutionConnectionSpec + from openhcs.agent.dto.functions import ( + FunctionCatalogPreparationHandle, + FunctionCatalogPreparationState, + ) from openhcs.agent.services.function_catalog_service import ( FunctionCatalogServiceABC, ) @@ -114,11 +119,82 @@ def snapshot(self) -> EndpointStartupStatus: with self._lock: return self._snapshot - def _set_message(self, message: str) -> None: + def start( + self, connection: ExecutionConnectionSpec + ) -> FunctionCatalogPreparationState: + """Start/coalesce the existing future and return its responsive handle.""" + from zmqruntime.messages import ProcessIdentity + + from openhcs.agent.dto.functions import FunctionCatalogPreparationHandle + + connection.require_port("Function catalog preparation") + handle = FunctionCatalogPreparationHandle(connection, ProcessIdentity.current()) + self.ensure_started() + return self.observe(handle) + + def observe( + self, handle: FunctionCatalogPreparationHandle + ) -> FunctionCatalogPreparationState: + """Project this future, never wait, restart or build another job store.""" + from openhcs.agent.dto.common import SCHEMA_VERSION, AgentError + from openhcs.agent.dto.functions import ( + FunctionCatalogPreparationOutcome as Outcome, + ) + from openhcs.agent.dto.functions import ( + FunctionCatalogPreparationState, + ) + + handle.require_current_owner() + errors = () + with self._lock: + future, snapshot = self._future, self._snapshot + if future is None: + outcome = Outcome.NOT_STARTED + elif future.cancelled(): + outcome = Outcome.CANCELLED + elif not future.done(): + outcome = ( + Outcome.CANCELLING + if self._cancellation.requested() + else Outcome.PENDING + ) + elif (error := future.exception()) is not None: + outcome = Outcome.FAILED + errors = ( + AgentError( + code="function_catalog_preparation_failed", + message=str(error), + exception_type=type(error).__name__, + ), + ) + else: + outcome = Outcome.READY + return FunctionCatalogPreparationState( + schema_version=SCHEMA_VERSION, + handle=handle, + outcome=outcome, + progress=snapshot, + errors=errors, + ) + + def cancel_preparation( + self, handle: FunctionCatalogPreparationHandle + ) -> FunctionCatalogPreparationState: + """Signal the same owner promptly; its thread unwinds the supervised child.""" + handle.require_current_owner() + self._cancellation.cancel() + return self.observe(handle) + + def _set_message( + self, + message: str, + *, + phase: EndpointStartupPhase = EndpointStartupPhase.PREPARING_CAPABILITIES, + ) -> None: with self._lock: self._snapshot = EndpointStartupStatus( sequence=self._snapshot.sequence + 1, - phase=EndpointStartupPhase.PREPARING_CAPABILITIES, + phase=phase, message=message, timestamp=time.time(), ) @@ -130,8 +206,16 @@ def _prepare(self, future: Future[None]) -> None: cancellation=self._cancellation, ) except CancelledError: + self._set_message( + "Function catalog preparation cancelled", + phase=EndpointStartupPhase.FAILED, + ) future.cancel() except BaseException as error: + self._set_message(str(error), phase=EndpointStartupPhase.FAILED) future.set_exception(error) else: + self._set_message( + "Function catalog ready", phase=EndpointStartupPhase.CONNECTED + ) future.set_result(None) diff --git a/openhcs/runtime/zmq_control.py b/openhcs/runtime/zmq_control.py index 3f8be7c7b0..c2c0996546 100644 --- a/openhcs/runtime/zmq_control.py +++ b/openhcs/runtime/zmq_control.py @@ -6,12 +6,20 @@ from collections.abc import Mapping from dataclasses import dataclass from pathlib import Path -from typing import TYPE_CHECKING, ClassVar +from typing import TYPE_CHECKING, ClassVar, Generic, TypeVar from metaclass_registry import AutoRegisterMeta, RegistryFamily, RegistryKeyAttribute from zmqruntime.messages import MessageFields, ResponseType +from openhcs.agent.dto.functions import FunctionCatalogControlRequestABC + if TYPE_CHECKING: + from openhcs.agent.dto.functions import ( + FunctionCatalogPreparationCancelRequest, + FunctionCatalogPreparationStartRequest, + FunctionCatalogPreparationState, + FunctionCatalogPreparationStatusRequest, + ) from openhcs.agent.services.function_catalog_service import FunctionCatalogService from openhcs.runtime.function_catalog_preparation import FunctionCatalogPreparation from openhcs.runtime.zmq_compilation import ZMQCompileArtifactRecord @@ -307,7 +315,120 @@ def handle_ready( ).to_control_response() -class CustomFunctionRegistrationMessageStrategy(FunctionCatalogMessageStrategy): +class CustomFunctionRegistrationDestinationMessageStrategy(ZMQControlMessageStrategy): + """Native store admission must not initialize/evaluate the function catalog.""" + + from openhcs.agent.dto.functions import CustomFunctionRegistrationDestinationRequest + + request_type = CustomFunctionRegistrationDestinationRequest + registry_key = request_type.message_type.value + + def handle(self, message: dict, context: ZMQControlRequestContext) -> dict: + from openhcs.agent.dto.functions import ( + CustomFunctionRegistrationDestinationControlResponse, + ) + + try: + request = self.request_type.from_control_payload(message) + return CustomFunctionRegistrationDestinationControlResponse( + value=context.require_function_catalog().custom_function_registration_destination( + request + ), + ).to_control_response() + except Exception as error: + return self.error_response(error) + + +PreparationRequestT = TypeVar( + "PreparationRequestT", bound=FunctionCatalogControlRequestABC +) + + +class FunctionCatalogPreparationMessageStrategy( + ZMQControlMessageStrategy, + Generic[PreparationRequestT], +): + """Responsive projection of the existing native preparation owner.""" + + request_type: ClassVar[type[FunctionCatalogControlRequestABC]] + + def handle(self, message: dict, context: ZMQControlRequestContext) -> dict: + from openhcs.agent.dto.functions import ( + FunctionCatalogPreparationStateControlResponse, + ) + + try: + request = self.request_type.from_control_payload(message) + return FunctionCatalogPreparationStateControlResponse( + value=self.preparation_state( + context.require_function_catalog_preparation(), request + ), + ).to_control_response() + except Exception as error: + return self.error_response(error) + + @abstractmethod + def preparation_state( + self, + preparation: FunctionCatalogPreparation, + request: PreparationRequestT, + ) -> FunctionCatalogPreparationState: + """Each request owns its operation, not a string action bag.""" + + +class StartFunctionCatalogPreparationStrategy( + FunctionCatalogPreparationMessageStrategy["FunctionCatalogPreparationStartRequest"], +): + from openhcs.agent.dto.functions import FunctionCatalogPreparationStartRequest + + request_type = FunctionCatalogPreparationStartRequest + registry_key = request_type.message_type.value + + def preparation_state( + self, + preparation: FunctionCatalogPreparation, + request: FunctionCatalogPreparationStartRequest, + ) -> FunctionCatalogPreparationState: + return preparation.start(request.connection) + + +class ReadFunctionCatalogPreparationStrategy( + FunctionCatalogPreparationMessageStrategy[ + "FunctionCatalogPreparationStatusRequest" + ], +): + from openhcs.agent.dto.functions import FunctionCatalogPreparationStatusRequest + + request_type = FunctionCatalogPreparationStatusRequest + registry_key = request_type.message_type.value + + def preparation_state( + self, + preparation: FunctionCatalogPreparation, + request: FunctionCatalogPreparationStatusRequest, + ) -> FunctionCatalogPreparationState: + return preparation.observe(request.handle) + + +class CancelFunctionCatalogPreparationStrategy( + FunctionCatalogPreparationMessageStrategy[ + "FunctionCatalogPreparationCancelRequest" + ], +): + from openhcs.agent.dto.functions import FunctionCatalogPreparationCancelRequest + + request_type = FunctionCatalogPreparationCancelRequest + registry_key = request_type.message_type.value + + def preparation_state( + self, + preparation: FunctionCatalogPreparation, + request: FunctionCatalogPreparationCancelRequest, + ) -> FunctionCatalogPreparationState: + return preparation.cancel_preparation(request.handle) + + +class CustomFunctionRegistrationMessageStrategy(ZMQControlMessageStrategy): """Register custom source through the execution endpoint catalog owner.""" from openhcs.agent.dto.functions import CustomFunctionRegistrationRequest @@ -315,19 +436,21 @@ class CustomFunctionRegistrationMessageStrategy(FunctionCatalogMessageStrategy): request_type = CustomFunctionRegistrationRequest registry_key = request_type.message_type.value - def handle_ready( - self, - message: dict, - function_catalog: "FunctionCatalogService", - ) -> dict: + def handle(self, message: dict, context: ZMQControlRequestContext) -> dict: from openhcs.agent.dto.functions import ( CustomFunctionRegistrationControlResponse, + FunctionCatalogPreparationHandle, ) - - request = self.request_type.from_control_payload(message) - return CustomFunctionRegistrationControlResponse( - value=function_catalog.register_custom_function(request), - ).to_control_response() + try: + request = self.request_type.from_control_payload(message) + context.require_function_catalog_preparation().observe( + FunctionCatalogPreparationHandle(request.connection, request.require_server_identity()) + ).require_ready() + return CustomFunctionRegistrationControlResponse( + value=context.require_function_catalog().register_custom_function(request), + ).to_control_response() + except Exception as error: + return self.error_response(error) class DebugSnapshotReadMessageStrategy( diff --git a/openhcs/runtime/zmq_execution_client.py b/openhcs/runtime/zmq_execution_client.py index 3f671f197e..9d9e9e63e0 100644 --- a/openhcs/runtime/zmq_execution_client.py +++ b/openhcs/runtime/zmq_execution_client.py @@ -70,10 +70,14 @@ if TYPE_CHECKING: from openhcs.agent.dto.functions import ( + CustomFunctionRegistrationDestination, + CustomFunctionRegistrationDestinationRequest, CustomFunctionRegistrationRequest, CustomFunctionRegistrationResult, FunctionCatalogControlRequest, + FunctionCatalogControlRequestABC, FunctionCatalogPage, + FunctionCatalogPreparationState, FunctionDetail, FunctionDetailControlRequest, FunctionReferenceControlRequest, @@ -894,30 +898,109 @@ def get_function_reference( def register_custom_function( self, request: CustomFunctionRegistrationRequest, + *, + operation_deadline: OperationDeadline | None = None, ) -> CustomFunctionRegistrationResult: - """Register custom source through this execution endpoint's catalog.""" + """Send one mutation; readiness belongs to preceding read-only discovery. + + Never poll/resend a source-bearing request, including when the server + reports preparation pending. A missing mutation receipt is uncertain. + """ from openhcs.agent.dto.functions import ( CustomFunctionRegistrationControlResponse, FunctionCatalogControlPayload, ) - if not self.is_connected() and not self.connect(): - raise RuntimeError("Failed to connect to execution server") - response = self._send_function_catalog_control_request( - FunctionCatalogControlPayload.from_request(request).to_dict() + deadline = operation_deadline or OperationDeadline.after_milliseconds( + self.config.control_timeout_ms, + operation="custom function registration", + ) + if not self.is_connected() and not self.connect_existing( + timeout=deadline.cap_seconds(1.0), + ): + raise RuntimeError( + "Custom registration requires an existing execution endpoint." + ) + payload = FunctionCatalogControlPayload.from_request(request).to_dict() + response = self._send_control_request( + payload, + timeout_ms=deadline.remaining_milliseconds(), ) return CustomFunctionRegistrationControlResponse.from_control_response( response ).result + def custom_function_registration_destination( + self, + request: CustomFunctionRegistrationDestinationRequest, + *, + operation_deadline: OperationDeadline | None = None, + ) -> CustomFunctionRegistrationDestination: + """Require the selected endpoint's native admission contract before mutation.""" + from openhcs.agent.dto.functions import ( + CustomFunctionRegistrationDestinationControlResponse, + FunctionCatalogControlPayload, + ) + + deadline = operation_deadline or OperationDeadline.after_milliseconds( + self.config.control_timeout_ms, + operation="custom registration destination", + ) + if not self.is_connected() and not self.connect_existing( + timeout=deadline.cap_seconds(1.0), + ): + raise RuntimeError( + "Registration destination requires an existing execution endpoint." + ) + payload = FunctionCatalogControlPayload.from_request(request).to_dict() + response = self._send_control_request( + payload, + timeout_ms=deadline.remaining_milliseconds(), + ) + return ( + CustomFunctionRegistrationDestinationControlResponse.from_control_response( + response + ).destination + ) + + def function_catalog_preparation( + self, + request: FunctionCatalogControlRequestABC, + *, + operation_deadline: OperationDeadline | None = None, + ) -> FunctionCatalogPreparationState: + """One responsive start/status/cancel exchange on an existing endpoint.""" + from openhcs.agent.dto.functions import ( + FunctionCatalogControlPayload, + FunctionCatalogPreparationStateControlResponse, + ) + + deadline = operation_deadline or OperationDeadline.after_milliseconds( + self.config.control_timeout_ms, + operation="function catalog preparation observation", + ) + if not self.is_connected() and not self.connect_existing( + timeout=deadline.cap_seconds(1.0) + ): + raise RuntimeError( + "Function catalog preparation requires an existing execution endpoint." + ) + response = self._send_control_request( + FunctionCatalogControlPayload.from_request(request).to_dict(), + timeout_ms=deadline.remaining_milliseconds(), + ) + return FunctionCatalogPreparationStateControlResponse.from_control_response( + response + ).value + def _send_function_catalog_control_request( self, request: dict, *, cancellation: OperationCancellation | None = None, ) -> dict: - """Poll a responsive endpoint while its catalog preparation is active.""" + """Poll read-only discovery while endpoint catalog preparation is active.""" from openhcs.agent.dto.functions import ( FunctionCatalogPreparationControlResponse, diff --git a/packaging/codex/openhcs/skills/use-openhcs/SKILL.md b/packaging/codex/openhcs/skills/use-openhcs/SKILL.md index 0a69897198..dd703ea50f 100644 --- a/packaging/codex/openhcs/skills/use-openhcs/SKILL.md +++ b/packaging/codex/openhcs/skills/use-openhcs/SKILL.md @@ -9,6 +9,7 @@ description: Operate local OpenHCS microscopy workflows through the bundled MCP 2. Call `openhcs_get_authoring_context` with `kind="first_use"` when choosing or resuming an OpenHCS workflow, then request exactly the task context it names. For multisite assembly, registration, normalisation, segmentation review, or image-result QC, `kind="image_analysis_workflow"` is the canonical operating guide; follow it rather than reconstructing those rules from this skill. 3. Treat Python pipeline source as one complete `PipelineDocument`: `pipeline_config` plus ordered `pipeline_steps`. Never send a steps-only fragment, mirror configuration through a second argument, or strip source bindings from the reviewed document. UI-visible and headless routes use these same declarations even though their process and state owners differ. 4. Call `openhcs_search_capabilities` with task-relevant workflow, target, or text filters. Its current `surface_profile`, registry-owned workflow metadata, side effects, and security metadata—not a remembered tool list—decide whether to use a UI-visible or exposed headless route. Use `openhcs_list_capabilities` only when the complete selected surface is required. + Before the first cold local function-catalogue search, discover `catalog preparation` capabilities and follow the existing [responsive preparation procedure](references/custom-function-authoring.md#register-on-the-intended-process-owner) (`openhcs_custom_function_workflow`) on the intended existing endpoint. Observe its exact handle until READY; keep cold warming separate from ordinary 10-second tool observations. A timeout is not permission to restart or replay. 5. Start with validated OpenHCS/CellProfiler benchmark examples, especially the first-class Official30 corpus. Search the biological task plus `OpenHCS Python`, retrieve the exact `openhcs_official30_benchmark_recipes` section with `max_chars=50000`, and inspect its native reference, pipeline, input/channel assumptions, settings, and case-specific parity evidence. Benchmark parity is meaningful validation for that reference scope; it is not biological raw/overlay acceptance for a different assay. Record the example source and evidence tier before adapting it. CellProfiler image, object, measurement, relationship, and export semantics lower into the same OpenHCS declarations and runtime. For a compact, structured example of those fields, retrieve `openhcs_official30_examplehuman_nuclei_recipe_card` when the task involves ExampleHuman-style nuclei segmentation; its selected-value parity and unassessed new-assay QA are deliberately separate. Before authoring, retain the example-selection record described in [the autonomous analysis strategy](references/analysis-strategy.md) (`openhcs_autonomous_analysis_strategy`); reading this instruction is not evidence of retrieving a pipeline. For an unfamiliar assay or an unresolved biological failure, use that guide's task-specific routes to image interpretation, preprocessing, segmentation diagnostics, measurements and recipe/error learning. Retrieve only the relevant guide; infer a provisional strategy from acquisition and matched raw channels before asking the user for missing information. These guides adapt Agentic-J's domain knowledge, not its Fiji command strings or model defaults. diff --git a/packaging/codex/openhcs/skills/use-openhcs/references/custom-function-authoring.md b/packaging/codex/openhcs/skills/use-openhcs/references/custom-function-authoring.md index a2f3f93493..c9236689fa 100644 --- a/packaging/codex/openhcs/skills/use-openhcs/references/custom-function-authoring.md +++ b/packaging/codex/openhcs/skills/use-openhcs/references/custom-function-authoring.md @@ -49,31 +49,103 @@ the source and intended storage target within the existing mutation authority. Retain exact source bytes and hash, MCP process identity, execution endpoint, registration receipt, returned function ID, import path and persisted paths. -For parallel isolated sessions, verify catalog ownership as well as GUI bridge -ownership. In the current local stdio route, the catalog endpoint is selected -from the UI config cache when the MCP process starts; passing a GUI connection -to a later UI call does not redirect catalog registration. Pin the owned cache -selector (`OPENHCS_UI_CONFIG_CACHE_FILE`) before starting that MCP process, then -verify its endpoint. Use the current reflected contract if this route changes. -Do not resolve a timeout by registering on a peer or default catalog. - -Also verify the persisted-source storage owner. Local custom-function imports -resolve through `CustomFunctionManager.storage_dir`; in an isolated session, -carry the GUI/backend's appropriate `XDG_DATA_HOME` into each consuming MCP -process at startup. The UI cache selects a catalog endpoint, not that local -source directory. Compare the registration receipt's persisted path with the -consumer's storage scope, then prove the stable import on that consumer. -Changing the launch environment does not update an already running process. -Do not copy the source into default storage or re-register it merely to mask -a directory mismatch; reconcile ownership and any uncertain mutation first. +Registration requires an explicit `port`, including for `persist=false`; the +UI cache is not mutation-route authority. Inspect the reflected connection +fields (`host`, `transport_mode`, `persistent`) and supply the intended owned +execution endpoint. For `persist=true`, also supply `function_name` (the public +identifier you deliberately authored) and an absolute `storage_dir`. Do not +infer the name by parsing source or omit the route to use a default server. + +Derive the caller-intended store through its native owner in the **exact launch +environment of the owned execution server**, before starting either process: + +```python +from openhcs.processing.custom_functions.manager import CustomFunctionManager +intended_store = CustomFunctionManager.default_storage_directory() +``` + +This is a non-creating lookup, not a new filename resolver. In a controlled +isolated launch, pin `XDG_DATA_HOME` in that environment and carry it into every +consumer needing stable imports. Retain the returned absolute path and owned +endpoint from the launcher. If the native launch environment is unknown, stop +and obtain that owner contract; do not guess a default path or submit source +to discover where it gets saved. No public MCP destination-discovery tool is +currently exposed for an arbitrary existing server. + +For example, the controlled synthetic acceptance driver supplies the native +owner's path and deliberately authored name, not a receipt-derived permission: + +```python +arguments = { + "source_code": reviewed_source, + "function_name": "registration_live_probe", + "storage_dir": str(intended_store), + "port": owned_port, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": True, +} +``` + +The existing MCP path policy admits the intended directory and owner-derived +file **before any endpoint dispatch**. The service then queries the native +read-only destination owner, verifies its actual store/file and process +incarnation, and binds that proof to the one registration request. Unsupported +destination proof, a different store, or a changed process rejects without +evaluating the source. The execution service independently admits the native +write before evaluation and persistence. Receipt checks after writing verify +the outcome; they are not write admission or a Python sandbox. + +Prepare the catalogue on the intended endpoint **before submitting source**: + +1. Call `openhcs_start_function_catalog_preparation` with the same explicit + `port`, `host`, `transport_mode` and `persistent` connection fields. It starts + or coalesces the endpoint's existing catalogue/kernel preparation future, + including its supervised preparation child and declared kernel-cache writes. + It returns promptly, not after the cold preparation finishes. +2. Retain the returned `handle` exactly: its `connection` and + `server_identity` (PID plus creation time) identify that native owner. + Pass those two fields to `openhcs_get_function_catalog_preparation_status`. + Observe `outcome` and `progress`; do not infer readiness from elapsed time or + a progress message. Only `outcome="ready"` admits registration. Pending + observations do not start another future or submit custom source. +3. If preparation fails or is cancelled, retain the error and handle. Do not + submit source or replace the runtime implicitly. To cancel your pending + operation, pass the same fields to `openhcs_cancel_function_catalog_preparation`; + it signals the existing owner promptly. Continue observing that handle until + terminal, preserving the supervised child's cleanup rather than restarting it. +4. Once ready, complete catalogue discovery on the selected endpoint, then + send registration once. Registration independently observes readiness once + within the existing control deadline. `function_catalog_not_ready` means no + source-bearing registration RPC was sent by this service; the native handler + also refuses cold registration without initiating preparation. It does not + poll a cold catalogue and write later after the caller's observation expires. + +The service never polls/resends a source-bearing request, including on pending, +error or missing receipt. A start/status observation timeout is not proof that +preparation did not start or finish: preserve its input/handle and reconcile +the same owner. It does not authorise a new source mutation or runtime fallback. + +For isolated sessions, also pin `OPENHCS_UI_CONFIG_CACHE_FILE` before MCP +startup and verify the selected catalog. That launch-time selector still owns +initial discovery; passing a later GUI connection does not redirect it. +An admitted registration selects its explicit endpoint for subsequent catalog +reads, including uncertainty reconciliation. The cache does not select the +local source directory. Changing environment variables does not update an +already running process. Verify the receipt's endpoint, process incarnation, +function ID, stable import and source bytes on each actual consuming process. +Do not copy into default storage to mask a mismatch. Use `openhcs_register_custom_function`, which delegates validation, persistence -and registry publication to `CustomFunctionManager`. Choose persistence when -the reviewed pipeline needs a stable import across GUI, backend or fresh worker -processes, after confirming the returned storage directory is in scope. -An ephemeral backend registration does not prove importability in another -process. A timeout leaves mutation outcome uncertain: reconcile the exact name -on the same endpoint before any explicit retry. Creating an existing name is +and registry publication to `CustomFunctionManager`. An ephemeral registration +does not prove importability in another process. After mutation dispatch, a +timeout or invalid receipt returns `custom_function_registration_uncertain`: +it preserves the explicit endpoint, intended store/name and process identity, +and performs no fallback or automatic registration retry. Keep the same MCP +handle and original input. Read-only discovery on that endpoint and inspection +of the admitted source may reconcile publication; a failed observation is not +proof of no mutation. Obtain explicit disposition before any new mutation. +Creating an existing name is not a persistence upgrade; use an exposed lifecycle update route or record the missing capability rather than overwriting its file or mutating private state. If a lifecycle update is unavailable, a deliberately versioned new callable diff --git a/packaging/codex/openhcs/skills/use-openhcs/references/measurement-interpretation.md b/packaging/codex/openhcs/skills/use-openhcs/references/measurement-interpretation.md index c683124c8c..7fbf8b9c0e 100644 --- a/packaging/codex/openhcs/skills/use-openhcs/references/measurement-interpretation.md +++ b/packaging/codex/openhcs/skills/use-openhcs/references/measurement-interpretation.md @@ -72,12 +72,12 @@ measurements, not ground truth or an automatically validated parameter choice. Availability is determined by the **live** capability registry, not this guide. These source contracts accompany [issue221](https://github.com/OpenHCSDev/openhcs/issues/221); -their source-pinned synthetic MCP/viewer path has verified original crop and -relative spacing, empirical values, unchanged raw pixels/presentation and -author-opened MCP bitmaps at 5da177ad9 with PolyStore c16b8fc, including actual -child shared-cache/no-download policy verification. This is not a -physical-calibration claim or installed readiness: parent merge/install and its -affected-entrypoint check remain separate. Both operations are read-only and +PR225 is merged and its installed synthetic MCP/native viewer journey at +OpenHCS fbf6b2d91 / PolyStore1209068 verified a cropped two-channel fixture, +empirical measurement, snapshot and native close. The earlier source proof +also verified unchanged raw pixels/presentation and shared-cache/no-download +child policy. Neither proof establishes physical calibration or biological +accuracy. Both operations are read-only and require a settled, scalar, non-multiscale image route in a native YX 2-D display. Unbound stacks/RGB, ambiguous records, missing axes, sparse padding, nonfinite inputs/pixels and out-of-bounds geometry fail explicitly; no coordinate clamps. diff --git a/tests/diagnostics/check_custom_registration_live.py b/tests/diagnostics/check_custom_registration_live.py new file mode 100644 index 0000000000..2551f51402 --- /dev/null +++ b/tests/diagnostics/check_custom_registration_live.py @@ -0,0 +1,908 @@ +"""Bounded real stdio MCP/native execution acceptance for issue230. + +Run only with the explicitly handed-off slot and existing shared interpreter. +The nonblocking flock is held until all owned handles are terminal. Unexpected +dispatch failures retain the same client/servers for operator disposition; +there is no retry, shared7777 connection, install, viewer, JVM or science input. +""" + +from __future__ import annotations + +import argparse +import fcntl +import hashlib +import json +import os +import subprocess +import sys +import time +import traceback +from pathlib import Path + +SOURCE = Path(__file__).resolve().parents[2] +PYTHON = Path("/home/ts/code/projects/openhcs/.venv/bin/python") +LOCK = Path("/home/ts/wt/openhcs-issue-batch-20260929/validation.lock") +PROBE = SOURCE / "tests/diagnostics/registration_probe_source.py" + + +def child_server(role: str, audit: Path, native_argv: list[str]) -> None: + """Diagnostic fault seam on the real launcher/server, not another registry.""" + import polystore + from zmqruntime.messages import ControlErrorResponse, MessageFields + + import openhcs + from openhcs.agent.dto.functions import ( + CustomFunctionRegistrationControlResponse, + FunctionCatalogControlField, + FunctionCatalogControlMessageType, + ) + from openhcs.runtime.zmq_execution_server import ZMQExecutionServer + from openhcs.runtime.zmq_execution_server_launcher import main + + native_imports = {"openhcs": openhcs.__file__, "polystore": polystore.__file__} + assert Path(openhcs.__file__).resolve() == SOURCE / "openhcs/__init__.py" + assert ( + Path(polystore.__file__).resolve().is_relative_to(SOURCE / "external/PolyStore") + ) + with audit.open("a") as stream: + stream.write( + json.dumps({"event": "native_imports", "paths": native_imports}) + "\n" + ) + + class DiagnosticExecutionServer(ZMQExecutionServer): + _server_type = "registration_acceptance_fixture" + + def handle_control_message(self, message): + with audit.open("a") as stream: + stream.write( + json.dumps( + { + "type": message[MessageFields.TYPE], + "time": time.time(), + } + ) + + "\n" + ) + if role == "unsupported" and message[MessageFields.TYPE] == ( + FunctionCatalogControlMessageType.CUSTOM_REGISTRATION_DESTINATION.value + ): + return ControlErrorResponse( + message="Unsupported destination proof fixture" + ).to_dict() + response = super().handle_control_message(message) + if role == "owned" and FunctionCatalogControlField.RESULT.value in response: + result = response[FunctionCatalogControlField.RESULT.value] + if ( + isinstance( + result, CustomFunctionRegistrationControlResponse.value_type + ) + and result.functions + and result.functions[0].name == "registration_live_delayed" + ): + with audit.open("a") as stream: + stream.write( + json.dumps( + { + "event": "persisted_before_delayed_receipt", + "result": str(result.source_file_paths), + "time": time.time(), + } + ) + + "\n" + ) + time.sleep( + 5.5 + ) # Controlled post-mutation fault; production timeout stays5000ms. + return response + + sys.argv = [sys.argv[0], *native_argv] + main(execution_server_type=DiagnosticExecutionServer) + + +def fixture(root: Path) -> tuple[Path, object]: + """Reuse the existing typed synthetic source-projection mechanisms.""" + import numpy as np + from polystore.virtual_workspace import SourcePixelRef + + from openhcs.constants import Microscope + from openhcs.core.image_file_serialization import ImageFileFormat + from openhcs.core.source_projection import ( + OpenHCSPlaneAddress, + SourcePlaneProjection, + SourceProjectionSet, + ) + from openhcs.core.virtual_workspace_metadata import AtomicMetadataWriter + from openhcs.microscopes.source_schema import SourceSchemaFilenameParser + + root.mkdir() + pixels = np.arange(64, dtype=np.uint16).reshape(8, 8) + path = root / "synthetic.tif" + fmt = ImageFileFormat.require_path(path) + from openhcs.core.runtime_image_values import ImagePayloadMetadata + + payload = ImagePayloadMetadata().payload_with(pixels) + fmt.write(path, payload) + projections = SourceProjectionSet( + ( + SourcePlaneProjection( + OpenHCSPlaneAddress.from_values("A01", 1, 1, 1, 1), + SourcePixelRef("disk", path.name), + image_metadata=fmt.persisted_metadata(path, payload), + ), + ) + ) + metadata = projections.metadata_dict( + parser=SourceSchemaFilenameParser(), + microscope_handler_name=Microscope.SOURCE_BINDINGS.value, + source_filename_parser_name="SourceSchemaFilenameParser", + grid_dimensions=[], + pixel_size=1, + main=True, + ) + AtomicMetadataWriter().replace_subdirectory_metadata( + root / "openhcs_metadata.json", ".", metadata + ) + return path, pixels + + +def run(args) -> None: + if Path(sys.executable).resolve() != PYTHON.resolve(): + raise SystemExit("Use the existing shared interpreter.") + if args.installed_entrypoint: + if "PYTHONPATH" in os.environ: + raise SystemExit("Installed acceptance requires PYTHONPATH unset.") + if Path.cwd().resolve().is_relative_to(SOURCE): + raise SystemExit("Installed acceptance requires cwd outside source.") + if ( + subprocess.check_output( + ["git", "rev-parse", "HEAD"], cwd=SOURCE, text=True + ).strip() + != args.expected_source_sha + ): + raise SystemExit("Source differs from pinned checkpoint.") + receipt_dir = args.receipts.resolve() + scratch = args.scratch.resolve() + if SOURCE not in receipt_dir.parents or receipt_dir.exists(): + raise SystemExit("Receipts must be a new persistent directory in this tree.") + if scratch.parent != Path("/home/ts/.cache/agent-scratch") or scratch.exists(): + raise SystemExit("Scratch must be a new named owned agent-scratch directory.") + lock = LOCK.open("a+") + try: + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError: + raise SystemExit(75) + guard = subprocess.run( + ["/home/ts/bin/agent-resource-check", "--assert-headroom"], + capture_output=True, + text=True, + check=False, + ) + resources = json.loads(guard.stdout) + if resources["ram_available_gib"] < 8 or any( + not reason.startswith("swap used ") for reason in resources["reasons"] + ): + raise SystemExit("Resource guard disallows this finite validation.") + receipt_dir.mkdir(parents=True) + scratch.mkdir() + owned = scratch / "owned" + foreign = scratch / "foreign" + owned.mkdir() + foreign.mkdir() + for key in ( + "OMP_NUM_THREADS", + "OPENBLAS_NUM_THREADS", + "MKL_NUM_THREADS", + "NUMEXPR_NUM_THREADS", + "NUMBA_NUM_THREADS", + "BLIS_NUM_THREADS", + ): + os.environ[key] = "1" + os.environ.update( + OPENHCS_CPU_ONLY="true", + OPENHCS_HEADLESS="true", + OPENHCS_USE_THREADING="true", + OPENHCS_SUBPROCESS_NO_GPU="1", + CUDA_VISIBLE_DEVICES="", + QT_QPA_PLATFORM="offscreen", + PYTHONDONTWRITEBYTECODE="1", + POLYSTORE_IMAGEJ_ALLOW_DOWNLOAD="false", + POLYSTORE_IMAGEJ_CACHE_ROOT="/home/ts/.cache/polystore/imagej", + OPENHCS_AGENT_READ_ROOTS=os.pathsep.join( + (str(scratch), str(SOURCE / "packaging")) + ), + OPENHCS_AGENT_WRITE_ROOTS=str(owned), + XDG_DATA_HOME=str(owned / "data"), + XDG_CACHE_HOME=str(owned / "cache"), + XDG_CONFIG_HOME=str(owned / "config"), + XDG_STATE_HOME=str(owned / "state"), + XDG_RUNTIME_DIR=str(owned / "runtime"), + NUMBA_CACHE_DIR=str(owned / "numba"), + MPLCONFIGDIR=str(owned / "matplotlib"), + OPENHCS_UI_CONFIG_CACHE_FILE=str(owned / "ui_config.config"), + ) + from dataclasses import replace + + import numpy as np + import polystore + from python_introspect import dataclass_from_mapping + from zmqruntime.client import EndpointShutdownMode + from zmqruntime.config import TransportMode + from zmqruntime.transport import TransportEndpoint, wait_for_endpoint_ready + + import openhcs + from openhcs.mcp.dev_client import McpDevClient + from openhcs.mcp.dev_client_core import McpDevServerSpec + from openhcs.processing.custom_functions.manager import CustomFunctionManager + from openhcs.runtime.zmq_config import OPENHCS_ZMQ_CONFIG + from openhcs.runtime.zmq_execution_client import ZMQExecutionClient + from openhcs.serialization.json import to_jsonable + + imports = {"openhcs": openhcs.__file__, "polystore": polystore.__file__} + assert Path(openhcs.__file__).resolve() == SOURCE / "openhcs/__init__.py" + assert ( + Path(polystore.__file__).resolve().is_relative_to(SOURCE / "external/PolyStore") + ) + dependencies = subprocess.check_output( + ["git", "submodule", "status"], cwd=SOURCE, text=True + ).splitlines() + assert not any(row.startswith(("-", "+", "U")) for row in dependencies) + receipt = { + "accepted": False, + "source_sha": args.expected_source_sha, + "imports": imports, + "submodules": dependencies, + "resources": resources, + "driver_pid": os.getpid(), + "source_live_not_installed": not args.installed_entrypoint, + "installed_entrypoint": args.installed_entrypoint, + "launch_cwd": str( + Path.cwd().resolve() if args.installed_entrypoint else SOURCE + ), + "calls": [], + "processes": [], + "original033": "preserved; not replayed", + "shared7777": "never contacted", + "uncertain_dispatch": None, + "scratch": str(scratch), + } + + def save(): + (receipt_dir / "journey.json").write_text(json.dumps(receipt, indent=2) + "\n") + + save() + config = replace( + OPENHCS_ZMQ_CONFIG, transport_mode=TransportMode.TCP, server_host="127.0.0.1" + ) + ports = {"owned": args.port, "foreign": args.port + 1, "unsupported": args.port + 2} + assert 7777 not in ports.values() + endpoints = { + role: TransportEndpoint("127.0.0.1", port, TransportMode.TCP) + for role, port in ports.items() + } + assert not any(endpoint.is_in_use(config) for endpoint in endpoints.values()) + from openhcs.pyqt_gui.config import UIConfig, save_ui_config_sync + + assert save_ui_config_sync( + UIConfig(zmq=replace(config, default_port=args.port, client_host="127.0.0.1")) + ) + image_path, input_pixels = fixture(owned / "plate") + before_hash = hashlib.sha256(image_path.read_bytes()).hexdigest() + probe_code = PROBE.read_text() + source_hash = hashlib.sha256(probe_code.encode()).hexdigest() + store = CustomFunctionManager.default_storage_directory() + from zmqruntime.messages import ExecutionStatus + + from openhcs.agent.capabilities import ( + AgentCapabilitySearchResult, + agent_capabilities, + ) + from openhcs.agent.dto.authoring import AuthoringContext + from openhcs.agent.dto.execution import ( + ExecutionJobRef, + ExecutionJobStatus, + OrchestratorSessionRef, + ) + from openhcs.agent.dto.functions import ( + CustomFunctionRegistrationRequest, + CustomFunctionRegistrationResult, + FunctionCatalogPage, + FunctionCatalogPreparationOutcome, + FunctionCatalogPreparationState, + FunctionDetail, + ) + from openhcs.agent.dto.knowledge import KnowledgeBaseDocument + + servers = [] + handles = [] + client = None + started = time.monotonic() + + class SourceMcpServerSpec(McpDevServerSpec): + # Explicit source-test projection through the existing launch owner. + mcp_environment_keys = (*McpDevServerSpec.mcp_environment_keys, "PYTHONPATH") + + def call(name, arguments, result_type=None, *, error_code=None): + check = subprocess.run( + ["/home/ts/bin/agent-resource-check", "--assert-headroom"], + capture_output=True, + text=True, + check=False, + ) + current_resources = json.loads(check.stdout) + scratch_bytes = sum( + path.lstat().st_size for path in scratch.rglob("*") if path.is_file() + ) + receipt.setdefault("resource_observations", []).append( + {"guard": current_resources, "scratch_bytes": scratch_bytes} + ) + save() + if scratch_bytes >= 80 * 1024 * 1024: + raise RuntimeError("80MiB scratch bound reached; no further dispatch.") + if current_resources["ram_available_gib"] < 8 or any( + not reason.startswith("swap used ") + for reason in current_resources["reasons"] + ): + raise RuntimeError("Non-swap resource gate closed; no further dispatch.") + if time.monotonic() - started > 240: + raise RuntimeError("Finite journey budget exhausted; no further dispatch.") + row = { + "tool": name, + "arguments": arguments, + "started_utc": time.time(), + "status": "dispatched", + } + receipt["calls"].append(row) + receipt["uncertain_dispatch"] = len(receipt["calls"]) + save() + tick = time.monotonic() + executed = client.execute( + [ + "--allow-error-payloads", + "call", + name, + "--arguments", + json.dumps(arguments), + "--json", + ], + timeout_seconds=10, + ) + row.update( + response=executed.payload, + elapsed_seconds=time.monotonic() - tick, + status="returned", + ) + save() + if executed.payload["errors"]: + raise RuntimeError( + f"Uncertain transport outcome for {name}; do not replay." + ) + payload = executed.payload["results"][0]["payloads"][0] + errors = payload.get("errors", []) + if error_code: + assert errors and errors[0]["code"] == error_code, payload + else: + assert not errors and not executed.payload["results"][0]["mcp_error"], ( + payload + ) + receipt["uncertain_dispatch"] = None + save() + print(f"{name}: {row['elapsed_seconds']:.3f}s", flush=True) + return dataclass_from_mapping(result_type, payload) if result_type else payload + + def register( + name="registration_live_probe", + root=store, + port=args.port, + code=probe_code, + *, + error_code=None, + ): + return call( + "openhcs_register_custom_function", + { + "source_code": code, + "function_name": name, + "storage_dir": str(root), + "port": port, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": True, + }, + None if error_code else CustomFunctionRegistrationResult, + error_code=error_code, + ) + + def finish_job(ref): + # call() enforces the single existing 240s journey/10s observation bounds. + while True: + status = call( + "openhcs_get_execution_status", + {"job_id": ref.job_id, "timeout_ms": 1000}, + ExecutionJobStatus, + ) + if status.is_terminal: + assert status.status == ExecutionStatus.COMPLETE.value, status + return status + time.sleep( + 0.2 + ) # Existing submitted-job lifecycle observation, never a submit replay. + + try: + for role, port in ports.items(): + env = os.environ.copy() + if role == "foreign": + env.update( + XDG_DATA_HOME=str(foreign / "data"), + OPENHCS_AGENT_WRITE_ROOTS=str(foreign), + ) + audit = receipt_dir / f"{role}-control.jsonl" + log = (receipt_dir / f"{role}-stderr.txt").open("w") + handles.append(log) + process = subprocess.Popen( + [ + str(PYTHON), + "-B", + str(Path(__file__).resolve()), + "--child-role", + role, + "--child-audit", + str(audit), + "--port", + str(port), + "--host", + "127.0.0.1", + "--transport-mode", + "tcp", + "--persistent", + "--log-file-path", + str(receipt_dir / f"{role}.log"), + ], + env=env, + cwd=Path(receipt["launch_cwd"]), + stdout=log, + stderr=subprocess.STDOUT, + ) + servers.append((role, process)) + pong = wait_for_endpoint_ready( + port, TransportMode.TCP, host="127.0.0.1", config=config, timeout=10 + ) + assert pong is not None and pong.process_identity is not None + assert pong.process_identity.pid == process.pid + receipt["processes"].append( + { + "role": role, + "port": port, + "identity": to_jsonable(pong.process_identity), + } + ) + save() + stderr = (receipt_dir / "mcp-stderr.txt").open("w") + handles.append(stderr) + client = McpDevClient( + str(PYTHON), + use_resident_server=False, + server_stderr=stderr, + initialize_timeout_seconds=10, + ) + if not args.installed_entrypoint: + client.server_spec = SourceMcpServerSpec(str(PYTHON)) + client.start() + health = call("openhcs_health_check", {}) + assert ( + Path(health["server_source_path"]).resolve() + == SOURCE / "openhcs/mcp/server.py" + ) + assert ( + not health["restart_required"] + and not health["server_source_changed_since_import"] + ) + receipt["mcp_pid"] = health["server_process_id"] + receipt["mcp_launch_environment"] = client.server_spec.environment() + for kind in ("first_use", "pipeline"): + context = call( + agent_capabilities.get_authoring_context.name, + {"kind": kind}, + AuthoringContext, + ) + assert ( + agent_capabilities.start_function_catalog_preparation.name + in context.content + ) + guide = call( + agent_capabilities.get_knowledge_document.name, + {"document_id": "openhcs_custom_function_workflow"}, + KnowledgeBaseDocument, + ) + assert not guide.truncated + assert ( + agent_capabilities.start_function_catalog_preparation.name in guide.content + ) + discovery = call( + agent_capabilities.search_capabilities.name, + {"query": "catalog preparation", "limit": 10}, + AgentCapabilitySearchResult, + ) + assert agent_capabilities.start_function_catalog_preparation.name in { + capability.name for capability in discovery.capabilities + } + receipt["readiness_guidance_reachable"] = True + call("openhcs_get_authoring_context", {"kind": "custom_function"}) + call("openhcs_search_capabilities", {"query": "custom function", "limit": 5}) + call( + "openhcs_register_custom_function", + {"source_code": probe_code}, + error_code="mcp_tool_failed", + ) + sentinel = foreign / "registration_live_probe.py" + sentinel.write_bytes(b"preserved outside sentinel\n") + sentinel_hash = hashlib.sha256(sentinel.read_bytes()).hexdigest() + register(root=foreign, error_code="agent_path_policy_rejected") + ancestor = owned / "ancestor-escape" + ancestor.symlink_to(foreign, target_is_directory=True) + register(root=ancestor, error_code="agent_path_policy_rejected") + store.mkdir(parents=True, exist_ok=True) + destination = store / "registration_live_probe.py" + destination.symlink_to(sentinel) + register(error_code="agent_path_policy_rejected") + destination.unlink() # Only the diagnostic symlink, never its target. + register(port=ports["foreign"], error_code="mcp_tool_failed") + register(port=ports["unsupported"], error_code="mcp_tool_failed") + assert hashlib.sha256(sentinel.read_bytes()).hexdigest() == sentinel_hash + for role in ("foreign", "unsupported"): + rows = [ + json.loads(row) + for row in (receipt_dir / f"{role}-control.jsonl") + .read_text() + .splitlines() + ] + assert all( + row.get("type") != CustomFunctionRegistrationRequest.message_type.value + for row in rows + ) + receipt["protected_sentinel"] = { + "path": str(sentinel), + "sha256_before_after": sentinel_hash, + "unchanged": True, + } + register(error_code="function_catalog_not_ready") + preparation_started = time.monotonic() + state = call( + "openhcs_start_function_catalog_preparation", + { + "port": args.port, + "host": "127.0.0.1", + "transport_mode": "tcp", + }, + FunctionCatalogPreparationState, + ) + receipt["preparation_handle"] = to_jsonable(state.handle) + stale_handle = replace( + state.handle, + server_identity=replace(state.handle.server_identity, create_time=0), + ) + call( + "openhcs_cancel_function_catalog_preparation", + to_jsonable(stale_handle), + error_code="mcp_tool_failed", + ) + receipt["stale_preparation_cancel_rejected"] = True + if not state.outcome.ready: + register(error_code="function_catalog_not_ready") + while not state.outcome.ready: + assert not state.outcome.terminal, state + time.sleep(1) + state = call( + "openhcs_get_function_catalog_preparation_status", + to_jsonable(state.handle), + FunctionCatalogPreparationState, + ) + assert state.outcome is FunctionCatalogPreparationOutcome.READY + receipt["cold_preparation_elapsed_seconds"] = ( + time.monotonic() - preparation_started + ) + audit_before_mutation = [ + json.loads(row) + for row in (receipt_dir / "owned-control.jsonl").read_text().splitlines() + ] + assert all( + row.get("type") != CustomFunctionRegistrationRequest.message_type.value + for row in audit_before_mutation + ) + receipt["zero_source_dispatch_before_ready"] = True + call( + "openhcs_search_functions", + {"query": "registration_live_probe", "limit": 5}, + FunctionCatalogPage, + ) + registration = register() + assert registration.connection.port == args.port + assert registration.server_identity.pid == servers[0][1].pid + assert registration.source_file_paths == (str(destination),) + assert hashlib.sha256(destination.read_bytes()).hexdigest() == source_hash + page = call( + "openhcs_search_functions", + {"query": "registration_live_probe", "limit": 10}, + FunctionCatalogPage, + ) + assert registration.functions[0].function_id in { + entry.function_id for entry in page.items + } + detail = call( + "openhcs_describe_function", + {"function_id": registration.functions[0].function_id}, + FunctionDetail, + ) + assert ( + detail.entry.import_path + == "openhcs.processing.custom_functions.registration_live_probe" + ) + from openhcs.core.config import ( + LazyPathPlanningConfig, + LazyStepMaterializationConfig, + LazyVFSConfig, + MaterializationBackend, + PipelineConfig, + ) + from openhcs.core.pipeline_document import PipelineDocumentAuthority + from openhcs.core.steps.function_step import FunctionStep + from openhcs.processing.custom_functions import registration_live_probe + + document = PipelineDocumentAuthority.from_values( + pipeline_config=PipelineConfig( + num_workers=1, + use_threading=True, + path_planning_config=LazyPathPlanningConfig( + global_output_folder=owned / "outputs" + ), + vfs_config=LazyVFSConfig( + materialization_backend=MaterializationBackend.DISK + ), + ), + pipeline_steps=[ + FunctionStep( + func=(registration_live_probe, {"offset": 3}), + name="SyntheticPlusThree", + step_materialization_config=LazyStepMaterializationConfig( + enabled=True + ), + ) + ], + ) + pipeline_source = PipelineDocumentAuthority.render(document) + (receipt_dir / "pipeline.py").write_text(pipeline_source) + inspected = call( + "openhcs_inspect_pipeline_source_artifact_plan", + { + "plate_path": str(image_path.parent), + "pipeline_source": pipeline_source, + }, + ) + session = call( + "openhcs_create_orchestrator_session_from_pipeline_source", + { + "plate_path": str(image_path.parent), + "pipeline_source": pipeline_source, + "port": args.port, + "host": "127.0.0.1", + "transport_mode": "tcp", + }, + OrchestratorSessionRef, + ) + compile_started = time.monotonic() + compiled = finish_job( + call( + "openhcs_submit_compile", + {"session_id": session.session_id, "wait": False}, + ExecutionJobRef, + ) + ) + receipt["compile_response"] = to_jsonable(compiled) + receipt["compile_elapsed_seconds"] = time.monotonic() - compile_started + execution_started = time.monotonic() + executed = finish_job( + call( + "openhcs_submit_pipeline_execution", + {"session_id": session.session_id, "wait": False}, + ExecutionJobRef, + ) + ) + receipt["execution_response"] = to_jsonable(executed) + receipt["execution_elapsed_seconds"] = time.monotonic() - execution_started + output_images = list((owned / "outputs").rglob("*.tif")) + assert output_images, inspected + from openhcs.core.image_file_serialization import ImageFileFormat + + checked = [] + for output in output_images: + image = ImageFileFormat.require_path(output).read(output) + actual = np.asarray(image) + np.testing.assert_array_equal(actual, input_pixels + 3) + checked.append( + { + "path": str(output), + "sha256": hashlib.sha256(output.read_bytes()).hexdigest(), + "shape": list(actual.shape), + } + ) + receipt["checked_outputs"] = checked + assert hashlib.sha256(image_path.read_bytes()).hexdigest() == before_hash + delayed_code = probe_code.replace( + "registration_live_probe", "registration_live_delayed" + ) + register( + name="registration_live_delayed", + code=delayed_code, + error_code="custom_function_registration_uncertain", + ) + delayed_path = store / "registration_live_delayed.py" + assert delayed_path.read_text() == delayed_code + delayed = call( + "openhcs_search_functions", + {"query": "registration_live_delayed", "limit": 5}, + FunctionCatalogPage, + ) + assert ( + len( + [ + entry + for entry in delayed.items + if entry.name == "registration_live_delayed" + ] + ) + == 1 + ) + audit_rows = [ + json.loads(row) + for row in (receipt_dir / "owned-control.jsonl").read_text().splitlines() + ] + assert ( + sum( + row.get("type") == CustomFunctionRegistrationRequest.message_type.value + for row in audit_rows + ) + == 2 + ), "Exactly one initial and one controlled-delayed source dispatch; no resend" + assert ( + len( + [ + row + for row in audit_rows + if row.get("event") == "persisted_before_delayed_receipt" + ] + ) + == 1 + ) + receipt.update( + accepted=True, + source_bearing_register_rpc_count=2, + no_registration_replay=True, + unchanged_input_sha256=before_hash, + ) + save() + except BaseException as error: + receipt["failure"] = { + "type": type(error).__name__, + "message": str(error), + "traceback": traceback.format_exc(), + } + save() + print( + "STOPPED: original calls/handles preserved. Inspect on same handle or enter close-owned; never replay.", + flush=True, + ) + for command in sys.stdin: + if command.strip() == "close-owned": + receipt["operator_disposition"] = ( + "Close only diagnostic owned processes; do not replay any recorded call." + ) + receipt["uncertain_dispatch"] = None + save() + break + if client is not None: + import shlex + + observation = client.execute(shlex.split(command), timeout_seconds=10) + receipt.setdefault("same_handle_observations", []).append( + observation.payload + ) + save() + print(observation.rendered_output, flush=True) + else: + raise RuntimeError( + "No operator disposition; retain diagnostic handles." + ) from error + finally: + if receipt["uncertain_dispatch"] is None: + if client is not None: + client.close() + receipt["client_closed"] = True + for role, process in reversed(servers): + process_row = next( + (row for row in receipt["processes"] if row["role"] == role), + None, + ) + if process.poll() is not None: + receipt.setdefault("closed_servers", []).append( + {"role": role, "returncode": process.returncode} + ) + continue + if process_row is None: + # Popen retains this exact child handle even if startup failed. + process.terminate() + process.wait(timeout=10) + receipt.setdefault("closed_servers", []).append( + { + "role": role, + "returncode": process.returncode, + "startup_failed": True, + } + ) + continue + identity = process_row["identity"] + heartbeat = endpoints[role].ping(config, timeout_ms=1000) + assert ( + heartbeat is not None + and to_jsonable(heartbeat.process_identity) == identity + ) + shutdown = ZMQExecutionClient.shutdown_endpoint_on_port( + ports[role], + EndpointShutdownMode.FORCE, + timeout=5, + transport_mode=TransportMode.TCP, + host="127.0.0.1", + config=config, + ) + process.wait(timeout=10) + receipt.setdefault("closed_servers", []).append( + { + "role": role, + "shutdown": to_jsonable(shutdown), + "returncode": process.returncode, + } + ) + for handle in handles: + handle.close() + receipt["runtime_terminal"] = True + receipt["journey_elapsed_seconds"] = time.monotonic() - started + save() + fcntl.flock(lock, fcntl.LOCK_UN) + lock.close() + save() + print( + json.dumps( + { + "receipt": str(receipt_dir / "journey.json"), + "accepted": receipt["accepted"], + "terminal": receipt.get("runtime_terminal"), + } + ), + flush=True, + ) + + +def main(): + if "--child-role" in sys.argv: + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument( + "--child-role", choices=("owned", "foreign", "unsupported"), required=True + ) + parser.add_argument("--child-audit", type=Path, required=True) + args, remainder = parser.parse_known_args() + child_server(args.child_role, args.child_audit, remainder) + return + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--released-runtime-slot", action="store_true", required=True) + parser.add_argument("--expected-source-sha", required=True) + parser.add_argument( + "--installed-entrypoint", + action="store_true", + help="Require ordinary installed imports with PYTHONPATH unset and cwd outside source.", + ) + parser.add_argument("--receipts", type=Path, required=True) + parser.add_argument("--scratch", type=Path, required=True) + parser.add_argument("--port", type=int, default=15991) + run(parser.parse_args()) + + +if __name__ == "__main__": + main() diff --git a/tests/diagnostics/registration_probe_source.py b/tests/diagnostics/registration_probe_source.py new file mode 100644 index 0000000000..98b95ddadd --- /dev/null +++ b/tests/diagnostics/registration_probe_source.py @@ -0,0 +1,8 @@ +"""Synthetic identity-plus-three contract; no file IO or biological semantics.""" + +from openhcs.core.memory import numpy + + +@numpy +def registration_live_probe(image, offset=3): + return image + offset diff --git a/tests/runtime_diagnostics/registration_attach_only_20260929/pytest.txt b/tests/runtime_diagnostics/registration_attach_only_20260929/pytest.txt new file mode 100644 index 0000000000..d2009d14ea --- /dev/null +++ b/tests/runtime_diagnostics/registration_attach_only_20260929/pytest.txt @@ -0,0 +1,16 @@ +openhcs=/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/__init__.py +polystore=/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src/polystore/__init__.py +......................................................... [100%] +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +57 passed, 25 deselected, 2 warnings in 7.22s diff --git a/tests/runtime_diagnostics/registration_attach_only_20260929/resources.txt b/tests/runtime_diagnostics/registration_attach_only_20260929/resources.txt new file mode 100644 index 0000000000..a02dbdb4d5 --- /dev/null +++ b/tests/runtime_diagnostics/registration_attach_only_20260929/resources.txt @@ -0,0 +1 @@ +elapsed=8.04 max_rss_kib=309668 exit=0 diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt01/foreign-control.jsonl b/tests/runtime_diagnostics/registration_live_20260929_attempt01/foreign-control.jsonl new file mode 100644 index 0000000000..1ed20c3722 --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt01/foreign-control.jsonl @@ -0,0 +1,2 @@ +{"type": "openhcs_custom_function_registration_destination", "time": 1790723227.1595523} +{"type": "force_shutdown", "time": 1790723302.7396393} diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt01/foreign-stderr.txt b/tests/runtime_diagnostics/registration_live_20260929_attempt01/foreign-stderr.txt new file mode 100644 index 0000000000..a69ddf7c41 --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt01/foreign-stderr.txt @@ -0,0 +1,20 @@ +2026-09-29 19:06:53,772 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:06:53,772 - openhcs.runtime.zmq_execution_server_launcher - INFO - ZMQ Execution Server +2026-09-29 19:06:53,772 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:06:53,772 - openhcs.runtime.zmq_execution_server_launcher - INFO - Log level: INFO (from --log-level=INFO) +2026-09-29 19:06:53,772 - openhcs.runtime.zmq_execution_server_launcher - INFO - Port: 15992 (control: 16992) +2026-09-29 19:06:53,772 - openhcs.runtime.zmq_execution_server_launcher - INFO - Host: 127.0.0.1 +2026-09-29 19:06:53,772 - openhcs.runtime.zmq_execution_server_launcher - INFO - Transport mode: tcp +2026-09-29 19:06:53,772 - openhcs.runtime.zmq_execution_server_launcher - INFO - Persistent: True +2026-09-29 19:06:53,772 - openhcs.runtime.zmq_execution_server_launcher - INFO - Log file: /home/ts/wt/openhcs-custom-function-admission-20260929/tests/runtime_diagnostics/registration_live_20260929_attempt01/foreign.log +2026-09-29 19:06:53,772 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:06:54,460 - zmqruntime.server - INFO - ZMQ Server started on tcp://127.0.0.1:15992 (PUB), control tcp://127.0.0.1:16992 +2026-09-29 19:06:54,460 - zmqruntime.execution.server - INFO - Queue worker thread started - will process executions sequentially +2026-09-29 19:06:54,460 - zmqruntime.execution.server - INFO - Started execution queue worker thread +2026-09-29 19:06:54,460 - openhcs.runtime.zmq_execution_server_launcher - INFO - Server ready - waiting for requests... +2026-09-29 19:06:54,633 - zmqruntime.server - INFO - Server ready +2026-09-29 19:08:22,753 - zmqruntime.execution.server - INFO - No live worker processes found to kill +2026-09-29 19:08:22,753 - zmqruntime.execution.server - INFO - Workers killed (0), server shutting down +2026-09-29 19:08:22,763 - zmqruntime.server - INFO - ZMQ Server stopped +2026-09-29 19:08:22,763 - polystore.backend_registry - INFO - Backend connections cleaned up (including process resources) +2026-09-29 19:08:22,763 - openhcs.runtime.zmq_execution_server_launcher - INFO - Server stopped diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt01/journey.json b/tests/runtime_diagnostics/registration_live_20260929_attempt01/journey.json new file mode 100644 index 0000000000..f60bfdc7c4 --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt01/journey.json @@ -0,0 +1,921 @@ +{ + "accepted": false, + "source_sha": "81e636ad43566deb4a46392b9474dad40da16853", + "imports": { + "openhcs": "/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/__init__.py", + "polystore": "/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src/polystore/__init__.py" + }, + "submodules": [ + " b0004daf3223326ac78689ba7b2e68abc18ae298 external/ObjectState (v1.1.8)", + " 1209068f5fc9e4f7025a24ada8a73ef30dd1ddd7 external/PolyStore (v0.2.19-11-g1209068)", + " fddd9857bcd21468d20bc8547a769e0c6c9f1906 external/arraybridge (v0.3.4)", + " ca0a87e873f929b311a87a4d60cd3bfba315dbcf external/metaclass-registry (v0.2.1)", + " 792b03413331fdf107e369c924c257a2f0b16126 external/pycodify (v0.1.3)", + " a51c3d965f8ffdefe95a2b4b37347a85341d483a external/pyqt-reactive (v0.3.24)", + " 4e8f8bde5f750d9a50eacac80acd3806123addd8 external/python-introspect (v0.1.14-1-g4e8f8bd)", + " 1d3b32f4fcead23d5d2079c975eb2ae7646a1478 external/zmqruntime (v0.2.24)" + ], + "resources": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.0, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.6, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "driver_pid": 1779538, + "source_live_not_installed": true, + "calls": [ + { + "tool": "openhcs_health_check", + "arguments": {}, + "started_utc": 1790723224.8823767, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_health_check", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "status": "ok", + "started_at_unix": 1790723223.8288894, + "service": "openhcs.mcp", + "openhcs_version": "0.8.6", + "packaged_resources_ready": true, + "packaged_resource_count": 56, + "missing_packaged_resource_paths": [], + "server_process_id": 1781137, + "server_source_path": "/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/mcp/server.py", + "server_import_mtime_ns": 1790721541146819947, + "server_current_mtime_ns": 1790721541146819947, + "server_source_changed_since_import": false, + "stale_source_paths": [], + "recovery_reason": "current", + "installation_pointer_path": null, + "installation_pointer_changed_since_import": false, + "installation_pointer_available": null, + "restart_required": false, + "restart_command": [], + "restart_command_is_stable": false, + "reconnect_required": false, + "reconnect_owner": null, + "retry_after_reconnect": false, + "automatic_recovery_on_reconnect": false, + "restart_hint": null + } + ] + } + ] + }, + "elapsed_seconds": 0.01049570000031963 + }, + { + "tool": "openhcs_get_authoring_context", + "arguments": { + "kind": "first_use" + }, + "started_utc": 1790723224.9553807, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_authoring_context", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "kind": "first_use", + "content": "=== CHOOSE AN OPENHCS WORKFLOW ===\nUse this context when you are starting or resuming work and need to choose the OpenHCS state owner or next workflow.\n\n=== CORE MODEL IN ONE PASS ===\n- OpenHCS is a typed compiler/runtime for high-content microscopy. It projects microscope data into a storage-independent virtual workspace, compiles PipelineConfig plus ordered FunctionStep declarations, resolves multidimensional grouping and artifact contracts, executes through typed runtime values, and validates results through plate and viewer evidence.\n- Choose the state owner before choosing file formats or tools. If the user asks to open, show, continue, or edit work in the desktop, the UI-owned route always takes precedence over folder inspection or a headless session.\n\n=== CHOOSE ONE TASK ROUTE ===\nRequest exactly one matching context with openhcs_get_authoring_context; deepen into another only when the workflow reaches that boundary. Reuse this router after a handoff or a change of task; first_use is not a one-time setup step.\n- kind=\"ui_visible_workflow\" \u2014 Work in the running desktop UI: the user asks to open, show, continue, or edit work in the visible OpenHCS desktop.\n- kind=\"domain_expert_assisted_setup\" \u2014 Set up a workflow from biological intent: a domain expert describes the experiment and expected result but wants the agent to handle OpenHCS mechanics.\n- kind=\"folder_onboarding\" \u2014 Inspect and onboard image data: the task starts from a local plate, image folder, or rich image container and does not require visible UI ownership.\n- kind=\"pipeline\" \u2014 Author or revise a typed pipeline: the sources are understood and the next task is choosing functions, axes, grouping, configuration, or artifact contracts.\n- kind=\"image_analysis_workflow\" \u2014 Design and validate an image-analysis workflow: an analysis needs multisite assembly, registration, normalisation, mosaic quality control, segmentation review, or image-result provenance.\n- kind=\"custom_function\" \u2014 Author a custom processing function: the required operation is not already in the function registry or needs a typed custom artifact contract.\n- kind=\"headless_execution\" \u2014 Compile and execute headlessly: a reviewed pipeline should run without becoming selected or editable in the desktop UI.\n- kind=\"debugging\" \u2014 Diagnose compiled and runtime behavior: a compile, execution, step output, runtime value, or artifact must be inspected at its owning boundary.\n- kind=\"viewer_review\" \u2014 Validate results in a managed viewer: execution produced images, labels, or ROIs that need bounded visual and structured validation.\n- kind=\"objectstate_editing\" \u2014 Inspect or edit typed UI state: one exact UI-backed field, inheritance value, snapshot, or branch must be inspected or changed.\n- kind=\"cellprofiler_translation\" \u2014 Translate or validate a CellProfiler workflow: the intent begins with a .cppipe, CellProfiler module vocabulary, or an exact parity claim.\n\n=== SAFE FIRST ACTION ===\n- Start read-only. Query the active surface through openhcs_search_capabilities with task-relevant workflow, target, or text filters; its registry-owned metadata is the authority for what this server exposes. Use openhcs_list_capabilities only when the complete selected surface is required.\n- Guides describe workflows across capability profiles. Confirm a named tool is exposed before calling it. Desktop actions are discovered separately through openhcs_ui_list_actions; an empty capability search does not establish that a running window lacks the action.\n- Before operating a live desktop workflow, call openhcs_ui_list_windows and resolve visible modal top-level windows first. Inspect each dialog through openhcs_ui_get_widget_tree and invoke its declared button through openhcs_ui_invoke_widget_action; acknowledge completed operations, cancel unsolicited update/restart prompts, and never use desktop-coordinate automation or leave a blocking dialog hidden behind the analysis viewer.\n- Request the matching task context before mutation. Do not read every knowledge document, enumerate every function, invent config fields, or load full-resolution image data up front.\n- When continuing an analysis, read and reuse its existing pipeline and configuration. Adapt only the required source/layout choices, preserve earlier outputs in a separate destination, and record unavailable acquisitions as missing rather than zero measurements. Validate a representative bounded run before expanding.\n- If returned guidance is truncated, retrieve the same context with a sufficient max_chars before acting on an incomplete workflow.\n- Before any write or execution, show the exact target and intended change, refresh revision/request tokens, obtain approval, validate, and compile before running.\n\n=== DEEPEN ONLY WHEN NEEDED ===\nRetrieve one relevant source-backed target with openhcs_get_knowledge_document; use its returned section ids to narrow further instead of loading every document.\n- openhcs_architecture_quick_start \u2014 OpenHCS architecture quick start: Front-door OpenHCS architecture and MCP route covering the public PipelineConfig plus FunctionStep model, ObjectState resolution, function patterns, axes and grouping, typed artifacts, CellProfiler translation, generic source bindings, compilation, runtime values, examples, and safe operation." + } + ] + } + ] + }, + "elapsed_seconds": 1.3423105829861015 + }, + { + "tool": "openhcs_get_authoring_context", + "arguments": { + "kind": "custom_function" + }, + "started_utc": 1790723226.3642004, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_authoring_context", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "kind": "custom_function", + "content": "=== AUTHOR A CUSTOM PROCESSING FUNCTION ===\nUse this context when the required operation is not already in the function registry or needs a typed custom artifact contract.\n\n=== CUSTOM FUNCTIONS AND RUNTIME OUTPUTS ===\n- Custom functions are registry functions used inside FunctionStep.func patterns: callable, (callable, kwargs), list chains, or component/group keyed patterns.\n- A reviewed custom function becomes an ordinary registry-described callable; it does not create a separate pipeline mechanism. If openhcs_search_functions has no contract-compatible implementation for an intended operation, use this route, register the callable, then describe its reflected contract before composing it.\n- CLAHE, median denoising, background subtraction, and flat-field correction are example preprocessing intents, not a promised built-in catalog. Implement only the missing operation, keep it backend-agnostic at the pipeline boundary, and let its memory decorator, ProcessingContract, and artifact declarations state compatibility.\n- Dict patterns are routed by processing_config.group_by; list and single-callable patterns apply the same callable chain to each grouped 3D array.\n- processing_config.variable_components are the axes stacked into each callable input array; group_by groups the remaining 3D arrays and only selects callable branches for dict patterns.\n- Memory decorators such as @numpy define runtime memory conversion; FunctionStep processing_config defines grouping, axes, and input source.\n- Declare outputs with typed ArtifactSpec values. The decorator owns their input/output role. MeasurementsArtifactType receives schema-bearing ColumnarRows; ObjectLabelsArtifactType receives complete integer label arrays and may use segmentation_mask_rois for materialization.\n- Path-bearing topology belongs in one SpatialGraphArtifactType output whose SpatialGraph nodes, edges, and scalar features are authoritative. A directed morphology forest can materialize through SWCOptions while SpatialGraphROIOptions projects those same paths and features to Napari; do not reduce graph semantics to a mask or rebuild them in viewer code.\n- Artifact graph planning owns output names, sidecar results, materialization intent, and downstream artifact inputs; do not encode those as ad hoc files or hidden globals. Request the viewer-review context only after execution produces artifacts to inspect.\n\n=== CUSTOM FUNCTION RULES ===\n- Decorate exactly one callable with a memory decorator such as @numpy.\n- The first image-like argument is supplied by OpenHCS at runtime; do not pass it as a FunctionStep kwarg.\n- Prefer concrete typed parameters with serializable defaults.\n- Preserve dtype unless intentionally changing representation.\n- Declare measurements as MeasurementsArtifactType and return ColumnarRows; declare segmentation as ObjectLabelsArtifactType and return the complete integer label array.\n- Declare path topology as SpatialGraphArtifactType and return one SpatialGraph; use SWCOptions and SpatialGraphROIOptions as projections of that same value rather than independent graph and ROI computations.\n- Do not close over GUI, filesystem, or live viewer state.\n\n=== MINIMAL NUMPY CUSTOM FUNCTION ===\n```python\nfrom openhcs.core.memory import numpy\nimport numpy as np\n\n@numpy\ndef robust_clip(image, low_percentile: float = 1.0, high_percentile: float = 99.0):\n \"\"\"Clip intensities to robust percentiles while preserving dtype.\"\"\"\n array = np.asarray(image)\n if array.size == 0:\n return image\n low = np.percentile(array, low_percentile)\n high = np.percentile(array, high_percentile)\n if high <= low:\n return image\n return np.clip(array, low, high).astype(array.dtype, copy=False)\n```\n\n=== REGISTRATION WORKFLOW ===\n- Read the targeted custom-function and artifact-contract knowledge documents below before inventing decorators or payload types.\n- Register reviewed source through openhcs_register_custom_function; that capability delegates to the nominal CustomFunctionManager and owns persistence and registry refresh.\n- Search with openhcs_search_functions, describe the returned function_id with openhcs_describe_function, then request the pipeline context to add the function and validate its compiled artifact plan.\n\n=== DEEPEN ONLY WHEN NEEDED ===\nRetrieve one relevant source-backed target with openhcs_get_knowledge_document; use its returned section ids to narrow further instead of loading every document.\n- openhcs_custom_functions \u2014 OpenHCS custom functions: How to add custom processing functions, memory decorators, validation, GPU custom functions, registry integration, and GUI availability.\n- openhcs_custom_function_management \u2014 OpenHCS custom function management: CustomFunctionManager lifecycle, atomic updates, module injection, reload signals, and persisted custom-function registry behavior.\n- openhcs_artifact_contract_system \u2014 OpenHCS artifact contract system: Canonical callable/module artifact contracts, graph planning, exact satisfaction, runtime observations, and materialization.\n- openhcs_custom_function_workflow \u2014 Add a missing analysis operation as a typed custom function: Add a missing operation through typed custom-function owners: explicit endpoint/name/store admission before dispatch, controlled-launch native destination proof, postdispatch uncertainty without replay, stable imports, compilation and bounded outputs; separate technical reuse from biological acceptance." + } + ] + } + ] + }, + "elapsed_seconds": 0.14661152701592073 + }, + { + "tool": "openhcs_search_capabilities", + "arguments": { + "query": "custom function", + "limit": 5 + }, + "started_utc": 1790723226.574141, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_search_capabilities", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "surface_profile": "full", + "query": { + "text": null, + "kind": null, + "workflow_group": null, + "workflow_stage": null, + "target_context": null, + "visibility": null, + "role": null, + "has_side_effects": null, + "side_effect_contains": null, + "offset": 0, + "limit": 5 + }, + "matched_count": 98, + "returned_count": 5, + "next_offset": 5, + "capabilities": [ + { + "name": "openhcs://capabilities", + "kind": "resource", + "title": "OpenHCS agent capability registry", + "description": "Lists the resources, tools, side effects, and extras exposed by this server.", + "workflow_group": "discovery", + "workflow_stage": "discovery", + "target_context": "server", + "visibility": "beginner", + "role": "primary", + "read_only": true, + "side_effects": [], + "requires_network": false, + "required_extras": [], + "runtime_requirements": [], + "data_exposure": [], + "security_requirements": [], + "input_type": null, + "output_type": "AgentCapabilityRegistry" + }, + { + "name": "openhcs_sweep_viewer_endpoints", + "kind": "tool", + "title": "Sweep viewer endpoints", + "description": "Sweeps the local OpenHCS IPC directory for live viewer endpoints, probes each through the viewer-window state authority, and classifies every live viewer as owned or foreign relative to this process. Dead sockets are filtered by a bind probe; non-viewer endpoint pairs (execution, UI bridge) are excluded.", + "workflow_group": "discovery", + "workflow_stage": "diagnostic", + "target_context": "server", + "visibility": "beginner", + "role": "diagnostic", + "read_only": true, + "side_effects": [], + "requires_network": false, + "required_extras": [], + "runtime_requirements": [], + "data_exposure": [ + "viewer_endpoint_inventory" + ], + "security_requirements": [], + "input_type": null, + "output_type": "ViewerEndpointDiscoveryResult" + }, + { + "name": "openhcs_health_check", + "kind": "tool", + "title": "Health check", + "description": "Reports OpenHCS MCP health, installed OpenHCS version, packaged-resource readiness, server process identity, source freshness, installation-generation freshness, and the client-owned reconnect contract for stale-process diagnostics.", + "workflow_group": "discovery", + "workflow_stage": "diagnostic", + "target_context": "server", + "visibility": "beginner", + "role": "diagnostic", + "read_only": true, + "side_effects": [], + "requires_network": false, + "required_extras": [], + "runtime_requirements": [], + "data_exposure": [ + "installed_openhcs_version", + "packaged_resource_readiness", + "packaged_resource_paths", + "mcp_process_identity", + "mcp_source_freshness", + "mcp_installation_generation" + ], + "security_requirements": [], + "input_type": null, + "output_type": "McpServerHealthResult" + }, + { + "name": "openhcs_list_capabilities", + "kind": "tool", + "title": "List capabilities", + "description": "Returns the canonical agent capability registry.", + "workflow_group": "discovery", + "workflow_stage": "discovery", + "target_context": "server", + "visibility": "beginner", + "role": "primary", + "read_only": true, + "side_effects": [], + "requires_network": false, + "required_extras": [], + "runtime_requirements": [], + "data_exposure": [], + "security_requirements": [], + "input_type": null, + "output_type": "AgentCapabilityRegistry" + }, + { + "name": "openhcs_search_capabilities", + "kind": "tool", + "title": "Search capabilities", + "description": "Returns a bounded task-specific projection of the selected canonical capability registry. Filter by registry-owned workflow group, stage, target context, visibility, role, side effects, or free text before choosing tools.", + "workflow_group": "discovery", + "workflow_stage": "discovery", + "target_context": "server", + "visibility": "beginner", + "role": "primary", + "read_only": true, + "side_effects": [], + "requires_network": false, + "required_extras": [], + "runtime_requirements": [], + "data_exposure": [], + "security_requirements": [], + "input_type": "AgentCapabilitySearchRequest", + "output_type": "AgentCapabilitySearchResult" + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.010072286997456104 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n" + }, + "started_utc": 1790723226.6511168, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "mcp_tool_failed", + "message": "Custom function registration requires an explicit port.", + "hint": "The MCP server caught this exception at the tool boundary.", + "exception_type": "ValueError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.008670338982483372 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/foreign", + "port": 15991, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790723226.7264938, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "agent_path_policy_rejected", + "message": "Writable path is outside allowed roots: /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/foreign. Effective writable roots: /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned.", + "hint": "Pass a local path under OPENHCS_AGENT_READ_ROOTS or OPENHCS_AGENT_WRITE_ROOTS as appropriate. Use openhcs_inspect_plate_path first to validate a plate folder.", + "exception_type": "AgentPathPolicyError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.009579994017258286 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned/ancestor-escape", + "port": 15991, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790723226.8008244, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "agent_path_policy_rejected", + "message": "Writable path is outside allowed roots: /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/foreign. Effective writable roots: /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned.", + "hint": "Pass a local path under OPENHCS_AGENT_READ_ROOTS or OPENHCS_AGENT_WRITE_ROOTS as appropriate. Use openhcs_inspect_plate_path first to validate a plate folder.", + "exception_type": "AgentPathPolicyError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.013808730989694595 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned/data/openhcs/custom_functions", + "port": 15991, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790723226.8834674, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "agent_path_policy_rejected", + "message": "Writable path is outside allowed roots: /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/foreign/registration_live_probe.py. Effective writable roots: /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned.", + "hint": "Pass a local path under OPENHCS_AGENT_READ_ROOTS or OPENHCS_AGENT_WRITE_ROOTS as appropriate. Use openhcs_inspect_plate_path first to validate a plate folder.", + "exception_type": "AgentPathPolicyError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.008715464995475486 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned/data/openhcs/custom_functions", + "port": 15992, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790723226.9602127, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "mcp_tool_failed", + "message": "Selected endpoint owns custom storage /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/foreign/data/openhcs/custom_functions, source /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/foreign/data/openhcs/custom_functions/registration_live_probe.py, not requested /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned/data/openhcs/custom_functions/registration_live_probe.py; no source was dispatched.", + "hint": "The MCP server caught this exception at the tool boundary.", + "exception_type": "ValueError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.20088870599283837 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned/data/openhcs/custom_functions", + "port": 15993, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790723227.2370539, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "mcp_tool_failed", + "message": "{'status': 'error', 'type': 'error', 'message': 'Unsupported destination proof fixture'}", + "hint": "The MCP server caught this exception at the tool boundary.", + "exception_type": "RuntimeError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.14866862702183425 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned/data/openhcs/custom_functions", + "port": 15991, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790723227.4554276, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [ + { + "code": "mcp_transport_failed", + "phase": "call_tool", + "exception_type": "TimeoutError", + "message": "", + "causes": [ + { + "exception_type": "TimeoutError", + "message": "" + } + ], + "hint": "The fresh OpenHCS MCP subprocess did not complete the requested stdio exchange. The dev client captures a bounded server stderr tail on transport failures so startup tracebacks remain visible without noisy successful calls.", + "server_stderr_tail": null + } + ], + "results": [] + }, + "elapsed_seconds": 10.011426267010393 + } + ], + "processes": [ + { + "role": "owned", + "port": 15991, + "identity": { + "pid": 1779678, + "create_time": 1790723205.17 + } + }, + { + "role": "foreign", + "port": 15992, + "identity": { + "pid": 1780013, + "create_time": 1790723209.4 + } + }, + { + "role": "unsupported", + "port": 15993, + "identity": { + "pid": 1780246, + "create_time": 1790723213.82 + } + } + ], + "original033": "preserved; not replayed", + "shared7777": "never contacted", + "uncertain_dispatch": null, + "scratch": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01", + "resource_observations": [ + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.0, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.0, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "scratch_bytes": 3188 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.0, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.0, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "scratch_bytes": 3188 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.0, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.0, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "scratch_bytes": 3188 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.0, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.0, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "scratch_bytes": 3188 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.0, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.0, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "scratch_bytes": 3188 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.0, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.0, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "scratch_bytes": 3215 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.0, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.0, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "scratch_bytes": 3215 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.0, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.0, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "scratch_bytes": 3328 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.0, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.0, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "scratch_bytes": 3215 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.0, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.0, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "scratch_bytes": 3215 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.0, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.0, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "scratch_bytes": 3215 + } + ], + "mcp_pid": 1781137, + "mcp_launch_environment": { + "HOME": "/home/ts", + "LOGNAME": "ts", + "PATH": "/home/ts/.codex/packages/standalone/releases/0.159.1-x86_64-unknown-linux-musl/codex-path:/home/ts/.codex/tmp/arg0/codex-arg0ZH6jHa:/home/ts/.codex/tmp/arg0/codex-arg004m4pr:/home/ts/.codex/packages/standalone/releases/0.159.0-x86_64-unknown-linux-musl/codex-path:/home/ts/.codex/tmp/arg0/codex-arg0ChXg03:/home/ts/.codex/packages/standalone/releases/0.158.0-x86_64-unknown-linux-musl/codex-path:/home/ts/.codex/tmp/arg0/codex-arg0a3jKy1:/home/ts/.codex/packages/standalone/releases/0.157.1-x86_64-unknown-linux-musl/codex-path:/home/ts/.codex/tmp/arg0/codex-arg0fLf3ul:/usr/lib/openai-codex/codex-path:/home/ts/.pyenv/bin:/home/ts/bin:/usr/local/bin:/home/ts/.elan/bin:/home/ts/programs/Brayns/bin:/home/ts/.pyenv/bin:/home/ts/bin:/usr/local/bin:/home/ts/.elan/bin:/home/ts/programs/Brayns/bin:/home/ts/.elan/bin:/home/ts/programs/Brayns/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/opt/android-sdk/emulator:/opt/android-ndk:/opt/android-sdk/build-tools/30.0.3:/opt/cuda/bin:/home/ts/.local/share/flatpak/exports/bin:/var/lib/flatpak/exports/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/home/ts/.local/bin:/root/.local/bin:/home/ts/bin:/usr/local/bin:/home/ts/code/scripts/bar:/home/ts/.local/bin:/root/.local/bin:/home/ts/bin:/usr/local/bin:/home/ts/code/scripts/bar:/home/ts/.local/bin:/root/.local/bin:/home/ts/bin:/usr/local/bin:/home/ts/.dotfiles/revsl.sh:/home/ts/.local/bin:/root/.local/bin:/home/ts/bin:/usr/local/bin:/home/ts/code/scripts/bar:/home/ts/.local/bin:/root/.local/bin:/home/ts/bin:/usr/local/bin:/home/ts/.dotfiles/revsl.sh:/opt/android-sdk/emulator:/opt/android-ndk", + "SHELL": "/bin/zsh", + "TERM": "dumb", + "USER": "ts", + "QT_QPA_PLATFORM": "offscreen", + "DISPLAY": ":0", + "XAUTHORITY": "/home/ts/.Xauthority", + "XDG_RUNTIME_DIR": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned/runtime", + "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus", + "XDG_SESSION_TYPE": "x11", + "XDG_DATA_HOME": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned/data", + "XDG_CACHE_HOME": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned/cache", + "XDG_CONFIG_HOME": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned/config", + "PYTHONPATH": "/home/ts/wt/openhcs-custom-function-admission-20260929:/home/ts/wt/openhcs-custom-function-admission-20260929/external/ObjectState/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/arraybridge/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/metaclass-registry/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/pycodify/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/pyqt-reactive/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/python-introspect/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/zmqruntime/src", + "OPENHCS_AGENT_READ_ROOTS": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01:/home/ts/wt/openhcs-custom-function-admission-20260929/packaging", + "OPENHCS_AGENT_WRITE_ROOTS": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned", + "OPENHCS_UI_CONFIG_CACHE_FILE": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned/ui_config.config", + "OPENHCS_CPU_ONLY": "true", + "OPENHCS_HEADLESS": "true", + "NUMBA_CACHE_DIR": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/owned/numba", + "OPENHCS_USE_THREADING": "true", + "POLYSTORE_IMAGEJ_CACHE_ROOT": "/home/ts/.cache/polystore/imagej", + "POLYSTORE_IMAGEJ_ALLOW_DOWNLOAD": "false", + "OMP_NUM_THREADS": "1", + "OPENBLAS_NUM_THREADS": "1", + "MKL_NUM_THREADS": "1", + "NUMEXPR_NUM_THREADS": "1", + "BLIS_NUM_THREADS": "1", + "NUMBA_NUM_THREADS": "1" + }, + "protected_sentinel": { + "path": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt01/foreign/registration_live_probe.py", + "sha256_before_after": "748cc865eb250527979464908234f357d1ddf6eceecc50057eed58df7c911b79", + "unchanged": true + }, + "failure": { + "type": "RuntimeError", + "message": "Uncertain transport outcome for openhcs_register_custom_function; do not replay.", + "traceback": "Traceback (most recent call last):\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/tests/diagnostics/check_custom_registration_live.py\", line 513, in run\n registration = register()\n ^^^^^^^^^^\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/tests/diagnostics/check_custom_registration_live.py\", line 371, in register\n return call(\n ^^^^^\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/tests/diagnostics/check_custom_registration_live.py\", line 347, in call\n raise RuntimeError(\nRuntimeError: Uncertain transport outcome for openhcs_register_custom_function; do not replay.\n" + }, + "same_handle_observations": [ + { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_search_functions", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_search_functions", + "errors": [ + { + "code": "mcp_tool_failed", + "message": "Function registry preparation process failed: main()\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/runtime/zmq_execution_server_launcher.py\", line 152, in main\n capability_preparer()\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/runtime/function_catalog_preparation.py\", line 32, in prepare_persistent_catalog\n RegistryService.prepare_in_current_process()\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/lib_registry/registry_service.py\", line 113, in prepare_in_current_process\n cls._metadata_cache = cls._metadata_from_instances(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/lib_registry/registry_service.py\", line 191, in _metadata_from_instances\n functions = registry_instance.load_or_discover_functions()\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/lib_registry/openhcs_registry.py\", line 410, in load_or_discover_functions\n functions = super().load_or_discover_functions()\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/lib_registry/unified_registry.py\", line 1939, in load_or_discover_functions\n functions = self.discover_functions()\n ^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/lib_registry/openhcs_registry.py\", line 464, in discover_functions\n module_name: module.openhcs_registry_functions()\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/cellprofiler/__init__.py\", line 63, in openhcs_registry_functions\n return tuple(\n ^^^^^^\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/cellprofiler/__init__.py\", line 64, in \n module_type.require_callable(function_name)\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/interop/cellprofiler/module_declarations.py\", line 569, in require_callable\n implementation_module = importlib.import_module(cls.__module__)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/home/ts/.pyenv/versions/3.12.3/lib/python3.12/importlib/__init__.py\", line 90, in import_module\n return _bootstrap._gcd_import(name[level:], package, level)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"\", line 1387, in _gcd_import\n File \"\", line 1360, in _find_and_load\n File \"\", line 1331, in _find_and_load_unlocked\n File \"\", line 935, in _load_unlocked\n File \"\", line 995, in exec_module\n File \"\", line 488, in _call_with_frames_removed\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/cellprofiler/intensity.py\", line 347, in \n from openhcs.processing.backends.cellprofiler.shape import (\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/cellprofiler/shape.py\", line 48, in \n from openhcs.processing.backends.cellprofiler.zernike import (\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/cellprofiler/zernike.py\", line 62, in \n from openhcs.processing.backends.cellprofiler.granularity import (\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/cellprofiler/granularity.py\", line 94, in \n from openhcs.processing.backends.cellprofiler._granularity_reconstruct import (\nModuleNotFoundError: No module named 'openhcs.processing.backends.cellprofiler._granularity_reconstruct'", + "hint": "The MCP server caught this exception at the tool boundary.", + "exception_type": "RuntimeError", + "path": null + } + ] + } + ] + } + ] + } + ], + "operator_disposition": "Close only diagnostic owned processes; do not replay any recorded call.", + "client_closed": true, + "closed_servers": [ + { + "role": "unsupported", + "shutdown": { + "succeeded": true, + "endpoint_terminated": true + }, + "returncode": 0 + }, + { + "role": "foreign", + "shutdown": { + "succeeded": true, + "endpoint_terminated": true + }, + "returncode": 0 + }, + { + "role": "owned", + "shutdown": { + "succeeded": true, + "endpoint_terminated": true + }, + "returncode": 0 + } + ], + "runtime_terminal": true +} diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt01/mcp-stderr.txt b/tests/runtime_diagnostics/registration_live_20260929_attempt01/mcp-stderr.txt new file mode 100644 index 0000000000..ca517f933b --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt01/mcp-stderr.txt @@ -0,0 +1 @@ +MCP progress: progress=0.0 message='Search processing functions: started' diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt01/owned-control.jsonl b/tests/runtime_diagnostics/registration_live_20260929_attempt01/owned-control.jsonl new file mode 100644 index 0000000000..912c0fdc5b --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt01/owned-control.jsonl @@ -0,0 +1,140 @@ +{"type": "openhcs_custom_function_registration_destination", "time": 1790723227.5803049} +{"type": "openhcs_custom_function_register", "time": 1790723227.5914738} +{"type": "openhcs_custom_function_register", "time": 1790723227.700273} +{"type": "openhcs_custom_function_register", "time": 1790723227.828733} +{"type": "openhcs_custom_function_register", "time": 1790723227.9353049} +{"type": "openhcs_custom_function_register", "time": 1790723228.0378568} +{"type": "openhcs_custom_function_register", "time": 1790723228.1401956} +{"type": "openhcs_custom_function_register", "time": 1790723228.2426481} +{"type": "openhcs_custom_function_register", "time": 1790723228.3451438} +{"type": "openhcs_custom_function_register", "time": 1790723228.4476557} +{"type": "openhcs_custom_function_register", "time": 1790723228.549765} +{"type": "openhcs_custom_function_register", "time": 1790723228.6522295} +{"type": "openhcs_custom_function_register", "time": 1790723228.7544117} +{"type": "openhcs_custom_function_register", "time": 1790723228.856656} +{"type": "openhcs_custom_function_register", "time": 1790723228.9588645} +{"type": "openhcs_custom_function_register", "time": 1790723229.0608008} +{"type": "openhcs_custom_function_register", "time": 1790723229.1632671} +{"type": "openhcs_custom_function_register", "time": 1790723229.2654696} +{"type": "openhcs_custom_function_register", "time": 1790723229.3676624} +{"type": "openhcs_custom_function_register", "time": 1790723229.4697216} +{"type": "openhcs_custom_function_register", "time": 1790723229.5817015} +{"type": "openhcs_custom_function_register", "time": 1790723229.6836612} +{"type": "openhcs_custom_function_register", "time": 1790723229.7856302} +{"type": "openhcs_custom_function_register", "time": 1790723229.887767} +{"type": "openhcs_custom_function_register", "time": 1790723229.9900217} +{"type": "openhcs_custom_function_register", "time": 1790723230.0922165} +{"type": "openhcs_custom_function_register", "time": 1790723230.1944058} +{"type": "openhcs_custom_function_register", "time": 1790723230.2966282} +{"type": "openhcs_custom_function_register", "time": 1790723230.3986053} +{"type": "openhcs_custom_function_register", "time": 1790723230.5005667} +{"type": "openhcs_custom_function_register", "time": 1790723230.6025488} +{"type": "openhcs_custom_function_register", "time": 1790723230.7045736} +{"type": "openhcs_custom_function_register", "time": 1790723230.806687} +{"type": "openhcs_custom_function_register", "time": 1790723230.9085698} +{"type": "openhcs_custom_function_register", "time": 1790723231.010428} +{"type": "openhcs_custom_function_register", "time": 1790723231.1129386} +{"type": "openhcs_custom_function_register", "time": 1790723231.2153547} +{"type": "openhcs_custom_function_register", "time": 1790723231.3177602} +{"type": "openhcs_custom_function_register", "time": 1790723231.4200447} +{"type": "openhcs_custom_function_register", "time": 1790723231.5223575} +{"type": "openhcs_custom_function_register", "time": 1790723231.6241837} +{"type": "openhcs_custom_function_register", "time": 1790723231.7261627} +{"type": "openhcs_custom_function_register", "time": 1790723231.8282433} +{"type": "openhcs_custom_function_register", "time": 1790723231.930196} +{"type": "openhcs_custom_function_register", "time": 1790723232.0324206} +{"type": "openhcs_custom_function_register", "time": 1790723232.1347198} +{"type": "openhcs_custom_function_register", "time": 1790723232.237456} +{"type": "openhcs_custom_function_register", "time": 1790723232.3399465} +{"type": "openhcs_custom_function_register", "time": 1790723232.4419832} +{"type": "openhcs_custom_function_register", "time": 1790723232.543925} +{"type": "openhcs_custom_function_register", "time": 1790723232.6459892} +{"type": "openhcs_custom_function_register", "time": 1790723232.7476933} +{"type": "openhcs_custom_function_register", "time": 1790723232.8498456} +{"type": "openhcs_custom_function_register", "time": 1790723232.9519124} +{"type": "openhcs_custom_function_register", "time": 1790723233.0537715} +{"type": "openhcs_custom_function_register", "time": 1790723233.1554577} +{"type": "openhcs_custom_function_register", "time": 1790723233.2576563} +{"type": "openhcs_custom_function_register", "time": 1790723233.360189} +{"type": "openhcs_custom_function_register", "time": 1790723233.462424} +{"type": "openhcs_custom_function_register", "time": 1790723233.5646634} +{"type": "openhcs_custom_function_register", "time": 1790723233.6668832} +{"type": "openhcs_custom_function_register", "time": 1790723233.768853} +{"type": "openhcs_custom_function_register", "time": 1790723233.8706975} +{"type": "openhcs_custom_function_register", "time": 1790723233.9724643} +{"type": "openhcs_custom_function_register", "time": 1790723234.074736} +{"type": "openhcs_custom_function_register", "time": 1790723234.1768725} +{"type": "openhcs_custom_function_register", "time": 1790723234.2789953} +{"type": "openhcs_custom_function_register", "time": 1790723234.3810422} +{"type": "openhcs_custom_function_register", "time": 1790723234.4831567} +{"type": "openhcs_custom_function_register", "time": 1790723234.585275} +{"type": "openhcs_custom_function_register", "time": 1790723234.6872966} +{"type": "openhcs_custom_function_register", "time": 1790723234.7892656} +{"type": "openhcs_custom_function_register", "time": 1790723234.8912764} +{"type": "openhcs_custom_function_register", "time": 1790723234.9931672} +{"type": "openhcs_custom_function_register", "time": 1790723235.0950842} +{"type": "openhcs_custom_function_register", "time": 1790723235.1972477} +{"type": "openhcs_custom_function_register", "time": 1790723235.299546} +{"type": "openhcs_custom_function_register", "time": 1790723235.4017649} +{"type": "openhcs_custom_function_register", "time": 1790723235.5041285} +{"type": "openhcs_custom_function_register", "time": 1790723235.6065872} +{"type": "openhcs_custom_function_register", "time": 1790723235.7088366} +{"type": "openhcs_custom_function_register", "time": 1790723235.811391} +{"type": "openhcs_custom_function_register", "time": 1790723235.913663} +{"type": "openhcs_custom_function_register", "time": 1790723236.0158513} +{"type": "openhcs_custom_function_register", "time": 1790723236.1179729} +{"type": "openhcs_custom_function_register", "time": 1790723236.219965} +{"type": "openhcs_custom_function_register", "time": 1790723236.3220487} +{"type": "openhcs_custom_function_register", "time": 1790723236.4241557} +{"type": "openhcs_custom_function_register", "time": 1790723236.5263987} +{"type": "openhcs_custom_function_register", "time": 1790723236.6285448} +{"type": "openhcs_custom_function_register", "time": 1790723236.7309413} +{"type": "openhcs_custom_function_register", "time": 1790723236.8330412} +{"type": "openhcs_custom_function_register", "time": 1790723236.934902} +{"type": "openhcs_custom_function_register", "time": 1790723237.0371342} +{"type": "openhcs_custom_function_register", "time": 1790723237.1393614} +{"type": "openhcs_custom_function_register", "time": 1790723237.2413702} +{"type": "openhcs_custom_function_register", "time": 1790723237.3434076} +{"type": "openhcs_custom_function_register", "time": 1790723237.4452434} +{"type": "openhcs_custom_function_register", "time": 1790723237.5471497} +{"type": "openhcs_custom_function_register", "time": 1790723237.6492825} +{"type": "openhcs_custom_function_register", "time": 1790723237.7514925} +{"type": "openhcs_custom_function_register", "time": 1790723237.8538373} +{"type": "openhcs_custom_function_register", "time": 1790723237.9560845} +{"type": "openhcs_custom_function_register", "time": 1790723238.0585363} +{"type": "openhcs_custom_function_register", "time": 1790723238.160728} +{"type": "openhcs_custom_function_register", "time": 1790723238.2631292} +{"type": "openhcs_custom_function_register", "time": 1790723238.3654606} +{"type": "openhcs_custom_function_register", "time": 1790723238.4675934} +{"type": "openhcs_custom_function_register", "time": 1790723238.569538} +{"type": "openhcs_custom_function_register", "time": 1790723238.67167} +{"type": "openhcs_custom_function_register", "time": 1790723238.7739446} +{"type": "openhcs_custom_function_register", "time": 1790723238.8760655} +{"type": "openhcs_custom_function_register", "time": 1790723238.9782755} +{"type": "openhcs_custom_function_register", "time": 1790723239.0804794} +{"type": "openhcs_custom_function_register", "time": 1790723239.1827438} +{"type": "openhcs_custom_function_register", "time": 1790723239.28506} +{"type": "openhcs_custom_function_register", "time": 1790723239.387107} +{"type": "openhcs_custom_function_register", "time": 1790723239.4892416} +{"type": "openhcs_custom_function_register", "time": 1790723239.5914938} +{"type": "openhcs_custom_function_register", "time": 1790723239.6936722} +{"type": "openhcs_custom_function_register", "time": 1790723239.7959273} +{"type": "openhcs_custom_function_register", "time": 1790723239.898175} +{"type": "openhcs_custom_function_register", "time": 1790723240.0002937} +{"type": "openhcs_custom_function_register", "time": 1790723240.1026585} +{"type": "openhcs_custom_function_register", "time": 1790723240.2052553} +{"type": "openhcs_custom_function_register", "time": 1790723240.307623} +{"type": "openhcs_custom_function_register", "time": 1790723240.409817} +{"type": "openhcs_custom_function_register", "time": 1790723240.5119922} +{"type": "openhcs_custom_function_register", "time": 1790723240.6142662} +{"type": "openhcs_custom_function_register", "time": 1790723240.716416} +{"type": "openhcs_custom_function_register", "time": 1790723240.8185954} +{"type": "openhcs_custom_function_register", "time": 1790723240.9212189} +{"type": "openhcs_custom_function_register", "time": 1790723241.023556} +{"type": "openhcs_custom_function_register", "time": 1790723241.1259916} +{"type": "openhcs_custom_function_register", "time": 1790723241.2282233} +{"type": "openhcs_custom_function_register", "time": 1790723241.3303478} +{"type": "openhcs_custom_function_register", "time": 1790723241.4324486} +{"type": "openhcs_custom_function_register", "time": 1790723241.53434} +{"type": "openhcs_function_catalog_search", "time": 1790723275.7328885} +{"type": "force_shutdown", "time": 1790723303.2127094} diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt01/owned-stderr.txt b/tests/runtime_diagnostics/registration_live_20260929_attempt01/owned-stderr.txt new file mode 100644 index 0000000000..ed717c4a88 --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt01/owned-stderr.txt @@ -0,0 +1,25 @@ +2026-09-29 19:06:49,476 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:06:49,477 - openhcs.runtime.zmq_execution_server_launcher - INFO - ZMQ Execution Server +2026-09-29 19:06:49,477 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:06:49,477 - openhcs.runtime.zmq_execution_server_launcher - INFO - Log level: INFO (from --log-level=INFO) +2026-09-29 19:06:49,477 - openhcs.runtime.zmq_execution_server_launcher - INFO - Port: 15991 (control: 16991) +2026-09-29 19:06:49,477 - openhcs.runtime.zmq_execution_server_launcher - INFO - Host: 127.0.0.1 +2026-09-29 19:06:49,477 - openhcs.runtime.zmq_execution_server_launcher - INFO - Transport mode: tcp +2026-09-29 19:06:49,477 - openhcs.runtime.zmq_execution_server_launcher - INFO - Persistent: True +2026-09-29 19:06:49,477 - openhcs.runtime.zmq_execution_server_launcher - INFO - Log file: /home/ts/wt/openhcs-custom-function-admission-20260929/tests/runtime_diagnostics/registration_live_20260929_attempt01/owned.log +2026-09-29 19:06:49,477 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:06:50,183 - zmqruntime.server - INFO - ZMQ Server started on tcp://127.0.0.1:15991 (PUB), control tcp://127.0.0.1:16991 +2026-09-29 19:06:50,183 - zmqruntime.execution.server - INFO - Queue worker thread started - will process executions sequentially +2026-09-29 19:06:50,183 - zmqruntime.execution.server - INFO - Started execution queue worker thread +2026-09-29 19:06:50,183 - openhcs.runtime.zmq_execution_server_launcher - INFO - Server ready - waiting for requests... +2026-09-29 19:06:50,214 - zmqruntime.server - INFO - Server ready +2026-09-29 19:07:07,592 - openhcs.processing.backends.lib_registry.registry_service - INFO - Registry metadata cache cleared +2026-09-29 19:07:07,592 - openhcs.processing.custom_functions.manager - INFO - Loaded 0 persisted custom function(s) +2026-09-29 19:07:07,599 - metaclass_registry.discovery - INFO - Discovered 5 registry classes for LibraryRegistryBase: ['CupyRegistry', 'OpenHCSRegistry', 'PyclesperantoRegistry', 'SkimageRegistry', 'RuntimeTestingRegistryBase'] +2026-09-29 19:07:07,609 - openhcs.processing.backends.lib_registry.registry_service - INFO - CPU-only registry discovery skipping CupyRegistry +2026-09-29 19:07:07,609 - openhcs.processing.backends.lib_registry.registry_service - INFO - CPU-only registry discovery skipping PyclesperantoRegistry +2026-09-29 19:08:23,224 - zmqruntime.execution.server - INFO - No live worker processes found to kill +2026-09-29 19:08:23,224 - zmqruntime.execution.server - INFO - Workers killed (0), server shutting down +2026-09-29 19:08:23,235 - zmqruntime.server - INFO - ZMQ Server stopped +2026-09-29 19:08:23,235 - polystore.backend_registry - INFO - Backend connections cleaned up (including process resources) +2026-09-29 19:08:23,235 - openhcs.runtime.zmq_execution_server_launcher - INFO - Server stopped diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt01/unsupported-control.jsonl b/tests/runtime_diagnostics/registration_live_20260929_attempt01/unsupported-control.jsonl new file mode 100644 index 0000000000..0fae0d0866 --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt01/unsupported-control.jsonl @@ -0,0 +1,2 @@ +{"type": "openhcs_custom_function_registration_destination", "time": 1790723227.3854418} +{"type": "force_shutdown", "time": 1790723302.3072264} diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt01/unsupported-stderr.txt b/tests/runtime_diagnostics/registration_live_20260929_attempt01/unsupported-stderr.txt new file mode 100644 index 0000000000..909da434a9 --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt01/unsupported-stderr.txt @@ -0,0 +1,20 @@ +2026-09-29 19:06:58,195 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:06:58,195 - openhcs.runtime.zmq_execution_server_launcher - INFO - ZMQ Execution Server +2026-09-29 19:06:58,195 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:06:58,195 - openhcs.runtime.zmq_execution_server_launcher - INFO - Log level: INFO (from --log-level=INFO) +2026-09-29 19:06:58,195 - openhcs.runtime.zmq_execution_server_launcher - INFO - Port: 15993 (control: 16993) +2026-09-29 19:06:58,195 - openhcs.runtime.zmq_execution_server_launcher - INFO - Host: 127.0.0.1 +2026-09-29 19:06:58,196 - openhcs.runtime.zmq_execution_server_launcher - INFO - Transport mode: tcp +2026-09-29 19:06:58,196 - openhcs.runtime.zmq_execution_server_launcher - INFO - Persistent: True +2026-09-29 19:06:58,196 - openhcs.runtime.zmq_execution_server_launcher - INFO - Log file: /home/ts/wt/openhcs-custom-function-admission-20260929/tests/runtime_diagnostics/registration_live_20260929_attempt01/unsupported.log +2026-09-29 19:06:58,196 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:06:58,883 - zmqruntime.server - INFO - ZMQ Server started on tcp://127.0.0.1:15993 (PUB), control tcp://127.0.0.1:16993 +2026-09-29 19:06:58,883 - zmqruntime.execution.server - INFO - Queue worker thread started - will process executions sequentially +2026-09-29 19:06:58,883 - zmqruntime.execution.server - INFO - Started execution queue worker thread +2026-09-29 19:06:58,883 - openhcs.runtime.zmq_execution_server_launcher - INFO - Server ready - waiting for requests... +2026-09-29 19:06:59,056 - zmqruntime.server - INFO - Server ready +2026-09-29 19:08:22,324 - zmqruntime.execution.server - INFO - No live worker processes found to kill +2026-09-29 19:08:22,325 - zmqruntime.execution.server - INFO - Workers killed (0), server shutting down +2026-09-29 19:08:22,335 - zmqruntime.server - INFO - ZMQ Server stopped +2026-09-29 19:08:22,335 - polystore.backend_registry - INFO - Backend connections cleaned up (including process resources) +2026-09-29 19:08:22,336 - openhcs.runtime.zmq_execution_server_launcher - INFO - Server stopped diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt02/foreign-control.jsonl b/tests/runtime_diagnostics/registration_live_20260929_attempt02/foreign-control.jsonl new file mode 100644 index 0000000000..d888b678e9 --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt02/foreign-control.jsonl @@ -0,0 +1,3 @@ +{"event": "native_imports", "paths": {"openhcs": "/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/__init__.py", "polystore": "/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src/polystore/__init__.py"}} +{"type": "openhcs_custom_function_registration_destination", "time": 1790725944.628144} +{"type": "force_shutdown", "time": 1790726194.249227} diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt02/foreign-stderr.txt b/tests/runtime_diagnostics/registration_live_20260929_attempt02/foreign-stderr.txt new file mode 100644 index 0000000000..13b3d72b80 --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt02/foreign-stderr.txt @@ -0,0 +1,20 @@ +2026-09-29 19:52:14,549 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:52:14,549 - openhcs.runtime.zmq_execution_server_launcher - INFO - ZMQ Execution Server +2026-09-29 19:52:14,549 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:52:14,549 - openhcs.runtime.zmq_execution_server_launcher - INFO - Log level: INFO (from --log-level=INFO) +2026-09-29 19:52:14,549 - openhcs.runtime.zmq_execution_server_launcher - INFO - Port: 15995 (control: 16995) +2026-09-29 19:52:14,549 - openhcs.runtime.zmq_execution_server_launcher - INFO - Host: 127.0.0.1 +2026-09-29 19:52:14,549 - openhcs.runtime.zmq_execution_server_launcher - INFO - Transport mode: tcp +2026-09-29 19:52:14,549 - openhcs.runtime.zmq_execution_server_launcher - INFO - Persistent: True +2026-09-29 19:52:14,549 - openhcs.runtime.zmq_execution_server_launcher - INFO - Log file: /home/ts/wt/openhcs-custom-function-admission-20260929/tests/runtime_diagnostics/registration_live_20260929_attempt02/foreign.log +2026-09-29 19:52:14,549 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:52:15,017 - zmqruntime.server - INFO - ZMQ Server started on tcp://127.0.0.1:15995 (PUB), control tcp://127.0.0.1:16995 +2026-09-29 19:52:15,018 - zmqruntime.execution.server - INFO - Queue worker thread started - will process executions sequentially +2026-09-29 19:52:15,018 - zmqruntime.execution.server - INFO - Started execution queue worker thread +2026-09-29 19:52:15,018 - openhcs.runtime.zmq_execution_server_launcher - INFO - Server ready - waiting for requests... +2026-09-29 19:52:15,049 - zmqruntime.server - INFO - Server ready +2026-09-29 19:56:34,266 - zmqruntime.execution.server - INFO - No live worker processes found to kill +2026-09-29 19:56:34,266 - zmqruntime.execution.server - INFO - Workers killed (0), server shutting down +2026-09-29 19:56:34,277 - zmqruntime.server - INFO - ZMQ Server stopped +2026-09-29 19:56:34,277 - polystore.backend_registry - INFO - Backend connections cleaned up (including process resources) +2026-09-29 19:56:34,277 - openhcs.runtime.zmq_execution_server_launcher - INFO - Server stopped diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt02/journey.json b/tests/runtime_diagnostics/registration_live_20260929_attempt02/journey.json new file mode 100644 index 0000000000..f7368cc979 --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt02/journey.json @@ -0,0 +1,7730 @@ +{ + "accepted": false, + "source_sha": "4c6fbac0a0edb7805b36f66e9c5a56d7d2cd58f5", + "imports": { + "openhcs": "/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/__init__.py", + "polystore": "/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src/polystore/__init__.py" + }, + "submodules": [ + " b0004daf3223326ac78689ba7b2e68abc18ae298 external/ObjectState (v1.1.8)", + " 1209068f5fc9e4f7025a24ada8a73ef30dd1ddd7 external/PolyStore (v0.2.19-11-g1209068)", + " fddd9857bcd21468d20bc8547a769e0c6c9f1906 external/arraybridge (v0.3.4)", + " ca0a87e873f929b311a87a4d60cd3bfba315dbcf external/metaclass-registry (v0.2.1)", + " 792b03413331fdf107e369c924c257a2f0b16126 external/pycodify (v0.1.3)", + " a51c3d965f8ffdefe95a2b4b37347a85341d483a external/pyqt-reactive (v0.3.24)", + " 4e8f8bde5f750d9a50eacac80acd3806123addd8 external/python-introspect (v0.1.14-1-g4e8f8bd)", + " 1d3b32f4fcead23d5d2079c975eb2ae7646a1478 external/zmqruntime (v0.2.24)" + ], + "resources": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.4, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 12.0, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "driver_pid": 1876976, + "source_live_not_installed": true, + "installed_entrypoint": false, + "launch_cwd": "/home/ts/wt/openhcs-custom-function-admission-20260929", + "calls": [ + { + "tool": "openhcs_health_check", + "arguments": {}, + "started_utc": 1790725942.4334848, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_health_check", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "status": "ok", + "started_at_unix": 1790725941.5992095, + "service": "openhcs.mcp", + "openhcs_version": "0.8.6", + "packaged_resources_ready": true, + "packaged_resource_count": 56, + "missing_packaged_resource_paths": [], + "server_process_id": 1877427, + "server_source_path": "/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/mcp/server.py", + "server_import_mtime_ns": 1790721541146819947, + "server_current_mtime_ns": 1790721541146819947, + "server_source_changed_since_import": false, + "stale_source_paths": [], + "recovery_reason": "current", + "installation_pointer_path": null, + "installation_pointer_changed_since_import": false, + "installation_pointer_available": null, + "restart_required": false, + "restart_command": [], + "restart_command_is_stable": false, + "reconnect_required": false, + "reconnect_owner": null, + "retry_after_reconnect": false, + "automatic_recovery_on_reconnect": false, + "restart_hint": null + } + ] + } + ] + }, + "elapsed_seconds": 0.009950784005923197 + }, + { + "tool": "openhcs_get_authoring_context", + "arguments": { + "kind": "first_use" + }, + "started_utc": 1790725942.5051858, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_authoring_context", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "kind": "first_use", + "content": "=== CHOOSE AN OPENHCS WORKFLOW ===\nUse this context when you are starting or resuming work and need to choose the OpenHCS state owner or next workflow.\n\n=== CORE MODEL IN ONE PASS ===\n- OpenHCS is a typed compiler/runtime for high-content microscopy. It projects microscope data into a storage-independent virtual workspace, compiles PipelineConfig plus ordered FunctionStep declarations, resolves multidimensional grouping and artifact contracts, executes through typed runtime values, and validates results through plate and viewer evidence.\n- Choose the state owner before choosing file formats or tools. If the user asks to open, show, continue, or edit work in the desktop, the UI-owned route always takes precedence over folder inspection or a headless session.\n\n=== CHOOSE ONE TASK ROUTE ===\nRequest exactly one matching context with openhcs_get_authoring_context; deepen into another only when the workflow reaches that boundary. Reuse this router after a handoff or a change of task; first_use is not a one-time setup step.\n- kind=\"ui_visible_workflow\" \u2014 Work in the running desktop UI: the user asks to open, show, continue, or edit work in the visible OpenHCS desktop.\n- kind=\"domain_expert_assisted_setup\" \u2014 Set up a workflow from biological intent: a domain expert describes the experiment and expected result but wants the agent to handle OpenHCS mechanics.\n- kind=\"folder_onboarding\" \u2014 Inspect and onboard image data: the task starts from a local plate, image folder, or rich image container and does not require visible UI ownership.\n- kind=\"pipeline\" \u2014 Author or revise a typed pipeline: the sources are understood and the next task is choosing functions, axes, grouping, configuration, or artifact contracts.\n- kind=\"image_analysis_workflow\" \u2014 Design and validate an image-analysis workflow: an analysis needs multisite assembly, registration, normalisation, mosaic quality control, segmentation review, or image-result provenance.\n- kind=\"custom_function\" \u2014 Author a custom processing function: the required operation is not already in the function registry or needs a typed custom artifact contract.\n- kind=\"headless_execution\" \u2014 Compile and execute headlessly: a reviewed pipeline should run without becoming selected or editable in the desktop UI.\n- kind=\"debugging\" \u2014 Diagnose compiled and runtime behavior: a compile, execution, step output, runtime value, or artifact must be inspected at its owning boundary.\n- kind=\"viewer_review\" \u2014 Validate results in a managed viewer: execution produced images, labels, or ROIs that need bounded visual and structured validation.\n- kind=\"objectstate_editing\" \u2014 Inspect or edit typed UI state: one exact UI-backed field, inheritance value, snapshot, or branch must be inspected or changed.\n- kind=\"cellprofiler_translation\" \u2014 Translate or validate a CellProfiler workflow: the intent begins with a .cppipe, CellProfiler module vocabulary, or an exact parity claim.\n\n=== SAFE FIRST ACTION ===\n- Start read-only. Query the active surface through openhcs_search_capabilities with task-relevant workflow, target, or text filters; its registry-owned metadata is the authority for what this server exposes. Use openhcs_list_capabilities only when the complete selected surface is required.\n- Guides describe workflows across capability profiles. Confirm a named tool is exposed before calling it. Desktop actions are discovered separately through openhcs_ui_list_actions; an empty capability search does not establish that a running window lacks the action.\n- Before operating a live desktop workflow, call openhcs_ui_list_windows and resolve visible modal top-level windows first. Inspect each dialog through openhcs_ui_get_widget_tree and invoke its declared button through openhcs_ui_invoke_widget_action; acknowledge completed operations, cancel unsolicited update/restart prompts, and never use desktop-coordinate automation or leave a blocking dialog hidden behind the analysis viewer.\n- Request the matching task context before mutation. Do not read every knowledge document, enumerate every function, invent config fields, or load full-resolution image data up front.\n- When continuing an analysis, read and reuse its existing pipeline and configuration. Adapt only the required source/layout choices, preserve earlier outputs in a separate destination, and record unavailable acquisitions as missing rather than zero measurements. Validate a representative bounded run before expanding.\n- If returned guidance is truncated, retrieve the same context with a sufficient max_chars before acting on an incomplete workflow.\n- Before any write or execution, show the exact target and intended change, refresh revision/request tokens, obtain approval, validate, and compile before running.\n\n=== DEEPEN ONLY WHEN NEEDED ===\nRetrieve one relevant source-backed target with openhcs_get_knowledge_document; use its returned section ids to narrow further instead of loading every document.\n- openhcs_architecture_quick_start \u2014 OpenHCS architecture quick start: Front-door OpenHCS architecture and MCP route covering the public PipelineConfig plus FunctionStep model, ObjectState resolution, function patterns, axes and grouping, typed artifacts, CellProfiler translation, generic source bindings, compilation, runtime values, examples, and safe operation." + } + ] + } + ] + }, + "elapsed_seconds": 1.3069605819764547 + }, + { + "tool": "openhcs_get_authoring_context", + "arguments": { + "kind": "custom_function" + }, + "started_utc": 1790725943.8703487, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_authoring_context", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "kind": "custom_function", + "content": "=== AUTHOR A CUSTOM PROCESSING FUNCTION ===\nUse this context when the required operation is not already in the function registry or needs a typed custom artifact contract.\n\n=== CUSTOM FUNCTIONS AND RUNTIME OUTPUTS ===\n- Custom functions are registry functions used inside FunctionStep.func patterns: callable, (callable, kwargs), list chains, or component/group keyed patterns.\n- A reviewed custom function becomes an ordinary registry-described callable; it does not create a separate pipeline mechanism. If openhcs_search_functions has no contract-compatible implementation for an intended operation, use this route, register the callable, then describe its reflected contract before composing it.\n- CLAHE, median denoising, background subtraction, and flat-field correction are example preprocessing intents, not a promised built-in catalog. Implement only the missing operation, keep it backend-agnostic at the pipeline boundary, and let its memory decorator, ProcessingContract, and artifact declarations state compatibility.\n- Dict patterns are routed by processing_config.group_by; list and single-callable patterns apply the same callable chain to each grouped 3D array.\n- processing_config.variable_components are the axes stacked into each callable input array; group_by groups the remaining 3D arrays and only selects callable branches for dict patterns.\n- Memory decorators such as @numpy define runtime memory conversion; FunctionStep processing_config defines grouping, axes, and input source.\n- Declare outputs with typed ArtifactSpec values. The decorator owns their input/output role. MeasurementsArtifactType receives schema-bearing ColumnarRows; ObjectLabelsArtifactType receives complete integer label arrays and may use segmentation_mask_rois for materialization.\n- Path-bearing topology belongs in one SpatialGraphArtifactType output whose SpatialGraph nodes, edges, and scalar features are authoritative. A directed morphology forest can materialize through SWCOptions while SpatialGraphROIOptions projects those same paths and features to Napari; do not reduce graph semantics to a mask or rebuild them in viewer code.\n- Artifact graph planning owns output names, sidecar results, materialization intent, and downstream artifact inputs; do not encode those as ad hoc files or hidden globals. Request the viewer-review context only after execution produces artifacts to inspect.\n\n=== CUSTOM FUNCTION RULES ===\n- Decorate exactly one callable with a memory decorator such as @numpy.\n- The first image-like argument is supplied by OpenHCS at runtime; do not pass it as a FunctionStep kwarg.\n- Prefer concrete typed parameters with serializable defaults.\n- Preserve dtype unless intentionally changing representation.\n- Declare measurements as MeasurementsArtifactType and return ColumnarRows; declare segmentation as ObjectLabelsArtifactType and return the complete integer label array.\n- Declare path topology as SpatialGraphArtifactType and return one SpatialGraph; use SWCOptions and SpatialGraphROIOptions as projections of that same value rather than independent graph and ROI computations.\n- Do not close over GUI, filesystem, or live viewer state.\n\n=== MINIMAL NUMPY CUSTOM FUNCTION ===\n```python\nfrom openhcs.core.memory import numpy\nimport numpy as np\n\n@numpy\ndef robust_clip(image, low_percentile: float = 1.0, high_percentile: float = 99.0):\n \"\"\"Clip intensities to robust percentiles while preserving dtype.\"\"\"\n array = np.asarray(image)\n if array.size == 0:\n return image\n low = np.percentile(array, low_percentile)\n high = np.percentile(array, high_percentile)\n if high <= low:\n return image\n return np.clip(array, low, high).astype(array.dtype, copy=False)\n```\n\n=== REGISTRATION WORKFLOW ===\n- Read the targeted custom-function and artifact-contract knowledge documents below before inventing decorators or payload types.\n- Register reviewed source through openhcs_register_custom_function; that capability delegates to the nominal CustomFunctionManager and owns persistence and registry refresh.\n- Search with openhcs_search_functions, describe the returned function_id with openhcs_describe_function, then request the pipeline context to add the function and validate its compiled artifact plan.\n\n=== DEEPEN ONLY WHEN NEEDED ===\nRetrieve one relevant source-backed target with openhcs_get_knowledge_document; use its returned section ids to narrow further instead of loading every document.\n- openhcs_custom_functions \u2014 OpenHCS custom functions: How to add custom processing functions, memory decorators, validation, GPU custom functions, registry integration, and GUI availability.\n- openhcs_custom_function_management \u2014 OpenHCS custom function management: CustomFunctionManager lifecycle, atomic updates, module injection, reload signals, and persisted custom-function registry behavior.\n- openhcs_artifact_contract_system \u2014 OpenHCS artifact contract system: Canonical callable/module artifact contracts, graph planning, exact satisfaction, runtime observations, and materialization.\n- openhcs_custom_function_workflow \u2014 Add a missing analysis operation as a typed custom function: Add a missing operation through typed custom-function owners: explicit endpoint/name/store admission, responsive native catalogue preparation with an incarnation-bound start/status/cancel handle, single source dispatch only after readiness, postdispatch uncertainty without replay, stable imports, compilation and bounded outputs; separate technical reuse from biological acceptance." + } + ] + } + ] + }, + "elapsed_seconds": 0.13100940300500952 + }, + { + "tool": "openhcs_search_capabilities", + "arguments": { + "query": "custom function", + "limit": 5 + }, + "started_utc": 1790725944.0636058, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_search_capabilities", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "surface_profile": "full", + "query": { + "text": null, + "kind": null, + "workflow_group": null, + "workflow_stage": null, + "target_context": null, + "visibility": null, + "role": null, + "has_side_effects": null, + "side_effect_contains": null, + "offset": 0, + "limit": 5 + }, + "matched_count": 101, + "returned_count": 5, + "next_offset": 5, + "capabilities": [ + { + "name": "openhcs://capabilities", + "kind": "resource", + "title": "OpenHCS agent capability registry", + "description": "Lists the resources, tools, side effects, and extras exposed by this server.", + "workflow_group": "discovery", + "workflow_stage": "discovery", + "target_context": "server", + "visibility": "beginner", + "role": "primary", + "read_only": true, + "side_effects": [], + "requires_network": false, + "required_extras": [], + "runtime_requirements": [], + "data_exposure": [], + "security_requirements": [], + "input_type": null, + "output_type": "AgentCapabilityRegistry" + }, + { + "name": "openhcs_sweep_viewer_endpoints", + "kind": "tool", + "title": "Sweep viewer endpoints", + "description": "Sweeps the local OpenHCS IPC directory for live viewer endpoints, probes each through the viewer-window state authority, and classifies every live viewer as owned or foreign relative to this process. Dead sockets are filtered by a bind probe; non-viewer endpoint pairs (execution, UI bridge) are excluded.", + "workflow_group": "discovery", + "workflow_stage": "diagnostic", + "target_context": "server", + "visibility": "beginner", + "role": "diagnostic", + "read_only": true, + "side_effects": [], + "requires_network": false, + "required_extras": [], + "runtime_requirements": [], + "data_exposure": [ + "viewer_endpoint_inventory" + ], + "security_requirements": [], + "input_type": null, + "output_type": "ViewerEndpointDiscoveryResult" + }, + { + "name": "openhcs_health_check", + "kind": "tool", + "title": "Health check", + "description": "Reports OpenHCS MCP health, installed OpenHCS version, packaged-resource readiness, server process identity, source freshness, installation-generation freshness, and the client-owned reconnect contract for stale-process diagnostics.", + "workflow_group": "discovery", + "workflow_stage": "diagnostic", + "target_context": "server", + "visibility": "beginner", + "role": "diagnostic", + "read_only": true, + "side_effects": [], + "requires_network": false, + "required_extras": [], + "runtime_requirements": [], + "data_exposure": [ + "installed_openhcs_version", + "packaged_resource_readiness", + "packaged_resource_paths", + "mcp_process_identity", + "mcp_source_freshness", + "mcp_installation_generation" + ], + "security_requirements": [], + "input_type": null, + "output_type": "McpServerHealthResult" + }, + { + "name": "openhcs_list_capabilities", + "kind": "tool", + "title": "List capabilities", + "description": "Returns the canonical agent capability registry.", + "workflow_group": "discovery", + "workflow_stage": "discovery", + "target_context": "server", + "visibility": "beginner", + "role": "primary", + "read_only": true, + "side_effects": [], + "requires_network": false, + "required_extras": [], + "runtime_requirements": [], + "data_exposure": [], + "security_requirements": [], + "input_type": null, + "output_type": "AgentCapabilityRegistry" + }, + { + "name": "openhcs_search_capabilities", + "kind": "tool", + "title": "Search capabilities", + "description": "Returns a bounded task-specific projection of the selected canonical capability registry. Filter by registry-owned workflow group, stage, target context, visibility, role, side effects, or free text before choosing tools.", + "workflow_group": "discovery", + "workflow_stage": "discovery", + "target_context": "server", + "visibility": "beginner", + "role": "primary", + "read_only": true, + "side_effects": [], + "requires_network": false, + "required_extras": [], + "runtime_requirements": [], + "data_exposure": [], + "security_requirements": [], + "input_type": "AgentCapabilitySearchRequest", + "output_type": "AgentCapabilitySearchResult" + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.009299249009927735 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n" + }, + "started_utc": 1790725944.1412935, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "mcp_tool_failed", + "message": "Custom function registration requires an explicit port.", + "hint": "The MCP server caught this exception at the tool boundary.", + "exception_type": "ValueError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.009471785015193745 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/foreign", + "port": 15994, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790725944.218453, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "agent_path_policy_rejected", + "message": "Writable path is outside allowed roots: /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/foreign. Effective writable roots: /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned.", + "hint": "Pass a local path under OPENHCS_AGENT_READ_ROOTS or OPENHCS_AGENT_WRITE_ROOTS as appropriate. Use openhcs_inspect_plate_path first to validate a plate folder.", + "exception_type": "AgentPathPolicyError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.007558557990705594 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/ancestor-escape", + "port": 15994, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790725944.2866933, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "agent_path_policy_rejected", + "message": "Writable path is outside allowed roots: /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/foreign. Effective writable roots: /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned.", + "hint": "Pass a local path under OPENHCS_AGENT_READ_ROOTS or OPENHCS_AGENT_WRITE_ROOTS as appropriate. Use openhcs_inspect_plate_path first to validate a plate folder.", + "exception_type": "AgentPathPolicyError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.007576522999443114 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/data/openhcs/custom_functions", + "port": 15994, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790725944.3598785, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "agent_path_policy_rejected", + "message": "Writable path is outside allowed roots: /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/foreign/registration_live_probe.py. Effective writable roots: /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned.", + "hint": "Pass a local path under OPENHCS_AGENT_READ_ROOTS or OPENHCS_AGENT_WRITE_ROOTS as appropriate. Use openhcs_inspect_plate_path first to validate a plate folder.", + "exception_type": "AgentPathPolicyError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.013567510002758354 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/data/openhcs/custom_functions", + "port": 15995, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790725944.4369924, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "mcp_tool_failed", + "message": "Selected endpoint owns custom storage /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/foreign/data/openhcs/custom_functions, source /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/foreign/data/openhcs/custom_functions/registration_live_probe.py, not requested /home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/data/openhcs/custom_functions/registration_live_probe.py; no source was dispatched.", + "hint": "The MCP server caught this exception at the tool boundary.", + "exception_type": "ValueError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.1938281979819294 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/data/openhcs/custom_functions", + "port": 15996, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790725944.695305, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "mcp_tool_failed", + "message": "{'status': 'error', 'type': 'error', 'message': 'Unsupported destination proof fixture'}", + "hint": "The MCP server caught this exception at the tool boundary.", + "exception_type": "RuntimeError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.15166049200342968 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/data/openhcs/custom_functions", + "port": 15994, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790725944.9131825, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "function_catalog_not_ready", + "message": "Catalog preparation FunctionCatalogPreparationHandle(connection=ExecutionConnectionSpec(host='127.0.0.1', port=15994, transport_mode=, persistent=True), server_identity=ProcessIdentity(pid=1877140, create_time=1790725928.37)) is not_started: Function catalog has not been requested. No source was dispatched.", + "hint": "Start/observe the same typed catalog preparation handle; no source was dispatched.", + "exception_type": "FunctionCatalogNotReadyError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.15642607500194572 + }, + { + "tool": "openhcs_start_function_catalog_preparation", + "arguments": { + "port": 15994, + "host": "127.0.0.1", + "transport_mode": "tcp" + }, + "started_utc": 1790725945.1442478, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_start_function_catalog_preparation", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.019741033000173047 + }, + { + "tool": "openhcs_cancel_function_catalog_preparation", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 0 + } + }, + "started_utc": 1790725945.2257223, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_cancel_function_catalog_preparation", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_cancel_function_catalog_preparation", + "errors": [ + { + "code": "mcp_tool_failed", + "message": "Function catalog preparation owner changed; handle is stale.", + "hint": "The MCP server caught this exception at the tool boundary.", + "exception_type": "RuntimeError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.019519852998200804 + }, + { + "tool": "openhcs_register_custom_function", + "arguments": { + "source_code": "\"\"\"Synthetic identity-plus-three contract; no file IO or biological semantics.\"\"\"\n\nfrom openhcs.core.memory import numpy\n\n\n@numpy\ndef registration_live_probe(image, offset=3):\n return image + offset\n", + "function_name": "registration_live_probe", + "storage_dir": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/data/openhcs/custom_functions", + "port": 15994, + "host": "127.0.0.1", + "transport_mode": "tcp", + "persist": true + }, + "started_utc": 1790725945.31083, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_register_custom_function", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "ok": false, + "tool": "openhcs_register_custom_function", + "errors": [ + { + "code": "function_catalog_not_ready", + "message": "Catalog preparation FunctionCatalogPreparationHandle(connection=ExecutionConnectionSpec(host='127.0.0.1', port=15994, transport_mode=, persistent=True), server_identity=ProcessIdentity(pid=1877140, create_time=1790725928.37)) is pending: Preparing function catalog and declared kernel caches. No source was dispatched.", + "hint": "Start/observe the same typed catalog preparation handle; no source was dispatched.", + "exception_type": "FunctionCatalogNotReadyError", + "path": null + } + ] + } + ] + } + ] + }, + "elapsed_seconds": 0.036365978012327105 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725946.4218647, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.013632312999106944 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725947.5078082, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.014705075998790562 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725948.5923054, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.018149595998693258 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725949.6819053, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.02612920297542587 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725950.782732, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.011742689996026456 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725951.8760407, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.016320899012498558 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725952.9753315, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.013631792011437938 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725954.0740933, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.013278522004839033 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725955.1610482, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01229845397756435 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725956.2481637, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.02160134000587277 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725957.3515923, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.014992260985309258 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725958.4485323, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.025714496994623914 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725959.5482492, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.02350493799895048 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725960.6398494, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01994419700349681 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725961.731657, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.012471902009565383 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725962.8219976, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.014474980009254068 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725963.9309413, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.014432830008445308 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725965.527001, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01461368301534094 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725966.6342132, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01812486900598742 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725967.759359, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.016731146984966472 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725968.8573766, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.017604171996936202 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725969.9596906, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.013895932992454618 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725971.0544791, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.019906897010514513 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725972.1539621, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.020844534010393545 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725973.2829902, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.02364578499691561 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725974.4001815, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.016399077023379505 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725975.4978924, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.021906988986302167 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725976.617755, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01982949100784026 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725977.7520185, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.018733482982497662 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725978.9030495, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.016134165023686364 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725980.251429, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.012884805008070543 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725981.3585713, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01914357001078315 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725982.4726999, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.020488088979618624 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725983.5919654, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.016515819006599486 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725984.7082696, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.02012346699484624 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725985.8380418, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.022962618997553363 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725986.9601324, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.018416490987874568 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725988.0930614, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.020093189988983795 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725989.2104504, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.023230597988003865 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725990.3249526, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.022541210986673832 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725991.459577, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.02419164901948534 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725992.5870695, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.014520466007525101 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725993.6957674, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.018396223982563242 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725994.805088, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.009939481999026611 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725995.9076688, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01843480599927716 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725997.0347424, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.012077925988705829 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725998.1595125, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.023367167013930157 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790725999.3054795, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.019783081981586292 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726000.4384606, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.019152335997205228 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726001.5523055, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.02190543498727493 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726002.6846435, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.011347020015818998 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726003.8103917, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.022081428993260488 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726004.954244, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.019776539993472397 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726006.0938456, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.013376126007642597 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726007.2122376, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.022256030002608895 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726008.350096, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.02125334899756126 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726009.4817917, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.019601006992161274 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726010.6134744, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01778990399907343 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726011.7605777, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01484952899045311 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726012.8984656, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.010255770990625024 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726014.017237, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.02159863300039433 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726015.1550694, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01752980099990964 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726016.28423, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01888160197995603 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726017.4166434, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.019336535973707214 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726018.5525494, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.017142947006504983 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726019.682027, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.016369971999665722 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726020.8137176, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.019583793997298926 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726021.9527614, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.016552668006625026 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726023.0951836, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01608225700329058 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726024.2384403, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.024532393988920376 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726025.395595, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.023884405993157998 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726026.5397196, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01092800498008728 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726027.6632855, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.022553502989467233 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726028.8179693, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.017575806996319443 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726029.9653265, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.02034565800568089 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726031.1198797, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.02407104999292642 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726032.268305, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.024656059016706422 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726033.419815, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.023066456022206694 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726034.5670688, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01293841699953191 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726035.682749, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01602269400609657 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726036.8269506, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01895371900172904 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726037.9690642, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.020973087986931205 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726039.1216767, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.018121911998605356 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726040.2697654, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.013876956014428288 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726041.4053273, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.017223991017090157 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726042.539506, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.01285576997906901 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726043.6755073, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.019571811018977314 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726044.844064, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.02216780200251378 + }, + { + "tool": "openhcs_get_function_catalog_preparation_status", + "arguments": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "started_utc": 1790726045.9986656, + "status": "returned", + "response": { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "pending", + "progress": { + "phase": "preparing_capabilities", + "message": "Preparing function catalog and declared kernel caches", + "sequence": 2, + "timestamp": 1790725945.1608434 + } + } + ] + } + ] + }, + "elapsed_seconds": 0.015043006016639993 + } + ], + "processes": [ + { + "role": "owned", + "port": 15994, + "identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + { + "role": "foreign", + "port": 15995, + "identity": { + "pid": 1877161, + "create_time": 1790725931.41 + } + }, + { + "role": "unsupported", + "port": 15996, + "identity": { + "pid": 1877225, + "create_time": 1790725934.23 + } + } + ], + "original033": "preserved; not replayed", + "shared7777": "never contacted", + "uncertain_dispatch": null, + "scratch": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02", + "resource_observations": [ + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.4, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3188 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.4, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.3, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3188 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3188 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3188 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3188 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3215 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3215 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3328 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3215 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3215 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3215 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3215 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3215 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 3215 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.1, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 5326 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.3, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 5326 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.5, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 5326 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.6, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 5326 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.6, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 5326 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.5, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 934290 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.5, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 934290 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.5, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 934290 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.5, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 934290 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.4, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 934290 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.4, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.4, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.4, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.4, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.3, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.1, + "reasons": [ + "swap used 11.0 GiB" + ], + "swap_used_gib": 11.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.8, + "reasons": [ + "swap used 11.3 GiB" + ], + "swap_used_gib": 11.3 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.8, + "reasons": [ + "swap used 11.5 GiB" + ], + "swap_used_gib": 11.5 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.8, + "reasons": [ + "swap used 11.6 GiB" + ], + "swap_used_gib": 11.6 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.1, + "reasons": [ + "swap used 11.7 GiB" + ], + "swap_used_gib": 11.7 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.8 GiB" + ], + "swap_used_gib": 11.8 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.1, + "reasons": [ + "swap used 11.8 GiB" + ], + "swap_used_gib": 11.8 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 11.9 GiB" + ], + "swap_used_gib": 11.9 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 12.0 GiB" + ], + "swap_used_gib": 12.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.4, + "reasons": [ + "swap used 12.0 GiB" + ], + "swap_used_gib": 12.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.4, + "reasons": [ + "swap used 12.0 GiB" + ], + "swap_used_gib": 12.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.4, + "reasons": [ + "swap used 12.0 GiB" + ], + "swap_used_gib": 12.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.3, + "reasons": [ + "swap used 12.0 GiB" + ], + "swap_used_gib": 12.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.3, + "reasons": [ + "swap used 12.0 GiB" + ], + "swap_used_gib": 12.0 + }, + "scratch_bytes": 1037446 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.5, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 1075999 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 1298672 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.8, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 1677327 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.7, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 2079697 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.7, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 2496580 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.7, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 2683546 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 2971868 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.8, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 3318251 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.7, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 3617614 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.8, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 4274736 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.8, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 4670711 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.5, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 4834239 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 5055135 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.5, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 5530219 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 5866520 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 6077365 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 6077365 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 6499279 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.1, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 6499279 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.0, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 6712778 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.0, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 6712778 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.9, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 6948809 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.9, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 7076655 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.8, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 7249756 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.8, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 7429205 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 7602501 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.5, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 8025843 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.4, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 8458502 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.4, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 8489564 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.4, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 8966157 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 9496371 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 9913580 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 10073413 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.4, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 10251090 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.5, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 10251090 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.5, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 10542978 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.5, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 10891663 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.5, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 11070358 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 11147204 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.7, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 11467676 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.1, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 11701656 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 10.7, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 11779886 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.0, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 12306598 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.1, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 12474107 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.1, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 12562450 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 12759791 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.2, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 12938953 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.3, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 13072739 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.3, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 13218103 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.4, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 13543343 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.4, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 13737363 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.5, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 13958014 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.5, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 14153803 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.5, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 14329180 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 14490436 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 14623616 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 14938986 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 15173905 + }, + { + "guard": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.3, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 11.6, + "reasons": [ + "swap used 12.2 GiB" + ], + "swap_used_gib": 12.2 + }, + "scratch_bytes": 15405256 + } + ], + "mcp_pid": 1877427, + "mcp_launch_environment": { + "HOME": "/home/ts", + "LOGNAME": "ts", + "PATH": "/home/ts/.codex/packages/standalone/releases/0.159.1-x86_64-unknown-linux-musl/codex-path:/home/ts/.codex/tmp/arg0/codex-arg0ZH6jHa:/home/ts/.codex/tmp/arg0/codex-arg004m4pr:/home/ts/.codex/packages/standalone/releases/0.159.0-x86_64-unknown-linux-musl/codex-path:/home/ts/.codex/tmp/arg0/codex-arg0ChXg03:/home/ts/.codex/packages/standalone/releases/0.158.0-x86_64-unknown-linux-musl/codex-path:/home/ts/.codex/tmp/arg0/codex-arg0a3jKy1:/home/ts/.codex/packages/standalone/releases/0.157.1-x86_64-unknown-linux-musl/codex-path:/home/ts/.codex/tmp/arg0/codex-arg0fLf3ul:/usr/lib/openai-codex/codex-path:/home/ts/.pyenv/bin:/home/ts/bin:/usr/local/bin:/home/ts/.elan/bin:/home/ts/programs/Brayns/bin:/home/ts/.pyenv/bin:/home/ts/bin:/usr/local/bin:/home/ts/.elan/bin:/home/ts/programs/Brayns/bin:/home/ts/.elan/bin:/home/ts/programs/Brayns/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/opt/android-sdk/emulator:/opt/android-ndk:/opt/android-sdk/build-tools/30.0.3:/opt/cuda/bin:/home/ts/.local/share/flatpak/exports/bin:/var/lib/flatpak/exports/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/home/ts/.local/bin:/root/.local/bin:/home/ts/bin:/usr/local/bin:/home/ts/code/scripts/bar:/home/ts/.local/bin:/root/.local/bin:/home/ts/bin:/usr/local/bin:/home/ts/code/scripts/bar:/home/ts/.local/bin:/root/.local/bin:/home/ts/bin:/usr/local/bin:/home/ts/.dotfiles/revsl.sh:/home/ts/.local/bin:/root/.local/bin:/home/ts/bin:/usr/local/bin:/home/ts/code/scripts/bar:/home/ts/.local/bin:/root/.local/bin:/home/ts/bin:/usr/local/bin:/home/ts/.dotfiles/revsl.sh:/opt/android-sdk/emulator:/opt/android-ndk", + "SHELL": "/bin/zsh", + "TERM": "dumb", + "USER": "ts", + "QT_QPA_PLATFORM": "offscreen", + "DISPLAY": ":0", + "XAUTHORITY": "/home/ts/.Xauthority", + "XDG_RUNTIME_DIR": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/runtime", + "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus", + "XDG_SESSION_TYPE": "x11", + "XDG_DATA_HOME": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/data", + "XDG_CACHE_HOME": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/cache", + "XDG_CONFIG_HOME": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/config", + "PYTHONPATH": "/home/ts/wt/openhcs-custom-function-admission-20260929:/home/ts/wt/openhcs-custom-function-admission-20260929/external/ObjectState/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/arraybridge/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/metaclass-registry/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/pycodify/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/pyqt-reactive/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/python-introspect/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/zmqruntime/src", + "OPENHCS_AGENT_READ_ROOTS": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02:/home/ts/wt/openhcs-custom-function-admission-20260929/packaging", + "OPENHCS_AGENT_WRITE_ROOTS": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned", + "OPENHCS_UI_CONFIG_CACHE_FILE": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/ui_config.config", + "OPENHCS_CPU_ONLY": "true", + "OPENHCS_HEADLESS": "true", + "NUMBA_CACHE_DIR": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/owned/numba", + "OPENHCS_USE_THREADING": "true", + "POLYSTORE_IMAGEJ_CACHE_ROOT": "/home/ts/.cache/polystore/imagej", + "POLYSTORE_IMAGEJ_ALLOW_DOWNLOAD": "false", + "OMP_NUM_THREADS": "1", + "OPENBLAS_NUM_THREADS": "1", + "MKL_NUM_THREADS": "1", + "NUMEXPR_NUM_THREADS": "1", + "BLIS_NUM_THREADS": "1", + "NUMBA_NUM_THREADS": "1" + }, + "protected_sentinel": { + "path": "/home/ts/.cache/agent-scratch/openhcs-registration-live-230-20260929-attempt02/foreign/registration_live_probe.py", + "sha256_before_after": "748cc865eb250527979464908234f357d1ddf6eceecc50057eed58df7c911b79", + "unchanged": true + }, + "preparation_handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "stale_preparation_cancel_rejected": true, + "failure": { + "type": "RuntimeError", + "message": "Preparation observation budget exhausted; preserve exact handle.", + "traceback": "Traceback (most recent call last):\n File \"/home/ts/wt/openhcs-custom-function-admission-20260929/tests/diagnostics/check_custom_registration_live.py\", line 565, in run\n raise RuntimeError(\nRuntimeError: Preparation observation budget exhausted; preserve exact handle.\n" + }, + "same_handle_observations": [ + { + "server": { + "command": "/home/ts/code/projects/openhcs/.venv/bin/python", + "module": "openhcs.mcp" + }, + "errors": [], + "results": [ + { + "tool": "openhcs_get_function_catalog_preparation_status", + "mcp_error": false, + "payloads": [ + { + "schema_version": "openhcs.agent.v1", + "errors": [], + "warnings": [], + "handle": { + "connection": { + "host": "127.0.0.1", + "port": 15994, + "transport_mode": "tcp", + "persistent": true + }, + "server_identity": { + "pid": 1877140, + "create_time": 1790725928.37 + } + }, + "outcome": "ready", + "progress": { + "phase": "connected", + "message": "Function catalog ready", + "sequence": 6, + "timestamp": 1790726055.660598 + } + } + ] + } + ] + } + ], + "operator_disposition": "Close only diagnostic owned processes; do not replay any recorded call.", + "client_closed": true, + "closed_servers": [ + { + "role": "unsupported", + "shutdown": { + "succeeded": true, + "endpoint_terminated": true + }, + "returncode": 0 + }, + { + "role": "foreign", + "shutdown": { + "succeeded": true, + "endpoint_terminated": true + }, + "returncode": 0 + }, + { + "role": "owned", + "shutdown": { + "succeeded": true, + "endpoint_terminated": true + }, + "returncode": 0 + } + ], + "runtime_terminal": true +} diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt02/mcp-stderr.txt b/tests/runtime_diagnostics/registration_live_20260929_attempt02/mcp-stderr.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt02/owned-control.jsonl b/tests/runtime_diagnostics/registration_live_20260929_attempt02/owned-control.jsonl new file mode 100644 index 0000000000..2c1d4b21a2 --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt02/owned-control.jsonl @@ -0,0 +1,98 @@ +{"event": "native_imports", "paths": {"openhcs": "/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/__init__.py", "polystore": "/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src/polystore/__init__.py"}} +{"type": "openhcs_custom_function_registration_destination", "time": 1790725945.0565147} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725945.0676496} +{"type": "openhcs_function_catalog_prepare", "time": 1790725945.1603625} +{"type": "openhcs_function_catalog_preparation_cancel", "time": 1790725945.2431324} +{"type": "openhcs_custom_function_registration_destination", "time": 1790725945.3353221} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725945.3461604} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725946.4348035} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725947.521267} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725948.6090548} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725949.706485} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725950.7945244} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725951.8919444} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725952.9892557} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725954.0865998} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725955.1729884} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725956.270118} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725957.3673756} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725958.4747596} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725959.5713673} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725960.6594055} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725961.7466016} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725962.8357959} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725963.9459987} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725965.5427525} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725966.653514} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725967.775487} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725968.875638} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725969.9751809} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725971.0750012} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725972.1750734} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725973.3147376} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725974.4169014} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725975.5193086} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725976.6422105} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725977.7761443} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725978.922314} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725980.2665021} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725981.379145} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725982.5003104} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725983.6117218} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725984.7330527} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725985.864213} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725986.984451} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725988.1166081} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725989.2377057} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725990.348807} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725991.4920843} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725992.6048312} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725993.7170646} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725994.8177562} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725995.929161} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725997.0495994} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725998.190302} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790725999.32981} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726000.4608407} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726001.5815823} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726002.7002165} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726003.839725} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726004.9803023} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726006.1111438} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726007.2426813} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726008.3763037} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726009.5073333} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726010.638207} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726011.7823439} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726012.9133728} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726014.0468552} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726015.177818} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726016.309998} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726017.4405713} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726018.5812442} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726019.712044} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726020.8423796} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726021.9820192} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726023.1226983} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726024.2738585} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726025.424494} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726026.5567365} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726027.6972005} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726028.8468223} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726029.996502} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726031.157095} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726032.2971299} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726033.4575956} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726034.5869844} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726035.7059975} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726036.856017} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726037.994711} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726039.1541295} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726040.2925549} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726041.4298224} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726042.559062} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726043.7085617} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726044.8794377} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726046.0203078} +{"type": "openhcs_function_catalog_preparation_status", "time": 1790726096.6193945} +{"type": "force_shutdown", "time": 1790726194.7410457} diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt02/owned-stderr.txt b/tests/runtime_diagnostics/registration_live_20260929_attempt02/owned-stderr.txt new file mode 100644 index 0000000000..00acd3ec1a --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt02/owned-stderr.txt @@ -0,0 +1,29 @@ +2026-09-29 19:52:11,560 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:52:11,560 - openhcs.runtime.zmq_execution_server_launcher - INFO - ZMQ Execution Server +2026-09-29 19:52:11,560 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:52:11,560 - openhcs.runtime.zmq_execution_server_launcher - INFO - Log level: INFO (from --log-level=INFO) +2026-09-29 19:52:11,560 - openhcs.runtime.zmq_execution_server_launcher - INFO - Port: 15994 (control: 16994) +2026-09-29 19:52:11,560 - openhcs.runtime.zmq_execution_server_launcher - INFO - Host: 127.0.0.1 +2026-09-29 19:52:11,560 - openhcs.runtime.zmq_execution_server_launcher - INFO - Transport mode: tcp +2026-09-29 19:52:11,560 - openhcs.runtime.zmq_execution_server_launcher - INFO - Persistent: True +2026-09-29 19:52:11,560 - openhcs.runtime.zmq_execution_server_launcher - INFO - Log file: /home/ts/wt/openhcs-custom-function-admission-20260929/tests/runtime_diagnostics/registration_live_20260929_attempt02/owned.log +2026-09-29 19:52:11,560 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:52:12,049 - zmqruntime.server - INFO - ZMQ Server started on tcp://127.0.0.1:15994 (PUB), control tcp://127.0.0.1:16994 +2026-09-29 19:52:12,049 - zmqruntime.execution.server - INFO - Queue worker thread started - will process executions sequentially +2026-09-29 19:52:12,049 - zmqruntime.execution.server - INFO - Started execution queue worker thread +2026-09-29 19:52:12,049 - openhcs.runtime.zmq_execution_server_launcher - INFO - Server ready - waiting for requests... +2026-09-29 19:52:12,212 - zmqruntime.server - INFO - Server ready +2026-09-29 19:54:11,526 - openhcs.processing.backends.lib_registry.registry_service - INFO - Registry metadata cache cleared +2026-09-29 19:54:11,526 - openhcs.processing.custom_functions.manager - INFO - Loaded 0 persisted custom function(s) +2026-09-29 19:54:11,543 - openhcs.processing.backends.lib_registry.registry_service - INFO - CPU-only registry discovery skipping CupyRegistry +2026-09-29 19:54:11,543 - openhcs.processing.backends.lib_registry.registry_service - INFO - CPU-only registry discovery skipping PyclesperantoRegistry +2026-09-29 19:54:15,185 - openhcs.processing.backends.lib_registry.unified_registry - INFO - 🔄 load_or_discover_functions called for openhcs +2026-09-29 19:54:15,189 - openhcs.processing.backends.lib_registry.unified_registry - INFO - ✅ Loaded 152 openhcs functions from cache +2026-09-29 19:54:15,189 - openhcs.processing.backends.lib_registry.unified_registry - INFO - 🔄 load_or_discover_functions called for skimage +2026-09-29 19:54:15,189 - openhcs.processing.backends.lib_registry.unified_registry - INFO - ✅ Loaded 114 skimage functions from cache +2026-09-29 19:54:15,189 - openhcs.processing.backends.lib_registry.registry_service - INFO - Total functions discovered: 266 +2026-09-29 19:56:34,755 - zmqruntime.execution.server - INFO - No live worker processes found to kill +2026-09-29 19:56:34,755 - zmqruntime.execution.server - INFO - Workers killed (0), server shutting down +2026-09-29 19:56:34,766 - zmqruntime.server - INFO - ZMQ Server stopped +2026-09-29 19:56:34,766 - polystore.backend_registry - INFO - Backend connections cleaned up (including process resources) +2026-09-29 19:56:34,766 - openhcs.runtime.zmq_execution_server_launcher - INFO - Server stopped diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt02/unsupported-control.jsonl b/tests/runtime_diagnostics/registration_live_20260929_attempt02/unsupported-control.jsonl new file mode 100644 index 0000000000..90f9f694e3 --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt02/unsupported-control.jsonl @@ -0,0 +1,3 @@ +{"event": "native_imports", "paths": {"openhcs": "/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/__init__.py", "polystore": "/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src/polystore/__init__.py"}} +{"type": "openhcs_custom_function_registration_destination", "time": 1790725944.8458376} +{"type": "force_shutdown", "time": 1790726193.765926} diff --git a/tests/runtime_diagnostics/registration_live_20260929_attempt02/unsupported-stderr.txt b/tests/runtime_diagnostics/registration_live_20260929_attempt02/unsupported-stderr.txt new file mode 100644 index 0000000000..b2dd7ff2f9 --- /dev/null +++ b/tests/runtime_diagnostics/registration_live_20260929_attempt02/unsupported-stderr.txt @@ -0,0 +1,20 @@ +2026-09-29 19:52:17,415 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:52:17,415 - openhcs.runtime.zmq_execution_server_launcher - INFO - ZMQ Execution Server +2026-09-29 19:52:17,415 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:52:17,415 - openhcs.runtime.zmq_execution_server_launcher - INFO - Log level: INFO (from --log-level=INFO) +2026-09-29 19:52:17,415 - openhcs.runtime.zmq_execution_server_launcher - INFO - Port: 15996 (control: 16996) +2026-09-29 19:52:17,415 - openhcs.runtime.zmq_execution_server_launcher - INFO - Host: 127.0.0.1 +2026-09-29 19:52:17,415 - openhcs.runtime.zmq_execution_server_launcher - INFO - Transport mode: tcp +2026-09-29 19:52:17,415 - openhcs.runtime.zmq_execution_server_launcher - INFO - Persistent: True +2026-09-29 19:52:17,415 - openhcs.runtime.zmq_execution_server_launcher - INFO - Log file: /home/ts/wt/openhcs-custom-function-admission-20260929/tests/runtime_diagnostics/registration_live_20260929_attempt02/unsupported.log +2026-09-29 19:52:17,415 - openhcs.runtime.zmq_execution_server_launcher - INFO - ============================================================ +2026-09-29 19:52:17,897 - zmqruntime.server - INFO - ZMQ Server started on tcp://127.0.0.1:15996 (PUB), control tcp://127.0.0.1:16996 +2026-09-29 19:52:17,897 - zmqruntime.execution.server - INFO - Queue worker thread started - will process executions sequentially +2026-09-29 19:52:17,897 - zmqruntime.execution.server - INFO - Started execution queue worker thread +2026-09-29 19:52:17,897 - openhcs.runtime.zmq_execution_server_launcher - INFO - Server ready - waiting for requests... +2026-09-29 19:52:18,070 - zmqruntime.server - INFO - Server ready +2026-09-29 19:56:33,776 - zmqruntime.execution.server - INFO - No live worker processes found to kill +2026-09-29 19:56:33,776 - zmqruntime.execution.server - INFO - Workers killed (0), server shutting down +2026-09-29 19:56:33,787 - zmqruntime.server - INFO - ZMQ Server stopped +2026-09-29 19:56:33,787 - polystore.backend_registry - INFO - Backend connections cleaned up (including process resources) +2026-09-29 19:56:33,787 - openhcs.runtime.zmq_execution_server_launcher - INFO - Server stopped diff --git a/tests/runtime_diagnostics/registration_main240_build_20260929/build.txt b/tests/runtime_diagnostics/registration_main240_build_20260929/build.txt new file mode 100644 index 0000000000..19dd501614 --- /dev/null +++ b/tests/runtime_diagnostics/registration_main240_build_20260929/build.txt @@ -0,0 +1,9 @@ +running build_ext +building 'openhcs.processing.backends.cellprofiler._granularity_reconstruct' extension +g++ -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall -fPIC -DPy_LIMITED_API=0x030B0000 -I/home/ts/code/projects/openhcs/.venv/include -I/home/ts/.pyenv/versions/3.12.3/include/python3.12 -c openhcs/processing/backends/cellprofiler/_granularity_reconstruct.cpp -o /home/ts/.cache/agent-scratch/openhcs-registration-main240-build-20260929/objects/openhcs/processing/backends/cellprofiler/_granularity_reconstruct.o -O3 +g++ -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall -shared /home/ts/.cache/agent-scratch/openhcs-registration-main240-build-20260929/objects/openhcs/processing/backends/cellprofiler/_granularity_reconstruct.o -L/home/ts/.pyenv/versions/3.12.3/lib -o /home/ts/.cache/agent-scratch/openhcs-registration-main240-build-20260929/lib/openhcs/processing/backends/cellprofiler/_granularity_reconstruct.abi3.so +building 'openhcs.core._tabular_native' extension +g++ -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall -fPIC -DPy_LIMITED_API=0x030B0000 -I/home/ts/code/projects/openhcs/.venv/include -I/home/ts/.pyenv/versions/3.12.3/include/python3.12 -c openhcs/core/_tabular_native.cpp -o /home/ts/.cache/agent-scratch/openhcs-registration-main240-build-20260929/objects/openhcs/core/_tabular_native.o -O3 +g++ -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall -shared /home/ts/.cache/agent-scratch/openhcs-registration-main240-build-20260929/objects/openhcs/core/_tabular_native.o -L/home/ts/.pyenv/versions/3.12.3/lib -o /home/ts/.cache/agent-scratch/openhcs-registration-main240-build-20260929/lib/openhcs/core/_tabular_native.abi3.so +copying /home/ts/.cache/agent-scratch/openhcs-registration-main240-build-20260929/lib/openhcs/processing/backends/cellprofiler/_granularity_reconstruct.abi3.so -> openhcs/processing/backends/cellprofiler +copying /home/ts/.cache/agent-scratch/openhcs-registration-main240-build-20260929/lib/openhcs/core/_tabular_native.abi3.so -> openhcs/core diff --git a/tests/runtime_diagnostics/registration_main240_build_20260929/imports.txt b/tests/runtime_diagnostics/registration_main240_build_20260929/imports.txt new file mode 100644 index 0000000000..27ceccaf54 --- /dev/null +++ b/tests/runtime_diagnostics/registration_main240_build_20260929/imports.txt @@ -0,0 +1,19 @@ +{ + "imports": { + "openhcs": "/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/__init__.py", + "polystore": "/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src/polystore/__init__.py" + }, + "extensions": [ + { + "path": "/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/cellprofiler/_granularity_reconstruct.abi3.so", + "bytes": 45440, + "sha256": "f0627903f263f701fc50736a934ee1b727ddcf4cbd97375f9933cf1c9182c091" + }, + { + "path": "/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/core/_tabular_native.abi3.so", + "bytes": 210808, + "sha256": "d0051154f8af59874004373603aabff0ae0576216c48076e1470d6569bb18b88" + } + ], + "scope": "source-only standard build/import, no install, kernel warming or JVM" +} diff --git a/tests/runtime_diagnostics/registration_main240_build_20260929/resources.txt b/tests/runtime_diagnostics/registration_main240_build_20260929/resources.txt new file mode 100644 index 0000000000..f379751c92 --- /dev/null +++ b/tests/runtime_diagnostics/registration_main240_build_20260929/resources.txt @@ -0,0 +1 @@ +elapsed=2.55 max_rss_kib=146140 exit=0 diff --git a/tests/runtime_diagnostics/registration_main240_tests_20260929/pytest.txt b/tests/runtime_diagnostics/registration_main240_tests_20260929/pytest.txt new file mode 100644 index 0000000000..f27d72cc6b --- /dev/null +++ b/tests/runtime_diagnostics/registration_main240_tests_20260929/pytest.txt @@ -0,0 +1,16 @@ +openhcs=/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/__init__.py +polystore=/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src/polystore/__init__.py +................ [100%] +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +16 passed, 2 warnings in 5.53s diff --git a/tests/runtime_diagnostics/registration_main240_tests_20260929/resources.txt b/tests/runtime_diagnostics/registration_main240_tests_20260929/resources.txt new file mode 100644 index 0000000000..e0eb7b7d3e --- /dev/null +++ b/tests/runtime_diagnostics/registration_main240_tests_20260929/resources.txt @@ -0,0 +1 @@ +elapsed=6.41 max_rss_kib=293380 exit=0 diff --git a/tests/runtime_diagnostics/registration_native_build_20260929/build.log b/tests/runtime_diagnostics/registration_native_build_20260929/build.log new file mode 100644 index 0000000000..72e05ece27 --- /dev/null +++ b/tests/runtime_diagnostics/registration_native_build_20260929/build.log @@ -0,0 +1,5 @@ +running build_ext +building 'openhcs.processing.backends.cellprofiler._granularity_reconstruct' extension +g++ -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall -fPIC -DPy_LIMITED_API=0x030B0000 -I/home/ts/code/projects/openhcs/.venv/include -I/home/ts/.pyenv/versions/3.12.3/include/python3.12 -c openhcs/processing/backends/cellprofiler/_granularity_reconstruct.cpp -o /home/ts/.cache/agent-scratch/openhcs-registration-native-build-20260929/objects/openhcs/processing/backends/cellprofiler/_granularity_reconstruct.o -O3 +g++ -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall -shared /home/ts/.cache/agent-scratch/openhcs-registration-native-build-20260929/objects/openhcs/processing/backends/cellprofiler/_granularity_reconstruct.o -L/home/ts/.pyenv/versions/3.12.3/lib -o /home/ts/.cache/agent-scratch/openhcs-registration-native-build-20260929/lib/openhcs/processing/backends/cellprofiler/_granularity_reconstruct.abi3.so +copying /home/ts/.cache/agent-scratch/openhcs-registration-native-build-20260929/lib/openhcs/processing/backends/cellprofiler/_granularity_reconstruct.abi3.so -> openhcs/processing/backends/cellprofiler diff --git a/tests/runtime_diagnostics/registration_native_build_20260929/import.json b/tests/runtime_diagnostics/registration_native_build_20260929/import.json new file mode 100644 index 0000000000..de6ed4c7f5 --- /dev/null +++ b/tests/runtime_diagnostics/registration_native_build_20260929/import.json @@ -0,0 +1,9 @@ +{ + "extension": "/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/processing/backends/cellprofiler/_granularity_reconstruct.abi3.so", + "bytes": 45440, + "sha256": "f0627903f263f701fc50736a934ee1b727ddcf4cbd97375f9933cf1c9182c091", + "cpp_sha256": "8aa0257600f694648979079ab824cfa706756e40418ae4d5362721f2d091b995", + "python": "/home/ts/code/projects/openhcs/.venv/bin/python", + "build": "setup.py build_ext --inplace --parallel 1 with owned build-temp/build-lib", + "scope": "source-tree build/import; installed225 already has its own compiled extension; no install, dependency download or registry bypass" +} diff --git a/tests/runtime_diagnostics/registration_native_build_20260929/resources.txt b/tests/runtime_diagnostics/registration_native_build_20260929/resources.txt new file mode 100644 index 0000000000..a966d5f270 --- /dev/null +++ b/tests/runtime_diagnostics/registration_native_build_20260929/resources.txt @@ -0,0 +1,23 @@ + Command being timed: "env OPENHCS_CPU_ONLY=true PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 PYTHONPATH=/home/ts/wt/openhcs-custom-function-admission-20260929:/home/ts/wt/openhcs-custom-function-admission-20260929/external/ObjectState/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/arraybridge/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/metaclass-registry/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/pycodify/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/pyqt-reactive/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/python-introspect/src:/home/ts/wt/openhcs-custom-function-admission-20260929/external/zmqruntime/src POLYSTORE_IMAGEJ_CACHE_ROOT=/home/ts/.cache/polystore/imagej POLYSTORE_IMAGEJ_ALLOW_DOWNLOAD=false OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 /home/ts/code/projects/openhcs/.venv/bin/python -B setup.py build_ext --inplace --parallel 1 --build-temp /home/ts/.cache/agent-scratch/openhcs-registration-native-build-20260929/objects --build-lib /home/ts/.cache/agent-scratch/openhcs-registration-native-build-20260929/lib" + User time (seconds): 1.33 + System time (seconds): 0.13 + Percent of CPU this job got: 98% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:01.48 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 93004 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 22710 + Voluntary context switches: 28 + Involuntary context switches: 142 + Swaps: 0 + File system inputs: 56 + File system outputs: 288 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 diff --git a/tests/runtime_diagnostics/registration_onboarding_20260929/pytest.txt b/tests/runtime_diagnostics/registration_onboarding_20260929/pytest.txt new file mode 100644 index 0000000000..6c95a33020 --- /dev/null +++ b/tests/runtime_diagnostics/registration_onboarding_20260929/pytest.txt @@ -0,0 +1,14 @@ +.......... [100%] +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +10 passed, 2 deselected, 2 warnings in 8.20s diff --git a/tests/runtime_diagnostics/registration_onboarding_20260929/pytest_initial.txt b/tests/runtime_diagnostics/registration_onboarding_20260929/pytest_initial.txt new file mode 100644 index 0000000000..3e18fb92f1 --- /dev/null +++ b/tests/runtime_diagnostics/registration_onboarding_20260929/pytest_initial.txt @@ -0,0 +1,83 @@ +..FFF.F..... [100%] +=================================== FAILURES =================================== +________ test_actual_source_backed_guides_fit_the_public_default_bound _________ + + def test_actual_source_backed_guides_fit_the_public_default_bound() -> None: + service = AgentAuthoringContextService( + function_catalog=_UnexpectedFunctionCatalog() + ) + for declaration in AuthoringContextDeclaration.__registry__.values(): + request = AuthoringContextRequest(kind=declaration.require_kind()) + complete = service.get_authoring_context(request.kind) + bounded = service.get_bounded_authoring_context(request) +> assert len(complete.content) <= request.max_chars +E assert 17387 <= 16000 +E + where 17387 = len('=== DESIGN AND VALIDATE AN IMAGE-ANALYSIS WORKFLOW ===\nUse this context when an analysis needs multisite assembly, r...pect images in Napari and Fiji, image browser streaming, hyperstacks, viewer instance management, and troubleshooting.') +E + where '=== DESIGN AND VALIDATE AN IMAGE-ANALYSIS WORKFLOW ===\nUse this context when an analysis needs multisite assembly, r...pect images in Napari and Fiji, image browser streaming, hyperstacks, viewer instance management, and troubleshooting.' = AuthoringContext(schema_version='openhcs.agent.v1', kind='image_analysis_workflow', content="=== DESIGN AND VALIDATE A...ect images in Napari and Fiji, image browser streaming, hyperstacks, viewer instance management, and troubleshooting.").content +E + and 16000 = AuthoringContextRequest(kind='image_analysis_workflow', max_chars=16000).max_chars + +tests/unit/agent/test_progressive_authoring_context.py:162: AssertionError +_ test_actual_mcp_onboarding_reaches_responsive_preparation_without_endpoint_contact[first_use] _ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fc67b71c500> +kind = 'first_use' + + @pytest.mark.parametrize("kind", ("first_use", "pipeline")) + def test_actual_mcp_onboarding_reaches_responsive_preparation_without_endpoint_contact( + monkeypatch, kind: str + ) -> None: +> from openhcs.agent.services.zmq_function_catalog_service import ( + ZMQFunctionCatalogService, + ) +E ModuleNotFoundError: No module named 'openhcs.agent.services.zmq_function_catalog_service' + +tests/unit/agent/test_progressive_authoring_context.py:170: ModuleNotFoundError +_ test_actual_mcp_onboarding_reaches_responsive_preparation_without_endpoint_contact[pipeline] _ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fc67b71eed0> +kind = 'pipeline' + + @pytest.mark.parametrize("kind", ("first_use", "pipeline")) + def test_actual_mcp_onboarding_reaches_responsive_preparation_without_endpoint_contact( + monkeypatch, kind: str + ) -> None: +> from openhcs.agent.services.zmq_function_catalog_service import ( + ZMQFunctionCatalogService, + ) +E ModuleNotFoundError: No module named 'openhcs.agent.services.zmq_function_catalog_service' + +tests/unit/agent/test_progressive_authoring_context.py:170: ModuleNotFoundError +_________ test_every_context_is_complete_within_the_progressive_bound __________ + + def test_every_context_is_complete_within_the_progressive_bound() -> None: + service, config_service = _service() + + for declaration in AuthoringContextDeclaration.__registry__.values(): + route = declaration.require_route() + context = service.get_authoring_context(declaration.require_kind()) + + assert context.content.startswith(f"=== {route.title.upper()} ===") +> assert len(context.content) < 16_000 +E assert 17087 < 16000 +E + where 17087 = len('=== DESIGN AND VALIDATE AN IMAGE-ANALYSIS WORKFLOW ===\nUse this context when an analysis needs multisite assembly, r...ng_semantics\n- openhcs_viewer_management — Title for openhcs_viewer_management: Summary for openhcs_viewer_management') +E + where '=== DESIGN AND VALIDATE AN IMAGE-ANALYSIS WORKFLOW ===\nUse this context when an analysis needs multisite assembly, r...ng_semantics\n- openhcs_viewer_management — Title for openhcs_viewer_management: Summary for openhcs_viewer_management' = AuthoringContext(schema_version='openhcs.agent.v1', kind='image_analysis_workflow', content="=== DESIGN AND VALIDATE A...g_semantics\n- openhcs_viewer_management — Title for openhcs_viewer_management: Summary for openhcs_viewer_management").content + +tests/unit/agent/test_progressive_authoring_context.py:258: AssertionError +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +=========================== short test summary info ============================ +FAILED tests/unit/agent/test_progressive_authoring_context.py::test_actual_source_backed_guides_fit_the_public_default_bound +FAILED tests/unit/agent/test_progressive_authoring_context.py::test_actual_mcp_onboarding_reaches_responsive_preparation_without_endpoint_contact[first_use] +FAILED tests/unit/agent/test_progressive_authoring_context.py::test_actual_mcp_onboarding_reaches_responsive_preparation_without_endpoint_contact[pipeline] +FAILED tests/unit/agent/test_progressive_authoring_context.py::test_every_context_is_complete_within_the_progressive_bound +4 failed, 8 passed, 2 warnings in 8.29s diff --git a/tests/runtime_diagnostics/registration_onboarding_20260929/resources.txt b/tests/runtime_diagnostics/registration_onboarding_20260929/resources.txt new file mode 100644 index 0000000000..ccb159486f --- /dev/null +++ b/tests/runtime_diagnostics/registration_onboarding_20260929/resources.txt @@ -0,0 +1,23 @@ + Command being timed: "/home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest --noconftest -o addopts= tests/unit/agent/test_progressive_authoring_context.py -k not actual_source_backed_guides_fit and not every_context_is_complete --basetemp=/home/ts/.cache/agent-scratch/openhcs-registration-onboarding-20260929/pytest_final -q" + User time (seconds): 8.34 + System time (seconds): 0.53 + Percent of CPU this job got: 98% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:09.03 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 308356 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 94245 + Voluntary context switches: 2 + Involuntary context switches: 46 + Swaps: 0 + File system inputs: 0 + File system outputs: 32 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 diff --git a/tests/runtime_diagnostics/registration_onboarding_20260929/resources_initial.txt b/tests/runtime_diagnostics/registration_onboarding_20260929/resources_initial.txt new file mode 100644 index 0000000000..bd92aab33f --- /dev/null +++ b/tests/runtime_diagnostics/registration_onboarding_20260929/resources_initial.txt @@ -0,0 +1,24 @@ +Command exited with non-zero status 1 + Command being timed: "/home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest --noconftest -o addopts= tests/unit/agent/test_progressive_authoring_context.py --basetemp=/home/ts/.cache/agent-scratch/openhcs-registration-onboarding-20260929/pytest -q" + User time (seconds): 8.47 + System time (seconds): 0.41 + Percent of CPU this job got: 99% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:08.95 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 277796 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 76898 + Voluntary context switches: 27 + Involuntary context switches: 39 + Swaps: 0 + File system inputs: 1240 + File system outputs: 64 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 1 diff --git a/tests/runtime_diagnostics/registration_onboarding_20260929/scope.md b/tests/runtime_diagnostics/registration_onboarding_20260929/scope.md new file mode 100644 index 0000000000..dfad0787c5 --- /dev/null +++ b/tests/runtime_diagnostics/registration_onboarding_20260929/scope.md @@ -0,0 +1,42 @@ +# Source-only onboarding closure + +Existing Python and explicit reviewed OpenHCS/eight submodule source paths; +imports verified under this tree. Shared ImageJ cache/downloadfalse, threads1, +no GUI/JVM/native preparation, no install, no shared7777 contact. Nonblocking +validation.lock held during bounded pytest. Initial guard /home19.8GiB RAM13.1GiB +warned disk+swap: no new native journey started. + +Skill-creator quick_validate.py packaging/codex/openhcs/skills/use-openhcs: +`Skill is valid!`. Focused syntax/lint E9,F63,F7,F82 and git diff --check pass. +Full Ruff has pre-existing quoted-forward-annotation warnings in context service; +no blanket cleanup claimed or performed. + +Initial file run: 4 failed,8 passed in8.29s. Two new test failures were an incorrect +test import (zmq_function_catalog_service instead of endpoint_function_catalog_service), +corrected in place. Two unchanged broad checks fail on image_analysis_workflow +exceeding the public16000-character bound. Read-only process-local exclusion of +the new registered section proved lengths: + +| Context | With section | Baseline without section | +| --- | ---: | ---: | +| first_use | 6169 | 5308 | +| pipeline | 12505 | 11644 | +| image_analysis_workflow | 17387 | 17387 | + +These lengths were measured before shortening the new pointer. The unchanged +image-analysis overflow is not repaired by this scoped registration PR. The two +existing whole-context bound tests are explicitly deselected in the final shard, +not reported passing. Parent owns that separate guide integration boundary. + +Final source shard:10 passed,2 deselected,8.20s; elapsed9.03s peak308356KiB exit0. +Two parametrized real-default-context/generated-FastMCP cases fetch first_use / +pipeline, retrieve the complete canonical source-backed knowledge document, and +discover the typed start capability through normal search_capabilities. An +interception fails any endpoint-client creation, so this discovery is independent +of cold catalogue readiness. This is source reachability, not stdio/installed proof. + +One nominal section inherits existing FirstUseWorkflowContext and +PipelineAuthoringRulesContext facets; section membership/rendering uses the +existing registry. Capability names derive from declarations. No duplicated +preparation procedure, tool inventory, guide, endpoint resolver or biological +hint. Diagnostic next journey performs the same read-only checks before search. diff --git a/tests/runtime_diagnostics/registration_owner_five_20260929/pytest.txt b/tests/runtime_diagnostics/registration_owner_five_20260929/pytest.txt new file mode 100644 index 0000000000..d04d1836c5 --- /dev/null +++ b/tests/runtime_diagnostics/registration_owner_five_20260929/pytest.txt @@ -0,0 +1,14 @@ +..... [100%] +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +5 passed, 2 warnings in 3.02s diff --git a/tests/runtime_diagnostics/registration_owner_five_20260929/receipt.json b/tests/runtime_diagnostics/registration_owner_five_20260929/receipt.json new file mode 100644 index 0000000000..b6626c9422 --- /dev/null +++ b/tests/runtime_diagnostics/registration_owner_five_20260929/receipt.json @@ -0,0 +1,39 @@ +{ + "source_sha": "c10bb3f858d88e7d8fff2fbbfee61c40ca81c887", + "imports": { + "openhcs": "/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/__init__.py", + "polystore": "/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src/polystore/__init__.py" + }, + "arguments": [ + "--noconftest", + "--import-mode=importlib", + "-p", + "no:cacheprovider", + "-o", + "addopts=", + "-q", + "--tb=short", + "--basetemp=/home/ts/.cache/agent-scratch/openhcs-registration-owner-five-20260929/pytest", + "tests/unit/agent/test_custom_registration_admission.py::test_manager_named_source_operations_use_filename_owner" + ], + "exit_code": 0, + "elapsed_seconds": 3.0643175909935962, + "peak_rss_kib": 183520, + "headroom_before": { + "free_gib": { + "/": 8.6, + "/dev/shm": 15.4, + "/home": 20.1, + "/tmp": 9.6 + }, + "level": "warning", + "ram_available_gib": 13.4, + "reasons": [ + "swap used 11.1 GiB" + ], + "swap_used_gib": 11.1 + }, + "scope": "five owner-interception source cases only; parent-authorized lightweight check despite disk warning", + "no_mcp_gui_jvm_install_or_science": true, + "owned_disposable_scratch": "/home/ts/.cache/agent-scratch/openhcs-registration-owner-five-20260929" +} diff --git a/tests/runtime_diagnostics/registration_readiness_20260929/pytest.txt b/tests/runtime_diagnostics/registration_readiness_20260929/pytest.txt new file mode 100644 index 0000000000..925a4adb9d --- /dev/null +++ b/tests/runtime_diagnostics/registration_readiness_20260929/pytest.txt @@ -0,0 +1,16 @@ +openhcs=/home/ts/wt/openhcs-custom-function-admission-20260929/openhcs/__init__.py +polystore=/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src/polystore/__init__.py +............................................... [100%] +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +47 passed, 25 deselected, 2 warnings in 7.32s diff --git a/tests/runtime_diagnostics/registration_readiness_20260929/receipt.json b/tests/runtime_diagnostics/registration_readiness_20260929/receipt.json new file mode 100644 index 0000000000..f851dbeba8 --- /dev/null +++ b/tests/runtime_diagnostics/registration_readiness_20260929/receipt.json @@ -0,0 +1,40 @@ +{ + "scope": "focused provider-free source checkpoint, not cold/live/installed acceptance", + "source_parent": "f5dbebb2798998646fcb85bfeff1185987d4a04e", + "polystore": "1209068f5fc9e4f7025a24ada8a73ef30dd1ddd7", + "python": "/home/ts/code/projects/openhcs/.venv/bin/python", + "source": "/home/ts/wt/openhcs-custom-function-admission-20260929", + "passed": 47, + "deselected": 25, + "pytest_seconds": 7.32, + "process_seconds": 8.13, + "peak_rss_kib": 305992, + "exit_code": 0, + "warnings": "two disabled async-plugin configuration options", + "pytest_arguments": [ + "--noconftest", "--import-mode=importlib", "-p", "no:cacheprovider", + "-o", "addopts=", "-q", "--tb=short", + "--basetemp=/home/ts/.cache/agent-scratch/openhcs-registration-readiness-20260929/pytest", + "tests/unit/agent/test_custom_registration_admission.py", + "tests/unit/test_function_catalog_zmq.py", + "-k", "registration or native_preparation or mcp_context or readiness or preparation_failure" + ], + "environment": { + "PYTHONPATH": "reviewed worktree plus all eight recorded submodule src directories", + "OPENHCS_CPU_ONLY": "true", + "PYTHONDONTWRITEBYTECODE": "1", + "PYTEST_DISABLE_PLUGIN_AUTOLOAD": "1", + "POLYSTORE_IMAGEJ_CACHE_ROOT": "/home/ts/.cache/polystore/imagej", + "POLYSTORE_IMAGEJ_ALLOW_DOWNLOAD": "false", + "OMP_OPENBLAS_MKL_NUMEXPR_NUMBA_BLIS_THREADS": "1" + }, + "lock": "nonblocking shared validation.lock, released on test process exit", + "scratch_bytes_allocated": 335872, + "owned_disposable_fixtures_removed_after_receipts": true, + "handles_terminal": true, + "limitations": [ + "controlled pending future, not actual cold supervised kernel preparation", + "no MCP subprocess, JVM, GUI, install, downloads or scientific inputs", + "new heavy journey held for non-swap disk headroom release" + ] +} diff --git a/tests/runtime_diagnostics/registration_readiness_20260929/resources.txt b/tests/runtime_diagnostics/registration_readiness_20260929/resources.txt new file mode 100644 index 0000000000..d0e9e13a79 --- /dev/null +++ b/tests/runtime_diagnostics/registration_readiness_20260929/resources.txt @@ -0,0 +1 @@ +elapsed=8.13 max_rss_kib=305992 exit=0 diff --git a/tests/unit/agent/test_custom_registration_admission.py b/tests/unit/agent/test_custom_registration_admission.py new file mode 100644 index 0000000000..bb865ab725 --- /dev/null +++ b/tests/unit/agent/test_custom_registration_admission.py @@ -0,0 +1,586 @@ +"""Built-in destination and route admission before custom source side effects.""" + +import asyncio +import json +from dataclasses import replace +from pathlib import Path +from types import SimpleNamespace + +import pytest +from zmqruntime.config import TransportMode +from zmqruntime.messages import ProcessIdentity +from zmqruntime.startup import EndpointStartupPhase, EndpointStartupStatus + +from openhcs.agent.dto.common import SCHEMA_VERSION +from openhcs.agent.dto.functions import ( + CustomFunctionRegistrationDestination, + CustomFunctionRegistrationDestinationRequest, + CustomFunctionRegistrationRequest, + CustomFunctionRegistrationResult, + FunctionCatalogNotReadyError, + FunctionCatalogPreparationHandle, + FunctionCatalogPreparationOutcome, + FunctionCatalogPreparationState, +) +from openhcs.agent.path_policy import AgentPathPolicy, AgentPathPolicyError +from openhcs.agent.services.endpoint_function_catalog_service import ( + CustomFunctionRegistrationUncertainError, + ZMQFunctionCatalogService, +) +from openhcs.agent.services.function_catalog_service import FunctionCatalogService +from openhcs.processing.custom_functions.manager import CustomFunctionManager +from openhcs.processing.custom_functions.runtime_registry import ( + CustomFunctionRuntimeRegistry, +) +from openhcs.runtime.zmq_config import OPENHCS_ZMQ_CONFIG + + +def request(root, **changes): + return replace( + CustomFunctionRegistrationRequest.from_fields( + source_code="@numpy\ndef boundary_probe(image):\n return image\n", + function_name="boundary_probe", + storage_dir=str(root), + port=15993, + transport_mode=TransportMode.TCP, + ), + **changes, + ) + + +def policy(root): + return AgentPathPolicy.with_roots(readable_roots=(root,), writable_roots=(root,)) + + +def preparation_state(handle, outcome=FunctionCatalogPreparationOutcome.READY): + return FunctionCatalogPreparationState( + schema_version=SCHEMA_VERSION, + handle=handle, + outcome=outcome, + progress=EndpointStartupStatus( + phase=EndpointStartupPhase.PREPARING_CAPABILITIES, message="Witness" + ), + ) + + +def test_missing_explicit_route_rejects_before_client_creation(tmp_path): + made = [] + catalog = ZMQFunctionCatalogService( + lambda: OPENHCS_ZMQ_CONFIG, + client_factory=lambda endpoint: made.append(endpoint), + path_policy=policy(tmp_path), + ) + with pytest.raises(ValueError, match="explicit port"): + catalog.register_custom_function( + CustomFunctionRegistrationRequest(source_code="not evaluated") + ) + assert not made + + +@pytest.mark.parametrize("escape", ("ordinary", "ancestor", "destination")) +def test_write_escape_rejects_before_endpoint_dispatch(tmp_path, escape): + owned = tmp_path / "owned" + outside = tmp_path / "outside" + owned.mkdir() + outside.mkdir() + sentinel = outside / "boundary_probe.py" + sentinel.write_text("preserve") + root = outside if escape == "ordinary" else owned / "custom" + if escape == "ancestor": + root.symlink_to(outside, target_is_directory=True) + elif escape == "destination": + root.mkdir() + (root / sentinel.name).symlink_to(sentinel) + made = [] + catalog = ZMQFunctionCatalogService( + lambda: OPENHCS_ZMQ_CONFIG, + client_factory=lambda endpoint: made.append(endpoint), + path_policy=policy(owned), + ) + with pytest.raises(AgentPathPolicyError): + catalog.register_custom_function(request(root)) + assert not made + assert sentinel.read_text() == "preserve" + + +@pytest.mark.parametrize( + "wrong_store, timeout", ((False, False), (True, False), (False, True)) +) +def test_exact_owned_route_and_store_precede_mutation(tmp_path, wrong_store, timeout): + root = tmp_path / "custom" + mutations = [] + endpoints = [] + + class Client: + def custom_function_registration_destination( + self, probe, *, operation_deadline=None + ): + assert probe.function_name == "boundary_probe" + native = tmp_path / "foreign" if wrong_store else root + return CustomFunctionRegistrationDestination( + str(native), str(native / "boundary_probe.py") + ) + + def register_custom_function(self, admitted, *, operation_deadline=None): + mutations.append(admitted) + assert admitted.admission_policy == policy(tmp_path) + assert admitted.server_identity == ProcessIdentity.current() + if timeout: + raise TimeoutError("controlled post-dispatch observation") + return CustomFunctionRegistrationResult( + schema_version=SCHEMA_VERSION, + connection=admitted.connection, + server_identity=admitted.server_identity, + storage_dir=str(root), + source_file_paths=(str(root / "boundary_probe.py"),), + ) + + def function_catalog_preparation( + self, read_request, *, operation_deadline=None + ): + assert not hasattr(read_request, "source_code") + assert not mutations + return preparation_state(read_request.handle) + + def disconnect(self): + pass + + def factory(endpoint): + endpoints.append(endpoint) + return Client() + + catalog = ZMQFunctionCatalogService( + lambda: OPENHCS_ZMQ_CONFIG, client_factory=factory, path_policy=policy(tmp_path) + ) + if wrong_store: + with pytest.raises(ValueError, match="no source was dispatched"): + catalog.register_custom_function(request(root)) + assert not mutations + elif timeout: + with pytest.raises(CustomFunctionRegistrationUncertainError, match="uncertain"): + catalog.register_custom_function(request(root)) + assert len(mutations) == 1 + assert catalog._config_provider().default_port == 15993 + else: + result = catalog.register_custom_function(request(root)) + assert result.connection.port == 15993 + assert len(mutations) == 1 + assert catalog._config_provider().default_port == 15993 + assert [endpoint.default_port for endpoint in endpoints] == [15993] + assert not root.exists() + catalog.close() + + +def test_native_destination_query_does_not_create_storage(tmp_path, monkeypatch): + monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "absent")) + result = FunctionCatalogService( + path_policy=policy(tmp_path) + ).custom_function_registration_destination( + CustomFunctionRegistrationDestinationRequest(function_name="boundary_probe") + ) + assert Path(result.storage_dir) == CustomFunctionManager.default_storage_directory() + assert Path(result.source_file_path) == CustomFunctionManager.source_path_for_name( + Path(result.storage_dir), "boundary_probe" + ) + assert not (tmp_path / "absent").exists() + + +def test_local_denial_precedes_manager_evaluation_and_creation(tmp_path, monkeypatch): + monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "foreign")) + evaluated = [] + monkeypatch.setattr( + CustomFunctionManager, "_prepare_source", lambda *args: evaluated.append(args) + ) + root = CustomFunctionManager.default_storage_directory() + with pytest.raises(AgentPathPolicyError): + FunctionCatalogService( + path_policy=policy(tmp_path / "owned") + ).register_custom_function(request(root)) + assert not evaluated + assert not (tmp_path / "foreign").exists() + + +@pytest.mark.parametrize("escape", ("ordinary", "ancestor", "destination")) +def test_native_escape_denial_preserves_registry_and_sentinel( + tmp_path, monkeypatch, escape +): + owned = tmp_path / "owned" + outside = tmp_path / "outside" + owned.mkdir() + outside.mkdir() + sentinel = outside / "boundary_probe.py" + sentinel.write_text("preserve native sentinel") + data_home = outside if escape == "ordinary" else owned + monkeypatch.setenv("XDG_DATA_HOME", str(data_home)) + root = CustomFunctionManager.default_storage_directory() + if escape == "ancestor": + root.parent.mkdir() + root.symlink_to(outside, target_is_directory=True) + elif escape == "destination": + root.mkdir(parents=True) + (root / sentinel.name).symlink_to(sentinel) + before = CustomFunctionRuntimeRegistry.metadata_by_name() + evaluated = [] + monkeypatch.setattr( + CustomFunctionManager, "_prepare_source", lambda *args: evaluated.append(args) + ) + with pytest.raises(AgentPathPolicyError): + FunctionCatalogService(path_policy=policy(owned)).register_custom_function( + request(root, server_identity=ProcessIdentity.current()) + ) + assert not evaluated + assert CustomFunctionRuntimeRegistry.metadata_by_name() == before + assert sentinel.read_text() == "preserve native sentinel" + + +def test_public_factory_cannot_expand_write_authority(tmp_path): + with pytest.raises(TypeError, match="admission_policy"): + CustomFunctionRegistrationRequest.from_fields( + source_code="", admission_policy=policy(tmp_path) + ) + with pytest.raises(TypeError, match="server_identity"): + CustomFunctionRegistrationRequest.from_fields( + source_code="", server_identity=ProcessIdentity.current() + ) + + +@pytest.mark.parametrize( + "identity", (None, replace(ProcessIdentity.current(), create_time=0)) +) +def test_changed_or_missing_native_owner_rejects_before_evaluation( + tmp_path, monkeypatch, identity +): + monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path)) + evaluated = [] + monkeypatch.setattr( + CustomFunctionManager, "_prepare_source", lambda *args: evaluated.append(args) + ) + root = CustomFunctionManager.default_storage_directory() + with pytest.raises(ValueError, match="no source was evaluated"): + FunctionCatalogService(path_policy=policy(tmp_path)).register_custom_function( + request(root, server_identity=identity) + ) + assert not evaluated + assert not root.exists() + + +def test_caller_admission_cannot_expand_native_server_policy(tmp_path, monkeypatch): + monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path)) + root = CustomFunctionManager.default_storage_directory() + admitted = request(root, server_identity=ProcessIdentity.current()).admitted( + policy(tmp_path) + ) + with pytest.raises(AgentPathPolicyError): + FunctionCatalogService( + path_policy=policy(tmp_path / "restricted") + ).register_custom_function(admitted) + assert not root.exists() + + +def test_native_destination_file_must_match_admitted_name(tmp_path): + destination = CustomFunctionRegistrationDestination( + str(tmp_path), str(tmp_path / "other.py") + ) + with pytest.raises(ValueError, match="no source was dispatched"): + destination.require_request(request(tmp_path)) + + +def test_wrong_owner_mutation_receipt_is_uncertain_not_replayed(tmp_path): + mutations = [] + + class Client: + def custom_function_registration_destination( + self, probe, *, operation_deadline=None + ): + return CustomFunctionRegistrationDestination( + str(tmp_path), str(tmp_path / "boundary_probe.py") + ) + + def register_custom_function(self, admitted, *, operation_deadline=None): + mutations.append(admitted) + return CustomFunctionRegistrationResult( + schema_version=SCHEMA_VERSION, + connection=admitted.connection, + server_identity=replace(ProcessIdentity.current(), create_time=0), + ) + + def function_catalog_preparation( + self, read_request, *, operation_deadline=None + ): + assert not hasattr(read_request, "source_code") + assert not mutations + return preparation_state(read_request.handle) + + def disconnect(self): + pass + + catalog = ZMQFunctionCatalogService( + lambda: OPENHCS_ZMQ_CONFIG, + client_factory=lambda endpoint: Client(), + path_policy=policy(tmp_path), + ) + try: + with pytest.raises( + CustomFunctionRegistrationUncertainError, match="Selected server" + ): + catalog.register_custom_function(request(tmp_path)) + assert len(mutations) == 1 + assert catalog._config_provider().default_port == 15993 + finally: + catalog.close() + + +@pytest.mark.parametrize( + "outcome", + tuple(state for state in FunctionCatalogPreparationOutcome if not state.ready), +) +def test_readiness_failure_precedes_mutation_and_is_not_postdispatch_uncertainty( + tmp_path, + outcome, +): + observed = [] + + class Client: + def custom_function_registration_destination( + self, probe, *, operation_deadline=None + ): + observed.append("destination") + return CustomFunctionRegistrationDestination( + str(tmp_path), str(tmp_path / "boundary_probe.py") + ) + + def function_catalog_preparation( + self, read_request, *, operation_deadline=None + ): + assert not hasattr(read_request, "source_code") + observed.append("read-only-preparation") + return preparation_state(read_request.handle, outcome) + + def register_custom_function(self, admitted, *, operation_deadline=None): + pytest.fail("Source must not be dispatched after readiness failure") + + def disconnect(self): + pass + + catalog = ZMQFunctionCatalogService( + lambda: OPENHCS_ZMQ_CONFIG, + client_factory=lambda endpoint: Client(), + path_policy=policy(tmp_path), + ) + try: + with pytest.raises(FunctionCatalogNotReadyError) as failure: + catalog.register_custom_function(request(tmp_path)) + assert not isinstance(failure.value, CustomFunctionRegistrationUncertainError) + assert observed == ["destination", "read-only-preparation"] + assert catalog._config_provider().default_port == 15993 + finally: + catalog.close() + + +def test_registration_cli_projects_declarations_and_rejects_missing_route(tmp_path): + from openhcs.mcp.dev_client import _build_parser, _calls_from_args + from openhcs.mcp.dev_client_core import McpDevCliUsageError + + parser = _build_parser() + missing = parser.parse_args(("register-custom-function", "--source-code", "source")) + with pytest.raises(McpDevCliUsageError, match="explicit port"): + _calls_from_args(missing) + args = parser.parse_args( + ( + "register-custom-function", + "--source-code", + "source", + "--port", + "15993", + "--storage-dir", + str(tmp_path), + "--function-name", + "boundary_probe", + "--transport-mode", + "tcp", + ) + ) + (call,) = _calls_from_args(args) + assert call.name == "openhcs_register_custom_function" + assert call.arguments["port"] == 15993 + assert call.arguments["transport_mode"] == "tcp" + assert call.arguments["storage_dir"] == str(tmp_path) + assert call.arguments["function_name"] == "boundary_probe" + assert "server_identity" not in call.arguments + + +def test_manager_admitted_persistence_uses_existing_registry_and_source_owner( + tmp_path, monkeypatch +): + monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path)) + for name in ( + "_declarations_by_name", + "_published_exports", + "_preparation_outcomes", + "_preparation_threads", + ): + monkeypatch.setattr(CustomFunctionRuntimeRegistry, name, {}) + monkeypatch.setattr(CustomFunctionRuntimeRegistry, "_source_revision", None) + manager = CustomFunctionManager(create_storage=False) + try: + [function] = manager.register_from_code( + request(manager.storage_dir).source_code, + expected_function_name="boundary_probe", + write_admission=policy(tmp_path).assert_writable, + clear_caches=False, + emit_signal=False, + ) + source = manager.source_path_for_function(function) + assert source == manager.source_path_for_name( + manager.storage_dir, "boundary_probe" + ) + assert source.read_text() == request(manager.storage_dir).source_code + assert ( + CustomFunctionRuntimeRegistry.metadata_by_name()["boundary_probe"].func + is function + ) + import numpy as np + + image = np.arange(4).reshape(2, 2) + np.testing.assert_array_equal(function(image), image) + CustomFunctionRuntimeRegistry.clear() + from openhcs.processing.custom_functions import boundary_probe + + assert boundary_probe.__module__ == "openhcs.processing.custom_functions" + np.testing.assert_array_equal(boundary_probe(image), image) + finally: + CustomFunctionRuntimeRegistry.clear() + + +@pytest.mark.parametrize("operation", ("require", "load", "read", "delete", "update")) +def test_manager_named_source_operations_use_filename_owner( + tmp_path, monkeypatch, operation +): + from openhcs.processing.custom_functions.source_namespace import ( + CustomFunctionSource, + ) + + monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path)) + manager = CustomFunctionManager(create_storage=False) + selected = [] + + def own_path(root, name): + selected.append((root, name)) + raise LookupError("filename-owner-witness") + + monkeypatch.setattr(manager, "source_path_for_name", own_path) + operations = { + "require": lambda: manager.require_source( + CustomFunctionSource( + function_name="boundary_probe", content_sha256="0" * 64 + ) + ), + "load": lambda: manager.load_custom_function("boundary_probe"), + "read": lambda: manager.get_function_code("boundary_probe"), + "delete": lambda: manager.delete_custom_function("boundary_probe"), + "update": lambda: manager.update_custom_function( + "boundary_probe", "not evaluated" + ), + } + with pytest.raises(LookupError, match="filename-owner-witness"): + operations[operation]() + assert selected == [(manager.storage_dir, "boundary_probe")] + assert not manager.storage_dir.exists() + + +@pytest.mark.parametrize("missing_route", (False, True)) +def test_real_generated_mcp_boundary_excludes_authority_and_denies_before_client( + tmp_path, missing_route +): + from openhcs.mcp.server import build_server + + made = [] + catalog = ZMQFunctionCatalogService( + lambda: OPENHCS_ZMQ_CONFIG, + client_factory=lambda endpoint: made.append(endpoint), + path_policy=policy(tmp_path / "owned"), + ) + + async def invoke(): + built = build_server(SimpleNamespace(function_catalog=catalog)) + tool = next( + tool + for tool in await built.list_tools() + if tool.name == "openhcs_register_custom_function" + ) + properties = tool.inputSchema["properties"] + assert {"port", "host", "storage_dir", "function_name"} <= properties.keys() + assert {"admission_policy", "server_identity"}.isdisjoint(properties) + arguments = { + "source_code": "not evaluated", + "function_name": "boundary_probe", + "storage_dir": str(tmp_path / "outside"), + } + if not missing_route: + arguments["port"] = 15993 + return await built.call_tool("openhcs_register_custom_function", arguments) + + result = asyncio.run(invoke()) + content = result[0] if isinstance(result, tuple) else result.content + payload = json.loads(content[0].text) + error = payload["errors"][0] + assert error["code"] == ( + "mcp_tool_failed" if missing_route else "agent_path_policy_rejected" + ) + assert not made + catalog.close() + + +def test_generated_mcp_preparation_tools_use_reflected_connection_and_handle(): + from openhcs.agent.dto.execution_connection import ExecutionConnectionSpec + from openhcs.mcp.server import build_server + + observed = [] + handle = FunctionCatalogPreparationHandle( + ExecutionConnectionSpec(port=15993, transport_mode=TransportMode.TCP), + ProcessIdentity.current(), + ) + + class Catalog: + def start_catalog_preparation(self, connection): + observed.append(connection) + return preparation_state(handle, FunctionCatalogPreparationOutcome.PENDING) + + def catalog_preparation_status(self, current_handle): + observed.append(current_handle) + return preparation_state(current_handle) + + def cancel_catalog_preparation(self, current_handle): + observed.append(current_handle) + return preparation_state( + current_handle, FunctionCatalogPreparationOutcome.CANCELLED + ) + + async def invoke(): + built = build_server(SimpleNamespace(endpoint_function_catalog=Catalog())) + tools = {tool.name: tool for tool in await built.list_tools()} + assert {"port", "host", "transport_mode"} <= tools[ + "openhcs_start_function_catalog_preparation" + ].inputSchema["properties"].keys() + assert {"connection", "server_identity"} == tools[ + "openhcs_get_function_catalog_preparation_status" + ].inputSchema["properties"].keys() + started = await built.call_tool( + "openhcs_start_function_catalog_preparation", + {"port": 15993, "transport_mode": "tcp"}, + ) + from openhcs.serialization.json import to_jsonable + + args = to_jsonable(handle) + status = await built.call_tool( + "openhcs_get_function_catalog_preparation_status", args + ) + cancelled = await built.call_tool( + "openhcs_cancel_function_catalog_preparation", args + ) + for response in (started, status, cancelled): + content = response[0] if isinstance(response, tuple) else response.content + assert not json.loads(content[0].text).get("errors") + + asyncio.run(invoke()) + assert observed == [handle.connection, handle, handle] diff --git a/tests/unit/agent/test_mcp_server.py b/tests/unit/agent/test_mcp_server.py index 00db35d779..516adb0339 100644 --- a/tests/unit/agent/test_mcp_server.py +++ b/tests/unit/agent/test_mcp_server.py @@ -4261,6 +4261,8 @@ def test_mcp_dev_client_function_commands_project_tool_arguments(tmp_path): str(custom_source), "--no-persist", "--full-signature", + "--port", + "15993", ) ) authoring_args = parser.parse_args( @@ -4290,6 +4292,12 @@ def test_mcp_dev_client_function_commands_project_tool_arguments(tmp_path): "source_code": "from openhcs.core.memory import numpy\n", "persist": False, "compact_signature": False, + "function_name": None, + "storage_dir": None, + "host": "localhost", + "port": 15993, + "transport_mode": None, + "persistent": True, } assert authoring_call.name == "openhcs_get_authoring_context" assert authoring_call.arguments == { diff --git a/tests/unit/agent/test_progressive_authoring_context.py b/tests/unit/agent/test_progressive_authoring_context.py index 524f712a6e..2d1d060c23 100644 --- a/tests/unit/agent/test_progressive_authoring_context.py +++ b/tests/unit/agent/test_progressive_authoring_context.py @@ -1,7 +1,12 @@ from __future__ import annotations +import asyncio +import json from pathlib import Path +import pytest +from python_introspect import dataclass_from_mapping + from openhcs.agent.authoring_contexts import ( AuthoringContextDeclaration, AuthoringContextRoute, @@ -10,12 +15,17 @@ UiVisibleWorkflowAuthoringContext, ViewerReviewAuthoringContext, ) -from openhcs.agent.capabilities import CapabilityTransport, agent_capabilities -from openhcs.agent.dto.authoring import AuthoringContextRequest +from openhcs.agent.capabilities import ( + AgentCapabilitySearchResult, + CapabilityTransport, + agent_capabilities, +) +from openhcs.agent.dto.authoring import AuthoringContext, AuthoringContextRequest from openhcs.agent.dto.common import SCHEMA_VERSION from openhcs.agent.dto.config import ConfigFieldSchema, ConfigSchema from openhcs.agent.dto.knowledge import ( KnowledgeBaseCatalog, + KnowledgeBaseDocument, KnowledgeBaseDocumentSummary, KnowledgeBaseDocumentTarget, ) @@ -153,6 +163,70 @@ def test_actual_source_backed_guides_fit_the_public_default_bound() -> None: assert bounded.content == complete.content +@pytest.mark.parametrize("kind", ("first_use", "pipeline")) +def test_actual_mcp_onboarding_reaches_responsive_preparation_without_endpoint_contact( + monkeypatch, kind: str +) -> None: + from openhcs.agent.services.endpoint_function_catalog_service import ( + ZMQFunctionCatalogService, + ) + from openhcs.mcp.context import create_agent_context + from openhcs.mcp.server import build_server + + def reject_endpoint_contact(*_args, **_kwargs): + raise AssertionError( + "guide/capability discovery must not contact a catalogue endpoint" + ) + + monkeypatch.setattr( + ZMQFunctionCatalogService, "_new_client", reject_endpoint_contact + ) + context = create_agent_context() + + async def invoke(): + built = build_server(context) + + async def read(capability, arguments, result_type): + response = await built.call_tool(capability.name, arguments) + content = response[0] if isinstance(response, tuple) else response.content + payload = json.loads(content[0].text) + assert not payload.get("errors") + return dataclass_from_mapping(result_type, payload) + + onboarding = await read( + agent_capabilities.get_authoring_context, + {"kind": kind}, + AuthoringContext, + ) + assert ( + agent_capabilities.start_function_catalog_preparation.name + in onboarding.content + ) + guide = await read( + agent_capabilities.get_knowledge_document, + {"document_id": "openhcs_custom_function_workflow"}, + KnowledgeBaseDocument, + ) + assert guide.document.document_id == "openhcs_custom_function_workflow" + assert not guide.truncated + assert ( + agent_capabilities.start_function_catalog_preparation.name in guide.content + ) + discovery = await read( + agent_capabilities.search_capabilities, + {"query": "catalog preparation", "limit": 10}, + AgentCapabilitySearchResult, + ) + assert agent_capabilities.start_function_catalog_preparation.name in { + capability.name for capability in discovery.capabilities + } + + try: + asyncio.run(invoke()) + finally: + context.endpoint_function_catalog.close() + + def test_first_use_projects_new_routes_from_the_nominal_registry() -> None: class _TemporaryAuthoringContext(AuthoringContextDeclaration): kind = "temporary_progressive_test" diff --git a/tests/unit/test_custom_function_lifecycle.py b/tests/unit/test_custom_function_lifecycle.py index 29a1be41f1..70163268db 100644 --- a/tests/unit/test_custom_function_lifecycle.py +++ b/tests/unit/test_custom_function_lifecycle.py @@ -311,7 +311,7 @@ def pause_reconstruction(self, declared, contract): def isolated_custom_runtime(monkeypatch, tmp_path): storage_dir = tmp_path / "custom_functions" storage_dir.mkdir() - monkeypatch.setattr(manager_module, "get_data_file_path", lambda _name: storage_dir) + monkeypatch.setattr(manager_module, "get_data_file_path", lambda _name, *, create=True: storage_dir) monkeypatch.setattr(CustomFunctionRuntimeRegistry, "_declarations_by_name", {}) monkeypatch.setattr(CustomFunctionRuntimeRegistry, "_published_exports", {}) monkeypatch.setattr(CustomFunctionRuntimeRegistry, "_preparation_outcomes", {}) diff --git a/tests/unit/test_function_catalog_zmq.py b/tests/unit/test_function_catalog_zmq.py index c20faa5bb3..65409e13c8 100644 --- a/tests/unit/test_function_catalog_zmq.py +++ b/tests/unit/test_function_catalog_zmq.py @@ -4,17 +4,23 @@ import inspect import textwrap import threading +import time from concurrent.futures import CancelledError, Future +from contextlib import contextmanager from dataclasses import replace import pytest -from zmqruntime import OperationCancellation +from zmqruntime import OperationCancellation, OperationDeadline from zmqruntime.client import EndpointConnectionPolicy from zmqruntime.execution import ExecutionServer +from zmqruntime.messages import ProcessIdentity from zmqruntime.startup import EndpointStartupPhase, EndpointStartupStatus +from openhcs.agent.dto.execution_connection import ExecutionConnectionSpec from openhcs.agent.dto.functions import ( CustomFunctionRegistrationControlResponse, + CustomFunctionRegistrationDestinationControlResponse, + CustomFunctionRegistrationDestinationRequest, CustomFunctionRegistrationRequest, CustomFunctionRegistrationResult, FunctionCatalogControlPayload, @@ -22,7 +28,12 @@ FunctionCatalogControlRequestABC, FunctionCatalogControlResponse, FunctionCatalogEntry, + FunctionCatalogPreparationCancelRequest, FunctionCatalogPreparationControlResponse, + FunctionCatalogPreparationOutcome, + FunctionCatalogPreparationStartRequest, + FunctionCatalogPreparationStateControlResponse, + FunctionCatalogPreparationStatusRequest, FunctionDetail, FunctionDetailControlRequest, FunctionDetailControlResponse, @@ -36,6 +47,7 @@ from openhcs.agent.services.function_catalog_service import FunctionCatalogService from openhcs.core.callable_contract import CallableImportIdentity from openhcs.core.function_reference import ImportableFunctionReference +from openhcs.runtime.function_catalog_preparation import FunctionCatalogPreparation from openhcs.runtime.zmq_control import ( ZMQControlMessageRouter, ZMQControlRequestContext, @@ -93,6 +105,7 @@ def test_local_mcp_context_uses_persisted_desktop_execution_endpoint( context = create_agent_context() assert isinstance(context.function_catalog, ZMQFunctionCatalogService) + assert context.function_catalog is context.endpoint_function_catalog assert context.function_catalog._config_provider() == endpoint_config context.function_catalog.close() @@ -105,6 +118,94 @@ def test_hosted_mcp_context_keeps_self_contained_catalog() -> None: assert isinstance(context.function_catalog, FunctionCatalogService) +@pytest.mark.parametrize("construction", ("default_factory", "raw", "injected")) +def test_real_mcp_context_composes_typed_preparation_and_catalog_authority( + monkeypatch, construction +) -> None: + import asyncio + import json + + from openhcs.agent.services.endpoint_function_catalog_service import ( + EndpointFunctionCatalogServiceABC, + ZMQFunctionCatalogService, + ) + from openhcs.mcp.context import OpenHCSAgentContext, create_agent_context + from openhcs.mcp.server import build_server + from openhcs.runtime.zmq_config import OPENHCS_ZMQ_CONFIG + from openhcs.serialization.json import to_jsonable + + connection = ExecutionConnectionSpec(port=22319) + endpoint = replace(OPENHCS_ZMQ_CONFIG, default_port=connection.port) + monkeypatch.setattr( + "openhcs.pyqt_gui.config.load_cached_ui_execution_endpoint_sync", + lambda: endpoint, + ) + preparation = FunctionCatalogPreparation(FunctionCatalogService()) + preparation._future = Future() + native = ZMQControlRequestContext( + compiled_artifacts={}, + function_catalog_preparation=preparation, + ) + observed = [] + + class Client: + def function_catalog_preparation(self, request, *, operation_deadline=None): + observed.append(request) + response = ZMQControlMessageRouter.handle( + FunctionCatalogControlPayload.from_request(request).to_dict(), + native, + ) + return FunctionCatalogPreparationStateControlResponse.from_control_response( + response + ).value + + def disconnect(self): + pass + + monkeypatch.setattr( + ZMQFunctionCatalogService, "_new_client", lambda self, config: Client() + ) + factories = { + "default_factory": create_agent_context, + "raw": OpenHCSAgentContext, + "injected": lambda: OpenHCSAgentContext( + endpoint_function_catalog=ZMQFunctionCatalogService(lambda: endpoint), + ), + } + context = factories[construction]() + assert isinstance( + context.endpoint_function_catalog, EndpointFunctionCatalogServiceABC + ) + assert context.function_catalog is context.endpoint_function_catalog + + async def invoke(): + built = build_server(context) + started = await built.call_tool( + "openhcs_start_function_catalog_preparation", + {"port": connection.port}, + ) + content = started[0] if isinstance(started, tuple) else started.content + payload = json.loads(content[0].text) + assert not payload["errors"] + assert payload["outcome"] == "pending" + arguments = to_jsonable(preparation.start(connection).handle) + for tool in ( + "openhcs_get_function_catalog_preparation_status", + "openhcs_cancel_function_catalog_preparation", + ): + response = await built.call_tool(tool, arguments) + content = response[0] if isinstance(response, tuple) else response.content + assert not json.loads(content[0].text)["errors"] + + try: + asyncio.run(invoke()) + assert len(observed) == 3 + assert preparation._thread is None + assert preparation._cancellation.requested() + finally: + context.endpoint_function_catalog.close() + + def test_gui_application_setup_does_not_initialize_execution_catalog() -> None: from openhcs.pyqt_gui.app import OpenHCSPyQtApp @@ -154,32 +255,19 @@ def _detail(entry: FunctionCatalogEntry | None = None) -> FunctionDetail: ) -class _StaticFunctionCatalogPreparation: - def __init__(self, future: Future[None]) -> None: - self.future = future - - def ensure_started(self) -> Future[None]: - return self.future - - def snapshot(self) -> EndpointStartupStatus: - return EndpointStartupStatus( - sequence=1, - phase=EndpointStartupPhase.PREPARING_CAPABILITIES, - message="Discovering functions", - timestamp=1.0, - ) - - def _context( preparation_future: Future[None] | None = None, ) -> ZMQControlRequestContext: ready = preparation_future or Future() if preparation_future is None: ready.set_result(None) + catalog = FunctionCatalogService() + preparation = FunctionCatalogPreparation(catalog) + preparation._future = ready return ZMQControlRequestContext( compiled_artifacts={}, - function_catalog=FunctionCatalogService(), - function_catalog_preparation=_StaticFunctionCatalogPreparation(ready), + function_catalog=catalog, + function_catalog_preparation=preparation, ) @@ -358,6 +446,163 @@ def test_catalog_client_cancellation_prevents_another_poll(monkeypatch) -> None: ) +@pytest.mark.parametrize("response_kind", ("ready", "pending", "timeout")) +def test_registration_client_sends_mutation_once_even_if_pending_or_uncertain( + monkeypatch, + response_kind, +) -> None: + client = ZMQExecutionClient(port=22319, persistent=True) + monkeypatch.setattr(client, "is_connected", lambda: True) + observed = [] + result = CustomFunctionRegistrationResult(schema_version="openhcs.agent.v1") + + def send(payload, *, timeout_ms): + assert 0 < timeout_ms <= 5000 + observed.append(payload) + if response_kind == "timeout": + raise TimeoutError("postdispatch observation") + if response_kind == "pending": + return FunctionCatalogPreparationControlResponse( + status=EndpointStartupStatus( + phase=EndpointStartupPhase.PREPARING_CAPABILITIES, + message="Preparing", + sequence=1, + timestamp=1.0, + ), + ).to_control_response() + return CustomFunctionRegistrationControlResponse( + value=result + ).to_control_response() + + monkeypatch.setattr(client, "_send_control_request", send) + monkeypatch.setattr( + client, + "_send_function_catalog_control_request", + lambda _payload: pytest.fail("Mutation cannot use the preparation poll loop"), + ) + request = CustomFunctionRegistrationRequest( + source_code="not evaluated", persist=False + ) + if response_kind == "ready": + assert client.register_custom_function(request) is result + else: + with pytest.raises( + TimeoutError if response_kind == "timeout" else RuntimeError + ): + client.register_custom_function(request) + assert observed == [FunctionCatalogControlPayload.from_request(request).to_dict()] + + +@pytest.mark.parametrize("operation", ("destination", "register")) +@pytest.mark.parametrize("endpoint_state", ("absent", "unresponsive")) +@pytest.mark.parametrize("explicit_deadline", (False, True)) +def test_registration_attach_only_preserves_missing_or_unresponsive_endpoint( + monkeypatch, + operation, + endpoint_state, + explicit_deadline, +): + """Exercise native attach-only, not a parallel fake connection lifecycle.""" + from zmqruntime.config import TransportMode + + client = ZMQExecutionClient(port=22319, transport_mode=TransportMode.TCP) + effects, attach_timeouts = [], [] + + def forbidden(effect): + def fail(*_args, **_kwargs): + effects.append(effect) + pytest.fail(f"Registration must not {effect}") + + return fail + + @contextmanager + def unlocked(*_args, **_kwargs): + yield True + + def unresponsive(timeout): + attach_timeouts.append(timeout) + return False + + monkeypatch.setattr("zmqruntime.client.endpoint_startup_lock", unlocked) + monkeypatch.setattr( + client, "_is_port_in_use", lambda port: endpoint_state == "unresponsive" + ) + monkeypatch.setattr(client, "_attach_existing_endpoint", unresponsive) + monkeypatch.setattr(client, "connect", forbidden("enter implicit startup")) + monkeypatch.setattr(client, "_spawn_server_process", forbidden("start a process")) + monkeypatch.setattr(client, "_kill_processes_on_port", forbidden("kill a process")) + monkeypatch.setattr( + client, "_send_control_request", forbidden("send a control/source request") + ) + deadline = ( + OperationDeadline.after_milliseconds(500, operation="controlled attach-only") + if explicit_deadline + else None + ) + calls = { + "destination": lambda: client.custom_function_registration_destination( + CustomFunctionRegistrationDestinationRequest( + function_name="boundary_probe" + ), + operation_deadline=deadline, + ), + "register": lambda: client.register_custom_function( + CustomFunctionRegistrationRequest( + source_code="never evaluated", persist=False + ), + operation_deadline=deadline, + ), + } + with pytest.raises(RuntimeError, match="existing execution endpoint"): + calls[operation]() + assert effects == [] + if endpoint_state == "absent": + assert attach_timeouts == [] + else: + assert len(attach_timeouts) == 1 + assert 0 < attach_timeouts[0] <= (0.5 if explicit_deadline else 1.0) + assert not client.is_connected() + + +@pytest.mark.parametrize("operation", ("destination", "register")) +def test_registration_expired_deadline_prevents_attachment_and_send( + monkeypatch, operation +): + client = ZMQExecutionClient(port=22319) + monkeypatch.setattr( + client, "connect", lambda **kwargs: pytest.fail("No implicit startup") + ) + monkeypatch.setattr( + client, + "connect_existing", + lambda **kwargs: pytest.fail("Expired before attachment"), + ) + monkeypatch.setattr( + client, + "_send_control_request", + lambda **kwargs: pytest.fail("Expired before dispatch"), + ) + deadline = OperationDeadline( + operation="already expired", timeout_ms=1, expires_at=0 + ) + calls = { + "destination": lambda: client.custom_function_registration_destination( + CustomFunctionRegistrationDestinationRequest( + function_name="boundary_probe" + ), + operation_deadline=deadline, + ), + "register": lambda: client.register_custom_function( + CustomFunctionRegistrationRequest( + source_code="never evaluated", persist=False + ), + operation_deadline=deadline, + ), + } + with pytest.raises(TimeoutError, match="already expired"): + calls[operation]() + + def test_catalog_client_applies_request_cancellation_to_endpoint_startup( monkeypatch, ) -> None: @@ -503,7 +748,9 @@ def test_zmq_router_registers_custom_source_through_catalog_owner( monkeypatch, ) -> None: request = CustomFunctionRegistrationRequest( - source_code="@numpy\ndef sample(image):\n return image\n" + source_code="@numpy\ndef sample(image):\n return image\n", + connection=ExecutionConnectionSpec(port=22319), + server_identity=ProcessIdentity.current(), ) result = CustomFunctionRegistrationResult( schema_version="openhcs.agent.v1", @@ -530,6 +777,137 @@ def test_zmq_router_registers_custom_source_through_catalog_owner( assert observed_requests == [request] +def test_native_registration_destination_bypasses_catalog_preparation( + tmp_path, + monkeypatch, +) -> None: + from zmqruntime.messages import ProcessIdentity + + monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "absent")) + context = _context(Future()) + + def reject_preparation(): + raise AssertionError( + "Destination admission cannot prepare the function catalog" + ) + + monkeypatch.setattr( + context.function_catalog_preparation, "ensure_started", reject_preparation + ) + response = ZMQControlMessageRouter.handle( + FunctionCatalogControlPayload.from_request( + CustomFunctionRegistrationDestinationRequest(function_name="boundary_probe") + ).to_dict(), + context, + ) + destination = ( + CustomFunctionRegistrationDestinationControlResponse.from_control_response( + response + ).destination + ) + assert destination.server_identity == ProcessIdentity.current() + assert destination.source_file_path == str( + tmp_path / "absent" / "openhcs" / "custom_functions" / "boundary_probe.py" + ) + assert not (tmp_path / "absent").exists() + + +def test_native_preparation_start_status_cancel_are_responsive_and_incarnation_owned(): + entered = threading.Event() + + class ControlledCatalog: + def prepare(self, *, status_callback, cancellation): + status_callback("Controlled preparation remains pending") + entered.set() + assert cancellation.wait(2) + raise CancelledError() + + preparation = FunctionCatalogPreparation(ControlledCatalog()) + context = ZMQControlRequestContext( + compiled_artifacts={}, function_catalog_preparation=preparation + ) + + def dispatch(request): + started = time.monotonic() + response = ZMQControlMessageRouter.handle( + FunctionCatalogControlPayload.from_request(request).to_dict(), context + ) + assert time.monotonic() - started < 0.5 + return FunctionCatalogPreparationStateControlResponse.from_control_response( + response + ).value + + try: + connection = ExecutionConnectionSpec(port=22319) + state = dispatch(FunctionCatalogPreparationStartRequest(connection)) + assert state.outcome is FunctionCatalogPreparationOutcome.PENDING + assert entered.wait(1) + future, thread = preparation._future, preparation._thread + assert ( + dispatch(FunctionCatalogPreparationStartRequest(connection)).handle + == state.handle + ) + assert preparation._future is future and preparation._thread is thread + assert ( + dispatch(FunctionCatalogPreparationStatusRequest(state.handle)).outcome + is FunctionCatalogPreparationOutcome.PENDING + ) + stale = replace( + state.handle, + server_identity=replace(state.handle.server_identity, create_time=0), + ) + with pytest.raises(RuntimeError, match="owner changed"): + preparation.cancel_preparation(stale) + assert not preparation._cancellation.requested() + cancelled = dispatch(FunctionCatalogPreparationCancelRequest(state.handle)) + assert cancelled.outcome in ( + FunctionCatalogPreparationOutcome.CANCELLING, + FunctionCatalogPreparationOutcome.CANCELLED, + ) + preparation.cancel_and_join() + assert ( + dispatch(FunctionCatalogPreparationStatusRequest(state.handle)).outcome + is FunctionCatalogPreparationOutcome.CANCELLED + ) + assert preparation._future is future and not thread.is_alive() + finally: + preparation.cancel_and_join() + + +def test_native_registration_does_not_start_or_wait_for_cold_preparation(monkeypatch): + context = ZMQControlRequestContext( + compiled_artifacts={}, + function_catalog=FunctionCatalogService(), + function_catalog_preparation=FunctionCatalogPreparation( + FunctionCatalogService() + ), + ) + monkeypatch.setattr( + context.function_catalog_preparation, + "ensure_started", + lambda: pytest.fail("Mutation cannot initiate warmup"), + ) + monkeypatch.setattr( + context.function_catalog, + "register_custom_function", + lambda _request: pytest.fail("Cold mutation must reject before evaluation"), + ) + request = CustomFunctionRegistrationRequest( + source_code="never evaluated", + persist=False, + connection=ExecutionConnectionSpec(port=22319), + server_identity=ProcessIdentity.current(), + ) + response = ZMQControlMessageRouter.handle( + FunctionCatalogControlPayload.from_request(request).to_dict(), context + ) + assert ( + response["status"] == "error" + and "No source was dispatched" in response["error"] + ) + assert context.function_catalog_preparation._future is None + + def test_zmq_router_delegates_search_to_catalog_owner(monkeypatch) -> None: catalog = _catalog() observed = [] diff --git a/tests/unit/test_function_step_transport.py b/tests/unit/test_function_step_transport.py index a2f16d8c72..b50344fffb 100644 --- a/tests/unit/test_function_step_transport.py +++ b/tests/unit/test_function_step_transport.py @@ -473,7 +473,7 @@ def codex_lazy_import_probe(image): encoding="utf-8", ) vars(custom_functions).pop(func_name, None) - monkeypatch.setattr(manager_module, "get_data_file_path", lambda _name: storage_dir) + monkeypatch.setattr(manager_module, "get_data_file_path", lambda _name, *, create=True: storage_dir) namespace: dict[str, object] = {} try: