diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000..afe469b493
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,7 @@
+# Tracked files are checked out as their committed bytes on every platform.
+# Benchmark corpora, reference-export sidecars and documentation audits are
+# compared by sha256, which Windows end-of-line conversion would change.
+* -text
+# Windows command scripts keep the platform's line endings.
+*.cmd text
+*.bat text
diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index 1d004578b0..0a06f4b235 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -152,6 +152,12 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 45
if: github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
+ # 11.6k tests no longer fit the 45-minute budget in one job; each group
+ # runs its share serially in one process.
+ strategy:
+ fail-fast: false
+ matrix:
+ group: [1, 2, 3]
env:
QT_QPA_PLATFORM: xcb
QT_XCB_GL_INTEGRATION: xcb_glx
@@ -201,7 +207,8 @@ jobs:
python -m scripts.install_ci_candidate \
--dependency-source submodules \
--wheel-directory "$RUNNER_TEMP/openhcs-candidate-wheels" \
- --extras dev,dev-gui,gui,cellprofiler-compat,fiji,mcp-http,napari
+ --extras dev,dev-gui,gui,cellprofiler-compat,fiji,mcp-http,napari \
+ --requirement pytest-split==0.11.0
- name: Materialize tracked Official30 unit fixture
run: |
@@ -238,13 +245,14 @@ jobs:
xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24 +extension GLX"
python scripts/run_installed_tests.py --coverage --repo-root "$GITHUB_WORKSPACE" --
tests/unit tests/core -p no:randomly -vv --tb=short
+ --splits 3 --group ${{ matrix.group }}
-o faulthandler_timeout=120
--cov-report=
- name: Upload unit coverage artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: coverage-unit
+ name: coverage-unit-${{ matrix.group }}
path: .coverage
include-hidden-files: true
if-no-files-found: error
@@ -2443,6 +2451,30 @@ jobs:
--extras "dev,dev-gui,cellprofiler-compat,$desktop_extras" \
--requirement pytest-split==0.11.0
+ # The test session forbids ImageJ downloads (tests/conftest.py); the
+ # Bio-Formats source tests read the declared Fiji bundle from this cache.
+ - name: Cache managed Java and ImageJ artifacts
+ uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
+ with:
+ path: |
+ ~/.cache/polystore/imagej
+ ~/.cache/cjdk
+ ~/.jgo
+ ~/.m2/repository
+ key: ${{ runner.os }}-openhcs-fiji-${{ hashFiles('pyproject.toml', 'external/PolyStore/src/polystore/imagej_distribution.py') }}
+
+ - name: Prewarm managed Fiji runtime
+ run: |
+ source test_wheel/bin/activate
+ python - <<'PY'
+ import imagej
+ import scyjava as sj
+ from polystore.imagej_runtime import FIJI_IMAGEJ_RUNTIME
+
+ ij = FIJI_IMAGEJ_RUNTIME.initialize(imagej, sj, mode="headless")
+ FIJI_IMAGEJ_RUNTIME.shutdown(ij, sj)
+ PY
+
- name: Smoke installed measured MCP protocol
run: |
source test_wheel/bin/activate
diff --git a/.github/workflows/website-pages.yml b/.github/workflows/website-pages.yml
index b0a85da525..5dc97e8d30 100644
--- a/.github/workflows/website-pages.yml
+++ b/.github/workflows/website-pages.yml
@@ -4,34 +4,36 @@ on:
pull_request:
paths:
- "website/**"
- - "openhcs/resources/assets/openhcs-*.svg"
+ - "openhcs/**"
+ - "external/**"
+ - "scripts/install_ci_candidate.py"
+ - "scripts/validate_local_release_floors.py"
- "docs/source/_static/ui.png"
- "scripts/build_website.py"
- "scripts/capture_media_gallery.py"
- "scripts/gallery_catalog.py"
- "scripts/website_gallery_projection.py"
- - "scripts/validate_local_release_floors.py"
- "docs/media_gallery_capture.md"
- "tests/unit/test_build_website.py"
- "tests/unit/test_capture_media_gallery.py"
- - "openhcs/__init__.py"
- "pyproject.toml"
- ".github/workflows/website-pages.yml"
push:
branches: [main]
paths:
- "website/**"
- - "openhcs/resources/assets/openhcs-*.svg"
+ - "openhcs/**"
+ - "external/**"
+ - "scripts/install_ci_candidate.py"
+ - "scripts/validate_local_release_floors.py"
- "docs/source/_static/ui.png"
- "scripts/build_website.py"
- "scripts/capture_media_gallery.py"
- "scripts/gallery_catalog.py"
- "scripts/website_gallery_projection.py"
- - "scripts/validate_local_release_floors.py"
- "docs/media_gallery_capture.md"
- "tests/unit/test_build_website.py"
- "tests/unit/test_capture_media_gallery.py"
- - "openhcs/__init__.py"
- "pyproject.toml"
- ".github/workflows/website-pages.yml"
workflow_dispatch:
@@ -39,14 +41,15 @@ on:
permissions:
contents: read
+# One group per ref: a push to main must not cancel a pull request's validation.
concurrency:
- group: pages
+ group: pages-${{ github.ref }}
cancel-in-progress: true
jobs:
validate:
runs-on: ubuntu-latest
- timeout-minutes: 10
+ timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -58,57 +61,13 @@ jobs:
with:
python-version: "3.12"
- - name: Install website test dependencies
+ - name: Install the OpenHCS candidate and the repository's test extra
run: |
- python -m pip install pytest
- mapfile -t local_project_paths < <(
- python - <<'PY'
- from pathlib import Path
-
- from packaging.utils import canonicalize_name
-
- from scripts.validate_local_release_floors import (
- discover_local_projects,
- read_project,
- )
-
- project = read_project(Path("pyproject.toml"))
- required_names = {
- canonicalize_name(name)
- for name in (
- "metaclass-registry",
- "polystore",
- "python-introspect",
- "zmqruntime",
- )
- }
- declared_names = {
- canonicalize_name(requirement.name)
- for requirement in project.dependencies
- }
- undeclared_names = required_names - declared_names
- if undeclared_names:
- raise SystemExit(
- "Missing website dependencies in project metadata: "
- + ", ".join(sorted(undeclared_names))
- )
- local_projects = {
- candidate.canonical_name: candidate
- for candidate in discover_local_projects()
- }
- missing_sources = required_names - local_projects.keys()
- if missing_sources:
- raise SystemExit(
- "Missing website dependency submodules: "
- + ", ".join(sorted(missing_sources))
- )
- print(
- *(local_projects[name].path.parent for name in sorted(required_names)),
- sep="\n",
- )
- PY
- )
- python -m pip install "${local_project_paths[@]}"
+ python -m pip install --upgrade pip build
+ python -m scripts.install_ci_candidate \
+ --dependency-source submodules \
+ --wheel-directory "$RUNNER_TEMP/openhcs-candidate-wheels" \
+ --extras dev
- name: Test, build, and validate landing page
run: |
diff --git a/README.md b/README.md
index dcb09fd10f..39f759dec9 100644
--- a/README.md
+++ b/README.md
@@ -98,7 +98,7 @@ graph LR
### 🛡️ Compile-Time Validation
-Configuration is resolved once into step snapshots and a compilation session. Typed plans then validate sources, artifacts, materialization, memory contracts, and worker requirements before execution begins. Errors surface immediately, not after hours of processing.
+Configuration is resolved once into a resolved pipeline definition and per-context compilation sessions. Typed plans then validate sources, artifacts, materialization, memory contracts, and worker requirements before execution begins. Errors surface immediately, not after hours of processing.
|
@@ -199,7 +199,7 @@ graph TD
| [**PyQT-reactive**](https://github.com/OpenHCSDev/PyQT-reactive) | UI form generation | React-style reactive forms from dataclasses with cross-window sync and flash animations |
| [**pycodify**](https://github.com/OpenHCSDev/pycodify) | Code ↔ object conversion | Python source as serialization format — type-preserving, diffable, editable, with collision handling |
| [**python-introspect**](https://github.com/OpenHCSDev/python-introspect) | Signature analysis | Pure-Python function/dataclass introspection for automatic UI generation and contract analysis |
-| [**metaclass-registry**](https://github.com/OpenHCSDev/metaclass-registry) | Plugin discovery | Zero-boilerplate registry system powering microscope handler and storage backend auto-discovery |
+| [**metaclass-registry**](https://github.com/OpenHCSDev/metaclass-registry) | Plugin discovery | Zero-boilerplate registry system powering dataset source and storage backend auto-discovery |
---
@@ -219,7 +219,7 @@ graph TD
| OMERO | Remote image and metadata access |
| OpenHCS format | Native generated and materialized plates |
-Source handlers are auto-detected and extensible through `metaclass-registry`.
+Dataset sources are auto-detected and extensible through `metaclass-registry`.
|
@@ -309,24 +309,27 @@ python -m pip install "openhcs[gui,mcp,viz]"
```python
# Or lower a CellProfiler pipeline into public OpenHCS declarations
+import queue
from pathlib import Path
from objectstate import ensure_global_config_context
from openhcs.core.config import GlobalPipelineConfig
from openhcs.core.orchestrator.orchestrator import PipelineOrchestrator
+from openhcs.core.progress import set_progress_queue
from openhcs.interop.cellprofiler.pipeline_import import import_cellprofiler_pipeline
-plate_path = Path("/data/plate").resolve()
+dataset_root = Path("/data/plate").resolve()
ensure_global_config_context(GlobalPipelineConfig, GlobalPipelineConfig())
steps, pipeline_config = import_cellprofiler_pipeline(
"analysis.cppipe",
- source_root=plate_path,
+ source_root=dataset_root,
)
orchestrator = PipelineOrchestrator(
- plate_path,
+ dataset_root,
pipeline_config=pipeline_config,
).initialize()
+set_progress_queue(queue.Queue()) # compilation emits progress events
execution_bundle = orchestrator.compile_pipelines(steps)
```
@@ -360,16 +363,18 @@ digests. Archived receipts without the latter two digests remain readable but
cannot verify those files. These routes do not create a
second pipeline runner or job-status system.
`openhcs-benchmark run-measured --plate PLATE --pipeline-source-file PIPELINE.py
---output-dir EVIDENCE --run-id ID --wait-timeout-ms 120000` submits that
-pipeline through ordinary headless job control, then calls the same receipt
-finalizer as MCP. Use `--execution-plate PREPARED_PLATE` when the input workspace
-has been prepared separately; the evidence directory must be empty.
+--output-dir EVIDENCE --run-id ID --wait-timeout-ms 120000` runs that
+pipeline through the OpenHCS session, then calls the same receipt finalizer as
+MCP. The source plate is never written: the run mirrors it into an execution
+workspace under `EVIDENCE/workspace` (or `--workspace-root`), or uses
+`--execution-plate PREPARED_PLATE` when the input workspace has been prepared
+separately; the evidence directory must be empty.
Use `--observation-scope outcomes` to retain per-axis success and provenance
without transferring runtime arrays; this does not compare output values.
-After an ordinary headless job completes, the expert
-`openhcs_finalize_measured_pipeline_run` tool can validate its runtime
-observation and retain that receipt from the exact submitted pipeline and
-server completion record. It does not submit another job.
+After the OpenHCS session finishes an ordinary run that exported a runtime
+observation, the expert `openhcs_finalize_measured_pipeline_run` tool can
+validate that observation and retain the receipt; the submission and server
+record come from the session. It does not submit another run.
📦 All installation options
@@ -432,7 +437,7 @@ Supported on Python 3.11 and 3.12. See [Glencoe Software](https://www.glencoesof
```
PipelineConfig + list[FunctionStep]
↓ resolve once
-StepSnapshot + CompilationSession
+ResolvedPipelineDefinition + CompilationSession
↓ derive and validate
typed CompiledStepPlan objects
↓ package
diff --git a/benchmark/adapters/openhcs.py b/benchmark/adapters/openhcs.py
index 41ae61024b..1abd59d761 100644
--- a/benchmark/adapters/openhcs.py
+++ b/benchmark/adapters/openhcs.py
@@ -18,6 +18,8 @@
from pathlib import Path
from typing import Any
+from objectstate import replace_raw
+
from benchmark.adapters.cppipe_source import (
CPPipeSourceRequest,
CPPipeSourceResolution,
@@ -308,7 +310,6 @@ def _execution_global_config(
request: OpenHCSRunRequest,
*,
output_suffix: str,
- output_dataset_root: Path,
compilation_debug_config: CompilationDebugConfig,
) -> GlobalPipelineConfig:
"""Select benchmark output paths without replacing declared path policy."""
@@ -330,7 +331,6 @@ def _execution_global_config(
),
compilation_debug_config=compilation_debug_config,
materialize_runtime_artifacts=request.materialize_runtime_artifacts,
- materialization_results_path=output_dataset_root / "results",
)
def _run_converted_cppipe_pipeline(
@@ -361,9 +361,6 @@ def _run_converted_cppipe_pipeline(
) from exc
output_suffix = f"_{request.pipeline_name}_converted_cppipe"
- output_dataset_root = (
- request.output_dir / f"{request.dataset_path.name}{output_suffix}"
- )
generated_source_path = request.output_dir / f"{cppipe_path.stem}_openhcs.py"
source_workspace_path = (
request.output_dir
@@ -417,7 +414,9 @@ def _run_converted_cppipe_pipeline(
"UI/ZMQ FunctionStep source before execution."
)
if compilation_debug_config.enabled:
- pipeline_config = replace(
+ # replace_raw keeps the imported config's unset fields inheriting
+ # from the global config; dataclasses.replace would freeze them.
+ pipeline_config = replace_raw(
pipeline_config,
compilation_debug_config=LazyCompilationDebugConfig(
enabled=compilation_debug_config.enabled,
@@ -430,7 +429,6 @@ def _run_converted_cppipe_pipeline(
global_config = self._execution_global_config(
request,
output_suffix=output_suffix,
- output_dataset_root=output_dataset_root,
compilation_debug_config=compilation_debug_config,
)
ensure_global_config_context(GlobalPipelineConfig, global_config)
@@ -550,7 +548,6 @@ def _run_converted_cppipe_pipeline(
raise ToolExecutionError(equivalence_failure_message)
metric_results = self._metric_results(request.metrics)
- output_dataset_root.mkdir(parents=True, exist_ok=True)
execution_output_root.mkdir(parents=True, exist_ok=True)
provenance = {
diff --git a/benchmark/cellprofiler_benchmark_cli.py b/benchmark/cellprofiler_benchmark_cli.py
index 10cc6df6bd..cc5f259faa 100644
--- a/benchmark/cellprofiler_benchmark_cli.py
+++ b/benchmark/cellprofiler_benchmark_cli.py
@@ -6,6 +6,7 @@
import json
import math
import os
+import signal
import sys
from abc import ABC, abstractmethod
from collections.abc import Sequence
@@ -231,7 +232,10 @@ def run(self, args: argparse.Namespace) -> int:
MeasuredRunEnded,
SessionMeasuredRun,
)
- from openhcs.agent.dto.session import DatasetRunRequest
+ from openhcs.agent.dto.session import (
+ DatasetRunRequest,
+ RuntimeObservationExportScopeName,
+ )
from openhcs.agent.path_policy import AgentPathPolicy
from openhcs.agent.session_access import AgentPathDatasetAccess
from openhcs.authoring.session.session import CallerThread, Session
@@ -241,9 +245,6 @@ def run(self, args: argparse.Namespace) -> int:
mirror_input_workspace,
)
from openhcs.runtime.zmq_config import OpenHCSZMQConfig
- from openhcs.runtime.zmq_execution_signature import (
- ZMQRuntimeObservationExportScope,
- )
from python_introspect import to_jsonable
output_dir = args.output_dir.expanduser().resolve()
@@ -281,7 +282,7 @@ def run(self, args: argparse.Namespace) -> int:
runtime_observation_export_path=str(
MeasuredPipelineRunArtifact.RUNTIME_OBSERVATION.path_in(output_dir)
),
- runtime_observation_export_scope=ZMQRuntimeObservationExportScope.named(
+ runtime_observation_export_scope=RuntimeObservationExportScopeName(
args.observation_scope
),
submit_timeout_ms=args.submit_timeout_ms,
@@ -322,7 +323,9 @@ def run(self, args: argparse.Namespace) -> int:
)
return 130
except MeasuredRunEnded as ended:
- print(json.dumps(to_jsonable(ended.row), sort_keys=True), file=sys.stderr)
+ print(
+ json.dumps(to_jsonable(ended.row), sort_keys=True), file=sys.stderr
+ )
raise RuntimeError(
f"Ordinary pipeline run did not complete: {ended}"
) from ended
@@ -825,7 +828,9 @@ def configure(
required=True,
help="MODE_LABEL=qualified_summary.csv; repeat for measured well/worker modes.",
)
- parser.add_argument("--scope", choices=("execution", "total", "amortization"), required=True)
+ parser.add_argument(
+ "--scope", choices=("execution", "total", "amortization"), required=True
+ )
parser.add_argument("--output-dir", type=Path, required=True)
return parser
@@ -1069,6 +1074,10 @@ def create_benchmark_argument_parser() -> argparse.ArgumentParser:
def main(argv: Sequence[str] | None = None) -> int:
"""Run the registered benchmark CLI."""
+ if sys.platform == "win32":
+ # Ctrl+Break is the Windows console interrupt a launcher can deliver
+ # to this process alone; it stops a run exactly as Ctrl+C does.
+ signal.signal(signal.SIGBREAK, signal.default_int_handler)
arguments = tuple(sys.argv[1:] if argv is None else argv)
parser = create_benchmark_argument_parser()
parsed = parser.parse_args(arguments)
diff --git a/benchmark/openhcs_measured_run.py b/benchmark/openhcs_measured_run.py
index 9523226820..b33e74ba6d 100644
--- a/benchmark/openhcs_measured_run.py
+++ b/benchmark/openhcs_measured_run.py
@@ -8,10 +8,7 @@
import sys
import time
from collections.abc import Callable, Mapping
-from dataclasses import (
- dataclass,
- field,
-)
+from dataclasses import dataclass
from pathlib import Path
from typing import Any
@@ -134,7 +131,12 @@ class _ZMQProgressTimingObserver:
compile_completed_at: float | None = None
execution_started_at: float | None = None
execution_completed_at: float | None = None
- last_progress_monotonic: float = field(default_factory=time.monotonic)
+ last_progress_monotonic: float | None = None
+ """Monotonic time of the latest progress; None until a run is submitted.
+
+ Starting the execution server is not execution: its client owns the
+ startup readiness timeout, so silence is measured from begin_run.
+ """
last_progress_phase: str = ""
last_progress_status: str = ""
on_event: Callable[[Mapping[str, Any]], None] | None = None
@@ -217,6 +219,8 @@ def _duration(started_at: float | None, ended_at: float | None) -> float | None:
def inactivity_seconds(self, *, observed_at: float | None = None) -> float:
"""Return elapsed monotonic time since the latest server progress event."""
+ if self.last_progress_monotonic is None:
+ return 0.0
current = time.monotonic() if observed_at is None else observed_at
return max(0.0, current - self.last_progress_monotonic)
@@ -366,9 +370,7 @@ def retain_measured_openhcs_completion(
if expected_axis_count is not None and expected_axis_count < 1:
raise ValueError("Expected axis count must be positive when declared.")
- auxiliary_params = ZMQAuxiliaryExecutionParams.from_transport(
- request.config_params
- )
+ auxiliary_params = ZMQAuxiliaryExecutionParams.from_transport(request.config_params)
observation_export_path = auxiliary_params.runtime_observation_export_path
if observation_export_path is None:
raise ValueError("Measured OpenHCS runs require runtime observation export.")
@@ -379,10 +381,7 @@ def retain_measured_openhcs_completion(
"OpenHCS ZMQ execution completed without writing runtime observation "
f"export: {observation_export_path}"
)
- if (
- auxiliary_params.runtime_observation_export_scope
- is ExportRuntimeOutcomes
- ):
+ if auxiliary_params.runtime_observation_export_scope is ExportRuntimeOutcomes:
observation_export = ZMQRuntimeExecutionOutcomeExport.read(
observation_export_path
)
diff --git a/docs/audits/architecture_a_m.json b/docs/audits/architecture_a_m.json
index a4e273211a..e32c1aa6d0 100644
--- a/docs/audits/architecture_a_m.json
+++ b/docs/audits/architecture_a_m.json
@@ -10,7 +10,7 @@
"authority": [
{
"path": "external/pyqt-reactive/src/pyqt_reactive/widgets/shared/abstract_manager_widget.py",
- "sha256": "92f8ede35a48392112c6c1aa17e959c3cc4a9450e69f5b1447cbc4ef5498dd5b",
+ "sha256": "befd5b285e4f64780a87609e0872dad84a05f399b96e0ccf684104a3e4de4cc6",
"role": "Defines the extracted abstract manager widget, lifecycle hooks, selection, and item-management mechanism."
},
{
@@ -20,10 +20,11 @@
}
],
"findings": [
- "The transition page linked only to a package-level overview instead of the exact owner narrative and OpenHCS integration page."
+ "The page is a transition stub for a mechanism now owned and documented by pyqt-reactive; its targets (pyqt-reactive architecture/abstract_manager_widget, ui_services_architecture, external_foundations) all still exist, so it stays a redirect."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/abstract_manager_widget.rst in full and compared it against the changed authorities: external/pyqt-reactive/src/pyqt_reactive/widgets/shared/abstract_manager_widget.py (U3 pyqt-reactive 0.5 advance); the class still lives at that path, pyqt-reactive still ships docs/architecture/abstract_manager_widget.rst, and both OpenHCS link targets exist, so the redirect remains accurate.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings.",
"2026-09-09: Reviewed the page sections affected by exact manager-item navigation, selection recovery guidance, and the pyqt-reactive dependency floor change from 0.3.20 to 0.3.21. Generic selection and deferred dispatch remain in pyqt-reactive; domain window identities stay in OpenHCS. Navigation acceptance does not replace action revision guards. Installation extras, Python support, code-document apply, and experimental/runtime semantics are unchanged by this batch. Updated the MCP architecture and basic-interface navigation explanations; retained existing setup/ownership links rather than duplicating a version catalogue. Only the reviewed navigation/dependency authorities were refreshed, excluding the scientific threshold patch. Final parent gates: 176 OpenHCS and 463 pyqt-reactive tests passed, including deferred target removal; full fresh-agent trial remains separate acceptance evidence.",
@@ -65,7 +66,7 @@
},
{
"path": "docs/source/architecture/abstraction_lattices.rst",
- "source_sha256": "aab6e8a69b89dc4f866cd83a639a4120a9ebbdfa366bd228fe8cda4202ea51b7",
+ "source_sha256": "01f3f213b569c46818674c2bf207749f196c91fa561ce43453203b6763036ee6",
"audience": [
"OpenHCS maintainers",
"extension authors",
@@ -75,53 +76,74 @@
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/core/registry_strategies.py",
- "sha256": "a41186665abbd308f224d0bb41d6618cd9d89b08acaf54ec2819f2024a6e0804",
- "role": "Defines the shared enum, nominal-type, and most-derived context selection authorities discussed by the page."
+ "path": "external/metaclass-registry/src/metaclass_registry/strategies.py",
+ "sha256": "5c00cd4d80f9f0ceffbc2ab95ad8c6abd44a8a66a974959445ef93df08dd4f0d",
+ "role": "Defines EnumKeyedStrategyMixin (for_enum_member), NominalTypeKeyedStrategyMixin and MostDerivedContextStrategyMixin."
},
{
"path": "openhcs/interop/cellprofiler/module_declarations.py",
- "sha256": "89dd65abc003c464ba6c489f52d3a4fa47be1d5f8b87d26ad72b7537f188cf8b",
- "role": "Defines CellProfiler module leaves as owners of module identity, lowering, headless-import validation, artifact semantics, and their declaration-derived function catalogue."
+ "sha256": "6ff4f4bc3eaf6b4c9fc42752493c2b9ba2e11c8994d7edaea81f586ac57c3b89",
+ "role": "Defines CellProfilerModule leaves and require_module as owners of module identity, lowering, headless-import validation and artifact semantics."
},
{
"path": "openhcs/interop/cellprofiler/module_artifact_declarations.py",
- "sha256": "666ec8c9adfd6a9015adf2b226df671534bb7e547cb0d76f1d16c40ac61e30a6",
+ "sha256": "5442b56c7dd7c913d38d18c5a76660ad6006e147ffc8f790712fb79b301ff035",
"role": "Defines the nominal interactive-module family that rejects headless import."
},
{
"path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
- "role": "Projects declaration-owned callable metadata, including memory contracts and artifact relations, at the nominal boundary."
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "Declares CallableContract, the declaration-owned callable metadata at the nominal boundary."
},
{
- "path": "openhcs/microscopes/microscope_base.py",
- "sha256": "ae19ce4f6e1cd2e60946eceb908722e1ff760d7654a775d8372abbc4ac1fc81f",
- "role": "Defines the microscope-handler registry and generic factory selection boundary."
+ "path": "openhcs/core/dataset_sources/source.py",
+ "sha256": "b3758a878fdf8ec28b6d9818ebfd96c5c470895575f7a79500c85f1490679281",
+ "role": "Defines the DatasetSource registry and its layout detection and workspace projection boundary."
+ },
+ {
+ "path": "openhcs/core/dataset_sources/source_bindings_source.py",
+ "sha256": "63e4a3f849ab5122ab7af8767a6288135b7330027dff962c80add93d5fd820f1",
+ "role": "Defines SourceBindingsSource, which composes a dataset source with SourceBindingsConfig declarations."
+ },
+ {
+ "path": "openhcs/core/runtime_plane_projection.py",
+ "sha256": "cf644451b6649dcec398fecbf961a4a29ea74f0bb2e0bdab052ec1be8a427f95",
+ "role": "Defines RuntimePlaneAxis and the RuntimePlaneAxisStrategy family used as the strategy example."
},
{
"path": "openhcs/agent/capabilities.py",
- "sha256": "7fe9ab91490dedd6f750a57386e91835cf1067a27d10079966510aa9821adaf0",
- "role": "Defines capability identity, transport availability, effects, and surface metadata on registered declarations."
+ "sha256": "87c8e2e5aeb425b9a2eb690d48bdd6f5af7a327c389deac261d7e4e0da5d609f",
+ "role": "Defines AgentCapabilityDeclaration and CapabilityTransport members carrying their transport semantics."
},
{
"path": "tests/unit/test_cellprofiler_static_deletion_gates.py",
- "sha256": "83d81d01ef7d828b2d0fe971a8487aa4cd049bc69e488f8f82aca2e9f02c2cbf",
+ "sha256": "54ad0f53d664d72af92af4836948d56ab5ec1a61eadbc3337756eea4ab88af7e",
"role": "Guards deletion of superseded CellProfiler mirrors and compatibility surfaces."
},
{
"path": "tests/unit/test_cellprofiler_pipeline_import.py",
- "sha256": "353f0c1d8e0bc3552d2e418d792794ab0631daa16c78a3e10191b1cc1ea07d73",
+ "sha256": "67576f37b7cbe0595acea633265f8e14677ca3c1b8204630413330273dc4e58a",
"role": "Verifies that importing an enabled interactive-only module fails explicitly."
+ },
+ {
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares SourceBindingsConfig, the source-binding owner the page contrasts with DatasetSource (moved here from source_bindings.py by K4)."
+ },
+ {
+ "path": "openhcs/core/artifacts/relations.py",
+ "sha256": "b1ae8be613fbabc372bd6692b41c5f925d4f968291f0a715bd1473d85cbed000",
+ "role": "Declares the ArtifactSpecRelation family for cross-artifact semantics."
}
],
- "findings": [
- "The 972-line page mixed a current mental model with benchmark inventories, migration checklists, WIP prescriptions, and stale proposed collapses; it now explains the current owners and treats commit history only as historical evidence.",
- "Transport initialization instructions were selected again inside the MCP server adapter; CapabilityTransport members now carry the registered nominal rendering strategy used by every consumer.",
- "Enabled interactive-only CellProfiler modules could otherwise disappear as non-executable declarations; the nominal module family now validates headless import and rejects them explicitly."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/abstraction_lattices.rst in full and compared it against the changed authorities: openhcs/interop/cellprofiler/module_declarations.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/core/callable_contract.py, openhcs/core/artifacts.py (K2 split it into the openhcs/core/artifacts package), openhcs/core/dataset_sources/source.py, openhcs/core/runtime_plane_projection.py, tests/unit/test_cellprofiler_static_deletion_gates.py, tests/unit/test_cellprofiler_pipeline_import.py, openhcs/core/config.py; CellProfilerModule.require_module, ArtifactSpecRelation, SourceBindingsConfig, RuntimePlaneAxis and RuntimePlaneAxisStrategy, and the three metaclass-registry strategy mixins with for_enum_member all still exist as the page describes, so no claim changed. Replaced the deleted artifacts.py authority with openhcs/core/artifacts/relations.py.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/abstraction_lattices.rst in full and compared it against the changed authorities: module_declarations.py, module_artifact_declarations.py and the pipeline-import test (K4 import moves, K5 flattened CallableContract metadata, PairByOrder), callable_contract.py (K5), dataset_sources/source.py and source_bindings_source.py (SourceBindingsConfig and source_bindings_defaults_to_base now imported from openhcs.core.config). The page names classes, not modules, for these owners; CellProfilerModule.require_module, CallableContract, ArtifactSpecRelation, DatasetSource, SourceBindingsConfig, SourceBindingsSource, the three metaclass-registry strategy mixins, RuntimePlaneAxisStrategy, AgentCapabilityDeclaration and CapabilityTransport all still exist, so no claim changed. Added openhcs/core/config.py as the current SourceBindingsConfig owner.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/abstraction_lattices.rst against B6's later changes to openhcs/interop/cellprofiler/module_declarations.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/core/callable_contract.py, openhcs/core/artifacts.py, openhcs/core/dataset_sources/source.py, openhcs/core/dataset_sources/source_bindings_source.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/abstraction_lattices.rst against the later B6 changes to openhcs/interop/cellprofiler/module_declarations.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/core/callable_contract.py, openhcs/core/artifacts.py, openhcs/core/dataset_sources/source.py, openhcs/core/dataset_sources/source_bindings_source.py, openhcs/agent/capabilities.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/abstraction_lattices.rst in full and compared it against the changed authorities: openhcs/core/registry_strategies.py (deleted; mixins now in metaclass_registry.strategies), openhcs/microscopes/microscope_base.py (deleted; DatasetSource family in openhcs/core/dataset_sources), module_declarations.py, module_artifact_declarations.py, callable_contract.py, openhcs/agent/capabilities.py and the two CellProfiler tests; replaced the microscope-handler owner and SourceBindingsHandler with DatasetSource and SourceBindingsSource, attributed the strategy mixins to metaclass-registry, and confirmed CellProfilerModule.require_module, ArtifactSpecRelation, RuntimePlaneAxisStrategy, for_enum_member and CapabilityTransport against current source. Historical commit references are left as history.",
"Re-read docs/source/architecture/abstraction_lattices.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/registry_strategies.py; openhcs/interop/cellprofiler/module_declarations.py; openhcs/core/callable_contract.py; openhcs/microscopes/microscope_base.py; openhcs/agent/capabilities.py; tests/unit/test_cellprofiler_static_deletion_gates.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -148,7 +170,7 @@
},
{
"path": "docs/source/architecture/analysis_consolidation_system.rst",
- "source_sha256": "3f3fa8f3f0ee441d4c2aa42bd79c2da3dc04635f4958b64ce49bcbfc25a618db",
+ "source_sha256": "d5e535d38edfd520b1fe69191d7547b4f02521631eb6fd71ea3de09bfbea7d83",
"audience": [
"OpenHCS runtime maintainers",
"analysis-output integrators"
@@ -157,47 +179,49 @@
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/core/orchestrator/analysis_consolidation.py",
- "sha256": "3ef9c20c347c1ac724353c84b24f9d7e13513aba7b9c5c01e194171b7e72bb52",
- "role": "Projects execution-ledger CSV content with each result directory's compiled storage context and the global output-plate destination."
+ "path": "openhcs/domains/microscopy/analysis_consolidation.py",
+ "sha256": "292f7138a00dc22dba5b8d8af5de6140d247b955723cc807cc982ffec7a7dd2d",
+ "role": "Defines AnalysisConsolidationHook, its saved/reused output observations, the per-directory destinations and the FileManager summary writer."
+ },
+ {
+ "path": "openhcs/core/post_execute.py",
+ "sha256": "42183491eae6a5a17fd68c179970b1682343f1f73f355291f2ba4b02c784905d",
+ "role": "Defines the PostExecuteHook family: binding from the global config, observation combine, and running after every partition succeeds."
},
{
"path": "openhcs/processing/backends/analysis/consolidate_analysis_results.py",
- "sha256": "aad546fdc069653d809f9ef6867ad2ae351276ea9ab66b377af7cd6ab2428d60",
- "role": "Defines nominal analysis sources and summary writers, in-memory merging, rendering, and consolidation policy."
+ "sha256": "508cdbf635705bd4b133569c7e745b2d163eb77871dd8be2dd2d994a6535c73a",
+ "role": "Defines AnalysisTableSource, AnalysisSummaryWriter, the inclusion selection, in-memory merging and summary rendering."
},
{
- "path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares AnalysisConsolidationConfig and its standalone consolidation policy."
+ "path": "openhcs/domains/microscopy/config.py",
+ "sha256": "79b8baefdbf22b85c199807f62b8234daca6904633a574886661a1c2e3d03a9b",
+ "role": "Declares AnalysisConsolidationConfig and PlateMetadataConfig."
},
{
- "path": "tests/unit/test_orchestrator_lane_planning.py",
- "sha256": "09c39f5cba68f8c15a0def8a09843b62d6da5c5a01e7558a9263ce4d311c078d",
- "role": "Exercises orchestrator lane planning and post-execution analysis boundaries used by consolidation."
+ "path": "external/PolyStore/src/polystore/base.py",
+ "sha256": "8f2082ade15072bc8e8eba363c75a1d1cff7c67c74845e6485f69eab244634dc",
+ "role": "Declares DataSink.contextual_save_kwargs used for backend save context."
},
{
"path": "tests/unit/test_function_artifact_materialization.py",
- "sha256": "ba4f8f5f165ab87c3000d6d371a6f25cf4ec6a5d39b5047abaa392c827fe9ae8",
- "role": "Proves consolidation consumes runtime CSV values, uses the compiled backend destination, ignores non-table artifacts, and propagates failures."
+ "sha256": "69b548e9eb1c60d2224b02b6f53932f580e0e12c6b808f9a69998541d3a28adf",
+ "role": "Proves the hook consumes saved CSV content, uses the compiled backend destination, skips a disabled config, and propagates failures."
},
{
"path": "tests/unit/test_routed_channel_selection.py",
- "sha256": "f0f7e10c99f6568048817f38ee10f648bb0b6fb4945cc0fae0f988761557745f",
- "role": "Executes a native plate with separate analysis destinations and verifies per-directory and merged output summaries without writing a summary into the input plate."
- },
- {
- "path": "openhcs/processing/materialization/core.py",
- "sha256": "1f5a89a1eb50766ef093df3fece16d6bb03422b92d062fe8ae6471656b1ebd45",
- "role": "Owns writer runtime-path selection before rendering and the shared ROI target request."
+ "sha256": "68f646ca37428ab983f02517409f8a811fa724d6777eb1ae975371b23d73f234",
+ "role": "Executes a native plate with separate analysis destinations and verifies per-directory and merged summaries without writing into the input plate."
}
],
- "findings": [
- "The page described discovery as a results-directory scan, while current orchestration consumes exact typed runtime observation records.",
- "Runtime consolidation formerly reopened virtual output addresses through the host filesystem and discarded failures; it now consumes execution-ledger content and writes through the compiled backend."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/analysis_consolidation_system.rst in full and compared it against the changed authorities: tests/unit/test_function_artifact_materialization.py, tests/unit/test_routed_channel_selection.py; the tests moved to the artifacts package imports, the MainFlowTarget output target and the family spellings of observation mode, filename identity and input source, and still prove the saved-CSV, compiled-destination and separate-destination behaviour the page states, so no claim changed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/analysis_consolidation_system.rst in full and compared it against the changed authorities: tests/unit/test_function_artifact_materialization.py (the streaming-filter test now uses the IncludePartitions class instead of PartitionFilterMode.INCLUDE, from K6/L-surface family changes); the page names no filter mode, and AnalysisConsolidationHook, PostExecuteHook, AnalysisTableSource, AnalysisSummaryWriter and DataSink.contextual_save_kwargs still exist at the listed owners, so no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/analysis_consolidation_system.rst against B6's later changes to openhcs/domains/microscopy/analysis_consolidation.py, openhcs/core/post_execute.py, tests/unit/test_function_artifact_materialization.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/analysis_consolidation_system.rst against the later B6 changes to openhcs/domains/microscopy/analysis_consolidation.py, openhcs/core/post_execute.py, tests/unit/test_function_artifact_materialization.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/analysis_consolidation_system.rst in full and compared it against the changed authorities: openhcs/core/orchestrator/analysis_consolidation.py (moved to openhcs/domains/microscopy/analysis_consolidation.py as a post-execute hook), consolidate_analysis_results.py, openhcs/core/config.py (consolidation config moved to openhcs/domains/microscopy/config.py), the three tests and openhcs/processing/materialization/core.py; rewrote the flow paragraph for AnalysisConsolidationHook and PostExecuteHook, removed the obsolete output-selection/skip-rendering paragraph, and confirmed the writer, DataSink save context, output-root destination and failure propagation against current source. Dropped test_orchestrator_lane_planning.py and materialization/core.py, which no longer own a stated claim.",
"Re-read docs/source/architecture/analysis_consolidation_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/orchestrator/analysis_consolidation.py; openhcs/core/config.py; tests/unit/test_orchestrator_lane_planning.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -215,7 +239,7 @@
},
{
"path": "docs/source/architecture/artifact_contract_system.rst",
- "source_sha256": "eb9380b348a7fccfb4385956bc1997b4dc4bf23aaf1f45abe2fd2fd8c1342d83",
+ "source_sha256": "576ad13f18601dd0dfc61c9ab24131eb7193caeee11138c2e63c42949025e43e",
"audience": [
"OpenHCS compiler maintainers",
"callable and artifact extension authors"
@@ -224,36 +248,77 @@
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/core/artifacts.py",
- "sha256": "bb958c74c98cb96c97c5ac1ea68ac997673aac6afcad57bdf0c9496065e895fa",
- "role": "Defines artifact families, specifications, references, relations, and materialisation declarations."
+ "path": "openhcs/core/callable_contract.py",
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "Defines CallableContract and its ordered artifact input and output collections."
},
{
- "path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
- "role": "Defines callable artifact input and output ABI ownership and relation validation."
+ "path": "openhcs/core/function_patterns.py",
+ "sha256": "c8004ae1e3f241c4ac6ce0a61c69e3c0d1ecfb5021e1bef5dd0fb1fd1251ca50",
+ "role": "Defines InvocationArtifactInputEdgePlan, its main_flow_projection and storage plan, and MainFlowInputProjection."
+ },
+ {
+ "path": "openhcs/core/pipeline/function_contracts.py",
+ "sha256": "67a654102b1d60c0a0f90e64ccaf3a8c177035cd11a0346734f872b329783d74",
+ "role": "Defines the special_inputs, artifact_inputs and artifact_outputs decorators."
},
{
"path": "openhcs/core/pipeline/path_planner.py",
- "sha256": "0ed8bb8d843ff41382f4de25367a74c9535a39425c6fc328091ad3025d526151",
+ "sha256": "cf31311ffa462d0cbd8eb8cf426d75d5ee2f5668de7b7291ca6787b5d5be118a",
"role": "Implements artifact satisfaction and exact compiled path planning."
},
+ {
+ "path": "tests/unit/test_implicit_main_flow_compilation.py",
+ "sha256": "5fc152553e9a13e023d21a0e297e9b2b9015755cb88eb391423b033f5986eb35",
+ "role": "Compiles grouped native output into a CellProfiler Threshold consumer and asserts the main-flow edge, group scope and execution scope."
+ },
{
"path": "tests/unit/test_callable_contract.py",
- "sha256": "493f76a54f5fe024ca8f10c53b1f810b8c369626ad7141205b42ef3a78282137",
+ "sha256": "9170f8694f388dbed5bfd36d882a5c0a4cdf28f50f51f7fdeaf4a955a9f3b412",
"role": "Verifies callable contract construction, validation, and artifact semantics."
},
{
"path": "tests/unit/test_artifact_input_projection_plan.py",
- "sha256": "a777c8fd900f15ca5f6a990fb48b36e1497a3555e5575e68487d7cb7b74cdd65",
+ "sha256": "1c4453a0c636f5a5ba9e9c1c240279170d34a19e29a64e9ba64fd37ae9e752b9",
"role": "Verifies occurrence-specific artifact input projection plans."
+ },
+ {
+ "path": "openhcs/core/artifacts/kinds.py",
+ "sha256": "216b913efad9ec76707e9f61be445fb6fa1c6601c36ef53e296acd4928604515",
+ "role": "Defines the registered ArtifactType families the Declarations section lists."
+ },
+ {
+ "path": "openhcs/core/artifacts/specs.py",
+ "sha256": "c4c50309a195bf85e1c0641413544d9581fc0ce1197043130126f7a4c70d8c4e",
+ "role": "Defines ArtifactSpec, ArtifactSpecCollection and MainFlowStackOutputSpec."
+ },
+ {
+ "path": "openhcs/core/artifacts/plans.py",
+ "sha256": "0d6a10a78f7177273deca4ad07b9bc729a2eb99254164f2606922674b7286b22",
+ "role": "Defines ArtifactSpecRef and the ArtifactInputPlan and ArtifactOutputPlan compiled plans with their relations and materialisation fields."
+ },
+ {
+ "path": "openhcs/core/artifacts/relations.py",
+ "sha256": "b1ae8be613fbabc372bd6692b41c5f925d4f968291f0a715bd1473d85cbed000",
+ "role": "Defines the ArtifactSpecRelation leaves, including the object measurement and member subject relations, and ObjectArtifactSubjectBinding."
+ },
+ {
+ "path": "openhcs/constants/input_source.py",
+ "sha256": "4a7929e66d11d5ad81c40f080ba8cd23d466797bfa780d59eae0e1aea3ffd9a4",
+ "role": "Declares ReadPipelineStart, the input source a step selects to read the pipeline-start flow."
}
],
"findings": [
- "The artifact-family inventory omitted spatial graphs, and an embedded extension procedure crossed the explanation/how-to boundary."
+ "The forward-planning section named the removed enum member PIPELINE_START; E1 made InputSource a class family, so the page now names ReadPipelineStart."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/artifact_contract_system.rst in full and compared it against the changed authorities: openhcs/core/artifacts.py (K2 split it into the openhcs/core/artifacts package), openhcs/core/callable_contract.py, openhcs/core/function_patterns.py, openhcs/core/pipeline/function_contracts.py, openhcs/core/pipeline/path_planner.py, tests/unit/test_implicit_main_flow_compilation.py, tests/unit/test_artifact_input_projection_plan.py; grepped every class, constant and field the page names (the ArtifactType kinds, MainFlowInputProjection members, PIPELINE_INPUT_ARTIFACT, unnamed_main_flow_artifact_name, MainFlowStackOutputSpec, the invocation edge and projection-key classes, SelectedPlaneImageOutput, SourceProjectedImageOutput, ObjectArtifactSubjectBinding) and all still exist; corrected the PIPELINE_START spelling to ReadPipelineStart and replaced the deleted artifacts.py authority with the package modules that own the named declarations.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/artifact_contract_system.rst in full and compared it against the changed authorities: callable_contract.py and test_callable_contract.py (K5: CallableMetadata became CallableDeclaration and the metadata wrapper was flattened into direct ArtifactSpecCollection fields), function_patterns.py (K5 pattern consolidation: AuthoredFunctionPattern, kwarg merge removed), function_contracts.py (special_inputs now stores artifact_input_parameter_names) and path_planner.py (metadata injection over the ArtifactGraph, source_bindings.compiled_plan). The page's claims still hold: special_inputs only names ABI slots, CallableContract carries ordered ArtifactSpecCollection inputs/outputs, InvocationArtifactInputEdgePlan keeps spec, storage_plan and main_flow_projection with the two-member MainFlowInputProjection enum, and every other named class (ArtifactPlanKeySelector, ArtifactDeclarationStepContext, MainFlowStackOutputSpec, SelectedPlaneImageOutput, SourceProjectedImageOutput, the object subject relations and binding, SourceImageProvenance, StepMaterializationConfig) still exists, so no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/artifact_contract_system.rst against B6's later changes to openhcs/core/pipeline/function_contracts.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/artifact_contract_system.rst against B6's later changes to openhcs/core/artifacts.py, openhcs/core/callable_contract.py, openhcs/core/function_patterns.py, openhcs/core/pipeline/function_contracts.py, openhcs/core/pipeline/path_planner.py, tests/unit/test_implicit_main_flow_compilation.py, tests/unit/test_callable_contract.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/artifact_contract_system.rst against the later B6 changes to openhcs/core/artifacts.py, openhcs/core/callable_contract.py, openhcs/core/function_patterns.py, openhcs/core/pipeline/function_contracts.py, openhcs/core/pipeline/path_planner.py, tests/unit/test_implicit_main_flow_compilation.py, tests/unit/test_callable_contract.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/artifact_contract_system.rst in full and compared it against the changed authorities: openhcs/core/artifacts.py, openhcs/core/callable_contract.py, openhcs/core/pipeline/path_planner.py, tests/unit/test_callable_contract.py, tests/unit/test_artifact_input_projection_plan.py; replaced the removed consumes_main_flow flag with main_flow_projection in the occurrence and implicit main-flow sections and the regression description, tidied the special_inputs sentence, and confirmed every other named declaration (ArtifactType families, relation leaves, SourceImageProvenance, ArtifactPlanKeySelector, SelectedPlaneImageOutput, RuntimeValueStore, StepMaterializationConfig) exists with the stated role.",
"Re-read docs/source/architecture/artifact_contract_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/artifacts.py; openhcs/core/callable_contract.py; openhcs/core/pipeline/path_planner.py; tests/unit/test_callable_contract.py; tests/unit/test_artifact_input_projection_plan.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -270,87 +335,65 @@
},
{
"path": "docs/source/architecture/batch_workflow_service.rst",
- "source_sha256": "5b35c78968bea4782ff67974f90428b37d7f00d79ef00509155f2b6d79917798",
+ "source_sha256": "5fe054e9e50b3615028bd270372778e384a15a71028c196f5ddddd313942fe4e",
"audience": [
- "OpenHCS desktop and execution-service maintainers"
+ "OpenHCS session and execution-service maintainers"
],
- "user_need": "Understand how Plate Manager composes compile, run, progress, status, and cleanup services.",
+ "user_need": "Understand how the session compiles and runs datasets for every client.",
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/pyqt_gui/services/plate_manager_batch_workflow.py",
- "sha256": "974aebec62dc0f283f92d8153594771d1206f02decdfd8ea5faae0a563676699",
- "role": "Implements the Plate Manager batch facade and compile-before-execute lifecycle."
- },
- {
- "path": "openhcs/pyqt_gui/widgets/shared/services/compile_batch_workflow_service.py",
- "sha256": "1b37faeb90ecbc326727bef124ba990a067dffca430955772f8f002356a451f8",
- "role": "Implements compile-only batch submission and result collection."
- },
- {
- "path": "openhcs/pyqt_gui/widgets/shared/services/batch_workflow_components.py",
- "sha256": "abd29f375ab12fdc56058f1c860ce26c3b52129658795eb1c95581d83b7480bd",
- "role": "Declares the lazily composed focused workflow services."
- },
- {
- "path": "tests/unit/pyqt_gui/test_batch_workflow_compile_engine.py",
- "sha256": "a8295fb6edbd0229128025327c975d7c916b791f5774df4a030e6c9778f3c4cb",
- "role": "Verifies batch compilation, artifact retention, status, and failure convergence."
- },
- {
- "path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
- "role": "Coordinates post-paint endpoint preparation, projects the browser-owned endpoint snapshot into both status-bar surfaces, and routes endpoint termination to the client lifecycle owner."
- },
- {
- "path": "external/pyqt-reactive/src/pyqt_reactive/services/zmq_server_scan_service.py",
- "sha256": "ae12092dfd8b85a3354b87a708f3c32aef787c2f9fd0402158d32d14b654c7de",
- "role": "Owns the immutable endpoint observation snapshot, derives per-port status without stored connection flags, and projects startup presentation through the lifecycle declaration."
+ "path": "openhcs/authoring/session/operations/datasets.py",
+ "sha256": "630a74c9e4fb026a7c47e78b3a07427c4d16c9630447a523b36dd8c73e17e430",
+ "role": "Declares CompileDatasets and its initialised, idle and non-empty-pipeline availability checks."
},
{
- "path": "tests/unit/pyqt_gui/test_main_zmq_window_routing.py",
- "sha256": "d02fe132fda83540f26ed4b7a79fc1409fabbf0ff6ce72f3d1ed8681258e9f6a",
- "role": "Proves startup callbacks only commit observations, one snapshot drives status text and the indicator, and endpoint termination descends to the client lifecycle owner."
+ "path": "openhcs/authoring/session/compilation.py",
+ "sha256": "219db74f43cc83c5c07f5bb4b2a461e4979d22dca48f00b4ea041852f35b2ac5",
+ "role": "Declares DatasetPipelineRequest snapshots and the CompiledDataset result."
},
{
- "path": "openhcs/agent/services/endpoint_function_catalog_service.py",
- "sha256": "977a74003819f2547548d322f4c5f0cb148b23fec20f31be4fa462bfcfb7ea04",
- "role": "Owns the shared asynchronous endpoint catalogue, exact revision state, and selected-reference projection for desktop and local MCP consumers."
+ "path": "openhcs/authoring/session/compile_batch.py",
+ "sha256": "3e7397caa5ea1b38c1abc47d363e37fc01b0b601ab1e6b37ed00226f7aa51f42",
+ "role": "Runs compile-only batches and compile-before-execution through BatchSubmitWaitEngine."
},
{
- "path": "openhcs/pyqt_gui/widgets/shared/services/zmq_client_service.py",
- "sha256": "8bb3b33ee64869a225e7ec23be3dcf4f5bf3622dd801f7e77bdd7826980cbd8e",
- "role": "Owns the persistent OpenHCS execution-client lifecycle and retires its exact session when that endpoint terminates."
+ "path": "external/zmqruntime/src/zmqruntime/execution/batch_submit_wait.py",
+ "sha256": "c447b2ccbc372a5336f5a3b32844b896dc5d58566cb3de31640ae3be2379edee",
+ "role": "Defines BatchSubmitWaitEngine, the submit-then-wait batch driver."
},
{
- "path": "external/zmqruntime/src/zmqruntime/client.py",
- "sha256": "fc8bdf68455673921d79742204bf6c88e42b52bbe407d10ef4a831e084ca86a8",
- "role": "Declares EndpointClientSession as the authoritative transport-client and application-admission proof."
+ "path": "openhcs/authoring/session/session.py",
+ "sha256": "ced615b580e4625e09873449b29ac67cdea87c8b8065c205f32e6d5921cf31fc",
+ "role": "Implements run_datasets (reserve, reset, connect, compile all, submit each with compile_artifact_id) and the endpoint status binding."
},
{
- "path": "openhcs/pyqt_gui/widgets/shared/services/execution_state.py",
- "sha256": "73d03020ffe437d8aaf4b6a5eeb4f43b91130de747d411648cb7fa2e4df27777",
- "role": "Owns active batch membership, terminal outcomes and terminal row supersession."
+ "path": "openhcs/authoring/session/submission.py",
+ "sha256": "e88dba60a818ce44d0ecf744b918f8a82421ccd0d9a6a5aebce7c15bb331f1dc",
+ "role": "Submits each execution with its compile_artifact_id and auxiliary observation-export parameters."
},
{
- "path": "tests/unit/pyqt_gui/test_compile_terminal_supersession.py",
- "sha256": "0b04d9c843ce2dcf16833b1c53b5853637802aa35c2d23de992c3db6c2bb2465",
- "role": "Tests recompilation superseding row activity while retaining batch outcomes."
+ "path": "openhcs/authoring/session/progress.py",
+ "sha256": "cd34f68461b50506600da7854a3bf57646f2f0ff7288d0627bc40cb852a16825",
+ "role": "Defines ExecutionProgress: one registry-mutation subscription, dirty marking and interval rebuilds on the session main thread."
},
{
- "path": "tests/unit/pyqt_gui/test_concurrent_initialization_progress.py",
- "sha256": "c8e960fa83adb73f3533cf4c75acafd59249a1993f4e650733101fdd3d62ddb8",
- "role": "Tests other-plate initialisation without replacing active execution progress."
+ "path": "tests/unit/test_u1_session_guards.py",
+ "sha256": "98de1f6239607e92d2aa35bd5e95f3e1c1c42887cc238e1291ffc830ca2cd4a5",
+ "role": "Guards that session state lives in the Qt-free session package and its operations are declared classes."
}
],
- "findings": [
- "The lifecycle explanation still described attach-only startup and omitted asynchronous endpoint-catalogue prewarming; it now follows the persistent endpoint and shared projection owners.",
- "The status explanation assigned the indicator to client connection lifecycle even though the browser snapshot now owns every endpoint-status projection; the page now names the shared snapshot boundary.",
- "GUI cleanup previously gated client-owner teardown on application admission, leaving an in-progress endpoint attempt alive; the explanation now records unconditional owner teardown and typed cancellation.",
- "Clarified current endpoint catalogue reads after remote registration and retained asynchronous/cache ownership."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/batch_workflow_service.rst against openhcs/authoring/session/operations/datasets.py: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/batch_workflow_service.rst in full and compared it against the changed authorities: openhcs/authoring/session/compile_batch.py, openhcs/authoring/session/session.py, openhcs/authoring/session/submission.py; E1 replaced the OrchestratorState and terminal-status enum members with classes and the session now takes the observation export scope as a class, but the compile-only and run flows, compile_artifact_id submission and lifecycle the page describes are unchanged and it names no state member, so no claim changed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/batch_workflow_service.rst in full and compared it against the changed authorities: openhcs/authoring/session/session.py (K6 debug command and replay mode became classes: RunDebugCommand, StopDebugCommand, PersistentPausedWorkerReplay); the page does not describe debug sessions, and CompileDatasets, DatasetPipelineRequest, CompiledDataset, BatchSubmitWaitEngine, compile_artifact_id submission, ExecutionProgress, Session.close and the endpoint_status binding all still exist, so no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/batch_workflow_service.rst against B6's later changes to openhcs/authoring/session/session.py: the writability probe makes one exclusive create attempt, the installed demo reports ViewerType.value, the unit session pre-creates its QApplication, and the GUI journey waits for each batch to end. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/batch_workflow_service.rst against B6's later changes to openhcs/authoring/session/session.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/batch_workflow_service.rst against B6's later changes to openhcs/authoring/session/operations/datasets.py, openhcs/authoring/session/compile_batch.py, openhcs/authoring/session/session.py, openhcs/authoring/session/submission.py, openhcs/authoring/session/progress.py, tests/unit/test_u1_session_guards.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/batch_workflow_service.rst against the later B6 changes to openhcs/authoring/session/operations/datasets.py, openhcs/authoring/session/compile_batch.py, openhcs/authoring/session/session.py, openhcs/authoring/session/submission.py, openhcs/authoring/session/progress.py, tests/unit/test_u1_session_guards.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/batch_workflow_service.rst in full and compared it against the changed authorities: the deleted openhcs/pyqt_gui batch workflow, compile batch, components, client service and execution-state modules and their tests (replaced by openhcs/authoring/session), openhcs/pyqt_gui/main.py, test_main_zmq_window_routing.py, endpoint_function_catalog_service.py, zmqruntime client.py and test_concurrent_initialization_progress.py; checked CompileDatasets availability, DatasetPipelineRequest/CompiledDataset, BatchSubmitWaitEngine, run_datasets ordering and compile_artifact_id submission, ExecutionProgress listener and interval rebuild, and the endpoint status binding against the session package. The page text is accurate; the record now points at the session owners.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings.",
"A focused 15-file architecture regression selection passed all 462 tests, including the authorities named for this page.",
@@ -375,7 +418,7 @@
},
{
"path": "docs/source/architecture/cellprofiler_interop.rst",
- "source_sha256": "ca51e69ffeb5e594de80ab27c0c71bcb1d78a76fd059ab093afbab9b7a9b0040",
+ "source_sha256": "6674c881ea67c190d8d5d7ea3fc5aa280273dfbfdb2701d7a9332abe629dd367",
"audience": [
"CellProfiler users",
"OpenHCS interoperability and compiler maintainers"
@@ -385,37 +428,52 @@
"authority": [
{
"path": "openhcs/interop/cellprofiler/pipeline_import.py",
- "sha256": "b547c838b2c86fe7cc5fe12d6cbf6a6715e83055c2d8c1a109ac22566d22c51e",
+ "sha256": "5aa8895c9f533603b7ef972202141e2439473f0604b97d66f5a027b8b3cd1c83",
"role": "Implements .cppipe parsing, declaration-owned headless validation, and lowering into PipelineConfig and FunctionStep declarations."
},
{
"path": "openhcs/interop/cellprofiler/module_declarations.py",
- "sha256": "89dd65abc003c464ba6c489f52d3a4fa47be1d5f8b87d26ad72b7537f188cf8b",
+ "sha256": "6ff4f4bc3eaf6b4c9fc42752493c2b9ba2e11c8994d7edaea81f586ac57c3b89",
"role": "Owns module-specific callable, setting, headless-import, scope, artifact, and function-catalogue declarations."
},
{
"path": "openhcs/interop/cellprofiler/module_artifact_declarations.py",
- "sha256": "666ec8c9adfd6a9015adf2b226df671534bb7e547cb0d76f1d16c40ac61e30a6",
+ "sha256": "5442b56c7dd7c913d38d18c5a76660ad6006e147ffc8f790712fb79b301ff035",
"role": "Defines the nominal interactive-module family that rejects headless import."
},
+ {
+ "path": "openhcs/interop/cellprofiler/compile_time_contracts.py",
+ "sha256": "2528a13067bd2d24f8592c430e5f83f84ddcab560c07ba0299b47f734c5ecfb5",
+ "role": "Defines CellProfilerInvocationContractProviderFactory, which walks the ResolvedPipelineDefinition steps with a forward artifact context."
+ },
+ {
+ "path": "openhcs/core/pipeline/compilation_session.py",
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "Declares ResolvedPipelineDefinition, the resolved step sequence the provider walks."
+ },
+ {
+ "path": "openhcs/constants/input_source.py",
+ "sha256": "4a7929e66d11d5ad81c40f080ba8cd23d466797bfa780d59eae0e1aea3ffd9a4",
+ "role": "Declares the InputSource family and its two members, ReadPreviousStep and ReadPipelineStart."
+ },
{
"path": "tests/unit/test_cellprofiler_artifact_declarations.py",
- "sha256": "627acfb57647e135e44511a8b24e3496c2a59b046f1f9b89c5ade1023cfce382",
+ "sha256": "9b45af5b1638781add722578b9b76efbf8ae4bea2bae9ad7425215ed22c3a228",
"role": "Verifies typed image, object, measurement, relationship, table, grid, graph, and special artifact declarations."
},
{
"path": "tests/integration/test_cellprofiler_official30_zmq.py",
- "sha256": "bc5abfddf076996a050b0601c46b6039d2d812ab57cd303847b241e477176f53",
+ "sha256": "e9f346e25a9fbb1ec7edf9e6e19c6d51c7d2000347ee61ef41b3017244322589",
"role": "Defines the executable Official30 import, execution, equivalence, and viewer evidence boundary."
},
{
"path": "tests/unit/test_cellprofiler_pipeline_import.py",
- "sha256": "353f0c1d8e0bc3552d2e418d792794ab0631daa16c78a3e10191b1cc1ea07d73",
+ "sha256": "67576f37b7cbe0595acea633265f8e14677ca3c1b8204630413330273dc4e58a",
"role": "Verifies that direct import rejects an enabled interactive-only module explicitly."
},
{
"path": "benchmark/cellprofiler_reference_exports.py",
- "sha256": "604f9a95f94eeb10365437b875a0ff3d558dbb2fda13e7e951242d417a69b49c",
+ "sha256": "3f175ad2173cffb2181246b6d5cc498c64dfe90b5f38f87265c1effafeb60e6f",
"role": "Derives digest-bound benchmark-only exporter pipelines from terminal importer contracts and owns numeric-image and categorical-label comparison semantics."
},
{
@@ -425,12 +483,16 @@
}
],
"findings": [
- "The artifact inventory omitted spatial graphs, and a module-authoring procedure duplicated the task-oriented developer guide inside an explanation.",
- "The explanation did not state that setting-backed output identities remain in public invocation keywords independently of downstream observation and are consumed before the raw runtime call.",
- "Enabled interactive-only modules could otherwise be silently omitted as non-executable declarations; the owning nominal module family now rejects headless import explicitly."
+ "The page called InputSource an enum; E1 made it a class family with the members ReadPreviousStep and ReadPipelineStart, so the page now says family."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/cellprofiler_interop.rst in full and compared it against the changed authorities: openhcs/interop/cellprofiler/pipeline_import.py, module_declarations.py, module_artifact_declarations.py, compile_time_contracts.py, openhcs/core/pipeline/compilation_session.py, openhcs/constants/input_source.py, tests/unit/test_cellprofiler_artifact_declarations.py, tests/unit/test_cellprofiler_pipeline_import.py, benchmark/cellprofiler_reference_exports.py; the importer still chooses previous-step or pipeline-start main flow from source and producer evidence (now by returning ReadPreviousStep or ReadPipelineStart), and the artifact families the page lists match the kinds in openhcs/core/artifacts/kinds.py; corrected the one sentence that called InputSource an enum.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/cellprofiler_interop.rst in full and compared it against the changed authorities: pipeline_import.py, module_declarations.py, module_artifact_declarations.py, compile_time_contracts.py (K4 moved SourceBindingsConfig/StepSourceBindingsConfig into openhcs.core.config; K5 flattened CallableContract.metadata into direct artifact_inputs/artifact_outputs/runtime_adapter fields), compilation_session.py, and the two CellProfiler unit tests (SourceBindingMatchMethod.ORDER became PairByOrder). The page names SourceBindingsConfig, CallableContract and the module pipeline without import paths or the metadata wrapper; every class and function it names (CPPipeParser, SettingsBinder, CellProfilerModuleArtifactContracts, SettingToKeywordBinding, CellProfilerInvocationContractProviderFactory, ArtifactDeclarationStepContext, FunctionStepExecutionScope.PLATE, FileBundleOptions, CPASQLiteRenderer, CellProfilerReferenceExportPlan, numpy_avx512_skx_svml_symbol_available, the two-member InputSource enum) still exists, so no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/cellprofiler_interop.rst against B6's later changes to tests/integration/test_cellprofiler_official30_zmq.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/cellprofiler_interop.rst against B6's later changes to openhcs/interop/cellprofiler/pipeline_import.py, openhcs/interop/cellprofiler/module_declarations.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/core/pipeline/compilation_session.py, tests/unit/test_cellprofiler_artifact_declarations.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/cellprofiler_interop.rst against the later B6 changes to openhcs/interop/cellprofiler/pipeline_import.py, openhcs/interop/cellprofiler/module_declarations.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/core/pipeline/compilation_session.py, tests/unit/test_cellprofiler_artifact_declarations.py, tests/integration/test_cellprofiler_official30_zmq.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/cellprofiler_interop.rst in full and compared it against the changed authorities: pipeline_import.py, module_declarations.py, module_artifact_declarations.py, test_cellprofiler_artifact_declarations.py, the Official30 integration test, test_cellprofiler_pipeline_import.py and benchmark/cellprofiler_reference_exports.py; replaced the removed StepSnapshot with ResolvedPipelineDefinition and confirmed the import flow, interactive-module rejection, InputSource members, FunctionStepExecutionScope.PLATE, the provider's named methods, the CPA SQLite/properties route and CellProfilerReferenceExportPlan against current source.",
"Re-read docs/source/architecture/cellprofiler_interop.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/interop/cellprofiler/pipeline_import.py; openhcs/interop/cellprofiler/module_declarations.py; tests/unit/test_cellprofiler_artifact_declarations.py; tests/integration/test_cellprofiler_official30_zmq.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -445,7 +507,7 @@
},
{
"path": "docs/source/architecture/code_ui_interconversion.rst",
- "source_sha256": "e92fe7d1580b8cbc121dbf13023c64faf85150de3e4bca9a13f99513ef6e2cea",
+ "source_sha256": "d2afa247a25b0b07128b0527d04f65eb83d86e135c748da798c905dcc73b0ebd",
"audience": [
"OpenHCS UI and agent-surface maintainers",
"advanced attached-agent users"
@@ -455,72 +517,72 @@
"authority": [
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
"role": "Defines the nominal pipeline code document and its rendering, parsing, and application boundary."
},
{
"path": "openhcs/pyqt_gui/services/ui_bridge_object_state.py",
- "sha256": "747c284c37e420092ffda9abbb8275fc7deaaff7f3676cecf3f5b1f324314744",
+ "sha256": "f615718186e4aaebdc12eec6bec6a97d47953322075c5b067974e5a16ecc8e80",
"role": "Projects live ObjectState scopes, fields, revisions, document capabilities, and mutation receipts through the UI bridge."
},
{
"path": "openhcs/agent/dto/ui_bridge.py",
- "sha256": "585b1701b01b92bfce91437a60881afa769e36de9126807c3f4fcaf0ffc006fb",
+ "sha256": "9b754a28e367e910bee35b6d900f54daf7121e224cac32e43908d147e7faea00",
"role": "Carries generic endpoint application and operation-lifecycle declarations through UI bridge descriptors and request/response envelopes."
},
{
"path": "openhcs/agent/services/ui_bridge_transport.py",
- "sha256": "32e759baf65b34989c99223d8fa9e64800ab566cd05160df2bc125b04dea5953",
+ "sha256": "1c2fb99aef62969cfabb9d22ccb954e8894ee72fd2e66e625304ffdb96a41b95",
"role": "Validates the schema, bridge protocol, application identity, and request identity before an MCP response is accepted."
},
{
"path": "openhcs/pyqt_gui/services/ui_bridge_server.py",
- "sha256": "667f582fffd4b489ca7274f8962a759c3d33d24b10ec8e6d1aa5cac01233b067",
+ "sha256": "f93a3570a834eca8596139676629dda48c3f22991fd8cb58cd03427af8b24a38",
"role": "Advertises the shared application identity and rejects incompatible requests before dispatching them into live UI state."
},
{
"path": "external/zmqruntime/src/zmqruntime/messages.py",
- "sha256": "0df6261b31d3e2758ced4c7ee5933dde0e866b8e30d7927c3fcb2641a8d97d44",
+ "sha256": "fb257a7ac25a47d8c604fbfbd88c96b92fac16f914a61dab81c784fe99d95378",
"role": "Owns generic exact endpoint-application compatibility and the typed mismatch error."
},
{
"path": "external/python-introspect/src/python_introspect/annotation_types.py",
- "sha256": "dc126879a8a581cea0758eec4df18ec20c47ca4b1e135abde23699b65b790bde",
- "role": "Owns declaration-derived enum discovery, accepted input strings, and member coercion across transparent annotation wrappers."
+ "sha256": "004a3ce2e214a184aaa3230ceaa89ccaa5f188c1c3d885aacad5222726c34b7a",
+ "role": "Owns enum_input_values: the accepted input strings for an enum or a declared choice family, and enum member coercion across transparent annotation wrappers."
},
{
"path": "openhcs/pyqt_gui/services/ui_agent_bridge.py",
- "sha256": "fa8e7a1cd300c6e5cfde5ac155d3e0fd0a7324632fb150280f57c828d8dfcecd",
+ "sha256": "55a21b2746e3ba27eff1a3315604725d340dbddce6645c8457f707484a2f1f0c",
"role": "Projects ObjectState branch and snapshot restoration through the attached-agent surface."
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/services/window_code_document.py",
- "sha256": "d0ab321af0eb2b14fac3ab2577c8c61672763d2f13a70a7ce8a35ee7bfab7c75",
+ "sha256": "56d92d665a7b3bf6acde28292679cad13388793e1acb5bea74926f4330150d42",
"role": "Owns generic window-document writability and declaration-normalised source comparison."
},
{
"path": "tests/unit/pyqt_gui/test_ui_agent_bridge.py",
- "sha256": "1feee4bb413738f04e81cbdf26244dc0d585a68921e6c7749a941a08518007a1",
+ "sha256": "422be9298e5120c898bd7110884ca770f1f033f07fd69be4ac0caa978d3014f4",
"role": "Verifies read-only rejection, semantic no-op application, and branch restoration through the UI bridge."
},
{
"path": "openhcs/pyqt_gui/services/function_step_code_document.py",
- "sha256": "3cb9ee3bf0411b0cd9ddf254934dd402de12abb630bc764cd724a9492f912814",
+ "sha256": "f89732e608ddaae8b44dac635a80e86a95eb6c953194558471ee6eed7ca75b65",
"role": "Defines the step-editor code-document projection and apply semantics."
},
{
"path": "tests/unit/pyqt_gui/test_pipeline_code_document_transactionality.py",
- "sha256": "b7e546e904b897f13d76f0c523e751afa541a75c627349ba79a3a87313d30f4c",
+ "sha256": "6604f4cc9ff8d1f3824f0479c4947c7d264aff59c7b3e139d670b659b63b39ec",
"role": "Verifies revision checks, validation, snapshots, rollback, and transactional application."
},
{
"path": "openhcs/pyqt_gui/widgets/step_parameter_editor.py",
- "sha256": "54e0bdd74f8d781c2bfdf1da559bbe7e2393f19ef74826d374a2281e8eb96258",
+ "sha256": "534f33acc5e295bfda4fbd73cbec49c4fbf662953e14842bf04ce59550f2dea3",
"role": "Applies edited function patterns through generic reconciliation before rebuilding parameter panes."
},
{
"path": "openhcs/serialization/pycodify_formatters.py",
- "sha256": "316ff58e34dcb87816082ead03347cd280fe72b4e3ab8ea31affc1627e548c23",
+ "sha256": "18b744cdeb8d9ce22801efe6f3f81fec346194019293e68f542dc13c11724a2d",
"role": "Supplies OpenHCS semantic equality and clean/full choices to shared parameter projection."
},
{
@@ -530,7 +592,7 @@
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/services/function_pattern_code_document.py",
- "sha256": "bdf41b58bff009890e6e696cfc4f18313adfb1669566b2566f9e7b93d863c071",
+ "sha256": "77b0bc021efaaac47bc5fb8c29aa50536162a9630dafd48c6ece42790837b94d",
"role": "Projects child parameter states into function-pattern code documents, retaining a child only while its function authority and editable surface match."
},
{
@@ -545,25 +607,35 @@
},
{
"path": "tests/unit/test_pycodify_formatters.py",
- "sha256": "445479f81fd70ce7f288d5b7537b9bbb85d1b5dd724d27baabf8e4c4e508128c",
+ "sha256": "78d695333a74b4a75a07ca7de87a5c685a6fd429c67412b43375d16a298a9c80",
"role": "Verifies clean/full parameter order and semantic default handling."
},
{
"path": "external/pyqt-reactive/tests/test_numeric_roundtrip.py",
"sha256": "5975b0ce8bd7bfcf1b21bf03593b72882c775a7982d834894081b6012c556566",
"role": "Verifies numeric value preservation and compact presentation."
+ },
+ {
+ "path": "external/ObjectState/src/objectstate/codegen.py",
+ "sha256": "347d2697c061a35e6e440e373fe4e8bfce1ad92b500e98da07faa26f6a3a1356",
+ "role": "Owns lazy-dataclass source formatting and field_needs_source, the clean-view decision that leaves unset lazy fields out of generated code."
+ },
+ {
+ "path": "external/ObjectState/src/objectstate/inspection.py",
+ "sha256": "aba713b8bba961c2fc53e31ea181ed807d8190f7290e8fb4647f0044c5ae771f",
+ "role": "Defines FieldInspection, the raw/resolved/dirty/provenance field view the UI bridge projection inherits."
}
],
"findings": [
- "The page duplicated the task-oriented attached-agent sequence and rules already owned by the code/UI user guide, and bare knowledge identifiers were not navigable RTD links.",
- "The page omitted the generic owner's dynamic writability and declaration-normalised no-op contracts.",
- "The field projection list omitted declaration-derived enum inputs exposed by ObjectState field help.",
- "The process boundary did not previously reject an MCP/UI pairing whose OpenHCS versions were incompatible despite sharing an older bridge protocol.",
- "Explained signature-ordered parameter projection, retained child-state reconciliation, and generic numeric presentation ownership.",
- "Clarified that function child-state retention is conditional on an unchanged editable callable surface; explicit keyword surface changes rebuild the child at its stable scope."
+ "The field-projection list said only enum inputs are exposed; after E1 many fields hold a registered class family, whose choice labels enum_input_values exposes the same way, so the page now names both."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/code_ui_interconversion.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/services/ui_bridge_object_state.py, openhcs/agent/dto/ui_bridge.py, openhcs/pyqt_gui/services/ui_bridge_server.py, openhcs/pyqt_gui/services/ui_agent_bridge.py, tests/unit/pyqt_gui/test_ui_agent_bridge.py, tests/unit/pyqt_gui/test_pipeline_code_document_transactionality.py; E1 made the bridge operation status, field filter and action status class families with unchanged wire names, and the Plate Manager document assignments a typed record; the code-document, revision, field-mutation and identity claims are unchanged, and the accepted-inputs bullet now covers registered-family choices as well as enums.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/code_ui_interconversion.rst in full and compared it against the changed authorities: openhcs/core/pipeline_document.py (renders through pycodify.generate_python_source), ui_bridge_object_state.py (field projection now extends objectstate.inspection.FieldInspection), zmqruntime messages.py and pyqt-reactive window_code_document.py / function_pattern_code_document.py (U3 advance; config code-document driver and external-editor preference moved into pyqt-reactive), function_step_code_document.py and step_parameter_editor.py (patch_lazy_constructors from ObjectState, CodeEditorFormUpdater from pyqt-reactive), pycodify_formatters.py and its test (L6: lazy-dataclass, enum, path and import formatters deleted in favour of objectstate.codegen and pycodify imported_name), and test_ui_agent_bridge.py (K6 debug/progress classes, DockPane). Corrected the equality/omission ownership sentence and added the ObjectState codegen and inspection owners; document, revision, field-mutation, bridge identity and parameter-order claims still hold.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/code_ui_interconversion.rst against B6's later changes to openhcs/pyqt_gui/services/ui_bridge_object_state.py, openhcs/agent/dto/ui_bridge.py, openhcs/pyqt_gui/services/ui_agent_bridge.py, tests/unit/pyqt_gui/test_ui_agent_bridge.py, tests/unit/pyqt_gui/test_pipeline_code_document_transactionality.py, tests/unit/test_pycodify_formatters.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/code_ui_interconversion.rst against the later B6 changes to openhcs/pyqt_gui/services/ui_bridge_object_state.py, openhcs/agent/dto/ui_bridge.py, openhcs/pyqt_gui/services/ui_agent_bridge.py, tests/unit/pyqt_gui/test_ui_agent_bridge.py, tests/unit/pyqt_gui/test_pipeline_code_document_transactionality.py, tests/unit/test_pycodify_formatters.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/code_ui_interconversion.rst in full and compared it against the changed authorities: openhcs/core/pipeline_document.py (PipelineDocumentAuthority renamed PipelineDocumentCodec; PipelineDocument unchanged), ui_bridge_object_state.py, openhcs/agent/dto/ui_bridge.py, ui_bridge_transport.py, ui_bridge_server.py, zmqruntime messages.py, python-introspect annotation_types.py, ui_agent_bridge.py and its test, function_step_code_document.py (now FunctionStepDocumentCodec), the code-document transactionality test, step_parameter_editor.py (the pickled .step settings files are gone, which the page never described), pycodify_formatters.py and its test; checked the schema/bridge-protocol/application identity checks on both bridge ends, the field-mutation request token without base revision, enum values in field help, code-document revision apply, and Plate Manager naming. Every claim the page makes still holds; no text change.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings.",
"A focused 15-file architecture regression selection passed all 462 tests, including the authorities named for this page.",
@@ -594,25 +666,30 @@
"authority": [
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
"role": "Lowers declarations into typed step plans, including declaration-derived framework-device assignments."
},
{
"path": "openhcs/core/pipeline/compilation_session.py",
- "sha256": "3c840a89baccc8665d415175458280fd904c1a837b7fd879e303d7dbca129a1c",
- "role": "Defines the scoped CompilationSession authority."
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "Defines the per-context CompilationSession and ResolvedPipelineDefinition."
},
{
"path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
- "role": "Assembles and validates worker-safe execution bundles without a process-global GPU registry."
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "Defines CompiledExecutionBundle with its runtime and pickle-safe transport contexts."
}
],
"findings": [
- "The old compilation-service URL no longer owns compiler facts and is intentionally a concise transition to the current model."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/compilation_service.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py; the stub states no compiler fact beyond its link to pipeline_compilation_system, which still exists.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/compilation_service.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py (ScopeKey-built scope ids, CompileProgressPhase/RunningProgressStatus classes, CompiledSourceBindingPlan() default) and openhcs/core/pipeline/compilation_session.py (strip_disabled_functions removed, invocation validation moved onto the graph); CompilationSession, ResolvedPipelineDefinition and CompiledExecutionBundle still exist and the redirect target exists, so the transition remains accurate.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/compilation_service.rst against B6's later changes to openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/compilation_service.rst against the later B6 changes to openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/compilation_service.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_execution.py; the page only names the session, typed-plan and execution-bundle model and redirects to pipeline_compilation_system, all of which still exist (CompilationSession, CompiledStepPlan, CompiledExecutionBundle), so the redirect stays unchanged.",
"Re-read docs/source/architecture/compilation_service.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline/compiler.py; openhcs/core/pipeline/compilation_session.py; openhcs/core/compiled_execution.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -623,7 +700,7 @@
},
{
"path": "docs/source/architecture/compilation_system_detailed.rst",
- "source_sha256": "f6b652564854eafd1e1d6670a2f6290c799d5aea05ddafff3fe6c8e7587af7bb",
+ "source_sha256": "d866a71b0092d3887ea91d83cbd76deb44fcf4302adf162794157dede8724ad4",
"audience": [
"OpenHCS maintainers following the former detailed compiler URL"
],
@@ -632,25 +709,35 @@
"authority": [
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
"role": "Implements the current compiler stages, including declaration-derived framework-device assignment."
},
{
- "path": "openhcs/core/pipeline/step_snapshot.py",
- "sha256": "d81a70b84d5c68fb738603653516d20da882fb51d74d41a6dca238823e3279cb",
- "role": "Defines immutable resolved step snapshots consumed during compilation."
+ "path": "openhcs/core/pipeline/compilation_session.py",
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "Defines ResolvedPipelineDefinition, which replaced resolved step snapshots, and CompilationSession."
},
{
"path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
"role": "Defines typed step-plan fields, exact framework footprints, and framework-local device bindings."
+ },
+ {
+ "path": "openhcs/core/compiled_execution.py",
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "Defines CompiledExecutionBundle."
}
],
"findings": [
- "The former fixed-phase narrative is historical; the active URL correctly redirects to current StepSnapshot, CompilationSession, typed-plan, and bundle ownership."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/compilation_system_detailed.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py; ResolvedPipelineDefinition, CompilationSession and CompiledExecutionBundle still exist and the link target pipeline_compilation_system still exists.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/compilation_system_detailed.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py (ScopeKey scope ids, progress phase/status classes), openhcs/core/pipeline/compilation_session.py (disabled-function stripping removed, graph-level invocation validation) and openhcs/core/compiled_step_plan.py (source binding and universe plans default-construct instead of .empty()); the named classes and the redirect target still exist, so the transition remains accurate.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/compilation_system_detailed.rst against B6's later changes to openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/compilation_system_detailed.rst against the later B6 changes to openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/compilation_system_detailed.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/step_snapshot.py (deleted in 0adb68108; resolved steps now live on ResolvedPipelineDefinition in compilation_session.py), openhcs/core/compiled_step_plan.py; replaced StepSnapshot with ResolvedPipelineDefinition in the redirect text and confirmed the redirect target page exists.",
"Re-read docs/source/architecture/compilation_system_detailed.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline/compiler.py; openhcs/core/pipeline/step_snapshot.py; openhcs/core/compiled_step_plan.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -670,20 +757,30 @@
"authority": [
{
"path": "external/ObjectState/src/objectstate/lazy_factory.py",
- "sha256": "f81ce722695dbab98fd02658f2015e5cb93c1e1e69547d24249e62d896e2a828",
+ "sha256": "b9bd1547f5f78a6e15399006c085b1459f3172e1d31637233a93200846e07c37",
"role": "Implements generic lazy dataclass creation and resolution owned by ObjectState."
},
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares OpenHCS domain configuration that consumes ObjectState mechanics."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares the kernel OpenHCS configuration dataclasses that consume ObjectState mechanics."
+ },
+ {
+ "path": "openhcs/domains/microscopy/config.py",
+ "sha256": "79b8baefdbf22b85c199807f62b8234daca6904633a574886661a1c2e3d03a9b",
+ "role": "Declares the microscopy domain's configuration sections composed into the same ObjectState mechanics."
}
],
"findings": [
- "The transition did not link the exact ObjectState architecture owner or the component-system integration page."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/component_configuration_framework.rst in full and compared it against the changed authorities: openhcs/core/config.py; the stub states no configuration field, and its targets component_system_integration and external_foundations still exist.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/component_configuration_framework.rst in full and compared it against the changed authorities: external/ObjectState/src/objectstate/lazy_factory.py (ObjectState 2.x advance) and openhcs/core/config.py (K4 source-binding classes, K6/L-surface family moves); the page states no configuration fact beyond ownership, ObjectState still owns lazy dataclass creation in lazy_factory.py and ships docs/architecture.rst, and both OpenHCS link targets exist, so the redirect remains accurate.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/component_configuration_framework.rst against B6's later changes to openhcs/core/config.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/component_configuration_framework.rst against the later B6 changes to external/ObjectState/src/objectstate/lazy_factory.py, openhcs/core/config.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/component_configuration_framework.rst in full and compared it against the changed authorities: external/ObjectState/src/objectstate/lazy_factory.py, openhcs/core/config.py (domain sections such as analysis consolidation moved to openhcs/domains/microscopy/config.py, now added as an authority); the page only states that ObjectState owns lazy configuration and that OpenHCS documents its domain declarations, and redirects to component_system_integration and external_foundations, both of which exist, so the redirect stays unchanged.",
"Re-read docs/source/architecture/component_configuration_framework.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: external/ObjectState/src/objectstate/lazy_factory.py; openhcs/core/config.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -703,7 +800,7 @@
"authority": [
{
"path": "external/python-introspect/src/python_introspect/signature_analyzer.py",
- "sha256": "73600e546753640964d4ba5308fdf808ed3a4a6f085417d8ac0745d9e2748ed7",
+ "sha256": "edbe51f6c92b2c334dc4533bbeaa252be649c1ac738d2ebfca4364f2708479dc",
"role": "Implements generic callable and signature analysis."
},
{
@@ -713,22 +810,23 @@
},
{
"path": "external/metaclass-registry/src/metaclass_registry/core.py",
- "sha256": "ed4dd1dfd0e4e05e916fec39f19b4b8bd526e776aa0c96e3abe9b1ffbdff0505",
- "role": "Implements generic nominal registration and family discovery."
+ "sha256": "72c8284e8a32dd193f13f36a425289d5293a32f4a381046db523aa87182e0be8",
+ "role": "Implements generic nominal registration through AutoRegisterMeta."
},
{
- "path": "openhcs/constants/constants.py",
- "sha256": "4cd6c29abd8b8e3ecc396b424d2b8b61e819a215fc1d770d3f6852b260a0691d",
- "role": "Declares the OpenHCS component enum families derived from the domain component configuration."
+ "path": "openhcs/domains/microscopy/axes.py",
+ "sha256": "531695809259324032e0b88632813323b4fe6a9a176ad6f636aeef8dec332399",
+ "role": "Declares the microscopy axis family, the OpenHCS domain declaration registered through metaclass-registry."
}
],
"findings": [
- "The legacy page treated processor metaprogramming as one extracted owner even though signature analysis and nominal registration belong to separate packages.",
- "python-introspect now also owns generic callable-declaration projection; the transition page now names that boundary.",
- "The unreferenced OpenHCS-local dynamic-interface registry contradicted the extracted ownership boundary and was removed."
+ "The constants.py authority no longer declares component enum families: G1 deleted them, and the domain declaration is now the microscopy AxisFamily in openhcs/domains/microscopy/axes.py. The redirect text itself names no deleted owner and stays accurate."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/component_processor_metaprogramming.rst against B6's later changes to openhcs/domains/microscopy/axes.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/component_processor_metaprogramming.rst against the later B6 changes to openhcs/domains/microscopy/axes.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/component_processor_metaprogramming.rst in full and compared it against the changed authorities: signature_analyzer.py, metaclass_registry/core.py, openhcs/constants/constants.py; confirmed signature analysis, callable-declaration projection and AutoRegisterMeta registration remain in the external packages, and replaced the deleted component-enum authority with the microscopy axis family declaration; no page edit was needed.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings.",
"On 2026-08-21, re-read the transition against signature_analyzer.py and callable_declaration.py after python-introspect 0.1.10.",
@@ -742,7 +840,7 @@
},
{
"path": "docs/source/architecture/component_system_integration.rst",
- "source_sha256": "5c4c60b9b9a02dfb0dd0f56ecc2bf4528e877314f14f76d0591ec112958e84c8",
+ "source_sha256": "28c20ffb0731f03ab08ed28bd2a431af411a8d30be02002bdfb3dcdc0931d0f4",
"audience": [
"OpenHCS compiler, source-model, and processing maintainers"
],
@@ -750,36 +848,44 @@
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/constants/constants.py",
- "sha256": "4cd6c29abd8b8e3ecc396b424d2b8b61e819a215fc1d770d3f6852b260a0691d",
- "role": "Declares the nominal component enum families and their distinct domains."
+ "path": "openhcs/core/axes.py",
+ "sha256": "8d93133639c576050f089fb8aab99937c8982aff07e742fea44f8a12e62bb7b2",
+ "role": "Declares Axis, AxisFamily and the PartitionAxis role whose single member partitions compiled contexts."
},
{
- "path": "openhcs/core/component_set.py",
- "sha256": "b3f7dbee802766aab3002256db044025abca8c4ce60be4648f7c1de7b13b41cc",
- "role": "Implements ordered enum-aware component set operations without copied name tables."
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares the step-level variable_components and group_by fields and the pipeline-level sequential_components field."
},
{
"path": "openhcs/core/components/component_values.py",
- "sha256": "3813d6f7d6d66fc21ea881e3dfcb72f54bd1bfe36212895f00be8f09e59d49dd",
- "role": "Carries one complete immutable value projection keyed by the canonical component declaration."
+ "sha256": "17d9e89c5b04eaef3f592efa9fd3677bdcec4b58b5f91092da2f1c1c64321a36",
+ "role": "Carries one complete immutable AxisValues projection keyed by the active family's axes."
},
{
"path": "openhcs/core/components/parser_metaprogramming.py",
- "sha256": "5eaf7c10d4d5667deb723c5b8d92410183ae06ce7b3092ddb71a88e9832d4b7e",
- "role": "Wraps complete component values in a nominal filename result and exposes string keys only at explicit boundaries."
+ "sha256": "e17a7df08e4c2506971c3039ed0ea3bc918c515d761fbeca54bc1efc156a51c3",
+ "role": "Wraps complete component values in FilenameParseResult and exposes string keys only at explicit boundaries."
+ },
+ {
+ "path": "openhcs/core/dataset_sources/interfaces.py",
+ "sha256": "dd88254bfd5da8b0fdf0a038e358f36b49db5b8cf0b93213d56dc928af965f00",
+ "role": "Defines MetadataComponentValueSet, the metadata-handler projection of complete axis values."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Consumes component declarations to construct execution axes, stacks, grouping, and typed plans."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Consumes the axis declarations to construct execution-axis contexts, stacks, grouping, and typed plans."
}
],
- "findings": [
- "The page did not explain how complete per-plate and per-filename component values cross internal boundaries without per-axis method or string-key mirrors."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/component_system_integration.rst in full and compared it against the changed authorities: openhcs/core/config.py, openhcs/core/pipeline/compiler.py; ProcessingConfig still declares variable_components and group_by and the pipeline config still declares sequential_components; the E1 changes to the materialization backend, input source and orchestrator state families touch nothing the page states, so no claim changed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/component_system_integration.rst in full and compared it against the changed authorities: openhcs/core/config.py (PartitionFilterMode became an IncludePartitions/ExcludePartitions class family; SourceBindingsConfig/StepSourceBindingsConfig moved in by K4) and openhcs/core/pipeline/compiler.py (ScopeKey scope ids, progress classes); the page names no filter mode or source-binding class, and variable_components, group_by, sequential_components, PartitionAxis, AxisValues, FilenameParseResult and MetadataComponentValueSet are unchanged, so no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/component_system_integration.rst against B6's later changes to openhcs/core/axes.py, openhcs/core/config.py, openhcs/core/dataset_sources/interfaces.py, openhcs/core/pipeline/compiler.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/component_system_integration.rst against the later B6 changes to openhcs/core/axes.py, openhcs/core/config.py, openhcs/core/dataset_sources/interfaces.py, openhcs/core/pipeline/compiler.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/component_system_integration.rst in full and compared it against the changed authorities: constants.py, component_set.py, component_values.py, parser_metaprogramming.py, compiler.py; verified AxisValues, FilenameParseResult, MetadataComponentValueSet, variable_components/group_by/sequential_components and AxisFamily.partition_axis in current source, and replaced the nonexistent multiprocessing_axis declaration with the family's PartitionAxis.",
"Re-read docs/source/architecture/component_system_integration.rst in full against repository base 32ca17c17 and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/constants/constants.py; openhcs/core/component_set.py; openhcs/core/pipeline/compiler.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -792,32 +898,47 @@
},
{
"path": "docs/source/architecture/component_validation_system.rst",
- "source_sha256": "c9d15356dc02939a216b2bf7bad60b43b2a244ce30d1eda4e61556c26b80ee02",
+ "source_sha256": "96dd35dc7c5ef2788463cf6f8ef6f085390aa4f5865391cc51eb5d794c4a0c20",
"audience": [
"OpenHCS configuration and compiler maintainers"
],
"user_need": "Look up the semantic component combinations rejected before runtime.",
"diataxis": "reference",
"authority": [
+ {
+ "path": "openhcs/core/axes.py",
+ "sha256": "8d93133639c576050f089fb8aab99937c8982aff07e742fea44f8a12e62bb7b2",
+ "role": "Defines AxisFamily.named decoding, variable_axes as every axis but the partition axis, and the Ungrouped declaration."
+ },
{
"path": "openhcs/core/components/validation.py",
- "sha256": "21ff922447de83ed3f62e54c25df7966419b4895be23a8a3c5d42a3981df554c",
- "role": "Defines shared enum conversion and component-combination validators."
+ "sha256": "3efbc509d3559a5bf3255312b652e6b8944427e2fa10fa7ca353753082099fe6",
+ "role": "Implements GenericValidator step checks and dictionary-pattern key checks against dataset values."
},
{
"path": "openhcs/core/pipeline/funcstep_contract_validator.py",
- "sha256": "73cb5250a5855625903f8b7deada667b83fba594d66de0ed4a68ec76afa3db19",
- "role": "Validates callable grouping, required axes, function-step components, and declared input, output, and execution memory."
+ "sha256": "d6144cff25772f57ede73d67635d0ed0fe2ffa1ec7d8b3913efbce2bd3eb2ed6",
+ "role": "Validates allowed group_by roles, required callable axis roles, and declared memory contracts per callable."
+ },
+ {
+ "path": "openhcs/core/pipeline/compiler.py",
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Rejects pipeline sequential axes that are also a step's variable axes."
},
{
"path": "tests/unit/test_funcstep_contract_validator.py",
- "sha256": "2188b297564b7c756af16f1138e304a1a7dadbdea28f7c440bc7b484d9eb615c",
- "role": "Covers accepted and rejected component, grouping, callable-contract, and execution-memory combinations."
+ "sha256": "fff3ca054398acd7794dbb1f2e70e9905933c9c8e438c5792a2627d60aa94eba",
+ "role": "Covers accepted and rejected grouping, required-role, and callable-contract combinations."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/component_validation_system.rst in full and compared it against the changed authorities: openhcs/core/pipeline/funcstep_contract_validator.py, openhcs/core/pipeline/compiler.py, tests/unit/test_funcstep_contract_validator.py; the validator now tests the plate-scoped main-input dependency by its NoMainFlowInput class and the test imports moved to the artifacts package; the grouping, required-role and sequential-axis checks the page lists are unchanged, so no claim changed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/component_validation_system.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py (ScopeKey scope ids, CompileProgressPhase/RunningProgressStatus progress classes, CompiledSourceBindingPlan() default) and tests/unit/test_funcstep_contract_validator.py (CallableDeclaration, admitted_graph); confirmed AxisFamily.named, variable_axes, Ungrouped, GenericValidator.validate_step/validate_dict_pattern_keys, validate_allowed_group_by, validate_required_axis_roles and validate_sequential_components_compatibility still exist and behave as stated; no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/component_validation_system.rst against B6's later changes to openhcs/core/axes.py, openhcs/core/pipeline/funcstep_contract_validator.py, openhcs/core/pipeline/compiler.py, tests/unit/test_funcstep_contract_validator.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/component_validation_system.rst against the later B6 changes to openhcs/core/axes.py, openhcs/core/pipeline/funcstep_contract_validator.py, openhcs/core/pipeline/compiler.py, tests/unit/test_funcstep_contract_validator.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/component_validation_system.rst in full and compared it against the changed authorities: validation.py, funcstep_contract_validator.py, test_funcstep_contract_validator.py; traced GenericValidator.validate_step, validate_dict_pattern_keys, validate_allowed_group_by, validate_required_axis_roles and the compiler's sequential-components check, and rewrote the check list and consumer guidance to match.",
"Re-read docs/source/architecture/component_validation_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/components/validation.py; openhcs/core/pipeline/funcstep_contract_validator.py; tests/unit/test_funcstep_contract_validator.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -827,7 +948,7 @@
},
{
"path": "docs/source/architecture/concurrency_model.rst",
- "source_sha256": "db244918e5b7ef7ba93ae17d1855954c630e577b56813f8ded29dafd4e7889bc",
+ "source_sha256": "2277aa8bbedf0052770b78bca2424ef45c580eeac121973ad39ba9807a14b0c9",
"audience": [
"OpenHCS runtime and multiprocessing maintainers"
],
@@ -836,51 +957,63 @@
"authority": [
{
"path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
- "role": "Defines the worker-safe CompiledExecutionBundle and its context-derived axis projection."
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "Defines the worker-safe CompiledExecutionBundle and its pickle-safe transport snapshot."
},
{
"path": "openhcs/core/context/processing_context.py",
- "sha256": "c7b79f50661c528261ce8c577fd67c753c84a5e499aa87cd5e540b53417351a2",
+ "sha256": "81c5c0bf411040bac2d93418093fff7c97c5ffd9ad942272c0f71c2982c96a44",
"role": "Owns the compiled multiprocessing-axis identity for one processing context."
},
{
- "path": "openhcs/core/orchestrator/worker_lanes.py",
- "sha256": "2e2a17b52e06f692c924554c387ed7aed9d25b3174c60e69d28bed6906f430b0",
- "role": "Groups runtime contexts into worker lanes from their declared axis identity."
+ "path": "openhcs/core/orchestrator/bundle_execution.py",
+ "sha256": "25446d6c6a43c22ff7104c6712c143f0276bc4749c2642fffac10db1455ef707",
+ "role": "Runs one compiled bundle, then the parent-owned dataset-scoped steps and post-execute hooks."
},
{
- "path": "tests/unit/test_orchestrator_lane_planning.py",
- "sha256": "09c39f5cba68f8c15a0def8a09843b62d6da5c5a01e7558a9263ce4d311c078d",
- "role": "Verifies that worker-lane grouping remains correct when context mapping keys do not encode axis identity."
+ "path": "openhcs/core/orchestrator/worker_lanes.py",
+ "sha256": "4273af5b358c7f4cf01a504bb151fdd0117fcc724cf121913cb9ebb5b7695115",
+ "role": "Defines WorkerLaneExecutionContext, the fork-inherited bundle state, and lane grouping from context-owned axis identity."
},
{
"path": "openhcs/core/orchestrator/worker_execution.py",
- "sha256": "3c952465268e4b6c44676a853bb9c99d2d04a0490c7d398d2cdaf8b3b5f966d7",
- "role": "Owns executor construction, submission, result collection, cancellation, and footprint-aware cleanup."
+ "sha256": "83ade0271c9b24ca1ba61c708d9e6f60a432b630ec6a8a2fac6d1018458e6be5",
+ "role": "Owns executor construction, the WorkerCommand family served by process workers, result collection, cancellation, and cleanup."
},
{
"path": "openhcs/core/orchestrator/cancellation.py",
"sha256": "f825b602fb020a23d602e1f93ed9cecea881eca16e07882fd7a91ab519445244",
- "role": "Owns the exact execution cancellation scope, pre-entry request handoff, and cooperative safe-boundary signal."
+ "role": "Owns the exact execution cancellation scope and cooperative safe-boundary signal."
},
{
"path": "openhcs/core/worker_start_policy.py",
"sha256": "9493c8729ddfc7457a3a58efb605457d5aa4a54cd42c3f4ef1bd9f28593d9475",
"role": "Owns multiprocessing start-method selection from the typed compiled GPU footprint."
},
+ {
+ "path": "tests/unit/test_orchestrator_lane_planning.py",
+ "sha256": "4af8c5e5a6d68a64f862a0bcb98f398a0a898948e6f8b5e462a4a11381f19524",
+ "role": "Verifies that worker-lane grouping remains correct when context mapping keys do not encode axis identity."
+ },
{
"path": "tests/unit/test_worker_start_policy.py",
- "sha256": "a3eaadb10898f10e50391310d65d338d60e9df2f4f6dec8b791be7dc75d31fc9",
+ "sha256": "5a6f5e1ef54c49bc7f49c8b7baa530e685a8b76daf37679488ec50d9fb5fc37c",
"role": "Verifies platform, server, override, and GPU start-method decisions."
+ },
+ {
+ "path": "openhcs/core/progress/types.py",
+ "sha256": "c46e825cd32801caae9fc107e0759976da054eadc56adc0d13354b3a8b7db209",
+ "role": "Declares the ProgressPhase and ProgressStatus class families, including AxisCompletedProgressPhase and SuccessProgressPhase."
}
],
- "findings": [
- "The cancellation explanation did not distinguish generic terminal status ownership, cooperative thread boundaries, and process-worker interruption.",
- "The execution-unit explanation left axis identity implicit and did not distinguish context-owned identity from context mapping keys."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/concurrency_model.rst in full and compared it against the changed authorities: openhcs/core/orchestrator/bundle_execution.py, worker_lanes.py, worker_execution.py and tests/unit/test_orchestrator_lane_planning.py (E1: OrchestratorState members became OrchestratorExecuting/Ready/ExecFailed/Completed classes, RuntimeObservationMode members became MergeAllRuntimeRecords/OmitRuntimeRecords classes, ViewerSettlePhase became settlement classes; K2: artifact imports from openhcs.core.artifacts submodules); the page names none of those members, and its bundle, lane, worker-command, start-method, progress-phase and cancellation claims still hold.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/concurrency_model.rst in full and compared it against the changed authorities: openhcs/core/orchestrator/bundle_execution.py (progress phase/status classes, lane results reordered to compiled axis order), openhcs/core/orchestrator/worker_execution.py (progress classes) and tests/unit/test_orchestrator_lane_planning.py; renamed the AXIS_COMPLETED and SUCCESS enum references to AxisCompletedProgressPhase and SuccessProgressPhase from openhcs/core/progress/types.py; the lane, worker-command, start-method and cancellation claims still hold.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/concurrency_model.rst against B6's later changes to openhcs/core/orchestrator/bundle_execution.py, openhcs/core/orchestrator/worker_execution.py, tests/unit/test_orchestrator_lane_planning.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/concurrency_model.rst against the later B6 changes to openhcs/core/orchestrator/bundle_execution.py, openhcs/core/orchestrator/worker_execution.py, tests/unit/test_orchestrator_lane_planning.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/concurrency_model.rst in full and compared it against the changed authorities: compiled_execution.py, processing_context.py, worker_lanes.py, test_orchestrator_lane_planning.py, worker_execution.py, test_worker_start_policy.py; confirmed CompiledExecutionBundle, WorkerLaneExecutionContext, resolve_worker_start_context, the cancellation signal and AXIS_COMPLETED, renamed compiled_plate_execution to bundle_execution, and corrected the lane, worker-command, progress and post-execute claims.",
"Re-read docs/source/architecture/concurrency_model.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/compiled_execution.py; openhcs/core/orchestrator/worker_execution.py; openhcs/core/orchestrator/cancellation.py; openhcs/core/worker_start_policy.py; tests/unit/test_worker_start_policy.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -902,25 +1035,30 @@
"authority": [
{
"path": "external/ObjectState/src/objectstate/lazy_factory.py",
- "sha256": "f81ce722695dbab98fd02658f2015e5cb93c1e1e69547d24249e62d896e2a828",
+ "sha256": "b9bd1547f5f78a6e15399006c085b1459f3172e1d31637233a93200846e07c37",
"role": "Implements reusable lazy dataclass configuration mechanics."
},
{
"path": "external/ObjectState/src/objectstate/context_manager.py",
- "sha256": "a01b0fc8aeb9b5b0888346f725f92cbfd96b90963bf20001b5414a5e6cd6c0e6",
+ "sha256": "412adb2bba3cf8f91a9c1e103ab1cebfd5d1d541a7d7787a3032d2ac8ab5cb9b",
"role": "Implements generic scoped context resolution."
},
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the OpenHCS global, pipeline, step, and standalone configuration hierarchy."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares GlobalPipelineConfig and the step and standalone configuration dataclasses OpenHCS composes over ObjectState."
}
],
"findings": [
- "The transition page did not route readers to the exact ObjectState owner narrative or the current OpenHCS system boundary."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/configuration_framework.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1: MaterializationBackend and InputSource became registered class families; K2: artifact import path); the stub states no configuration fact, and its targets (ObjectState docs/architecture.rst, system_overview, external_foundations) still exist.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/configuration_framework.rst in full and compared it against the changed authorities: external/ObjectState/src/objectstate/lazy_factory.py, external/ObjectState/src/objectstate/context_manager.py (current files) and openhcs/core/config.py (PartitionFilterMode became a class family, K4 source-binding declarations imported from openhcs.core.source_bindings); the redirect's only claims (ObjectState owns lazy configuration and resolution, OpenHCS owns its domain declarations such as GlobalPipelineConfig) still hold, and the linked ObjectState architecture page and system_overview exist.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/configuration_framework.rst against B6's later changes to openhcs/core/config.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/configuration_framework.rst against the later B6 changes to external/ObjectState/src/objectstate/lazy_factory.py, openhcs/core/config.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/configuration_framework.rst in full and compared it against the changed authorities: lazy_factory.py, context_manager.py, openhcs/core/config.py; confirmed ObjectState still owns lazy configuration and context resolution, the linked system_overview and external_foundations pages exist, and no page edit was needed.",
"Re-read docs/source/architecture/configuration_framework.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: external/ObjectState/src/objectstate/lazy_factory.py; external/ObjectState/src/objectstate/context_manager.py; openhcs/core/config.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -940,20 +1078,25 @@
"authority": [
{
"path": "external/ObjectState/src/objectstate/context_manager.py",
- "sha256": "a01b0fc8aeb9b5b0888346f725f92cbfd96b90963bf20001b5414a5e6cd6c0e6",
+ "sha256": "412adb2bba3cf8f91a9c1e103ab1cebfd5d1d541a7d7787a3032d2ac8ab5cb9b",
"role": "Implements reusable configuration-context registration and resolution."
},
{
"path": "openhcs/core/pipeline/compilation_session.py",
- "sha256": "3c840a89baccc8665d415175458280fd904c1a837b7fd879e303d7dbca129a1c",
- "role": "Consumes already resolved configuration in a scoped compiler session."
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "Consumes an already resolved GlobalPipelineConfig in a scoped compiler session."
}
],
"findings": [
- "The transition page did not link the exact ObjectState architecture owner or the current OpenHCS resolution path."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/context_system.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compilation_session.py (K2: ArtifactSpecRef imported from openhcs.core.artifacts.plans); the stub states no compiler fact, and its targets (ObjectState docs/architecture.rst, system_overview, external_foundations) still exist.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/context_system.rst in full and compared it against the changed authorities: external/ObjectState/src/objectstate/context_manager.py (current file) and openhcs/core/pipeline/compilation_session.py (invocation-declaration validation moved onto ArtifactGraph.validate_invocation_declarations, disabled-function stripping removed from step normalization); ObjectState still owns context registration and resolution and the compiler session still consumes a resolved GlobalPipelineConfig, so the redirect stands unchanged.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/context_system.rst against B6's later changes to openhcs/core/pipeline/compilation_session.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/context_system.rst against the later B6 changes to openhcs/core/pipeline/compilation_session.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/context_system.rst in full and compared it against the changed authorities: context_manager.py, compilation_session.py; confirmed ObjectState owns context management and the compiler session consumes a resolved GlobalPipelineConfig; no page edit was needed.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings."
]
@@ -999,7 +1142,7 @@
"authority": [
{
"path": "openhcs/processing/custom_functions/manager.py",
- "sha256": "aa5aa5a7032cd38c170d29d800175022883f04df7ce2d3f2fb4f6decad43f97d",
+ "sha256": "77612ea5ed53888c36e8ed31f763d8eaf5e0af9e4f32b4beaaa7cff09012b130",
"role": "Owns persisted custom-function lifecycle and projects validated declarations through the custom runtime registry."
},
{
@@ -1009,10 +1152,14 @@
}
],
"findings": [
- "The architecture URL no longer owns a mechanism and is intentionally a transition to the maintained user workflow."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/custom_function_registration_system.rst in full and compared it against the changed authorities: openhcs/processing/custom_functions/manager.py (E1: CustomFunctionLifetime.PERSISTED became the PersistedLifetime class); the stub names no lifetime, and its target user_guide/custom_function_management still exists.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/custom_function_registration_system.rst against B6's later changes to openhcs/processing/custom_functions/manager.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/custom_function_registration_system.rst against the later B6 changes to openhcs/processing/custom_functions/manager.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/custom_function_registration_system.rst in full and compared it against the changed authority manager.py; confirmed the user_guide/custom_function_management target exists and CustomFunctionManager still owns persisted custom functions through the runtime registry; no page edit was needed.",
"Re-read docs/source/architecture/custom_function_registration_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/processing/custom_functions/manager.py; openhcs/core/steps/function_step.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -1022,7 +1169,7 @@
},
{
"path": "docs/source/architecture/declarative_window_system.rst",
- "source_sha256": "480757c11ae720000f495ecfd073bc695a59f3ccf60a1c4069e0990a28828f96",
+ "source_sha256": "9dd05739e15cd02e259e4537e18c8f84485ccfc1440e0fa51500c516003785bd",
"audience": [
"OpenHCS UI maintainers following the former declarative-window URL"
],
@@ -1031,7 +1178,7 @@
"authority": [
{
"path": "external/pyqt-reactive/src/pyqt_reactive/services/window_manager.py",
- "sha256": "84fff75ff74e59c5d7080f103ad1a4baa2c884aff3d2f87891a0afdae9a5c332",
+ "sha256": "782472813aebe2b2ad7586db6ffb9809b68ac82a98d41f95ab9f25a22a7dbc21",
"role": "Implements live top-level window discovery plus generic registered window lifecycle, reuse, parentage, and cleanup."
},
{
@@ -1041,8 +1188,13 @@
},
{
"path": "openhcs/agent/ui_bridge_identities.py",
- "sha256": "5913d14b99340174af87b2a7cafa3cdd080a331553c0cf59392ade7d253f1234",
- "role": "Owns the nominal declarations for stable OpenHCS widget and window identities, presentation titles, and complete stable-window family projection."
+ "sha256": "66dcf838d08cac2004ab8f73db777f73270cc520697281cf4f94a5fc6af4dc64",
+ "role": "Owns the nominal stable widget and window identities, including session-view windows whose title comes from the rendered view."
+ },
+ {
+ "path": "openhcs/authoring/session/views.py",
+ "sha256": "296adbceb7db7a7f7ac3cb09dbe8bb6620dec588a3b593ef2239108298bee5c8",
+ "role": "Declares the SessionView family, each view's title, and the operations its window binds."
},
{
"path": "openhcs/pyqt_gui/services/ui_window_ids.py",
@@ -1051,15 +1203,19 @@
},
{
"path": "tests/unit/test_build_website.py",
- "sha256": "4a8952ba0f51590ffd9f1d787986653802506807079a7e0d118343264252e0e0",
- "role": "Verifies that the stable UI window reference derives its identities and titles from the nominal registry."
+ "sha256": "e88fc8ae73c5610dfb0a9948271a016e666451228ecade92aa1b2892da106059",
+ "role": "Verifies that the stable UI window reference derives its identities and titles from the identity registry."
}
],
- "findings": [
- "The legacy mechanism is extracted; the final page is correctly constrained to owner-package links and the OpenHCS integration boundary."
- ],
- "disposition": "redirect",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/declarative_window_system.rst in full and compared it against the changed authorities: openhcs/authoring/session/views.py (E1: debug-phase status prefix read from the phase class; orchestrator state, terminal status and debug phase projected by name); the dataset, pipeline and execution session views still exist and the page states nothing about those projections; the pyqt-reactive window-manager and scope-window-factory targets still exist.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/declarative_window_system.rst in full and compared it against the changed authority external/pyqt-reactive/src/pyqt_reactive/services/window_manager.py (current file): WindowManager still provides visible top-level window discovery, register/unregister, show_or_focus reuse by scope, focus_and_navigate, and close cleanup; the linked pyqt-reactive window_manager_usage and scope_window_factory pages exist; no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/declarative_window_system.rst against B6's later changes to openhcs/authoring/session/views.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/declarative_window_system.rst against B6's later changes to openhcs/authoring/session/views.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/declarative_window_system.rst against the later B6 changes to openhcs/authoring/session/views.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/declarative_window_system.rst in full and compared it against the changed authorities: ui_bridge_identities.py, test_build_website.py; confirmed the pyqt-reactive window-manager and scope-window-factory pages and sources exist, traced UiSessionViewWidgetIdentityDeclaration to SessionView.rendered_in, and revised the OpenHCS-retained ownership sentence.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings.",
"On 2026-08-28, re-read the complete redirect after stable window strings moved to nominal identity declarations; OpenHCS still owns product identities while pyqt-reactive owns generic lifecycle.",
@@ -1080,20 +1236,25 @@
"authority": [
{
"path": "openhcs/core/function_patterns.py",
- "sha256": "fe76286a2d378a39fd6061273363ccebbdc88f1dde6b1d36ed54b9cc28539e4a",
- "role": "Defines callable, sequential-chain, and dictionary-fan-out patterns and derives execution memory through nested declarations."
+ "sha256": "c8004ae1e3f241c4ac6ce0a61c69e3c0d1ecfb5021e1bef5dd0fb1fd1251ca50",
+ "role": "Defines callable, sequential-chain, and dictionary-fan-out patterns and their grouped source keys."
},
{
"path": "tests/unit/test_function_patterns.py",
- "sha256": "2b18e6f40f3f5e4a9673f954544b0113eba710675573fcda3fac6ef18d5eb725",
+ "sha256": "2eb64da61d0ae1ef2aedacddafc2f81b886515e9c500ac80b2cbcd49c48f5d9d",
"role": "Verifies dictionary fan-out, composition, and invalid pattern rejection."
}
],
"findings": [
- "The case-study URL is historical and now serves only as a stable route to the current conceptual owner."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/dict_pattern_case_study.rst in full and compared it against the changed authorities: openhcs/core/function_patterns.py and tests/unit/test_function_patterns.py (K2: artifact imports split across openhcs.core.artifacts plans, specs, kinds, relations and input_projection); the stub states no pattern fact, and its target concepts/function_patterns still exists.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/dict_pattern_case_study.rst in full and compared it against the changed authorities: openhcs/core/function_patterns.py (invocation-artifact imports and FunctionInvocationKey.from_callable removed, K5 callable-contract patterns) and tests/unit/test_function_patterns.py; dictionary fan-out keyed by group is still part of FunctionPatternSyntax and covered by test_invocation_positions_are_renumbered_per_dict_group, and concepts/function_patterns still exists, so the redirect stands.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/dict_pattern_case_study.rst against B6's later changes to openhcs/core/function_patterns.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/dict_pattern_case_study.rst against the later B6 changes to openhcs/core/function_patterns.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/dict_pattern_case_study.rst in full and compared it against the changed authorities: function_patterns.py, test_function_patterns.py; confirmed NormalizedFunctionPattern still carries grouped dictionary keys and the redirect target exists; no page edit was needed.",
"Re-read docs/source/architecture/dict_pattern_case_study.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/function_patterns.py; tests/unit/test_function_patterns.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -1112,7 +1273,7 @@
"authority": [
{
"path": "external/ObjectState/src/objectstate/lazy_factory.py",
- "sha256": "f81ce722695dbab98fd02658f2015e5cb93c1e1e69547d24249e62d896e2a828",
+ "sha256": "b9bd1547f5f78a6e15399006c085b1459f3172e1d31637233a93200846e07c37",
"role": "Implements dynamic lazy dataclass generation, validation, and resolution."
},
{
@@ -1122,17 +1283,20 @@
}
],
"findings": [
- "The transition page did not identify the exact ObjectState architecture owner or the current OpenHCS system route."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/dynamic_dataclass_factory.rst in full and compared it against the changed authority external/ObjectState/src/objectstate/lazy_factory.py (current file); lazy-dataclass generation and resolution remain in ObjectState and the linked pages exist, so the redirect stands.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/dynamic_dataclass_factory.rst against the later B6 changes to external/ObjectState/src/objectstate/lazy_factory.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/dynamic_dataclass_factory.rst in full and compared it against the changed authority external/ObjectState/src/objectstate/lazy_factory.py; confirmed ObjectState still owns lazy dataclass generation and the linked pages exist; no page edit was needed.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings."
]
},
{
"path": "docs/source/architecture/experimental_analysis_system.rst",
- "source_sha256": "9b143aff0b24ea2c3d9dbebe9f4789f764c3e81d1258747ee2fe33eed8e4e943",
+ "source_sha256": "b4454a8c8396ac2861f2b8bbb8d9918c00a9aca7a249b475e904b8aa42bf0363",
"audience": [
"OpenHCS experimental-analysis maintainers",
"desktop analysis integrators"
@@ -1141,37 +1305,47 @@
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares standalone ExperimentalAnalysisConfig options and all directory-workflow filenames."
+ "path": "openhcs/domains/microscopy/config.py",
+ "sha256": "79b8baefdbf22b85c199807f62b8234daca6904633a574886661a1c2e3d03a9b",
+ "role": "Declares standalone ExperimentalAnalysisConfig with all five directory-workflow filenames, outside the GlobalConfigSection family, and NormalizationMethod's member-owned calculations."
},
{
"path": "openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py",
- "sha256": "316be268b761bfa74ed1df137f2ba014a1f1c273aa9abd598a8a2e10388afb79",
- "role": "Coordinates the workbook-declared format strategy, replicate-local normalization, exports, and configuration-projected run_directory boundary."
+ "sha256": "782f462451aec3f98fc09956492cf4882531cdc41a459ea3c2a539353ed244b9",
+ "role": "Coordinates the workbook-declared format strategy, replicate-local normalization, exports, and the configuration-projected run_directory boundary."
+ },
+ {
+ "path": "openhcs/processing/backends/experimental_analysis/result_formats.py",
+ "sha256": "7581999983e5acc930733451dddc84f69bf5dfbb94cfd9954f35690b7a6dd885",
+ "role": "Defines the CX5 and MetaXpress ExperimentalResultFormatStrategy leaves, registered by the scope_name a workbook declares, for result reading and plate population."
},
{
- "path": "openhcs/formats/experimental_result_formats.py",
- "sha256": "ce46b895e4caafec7d026112cacfdfc496de0cb4cd5978cceb94db02b98fe288",
- "role": "Defines the scope-keyed CX5 and MetaXpress leaves that own result reading, feature extraction, and plate population."
+ "path": "openhcs/processing/backends/experimental_analysis/analysis.py",
+ "sha256": "96b29239552724475c9fb010248bb266340fa0bbf06ae890f6074b8620c6183b",
+ "role": "Owns layout parsing, exclusion projection, replicate averaging, and table/plate-grid export."
},
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
- "role": "Routes the desktop analysis action through ExperimentalAnalysisEngine rather than the deprecated wrapper."
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
+ "role": "Routes the desktop analysis action through ExperimentalAnalysisEngine.run_directory with the default configuration."
},
{
"path": "tests/unit/test_experimental_analysis_engine.py",
- "sha256": "23b5d73def871a4c1f1e60625faf56d4664a315f90ddbddff7fa1ceab2117c17",
- "role": "Verifies configured path projection, exclusions, normalization-method delegation, mirror deletion, and an end-to-end MetaXpress directory run."
+ "sha256": "641fadecf8c940cc2316a06771c74db5f3f2db9dafdfbfebdc535760d6d776ec",
+ "role": "Verifies configured path projection, exclusions, normalization-method delegation, and an end-to-end MetaXpress directory run."
}
],
"findings": [
- "The audit exposed a source-level mirror: the desktop hardcoded four filenames and called a deprecated wrapper; the owner was repaired and the page now describes the configuration-owned engine path.",
- "Source verification found three parallel CX5 and MetaXpress dispatch systems plus caller-owned normalization selection. They were collapsed into scope-keyed result-format leaves and NormalizationMethod member behavior before documenting the final boundary."
+ "E1 deleted the ExperimentalAnalysisScope enum; the page still said result readers were selected by the workbook's ExperimentalAnalysisScope declaration. Each ExperimentalResultFormatStrategy leaf now names the workbook scope it serves as scope_name."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/architecture/experimental_analysis_system.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/experimental_analysis_system.rst in full and compared it against the changed authorities: unified_analysis_engine.py, result_formats.py and analysis.py (E1: ExperimentalAnalysisScope deleted, formats resolved by ExperimentalResultFormatStrategy.for_scope over the scope_name registry) and openhcs/pyqt_gui/main.py (selection and update-check classes); replaced the ExperimentalAnalysisScope sentence with the scope_name selection, and confirmed NormalizationMethod, ExperimentalAnalysisConfig and the desktop ExperimentalAnalysisEngine call are unchanged.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/experimental_analysis_system.rst in full and compared it against the changed authority openhcs/pyqt_gui/main.py (U3 moved dock layout, shortcuts and time-travel navigation to pyqt-reactive); the desktop analysis action still constructs ExperimentalAnalysisEngine() with its default configuration and calls run_directory, and ExperimentalAnalysisConfig and NormalizationMethod are still declared in openhcs/domains/microscopy/config.py; no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/experimental_analysis_system.rst against B6's later changes to openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py, openhcs/processing/backends/experimental_analysis/result_formats.py, openhcs/processing/backends/experimental_analysis/analysis.py, openhcs/pyqt_gui/main.py, tests/unit/test_experimental_analysis_engine.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/experimental_analysis_system.rst against the later B6 changes to openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py, openhcs/processing/backends/experimental_analysis/result_formats.py, openhcs/processing/backends/experimental_analysis/analysis.py, openhcs/pyqt_gui/main.py, tests/unit/test_experimental_analysis_engine.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/experimental_analysis_system.rst in full and compared it against the changed authorities: core/config.py, unified_analysis_engine.py, the moved formats/experimental_result_formats.py, pyqt_gui/main.py, test_experimental_analysis_engine.py; traced ExperimentalAnalysisConfig and NormalizationMethod to domains/microscopy/config.py, the result-format strategies to result_formats.py and layout parsing to analysis.py, confirmed the desktop action calls ExperimentalAnalysisEngine().run_directory, and removed the stale deprecated-wrapper clause.",
"Re-read docs/source/architecture/experimental_analysis_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/config.py; openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py; openhcs/formats/experimental_result_formats.py; openhcs/pyqt_gui/main.py; tests/unit/test_experimental_analysis_engine.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -1188,7 +1362,7 @@
},
{
"path": "docs/source/architecture/external_foundations.rst",
- "source_sha256": "2c95e2864cf17a5a6195c2fc3c498ac99eb4cf450a841f7a8d2417a94d53f1cb",
+ "source_sha256": "d07de11d351f9e9920a8d56961e370a079266f547b2fbd877dd7a0fcf84590b3",
"audience": [
"OpenHCS maintainers",
"first-party package integrators"
@@ -1198,7 +1372,7 @@
"authority": [
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
"role": "Pins the eight first-party package constraints and documents their installed integration surface."
},
{
@@ -1213,12 +1387,12 @@
},
{
"path": "external/ObjectState/README.md",
- "sha256": "1d72de341fc17c96587a75bba4a913787321bf598b848474a833b34a0547842e",
+ "sha256": "e3c31ab46f9e4a754b9bfebe496a5c84a06461fe10c69a203fc04cff49dc56b5",
"role": "States ObjectState ownership of lazy state, contexts, provenance, and editing mechanics."
},
{
"path": "external/arraybridge/README.md",
- "sha256": "b108a144e2f797425a1734f745a73ac7a215bd5e58b3f6fe4342b18e7d16b892",
+ "sha256": "2fe1a330ca101dadb6cea5ac6f0482a5ffe8c7e0e4b9bd7bc8bc2220e106415a",
"role": "States ArrayBridge ownership of array conversion, memory mechanics, and callable input, output, and execution metadata."
},
{
@@ -1226,11 +1400,31 @@
"sha256": "0a8a5f1a61552b057997e94e4c89a19b5615875f307949e0d11784179f2a94f2",
"role": "Owns framework-neutral array geometry inspection without device-to-host conversion."
},
+ {
+ "path": "external/arraybridge/src/arraybridge/array_operations.py",
+ "sha256": "ea8c4e21ffbef5b5d502f9e5b7779bd5a341cec4408337d16ba89613f4305636",
+ "role": "Owns native array operations as one family leaf per MemoryType declaration."
+ },
+ {
+ "path": "external/arraybridge/src/arraybridge/types.py",
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
+ "role": "Owns declaration-derived child-process environments for optional array frameworks."
+ },
{
"path": "external/metaclass-registry/README.md",
- "sha256": "668dab2cc7c536778ca0010f957c85335eda5eb2392e60c62d08b11d79009c37",
+ "sha256": "d89a6b35231f21cb398d02b484b0fc1c3a6e72c29831ab19b32852d201f6988f",
"role": "States metaclass-registry ownership of reusable nominal registration and host-logging-safe discovery imports."
},
+ {
+ "path": "external/metaclass-registry/src/metaclass_registry/strategies.py",
+ "sha256": "5c00cd4d80f9f0ceffbc2ab95ad8c6abd44a8a66a974959445ef93df08dd4f0d",
+ "role": "Owns the shared registered-strategy and nominal selection mixins moved out of OpenHCS."
+ },
+ {
+ "path": "external/metaclass-registry/src/metaclass_registry/caches.py",
+ "sha256": "aa5681adb13a5424f7e8ee5b0fa601b23409f7093dc3e755c23c0b16e7a16e21",
+ "role": "Owns the bounded cache and the process-local cache family."
+ },
{
"path": "external/PolyStore/README.md",
"sha256": "c054ab7a02e743a089ea7e258444ca2982318b86af27457877cc60156a3831cf",
@@ -1243,28 +1437,38 @@
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/services/window_snapshot.py",
- "sha256": "c21e85912ab6715cc9c97bc796b9e5ed03610bf96f786d923557f18a876081f9",
+ "sha256": "bced886e6bbfd69ae9bdebf950027b809e72c23eddadcaa5fcf7151adf7ae913",
"role": "Owns safe Qt-rendered capture and optional bounded observations of actual flash-renderer frames."
},
- {
- "path": "external/pyqt-reactive/docs/architecture/window_snapshot.rst",
- "sha256": "76100ca0e5342138a0abe0b7e026939f33a06da9d9bff6edff3cc26f165c6378",
- "role": "Documents the generic capture scopes and the application integration boundary."
- },
{
"path": "external/python-introspect/README.md",
- "sha256": "d861f8aff2e79d9e77fbb2286b2f353c5b568412482a57efce9a488e58d543fe",
+ "sha256": "059dcfaafc0b2222e8e64b7fe072d24bddfb5a347a6f25113e715ed7946e44a1",
"role": "States python-introspect ownership of callable/signature analysis and signature-derived declaration projection."
},
+ {
+ "path": "external/python-introspect/src/python_introspect/jsonable.py",
+ "sha256": "c845361d0d14914798d9a1b5329d681300511fc7e95c67508943425716930ebd",
+ "role": "Owns the to_jsonable encoder paired with the dataclass_from_mapping decoder, registered_class_from_key and RegisteredClassChoices."
+ },
+ {
+ "path": "external/python-introspect/src/python_introspect/public_api.py",
+ "sha256": "f3093140a9a7c65ff8e94e3d0f3ab85d720eaa6ad840e0f44b4725f5b73e6059",
+ "role": "Owns declared public-name derivation and the lazy_exports helper."
+ },
{
"path": "external/zmqruntime/README.md",
- "sha256": "c8f7fa1b36636d6cbcee78bf259f2e2511ef7e19a5533ae246548167e35b3eb9",
+ "sha256": "03d1d715f46d2bd9c4b4b4142fd4622764c3109b2671801fae5163415e41718c",
"role": "States ZMQRuntime ownership of generic process and transport lifecycle."
},
{
"path": "external/zmqruntime/src/zmqruntime/client.py",
- "sha256": "fc8bdf68455673921d79742204bf6c88e42b52bbe407d10ef4a831e084ca86a8",
- "role": "Owns exact spawned endpoint-process adapters, retained exit observation, automatic child reaping, and owner groups without process rediscovery."
+ "sha256": "7d3cce31d5abaa070927f02fe87d919687619e2cf4adbc37ab82483b6b8fc212",
+ "role": "Owns exact spawned endpoint-process adapters, retained exit observation, automatic child reaping, and owner groups."
+ },
+ {
+ "path": "external/zmqruntime/src/zmqruntime/process_launch.py",
+ "sha256": "a9b913aee1c08967cc86fa7def39e19f2bac49c444c6c6db8d375d77e1a31b60",
+ "role": "Owns the per-platform background-process launch policy moved out of pyqt-reactive."
},
{
"path": "external/pycodify/README.md",
@@ -1272,24 +1476,40 @@
"role": "States pycodify ownership of Python-source serialisation and formatter composition."
},
{
- "path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
- "role": "Owns declaration-derived child-process environments for optional array frameworks."
+ "path": "external/ObjectState/src/objectstate/scope_key.py",
+ "sha256": "d0dd6e56cebf413d6869b07c7b9d8668ba954ed5aeead36c70288920a5b412b2",
+ "role": "Owns the ScopeKey scope grammar (child, parent, root, ancestors, containment) moved into ObjectState by U3."
+ },
+ {
+ "path": "external/ObjectState/src/objectstate/codegen.py",
+ "sha256": "347d2697c061a35e6e440e373fe4e8bfce1ad92b500e98da07faa26f6a3a1356",
+ "role": "Owns LazyResolutionFormatter and field_needs_source, the pycodify formatting of lazy configurations moved out of OpenHCS by L6."
+ },
+ {
+ "path": "external/pycodify/src/pycodify/core.py",
+ "sha256": "f5ae16c3bc62d2b2dc24f2701fb7fa936c6dd64fc602d3e64aabc8a38730ef18",
+ "role": "Owns imported_name, PythonSourceLiteral and PathRendering, the qualified-name, value-owned literal and path-factoring rules."
+ },
+ {
+ "path": "external/python-introspect/src/python_introspect/field_label.py",
+ "sha256": "8c2d9d94dd235181aee2097764cc7199948cde2b4de74e2a9bd5f58f062452b4",
+ "role": "Owns the FieldLabel annotation marker moved out of openhcs/core/field_label.py."
+ },
+ {
+ "path": "external/pyqt-reactive/src/pyqt_reactive/widgets/shared/dock_layout.py",
+ "sha256": "6643f7eb404ffc6eba0f7b57cf7d5ef1631efd089d56d9c905f2cf8a743ef13a",
+ "role": "Owns DockPane and DockLayoutStore, part of the generic Qt machinery (with editable_table, grid_selector, time_travel and config_window) that U3 moved into pyqt-reactive."
}
],
"findings": [
- "ArrayBridge 0.3 moved callable input, output, and execution memory metadata keys into its decorator contract; the catalogue now distinguishes package ownership from OpenHCS callable-contract projection and compiled resource planning.",
- "python-introspect 0.1.10 added signature-derived callable-declaration projection to its generic owner boundary.",
- "ArrayBridge 0.3.1 added framework-neutral array geometry so semantic consumers can inspect shape without implicitly transferring device arrays to the host.",
- "Qt-rendered snapshot capture moved to pyqt-reactive; OpenHCS retains window discovery, authorisation, and domain transport.",
- "Exact spawned endpoint-process ownership moved to ZMQRuntime; OpenHCS retains viewer persistence and application shutdown policy.",
- "ZMQRuntime 0.2.15 made each retained endpoint process observe and reap its own terminal exit without caller polling.",
- "ZMQRuntime 0.2.17 made owner-requested connection-attempt cancellation a typed generic result and disconnects a connection that wins a late readiness race.",
- "The package catalogue did not name ArrayBridge child-process environment ownership or metaclass-registry host-logging-safe discovery imports.",
- "The OMERO boundary assigned deployment to the alpha web application instead of the OpenHCS bundle and lifecycle owner."
+ "E1 put the registry-key spelling of registered class families and the RegisteredClassChoices field annotation into python-introspect's jsonable module; the python-introspect entry did not list them."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/external_foundations.rst in full and compared it against the changed authorities: pyproject.toml (python-introspect floor 0.2.8; still eight first-party packages) and external/python-introspect/src/python_introspect/jsonable.py (registered_class_from_key and RegisteredClassChoices added beside to_jsonable); added the registered-class spelling and choices to the python-introspect entry and confirmed dataclass_from_mapping still lives in python-introspect and the strategy mixins still live in metaclass-registry.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/external_foundations.rst in full and compared it against the changed authorities: pyproject.toml (pycodify 0.2, objectstate 2.1, pyqt-reactive 0.5, zmqruntime and python-introspect floors; still eight first-party packages), external/ObjectState/README.md (objectstate.codegen) and external/python-introspect/src/python_introspect/jsonable.py (to_jsonable unchanged), plus the U3 and L6 surface notes; added the moved ScopeKey, inspection, codegen, field-label, Qt machinery and pycodify rules to the catalogue and confirmed OpenHCS still keeps its domain formatters in openhcs/serialization/pycodify_formatters.py.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/external_foundations.rst against the later B6 changes to pyproject.toml: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/external_foundations.rst in full and compared it against the changed authorities: pyproject.toml (zmqruntime 0.6, polystore 0.6, objectstate 1.3.1, python-introspect 0.2.5, metaclass-registry 0.3.0, arraybridge 0.4.1, pyqt-reactive 0.4 bounds; still eight first-party packages), the package READMEs, window_snapshot.py, client.py and arraybridge types.py; added the L1, L2, L3 and L5 ownership moves to the catalogue and confirmed the PolyStore, OMERO and pyqt-reactive claims against current source.",
"Re-read docs/source/architecture/external_foundations.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: pyproject.toml; external/ObjectState/README.md; external/arraybridge/README.md; external/metaclass-registry/README.md; external/PolyStore/README.md; external/pyqt-reactive/README.md; external/python-introspect/README.md; external/zmqruntime/README.md; external/pycodify/README.md.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -1328,7 +1548,7 @@
},
{
"path": "docs/source/architecture/external_integrations_overview.rst",
- "source_sha256": "cd950b20705e1661d76f3c2ba9a66e68a37a88020228083327b77a9e40b688f1",
+ "source_sha256": "b2e0a00a4927035f23c2efa7900cf2f30ec968c0acc7cf7dded57adddbf76895",
"audience": [
"OpenHCS integrators",
"architecture reviewers"
@@ -1338,27 +1558,37 @@
"authority": [
{
"path": "openhcs/interop/cellprofiler/pipeline_import.py",
- "sha256": "b547c838b2c86fe7cc5fe12d6cbf6a6715e83055c2d8c1a109ac22566d22c51e",
- "role": "Implements CellProfiler lowering and declaration-owned headless validation before public OpenHCS pipeline declarations are emitted."
+ "sha256": "5aa8895c9f533603b7ef972202141e2439473f0604b97d66f5a027b8b3cd1c83",
+ "role": "Implements CellProfiler lowering to FunctionStep and PipelineConfig declarations with declaration-owned headless validation."
+ },
+ {
+ "path": "openhcs/interop/cellprofiler/pipeline_importer.py",
+ "sha256": "618cc6bce994d1c5d323a7397930afbe6e7b3c65f6ecc9da53a94e1a61ff6c63",
+ "role": "Registers the .cppipe suffix as a PipelineImporter family member from interop."
+ },
+ {
+ "path": "openhcs/core/pipeline_import.py",
+ "sha256": "b48f0a37e621f36fe256138472c4bda1e87d2d0d45be83361a40ce6cbfea4e55",
+ "role": "Defines the kernel's suffix-keyed PipelineImporter family."
},
{
"path": "openhcs/interop/cellprofiler/module_declarations.py",
- "sha256": "89dd65abc003c464ba6c489f52d3a4fa47be1d5f8b87d26ad72b7537f188cf8b",
+ "sha256": "6ff4f4bc3eaf6b4c9fc42752493c2b9ba2e11c8994d7edaea81f586ac57c3b89",
"role": "Defines the nominal module validation hook queried by the importer."
},
{
"path": "openhcs/interop/cellprofiler/module_artifact_declarations.py",
- "sha256": "666ec8c9adfd6a9015adf2b226df671534bb7e547cb0d76f1d16c40ac61e30a6",
+ "sha256": "5442b56c7dd7c913d38d18c5a76660ad6006e147ffc8f790712fb79b301ff035",
"role": "Defines the interactive-module family that rejects headless import."
},
{
- "path": "openhcs/microscopes/microscope_base.py",
- "sha256": "ae19ce4f6e1cd2e60946eceb908722e1ff760d7654a775d8372abbc4ac1fc81f",
- "role": "Defines registered microscope discovery and source-specific declaration ownership."
+ "path": "openhcs/core/dataset_sources/source.py",
+ "sha256": "b3758a878fdf8ec28b6d9818ebfd96c5c470895575f7a79500c85f1490679281",
+ "role": "Defines the registered DatasetSource family that microscope handlers implement."
},
{
"path": "external/PolyStore/src/polystore/base.py",
- "sha256": "ae641affae21b61448816a14bd68b7f2f3fc0082f0f1fbe51079ed32520450b1",
+ "sha256": "8f2082ade15072bc8e8eba363c75a1d1cff7c67c74845e6485f69eab244634dc",
"role": "Defines the generic storage backend boundary consumed by OpenHCS integrations."
},
{
@@ -1373,27 +1603,28 @@
},
{
"path": "external/zmqruntime/src/zmqruntime/execution/server.py",
- "sha256": "34e9e92c1a8a975a7a716d0c869b540e1ea8376c90cc71aa27d9614d08517a0d",
+ "sha256": "3afc85924853fdb245b2d7ed1e2d68e4a4262edcd84f36c355b9bfd9c4fad926",
"role": "Implements the generic remote-execution lifecycle kept outside OpenHCS domain code."
},
{
"path": "openhcs/runtime/zmq_server_hooks.py",
- "sha256": "b10f2a9a409ea1b97ad04ab4fa1fcea2fb41b9fbeaa43f7f31f0371be6ef484c",
- "role": "Maps generic targeted interruption onto the exact OpenHCS orchestrator and its owned workers."
+ "sha256": "c3027f3495c5ec188a0f9176db7fd0fcbb22451892248c9fd41d73347ac236dd",
+ "role": "Maps generic targeted cancellation onto the exact OpenHCS orchestrator and its owned workers."
},
{
"path": "tests/unit/test_cellprofiler_pipeline_import.py",
- "sha256": "353f0c1d8e0bc3552d2e418d792794ab0631daa16c78a3e10191b1cc1ea07d73",
+ "sha256": "67576f37b7cbe0595acea633265f8e14677ca3c1b8204630413330273dc4e58a",
"role": "Verifies explicit rejection of an interactive-only module during direct import."
}
],
- "findings": [
- "The overview named cancellation ownership but did not explain the generic terminal-state and OpenHCS cooperative-interruption boundary.",
- "The OMERO summary assigned deployment to the web plugin instead of the OpenHCS deployment bundle and lifecycle owner.",
- "The CellProfiler summary omitted the declaration-owned headless-import boundary for interactive-only modules."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/external_integrations_overview.rst in full and compared it against the changed authorities: openhcs/interop/cellprofiler/pipeline_import.py, module_declarations.py, module_artifact_declarations.py (E1: InputSource became ReadPipelineStart/ReadPreviousStep classes; K2 artifact import paths; K3 object-count feature naming), openhcs/core/dataset_sources/source.py (get_required_backend removed, absolute source paths) and tests/unit/test_cellprofiler_pipeline_import.py; the page names none of those members and its importer, DatasetSource, ZMQRuntime and OMERO ownership claims still hold.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/external_integrations_overview.rst in full and compared it against the changed authorities: openhcs/interop/cellprofiler/pipeline_import.py, module_declarations.py and module_artifact_declarations.py (K4 SourceBindingsConfig/StepSourceBindingsConfig now imported from openhcs.core.config; K5 contract fields read directly instead of through metadata), openhcs/core/dataset_sources/source.py (import move) and tests/unit/test_cellprofiler_pipeline_import.py (PairByOrder); the CppipeImporter is still a PipelineImporter family member, lowering still produces PipelineConfig plus FunctionStep declarations, interactive-only modules still fail, and DatasetSource remains the source family; no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/external_integrations_overview.rst against B6's later changes to openhcs/interop/cellprofiler/pipeline_import.py, openhcs/interop/cellprofiler/module_declarations.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/core/dataset_sources/source.py, openhcs/runtime/zmq_server_hooks.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/external_integrations_overview.rst against the later B6 changes to openhcs/interop/cellprofiler/pipeline_import.py, openhcs/interop/cellprofiler/module_declarations.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/core/dataset_sources/source.py, openhcs/runtime/zmq_server_hooks.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/external_integrations_overview.rst in full and compared it against the changed authorities: pipeline_import.py, module_declarations.py, module_artifact_declarations.py, the deleted microscope_base.py, polystore/base.py, zmqruntime execution/server.py, zmq_server_hooks.py, test_cellprofiler_pipeline_import.py; replaced MicroscopeHandler with the DatasetSource family, named the PipelineImporter registration, and confirmed the CellProfiler, OMERO and cancellation claims and every linked page.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings.",
"On 2026-08-25, re-read the complete explanation against the canonical Compose declaration, plugin compatibility owner, and PolyStore boundary; corrected OMERO deployment ownership.",
@@ -1445,25 +1676,26 @@
"authority": [
{
"path": "openhcs/runtime/fiji_stream_visualizer.py",
- "sha256": "436a64ca81b9552c6a97acc9eea12cd509c7d746c5647c737060224cf2ea2632",
- "role": "Implements the OpenHCS Fiji visualization adapter over compiled streaming declarations."
+ "sha256": "e2f24498a085bd5a3dc850b061386a0c0e3b0d3fc165413b5a9012b0825e47cc",
+ "role": "Implements the OpenHCS Fiji visualization adapter, keyed by the FijiViewer family member, over the shared managed-viewer lifecycle."
},
{
"path": "external/PolyStore/src/polystore/fiji_stream.py",
- "sha256": "62a60a36f1da5668f60bc0b4e3a5d0e65008042b2361787f4909224dd9b51db9",
+ "sha256": "b1bd2ff8fc047e84dd24691d5fce0e60b25cdab06063b5889a95acca1d5ba0c1",
"role": "Implements generic Fiji streaming storage mechanics."
},
{
"path": "external/zmqruntime/src/zmqruntime/viewer_protocol.py",
- "sha256": "5580efffddca2f723b11a652f482fbdb52ebae657620fad95217b313997b9fc9",
+ "sha256": "118829a406428e38f0c04a3a6f54103dc110f4a330f69264900f3a4bb55e62d7",
"role": "Defines the generic viewer-control transport protocol."
}
],
"findings": [
- "The former architecture page no longer owns streaming facts and correctly redirects to the current wrapper and package boundaries."
+ "The fiji_stream_visualizer.py change (FijiViewer family member replacing ViewerType.FIJI, start logic moved into the shared lifecycle) and the library changes do not affect the redirect; the page names no deleted owner and both linked pages exist."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/fiji_streaming_system.rst in full and compared it against the changed authorities: fiji_stream_visualizer.py, polystore/fiji_stream.py, zmqruntime/viewer_protocol.py; confirmed the redirect targets streaming_boundary_and_wrappers and external_foundations exist and the Fiji adapter still lives in OpenHCS over PolyStore and ZMQRuntime; no page edit was needed.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings.",
"2026-09-22 PR #126 PolyStore-release follow-up: re-read the complete page against the PolyStore 0.2.18 `fiji_stream.py` change, which removed the subclass destructor and now relies on the shared `StreamingBackend` teardown authority for idempotent, destructor-safe cleanup. The page is a redirect stub with no subclass destructor or Fiji-internal lifecycle prose, so its reference disposition and navigation remain accurate. Refreshed only this compared evidence hash; details are retained in `docs/audits/pr126_authority_delta_review_20260922.md`."
@@ -1480,8 +1712,8 @@
"authority": [
{
"path": "external/pyqt-reactive/src/pyqt_reactive/animation/flash_mixin.py",
- "sha256": "37cd2b32047109fc16255a3f9a9938ae553d5f8bd5987cd10460c7ba500f1486",
- "role": "Implements reusable flash propagation, widget-geometry masking, paint acknowledgement, and animation coordination."
+ "sha256": "53e77aa4d0a40285dc33b1a7a533e0cdef9985342eebc1fca07ebe05b9e51c3c",
+ "role": "Implements reusable flash propagation, widget-geometry masking, label padding derived from animation config, and paint-acknowledged animation coordination."
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/animation/flash_config.py",
@@ -1490,15 +1722,17 @@
},
{
"path": "external/pyqt-reactive/tests/test_flash_destruction_lifetime.py",
- "sha256": "a567e8b978182773d316e9a7dc7934b80da2708e2804bd8e84898b4b1a030b37",
- "role": "Verifies Qt-owned cleanup receivers survive native sender destruction and Python garbage collection."
+ "sha256": "3d7f50d0677cc7deee95f879e9a5007dc7551453449f8299dc585499cea86cf3",
+ "role": "Verifies Qt-owned cleanup survives cycle collection and that a pending flash registration ends with the widgets it closes over."
}
],
"findings": [
- "The compatibility page lacked the exact owner-page link and pointed twice at the package-level boundary."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/flash_animation_system.rst in full and compared it against the changed authority external/pyqt-reactive/src/pyqt_reactive/animation/flash_mixin.py (current file); the flash mechanism stays in pyqt-reactive, its architecture/flash_animation_system page exists, and the OpenHCS boundary links still resolve, so the redirect stands.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/flash_animation_system.rst in full and compared it against the changed authorities: external/pyqt-reactive/src/pyqt_reactive/animation/flash_mixin.py and external/pyqt-reactive/tests/test_flash_destruction_lifetime.py; confirmed the pyqt-reactive flash page and the ui_services_architecture and external_foundations targets still exist and updated the evidence roles; the page text needed no change.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings.",
"On 2026-09-06, re-read the complete redirect and canonical pyqt-reactive flash page against the native widget raster surface, shared mask projection, resolved-change notification ownership and unchanged FlashConfig timing. The redirect still points to the package that owns these generic mechanisms. Native RGB/BGR coverage passed 240 checks across 100%, 125% and 150% scaling; 460 reactive tests, nine OpenHCS form integration tests and five real MCP reset/edit checks passed. Reactive main CI 34044272928 passed every platform job.",
@@ -1517,20 +1751,25 @@
"authority": [
{
"path": "openhcs/core/function_patterns.py",
- "sha256": "fe76286a2d378a39fd6061273363ccebbdc88f1dde6b1d36ed54b9cc28539e4a",
- "role": "Defines callable, sequential-chain, and dictionary-fan-out patterns and derives execution memory through nested declarations."
+ "sha256": "c8004ae1e3f241c4ac6ce0a61c69e3c0d1ecfb5021e1bef5dd0fb1fd1251ca50",
+ "role": "Normalizes callable, (callable, kwargs), list-chain, and dict patterns into named invocations and compiles them with their memory and artifact plans."
},
{
"path": "tests/unit/test_function_patterns.py",
- "sha256": "2b18e6f40f3f5e4a9673f954544b0113eba710675573fcda3fac6ef18d5eb725",
- "role": "Verifies composition, flattening, validation, and dictionary pattern semantics."
+ "sha256": "2eb64da61d0ae1ef2aedacddafc2f81b886515e9c500ac80b2cbcd49c48f5d9d",
+ "role": "Verifies invocation identity for list and dict patterns, normalization, tuple-leaf validation, and compiled pattern semantics."
}
],
"findings": [
- "The former architecture URL is retained only as a stable transition to the current concept page."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/function_pattern_system.rst in full and compared it against the changed authorities: openhcs/core/function_patterns.py and tests/unit/test_function_patterns.py (K2: artifact imports split across openhcs.core.artifacts submodules); the stub states no pattern fact, and its target concepts/function_patterns still exists.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/function_pattern_system.rst in full and compared it against the changed authorities: openhcs/core/function_patterns.py (K5: invocation-artifact imports and FunctionInvocationKey.from_callable removed, compile_function_pattern takes an admitted artifact graph) and tests/unit/test_function_patterns.py; normalize_function_pattern and compile_function_pattern still own callable, chain and dict pattern semantics and concepts/function_patterns exists, so the redirect stands.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/function_pattern_system.rst against B6's later changes to openhcs/core/function_patterns.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/function_pattern_system.rst against the later B6 changes to openhcs/core/function_patterns.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/function_pattern_system.rst in full and compared it against the changed authorities: openhcs/core/function_patterns.py and tests/unit/test_function_patterns.py; confirmed the module still accepts callable, (callable, kwargs), list and dict patterns and that docs/source/concepts/function_patterns.rst exists; the page text needed no change.",
"Re-read docs/source/architecture/function_pattern_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/function_patterns.py; tests/unit/test_function_patterns.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -1549,31 +1788,35 @@
"authority": [
{
"path": "external/python-introspect/src/python_introspect/signature_analyzer.py",
- "sha256": "73600e546753640964d4ba5308fdf808ed3a4a6f085417d8ac0745d9e2748ed7",
- "role": "Implements generic wrapped-target and parameter signature analysis."
+ "sha256": "edbe51f6c92b2c334dc4533bbeaa252be649c1ac738d2ebfca4364f2708479dc",
+ "role": "Implements generic wrapped-target and parameter signature analysis used by the OpenHCS editors."
},
{
"path": "external/python-introspect/src/python_introspect/callable_declaration.py",
"sha256": "3bb8b474624fe6b2964a5c1f9a6aa540b2703500bfa124e7808995cb789d6951",
- "role": "Orders supplied parameters by signature with optional default omission and caller-owned equality."
+ "role": "Provides callable_declaration_kwargs, which FunctionStep and the code serializer use to order supplied parameters by signature."
},
{
"path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
- "role": "Adds OpenHCS processing, runtime, artifact, scope, and ArrayBridge memory semantics to analysed callables."
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "Defines CallableDeclaration and CallableContract, which add OpenHCS processing, runtime, artifact, scope, and ArrayBridge memory semantics on top of python-introspect annotation helpers."
},
{
"path": "tests/unit/test_callable_contract.py",
- "sha256": "493f76a54f5fe024ca8f10c53b1f810b8c369626ad7141205b42ef3a78282137",
- "role": "Verifies OpenHCS callable contracts projected from declaration-owned metadata, including execution memory."
+ "sha256": "9170f8694f388dbed5bfd36d882a5c0a4cdf28f50f51f7fdeaf4a955a9f3b412",
+ "role": "Verifies OpenHCS callable contracts projected from declared callable fields, including execution memory and runtime batching."
}
],
"findings": [
- "The transition page did not separate the extracted signature-analysis owner from OpenHCS domain callable semantics.",
- "The extracted owner now also projects callable declarations from signature defaults."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/function_reference_pattern.rst in full and compared it against the changed authorities: openhcs/core/callable_contract.py (K2: artifact imports from openhcs.core.artifacts plans, specs and kinds; artifact type spelled by value); the stub states no callable-contract fact, and its targets processing_semantics and external_foundations still exist.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/function_reference_pattern.rst in full and compared it against the changed authorities: openhcs/core/callable_contract.py (K5: CallableMetadata replaced by CallableDeclaration, which CallableContract now subclasses) and tests/unit/test_callable_contract.py; generic signature analysis and callable_declaration_kwargs still come from python-introspect and OpenHCS callable semantics stay in callable_contract, so the redirect stands; refreshed the callable_contract and test evidence roles.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/function_reference_pattern.rst against B6's later changes to openhcs/core/callable_contract.py, tests/unit/test_callable_contract.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/function_reference_pattern.rst against the later B6 changes to openhcs/core/callable_contract.py, tests/unit/test_callable_contract.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/function_reference_pattern.rst in full and compared it against the changed authorities: external/python-introspect/src/python_introspect/signature_analyzer.py, openhcs/core/callable_contract.py and tests/unit/test_callable_contract.py; grepped OpenHCS imports of python_introspect and confirmed docs/source/architecture/processing_semantics.rst exists; the page text needed no change.",
"Re-read docs/source/architecture/function_reference_pattern.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: external/python-introspect/src/python_introspect/signature_analyzer.py; openhcs/core/callable_contract.py; tests/unit/test_callable_contract.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -1599,20 +1842,21 @@
"authority": [
{
"path": "external/metaclass-registry/src/metaclass_registry/core.py",
- "sha256": "ed4dd1dfd0e4e05e916fec39f19b4b8bd526e776aa0c96e3abe9b1ffbdff0505",
+ "sha256": "72c8284e8a32dd193f13f36a425289d5293a32f4a381046db523aa87182e0be8",
"role": "Implements AutoRegisterMeta, RegistryConfig, families, and generic registry ownership."
},
{
- "path": "openhcs/core/registry_strategies.py",
- "sha256": "a41186665abbd308f224d0bb41d6618cd9d89b08acaf54ec2819f2024a6e0804",
- "role": "Defines OpenHCS generic selection over registered domain declarations."
+ "path": "external/metaclass-registry/src/metaclass_registry/strategies.py",
+ "sha256": "5c00cd4d80f9f0ceffbc2ab95ad8c6abd44a8a66a974959445ef93df08dd4f0d",
+ "role": "Provides the selection strategy mixins that OpenHCS declaration families import directly since the OpenHCS strategy module was deleted."
}
],
"findings": [
- "The transition page did not link the current metaclass-registry owner inventory directly."
+ "The listed authority openhcs/core/registry_strategies.py was deleted (b221f8491); its strategy mixins now live in metaclass_registry.strategies, which strengthens the page's claim that metaclass-registry owns the reusable mechanism. Authority replaced; page text unchanged."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/function_registry_system.rst in full and compared it against the changed authorities: external/metaclass-registry/src/metaclass_registry/core.py and the deleted openhcs/core/registry_strategies.py (moved to metaclass_registry.strategies and caches in b221f8491); confirmed no OpenHCS importer re-exports the old module and the nominal_ownership and external_foundations targets exist; the page text needed no change.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings.",
"On 2026-08-25, re-read the complete page against metaclass-registry 0.2.1, ArrayBridge 0.3.3, the public native-catalogue projection, declaration-derived child environments, and the updated package floors; revised the page where those semantics are user-relevant and confirmed its remaining claims against current source and tests."
@@ -1620,7 +1864,7 @@
},
{
"path": "docs/source/architecture/gpu_resource_management.rst",
- "source_sha256": "998a32e50c5ed6c59b44f3f7b0fc686b19fe619c63b459e0be7b273c555bc637",
+ "source_sha256": "812a3f6e083891191e39168946919f3633f4b5f80ea66de5fe65b40483c68ef9",
"audience": [
"OpenHCS compiler and runtime maintainers",
"GPU integration authors"
@@ -1630,48 +1874,53 @@
"authority": [
{
"path": "external/arraybridge/src/arraybridge/decorators.py",
- "sha256": "99823105b0ae59054df1d84013633b8b6628d0a6dc3326383199ac7c65c69bed",
+ "sha256": "28f9bf4770bd4cc1ef34045fdef28065063ede029296f62b8a6c4140cc88e40e",
"role": "Owns callable input, output, and execution memory metadata keys and their decorator projection."
},
{
"path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
- "role": "Declares framework capabilities, callable memory metadata, and the child-process import environment owned by each MemoryType member."
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
+ "role": "Declares MemoryContractAttribute keys and each MemoryType member's import policy, device discovery, device scope, allocator cleanup, and subprocess_environment projection."
},
{
"path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
"role": "Projects declaration-owned input, output, and execution memory into immutable callable contracts."
},
{
"path": "openhcs/core/framework_device_resolver.py",
"sha256": "be96430174bc576c0b8083879c579ef3fb9b83c4690234751459c2d7bc5556a8",
- "role": "Resolves each required GPU framework through its ArrayBridge declaration and rejects CPU-only requests before imports."
+ "role": "Resolves each required GPU framework through its ArrayBridge declaration and rejects requests while GPU imports are disabled, before any framework import."
},
{
"path": "openhcs/core/pipeline/framework_device_assignment.py",
- "sha256": "f77f3fb2d30c4af8cab6210dbe294f9fb5e246dad0a6bbf88bcaf1e2b58dead7",
- "role": "Resolves the pipeline-wide framework footprint and projects exact bindings onto each step plan."
+ "sha256": "cdfde10f996ab682952a1d4948d3de2d23d48353eae41f3124770e19fc28a193",
+ "role": "Resolves the pipeline-wide framework footprint and projects exact bindings onto each step plan and compiled invocation."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Invokes callable validation and framework-device assignment before assembling the execution bundle."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Calls assign_framework_devices on each context's step plans before bundle assembly."
},
{
"path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
- "role": "Owns framework-local device bindings, derived GPU footprints, execution scopes, and cleanup requests."
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "Owns FrameworkDeviceAssignment bindings, merged cleanup footprints, and the requires_gpu projection."
},
{
"path": "openhcs/core/orchestrator/worker_execution.py",
- "sha256": "3c952465268e4b6c44676a853bb9c99d2d04a0490c7d398d2cdaf8b3b5f966d7",
- "role": "Consumes compiled footprints for worker execution and lane-appropriate framework cleanup."
+ "sha256": "83ade0271c9b24ca1ba61c708d9e6f60a432b630ec6a8a2fac6d1018458e6be5",
+ "role": "Releases each axis's compiled footprint after process and inline contexts and the merged footprint only after thread-backed lanes join."
},
{
"path": "openhcs/core/worker_start_policy.py",
"sha256": "9493c8729ddfc7457a3a58efb605457d5aa4a54cd42c3f4ef1bd9f28593d9475",
- "role": "Projects the compiled GPU footprint into CUDA-safe multiprocessing start decisions."
+ "role": "Projects WorkerStartExecutionFacts into one start decision that requires spawn for GPU execution unless unsafe fork is explicit."
+ },
+ {
+ "path": "openhcs/utils/environment.py",
+ "sha256": "1985ffbe42b5dfb283b78b6282f9b56e08cd51d5a9e79f2dd4eebffcb6da0bf5",
+ "role": "Owns OPENHCS_CPU_ONLY and the CPU-only mode that --no-gpu enables before framework imports."
},
{
"path": "tests/unit/test_framework_device_resolver.py",
@@ -1680,22 +1929,23 @@
},
{
"path": "tests/unit/test_framework_execution_scope.py",
- "sha256": "888a7693d387b0d5007c9868f0b0344fa3df26620f35e40b45832f0931ef7126",
+ "sha256": "b61d2df837d570f99031b29cadf75d2ac7381617cdd845b2fb67914eb5015a3f",
"role": "Verifies execution-memory device scopes and exact per-lane allocator cleanup."
},
{
"path": "tests/unit/test_worker_start_policy.py",
- "sha256": "a3eaadb10898f10e50391310d65d338d60e9df2f4f6dec8b791be7dc75d31fc9",
+ "sha256": "5a6f5e1ef54c49bc7f49c8b7baa530e685a8b76daf37679488ec50d9fb5fc37c",
"role": "Verifies GPU-sensitive worker start policy and explicit unsafe overrides."
}
],
- "findings": [
- "The former process-level GPU scheduler and GPU-memory validator were deleted; capability now descends from ArrayBridge declarations into a pipeline-wide framework assignment and exact per-step bindings, including execution memory.",
- "Worker execution now consumes the compiled framework/device footprint for device scopes and cleanup instead of a scalar GPU identifier or copied framework registry.",
- "The explanation did not state that MemoryType declarations project framework import requirements into fresh catalogue and execution-server processes."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/gpu_resource_management.rst in full and compared it against the changed authorities: openhcs/core/callable_contract.py and compiled_step_plan.py (K2 artifact import paths, UnresolvedStepInput default), openhcs/core/pipeline/compiler.py (E1: orchestrator state and MaterializeToZarr classes, PrimaryPlaneRole), openhcs/core/orchestrator/worker_execution.py (runtime observation mode classes) and openhcs/utils/environment.py (AgentRuntimePlatform rename); none touches the memory-declaration, device-assignment, worker-start or cleanup claims, which still hold.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/gpu_resource_management.rst in full and compared it against the changed authorities: openhcs/core/callable_contract.py (K5: input, output and execution memory are now declared CallableDeclaration fields read from the same ArrayBridge MemoryContractAttribute keys; declared_memory_types unchanged), openhcs/core/pipeline/compiler.py (ScopeKey scope ids, progress classes), openhcs/core/compiled_step_plan.py (source plan default constructors), openhcs/core/orchestrator/worker_execution.py (progress classes) and tests/unit/test_framework_execution_scope.py (admitted_graph); FrameworkDeviceResolver, WorkerStartExecutionFacts, MemoryType.subprocess_environment and per-lane cleanup are unchanged; no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/gpu_resource_management.rst against B6's later changes to openhcs/core/callable_contract.py, openhcs/core/pipeline/compiler.py, openhcs/core/compiled_step_plan.py, openhcs/core/orchestrator/worker_execution.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/gpu_resource_management.rst against the later B6 changes to openhcs/core/callable_contract.py, openhcs/core/pipeline/compiler.py, openhcs/core/compiled_step_plan.py, openhcs/core/orchestrator/worker_execution.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/gpu_resource_management.rst in full and compared it against the changed authorities: arraybridge decorators.py and types.py, openhcs/core/callable_contract.py, openhcs/core/pipeline/framework_device_assignment.py, openhcs/core/pipeline/compiler.py, openhcs/core/compiled_step_plan.py, openhcs/core/orchestrator/worker_execution.py and the worker-start and execution-scope tests; traced assign_framework_devices from the compiler, FrameworkDeviceResolver's gpu_imports_disabled check, MemoryType.subprocess_environment() callers, _release_runtime_resources per axis and after threaded lanes, and resolve_worker_start_context; replaced one jargon phrase and refreshed evidence roles.",
"Re-read docs/source/architecture/gpu_resource_management.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: external/arraybridge/src/arraybridge/decorators.py; external/arraybridge/src/arraybridge/types.py; openhcs/core/callable_contract.py; openhcs/core/framework_device_resolver.py; openhcs/core/pipeline/framework_device_assignment.py; openhcs/core/pipeline/compiler.py; openhcs/core/compiled_step_plan.py; openhcs/core/orchestrator/worker_execution.py; openhcs/core/worker_start_policy.py; tests/unit/test_framework_device_resolver.py; tests/unit/test_framework_execution_scope.py; tests/unit/test_worker_start_policy.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -1756,27 +2006,40 @@
"sha256": "8e1243404095691030e9ebb95bb5611679f96c66c6b6819ae71e4b41249e74bc",
"role": "Defines the public FunctionStep declaration named by the architecture invariants."
},
+ {
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares GlobalPipelineConfig and the step variable_components and group_by fields, with group_by defaulting from the active AxisFamily."
+ },
+ {
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "Defines the ProcessingContract family named as an independent declaration in the invariants."
+ },
{
"path": "openhcs/core/pipeline/compilation_session.py",
- "sha256": "3c840a89baccc8665d415175458280fd904c1a837b7fd879e303d7dbca129a1c",
- "role": "Defines the scoped compiler authority named by the navigation map."
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "Defines CompilationSession, the scoped compiler state named by the invariants."
},
{
"path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
- "role": "Defines typed compiled plan ownership, including declaration-derived framework footprints and exact device bindings."
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "Defines typed CompiledStepPlan fields, including framework footprints and device bindings."
},
{
"path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
- "role": "Defines the worker-safe runtime bundle boundary without a process-global GPU registry."
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "Defines the CompiledExecutionBundle that runtime workers consume."
}
],
- "findings": [
- "The landing page cited an old documentation plan as a completed-disposition authority; active navigation now stands on current pages and source boundaries only."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/index.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1: MaterializationBackend and InputSource families), openhcs/core/pipeline/compilation_session.py and openhcs/core/compiled_step_plan.py (K2 artifact import paths, UnresolvedStepInput); every toctree target still exists and the architecture invariants (PipelineConfig plus FunctionStep, CompilationSession, CompiledStepPlan, ProcessingContract, CompiledExecutionBundle) still hold.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/index.rst in full and compared it against the changed authorities: openhcs/core/config.py (PartitionFilterMode family, K4 source-binding declarations), openhcs/core/processing_contracts.py (CallableMetadata renamed CallableDeclaration), openhcs/core/pipeline/compilation_session.py (graph-owned invocation validation) and openhcs/core/compiled_step_plan.py (source plan default constructors); the invariants (PipelineConfig plus FunctionStep, CompilationSession and typed CompiledStepPlan, separate variable_components, group_by and ProcessingContract declarations, CompiledExecutionBundle for workers) still hold; no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/index.rst against B6's later changes to openhcs/core/config.py, openhcs/core/processing_contracts.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/index.rst against the later B6 changes to openhcs/core/config.py, openhcs/core/processing_contracts.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/index.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py and openhcs/core/compiled_execution.py; verified each architecture invariant against the current declarations and that every toctree target file exists; the page text needed no change.",
"Re-read docs/source/architecture/index.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/steps/function_step.py; openhcs/core/pipeline/compilation_session.py; openhcs/core/compiled_step_plan.py; openhcs/core/compiled_execution.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -1786,7 +2049,7 @@
},
{
"path": "docs/source/architecture/list_item_preview_system.rst",
- "source_sha256": "59ec9b7019bcc8baae04e3cded674beb98cc808c040fd82f47852b253467d5ec",
+ "source_sha256": "a1ddaf8e8135bea0564aa5e85713f6abe254447dacb24bf4dc586858f48e0782",
"audience": [
"OpenHCS UI maintainers following the extracted list-preview URL"
],
@@ -1796,7 +2059,7 @@
{
"path": "external/pyqt-reactive/src/pyqt_reactive/strategies/preview_formatting.py",
"sha256": "dc1a423246ca40f13633847d5d3ffde3916e53c1a8a8a92de893d3e80ad8fda7",
- "role": "Owns preview detail, collection bounds, and wrapping declarations."
+ "role": "Owns FormattingConfig preview detail, collection bounds, and wrapping declarations."
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/protocols/preview_formatter.py",
@@ -1805,33 +2068,33 @@
},
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the WellFilterConfig family preview policy and WellFilterMode member operators."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares PartitionFilterConfig's show_preview_without_partition_filter policy and PartitionFilterMode members' preview_prefix operators."
},
{
"path": "openhcs/pyqt_gui/widgets/config_preview_formatters.py",
- "sha256": "67b2cbdddc7ef1bced7d549ce5bd29ed7222cacb4d8ea255476ec36f5cab6f62",
- "role": "Projects OpenHCS well-filter declarations through the generic preview API."
+ "sha256": "490bd6f32183c44028ae06a4a5ca092dd38b44c198ae623d232dc62c5f502183",
+ "role": "format_partition_filter_config projects the partition-filter declarations through the generic preview label API."
},
{
"path": "openhcs/pyqt_gui/services/reactor_providers.py",
- "sha256": "e937c310e6dd7f06bf8e2cfc227ce442310f6d249fc0abed4ed0e588b4121fe8",
- "role": "Registers the OpenHCS domain formatter with the generic preview service."
+ "sha256": "ca5fe97ba932805dd0f8cfa3bc85352cb428b12a46fcae83751382092d77475c",
+ "role": "Registers format_partition_filter_config for PartitionFilterConfig with the generic preview service."
},
{
"path": "openhcs/pyqt_gui/config.py",
- "sha256": "a3eece2e5b2926fc6fc0edd197221da451ab7c0d0d6f4ffc8bb2ff865ad74bd4",
- "role": "Embeds the persisted generic list-preview policy in UIConfig."
+ "sha256": "d092cb20206127484fbbc80a38a5152462b9b8991e848a814c85f564d51f4db8",
+ "role": "Embeds FormattingConfig as UIConfig.list_previews."
},
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
- "role": "Applies the same preview policy to both managers at startup and after configuration save."
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
+ "role": "_apply_list_preview_config applies list_previews to Plate Manager and Pipeline Editor at startup and on UI config change."
},
{
"path": "tests/unit/pyqt_gui/test_main_config_propagation.py",
- "sha256": "291dc45ca20ead6025c255a8109a2e6c55d90bd7858e80ad12484dafa733d7c5",
- "role": "Verifies live preview policy propagation through the main configuration lifecycle."
+ "sha256": "303d90b43bee7bd531a1c7664fbf9707a2e7be1696a5147614f93c40e5c7021f",
+ "role": "Verifies the list preview config reaches both real lists."
},
{
"path": "tests/unit/pyqt_gui/test_source_binding_preview_policy.py",
@@ -1841,7 +2104,7 @@
{
"path": "external/pyqt-reactive/src/pyqt_reactive/widgets/shared/list_item_delegate.py",
"sha256": "282c8fe0c88f9bfd51df7eb95c739a8d428d2e9d99478796bb073c698f5907a7",
- "role": "Owns per-row wrapping overrides, inherited view defaults, shared native/MCP toggling, and delegate paint acknowledgement."
+ "role": "Owns PreviewWrapMode per-row wrapping overrides, inherited view defaults, and delegate paint acknowledgement."
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/widgets/shared/styled_text_layout.py",
@@ -1856,7 +2119,7 @@
{
"path": "external/pyqt-reactive/src/pyqt_reactive/services/widget_tree_projection.py",
"sha256": "05d5221d64e74ff14483f203975f17543381d959c30ee5918c200fa8e9957547",
- "role": "Owns row action declarations, automatic selection and explicit preview toggling."
+ "role": "Declares TogglePreviewAction (item_preview_toggle), shared by native clicks and the MCP row action."
},
{
"path": "external/pyqt-reactive/tests/test_list_preview_wrapping.py",
@@ -1864,14 +2127,15 @@
"role": "Native Qt regressions prove row wrapping, disclosure, refresh/reorder preservation, graphical guides, structural action parity, and delegate paint acknowledgement."
}
],
- "findings": [
- "The compatibility page lacked the exact owner-page link and duplicated a package-level boundary link.",
- "The page did not identify the OpenHCS declarations that own well-filter preview labels, empty-selection visibility, and include or exclude operators.",
- "Added the persisted generic preview policy and its shared two-manager application boundary; retained declaration-owned field selection.",
- "Clarified row-arrow overrides and repeated graphical guides, distinguishing shared wrapping defaults from row-owned display choices."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/architecture/list_item_preview_system.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/list_item_preview_system.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1: MaterializationBackend and InputSource families; PartitionFilterMode subclasses and preview_prefix unchanged), openhcs/pyqt_gui/services/reactor_providers.py (OrchestratorCreated), openhcs/pyqt_gui/config.py (log-level helper, AgentRuntimePlatform rename; UIConfig.list_previews unchanged) and openhcs/pyqt_gui/main.py (selection and update-check classes); the formatter, shared-policy and wrapping claims still hold and the pyqt-reactive target page still exists.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/list_item_preview_system.rst in full and compared it against the page change (PartitionFilterMode members to subclasses) and the changed authorities: openhcs/core/config.py (PartitionFilterMode subclasses declare preview_prefix), openhcs/pyqt_gui/services/reactor_providers.py (still registers the one PartitionFilterConfig preview formatter; code documents now use pycodify generate_python_source) and openhcs/pyqt_gui/main.py (U3 moves; still applies UIConfig.list_previews to each manager via set_preview_config); every claim holds.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/list_item_preview_system.rst against B6's later changes to openhcs/core/config.py, openhcs/pyqt_gui/services/reactor_providers.py, openhcs/pyqt_gui/main.py, tests/unit/pyqt_gui/test_main_config_propagation.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/list_item_preview_system.rst against the later B6 changes to openhcs/core/config.py, openhcs/pyqt_gui/services/reactor_providers.py, openhcs/pyqt_gui/main.py, tests/unit/pyqt_gui/test_main_config_propagation.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/list_item_preview_system.rst in full and compared it against the changed authorities: openhcs/core/config.py, openhcs/pyqt_gui/widgets/config_preview_formatters.py, openhcs/pyqt_gui/services/reactor_providers.py, openhcs/pyqt_gui/main.py and tests/unit/pyqt_gui/test_main_config_propagation.py; confirmed PartitionFilterConfig/PartitionFilterMode preview declarations, the single formatter registration, UIConfig.list_previews, _apply_list_preview_config for both managers, and TogglePreviewAction; corrected one stale well-selection phrase.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings.",
"On 2026-08-24, re-read the complete reference and its generic and domain authorities after moving preview semantics onto the WellFilterConfig family and WellFilterMode members; the page now names the projection boundary without duplicating a registry.",
@@ -1885,7 +2149,7 @@
},
{
"path": "docs/source/architecture/mcp_distribution.rst",
- "source_sha256": "11d1d54561689e08a1b6c8ceaedfa1f4de7fd82dfa25e20e1b2ae813528ee0ce",
+ "source_sha256": "671e3066e2e1ff97544e6c08b35f0f088d5de4a85abdb912aee735d7c54e8c56",
"audience": [
"OpenHCS MCP maintainers",
"packaging and hosted-deployment integrators"
@@ -1895,28 +2159,43 @@
"authority": [
{
"path": "openhcs/agent/capabilities.py",
- "sha256": "7fe9ab91490dedd6f750a57386e91835cf1067a27d10079966510aa9821adaf0",
- "role": "Declares capability identity, services, side effects, security, transport availability, and surface profiles."
+ "sha256": "87c8e2e5aeb425b9a2eb690d48bdd6f5af7a327c389deac261d7e4e0da5d609f",
+ "role": "Declares AgentCapabilityDeclaration fields (service, side effects, security, data exposure, transport availability, progress heartbeat and worker-thread safety), CapabilityTransport semantics, local surface profiles, and one tool per headless session operation plus openhcs_session_events."
},
{
- "path": "openhcs/mcp/dev_client_commanding.py",
- "sha256": "e1c987624c76be157a6524cda985498351da3c77ef19c99b85e05dd5c2e9b970",
- "role": "Validates one-shot development commands against the selected surface before starting an MCP subprocess."
+ "path": "openhcs/mcp/server.py",
+ "sha256": "c3dd3a396a3c304341b06c7c813036e779c921fa915060d2d3d2ed594b00b697",
+ "role": "Binds the selected declaration registry into FastMCP with outputContract metadata and structured output, runs non-worker-safe capabilities through the main-thread dispatcher, and binds the session to the main thread."
+ },
+ {
+ "path": "openhcs/mcp/execution.py",
+ "sha256": "7e94fd4c876944803bdd7d5b93300cda1376326be848ff5636fe2de1b71db096",
+ "role": "Runs the MCP SDK loop on a worker thread while a Qt core application and McpMainThreadDispatcher own main-thread calls."
},
{
- "path": "openhcs/mcp/dev_client.py",
- "sha256": "cb91fcaf3040ff9bc41d6145b71aac8249b42b0369ce8547531cccf7f76dcb3a",
- "role": "Validates commands before dispatch through an initialized persistent development session."
+ "path": "openhcs/mcp/stdio.py",
+ "sha256": "ec2935e01e9dc7c0ea56781c174f490528002bd736ac19062bd3a848f7397e7e",
+ "role": "Owns protocol stdin and stdout reservation, routes application output to stderr, and runs the transport through McpTransportExecutor."
+ },
+ {
+ "path": "tests/unit/agent/test_mcp_stdio_transport.py",
+ "sha256": "ffad185f736a7ead7b6e2e7699fb3c9b034c24534af14ea8848e0eb6989cfe1a",
+ "role": "Proves protocol isolation, cold native imports after handshake, and process-handle restoration."
+ },
+ {
+ "path": "tests/unit/agent/test_mcp_distribution_runtime.py",
+ "sha256": "3f443a32c1ba1549e22275fee7a0d05fc26a89cc14cdbdad230a05ae20be42a2",
+ "role": "Proves building the local and hosted MCP servers imports no PyQt6."
},
{
"path": "openhcs/mcp/dev_client_core.py",
- "sha256": "6b05721536e92ed0f9ce4fdd0b6ebd4dbc75201277d1df1d625bf87456d5a57e",
- "role": "Resolves tool identity and compatible profiles from the capability declarations at the shared call boundary."
+ "sha256": "1bb405ee541cdb48b27c355aeb71ad680ec8c14f9fd31089814e7ac01751b89b",
+ "role": "Resolves tool identity and compatible profiles from capability declarations and renews the command deadline on each response or progress notification."
},
{
- "path": "openhcs/mcp/server.py",
- "sha256": "ed65a008b52db505393413f0fbadf6b096c648ff48340c7e2dda8584750f6c68",
- "role": "Binds the selected declaration registry into the local stdio MCP server."
+ "path": "openhcs/mcp/dev_client_session.py",
+ "sha256": "16a508501214f9a393636320fad49d8015936c30bd8c85737d277aecf602876a",
+ "role": "Implements execute-source as add-datasets, set-pipeline, then initialize, compile and run stages followed through session events."
},
{
"path": "openhcs/mcp/control_timeout.py",
@@ -1924,64 +2203,39 @@
"role": "Projects UI-bridge and viewer connection timeout declarations into bounded MCP caller policies."
},
{
- "path": "openhcs/agent/dto/ui_bridge.py",
- "sha256": "585b1701b01b92bfce91437a60881afa769e36de9126807c3f4fcaf0ffc006fb",
- "role": "Declares the UI bridge connection and its default control timeout."
- },
- {
- "path": "openhcs/agent/dto/viewer.py",
- "sha256": "293ad9fcbd7570e74170e65f9e9d0c51eb78ead57f41213a2878ad6a95d055e3",
- "role": "Declares viewer control requests and their default timeout."
+ "path": "openhcs/mcp/context.py",
+ "sha256": "1292c9bb7173e6afe5aa6237b861c5f39bc2e854f364fd722c75a3c6b76f534e",
+ "role": "Composes local stdio authoring and the headless session with the saved desktop execution endpoint while keeping hosted HTTP self-contained."
},
{
- "path": "openhcs/mcp/context.py",
- "sha256": "7bb92e1357e02a0676243370179e5ff471d4e15c4c88b9a9f1d69b79f505c7cf",
- "role": "Composes local stdio authoring with the persisted desktop execution endpoint while keeping hosted HTTP self-contained."
+ "path": "openhcs/authoring/session/session.py",
+ "sha256": "ced615b580e4625e09873449b29ac67cdea87c8b8065c205f32e6d5921cf31fc",
+ "role": "Defines the headless Session whose datasets and pipelines live in the MCP process's ObjectState and whose events clients follow."
},
{
"path": "openhcs/pyqt_gui/config.py",
- "sha256": "a3eece2e5b2926fc6fc0edd197221da451ab7c0d0d6f4ffc8bb2ff865ad74bd4",
+ "sha256": "d092cb20206127484fbbc80a38a5152462b9b8991e848a814c85f564d51f4db8",
"role": "Declares UIConfig and projects its unique execution endpoint without constructing unrelated UI configuration."
},
{
"path": "openhcs/core/config_document.py",
- "sha256": "37f66fd971806e059da6194c003e20a7ad08f69066eec4af7ebb11b4b70a08db",
- "role": "Derives one typed nested field from the canonical configuration Python document and its dataclass schema."
- },
- {
- "path": "tests/unit/agent/test_mcp_distribution_runtime.py",
- "sha256": "4f28f8eff46a5de20726bc80316f3fec276e51646b6dd133dbef7b39e6c00004",
- "role": "Proves cold local MCP composition does not import PyQt6."
- },
- {
- "path": "tests/unit/test_config_document.py",
- "sha256": "0ee12db4633ab39b34f630e4ee1b0c48b40d8226f91fddc504bb3311b9612cda",
- "role": "Proves explicit, defaulted, and invalid typed field projections from canonical configuration source."
- },
- {
- "path": "openhcs/mcp/stdio.py",
- "sha256": "4a5299915413293e5c2492d4e3889614231a1286f93a976094e4d2eba77ea9c0",
- "role": "Owns protocol stdin and stdout reservation, isolates application input and native-library probes, and routes application output to stderr."
- },
- {
- "path": "tests/unit/agent/test_mcp_stdio_transport.py",
- "sha256": "4f7c439168dacc350d7fc31618f10dfd7ce7478d52bd5ba13ed54e720291aff2",
- "role": "Proves persistent protocol isolation, cold native imports after handshake, and process-handle restoration on normal and exceptional exits."
+ "sha256": "9695a6f300d626ceddaf4e0cd18961fd5600b63becf9a133cfa7f0b654b32cfb",
+ "role": "Derives one typed nested field from a configuration Python document and its dataclass schema."
},
{
"path": "openhcs/mcp/http.py",
- "sha256": "56430a939d65607847486d417c693c91523bf5571cb969389c37ff917f4793c4",
+ "sha256": "cac880ea5dfee3cc545c80d09c4a20ab960f40c1104fe1cb724ff34a56d93956",
"role": "Builds the stateless hosted Streamable HTTP server and rejects mutating hosted declarations."
},
{
"path": "openhcs/mcp/http_auth.py",
- "sha256": "1c2801842259c91a764940264e07ad5a05cc6492e42f04a841b0ceeb026d73e2",
+ "sha256": "54f5291960610c0aef4ec7aa6c4afa781d9828c78e62dd604a34e57c0128b3c5",
"role": "Declares public read-only and OAuth deployment policy, URL, origin, host, and audit constraints."
},
{
"path": "openhcs/mcp/client_registration.py",
- "sha256": "28459b95d3b155778ce178f36302cb830958b74f2603980641ec492d81c41a9a",
- "role": "Projects one stable launcher into registered local-client configurations."
+ "sha256": "4d6fc5f744617ab1d0e9b8317d07965496aaa6b098a4d179ef95cc6baec35671",
+ "role": "Projects one stable launcher into registered local-client configurations and synchronises the skill bundle into clients that declare a skill directory."
},
{
"path": "tests/unit/agent/test_hosted_mcp_semantic_ownership.py",
@@ -1989,79 +2243,44 @@
"role": "Guards declaration-owned hosted transport selection and absence of copied tool lists."
},
{
- "path": "openhcs/desktop_deployment.py",
- "sha256": "d5393386a14b693af3356484bdcb560e8a3cc0faea061352f46854c0cfead051",
- "role": "Owns Windows stable-launcher publication and bounded deferral while a valid launcher is live."
+ "path": "openhcs/desktop/deployment.py",
+ "sha256": "aafb7c77756cd3cd9de71bec7a6360058cd14e3aebc27b5e014c3b74271307cf",
+ "role": "Defines DesktopDeployment and its Windows and macOS leaves, including bounded deferral of the live Windows launcher."
},
{
- "path": "tests/unit/test_desktop_deployment.py",
- "sha256": "258a657c79a5ad96eacc06a9d4436018ffa10d7ca6d4ae56d5630e0a81c52238",
+ "path": "tests/unit/desktop/test_desktop_deployment.py",
+ "sha256": "6063b676685472fd6431b8ad90329552ce84f953aa938bb964c3b0eefaf11fb0",
"role": "Proves live-launcher sharing violations defer only the cache projection and all unsafe failures remain fatal."
},
{
- "path": "openhcs/desktop_installation.py",
- "sha256": "244ade0711356f68df9f376e2517dfe1c9fb77cc960c517f7c4e8ee418b5711f",
- "role": "Nominally declares and validates native install policy, then projects one exact package selection for installers and staged updates."
+ "path": "openhcs/desktop/installation.py",
+ "sha256": "b2124aaa3e4ef4a310f91a51c1879de41de1818b25fc619cc7cdad888c4202b6",
+ "role": "Declares DESKTOP_INSTALL_PROFILE and projects one exact package selection for installers and staged updates."
},
{
"path": "scripts/render_installer_contract.py",
- "sha256": "da9045e1012cb0a71ff0dfd2f2d9fc98232299f5a2f019b01228e344cf3b40e7",
+ "sha256": "1be39dfcc514e2f6f544e6907b3af0992485d3752877502fc761f8da760a7785",
"role": "Derives the native contract from installer policy, project entry points, brand identity, and the exact release version."
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/services/window_snapshot.py",
- "sha256": "c21e85912ab6715cc9c97bc796b9e5ed03610bf96f786d923557f18a876081f9",
- "role": "Owns safe Qt-rendered capture and optional bounded observations of actual flash-renderer frames."
+ "sha256": "bced886e6bbfd69ae9bdebf950027b809e72c23eddadcaa5fcf7151adf7ae913",
+ "role": "Declares the widget and window capture scopes and owns safe Qt-rendered capture."
},
{
"path": "openhcs/pyqt_gui/services/ui_bridge_windows.py",
- "sha256": "edb992542dffb88a9cc4557dbd6647b76d5b68584dca27a4c9486ef545be74e9",
- "role": "Resolves authorised OpenHCS windows, projects generic snapshot results, and distinguishes focus from accepted target navigation in application DTOs."
- },
- {
- "path": "tests/unit/agent/test_mcp_server.py",
- "sha256": "b99b6ca27be471f95ddf0852f8f94ce02797a459bf98bf6e0fa4851c0bb8c698",
- "role": "Proves MCP registration, declaration-derived development-client surface preflight, and rejection of undeclared native capture."
- },
- {
- "path": "openhcs/demo/synthetic_data.py",
- "sha256": "7a8f4a79e716ad944d5ceb1ed14e62cab9211aee65b1c0dc571e097e0f4d3c09",
- "role": "Owns per-generator MT19937 state without changing seeded image generation or caller randomness."
- },
- {
- "path": "openhcs/agent/services/synthetic_plate_service.py",
- "sha256": "2860714a9f1f2e66df403bc72331195d912cb180600889a1b4f4207642cc776b",
- "role": "Owns complete synthetic generation, post-write plate inspection, and the typed result."
- },
- {
- "path": "tests/unit/agent/test_synthetic_plate_mcp_progress.py",
- "sha256": "281197bf81d03a8e477026df293dbb385342d2a68c3d10c2897f6504ea85c74d",
- "role": "Exercises real MCP subprocess progress and delayed post-inspection completion against an inactivity deadline, with a heartbeat-disabled timeout control."
- },
- {
- "path": "tests/unit/test_synthetic_generator_randomness.py",
- "sha256": "6ab3efd7f4e083744dc4dda26d97c60b6a7649dd44432362020027b92271717f",
- "role": "Proves pixel equivalence with the original global RNG leaf, caller-state isolation, and deterministic concurrent generators."
- },
- {
- "path": "openhcs/agent/services/llm_context_service.py",
- "sha256": "f26b697684b185bd5ebe122de14f6211cf5377cc21f31c917cca53cea6b4e592",
- "role": "Composes operating-guide sections from nominal context facets and bounds returned guidance."
+ "sha256": "a10489e1bae296bc224b339ee32e2e99537175f891b86f901b4d7503d6f38f13",
+ "role": "Resolves authorised OpenHCS windows, projects snapshot results, and distinguishes focus from accepted target navigation."
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/services/window_navigation.py",
"sha256": "88da77ed07ea220b7c6874e71029b5bd50805fb2c3048701f9df773fe17257f2",
- "role": "Owns typed target admission, the deferred exact-membership recheck, and optional terminal execution/exposure facts."
- },
- {
- "path": "external/pyqt-reactive/src/pyqt_reactive/widgets/shared/manager_selection_controller.py",
- "sha256": "900fcec4c0a8e61b60130cc7a53ef8b0fd157d906182f96aa85fe57e5f008d46",
- "role": "Owns item identity resolution and ordinary Qt list selection."
+ "role": "Owns typed target navigation and the deferred exact-membership recheck."
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/services/widget_tree_projection.py",
"sha256": "05d5221d64e74ff14483f203975f17543381d959c30ee5918c200fa8e9957547",
- "role": "Owns row action declarations, automatic selection and explicit preview toggling."
+ "role": "Owns bounded tree projection, row action declarations, automatic selection and explicit preview toggling."
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/core/deferred_callback.py",
@@ -2069,57 +2288,53 @@
"role": "Owns QObject-parented delivery, target-destruction cancellation and callback retirement."
},
{
- "path": "external/pyqt-reactive/tests/test_deferred_callback.py",
- "sha256": "1c59913d1b19a9725e5735bbd342dc115fdf4b555863427169b5001324e83b1c",
- "role": "Real Qt regressions cover callback retirement, owner destruction, widget cancellation and persistent row identity."
+ "path": "openhcs/agent/services/synthetic_plate_service.py",
+ "sha256": "eb9e9ef693a877d65280004f8f85f1b6d6ab9be1c51aa3d2a0e1ed96630faa2d",
+ "role": "Owns complete synthetic generation, post-write plate inspection, and the typed result."
},
{
- "path": "benchmark/cellprofiler_benchmark_cli.py",
- "sha256": "2b92755c556c1d66d28ffa0af6e2ac61cac5af4631514382290a2af0f907c26b",
- "role": "Owns the registry-derived installed benchmark execution and report command surface."
+ "path": "openhcs/demo/synthetic_data.py",
+ "sha256": "be80973ecd87b4b3b36b7a7283d962a5d7cafd491cfa16eafe4622e09391463a",
+ "role": "Owns per-generator random state without changing seeded image generation or caller randomness."
},
{
- "path": "benchmark/contracts/run_receipt.py",
- "sha256": "1704e1ab47411d25d382b95871c96c1e60c94db14b8ffd55f4dc6c8a917758b9",
- "role": "Owns the shared comparison-run declaration, lifecycle enum, and validated receipt serialization and parsing."
+ "path": "openhcs/agent/services/llm_context_service.py",
+ "sha256": "a6511e95142361137fdfd2c50f0d251db14a8509b99c14f3883f48cbee18bca5",
+ "role": "Composes operating-guide sections from AuthoringContextSection facets and bounds returned guidance."
},
{
- "path": "benchmark/contracts/control.py",
- "sha256": "0bce24c29ea9dfb7b50c88937b616118078505db6f497312fff87766397e5ce9",
- "role": "Declares the lightweight typed benchmark inspection request, result, and artifact contracts imported by capability discovery."
+ "path": "benchmark/mcp_extension.py",
+ "sha256": "056ab5834fa3c37df1a36a37d80f2a821fb4b59c39892d7737e62fa6a57cb53b",
+ "role": "Declares the benchmark list, inspect, report, and measured-run finalise/inspect/report capabilities."
},
{
"path": "benchmark/control.py",
- "sha256": "4706078a8fbeb516a52bc51600751ae86c39c3ef7e397a2f8cf13d49204db941",
- "role": "Implements contained read-only receipt, streamed observation-progress, and structured-artifact inspection without importing benchmark execution modules."
+ "sha256": "b10dd87d7bc44063566e0c53d849091c0fc43a345b434c13467df1ede86ac892",
+ "role": "Implements contained read-only receipt, observation-progress, and artifact inspection without importing benchmark execution modules."
},
{
- "path": "tests/unit/test_benchmark_control.py",
- "sha256": "ee75209799216c90cfa3a585dd9b7af99c99405f93e9fb5e4f900ea26d9d3ea0",
- "role": "Proves receipt lifecycle, path policy, typed MCP projection, artifact discovery, and lightweight capability imports."
+ "path": "benchmark/control_service.py",
+ "sha256": "a1256b349ffdc8d04cab927326d97821462b61f57b29e564b61cc627302be49c",
+ "role": "Finalises measured-run evidence from the session's finished execution."
+ },
+ {
+ "path": "benchmark/cellprofiler_benchmark_cli.py",
+ "sha256": "56a9b4e8bb7d8195095eb99a849f717e79ca37aa429c612804584cbd7939f420",
+ "role": "Owns the installed openhcs-benchmark execution, report and list-cases command surface."
}
],
- "findings": [
- "The MCP adapter owned a separate 750-millisecond default and two-second cap for UI and viewer requests; it now derives both bounds from the corresponding connection declarations.",
- "The page claimed multiple transport projections but described only local stdio; it now explains the declaration-filtered, read-only hosted HTTP lane and its security owner.",
- "The desktop profile boundary now states that both UI-owned and independent headless execution are available while retaining separate state owners.",
- "The Windows distribution explanation now distinguishes the stable launcher cache from the separately published current-environment authority and its bounded sharing-violation retry.",
- "The page did not describe the screenshot privacy boundary; it now separates generic bounded Qt rendering from OpenHCS window discovery, authorisation, DTOs, and transport.",
- "The native installer contract is now documented as a generated projection instead of a manually mirrored product declaration.",
- "Service-local stdout guards could not protect the persistent stdio protocol from delayed imports or background native writes; the transport now owns the process channel for its full lifetime.",
- "The window projection previously allowed focus to imply successful target navigation; it now reports navigation only from the registered nominal driver and returns a typed error for an unsupported target.",
- "Dynamic Qt target lookup bypassed pyqt-reactive's live-window projection after discovery, so a deleted C++ window could escape through its stale Python wrapper; target resolution now uses the generic lifecycle boundary.",
- "The development client allowed a command excluded by its selected profile to reach the MCP server and degrade into a low-level unknown-tool response; preflight and compatible profile names now derive from the capability declarations.",
- "Constructing the complete UIConfig in a local MCP process pulled Qt-only shortcut validation across the headless boundary; MCP now projects the unique execution endpoint field from the canonical saved document and dataclass schema.",
- "Local and hosted initialization instructions were dispatched again inside the MCP server; the CapabilityTransport declaration now delegates to a transport-owned nominal strategy.",
- "Claude Desktop registration repeated Windows and macOS path decisions inside one target; registered platform leaves now own those paths while the target retains client identity, detection, and configuration format.",
- "Separate UI and viewer timeout profile classes had become behaviorally identical after timeout derivation moved to the connection contracts; the generated binders now use the sole command policy directly.",
- "The persistent development client wrapped a progress-aware MCP request in a second absolute wall-clock timeout; the duplicate ceiling was removed and the explanation now names transport inactivity as the client boundary.",
- "Clarified generic row-action ownership, explicit preview toggling and selection as the automatic default.",
- "The distribution explanation did not distinguish the packaged benchmark execution CLI from the expert local MCP capability that only inspects an existing authorized run directory."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/mcp_distribution.rst against benchmark/cellprofiler_benchmark_cli.py: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/mcp_distribution.rst in full and compared it against the changed authorities: openhcs/mcp/server.py (registered-family defaults in tool schemas, recovery reason by name), dev_client_core.py and dev_client_session.py (poll status, field filter and orchestrator state classes), http.py and http_auth.py (McpHttpAuthenticationMode became PublicReadOnlyAccess/OAuthIntrospectionAccess classes), client_registration.py and openhcs/desktop/deployment.py (status classes; platform leaves keyed by AgentRuntimePlatform class), openhcs/authoring/session/session.py (current file: E1 state families; events_after, set_transport_config and FinishedExecution unchanged), ui_bridge_windows.py (error_dialog flag), llm_context_service.py, benchmark/control.py and benchmark/cellprofiler_benchmark_cli.py (export scope classes) and the two changed tests; CapabilityTransport is still an Enum with HOSTED_STREAMABLE_HTTP, the Windows and macOS registration and deployment leaves, the public or OAuth-introspection policy and the benchmark capability claims still hold, and the page names no renamed member.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/mcp_distribution.rst in full and compared it against the changed authorities: authoring/session/session.py (K6 debug command/replay classes), core/config_document.py (L6: rendering through pycodify.generate_python_source; project_dataclass_field_from_source unchanged) and pyqt_gui/services/ui_bridge_windows.py (U3: dirty/signature markers from objectstate.inspection, DockPane from pyqt-reactive); none changes the headless-session, saved-endpoint projection, window-capture, navigation or row-action claims, so no prose change was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/mcp_distribution.rst against B6's later changes to openhcs/authoring/session/session.py: the writability probe makes one exclusive create attempt, the installed demo reports ViewerType.value, the unit session pre-creates its QApplication, and the GUI journey waits for each batch to end. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/mcp_distribution.rst against B6's later changes to openhcs/mcp/execution.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/mcp_distribution.rst against B6's later changes to openhcs/authoring/session/session.py, benchmark/cellprofiler_benchmark_cli.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/mcp_distribution.rst against B6's later changes to openhcs/mcp/server.py, openhcs/mcp/dev_client_core.py, openhcs/mcp/dev_client_session.py, openhcs/mcp/context.py, openhcs/authoring/session/session.py, openhcs/pyqt_gui/services/ui_bridge_windows.py, benchmark/control_service.py, benchmark/cellprofiler_benchmark_cli.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/mcp_distribution.rst against the later B6 changes to openhcs/agent/capabilities.py, openhcs/mcp/server.py, openhcs/mcp/execution.py, openhcs/mcp/dev_client_core.py, openhcs/mcp/dev_client_session.py, openhcs/mcp/context.py, openhcs/authoring/session/session.py, openhcs/pyqt_gui/services/ui_bridge_windows.py, benchmark/control_service.py, benchmark/cellprofiler_benchmark_cli.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/mcp_distribution.rst in full and compared it against the changed authorities: openhcs/agent/capabilities.py, openhcs/mcp/server.py, stdio.py, context.py, http.py, client_registration.py, dev_client*.py, the deleted openhcs/desktop_deployment.py and desktop_installation.py (moved to openhcs.desktop in 73a2aece2), ui_bridge_windows.py, synthetic-plate and benchmark control owners and tests; verified every named symbol by grep, traced McpTransportExecutor, the binder's dispatcher path, SessionJourney.run_source and skill synchronisation; corrected the PyQt/threading, session-execution, desktop-deployment, client-registration and finalisation passages.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings.",
"A focused 15-file architecture regression selection passed all 462 tests, including the authorities named for this page.",
@@ -2172,7 +2387,7 @@
},
{
"path": "docs/source/architecture/measurement_equivalence_system.rst",
- "source_sha256": "f071f7effe7d94ef86cfcfaa24f479249fce185cba690c670ff247597aafa948",
+ "source_sha256": "1ccad7b80c8dd238b8318071f1d96fdc968e15fa277578f9161bc3cc35803102",
"audience": [
"OpenHCS equivalence maintainers",
"CellProfiler parity reviewers"
@@ -2182,28 +2397,48 @@
"authority": [
{
"path": "openhcs/core/equivalence/policy.py",
- "sha256": "aaae502d1bb818ba38761374f02d8d4b5cf78a798bb2965f157216eba72cef79",
- "role": "Declares tolerances, dialect, missing-value, stability, and feature-specific equivalence policy."
+ "sha256": "790d4fa07feac199d95acab616924e35ee95cc7861136e0de66c97683e3647e2",
+ "role": "Declares RuntimeEquivalencePolicy tolerances, MeasurementDialect selection, name mode, stability, and feature-specific numeric tolerances."
},
{
"path": "openhcs/core/equivalence/keys.py",
- "sha256": "d2b2c2702dd5ad75c7df32905e7abc3ee4bb453c427503387fd210c2631dab5e",
- "role": "Defines semantic measurement feature, row, source, and aggregate identities."
+ "sha256": "78f2f9851bade6dad6307f5520a46789af31144240e7f55b9cda7f65c7abadc4",
+ "role": "Defines RuntimeMeasurementFeatureKey and the semantic row, source, and aggregate identities."
+ },
+ {
+ "path": "openhcs/core/equivalence/measurement_features.py",
+ "sha256": "5593fc6a55967f35effeb1e5b3a55a93dcbfbf59f0baecc06c2ac641036d33cc",
+ "role": "Defines the RuntimeMeasurementFeatureSemanticProfile most-derived strategy family."
+ },
+ {
+ "path": "openhcs/core/equivalence/relationships.py",
+ "sha256": "9d4d85b89918efc19e5b8a0349f8b9d4097cd7aacf93d31bf503d5829148fd49",
+ "role": "Defines the relationship aggregate-feature semantics that production measurement execution keeps."
+ },
+ {
+ "path": "openhcs/core/measurement_dialect.py",
+ "sha256": "4eb11b861af78a3b86108a95bd07295ad8efbcb3e125cfbda429ef9839e9ad47",
+ "role": "Defines the registered MeasurementDialect family and PlainMeasurementDialect."
+ },
+ {
+ "path": "benchmark/equivalence/report.py",
+ "sha256": "2711073c4039c38efcf69e2a07a88dac41bf2600337268019a896469f1a84b03",
+ "role": "Defines typed difference records and RuntimeEquivalenceReport outside the product package."
},
{
- "path": "openhcs/core/equivalence/report.py",
- "sha256": "de6381c0f804db72af16c9f96905b886aa9fa7b5138a34f867e871b701b62c36",
- "role": "Defines typed difference records and the final RuntimeEquivalenceReport."
+ "path": "tests/unit/test_equivalence_tooling_boundary.py",
+ "sha256": "91542052bd6ba03b0bac8899f09bf5e6f86db2341a3a0ff60b48f57caf1ce415",
+ "role": "Guards that no product module imports benchmark and that openhcs.core.equivalence keeps only production-reachable modules."
},
{
"path": "benchmark/adapters/openhcs.py",
- "sha256": "177d91604f9be782b3fb9ed8641e0fb80d1c5182ad48d2e37e88c8bf3dc75596",
- "role": "Builds ordinary benchmark runtime snapshots and strict CellProfiler policy, preserves unmodified fields of caller-declared path-planning, VFS, and consolidation policies while selecting benchmark overrides, and applies digest-bound reference-export comparisons."
+ "sha256": "fbfc303a156081a5646993e41429fa1459cef7776df12f29f593dd1641222ad8",
+ "role": "Builds benchmark runtime snapshots, the strict CellProfiler policy at 1e-6 tolerances, and digest-bound reference-export comparisons."
},
{
"path": "benchmark/openhcs_measured_run.py",
- "sha256": "1e0cccc518fb6bec9c92ea8057dd7adfc7305b2d90a6da8f152c1b97601878ab",
- "role": "Wraps the ordinary compile-and-execute operation with observation validation, execution-ID admission, phase timing, endpoint provenance, and the completed-run receipt; repeated measurements may reuse one connected client."
+ "sha256": "22bebf055ec452170aa59e75468760094a42c4a4688a132b383bdef33fa3a11a",
+ "role": "Wraps the ordinary compile-and-execute submission with observation validation, phase timing from completed server records, endpoint provenance, and the completed-run receipt."
},
{
"path": "benchmark/contracts/run_artifacts.py",
@@ -2212,37 +2447,42 @@
},
{
"path": "benchmark/timing.py",
- "sha256": "00b16a0e95fbc6164e6ab761f1a397441cc71b2699eaf046f46745658a448a8e",
- "role": "Owns benchmark phase declarations, disjoint additive totals, and validated durations from completed ordinary server jobs."
+ "sha256": "5a98e0a02184d0b7f42651361d55fccaad0fc9fcc7112d985cbfe60317718f2d",
+ "role": "Owns benchmark phase declarations and additive totals that exclude nested runtime observations and benchmark validation phases."
},
{
"path": "benchmark/control_service.py",
- "sha256": "bf2b39696a49d8c87cf74d39770d63013972b9c8cf884863c467ccdf46eb12d3",
- "role": "Finalises benchmark evidence from the ordinary execution service's exact completed submission and job record."
+ "sha256": "a1256b349ffdc8d04cab927326d97821462b61f57b29e564b61cc627302be49c",
+ "role": "Finalises benchmark evidence from the OpenHCS session's finished execution: its request, endpoint handshake, and completion record."
+ },
+ {
+ "path": "benchmark/control.py",
+ "sha256": "b10dd87d7bc44063566e0c53d849091c0fc43a345b434c13467df1ede86ac892",
+ "role": "Derives unreceipted_artifacts and retained_evidence_valid during measured-run inspection."
},
{
"path": "openhcs/runtime/zmq_execution_observation.py",
- "sha256": "e52b2e9784daa396b493f1f569fbcbb0d394e7e3c03d1a0c67d6594778ce64d3",
- "role": "Declares ordinary value and outcome exports, compiled axis-owned value expectations, producing execution identity, and exclusive evidence-file creation."
+ "sha256": "db40a0d1e82bdbe778f948619e62a93d74c16d3155ebc701a067bbba164f0efa",
+ "role": "Declares value and outcome exports with required compiled axis membership and rejects exports from other schema versions."
},
{
"path": "benchmark/contracts/measured_run_receipt.py",
- "sha256": "67c71873d050e05c0e4a1e231c46b700a765c12cade51269553cc7a5cb96989e",
- "role": "Declares the typed single-pipeline success receipt and its stable serialisation boundary without mirroring runtime job status."
+ "sha256": "e19f8a8f3448027fc8ad9139cd843b4cea75369890ae950230a20528aff35128",
+ "role": "Declares the typed single-pipeline receipt, accepting archived receipts without retained-artifact digests and requiring them on current receipts."
},
{
"path": "benchmark/manifests/official30_portable_axis1.json",
- "sha256": "7f11549c0bc870f9ce2792afc9176f52b6a2b83236e47b33cbedb4a2cac73267",
+ "sha256": "1be9bb67ea05d78fd1e0b8991b994ff5635da2fff0358302b8e0df888e62888e",
"role": "Defines the exact portable 30-case acceptance corpus."
},
{
"path": "tests/integration/test_cellprofiler_official30_zmq.py",
- "sha256": "bc5abfddf076996a050b0601c46b6039d2d812ab57cd303847b241e477176f53",
- "role": "Implements the headless and non-persistent Napari acceptance routes over all manifest cases."
+ "sha256": "e9f346e25a9fbb1ec7edf9e6e19c6d51c7d2000347ee61ef41b3017244322589",
+ "role": "Implements the headless and non-persistent Napari acceptance routes over all manifest cases, plus Fiji variant tests."
},
{
"path": "benchmark/cellprofiler_reference_exports.py",
- "sha256": "604f9a95f94eeb10365437b875a0ff3d558dbb2fda13e7e951242d417a69b49c",
+ "sha256": "3f175ad2173cffb2181246b6d5cc498c64dfe90b5f38f87265c1effafeb60e6f",
"role": "Owns terminal-artifact exporter derivation, exact output-inventory selection, numeric-image tolerance, categorical-label equality, and explicit single-plane label projection."
},
{
@@ -2251,27 +2491,38 @@
"role": "Proves digest and inventory failures remain fail-closed and verifies the two declared per-artifact comparison contracts."
},
{
- "path": "tests/unit/test_openhcs_adapter.py",
- "sha256": "2f117fab8c889fb50a2ee734fe802b0ac6cbcbe9945a33d24c1482fbcd7eb955",
- "role": "Proves validated reference-export comparisons and retention of caller-declared path-planning, VFS, and consolidation settings not overridden by benchmark policy."
+ "path": "tests/unit/test_benchmark_timing.py",
+ "sha256": "a6d64635ab230da628c417a08e482f95b1c2a90ba6b9389abb21a459ce42da89",
+ "role": "Proves completed-job duration validation and the additive-total exclusions."
},
{
- "path": "tests/unit/test_openhcs_measured_run.py",
- "sha256": "ebb8b7741eec1130969325ed30f425752f6b47aaa59a5c03655002bc3bb53690",
- "role": "Proves ordinary non-CellProfiler observation validation, execution-ID admission, pre-connect rejection, and distinct receipts for repeated measurements on one client."
+ "path": "benchmark/equivalence/exported_relationships.py",
+ "sha256": "8df617edf9fd1865e78f22a9089da2e8c44ab1204422514151a3e159586730ce",
+ "role": "Defines the registered object-instance-key plane alignment strategies used for relationship comparison."
},
{
- "path": "tests/unit/test_benchmark_timing.py",
- "sha256": "0b25284f3404072758fed50c9818e41f8d25e9c9b7fdacb193afd198cf741e1d",
- "role": "Proves completed-job duration validation and that overlapping progress and server intervals are excluded from additive phase totals."
+ "path": "benchmark/equivalence/measurement_rows.py",
+ "sha256": "a3da752a21b0ce57a527c1d7be5975d951ef82bcb48d48f09658c2b163ef3eba",
+ "role": "Owns row identity and wide/long-form row projection for comparison, outside the product package."
+ },
+ {
+ "path": "benchmark/equivalence/native_shape_rows.py",
+ "sha256": "759d9dc2816778df2aed5c8bec6809b2c0cbabf791521c03f587c7305db66a7f",
+ "role": "Moves positionally written native AreaShape values back onto their objects before comparison."
}
],
"findings": [
- "The canonical Linux acceptance command hardcoded one developer's /home cache path; it now uses the XDG cache root with a portable home fallback.",
- "Measured-run inspection now names retained_evidence_valid and states that it verifies retained receipt and observation evidence, not pipeline image or table outputs."
+ "K3 moved measurement_rows.py, measurement_facts.py and the object-instance plane-alignment strategies from openhcs.core.equivalence to benchmark/equivalence; the page still listed rows among what the product package keeps.",
+ "K3 keeps AreaShape values on the object they measured and reads native CellProfiler reference tables through benchmark/equivalence/native_shape_rows.py; the reference-comparison section did not say the native values are moved back to their objects before comparison."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6 final), benchmark/openhcs_measured_run.py changed by black formatting only (AST-equal) after docs/source/architecture/measurement_equivalence_system.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/measurement_equivalence_system.rst in full and compared it against the changed authorities: openhcs/core/equivalence/keys.py, measurement_features.py and relationships.py (K3: row and fact projection and plane alignment moved to benchmark/equivalence, aggregate-feature semantics stay), openhcs/core/measurement_dialect.py (SourceNameEncoding family; row qualifiers in openhcs/core/measurement_row_qualifiers.py), benchmark/adapters/openhcs.py (shape_rows_on_their_labels on reference snapshots, MaterializeToDisk), benchmark/openhcs_measured_run.py, benchmark/control.py, benchmark/contracts/measured_run_receipt.py and openhcs/runtime/zmq_execution_observation.py (E1: export scope and axis status classes) and benchmark/cellprofiler_reference_exports.py (K2 import path); corrected the product-package module list, added the native AreaShape row placement, and confirmed that no openhcs module imports benchmark and that the dialect, policy, receipt, timing and Official30 claims still hold.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/measurement_equivalence_system.rst against B6's later changes to tests/integration/test_cellprofiler_official30_zmq.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/measurement_equivalence_system.rst against B6's later changes to openhcs/core/equivalence/keys.py, openhcs/core/equivalence/measurement_features.py, openhcs/core/measurement_dialect.py, benchmark/control_service.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/measurement_equivalence_system.rst against the later B6 changes to openhcs/core/equivalence/keys.py, openhcs/core/equivalence/measurement_features.py, openhcs/core/measurement_dialect.py, benchmark/adapters/openhcs.py, benchmark/openhcs_measured_run.py, benchmark/control_service.py, tests/integration/test_cellprofiler_official30_zmq.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/measurement_equivalence_system.rst in full and compared it against the changed authorities: openhcs/core/equivalence/policy.py and keys.py, the deleted openhcs/core/equivalence/report.py (moved to benchmark/equivalence in f16177ed7), benchmark/adapters/openhcs.py, openhcs_measured_run.py, timing.py, control_service.py, openhcs/runtime/zmq_execution_observation.py, measured_run_receipt.py, the Official30 manifest and integration test, reference exports and their tests; corrected the dialect name, the comparison-package boundary, archived-export readability, the session-based finalisation path and the additive-total rule.",
"Read the complete page and challenged accuracy, freshness, duplication, boundary, flow, and navigation against the listed current authorities.",
"python scripts/validate_docs.py docs/source/architecture passed, and .venv Sphinx -E -W --keep-going rendered all 152 active pages without warnings.",
"A focused 15-file architecture regression selection passed all 462 tests, including the authorities named for this page.",
@@ -2309,13 +2560,13 @@
"authority": [
{
"path": "external/arraybridge/src/arraybridge/decorators.py",
- "sha256": "99823105b0ae59054df1d84013633b8b6628d0a6dc3326383199ac7c65c69bed",
- "role": "Owns callable input, output, and execution memory metadata keys and decorator semantics."
+ "sha256": "28f9bf4770bd4cc1ef34045fdef28065063ede029296f62b8a6c4140cc88e40e",
+ "role": "Owns the memory decorators that write callable input, output, and execution memory metadata."
},
{
"path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
- "role": "Declares reusable memory types together with framework capability and device behaviour."
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
+ "role": "Declares MemoryType members with framework capability and device behaviour, and MemoryContractAttribute's three metadata keys."
},
{
"path": "external/arraybridge/src/arraybridge/converters.py",
@@ -2324,13 +2575,13 @@
},
{
"path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
- "role": "Projects ArrayBridge metadata into immutable callable input, output, and execution memory contracts."
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "CallableDeclaration declares the input, output and execution memory types under ArrayBridge's MemoryContractAttribute keys; its namespace codec is derived from those field declarations."
},
{
"path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
- "role": "Derives exact step framework footprints and owns framework-local device bindings."
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "Owns FrameworkDeviceAssignment framework-local device bindings and footprint cleanup."
},
{
"path": "openhcs/core/framework_device_resolver.py",
@@ -2339,11 +2590,15 @@
}
],
"findings": [
- "The transition page did not link the exact ArrayBridge owner inventory or the current OpenHCS planning explanation.",
- "The transition pointed to the deleted GPU-memory validator; current OpenHCS ownership is callable-contract projection, compiled framework bindings, and declaration-derived capability resolution."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/memory_type_system.rst in full and compared it against the changed authorities: openhcs/core/callable_contract.py and openhcs/core/compiled_step_plan.py (K2: imports from the openhcs/core/artifacts package modules; E1: UnresolvedStepInput default); confirmed neither change touches the memory-type keys or FrameworkDeviceAssignment, and that gpu_resource_management, external_foundations and openhcs/core/runtime_adapters.py still exist; the page text needed no change.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/memory_type_system.rst in full and compared it against the changed authorities: openhcs/core/callable_contract.py (K5 declared-field CallableDeclaration) and openhcs/core/compiled_step_plan.py (CompiledSourceBindingPlan defaults); confirmed CallableDeclaration still keys memory types by arraybridge MemoryContractAttribute, FrameworkDeviceAssignment still owns footprint cleanup, and runtime_adapters.py, gpu_resource_management and external_foundations exist; updated one authority role.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/memory_type_system.rst against B6's later changes to openhcs/core/callable_contract.py, openhcs/core/compiled_step_plan.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/memory_type_system.rst against the later B6 changes to openhcs/core/callable_contract.py, openhcs/core/compiled_step_plan.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/memory_type_system.rst in full and compared it against the changed authorities: external/arraybridge/src/arraybridge/decorators.py, types.py, openhcs/core/callable_contract.py and openhcs/core/compiled_step_plan.py; confirmed MemoryContractAttribute's INPUT/OUTPUT/EXECUTION keys, FrameworkDeviceAssignment, openhcs/core/runtime_adapters.py and the gpu_resource_management and external_foundations targets; the page text needed no change.",
"Re-read docs/source/architecture/memory_type_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: external/arraybridge/src/arraybridge/decorators.py; external/arraybridge/src/arraybridge/types.py; external/arraybridge/src/arraybridge/converters.py; openhcs/core/callable_contract.py; openhcs/core/compiled_step_plan.py; openhcs/core/framework_device_resolver.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -2359,57 +2614,68 @@
},
{
"path": "docs/source/architecture/microscope_handler_integration.rst",
- "source_sha256": "7568ee810dd5ff9f13643ee2434a239e6e79d42d0e3dd57d2690b406d41b254f",
+ "source_sha256": "485cbdb1bbd21e8d5266ed571a66435ba97b294b56f271fc00841a5340b80534",
"audience": [
"OpenHCS source-model maintainers",
"microscope integration authors"
],
- "user_need": "Understand how registered microscope handlers own detection, metadata, source projection, and backend compatibility.",
+ "user_need": "Understand how registered dataset sources own detection, metadata, source projection, and backend compatibility.",
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/microscopes/microscope_base.py",
- "sha256": "ae19ce4f6e1cd2e60946eceb908722e1ff760d7654a775d8372abbc4ac1fc81f",
- "role": "Defines the handler root, registry-backed factory, uniform plate context, detection, and declaration-owned workspace-backend replay boundary."
+ "path": "openhcs/core/dataset_sources/source.py",
+ "sha256": "b3758a878fdf8ec28b6d9818ebfd96c5c470895575f7a79500c85f1490679281",
+ "role": "Defines the DatasetSource family keyed by source_name, its selection-role mixins, detection_order, create hook, workspace backend registration, and virtual-workspace metadata recording."
+ },
+ {
+ "path": "openhcs/core/dataset_sources/choice.py",
+ "sha256": "497eb028aabb80283be8a621ea94e09881dfcd51b2e34e2bbecafa09e004c1f6",
+ "role": "Defines DatasetSourceChoice, AutoDetectedSource, and open(), which selects the source class, applies declared-file selection, calls create, and binds dataset_root."
+ },
+ {
+ "path": "openhcs/core/dataset_sources/discovery.py",
+ "sha256": "d3c2cd7a43dce30ab3f74a9fcfb1f1d7be389f4ab101f867ac5a2fe418a16c93",
+ "role": "Discovers kernel members and imports the active AxisFamily's extension_modules without naming a domain module."
},
{
- "path": "openhcs/microscopes/openhcs.py",
- "sha256": "a8d416d4ac89e01f2a41a559632f62b67a06e6294833ea9d0af82fdec6787e7e",
- "role": "Replays a persisted workspace through the source handler type recorded by the canonical OpenHCS metadata."
+ "path": "openhcs/core/dataset_sources/interfaces.py",
+ "sha256": "dd88254bfd5da8b0fdf0a038e358f36b49db5b8cf0b93213d56dc928af965f00",
+ "role": "Defines MetadataHandler.component_value_set and MetadataComponentValueSet keyed by the active family's axes."
},
{
- "path": "openhcs/microscopes/microscope_interfaces.py",
- "sha256": "a603fa1c58ba005f8ba22713e46efe4e33aee0f762ba188467482ae3ae429200",
- "role": "Declares the single complete metadata-component projection implemented by microscope metadata handlers."
+ "path": "openhcs/core/dataset_sources/openhcs_format.py",
+ "sha256": "0c1241f2d729e724e076f315acd042afa9c4e9022cc5046c3255753c408f3c2a",
+ "role": "Reopens a persisted workspace by looking up the recorded source_name and re-registering that source's workspace backends."
},
{
- "path": "openhcs/core/source_bindings.py",
- "sha256": "e0afbbe40cce3847bcc2314910d0083ecc579a5a0d6e4278c3a5df4c62e7eb01",
- "role": "Declares named source bindings independently of concrete microscope handlers."
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares PipelineConfig.dataset_source as a DatasetSourceChoice class defaulting to AutoDetectedSource, and the SourceBindingsConfig declaration the source-binding source consumes."
},
{
- "path": "tests/unit/test_microscope_handler_identity.py",
- "sha256": "3ed9e436b4d9ce2770f5eeed5c884c7621f22eab665cd3cc9280df95d3e10ba8",
- "role": "Verifies registered identity, explicit construction, auto-detection, and invalid handler failure."
+ "path": "openhcs/microscopes/bioformats.py",
+ "sha256": "3b4ba677bebe178d0613f65ffdcadee349e25af82837fd29a1b33898f493d966",
+ "role": "Shows a decoder-owned source metadata handler replaced by the workspace metadata handler after the workspace is written."
},
{
- "path": "tests/unit/test_bioformats_microscope_handler.py",
- "sha256": "d12e4e85a13bc28f7d67d1547ce8a347897c89436a129aebf45bc34cd5a193d6",
- "role": "Verifies generic plate-folder binding and BioFormats handler metadata behaviour."
+ "path": "tests/unit/test_dataset_source_family.py",
+ "sha256": "4cb651b475e5e033ab94824567b8a35b0ee02f313254082e03e35cd96585d829",
+ "role": "Verifies every registered source declares identity, selection role and backends, and that metadata detection and the factory use declared identity."
},
{
- "path": "tests/unit/agent/test_plate_inspection_service.py",
- "sha256": "fafd340d60d66e21ca0ac42cdc1a7f0fee5672788fa219cf7f7a7fefe8841194",
- "role": "Verifies automatic inspection can reopen both native and SourceBindings virtual workspaces from persisted metadata."
+ "path": "tests/unit/test_dataset_source_guards.py",
+ "sha256": "adfeac23f6196a0c5393dc15b71df04fda0ec34e5ae23bfdaa22e0a165f68170",
+ "role": "Guards that the kernel imports no domain package and that the deleted microscope-handler names do not return."
}
],
- "findings": [
- "The audit exposed a factory-side concrete class-name branch for OpenHCS plate context and an embedded extension procedure; the root now binds plate_folder uniformly and the page links to the task guide.",
- "Prepared SourceBindings workspaces could not be inspected through automatic OpenHCS metadata detection because replay reconstructed an ingestion instance without its original configuration; the handler declaration now owns backend replay directly.",
- "Microscope metadata exposed one method per concrete axis; handlers now return one complete component value set keyed by the canonical declaration."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/microscope_handler_integration.rst in full and compared it against the changed authorities: openhcs/core/dataset_sources/source.py (get_required_backend deleted, virtual-workspace root compared through absolute_source_path) and openhcs/core/config.py (E1: MaterializationBackend and InputSource became class families); the page states compatible storage backends without naming get_required_backend or MaterializationBackend, and DatasetSourceChoice, detection_order, the selection-role mixins, PipelineConfig.dataset_source and SourceBindingsConfig are unchanged, so no prose change was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/microscope_handler_integration.rst in full and compared it against the changed authorities: dataset_sources/source.py, choice.py, openhcs_format.py and microscopes/bioformats.py (K4: SourceBindingsConfig and source_bindings_defaults_to_base now imported from core/config.py) and core/config.py (K4: SourceBindingsConfig declared there; PartitionFilterMode became a class family); the page names SourceBindingsConfig without a module path, and DatasetSourceChoice.choices/open, detection_order, extension_modules discovery and PipelineConfig.dataset_source are unchanged, so no prose change was needed; the config.py evidence role now also covers SourceBindingsConfig.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/microscope_handler_integration.rst against B6's later changes to openhcs/core/dataset_sources/source.py, openhcs/core/dataset_sources/choice.py, openhcs/core/dataset_sources/interfaces.py, openhcs/core/dataset_sources/openhcs_format.py, openhcs/core/config.py, openhcs/microscopes/bioformats.py, tests/unit/test_dataset_source_family.py, tests/unit/test_dataset_source_guards.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/microscope_handler_integration.rst against the later B6 changes to openhcs/core/dataset_sources/source.py, openhcs/core/dataset_sources/choice.py, openhcs/core/dataset_sources/interfaces.py, openhcs/core/dataset_sources/openhcs_format.py, openhcs/core/config.py, openhcs/microscopes/bioformats.py, tests/unit/test_dataset_source_family.py, tests/unit/test_dataset_source_guards.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/microscope_handler_integration.rst in full and compared it against the changed authorities: the deleted openhcs/microscopes/microscope_base.py, openhcs.py and microscope_interfaces.py (moved to openhcs/core/dataset_sources in c6d9a52ce), openhcs/core/source_bindings.py and the Bio-Formats and plate-inspection tests; traced DatasetSource registration and discovery, DatasetSourceChoice.open, detection_order, PipelineConfig.dataset_source, component_value_set, and workspace replay through the recorded source name; rewrote the page in DatasetSource vocabulary.",
"Re-read docs/source/architecture/microscope_handler_integration.rst in full against repository base 32ca17c17 and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/microscopes/microscope_base.py; openhcs/core/source_bindings.py; tests/unit/test_microscope_handler_identity.py; tests/unit/test_bioformats_microscope_handler.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
@@ -2424,7 +2690,7 @@
},
{
"path": "docs/source/architecture/multiprocessing_coordination_system.rst",
- "source_sha256": "816ae37a0aa964c708bf3b7855583b1e0ce706571a231df60caf1405eecee5ee",
+ "source_sha256": "d8e12a688c5e7b27a98ee51b9e9261176a4a6181a9e7321ef02f3068bf2d2f76",
"audience": [
"OpenHCS compiler and multiprocessing maintainers"
],
@@ -2433,23 +2699,33 @@
"authority": [
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compiles source execution-axis and framework-device facts into frozen contexts and typed plans."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Discovers partition-axis values through the active AxisFamily and compiles one frozen ProcessingContext per value."
},
{
- "path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
- "role": "Assembles and validates the one worker-safe execution bundle without a process-global GPU registry."
+ "path": "openhcs/core/orchestrator/bundle_execution.py",
+ "sha256": "25446d6c6a43c22ff7104c6712c143f0276bc4749c2642fffac10db1455ef707",
+ "role": "Validates compiled contexts and worker-safe steps before worker setup and builds the worker-lane execution plan."
+ },
+ {
+ "path": "openhcs/core/orchestrator/worker_lanes.py",
+ "sha256": "4273af5b358c7f4cf01a504bb151fdd0117fcc724cf121913cb9ebb5b7695115",
+ "role": "Plans deterministic worker lanes from compiled axis contexts and installs the bundle in workers through ForkInheritedWorkerExecutionState."
},
{
"path": "openhcs/core/orchestrator/worker_execution.py",
- "sha256": "3c952465268e4b6c44676a853bb9c99d2d04a0490c7d398d2cdaf8b3b5f966d7",
- "role": "Installs the bundle, submits one task per compiled axis, collects typed results, and performs footprint-aware cleanup."
+ "sha256": "83ade0271c9b24ca1ba61c708d9e6f60a432b630ec6a8a2fac6d1018458e6be5",
+ "role": "Submits one task per active lane, collects per-axis ExecutionResult values, ships or inherits the bundle, and terminates owned process workers on cancellation."
+ },
+ {
+ "path": "openhcs/core/compiled_execution.py",
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "Defines CompiledExecutionBundle with runtime contexts and pickle-safe transport snapshots."
},
{
"path": "openhcs/core/orchestrator/cancellation.py",
"sha256": "f825b602fb020a23d602e1f93ed9cecea881eca16e07882fd7a91ab519445244",
- "role": "Defines the parent-owned cancellation scope observed directly by inline and threaded lanes."
+ "role": "Defines the execution-scoped cooperative cancellation signal observed by inline and threaded lanes."
},
{
"path": "openhcs/core/worker_start_policy.py",
@@ -2457,11 +2733,14 @@
"role": "Selects one multiprocessing context from the compiled GPU footprint."
}
],
- "findings": [
- "The page incorrectly described cancellation as a cross-process channel instead of a parent-owned interruption boundary."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/multiprocessing_coordination_system.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, orchestrator/bundle_execution.py, worker_lanes.py and worker_execution.py (E1: OrchestratorState and RuntimeObservationMode became class families; K2: artifacts package imports); the page names no orchestrator state or observation mode; PipelineCompiler, ProcessingContext, ExecutionResult, CompiledExecutionBundle and ForkInheritedWorkerExecutionState.require_current are unchanged, so no prose change was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/multiprocessing_coordination_system.rst in full and compared it against the changed authorities: pipeline/compiler.py (U3 ScopeKey step scopes, K6 progress phase/status classes, CompiledSourceBindingPlan() empty value), orchestrator/bundle_execution.py (lane results now reordered to compiled axis order) and orchestrator/worker_execution.py (K6 progress classes); every numbered step, ForkInheritedWorkerExecutionState, the per-lane task submission, per-axis ExecutionResult collection and the cancellation split still hold; no prose change needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/multiprocessing_coordination_system.rst against B6's later changes to openhcs/core/pipeline/compiler.py, openhcs/core/orchestrator/bundle_execution.py, openhcs/core/orchestrator/worker_execution.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/multiprocessing_coordination_system.rst against the later B6 changes to openhcs/core/pipeline/compiler.py, openhcs/core/orchestrator/bundle_execution.py, openhcs/core/orchestrator/worker_execution.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/multiprocessing_coordination_system.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, openhcs/core/compiled_execution.py and openhcs/core/orchestrator/worker_execution.py; traced _axis_values_to_process through AxisFamily.active().partition_axis(), validate_bundle_execution, CompiledContextLanePlanner, PooledWorkerLaneRunner._submit_lanes, ExecuteShippedLane and ProcessWorkerExecutorResources.cancel_execution; corrected the task granularity, axis vocabulary, bundle installation and cancellation wording.",
"Re-read docs/source/architecture/multiprocessing_coordination_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline/compiler.py; openhcs/core/compiled_execution.py; openhcs/core/orchestrator/worker_execution.py; openhcs/core/orchestrator/cancellation.py; openhcs/core/worker_start_policy.py.",
"Focused feature-lattice QA passed 65 tests covering framework resolution, execution scope, callable contracts, compiled execution, validation, and worker policy.",
diff --git a/docs/audits/architecture_n_z_development.json b/docs/audits/architecture_n_z_development.json
index 3d583b9137..892706e141 100644
--- a/docs/audits/architecture_n_z_development.json
+++ b/docs/audits/architecture_n_z_development.json
@@ -10,12 +10,12 @@
"authority": [
{
"path": "openhcs/runtime/napari_viewer_server.py",
- "sha256": "64ccfef65a10e04878ef0806ae72d0446540efcbfc72af46c4e2ab785418a84c",
- "role": "Detached Napari endpoint startup and application event-loop integration."
+ "sha256": "ac54f8bfdce33150c31b38bcd5cf6069b5fd66fb7c05d5a6951610ec3d895bfd",
+ "role": "Detached Napari endpoint: accepted stream batches, layer routing, the NapariLayerKind family, and dimension-label overlays with their route-source family."
},
{
"path": "openhcs/runtime/napari_streaming_handlers.py",
- "sha256": "86f4ab319c7c68b626e7e2a1adef81eb3d7244f044b5b3e4dddd78bbee2aa4b6",
+ "sha256": "b43f29163cef420c77f30e46e92d94145520f7534077d54636a0ab141da32e7e",
"role": "Napari route handlers that project typed received batches onto native viewer state."
},
{
@@ -25,10 +25,12 @@
}
],
"findings": [
- "This legacy URL owns no current mechanism and should only route readers to the current integration and owner documentation."
+ "The page is a redirect stub; its link targets still exist and own the content (checked 2026-10-11, B6). The record's napari_viewer_server.py role named variable-size display strategies, which E1 moved to the NapariVariableSizeHandling family in openhcs/runtime/viewer_display.py; the role now names what the endpoint still owns."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/napari_integration_architecture.rst in full and compared it against the changed authorities: openhcs/runtime/napari_viewer_server.py and openhcs/runtime/napari_streaming_handlers.py (E1: NapariDimensionLabelRouteSource and NapariLayerKind became class families in the endpoint; the variable-size display strategies moved onto the NapariVariableSizeHandling family in openhcs/runtime/viewer_display.py); confirmed both redirect targets exist and the endpoint and handlers remain the OpenHCS Napari wrappers; the page text needed no change, and the endpoint evidence role no longer claims the variable-size strategies.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/napari_integration_architecture.rst in full and compared it against the changed authorities: openhcs/runtime/napari_viewer_server.py and openhcs/runtime/napari_streaming_handlers.py; confirmed both redirect targets exist and the modules remain the OpenHCS Napari wrappers; the page text needed no change.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"tests/unit/test_napari_streaming_handlers.py and tests/unit/test_viewer_protocol.py exercise the named OpenHCS and transport boundaries",
"On 2026-08-25, re-read the complete redirect against the changed Napari endpoint owner; bounded payload projection and atomic layer isolation do not change this legacy route's current-owner destination.",
@@ -47,12 +49,12 @@
"authority": [
{
"path": "openhcs/core/viewer_streaming_service.py",
- "sha256": "46982fd46f47b962033e02d82afb6c3ce2abbafdabfbce99e591d9c6dff2fbab",
- "role": "Compiled output streaming, viewer settlement, state evidence, and lifecycle cleanup."
+ "sha256": "50742cdcb589dc9fba5a177c0107c046dd63c518c5aa77bd8a80e4f69e898e07",
+ "role": "Compiled output streaming parametrized by viewer type, viewer settlement, and lifecycle cleanup."
},
{
"path": "openhcs/runtime/napari_stream_visualizer.py",
- "sha256": "a9ab90f3ddbfc2393683b4baf163fdb7030384b8985e8fee5b5aea20614ce2c1",
+ "sha256": "44087b2dbdc988e5854841fe2e47830a02bcb9e0c187e613d4cd250e8d763c2b",
"role": "OpenHCS Napari visualiser declaration and launch adapter."
},
{
@@ -62,10 +64,14 @@
}
],
"findings": [
- "The old page no longer owns streaming facts and must remain a compatibility route to the current boundary."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/napari_streaming_system.rst in full and compared it against the changed authorities: openhcs/core/viewer_streaming_service.py (E1: viewer lifecycle is assigned as a ViewerLifecycle class, ViewerConnectedExternal); confirmed both redirect targets exist and the module remains the OpenHCS streaming service; the page text needed no change.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/napari_streaming_system.rst against B6's later changes to openhcs/core/viewer_streaming_service.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/napari_streaming_system.rst against the later B6 changes to openhcs/core/viewer_streaming_service.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/napari_streaming_system.rst in full and compared it against the changed authorities: openhcs/core/viewer_streaming_service.py and openhcs/runtime/napari_stream_visualizer.py; confirmed both redirect targets exist and the modules remain the OpenHCS streaming service and Napari adapter; the page text needed no change.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"tests/unit/test_streaming_service.py and tests/unit/test_napari_streaming_handlers.py verify the current route",
"On 2026-08-25, re-read the complete page against metaclass-registry 0.2.1, ArrayBridge 0.3.3, the public native-catalogue projection, declaration-derived child environments, and the updated package floors; revised the page where those semantics are user-relevant and confirmed its remaining claims against current source and tests.",
@@ -74,7 +80,7 @@
},
{
"path": "docs/source/architecture/nominal_ownership.rst",
- "source_sha256": "ab2d1a92cb778414fb8624533ef6de74cf92e70ba87c92a495bbe8ae70faf7be",
+ "source_sha256": "088cabe6cf370dc0c01a3927334fbb054ec7604a9256bab1ff052b7390299378",
"audience": [
"OpenHCS maintainers and extension authors"
],
@@ -82,38 +88,49 @@
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/core/registry_strategies.py",
- "sha256": "a41186665abbd308f224d0bb41d6618cd9d89b08acaf54ec2819f2024a6e0804",
- "role": "OpenHCS enum, nominal-type, and most-derived strategy selection implementations."
+ "path": "external/metaclass-registry/src/metaclass_registry/strategies.py",
+ "sha256": "5c00cd4d80f9f0ceffbc2ab95ad8c6abd44a8a66a974959445ef93df08dd4f0d",
+ "role": "EnumKeyedStrategyMixin, NominalTypeKeyedStrategyMixin and MostDerivedContextStrategyMixin selection semantics described under Registry roots."
},
{
"path": "external/metaclass-registry/src/metaclass_registry/core.py",
- "sha256": "ed4dd1dfd0e4e05e916fec39f19b4b8bd526e776aa0c96e3abe9b1ffbdff0505",
- "role": "Generic AutoRegisterMeta registry ownership and configuration mechanics."
+ "sha256": "72c8284e8a32dd193f13f36a425289d5293a32f4a381046db523aa87182e0be8",
+ "role": "AutoRegisterMeta __registry__, RegistryConfig and RegistryFamily ownership."
},
{
- "path": "tests/unit/test_registry_owner_iteration.py",
- "sha256": "3e042f8980dfac25cf7e27d42f386fc6a63329d4ca7f21348a21589ab56a3046",
- "role": "Proves semantic-control parameters and strategy iteration derive from their nominal declaration owners."
+ "path": "openhcs/core/runtime_plane_projection.py",
+ "sha256": "cf644451b6649dcec398fecbf961a4a29ea74f0bb2e0bdab052ec1be8a427f95",
+ "role": "RuntimeSliceProjectableValue ABC on which owned runtime values declare their slice projection."
+ },
+ {
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "ProcessingContract subclass family owning per-callable execution over the plane axis."
},
{
"path": "openhcs/processing/backends/lib_registry/openhcs_registry.py",
- "sha256": "aa3079341cb2af8788938e14efee6f9f6c4b6c01874227e74c6340850d525884",
- "role": "Derives the public native callable catalogue while retaining explicit reconstruction from each callable declaration."
+ "sha256": "2a1de7f8af9ade2cba450cbbcc1abd6510a8e3edb58217c6c08fa067bde21de2",
+ "role": "Native callable catalogue derived from each callable's nominal catalogue declaration and the public module surface."
},
{
"path": "openhcs/interop/cellprofiler/module_declarations.py",
- "sha256": "89dd65abc003c464ba6c489f52d3a4fa47be1d5f8b87d26ad72b7537f188cf8b",
- "role": "CellProfiler module declarations that own callable names, catalogue placement, and headless-import eligibility."
+ "sha256": "6ff4f4bc3eaf6b4c9fc42752493c2b9ba2e11c8994d7edaea81f586ac57c3b89",
+ "role": "CellProfilerModule declarations owning callable names, catalogue placement, and headless-import eligibility."
+ },
+ {
+ "path": "tests/unit/test_registry_owner_iteration.py",
+ "sha256": "1bbff81d29c9108c00382ccd1151c374ae0d68de38427034202ebb995abc5765",
+ "role": "Proves semantic-control parameters and strategy iteration derive from their nominal declaration owners."
}
],
- "findings": [
- "The explanation omitted nominal callable catalogue-module ownership and the CellProfiler package's declaration-derived projection.",
- "The catalogue ownership rule did not distinguish the public browsable surface from explicitly transported private decorated callables.",
- "The ownership explanation did not include declaration-owned CellProfiler headless-import eligibility."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/nominal_ownership.rst in full and compared it against the changed authorities: openhcs/core/runtime_plane_projection.py (K2/K3: stored_slices, match_alignment and contextualized_output added to RuntimeSliceProjectableValue) and openhcs/interop/cellprofiler/module_declarations.py (E1: InputSource class family); confirmed owned values still subclass RuntimeSliceProjectableValue while RuntimeSliceProjection handles foreign values, ArtifactType is still the artifact-kind root (now in openhcs/core/artifacts/kinds.py), and OpenHCS still imports all three selection mixins from metaclass_registry.strategies after E1; no prose change was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/nominal_ownership.rst in full and compared it against the changed authorities: openhcs/core/processing_contracts.py (K5: CallableMetadata renamed CallableDeclaration for signature lookup) and interop/cellprofiler/module_declarations.py (K4: SourceBindingsConfig imported from core/config.py; K5: contracts carry artifact inputs/outputs directly); re-checked that the three metaclass_registry.strategies mixins, RegistryConfig/RegistryFamily, RuntimeSliceProjectableValue, RuntimeSliceProjection, ProcessingContract and CellProfilerModule exist with the described roles; the page names none of the renamed symbols, so no prose change was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/nominal_ownership.rst against B6's later changes to openhcs/core/processing_contracts.py, openhcs/processing/backends/lib_registry/openhcs_registry.py, openhcs/interop/cellprofiler/module_declarations.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/nominal_ownership.rst against the later B6 changes to openhcs/core/processing_contracts.py, openhcs/processing/backends/lib_registry/openhcs_registry.py, openhcs/interop/cellprofiler/module_declarations.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/nominal_ownership.rst in full and compared it against the changed authorities: deleted openhcs/core/registry_strategies.py (now metaclass_registry/strategies.py), metaclass_registry/core.py, tests/unit/test_registry_owner_iteration.py, lib_registry/openhcs_registry.py, interop/cellprofiler/module_declarations.py; verified ArtifactType, ProcessingContract, CellProfilerModule, the three mixins and the equivalence MostDerivedContextStrategyMixin users exist; replaced the deleted RuntimeSliceProjectionStrategy example and named the mixins' new home.",
"Re-read docs/source/architecture/nominal_ownership.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/registry_strategies.py; external/metaclass-registry/src/metaclass_registry/core.py; tests/unit/test_registry_owner_iteration.py; openhcs/processing/backends/lib_registry/openhcs_registry.py; openhcs/interop/cellprofiler/module_declarations.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -134,7 +151,7 @@
"authority": [
{
"path": "external/PolyStore/src/polystore/omero_local.py",
- "sha256": "35d39564317510234c2a3ded00bb06a09417d1b6f7359e52e74c7bd0a086f642",
+ "sha256": "e549d4811539cec9842ceac007eb666380ef00d6838a9b69d91eb02de64d1daa",
"role": "Generic OMERO virtual-backend addressing, listing, pixel access, and declaration-owned output dispatch."
},
{
@@ -185,6 +202,7 @@
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/omero_backend_system.rst against the later B6 changes to external/PolyStore/src/polystore/omero_local.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
"Re-read docs/source/architecture/omero_backend_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: external/PolyStore/src/polystore/omero_local.py; external/PolyStore/src/polystore/omero_tables.py; external/PolyStore/docs/source/guides/omero_backend.rst; openhcs/omero/docker-compose.yml; openhcs/runtime/omero_instance_manager.py; openhcs/omero/plugin/setup.py; openhcs/omero/plugin/omero_openhcs/apps.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -201,7 +219,7 @@
},
{
"path": "docs/source/architecture/orchestrator_cleanup_guarantees.rst",
- "source_sha256": "1fabf4a602e5856b9ac754b390478f7c2859a6bc6e088e7c55a4e5901ccb54a7",
+ "source_sha256": "2c2e37216d38b8574fac036c04d210d96efc2d8ec3c6718ec21dcfbdfdb89926",
"audience": [
"OpenHCS runtime maintainers"
],
@@ -209,63 +227,59 @@
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/core/orchestrator/compiled_plate_execution.py",
- "sha256": "80998f355c3f4be9027cb91cabf581f51efd870fb86960f5f6ecd615bcdc89ef",
- "role": "Parent compiled-plate execution, terminal steps, viewer settlement, evidence, and cleanup ordering."
+ "path": "openhcs/core/orchestrator/bundle_execution.py",
+ "sha256": "25446d6c6a43c22ff7104c6712c143f0276bc4749c2642fffac10db1455ef707",
+ "role": "Parent compiled-bundle execution: executor shutdown, BrokenProcessPool handling, parent resource release, reverse-order viewer bootstrap rollback, and viewer settlement ordering."
},
{
"path": "openhcs/core/orchestrator/worker_execution.py",
- "sha256": "3c952465268e4b6c44676a853bb9c99d2d04a0490c7d398d2cdaf8b3b5f966d7",
- "role": "Worker task execution and owner-bound runtime/framework cleanup for process, inline, and thread lanes."
+ "sha256": "83ade0271c9b24ca1ba61c708d9e6f60a432b630ec6a8a2fac6d1018458e6be5",
+ "role": "Inline, process and thread lane resources: per-axis image-cache release, process-local runtime/framework cleanup, and thread-lane deferral to the parent."
},
{
- "path": "openhcs/core/orchestrator/orchestrator.py",
- "sha256": "843e994d7b16592f43a15ce36c33a51ead534cf22df792838ce2b800621114b7",
- "role": "PipelineOrchestrator configuration ownership, resolution, compiler setup, and executor lifecycle."
+ "path": "openhcs/core/orchestrator/cancellation.py",
+ "sha256": "f825b602fb020a23d602e1f93ed9cecea881eca16e07882fd7a91ab519445244",
+ "role": "Execution cancellation scopes that retain pre-entry requests and finish per execution."
},
{
"path": "openhcs/runtime/viewer_protocol.py",
- "sha256": "30441eab97da9ffcb5b9dc33d65c2ecc56c426e79bb2b27eedc78a68aaf13bc3",
- "role": "Distinguishes external viewer clients from owned viewer processes during failed-bootstrap rollback."
+ "sha256": "347a65b63c149f2f437014d537cfaf481a33801d860742f4083b7377494e792f",
+ "role": "ViewerLifecycle family: ViewerConnectedExternal and ViewerOwnedProcess each implement rollback_failed_bootstrap, releasing an external client or stopping an OpenHCS-owned viewer process."
},
{
- "path": "openhcs/core/orchestrator/cancellation.py",
- "sha256": "f825b602fb020a23d602e1f93ed9cecea881eca16e07882fd7a91ab519445244",
- "role": "Owns cancellation scopes so pre-entry requests are retained and sequential executions do not share stale cancellation state."
+ "path": "openhcs/core/compiled_step_plan.py",
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "Framework/device assignment merge and cleanup_loaded used for the compiled framework/device footprint."
},
{
"path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
- "role": "Framework-local device scopes, installed-framework cleanup, and device discovery owned by ArrayBridge memory declarations."
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
+ "role": "Per-framework cleanup hooks (CuPy, Torch) owned by ArrayBridge memory declarations."
},
{
- "path": "tests/unit/test_framework_device_resolver.py",
- "sha256": "ba1814ae8eae05a39b594a81b4fc0d1444bf1334574bf4efe49aa829eec43a65",
- "role": "Regression proof for independent framework namespaces and exact compiled device bindings."
+ "path": "openhcs/core/context/processing_context.py",
+ "sha256": "81c5c0bf411040bac2d93418093fff7c97c5ffd9ad942272c0f71c2982c96a44",
+ "role": "release_execution_image_cache clears the context-local image-stack cache without discarding observations."
},
{
"path": "tests/unit/test_orchestrator_lane_planning.py",
- "sha256": "09c39f5cba68f8c15a0def8a09843b62d6da5c5a01e7558a9263ce4d311c078d",
+ "sha256": "4af8c5e5a6d68a64f862a0bcb98f398a0a898948e6f8b5e462a4a11381f19524",
"role": "Proves partial viewer acquisition and readiness failures roll back every acquired viewer."
},
- {
- "path": "openhcs/core/context/processing_context.py",
- "sha256": "c7b79f50661c528261ce8c577fd67c753c84a5e499aa87cd5e540b53417351a2",
- "role": "Owns explicit release of the context-local image-stack cache without discarding observations."
- },
{
"path": "tests/unit/test_runtime_stack_cache_lifetime.py",
- "sha256": "bfcfbfb485de0ccdc24251020db9cc66eba2b0f31d019553814b4e6f1fde48a2",
+ "sha256": "c92b69339b57d51498f2e879ab0d2bf0059efa2cae4fdc1ce91663eb95e4bd31",
"role": "Tests context-local image-cache lifetime across success and failure."
}
],
- "findings": [
- "The cleanup ordering omitted exact terminal-state ownership, cooperative safe boundaries, and closure of the execution cancellation scope.",
- "Global GPU scheduler cleanup was removed; cleanup now follows each compiled framework/device footprint at its owning process boundary.",
- "Viewer bootstrap previously returned its acquired set only after every viewer was created, so a later acquisition failure could leak earlier viewer processes."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/orchestrator_cleanup_guarantees.rst in full and compared it against the changed authorities: openhcs/core/orchestrator/bundle_execution.py and worker_execution.py (E1 class families for orchestrator state and observation mode), openhcs/runtime/viewer_protocol.py (E1: ViewerLifecycleState replaced by the ViewerLifecycle class family), openhcs/core/compiled_step_plan.py and the two test files; traced rollback_failed_bootstrap onto ViewerConnectedExternal (releases the client, leaves the endpoint) and ViewerOwnedProcess (stops the process), which is what the page states; parent and worker cleanup ordering is unchanged; no prose change was needed, and the viewer_protocol.py evidence role now names the lifecycle family.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/orchestrator_cleanup_guarantees.rst in full and compared it against the changed authorities: orchestrator/bundle_execution.py (K6 progress classes; lane results reordered to compiled axis order), orchestrator/worker_execution.py (K6 progress classes), compiled_step_plan.py (empty plan defaults) and tests/unit/test_orchestrator_lane_planning.py (progress phase name instead of value); none touches executor shutdown, viewer rollback, lane cleanup ownership, image-cache release or the terminal ordering, so no prose change was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/orchestrator_cleanup_guarantees.rst against B6's later changes to openhcs/core/orchestrator/bundle_execution.py, openhcs/core/orchestrator/worker_execution.py, openhcs/runtime/viewer_protocol.py, openhcs/core/compiled_step_plan.py, tests/unit/test_orchestrator_lane_planning.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/orchestrator_cleanup_guarantees.rst against the later B6 changes to openhcs/core/orchestrator/bundle_execution.py, openhcs/core/orchestrator/worker_execution.py, openhcs/runtime/viewer_protocol.py, openhcs/core/compiled_step_plan.py, tests/unit/test_orchestrator_lane_planning.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/orchestrator_cleanup_guarantees.rst in full and compared it against the changed authorities: compiled_plate_execution.py (renamed to bundle_execution.py, 4cd901c9f), worker_execution.py, orchestrator.py, viewer_protocol.py, arraybridge types.py, test_orchestrator_lane_planning.py, processing_context.py, test_runtime_stack_cache_lifetime.py; checked executor shutdown and BrokenProcessPool handling, reverse-order viewer rollback with external-client handling, thread-lane deferral of runtime release, per-axis image-cache release and cancellation-scope finish; renamed the boundary in the opening sentence. Dropped orchestrator.py and test_framework_device_resolver.py as authorities; added compiled_step_plan.py for the device-footprint cleanup.",
"Re-read docs/source/architecture/orchestrator_cleanup_guarantees.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/orchestrator/compiled_plate_execution.py; openhcs/core/orchestrator/worker_execution.py; openhcs/core/orchestrator/orchestrator.py; openhcs/core/orchestrator/cancellation.py; external/arraybridge/src/arraybridge/types.py; tests/unit/test_framework_device_resolver.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -281,7 +295,7 @@
},
{
"path": "docs/source/architecture/orchestrator_configuration_management.rst",
- "source_sha256": "1cf4d86ac3a102396af9ad8515466dc6cb55dd81c7c53190503210349b7f6b5b",
+ "source_sha256": "64ec638e772a8d62e5a55a13429dd832adc64d0b2dabd581acf19aeec5efc07e",
"audience": [
"OpenHCS compiler and configuration maintainers"
],
@@ -290,23 +304,33 @@
"authority": [
{
"path": "openhcs/core/orchestrator/orchestrator.py",
- "sha256": "843e994d7b16592f43a15ce36c33a51ead534cf22df792838ce2b800621114b7",
- "role": "PipelineOrchestrator configuration ownership, resolution, compiler setup, and executor lifecycle."
+ "sha256": "a7097eec86c5966552bcb546a2161d1a08e6a4c630e6b156de3b8354b0026b54",
+ "role": "PipelineOrchestrator: __objectstate_delegate__ to pipeline_config, dataset_root identity, and initialize enforcing the source role's availability check."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compilation order, declaration resolution, callable warm-up, framework-local device assignment, and bundle construction."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Compiler consuming ObjectState-resolved steps and the resolved global configuration once per compilation."
+ },
+ {
+ "path": "openhcs/core/dataset_sources/source.py",
+ "sha256": "b3758a878fdf8ec28b6d9818ebfd96c5c470895575f7a79500c85f1490679281",
+ "role": "DatasetSource family and SourceSelectionRole.require_available_source, which owns the local-directory availability contract."
},
{
"path": "external/ObjectState/src/objectstate/object_state.py",
- "sha256": "735ce860ab650b281534e24e3c7354f37b64bb06832cf186d6b1f626f87108fe",
- "role": "Generic editable ObjectState and snapshot boundary."
+ "sha256": "413a7bcd27695a823805cbc16a393569e30293a70c8d60d66dc6d56d3b589a4a",
+ "role": "Generic editable ObjectState scope, resolution and snapshot mechanics."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/orchestrator_configuration_management.rst in full and compared it against the changed authorities: openhcs/core/orchestrator/orchestrator.py and openhcs/core/pipeline/compiler.py (E1: OrchestratorState became a class family) and openhcs/core/dataset_sources/source.py (get_required_backend deleted); confirmed __objectstate_delegate__ is still pipeline_config, initialize still calls source_selection_role().require_available_source and a failed check sets OrchestratorInitFailed without marking the orchestrator initialized; the page names no state member, so no prose change was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/orchestrator_configuration_management.rst in full and compared it against the changed authorities: orchestrator.py (K4 import of source_bindings_defaults_to_base from core/config.py), pipeline/compiler.py (U3 ScopeKey compiler scope ids, K6 progress classes), dataset_sources/source.py (K4 import move) and objectstate/object_state.py (U3 library release); confirmed __objectstate_delegate__ = 'pipeline_config', initialize opening the configured DatasetSource and calling its SourceSelectionRole.require_available_source on dataset_root, and once-per-compilation resolution; no prose change needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/orchestrator_configuration_management.rst against B6's later changes to openhcs/core/orchestrator/orchestrator.py, openhcs/core/pipeline/compiler.py, openhcs/core/dataset_sources/source.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/orchestrator_configuration_management.rst against the later B6 changes to openhcs/core/orchestrator/orchestrator.py, openhcs/core/pipeline/compiler.py, openhcs/core/dataset_sources/source.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/orchestrator_configuration_management.rst in full and compared it against the changed authorities: orchestrator.py, pipeline/compiler.py, objectstate/object_state.py; checked the pipeline_config ObjectState delegation, once-per-compilation resolution, and the initialize availability check (now dataset_source.open plus SourceSelectionRole.require_available_source); corrected the availability paragraph's handler/plate-path vocabulary and added dataset_sources/source.py as authority.",
"Re-read docs/source/architecture/orchestrator_configuration_management.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/orchestrator/orchestrator.py; openhcs/core/pipeline/compiler.py; external/ObjectState/src/objectstate/object_state.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -326,7 +350,7 @@
"authority": [
{
"path": "external/pyqt-reactive/src/pyqt_reactive/forms/parameter_form_manager.py",
- "sha256": "a561e62e29af42630fa107b0929fc87b74007f7ed14ec8f0a6881c49070dc923",
+ "sha256": "2c59c3dc2f95a349970815a33c2afcf5158c97f4627973a8a0c6826f8ca9ae6c",
"role": "Reusable parameter-form lifecycle and value projection."
},
{
@@ -346,10 +370,11 @@
}
],
"findings": [
- "The transition page linked the same OpenHCS boundary twice instead of linking the extracted owner narrative."
+ "The page carries no product claim beyond the redirect; after U3 moved more generic Qt machinery into pyqt-reactive, parameter_form_manager.py, deferred_callback.py and the owner page still exist at the linked targets, so the redirect stays correct."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/parameter_form_lifecycle.rst in full and compared it against the changed authorities: pyqt_reactive/forms/parameter_form_manager.py (U3 library release); confirmed ParameterFormManager still owns form build, completion/failure callbacks and value projection, and that the pyqt-reactive owner page and external_foundations target exist; no prose change needed.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"Sphinx resolved the pyqt-reactive intersphinx target backed by its current owner source",
"Re-reviewed the public form-build completion, failure, and signal lifecycle against the complete dependent page and the updated pyqt-reactive owner documentation.",
@@ -397,7 +422,7 @@
"authority": [
{
"path": "external/pyqt-reactive/src/pyqt_reactive/forms/parameter_form_manager.py",
- "sha256": "a561e62e29af42630fa107b0929fc87b74007f7ed14ec8f0a6881c49070dc923",
+ "sha256": "2c59c3dc2f95a349970815a33c2afcf5158c97f4627973a8a0c6826f8ca9ae6c",
"role": "Reusable parameter-form lifecycle and value projection."
},
{
@@ -407,10 +432,11 @@
}
],
"findings": [
- "The transition page duplicated the OpenHCS boundary link and omitted the extracted owner document."
+ "The page carries no product claim beyond the redirect; after U3, parameter_form_manager.py and the pyqt-reactive parametric_widget_creation owner page still exist at the linked targets, so the redirect stays correct."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/parametric_widget_creation.rst in full and compared it against the changed authorities: pyqt_reactive/forms/parameter_form_manager.py (U3 library release); confirmed ParameterFormManager still builds widgets from parameter declarations (build_form/setup_ui) and that the owner page and external_foundations target exist; no prose change needed.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"Sphinx resolved the current owner-document target",
"Re-reviewed the public form-build completion, failure, and signal lifecycle against the complete dependent page and the updated pyqt-reactive owner documentation.",
@@ -429,32 +455,34 @@
"authority": [
{
"path": "external/python-introspect/src/python_introspect/signature_analyzer.py",
- "sha256": "73600e546753640964d4ba5308fdf808ed3a4a6f085417d8ac0745d9e2748ed7",
- "role": "Generic callable signature-analysis ownership."
+ "sha256": "edbe51f6c92b2c334dc4533bbeaa252be649c1ac738d2ebfca4364f2708479dc",
+ "role": "SignatureAnalyzer: the generic callable signature analysis the redirect assigns to python-introspect."
},
{
"path": "external/python-introspect/src/python_introspect/callable_declaration.py",
"sha256": "3bb8b474624fe6b2964a5c1f9a6aa540b2703500bfa124e7808995cb789d6951",
- "role": "Orders supplied parameters by signature with optional default omission and caller-owned equality."
+ "role": "Generic callable-declaration projection: orders supplied parameters by signature with optional default omission."
},
{
"path": "external/python-introspect/docs/source/index.rst",
- "sha256": "c98c4bcc2623fb87c1b4eb0858819ed11efa3618fd0fa56e8df3dccba415d6a9",
- "role": "Current owner-package narrative for the extracted mechanism referenced by docs/source/architecture/parser_metaprogramming_system.rst."
+ "sha256": "63f46ce920b1c34b3c48fff239f45dce9496d4b46830380bad8618c2d10a873c",
+ "role": "Owner-package documentation the redirect points readers toward."
},
{
"path": "openhcs/processing/backends/lib_registry/registry_service.py",
- "sha256": "046d3d088f685a993ddad6b5923cf63beef84b201127b72ae32af6721dd5ad63",
- "role": "Canonical callable catalogue preparation, declaration-local projection, lookup caching, and complete derived-view clearing."
+ "sha256": "f2e981157632adb5020828720ec26bfa6444ab52f545e1e71b1524242eaed6a3",
+ "role": "OpenHCS consumer: imports SignatureAnalyzer from python-introspect for callable catalogue preparation instead of keeping its own signature parser."
}
],
"findings": [
- "Reusable parser and signature mechanics have moved to python-introspect; this page is only a compatibility route.",
- "The redirect repeated the same external-foundations destination instead of stating the package policy once.",
- "python-introspect now also owns generic callable-declaration projection."
+ "The page carries no product claim beyond the redirect; the only owner change is registry_service.py reading processing_contract from the callable declaration instead of its metadata (K5), which this page does not describe."
],
- "disposition": "revise",
+ "disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/parser_metaprogramming_system.rst in full and compared it against the changed authorities: lib_registry/registry_service.py (K5: processing_contract read from reference.declaration); confirmed registry_service.py still imports SignatureAnalyzer from python-introspect and that external_foundations exists; no prose change needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/parser_metaprogramming_system.rst against B6's later changes to openhcs/processing/backends/lib_registry/registry_service.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/parser_metaprogramming_system.rst against the later B6 changes to openhcs/processing/backends/lib_registry/registry_service.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/parser_metaprogramming_system.rst in full and compared it against the changed authorities: python_introspect/signature_analyzer.py, python-introspect docs index, lib_registry/registry_service.py; confirmed python-introspect still owns signature analysis and declaration projection, registry_service.py now imports SignatureAnalyzer from it, and the external_foundations target exists; no prose change needed. Disposition corrected from revise to redirect, since the page is only a route.",
"Re-read docs/source/architecture/parser_metaprogramming_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: external/python-introspect/src/python_introspect/signature_analyzer.py; external/python-introspect/docs/source/index.rst; openhcs/processing/backends/lib_registry/registry_service.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -478,32 +506,37 @@
"authority": [
{
"path": "openhcs/core/source_bindings.py",
- "sha256": "e0afbbe40cce3847bcc2314910d0083ecc579a5a0d6e4278c3a5df4c62e7eb01",
- "role": "Public typed source-binding declaration vocabulary."
+ "sha256": "890473715373d1adc6b12fec2b57eab6f179cbca7d837920c0388d620ce8b8c2",
+ "role": "Typed source-binding declaration vocabulary: bindings, selectors, metadata rules, the SourceSetPairing family (PairByOrder, PairByMetadata) and the compiled plan."
},
{
"path": "openhcs/core/source_binding_selection.py",
- "sha256": "5afcabc6dae1181ae219294baedefe3c1b7ef41bbe03d44dd779a126c9405df1",
- "role": "Nominal source selection, matching, and component projection strategies."
+ "sha256": "e837296accf4dd3353956a8e5f267d3fdd4581fb01db1227bc592a2ed6b4d0d3",
+ "role": "Source candidate matching, anchor-pattern policies, and source-universe requests that replaced pattern detection."
},
{
- "path": "openhcs/microscopes/source_bindings_handler.py",
- "sha256": "88f7c624310ea31a4cf5de884fdae295e4675a578f8a2821a837140e4e4b07b2",
- "role": "Arbitrary-folder ingestion and declared source-binding projection."
+ "path": "openhcs/core/dataset_sources/source_bindings_source.py",
+ "sha256": "63e4a3f849ab5122ab7af8767a6288135b7330027dff962c80add93d5fd820f1",
+ "role": "SourceBindingsSource: the dataset source that ingests arbitrary image folders through source-binding declarations."
}
],
"findings": [
- "The former pattern-detection model has been replaced by typed source declarations and strategies."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/pattern_detection_system.rst in full and compared it against the changed authorities: openhcs/core/source_bindings.py and openhcs/core/source_binding_selection.py (E1: source-binding kinds became class families; anchor and match status enums deleted; SourceUniverseRequest keyed by binding origin class); confirmed the source_model target exists and that SourceSetPairing, PairByOrder and PairByMetadata are still declared in source_bindings.py; the page names no module or class, so its text needed no change.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/pattern_detection_system.rst in full and compared it against the changed authorities: source_bindings.py (K4: config classes moved to core/config.py, SourceSetPairing replaces SourceBindingMatchPlan), source_binding_selection.py, dataset_sources/source_bindings_source.py; the page only links to source_model, which exists; corrected the authority role only.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/pattern_detection_system.rst against B6's later changes to openhcs/core/source_binding_selection.py, openhcs/core/dataset_sources/source_bindings_source.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/pattern_detection_system.rst against the later B6 changes to openhcs/core/source_bindings.py, openhcs/core/source_binding_selection.py, openhcs/core/dataset_sources/source_bindings_source.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/pattern_detection_system.rst in full and compared it against the changed authorities: source_bindings.py, source_binding_selection.py, and the deleted microscopes/source_bindings_handler.py (G4, c6d9a52ce); confirmed typed source matching still owns the model and the source_model target page exists; replaced the deleted handler authority with dataset_sources/source_bindings_source.py; no prose change needed.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"tests/unit/test_source_bindings.py and tests/unit/test_source_projection.py passed against the current source model"
]
},
{
"path": "docs/source/architecture/pattern_grouping_and_special_outputs.rst",
- "source_sha256": "42dcebbbb001891b323a38fb786c50ad1def8d13f52ec0b77750fd8b846ab293",
+ "source_sha256": "ebcb6664cbc3b3e80fb6dfa26e3f03c5b8990a00fef5c499052fb3aad5ef6f1b",
"audience": [
"OpenHCS maintainers migrating older grouping and special-output concepts"
],
@@ -512,30 +545,35 @@
"authority": [
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Pipeline, global, step, processing, and source configuration declarations."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "ProcessingConfig field docstrings: variable_components is the stack-axis authority and group_by partitions already assembled values, used for dictionary-pattern branch selection."
},
{
- "path": "openhcs/core/pipeline/artifact_planning.py",
- "sha256": "8f2a9fe778df5699218185b2b52f9537a57a929f64ae2a43915d14e04a2e7135",
- "role": "Exact ArtifactSpecRef planning and nominal artifact strategy selection."
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "ProcessingContract family, which moved here from the library registry module (G5)."
},
{
- "path": "openhcs/core/runtime_stores.py",
- "sha256": "5f323ec8edd4221ee30e34ed0c9c09e0f4e45ef502749c1a2d331b7a5452af99",
- "role": "RuntimeValueStore recording, replacement, query, and observation semantics."
+ "path": "openhcs/core/pipeline/artifact_planning.py",
+ "sha256": "377558d738198216e9f240af6e19dfd71b5a81d826a2044ec957fa410abcdf92",
+ "role": "Artifact producer/consumer graph and output materialization planning behind the artifact-contract route."
},
{
- "path": "openhcs/processing/backends/lib_registry/unified_registry.py",
- "sha256": "70462cddcad1bfee447ce0786a9e4a85abbd297726d59a1208156af0ef3a3743",
- "role": "Processing-contract execution, registry-owned catalogue admission and cache semantics, and external-signature authoring exclusions."
+ "path": "openhcs/core/runtime_stores.py",
+ "sha256": "35fe71906bfd16842de347776a0c5cc846550ad490cc3628885129d746ec6f31",
+ "role": "RuntimeValueStore and runtime artifact queries behind the runtime-value route."
}
],
"findings": [
- "The previous URL combined independent grouping, callable, artifact, and path semantics and now serves only as a migration route."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/pattern_grouping_and_special_outputs.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1: MaterializationBackend and InputSource class families), openhcs/core/pipeline/artifact_planning.py (K2 artifacts package imports, E1 StepInputDependency subclasses) and openhcs/core/runtime_stores.py (K2 imports); confirmed the ProcessingConfig variable_components and group_by docstrings are unchanged and all four link targets exist; the page text needed no change.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/pattern_grouping_and_special_outputs.rst in full and compared it against the changed authorities: core/config.py, core/processing_contracts.py, pipeline/artifact_planning.py and core/runtime_stores.py; confirmed the ProcessingConfig docstrings still make variable_components the assembled-array axis and group_by a partition of already assembled values, ProcessingContract and RuntimeValueStore exist, and the four linked pages exist; removed one filler word.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/pattern_grouping_and_special_outputs.rst against B6's later changes to openhcs/core/config.py, openhcs/core/processing_contracts.py, openhcs/core/runtime_stores.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/pattern_grouping_and_special_outputs.rst against the later B6 changes to openhcs/core/config.py, openhcs/core/processing_contracts.py, openhcs/core/runtime_stores.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/pattern_grouping_and_special_outputs.rst in full and compared it against the changed authorities: core/config.py, pipeline/artifact_planning.py, core/runtime_stores.py, lib_registry/unified_registry.py; checked the group_by/variable_components claims against the ProcessingConfig docstrings, confirmed ProcessingContract still exists (now in core/processing_contracts.py) and that all four target pages exist; replaced unified_registry.py with processing_contracts.py as authority; no prose change needed.",
"Re-read docs/source/architecture/pattern_grouping_and_special_outputs.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/config.py; openhcs/core/pipeline/artifact_planning.py; openhcs/core/runtime_stores.py; openhcs/processing/backends/lib_registry/unified_registry.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -548,7 +586,7 @@
},
{
"path": "docs/source/architecture/pipeline_compilation_system.rst",
- "source_sha256": "a8b349cd9e24e5ca683416666c05ce71e9c74fbdddd4f216f20c1b0837ac36a6",
+ "source_sha256": "aa4e93269f118b794c42b4d42270dda228308194ec33b9d7170c4d373714b293",
"audience": [
"OpenHCS compiler, runtime, and extension maintainers"
],
@@ -557,77 +595,64 @@
"authority": [
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compilation order, declaration resolution, callable warm-up, framework-local device assignment, and bundle construction."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "_resolve_pipeline_once ObjectState resolution, AxisCompilationRequest context projection, stage order, worker assignment, and bundle construction."
},
{
"path": "openhcs/core/pipeline/compilation_session.py",
- "sha256": "3c840a89baccc8665d415175458280fd904c1a837b7fd879e303d7dbca129a1c",
- "role": "Resolved step/session identity and typed plan assembly invariants."
- },
- {
- "path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
- "role": "CompiledExecutionBundle and runtime-environment authority; execution-wide worker facts and context-derived axis identities remain separate from per-step framework devices."
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "ResolvedPipelineDefinition scope/provenance validation and CompilationSession fields and missing-plan errors."
},
{
- "path": "openhcs/core/context/processing_context.py",
- "sha256": "c7b79f50661c528261ce8c577fd67c753c84a5e499aa87cd5e540b53417351a2",
- "role": "Owns the compiled multiprocessing-axis identity carried by each runtime context."
- },
- {
- "path": "tests/unit/test_compiled_execution.py",
- "sha256": "a7ef4c3f011e0770d8e7a48928aa25c6ca550095c1f80146a25170d449ecc31f",
- "role": "Verifies that bundle axis projection derives from context declarations rather than mapping-key syntax."
- },
- {
- "path": "openhcs/core/orchestrator/compiled_plate_execution.py",
- "sha256": "80998f355c3f4be9027cb91cabf581f51efd870fb86960f5f6ecd615bcdc89ef",
- "role": "Parent execution ordering for plate-scoped work, completed-output metadata projection, and viewer settlement."
+ "path": "openhcs/core/compiled_step_plan.py",
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "CompiledStepPlan typed field inventory and per-step framework-device assignment."
},
{
- "path": "openhcs/core/steps/function_outputs.py",
- "sha256": "e02275c0c1d8ac00633d97f22d54bc5569dddc4f11971a0106743b94a78c8d5b",
- "role": "Declaration-owned OpenHCS metadata targets, typed projection persistence, and completed-plate deduplication across compiled axes."
+ "path": "openhcs/core/compiled_execution.py",
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "CompiledExecutionBundle contexts, worker assignments and context-derived axis ids; CompiledRuntimeEnvironmentPlan start-method reason, server mode, GPU and worker facts."
},
{
- "path": "tests/unit/test_compilation_session.py",
- "sha256": "4224c87a6b67e4d8dde2a5c6b786ea7998474819de56da2b9d954aa2b479feb4",
- "role": "Regression proof for one-to-one step, state, snapshot, context, and plan identity."
+ "path": "openhcs/core/callable_contract.py",
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "CallableContract.from_prepared_callable, process-local resolve_runtime_callable cache, and AXIS/PLATE FunctionStepExecutionScope."
},
{
- "path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
- "role": "Callable-declared input, output, and execution framework roles consumed by compilation."
+ "path": "openhcs/core/function_patterns.py",
+ "sha256": "c8004ae1e3f241c4ac6ce0a61c69e3c0d1ecfb5021e1bef5dd0fb1fd1251ca50",
+ "role": "CompiledFunctionInvocation binds its runtime callable at construction and rebuilds it after process transport."
},
{
- "path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
- "role": "Per-step framework-device assignment and typed compiled-plan ownership."
+ "path": "openhcs/processing/backends/lib_registry/registry_service.py",
+ "sha256": "f2e981157632adb5020828720ec26bfa6444ab52f545e1e71b1524242eaed6a3",
+ "role": "prepare_in_current_process endpoint catalogue preparation and registered_callable owner projection."
},
{
- "path": "openhcs/core/pipeline/framework_device_assignment.py",
- "sha256": "f77f3fb2d30c4af8cab6210dbe294f9fb5e246dad0a6bbf88bcaf1e2b58dead7",
- "role": "Pipeline-wide declaration-derived framework footprint and per-step binding selection."
+ "path": "openhcs/core/orchestrator/bundle_execution.py",
+ "sha256": "25446d6c6a43c22ff7104c6712c143f0276bc4749c2642fffac10db1455ef707",
+ "role": "Parent ordering of plate-scoped steps, completed-dataset metadata finalization, and viewer settlement."
},
{
- "path": "openhcs/core/framework_device_resolver.py",
- "sha256": "be96430174bc576c0b8083879c579ef3fb9b83c4690234751459c2d7bc5556a8",
- "role": "Compile-time framework-local device discovery and fail-closed availability validation."
+ "path": "openhcs/core/steps/function_outputs.py",
+ "sha256": "a8a8fd0297c421a8a9fa6dc0d6d3b8be952a5ba426e7c7811faf70bfc21fe38d",
+ "role": "OpenHCSMetadataTarget declarations, typed projection persistence, and completed-plate deduplication across axes."
},
{
- "path": "tests/unit/test_framework_device_resolver.py",
- "sha256": "ba1814ae8eae05a39b594a81b4fc0d1444bf1334574bf4efe49aa829eec43a65",
- "role": "Regression proof for independent framework namespaces and exact per-step selection."
+ "path": "tests/unit/test_compilation_session.py",
+ "sha256": "f7d5483997bf604f2c4fccaf08c165ae1a7c881079a250add3c98ce02250e2c0",
+ "role": "Regression proof that axis sessions share one resolved pipeline and own their axis plans."
}
],
- "findings": [
- "The typed-plan inventory still described a global GPU assignment and did not explain declaration-owned native catalogue placement.",
- "The execution explanation did not state when multi-axis output metadata becomes a complete plate projection.",
- "The execution-bundle explanation did not distinguish context-owned axis identity from runtime-context mapping keys."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/pipeline_compilation_system.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, compilation_session.py, compiled_step_plan.py, callable_contract.py, function_patterns.py, orchestrator/bundle_execution.py, steps/function_outputs.py and tests/unit/test_compilation_session.py (K2: artifacts package imports; E1: OrchestratorState, MaterializationBackend, InputSource, StepInputDependency and RuntimeObservationMode class families); the page names none of the replaced enum members; AXIS/PLATE FunctionStepExecutionScope, _resolve_pipeline_once, AxisCompilationRequest, from_prepared_callable, prepare_in_current_process and registered_callable are unchanged, so no prose change was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/pipeline_compilation_system.rst in full and compared it against the changed authorities: pipeline/compiler.py, compilation_session.py (graph.validate_invocation_declarations; disabled-function stripping dropped from normalization), compiled_step_plan.py, callable_contract.py (K5), function_patterns.py (AuthoredFunctionPattern, graph-resolved invocation compile), lib_registry/registry_service.py, orchestrator/bundle_execution.py and tests/unit/test_compilation_session.py; re-verified _resolve_pipeline_once, ResolvedPipelineDefinition, CompilationSession, AxisCompilationRequest, FunctionStepTransportAuthority, RegistryService.registered_callable/prepare_in_current_process, CallableContract.from_prepared_callable, resolve_runtime_callable, CompiledFunctionInvocation.runtime_callable, CompiledRuntimeEnvironmentPlan, input/output memory types on CompiledStepPlan, AXIS/PLATE scopes and OpenHCSMetadataTarget; removed one filler word.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/pipeline_compilation_system.rst against B6's later changes to openhcs/core/steps/function_outputs.py: metadata documents are written only for backends that host local files (not virtual backends such as OMERO); the image-source guide test reads axis-family spellings. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/pipeline_compilation_system.rst against B6's later changes to openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py, openhcs/core/callable_contract.py, openhcs/core/function_patterns.py, openhcs/processing/backends/lib_registry/registry_service.py, openhcs/core/orchestrator/bundle_execution.py, openhcs/core/steps/function_outputs.py, tests/unit/test_compilation_session.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/pipeline_compilation_system.rst against the later B6 changes to openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py, openhcs/core/callable_contract.py, openhcs/core/function_patterns.py, openhcs/processing/backends/lib_registry/registry_service.py, openhcs/core/orchestrator/bundle_execution.py, openhcs/core/steps/function_outputs.py, tests/unit/test_compilation_session.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/pipeline_compilation_system.rst in full and compared it against the changed authorities: pipeline/compiler.py, compilation_session.py, compiled_execution.py, context/processing_context.py, test_compiled_execution.py, compiled_plate_execution.py (renamed bundle_execution.py), steps/function_outputs.py, test_compilation_session.py, callable_contract.py, compiled_step_plan.py, framework_device_assignment.py; verified each named class/function exists (FunctionStepTransportAuthority, RegistryService.registered_callable, AxisCompilationRequest, CompiledRuntimeEnvironmentPlan, RuntimeArtifactBatch, OpenHCSMetadataTarget, FunctionStepExecutionScope AXIS/PLATE); rewrote the resolve-once, preparation, session and plan-inventory passages that named deleted StepSnapshot, prepare_compiled_context_callables and source load plans. Dropped the framework-device resolver/assignment files and test_framework_device_resolver.py as authorities since the page states only that bindings stay on step plans.",
"Re-read docs/source/architecture/pipeline_compilation_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline/compiler.py; openhcs/core/pipeline/compilation_session.py; openhcs/core/compiled_execution.py; openhcs/core/orchestrator/compiled_plate_execution.py; openhcs/core/steps/function_outputs.py; tests/unit/test_compilation_session.py; openhcs/core/callable_contract.py; openhcs/core/compiled_step_plan.py; openhcs/core/pipeline/framework_device_assignment.py; openhcs/core/framework_device_resolver.py; tests/unit/test_framework_device_resolver.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -645,82 +670,85 @@
},
{
"path": "docs/source/architecture/plate_manager_services.rst",
- "source_sha256": "06abc350333f3c0d51f3418db49de6c51d7eb3f5d27d523e1a113a5b2b2b943d",
+ "source_sha256": "428c259d79266a0ddd55efb210a8fe0bac05ccce1d373789c1cd2ff2b8bfe757",
"audience": [
- "OpenHCS desktop workflow maintainers"
+ "OpenHCS session and client (GUI, MCP, script) maintainers"
],
- "user_need": "Locate the service that owns a Plate Manager compile, submit, control, progress, or debug responsibility.",
+ "user_need": "Locate the session module that owns a dataset, pipeline, compile, run, control, progress, or debug responsibility.",
"diataxis": "reference",
"authority": [
{
- "path": "openhcs/pyqt_gui/services/plate_manager_batch_workflow.py",
- "sha256": "974aebec62dc0f283f92d8153594771d1206f02decdfd8ea5faae0a563676699",
- "role": "Plate Manager compile-all-then-execute-all facade and lifecycle."
+ "path": "openhcs/authoring/session/session.py",
+ "sha256": "ced615b580e4625e09873449b29ac67cdea87c8b8065c205f32e6d5921cf31fc",
+ "role": "Session class, MainThread/DispatcherThread, DatasetAccess and Renderer ports, dataset add/delete/split and work/mutation checks."
},
{
- "path": "openhcs/pyqt_gui/widgets/shared/services/batch_workflow_components.py",
- "sha256": "abd29f375ab12fdc56058f1c860ce26c3b52129658795eb1c95581d83b7480bd",
- "role": "Lazy owner of Plate Manager compile, submit, control, progress, debug, and measurement services."
+ "path": "openhcs/authoring/session/operations/__init__.py",
+ "sha256": "d890789ee230059f6e77fea0c991c38362dd2c726e6ade1af7f203607c85cb90",
+ "role": "SessionOperation, HeadlessOperation and RendererOperation family roots."
},
{
- "path": "tests/unit/pyqt_gui/test_plate_manager_widget.py",
- "sha256": "b02e81c4a73213577df02a1effbd6843935f5c92f459d6c61ab2998bd489ad58",
- "role": "Regression evidence for Plate Manager service composition and UI delegation."
+ "path": "openhcs/authoring/session/views.py",
+ "sha256": "296adbceb7db7a7f7ac3cb09dbe8bb6620dec588a3b593ef2239108298bee5c8",
+ "role": "SessionView family with DatasetListView and PipelineStepsView."
},
{
- "path": "openhcs/pyqt_gui/widgets/shared/services/plate_manager_workflows.py",
- "sha256": "d640bf4b2710009dc3f92719ec97cf09fe3d93ed7eb1bebaf2b02913e8fafbfd",
- "role": "Owns all-plates and selected-plates code-document mutation semantics."
+ "path": "openhcs/authoring/session/events.py",
+ "sha256": "3df85cff1c283becb247d9c5cb24fe1441c8dcaf8d2810b560b7398d41834449",
+ "role": "SessionEvent family and event log with subscribe and events-after reads."
},
{
- "path": "openhcs/pyqt_gui/services/pipeline_object_state_binding.py",
- "sha256": "afb190ac9ac38e800c8fe04497bbb78a2f1b6381a21eb14890e3061c61ae5b0b",
- "role": "Owns incremental pipeline binding and complete declaration-graph replacement."
+ "path": "openhcs/authoring/session/compile_batch.py",
+ "sha256": "3e7397caa5ea1b38c1abc47d363e37fc01b0b601ab1e6b37ed00226f7aa51f42",
+ "role": "Compile batches that check work is allowed and mark datasets compile-pending before connecting."
},
{
- "path": "openhcs/core/steps/function_step.py",
- "sha256": "8e1243404095691030e9ebb95bb5611679f96c66c6b6819ae71e4b41249e74bc",
- "role": "Owns complete FunctionStep declaration equality through canonical callable-pattern projection."
+ "path": "openhcs/authoring/session/dataset_document.py",
+ "sha256": "ddb55e52107819b22d7bb7d18e6310d98eb0600471a722cb789a5a68028d26f3",
+ "role": "DatasetDocumentScope family and payload scope-id check for selected code documents."
},
{
- "path": "openhcs/pyqt_gui/widgets/shared/services/pipeline_editor_workflows.py",
- "sha256": "6664126ac4daa851cacff5014e04a0c09a56bbc76f62661ff97d713e9137d447",
- "role": "Applies a complete Pipeline Editor document and commits the reconciled ObjectState graph."
+ "path": "openhcs/authoring/session/pipelines.py",
+ "sha256": "febf9f9f464b2600554256f16929c408d4bdcaedd6ca99c71810bcb7be519519",
+ "role": "Pipeline declaration reconciliation and saved-baseline commits."
},
{
- "path": "openhcs/agent/services/endpoint_function_catalog_service.py",
- "sha256": "977a74003819f2547548d322f4c5f0cb148b23fec20f31be4fa462bfcfb7ea04",
- "role": "Owns the shared asynchronous endpoint catalogue used by desktop startup, the Function Selector, and local MCP authoring."
+ "path": "openhcs/agent/capabilities.py",
+ "sha256": "87c8e2e5aeb425b9a2eb690d48bdd6f5af7a327c389deac261d7e4e0da5d609f",
+ "role": "SessionOperationInvocation deriving MCP tools from headless operations, and the openhcs_session_events capability."
},
{
- "path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
- "role": "Coordinates post-paint endpoint preparation and callable-catalogue prewarming."
+ "path": "openhcs/core/pipeline_import.py",
+ "sha256": "b48f0a37e621f36fe256138472c4bda1e87d2d0d45be83361a40ce6cbfea4e55",
+ "role": "PipelineImporter family keyed by file suffix."
},
{
- "path": "tests/unit/pyqt_gui/test_pipeline_editor_widget.py",
- "sha256": "8f28719c5bd9eb5681e67af8e1f7aceb445bcae97c12a13348f943c8b6bafa35",
- "role": "Proves exact reconciled editor state using the installed synthetic demo pipeline declaration."
+ "path": "openhcs/interop/cellprofiler/dataset_scope.py",
+ "sha256": "0a3dc691a04c95bdb115fe1586374af1ea8401787b600e2c109c1ad8acfcaf43",
+ "role": "CellProfiler dataset scope pairing a source folder with one .cppipe."
},
{
- "path": "openhcs/demo/synthetic_plate_pipeline.py",
- "sha256": "3f8916ff71938ac5b53c7f55329b037653519e9592cf564257f78dde158fbd11",
- "role": "Installed synthetic pipeline declaration used by the Pipeline Editor widget regression fixture."
+ "path": "openhcs/pyqt_gui/session_rendering.py",
+ "sha256": "deee8c93c5bfd41c5ee25abe959938426ed17120275b3b6ee9ccc25b2dd0d690",
+ "role": "QtSessionEventRelay GUI subscriber."
},
{
- "path": "openhcs/pyqt_gui/services/plate_manager_state_projection.py",
- "sha256": "814651335543000eb3855975a80af311b6290926da233d02ab5e95ba0daa82ba",
- "role": "Owns lightweight current row activity and the richer result relation projection."
+ "path": "openhcs/mcp/server.py",
+ "sha256": "c3dd3a396a3c304341b06c7c813036e779c921fa915060d2d3d2ed594b00b697",
+ "role": "MCP binds the session to DispatcherThread over McpMainThreadDispatcher."
}
],
- "findings": [
- "The reference omitted code-document mutation scope and the identity distinction between incremental edits and complete pipeline replacement.",
- "The service map omitted Pipeline Editor saved-baseline commits and the asynchronous endpoint-owned function-catalogue projection.",
- "Callable occurrence identity now excludes wrapper and explicit-default differences that do not change a FunctionStep declaration.",
- "Cleanup previously skipped the client owner before endpoint admission; the reference now records one teardown boundary for admitted clients and in-progress attempts."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/plate_manager_services.rst in full and compared it against the changed authorities: openhcs/authoring/session/session.py (read in its current form), views.py, events.py, compile_batch.py and dataset_document.py (E1: orchestrator state, terminal status, debug phase and selection scope became class families spelled by name) and openhcs/mcp/server.py (tool schemas spell family defaults by registry key); confirmed Session, MainThread, DispatcherThread, DatasetAccess, Renderer, the SessionView and SessionOperation roots, DatasetDocumentScope, every listed session module, core/dataset_sources/dataset_scopes.py, QtSessionEventRelay and the DispatcherThread binding in MCP and the GUI; no prose change was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/plate_manager_services.rst in full and compared it against the changed authorities: authoring/session/session.py (K6: debug command and replay mode are now classes such as RunDebugCommand and PersistentPausedWorkerReplay), dataset_document.py (L6: retain_only_authored_paths now from objectstate) and pipelines.py (U3: function scope ids built with ScopeKey); re-checked every module in the ownership map, MainThread/DispatcherThread/DatasetAccess/Renderer, QtSessionEventRelay, openhcs_session_events, McpMainThreadDispatcher, require_work_allowed/require_definition_mutation_allowed and PipelineImporter; none of the changed names appears on the page, so no prose change was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/plate_manager_services.rst against B6's later changes to openhcs/authoring/session/session.py: the writability probe makes one exclusive create attempt, the installed demo reports ViewerType.value, the unit session pre-creates its QApplication, and the GUI journey waits for each batch to end. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/plate_manager_services.rst against B6's later changes to openhcs/authoring/session/views.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/plate_manager_services.rst against B6's later changes to openhcs/authoring/session/session.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/plate_manager_services.rst against B6's later changes to openhcs/authoring/session/session.py, openhcs/authoring/session/views.py, openhcs/authoring/session/events.py, openhcs/authoring/session/compile_batch.py, openhcs/authoring/session/dataset_document.py, openhcs/pyqt_gui/session_rendering.py, openhcs/mcp/server.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/plate_manager_services.rst against the later B6 changes to openhcs/authoring/session/session.py, openhcs/authoring/session/views.py, openhcs/authoring/session/events.py, openhcs/authoring/session/compile_batch.py, openhcs/authoring/session/dataset_document.py, openhcs/agent/capabilities.py, openhcs/pyqt_gui/session_rendering.py, openhcs/mcp/server.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/plate_manager_services.rst in full and compared it against the changed authorities: the page itself (rewritten in U1), deleted plate_manager_batch_workflow.py, batch_workflow_components.py, pipeline_object_state_binding.py and plate_manager_state_projection.py, changed plate_manager_workflows.py, pipeline_editor_workflows.py, endpoint_function_catalog_service.py, pyqt_gui/main.py, the Plate Manager and Pipeline Editor widget tests, and demo/synthetic_plate_pipeline.py; verified every named module in openhcs/authoring/session, SessionOperation/SessionView/SessionEvent families, DatasetListView, PipelineStepsView, DatasetDocumentScope, PipelineImporter keyed by suffix, the CellProfiler .cppipe dataset scope, QtSessionEventRelay, openhcs_session_events, DispatcherThread use in the GUI and MCP, compile-artifact ids on submission and compile-pending reservation; replaced all authorities with session-package owners.",
"Re-read docs/source/architecture/plate_manager_services.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/pyqt_gui/services/plate_manager_batch_workflow.py; openhcs/pyqt_gui/widgets/shared/services/batch_workflow_components.py; tests/unit/pyqt_gui/test_plate_manager_widget.py; openhcs/pyqt_gui/widgets/shared/services/plate_manager_workflows.py; openhcs/pyqt_gui/services/pipeline_object_state_binding.py; openhcs/pyqt_gui/widgets/shared/services/pipeline_editor_workflows.py; openhcs/agent/services/endpoint_function_catalog_service.py; openhcs/pyqt_gui/main.py; tests/unit/pyqt_gui/test_pipeline_editor_widget.py; openhcs/demo/synthetic_plate_pipeline.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -752,25 +780,26 @@
"authority": [
{
"path": "external/metaclass-registry/src/metaclass_registry/core.py",
- "sha256": "ed4dd1dfd0e4e05e916fec39f19b4b8bd526e776aa0c96e3abe9b1ffbdff0505",
- "role": "Generic AutoRegisterMeta registry ownership and configuration mechanics."
+ "sha256": "72c8284e8a32dd193f13f36a425289d5293a32f4a381046db523aa87182e0be8",
+ "role": "AutoRegisterMeta, RegistryConfig and LazyDiscoveryDict: the generic registration mechanism the redirect assigns to metaclass-registry."
},
{
- "path": "external/metaclass-registry/docs/source/patterns.rst",
- "sha256": "cb67e1b458cb073d226ad6691f4ed107bd35b887ad65452157a7283009707402",
- "role": "Current owner-package narrative for the extracted mechanism referenced by docs/source/architecture/plugin_registry_advanced.rst."
+ "path": "external/metaclass-registry/src/metaclass_registry/strategies.py",
+ "sha256": "5c00cd4d80f9f0ceffbc2ab95ad8c6abd44a8a66a974959445ef93df08dd4f0d",
+ "role": "Enum-keyed, nominal-type-keyed and most-derived context strategy mixins, imported by OpenHCS families from metaclass_registry.strategies."
},
{
- "path": "openhcs/core/registry_strategies.py",
- "sha256": "a41186665abbd308f224d0bb41d6618cd9d89b08acaf54ec2819f2024a6e0804",
- "role": "OpenHCS enum, nominal-type, and most-derived strategy selection implementations."
+ "path": "external/metaclass-registry/docs/source/patterns.rst",
+ "sha256": "cb67e1b458cb073d226ad6691f4ed107bd35b887ad65452157a7283009707402",
+ "role": "Owner-package documentation of advanced registration patterns the redirect points readers toward."
}
],
"findings": [
- "Generic registration mechanics were extracted and this page is now a compatibility route."
+ "Authority openhcs/core/registry_strategies.py was deleted (b221f8491); the enum, nominal-type and most-derived strategy mixins now live in metaclass_registry.strategies, which strengthens the redirect's claim that metaclass-registry owns the reusable mechanism. Only the record changed."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/plugin_registry_advanced.rst in full and compared it against the changed authorities: metaclass_registry/core.py and the deleted openhcs/core/registry_strategies.py (moved to metaclass_registry/strategies.py in b221f8491); confirmed OpenHCS modules import AutoRegisterMeta and the strategy mixins from metaclass_registry and that nominal_ownership and external_foundations exist; no prose change needed.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"Current imports and tests/unit/test_registry_owner_iteration.py confirm the extracted owner and OpenHCS consumer boundary",
"On 2026-08-25, re-read the complete page against metaclass-registry 0.2.1, ArrayBridge 0.3.3, the public native-catalogue projection, declaration-derived child environments, and the updated package floors; revised the page where those semantics are user-relevant and confirmed its remaining claims against current source and tests."
@@ -787,25 +816,26 @@
"authority": [
{
"path": "external/metaclass-registry/src/metaclass_registry/core.py",
- "sha256": "ed4dd1dfd0e4e05e916fec39f19b4b8bd526e776aa0c96e3abe9b1ffbdff0505",
- "role": "Generic AutoRegisterMeta registry ownership and configuration mechanics."
+ "sha256": "72c8284e8a32dd193f13f36a425289d5293a32f4a381046db523aa87182e0be8",
+ "role": "AutoRegisterMeta, RegistryConfig and LazyDiscoveryDict: the generic registration mechanism the redirect assigns to metaclass-registry."
},
{
- "path": "external/metaclass-registry/docs/source/index.rst",
- "sha256": "f707777948f83a5bba22528814b43520e61c76da730d0b93e0c5f3f91d6c5b12",
- "role": "Current owner-package narrative for the extracted mechanism referenced by docs/source/architecture/plugin_registry_system.rst."
+ "path": "external/metaclass-registry/src/metaclass_registry/strategies.py",
+ "sha256": "5c00cd4d80f9f0ceffbc2ab95ad8c6abd44a8a66a974959445ef93df08dd4f0d",
+ "role": "Strategy mixins OpenHCS families now import from metaclass_registry.strategies instead of an OpenHCS module."
},
{
- "path": "openhcs/core/registry_strategies.py",
- "sha256": "a41186665abbd308f224d0bb41d6618cd9d89b08acaf54ec2819f2024a6e0804",
- "role": "OpenHCS enum, nominal-type, and most-derived strategy selection implementations."
+ "path": "external/metaclass-registry/docs/source/index.rst",
+ "sha256": "f707777948f83a5bba22528814b43520e61c76da730d0b93e0c5f3f91d6c5b12",
+ "role": "Owner-package documentation the redirect points readers toward."
}
],
"findings": [
- "Generic registration mechanics were extracted and this page is now a compatibility route."
+ "Authority openhcs/core/registry_strategies.py was deleted (b221f8491); its strategy mixins moved into metaclass_registry.strategies. Only the record changed."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/plugin_registry_system.rst in full and compared it against the changed authorities: metaclass_registry/core.py and the deleted openhcs/core/registry_strategies.py (moved to metaclass_registry/strategies.py in b221f8491); confirmed the redirect's ownership claim and its two target pages; no prose change needed.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"Current imports and registry tests confirm the extracted owner",
"On 2026-08-25, re-read the complete page against metaclass-registry 0.2.1, ArrayBridge 0.3.3, the public native-catalogue projection, declaration-derived child environments, and the updated package floors; revised the page where those semantics are user-relevant and confirmed its remaining claims against current source and tests."
@@ -813,7 +843,7 @@
},
{
"path": "docs/source/architecture/processing_semantics.rst",
- "source_sha256": "9d1a0203f2a4236ea102cacd8e48735a24d93d55d3d5c807ed69a2f30d451278",
+ "source_sha256": "74a5847ebc8c9e7245fd81c44ec3eaca4229edbd31afa3b81863f5fad926afc0",
"audience": [
"Pipeline authors and OpenHCS processing maintainers"
],
@@ -822,41 +852,50 @@
"authority": [
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Pipeline, global, step, processing, and source configuration declarations."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "ProcessingConfig variable_components, group_by and input_source field semantics (input_source chooses an InputSource class), and StepMaterializationConfig."
},
{
- "path": "openhcs/processing/backends/lib_registry/unified_registry.py",
- "sha256": "70462cddcad1bfee447ce0786a9e4a85abbd297726d59a1208156af0ef3a3743",
- "role": "Processing-contract execution, registry-owned catalogue admission and cache semantics, and external-signature authoring exclusions."
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "ProcessingContract family: Pure2DContract, Pure3DContract, FlexibleContract, VolumetricToSliceContract and collapses_input_plane_axis."
},
{
"path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
- "role": "Callable ABI including input/output/execution memory roles, runtime-owned parameter projection, and artifact contracts."
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "CallableDeclaration fields (memory types, allowed group_by roles, stack requirement, processing contract, execution scope, runtime adapter, image mode, ImagePayloadConsumption, artifact declarations) inherited by CallableContract."
},
{
- "path": "openhcs/core/runtime_slice_projection.py",
- "sha256": "8a1cb65d3025ed8a87ee59af26deac9765a8f59b21bf2cd61b6f96d82999641d",
- "role": "Nominal runtime slice projection registry and aggregation behaviour."
+ "path": "openhcs/core/payload_axes.py",
+ "sha256": "5d05309c16f8704a7d384a258961336cb22d6a1b366b36d02c454361982412b1",
+ "role": "Declared tensor payload dimensions (plane axis, spatial domain, other axis specs)."
},
{
- "path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
- "role": "Per-step callable memory footprint and framework-device binding authority."
+ "path": "openhcs/core/runtime_plane_projection.py",
+ "sha256": "cf644451b6649dcec398fecbf961a4a29ea74f0bb2e0bdab052ec1be8a427f95",
+ "role": "RuntimeSliceProjectableValue: owned values declare slice count and per-slice projection."
},
{
- "path": "openhcs/core/pipeline/framework_device_assignment.py",
- "sha256": "f77f3fb2d30c4af8cab6210dbe294f9fb5e246dad0a6bbf88bcaf1e2b58dead7",
- "role": "Declaration-derived framework footprint and per-step binding selection."
+ "path": "openhcs/core/runtime_slice_projection.py",
+ "sha256": "f59785d901d0d7bcc3ac1fc1be606bd1728a727ea7347cdc1dd4f549cc3193fa",
+ "role": "RuntimeSliceProjection foreign-value handling and the missing-declaration error."
+ },
+ {
+ "path": "openhcs/core/compiled_step_plan.py",
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "CompiledStepPlan storage of resolved callable facts and framework-local device bindings."
}
],
"findings": [
- "Callable ownership now includes an execution-memory role and compiler-selected framework-local device bindings.",
- "The callable section used the narrower runtime-bound term after artifact, configuration, context, and adapter inputs became one runtime-owned authoring exclusion."
+ "E1 turned InputSource into a class family (ReadPreviousStep, ReadPipelineStart); the common-mistakes list still said 'a third InputSource enum value'. Now 'a third InputSource choice'."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/processing_semantics.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1: InputSource is a class family, ReadPreviousStep and ReadPipelineStart; MaterializationBackend family), callable_contract.py and compiled_step_plan.py (K2 imports), runtime_plane_projection.py and runtime_slice_projection.py (stored_slices, match_alignment, contextualized_output added); corrected the common-mistakes bullet that called InputSource an enum; confirmed the two input-source choices, the four contract classes, RuntimeSliceProjectableValue ownership and the foreign-value role of RuntimeSliceProjection.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/processing_semantics.rst in full and compared it against the changed authorities: core/config.py (K4 source-binding config classes moved in, PartitionFilterMode family; variable_components, group_by, input_source and StepMaterializationConfig unchanged), processing_contracts.py (CallableMetadata renamed CallableDeclaration), callable_contract.py (K5 declared-field CallableDeclaration, CallableContract subclass) and compiled_step_plan.py (empty plan defaults); confirmed InputSource still has two members, the four contract subclasses and FlexibleContract's bases, FunctionStepExecutionScope and ImagePayloadConsumption; no prose change needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/processing_semantics.rst against B6's later changes to openhcs/core/config.py, openhcs/core/processing_contracts.py, openhcs/core/callable_contract.py, openhcs/core/compiled_step_plan.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/processing_semantics.rst against the later B6 changes to openhcs/core/config.py, openhcs/core/processing_contracts.py, openhcs/core/callable_contract.py, openhcs/core/compiled_step_plan.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/processing_semantics.rst in full and compared it against the changed authorities: core/config.py, lib_registry/unified_registry.py, callable_contract.py, runtime_slice_projection.py, compiled_step_plan.py, framework_device_assignment.py; verified the ProcessingConfig field semantics, the four ProcessingContract subclasses and keys, contract=Pure2DContract decorator use, CallableContract fields, FunctionStepExecutionScope and ImagePayloadConsumption; rewrote the payload-rank intro, contract names and the runtime-projection paragraph. Replaced unified_registry.py and framework_device_assignment.py with processing_contracts.py, payload_axes.py and runtime_plane_projection.py as authorities.",
"Re-read docs/source/architecture/processing_semantics.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/config.py; openhcs/processing/backends/lib_registry/unified_registry.py; openhcs/core/callable_contract.py; openhcs/core/runtime_slice_projection.py; openhcs/core/compiled_step_plan.py; openhcs/core/pipeline/framework_device_assignment.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -871,7 +910,7 @@
},
{
"path": "docs/source/architecture/progress_runtime_projection_system.rst",
- "source_sha256": "6cd52fe8420d85bf7429ab128e3ba3a92a42bd158cc35767b9c1673ef51a6441",
+ "source_sha256": "fab87f05006b4b1ce13788ca20e608d6a2d388b38474bd50b1af68bda3a87991",
"audience": [
"OpenHCS runtime, debugger, and UI maintainers"
],
@@ -880,51 +919,75 @@
"authority": [
{
"path": "openhcs/core/progress/projection.py",
- "sha256": "f07ac1c460d0761c2b70c834aae57f77994ac43bb3ef5cd688e4196ccc5d9fce",
- "role": "OpenHCS plate-runtime states and typed domain projection over generic progress."
+ "sha256": "a643ebfa722014a0f03ff5bc168daf14e23a81bbc58f3159bc3bd6e8ba191617",
+ "role": "SubjectRuntimeState family (labels, terminal and server-lifecycle policy), SubjectRuntimeIdentity, state_counts and ExecutionRuntimeProjection heartbeat reconciliation."
+ },
+ {
+ "path": "openhcs/core/progress/runtime_tree.py",
+ "sha256": "75599ae64cf21ebbc2496c0a77b2e3b40b465f867cebc290066fc65c4513dff3",
+ "role": "Runtime tree node kinds subject, worker, partition, step and compilation."
+ },
+ {
+ "path": "openhcs/core/progress/types.py",
+ "sha256": "c46e825cd32801caae9fc107e0759976da054eadc56adc0d13354b3a8b7db209",
+ "role": "ProgressStream, ProgressPhase and ProgressStatus class families (PatternGroupProgressPhase on the step stream, AxisCompletedProgressPhase and SuccessProgressPhase), ProgressEvent and set_progress_queue."
+ },
+ {
+ "path": "openhcs/core/progress/registry.py",
+ "sha256": "4172a15b1f1f5fab4c0490aa5f454f48678bdd477fa1c06d28c37ac0621a01e3",
+ "role": "ProgressRegistry keys retained events by subject, axis and stream."
+ },
+ {
+ "path": "openhcs/core/progress/debug_projection.py",
+ "sha256": "82666d4afc2703b47cf954b2f74b36824142131844bc2c6a7645556381780531",
+ "role": "RuntimeProjectionBuilder and DebugRuntimeProjectionBuilder over one event snapshot; DebugRuntimeProjection current and last frames."
},
{
"path": "openhcs/core/debug.py",
- "sha256": "072259811bc50bfe71cce9a7cac3e9867bfd853bd34ab5a74b01d9fe14b68f36",
- "role": "Nominal debug command, boundary, snapshot, replay, and paused-worker declarations."
+ "sha256": "311b003ce50b69abbbdd934e1ff53cade27105605b6c3d7c980fc4ac81b8c2a4",
+ "role": "DebugCommandType and DebugBoundary families, command-keyed execution and stop strategies, cursor, session, snapshot, artifact identity, snapshot store, warm-replay and paused-worker declarations."
},
{
"path": "openhcs/core/debug_session_projection.py",
- "sha256": "0a630eb03a83d741a4ed55838adb8ee65dd4fde9be07878aeba5e7a8c86c7378",
- "role": "Typed debug-session phase selection from exact runtime context."
+ "sha256": "a088647748f3f3dace29ace81a6efacf3af32d66fd846417eedd5755b3545868",
+ "role": "DebugSessionPhase family: for_context picks the matching phase class by priority."
},
{
- "path": "external/zmqruntime/src/zmqruntime/progress/projection.py",
- "sha256": "72b22f765dabbcf6983c619c8cc5404bc52518186ef72d85a6f6c6ab65cb426f",
- "role": "Generic progress projection kernel."
+ "path": "openhcs/authoring/session/progress.py",
+ "sha256": "cd34f68461b50506600da7854a3bf57646f2f0ff7288d0627bc40cb852a16825",
+ "role": "ExecutionProgress: dirty-marking registry subscription and interval rebuild on the session main thread, published as session events."
},
{
- "path": "external/zmqruntime/src/zmqruntime/execution/progress_stream.py",
- "sha256": "b2dd8e27f8fcac010beef3f8532fd9d584d7bae8c3f5591dcee0e5a235d6213a",
- "role": "Owns the immutable latest event and monotonic sequence observation retained per execution."
+ "path": "openhcs/agent/capabilities.py",
+ "sha256": "87c8e2e5aeb425b9a2eb690d48bdd6f5af7a327c389deac261d7e4e0da5d609f",
+ "role": "openhcs_session_events and openhcs_session_datasets headless readers."
},
{
- "path": "openhcs/agent/services/execution_session_service.py",
- "sha256": "1a9a70d2924b1854338612b212f00888bacba5cd33eb23608be294bb06c76399",
- "role": "Retains the exact submitting client and projects its authoritative progress observation."
+ "path": "openhcs/core/orchestrator/bundle_execution.py",
+ "sha256": "25446d6c6a43c22ff7104c6712c143f0276bc4749c2642fffac10db1455ef707",
+ "role": "execute_dataset_scoped_steps and the parent ordering before the pipeline SuccessProgressPhase event."
},
{
- "path": "openhcs/agent/dto/execution.py",
- "sha256": "1fbdc008937e3a043cf077e2243b40f6a0efba34d9166716c423af9b85445684",
- "role": "Declares the typed public job status and delegates terminal semantics to the generic lifecycle declaration."
+ "path": "openhcs/runtime/zmq_progress.py",
+ "sha256": "7f182cb0832ccaa1bbc8f5d78034eda73d1eb2fb572b360b5eddeefb6751a0e5",
+ "role": "ZMQCompilerProgressQueue and ZMQProgressEmitter for execution-server events."
},
{
- "path": "tests/unit/agent/test_agent_services.py",
- "sha256": "0c3a5225c1e832d9fbd5a423ef0cfd495451c0872b6b718d5fd66726f0758fdf",
- "role": "Proves same-client reuse, exact observation serialization, terminal caching, and one-time client release."
+ "path": "external/zmqruntime/src/zmqruntime/progress/projection.py",
+ "sha256": "ce72275812c552779df7e8bc9412659087a9a929f585b1994978615c68e8f33e",
+ "role": "Generic progress projection kernel the OpenHCS adapter specializes."
}
],
"findings": [
- "The canonical-owner section used checkout-local external/zmqruntime paths that cannot resolve in published documentation.",
- "Link checking showed that the equivalent deep Read the Docs pages are not deployed; the page now links to the exact owner sources at the pinned zmqruntime release."
+ "E1 merged DebugSessionPhaseDeclarationBase and the DebugSessionPhase enum into one DebugSessionPhase class family whose for_context selects the phase; the projection-path paragraph named the deleted class. Corrected."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/progress_runtime_projection_system.rst in full and compared it against the changed authorities: openhcs/core/debug.py (K2 imports), openhcs/core/debug_session_projection.py (E1: DebugSessionPhaseDeclarationBase and the DebugSessionPhase enum merged into the DebugSessionPhase class family) and openhcs/core/orchestrator/bundle_execution.py (E1 class families); replaced the deleted DebugSessionPhaseDeclarationBase with DebugSessionPhase.for_context; confirmed the debugger names in openhcs/core/debug.py, execute_dataset_scoped_steps and LiveMeasurementProgressPayload.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/progress_runtime_projection_system.rst in full and compared it against the changed authorities: core/progress/projection.py, runtime_tree.py, types.py, debug_projection.py, core/debug.py (K6 families), orchestrator/bundle_execution.py and runtime/zmq_progress.py (progress classes); checked ProgressStream/Phase/Status classes, ProgressRegistry's (subject, axis, stream) key, DebugCommandType.apply_to_worker, the command-keyed DebugInvocationExecutionStrategy/DebugStepStopStrategy, DebugBoundary members, DebugRuntimeProjection fields, and that SubjectRuntimeState, state_counts, node kinds, ExecutionProgress, execute_dataset_scoped_steps and the zmqruntime owner pages exist; corrected the progress vocabulary, the debugger-ownership bullets and the frame list.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/progress_runtime_projection_system.rst against B6's later changes to openhcs/core/progress/projection.py, openhcs/core/progress/runtime_tree.py, openhcs/core/progress/types.py, openhcs/core/debug.py, openhcs/authoring/session/progress.py, openhcs/core/orchestrator/bundle_execution.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/progress_runtime_projection_system.rst against the later B6 changes to openhcs/core/progress/projection.py, openhcs/core/progress/runtime_tree.py, openhcs/core/progress/types.py, openhcs/core/debug.py, openhcs/authoring/session/progress.py, openhcs/agent/capabilities.py, openhcs/core/orchestrator/bundle_execution.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/progress_runtime_projection_system.rst in full and compared it against the changed authorities: the page itself (edited in U1), core/progress/projection.py, core/debug.py, zmqruntime progress/projection.py and execution/progress_stream.py, deleted agent/services/execution_session_service.py, agent/dto/execution.py, tests/unit/agent/test_agent_services.py; verified every named class/function (all debug declarations, RuntimeProjectionBuilder, ExecutionProgress, LiveMeasurementProgressPayload, ZMQ progress emitters, phases) and corrected the plate-to-subject vocabulary, tree path, Qt-timer coalescing description and the dataset-scoped step function name.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"tests/unit/progress/test_projection.py and tests/unit/progress/test_debug_projection.py passed; published owner URLs now replace checkout-local paths",
"Sphinx linkcheck resolved both pinned zmqruntime owner-source links",
@@ -941,7 +1004,7 @@
},
{
"path": "docs/source/architecture/quick_start.rst",
- "source_sha256": "4dcc151050f02da75a6819db0be0053a1dcbefcf236b34998536ad9fbece915d",
+ "source_sha256": "089e7b6a3d66efef0ac3aa8cbbcf9eaf79198a8ad3e51c88799bfef7deab77b0",
"audience": [
"New OpenHCS users, developers, and coding agents"
],
@@ -950,51 +1013,82 @@
"authority": [
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
- "role": "PipelineDocument construction and atomic configuration-step identity."
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
+ "role": "PipelineDocumentCodec.from_values and the optional pipeline_config / required pipeline_steps rule."
},
{
- "path": "openhcs/core/steps/function_step.py",
- "sha256": "8e1243404095691030e9ebb95bb5611679f96c66c6b6819ae71e4b41249e74bc",
- "role": "Public FunctionStep callable-pattern and nested step-configuration declaration."
+ "path": "openhcs/core/steps/abstract.py",
+ "sha256": "2bffdfe02664c4974797bb6cefbf7f84ebb6912fcc11e026a3f2d8b3da5262b8",
+ "role": "Step constructor accepting processing_config and source_bindings nested declarations."
},
{
- "path": "openhcs/agent/authoring_contexts.py",
- "sha256": "6b68414c90e98d905f0b792440f12fad5dbd78abcd0c2c500b19fa6279184939",
- "role": "Declaration-owned progressive MCP authoring contexts and knowledge targets."
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "Pure2DContract and Pure3DContract named in the minimal example and the locality summary."
+ },
+ {
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "SourceBindingsConfig, StepSourceBindingsConfig and their lazy forms used in the folder example (declared here since K4)."
+ },
+ {
+ "path": "openhcs/core/source_bindings.py",
+ "sha256": "890473715373d1adc6b12fec2b57eab6f179cbca7d837920c0388d620ce8b8c2",
+ "role": "Selector, NamedSourceBinding, component-selector and metadata-rule types, and the filter-subject, match-type and metadata-source class families (SourceFileName, SourceExtension, TextContains, PathIsImage, FromFileName) the folder example imports."
+ },
+ {
+ "path": "openhcs/core/dataset_sources/source_bindings_source.py",
+ "sha256": "63e4a3f849ab5122ab7af8767a6288135b7330027dff962c80add93d5fd820f1",
+ "role": "SourceBindingsSource, selected when a non-empty source-binding declaration is present."
},
{
"path": "openhcs/interop/cellprofiler/pipeline_import.py",
- "sha256": "b547c838b2c86fe7cc5fe12d6cbf6a6715e83055c2d8c1a109ac22566d22c51e",
- "role": "Public CellProfiler pipeline lowering and declaration-owned headless-import validation."
+ "sha256": "5aa8895c9f533603b7ef972202141e2439473f0604b97d66f5a027b8b3cd1c83",
+ "role": "import_cellprofiler_pipeline signature and headless-import rejection of interactive modules."
},
{
- "path": "tests/unit/test_cellprofiler_pipeline_import.py",
- "sha256": "353f0c1d8e0bc3552d2e418d792794ab0631daa16c78a3e10191b1cc1ea07d73",
- "role": "Proves interactive CellProfiler modules fail headless import instead of disappearing from the lowered pipeline."
+ "path": "openhcs/agent/capabilities.py",
+ "sha256": "87c8e2e5aeb425b9a2eb690d48bdd6f5af7a327c389deac261d7e4e0da5d609f",
+ "role": "MCP tool names used in the first-session sequence (health check, authoring context, capability and knowledge search, config schema, pipeline drafts)."
+ },
+ {
+ "path": "openhcs/agent/authoring_contexts.py",
+ "sha256": "a714fab8481102a5ab7800a465fcbdf7267d1ed4468cb3803b75e03f2f29ff33",
+ "role": "first_use authoring context and the Official30 recipe knowledge target."
+ },
+ {
+ "path": "openhcs/authoring/session/session.py",
+ "sha256": "ced615b580e4625e09873449b29ac67cdea87c8b8065c205f32e6d5921cf31fc",
+ "role": "Session shared by the desktop application and MCP for progress, cancellation and results."
},
{
"path": "tests/unit/test_pipeline_document.py",
- "sha256": "2c18222ac2a34ee267788063a374112b17874c64693bfe542669c4183bc90c36",
+ "sha256": "4fe05636d5a9eb93f576904a774d30653a3686c7c8f61031ec0d316854cf5303",
"role": "Proves missing pipeline_config selects PipelineConfig() while explicit invalid values and missing steps remain rejected."
},
{
- "path": "openhcs/agent/services/llm_context_service.py",
- "sha256": "f26b697684b185bd5ebe122de14f6211cf5377cc21f31c917cca53cea6b4e592",
- "role": "Renders the declaration-composed operating guides, discovery boundaries and workflow instructions."
+ "path": "tests/unit/test_cellprofiler_pipeline_import.py",
+ "sha256": "67576f37b7cbe0595acea633265f8e14677ca3c1b8204630413330273dc4e58a",
+ "role": "Proves interactive CellProfiler modules fail headless import instead of disappearing."
},
{
- "path": "tests/unit/agent/test_progressive_authoring_context.py",
- "sha256": "533f8c704914112479c8adcb78ee14a006f750d5894b334c29cbd1380bc899d7",
- "role": "Checks bounded guide rendering, nominal route projection and operating/profile boundaries."
+ "path": "openhcs/constants/input_source.py",
+ "sha256": "4a7929e66d11d5ad81c40f080ba8cd23d466797bfa780d59eae0e1aea3ffd9a4",
+ "role": "InputSource class family; ReadPipelineStart is the choice the folder example assigns."
}
],
"findings": [
- "The pipeline-source boundary previously implied both assignments were mandatory; it now records that pipeline_steps is required and an omitted pipeline_config selects PipelineConfig().",
- "The CellProfiler route did not state that interactive modules fail headless import rather than being silently omitted."
+ "E1 replaced the enums the unsupported-folder example used with class families: InputSource.PIPELINE_START, MetadataSource.FILE_NAME, SourceFilterSubject.FILE/EXTENSION and SourceFilterMatchType.CONTAINS/IS_IMAGE no longer exist, so the example raised AttributeError. It now uses ReadPipelineStart, FromFileName, SourceFileName, SourceExtension, TextContains and PathIsImage."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/quick_start.rst in full and compared it against the changed authorities: openhcs/core/steps/abstract.py, openhcs/core/config.py, openhcs/core/source_bindings.py, openhcs/interop/cellprofiler/pipeline_import.py, openhcs/authoring/session/session.py and tests/unit/test_cellprofiler_pipeline_import.py (E1: InputSource, MetadataSource, SourceFilterSubject and SourceFilterMatchType became class families); rewrote the folder example to the family classes and executed both self-contained Python blocks against the worktree (they construct without error); confirmed import_cellprofiler_pipeline's signature and return order, the preset paths and the first_use context.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/quick_start.rst in full and compared it against the changed authorities: pipeline_document.py (rendering now through pycodify generate_python_source, L6), steps/abstract.py and source_bindings.py (K4: SourceBindingsConfig/StepSourceBindingsConfig now declared in core/config.py, SourceSetPairing family), processing_contracts.py (K5 CallableDeclaration rename), dataset_sources/source_bindings_source.py, cellprofiler/pipeline_import.py, authoring/session/session.py (K6 debug command classes) and both tests; imported every name in the page's code examples from the current tree (Lazy*Config from core.config, the selector/filter/metadata types from core.source_bindings, PipelineDocumentCodec.from_values, Pure2DContract, import_cellprofiler_pipeline signature, InputSource.PIPELINE_START, Microscopy.Channel, stack_percentile_normalize, SourceBindingsSource, Session). No claim on the page changed; the authority list now names core/config.py for the source-binding config classes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/quick_start.rst against B6's later changes to openhcs/authoring/session/session.py: the writability probe makes one exclusive create attempt, the installed demo reports ViewerType.value, the unit session pre-creates its QApplication, and the GUI journey waits for each batch to end. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/quick_start.rst against B6's later changes to openhcs/authoring/session/session.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/quick_start.rst against B6's later changes to openhcs/core/processing_contracts.py, openhcs/core/dataset_sources/source_bindings_source.py, openhcs/interop/cellprofiler/pipeline_import.py, openhcs/authoring/session/session.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/quick_start.rst against the later B6 changes to openhcs/core/processing_contracts.py, openhcs/core/source_bindings.py, openhcs/core/dataset_sources/source_bindings_source.py, openhcs/interop/cellprofiler/pipeline_import.py, openhcs/agent/capabilities.py, openhcs/authoring/session/session.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/quick_start.rst in full and compared it against the changed authorities: the page itself (edited in G5/G1), pipeline_document.py, agent/authoring_contexts.py, cellprofiler/pipeline_import.py, test_cellprofiler_pipeline_import.py, test_pipeline_document.py, agent/services/llm_context_service.py, test_progressive_authoring_context.py; verified every MCP tool name in agent/capabilities.py, the analysis-strategy section id in the knowledge manifest, the code-example imports (Microscopy.Channel, stack_percentile_normalize, Lazy*Config, PipelineDocumentCodec) and the MFD preset paths; corrected StepSnapshot, PURE_2D/PURE_3D, SourceBindingsHandler, the missing InputSource import and the progress owner. scripts/validate_docs.py reports no code-block or import finding for the page.",
"Reviewed the complete page against PipelineDocumentAuthority and the public pipeline declarations after the compatibility change.",
"Passed the documentation validator and tests/unit/test_pipeline_document.py for parsing, defaulting, rejection, and canonical rendering behavior.",
"On 2026-08-27, re-read and revised the complete explanation against the current CellProfiler importer and its focused regression; interactive modules now fail headless import explicitly instead of being silently omitted.",
@@ -1006,7 +1100,7 @@
},
{
"path": "docs/source/architecture/roi_system.rst",
- "source_sha256": "15c214ff7075f5e87c56620dcf2730405fdd6845c83d6c379409caca919dff61",
+ "source_sha256": "6a999d9dc4dbe7fac3c1f08f3078ef52f6be8d09fadc594b7902ed980d6a0adb",
"audience": [
"OpenHCS ROI, viewer, and materialisation maintainers"
],
@@ -1015,28 +1109,49 @@
"authority": [
{
"path": "openhcs/napari_roi_manager/widgets/_roi_manager.py",
- "sha256": "9ae7518576902c4a6d0384e387a08ae73528b39cf3878039c1fc70cfaf6c8a19",
- "role": "Native Napari Shapes-layer ROI manager projection and selection behaviour."
+ "sha256": "3234a3735245f225c8035a8d8f12af085cc4e2653e4a7fd49f124808cf01d358",
+ "role": "ROI manager bound to the active native Shapes layer through connect_layer, with observer release on widget destruction."
},
{
"path": "openhcs/processing/materialization/core.py",
- "sha256": "1f5a89a1eb50766ef093df3fece16d6bb03422b92d062fe8ae6471656b1ebd45",
- "role": "Nominal artifact materialisation and ROI projection strategies."
+ "sha256": "79db4b008483dfb5ac752f3a5da692a941cba7caae25a6ec26085bbdb4bf278f",
+ "role": "ROI output writers, polyline graph-edge writer with its 2D-only rejection, and projected filename resolution."
+ },
+ {
+ "path": "openhcs/processing/materialization/options.py",
+ "sha256": "e7487e9ad44276d4f2880f0d9b03fd6396f8053b9bda5775ab25caaf023554ca",
+ "role": "SpatialGraphROIOptions and SWCOptions declarations."
+ },
+ {
+ "path": "openhcs/core/artifacts/plans.py",
+ "sha256": "0d6a10a78f7177273deca4ad07b9bc729a2eb99254164f2606922674b7286b22",
+ "role": "ArtifactOutputPlan: artifact type, producer, execution scope, materialization options and object_subject_binding."
+ },
+ {
+ "path": "openhcs/core/artifacts/relations.py",
+ "sha256": "b1ae8be613fbabc372bd6692b41c5f925d4f968291f0a715bd1473d85cbed000",
+ "role": "ArtifactSpecRelation family and ObjectArtifactSubjectBinding feature metadata."
},
{
"path": "external/PolyStore/src/polystore/roi.py",
- "sha256": "a3bd2db5696d4aca9f8a7d00bf4b58bfe627564eed5aeb17aa89b8194ba13375",
- "role": "Generic ROI values and persistence mechanics."
+ "sha256": "fa945e9a3f7272e72f3ea022c7a3ae6cd30520ed7924591dd3f3cad5d0e71fc0",
+ "role": "Generic ROI value types and ImageJ ROI archive persistence."
},
{
"path": "tests/unit/test_napari_roi_manager_integration.py",
- "sha256": "de66b3d7aeb49b100ac5bb3c3acdf1c14afc19fb3681071b4356f6d7949a52dc",
+ "sha256": "15b0417fc6388f932ac6cb8b35102721918b987b68e52caef04b1c6bc5ea9e81",
"role": "Regression proof for the native Shapes-layer ROI manager seam."
}
],
- "findings": [],
+ "findings": [
+ "K2 split openhcs/core/artifacts.py into a package; the record's authority now names artifacts/plans.py and artifacts/relations.py. The page text names the classes without a module path and is unchanged."
+ ],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/roi_system.rst in full and compared it against the changed authorities: openhcs/napari_roi_manager/widgets/_roi_manager.py (E1: RoiTableColumn became a class family), openhcs/processing/materialization/core.py and options.py (E1: MaterializedFilenameIdentity class family; output_plan is now required), the deleted openhcs/core/artifacts.py (K2: split into the openhcs/core/artifacts package) and tests/unit/test_napari_roi_manager_integration.py; confirmed connect_layer, SpatialGraphROIOptions, SWCOptions, ArtifactOutputPlan (artifacts/plans.py) and ArtifactSpecRelation with ObjectArtifactSubjectBinding (artifacts/relations.py); the page names classes without module paths, so no prose change was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/roi_system.rst against B6's later changes to openhcs/processing/materialization/core.py, openhcs/core/artifacts.py, tests/unit/test_napari_roi_manager_integration.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/roi_system.rst against the later B6 changes to openhcs/processing/materialization/core.py, openhcs/core/artifacts.py, tests/unit/test_napari_roi_manager_integration.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/roi_system.rst in full and compared it against the changed authorities: napari_roi_manager/widgets/_roi_manager.py (observer release on destroy), processing/materialization/core.py (new ROIOutput/PointROIOutput writers), polystore/roi.py, test_napari_roi_manager_integration.py; verified SpatialGraphROIOptions, SWCOptions, SpatialGraph, ArtifactOutputPlan, ArtifactSpecRelation, ObjectArtifactSubjectBinding and connect_layer exist and that the polyline writer rejects 3D graphs; reordered one misplaced paragraph. Added options.py and core/artifacts.py as authorities for the named declarations.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"Source inspection confirmed native Shapes ownership and nominal materialisation strategies; the Napari ROI integration test covers the UI seam",
"On 2026-08-24, rechecked the complete explanation after materialization outputs gained nominal text behavior; no ROI ownership or projection claim changed.",
@@ -1048,7 +1163,7 @@
},
{
"path": "docs/source/architecture/runtime_value_system.rst",
- "source_sha256": "130ae142dd1d147f24317d06c900d6701482fd492d580ee819082c8a756f7703",
+ "source_sha256": "26890bf40926be1ebe4cf728c31b565a0b7c5b854896b082e4da78a7fd32040f",
"audience": [
"OpenHCS runtime and artifact maintainers"
],
@@ -1057,51 +1172,80 @@
"authority": [
{
"path": "openhcs/core/runtime_artifact_values.py",
- "sha256": "9ac62ec4c4a6a6f7f375026ea112cc2e43f09ee6e6ca270a3d955be8ef5347cf",
- "role": "RuntimeValue, ArtifactKey, location, and nominal payload wrapping."
+ "sha256": "ce42e38f893459b9cb0ed6d779fabd5818204301ab1acc13ab0a7df32e6e3a2d",
+ "role": "RuntimeValue, ArtifactKey, execution scope, and the output-normalization entry that calls the artifact type's contextualize_output_from_projector."
},
{
"path": "openhcs/core/runtime_stores.py",
- "sha256": "5f323ec8edd4221ee30e34ed0c9c09e0f4e45ef502749c1a2d331b7a5452af99",
- "role": "RuntimeValueStore recording, replacement, query, and observation semantics."
+ "sha256": "35fe71906bfd16842de347776a0c5cc846550ad490cc3628885129d746ec6f31",
+ "role": "RuntimeValueStore recording, replacement, RuntimeArtifactQuery resolution, and observation semantics."
+ },
+ {
+ "path": "openhcs/core/runtime_plane_projection.py",
+ "sha256": "cf644451b6649dcec398fecbf961a4a29ea74f0bb2e0bdab052ec1be8a427f95",
+ "role": "RuntimeSliceProjectableValue ABC, its indexed, invariant and identity-projectable subclasses, and the default contextualized_output hook selection."
},
{
"path": "openhcs/core/runtime_slice_projection.py",
- "sha256": "8a1cb65d3025ed8a87ee59af26deac9765a8f59b21bf2cd61b6f96d82999641d",
- "role": "Nominal runtime slice projection registry and aggregation behaviour."
+ "sha256": "f59785d901d0d7bcc3ac1fc1be606bd1728a727ea7347cdc1dd4f549cc3193fa",
+ "role": "RuntimeSliceProjection foreign-value list, rejection of undeclared owned values, and contextualized_output for foreign returned values."
+ },
+ {
+ "path": "openhcs/core/artifacts/kinds.py",
+ "sha256": "216b913efad9ec76707e9f61be445fb6fa1c6601c36ef53e296acd4928604515",
+ "role": "ArtifactType.contextualize_output root with its per-value hooks, and the image, object-label, measurement, spatial-grid and spatial-graph overrides."
+ },
+ {
+ "path": "openhcs/core/artifacts/plans.py",
+ "sha256": "0d6a10a78f7177273deca4ad07b9bc729a2eb99254164f2606922674b7286b22",
+ "role": "ArtifactSpecRef identity and the source_context_source relation an output plan resolves."
},
{
"path": "openhcs/core/runtime_image_values.py",
- "sha256": "aec6e31b907c2ca5a69b3faf5fe3366654d9506b1191f90a7cbeb8ca1dbef08c",
- "role": "Owns semantic image payload metadata, its serialized projection, geometry use, masks, source provenance, and context projection."
+ "sha256": "4dd5173cbeaba217bffd28b04c05f6bac3f49711bfd9b5bc7f791364bfbc87c8",
+ "role": "ImagePayload metadata (mask, axes, plane axis, provenance) and contextualize_image_output for image sources."
},
{
- "path": "external/arraybridge/src/arraybridge/array_geometry.py",
- "sha256": "0a8a5f1a61552b057997e94e4c89a19b5615875f307949e0d11784179f2a94f2",
- "role": "Owns framework-neutral shape inspection without moving device-resident array data."
+ "path": "openhcs/core/runtime_object_labels.py",
+ "sha256": "0e6f92699f87013550e6426a70bd2e458fab881c4108aa1559cb17121110c284",
+ "role": "ObjectLabelValue.contextualize_image_output plane-axis rule for label-rendered images."
},
{
- "path": "openhcs/core/steps/function_runtime.py",
- "sha256": "b1449120bebe7cf4898c055d841dc258c7d91d976d9e5ff7396aed81ad5011e7",
- "role": "Runtime callable invocation and output-context strategy implementation."
+ "path": "openhcs/core/projected_image_output.py",
+ "sha256": "3870623dae8a400a8f6f0ab9a2b1d4f82ee9c2c71f1e0528d35986e75e13a0c5",
+ "role": "SourceProjectedImageOutput and SelectedPlaneImageOutput source-plane selection."
+ },
+ {
+ "path": "openhcs/core/callable_contract.py",
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "CallableContract.resolve_returned_plan_values binding returned positions to exact output plan refs."
+ },
+ {
+ "path": "external/arraybridge/src/arraybridge/array_geometry.py",
+ "sha256": "0a8a5f1a61552b057997e94e4c89a19b5615875f307949e0d11784179f2a94f2",
+ "role": "Framework-neutral shape inspection without moving device-resident array data."
},
{
"path": "tests/unit/test_function_artifact_outputs.py",
- "sha256": "600465c47acd7b69c1c2036a05173d9018e1d5c118ff71474a95d86fd14fda49",
- "role": "Proves special artifacts bypass image-axis projection during runtime materialisation."
+ "sha256": "6be6e5ad7669f4e10d59f593c52926cc46c4e39ea2294ea67fe6a94996ea877f",
+ "role": "Proves image-output contextualization and that named artifacts bypass unrelated source-axis projection, using admitted artifact graphs."
},
{
"path": "tests/unit/test_runtime_slice_projection.py",
- "sha256": "4066679ba1293f7865f5c8aa9e0a1ee7cff9124b270180d458f2df1a7bf3e685",
- "role": "Proves declared image slices are projected without implicit device-to-host conversion."
+ "sha256": "d47cad11b66f1bb3a7e77796bf5cca83599ef8ef4f876ac685f06e002dc9552a",
+ "role": "Proves undeclared value types are rejected and declared slices project without implicit device-to-host conversion."
}
],
"findings": [
- "The explanation did not state that declared special artifacts bypass image-axis projection through their nominal context strategy.",
- "GPU integration exposed shape inspection that implicitly converted CuPy payloads to NumPy; generic geometry now belongs to ArrayBridge and semantic projection consumes it without transferring the data."
+ "After K2 (artifacts package), ArtifactType.contextualize_output no longer goes straight from the artifact type to the source: it asks the returned value's contextualized_output which of the type's hooks applies (contextualize_value, contextualize_image, contextualize_projected_image, contextualize_aligned), and ImageArtifactType.contextualize_image then calls the source's contextualize_image_output. The page described this as a two-stage dispatch on artifact type and source; the paragraph now names the returned value's part."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/runtime_value_system.rst in full and compared it against the changed authorities: openhcs/core/artifacts.py (now the openhcs/core/artifacts package, K2), runtime_artifact_values.py, runtime_stores.py, runtime_plane_projection.py, runtime_slice_projection.py, runtime_image_values.py, runtime_object_labels.py, projected_image_output.py, callable_contract.py, tests/unit/test_function_artifact_outputs.py; read ArtifactType.contextualize_output and ImageArtifactType.contextualize_image in artifacts/kinds.py and corrected the function-output paragraph to include the returned value's contextualized_output step; confirmed the five overriding artifact types, source_context_source (artifacts/plans.py), resolve_returned_plan_values, SourceProjectedImageOutput.resolve_source_context and every named class still exist; the other diffs are import moves, new helper methods and the object-label storage enum dropping its payload-shape member data, none of which the page states.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/runtime_value_system.rst in full and compared it against the changed authorities: openhcs/core/runtime_stores.py, openhcs/core/callable_contract.py, tests/unit/test_function_artifact_outputs.py; runtime_stores dropped to_dict/from_dict on RuntimeArtifactLocation/Address (no page claim relies on them); K5 restructured callable metadata into CallableDeclaration but resolve_returned_plan_values remains on CallableContract; the test changes are fixture-only (admitted_graph, CompiledSourceBindingPlan()). Grepped every class and method the page names (RuntimeArrayPayload through ObjectLabelValue, RuntimeArtifactQuery, RuntimeExecutionAxisScope, ComponentGroupScope, source_context_source); all exist. No correction needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/runtime_value_system.rst against B6's later changes to openhcs/core/runtime_stores.py, openhcs/core/artifacts.py, openhcs/core/runtime_object_labels.py, openhcs/core/callable_contract.py, tests/unit/test_function_artifact_outputs.py, tests/unit/test_runtime_slice_projection.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/runtime_value_system.rst against the later B6 changes to openhcs/core/runtime_stores.py, openhcs/core/artifacts.py, openhcs/core/runtime_object_labels.py, openhcs/core/callable_contract.py, tests/unit/test_function_artifact_outputs.py, tests/unit/test_runtime_slice_projection.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/runtime_value_system.rst in full and compared it against the changed authorities: runtime_artifact_values.py, runtime_stores.py, runtime_slice_projection.py, runtime_image_values.py, steps/function_runtime.py and the two test files; verified every named class/function exists, replaced the deleted output matcher, slice-projection strategy registry and function-output context strategies with their current owners (resolve_returned_plan_values, RuntimeSliceProjectableValue, ArtifactType.contextualize_output and contextualize_image_output), and confirmed the persisted-metadata reload fallback in source_workspace_projection.py still holds.",
"Re-read docs/source/architecture/runtime_value_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/runtime_artifact_values.py; openhcs/core/runtime_stores.py; openhcs/core/runtime_slice_projection.py; openhcs/core/steps/function_runtime.py; tests/unit/test_function_artifact_outputs.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -1124,7 +1268,7 @@
"authority": [
{
"path": "external/pyqt-reactive/src/pyqt_reactive/forms/parameter_form_manager.py",
- "sha256": "a561e62e29af42630fa107b0929fc87b74007f7ed14ec8f0a6881c49070dc923",
+ "sha256": "2c59c3dc2f95a349970815a33c2afcf5158c97f4627973a8a0c6826f8ca9ae6c",
"role": "Reusable parameter-form lifecycle and value projection."
},
{
@@ -1134,10 +1278,11 @@
}
],
"findings": [
- "The transition page duplicated the OpenHCS boundary link and omitted the owner document."
+ "Redirect still correct after U3: the pyqt-reactive owner document exists and OpenHCS holds no scope-feedback mechanism; no page change needed."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/scope_visual_feedback_system.rst in full and compared it against the changed authorities: pyqt_reactive/forms/parameter_form_manager.py; the page states only that pyqt-reactive owns scope visual feedback; U3 moved more generic Qt machinery into pyqt-reactive but did not move this mechanism back, and the linked owner document external/pyqt-reactive/docs/architecture/scope_visual_feedback_system.rst still exists. No correction needed.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"Sphinx resolved the current pyqt-reactive owner target",
"Re-reviewed the public form-build completion, failure, and signal lifecycle against the complete dependent page and the updated pyqt-reactive owner documentation.",
@@ -1176,7 +1321,7 @@
},
{
"path": "docs/source/architecture/serialization_boundaries.rst",
- "source_sha256": "28083415985860019b3ee15d0c69e9c07c903584a3890cd53b7a1676a6718c74",
+ "source_sha256": "f3f6ee89037b43f7babe9c4c9342a2255894210517c295afb668e9b69aae03eb",
"audience": [
"OpenHCS serialization and transport maintainers"
],
@@ -1184,24 +1329,39 @@
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/serialization/json.py",
- "sha256": "fa1e2aeae1e5d6896944d1516c17db0ac486157b335d7be06fcb553ab3203f1f",
- "role": "Bounded JSON singledispatch projection for callables, registered types, dataclasses, paths, enums, and collections."
+ "path": "external/python-introspect/src/python_introspect/jsonable.py",
+ "sha256": "c845361d0d14914798d9a1b5329d681300511fc7e95c67508943425716930ebd",
+ "role": "to_jsonable singledispatch encoder: scalars, mappings, sequences, paths, enums, callables by import path, dataclasses, and AutoRegisterMeta types by registry key."
+ },
+ {
+ "path": "external/python-introspect/src/python_introspect/dataclass_projection.py",
+ "sha256": "7a8edc205fae5c663f2bfd70017c96492008171b40700bb9b41e3f52e4dcf9c6",
+ "role": "dataclass_from_mapping decoder paired with to_jsonable."
},
{
"path": "openhcs/core/image_file_serialization.py",
- "sha256": "d3aec14d46696ac82d8cc0a7554f26550bdde76afd51c4e1a5d3be27e8545d5f",
- "role": "Nominal image-container format and pixel-semantics registry."
+ "sha256": "644a01f8cccdf8e3d663202e3207d8750b4cf2f03f42c5bcc82dcaffb2c3fb5d",
+ "role": "ImageFileFormat registry: suffix matching, host preparation, read/write, source metadata, intensity scale, and declared pixel-band axes."
},
{
"path": "external/pycodify/src/pycodify/core.py",
- "sha256": "b32dea495ceb8745c1b363f2e42aa62eaa28a1d54198a5d6541e0b1546facfb4",
- "role": "Executable Python rendering and formatter composition."
+ "sha256": "f5ae16c3bc62d2b2dc24f2701fb7fa936c6dd64fc602d3e64aabc8a38730ef18",
+ "role": "generate_python_source, FormatContext (clean mode, PathRendering), formatter registration, and the PythonSourceLiteral ABC."
+ },
+ {
+ "path": "external/ObjectState/src/objectstate/codegen.py",
+ "sha256": "347d2697c061a35e6e440e373fe4e8bfce1ad92b500e98da07faa26f6a3a1356",
+ "role": "LazyResolutionFormatter and LazyDataclassFormatter: source formatting for lazy configuration dataclasses."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/serialization_boundaries.rst in full and compared it against the changed authorities: external/python-introspect/src/python_introspect/jsonable.py (current file read; no reviewed version in history); confirmed to_jsonable is a singledispatch function with registrations for mappings, sequences and sets, callables, AutoRegisterMeta registered types (projected through the registry key), Enum, Path and JSON scalars plus the dataclass projection, that dataclass_from_mapping is the decoder in dataclass_projection.py, and that PythonSourceLiteral and ImageFileFormat exist; no claim changed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/serialization_boundaries.rst in full and compared it against the changed authorities: python_introspect/jsonable.py, python_introspect/dataclass_projection.py, pycodify/core.py; to_jsonable still dispatches scalars, mappings, sequences, paths, enums, callables by import path, dataclasses and AutoRegisterMeta types by registry key, and dataclass_from_mapping is its decoder. L6 moved PythonSourceLiteral and path factoring into pycodify 0.2 (FormatContext.paths) and lazy-config formatting into objectstate.codegen; added those owners to the Python-source section. Unformattable values now raise in pycodify (FallbackFormatter deleted), consistent with the extension rule.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/serialization_boundaries.rst against B6's later changes to openhcs/core/image_file_serialization.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/serialization_boundaries.rst against the later B6 changes to openhcs/core/image_file_serialization.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/serialization_boundaries.rst in full and compared it against the changed authorities: openhcs/serialization/json.py (deleted, L2) and openhcs/core/image_file_serialization.py; checked every to_jsonable dispatch case in python_introspect/jsonable.py, the dataclass_from_mapping decoder, OpenHCS to_jsonable.register users, and ImageFileFormat suffix/prepare/read/write/pixel-semantics members; no prose change was needed.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"tests/unit/agent/test_agent_serialization.py and tests/unit/test_image_file_serialization.py cover the named boundaries"
]
@@ -1266,39 +1426,57 @@
},
{
"path": "docs/source/architecture/source_model.rst",
- "source_sha256": "ba603eb36228385593c44bec049517320dfd29013ffb49cbdce81bc675ec11e0",
+ "source_sha256": "73cae6b1f57916e13fcdfd69eb97c09dbe0d518b4e9980f8b42743f245ff50db",
"audience": [
"Pipeline authors and OpenHCS source/compiler maintainers"
],
"user_need": "Understand how arbitrary and native image sources become typed compiled inputs.",
"diataxis": "explanation",
"authority": [
+ {
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "SourceBindingsConfig and StepSourceBindingsConfig declarations and source_bindings_defaults_to_base."
+ },
{
"path": "openhcs/core/source_bindings.py",
- "sha256": "e0afbbe40cce3847bcc2314910d0083ecc579a5a0d6e4278c3a5df4c62e7eb01",
- "role": "Public typed source-binding declaration vocabulary."
+ "sha256": "890473715373d1adc6b12fec2b57eab6f179cbca7d837920c0388d620ce8b8c2",
+ "role": "Declaration vocabulary (ImagePlaneSource, selectors, filters, MetadataExtractionRule, NamedSourceBinding, SourceSetPairing/PairByOrder/PairByMetadata) and the compiled binding and universe plans."
},
{
"path": "openhcs/core/source_binding_workspace.py",
- "sha256": "7dce6e45b9e846713b246561340d43de378e251e35f6ef68d5527fccc1b31ad3",
- "role": "Compiled virtual source workspace and binding projection."
+ "sha256": "e242d9b932d52c1099756881870f926a273f5ec08663938347580cb4a70e8283",
+ "role": "Source-binding workspace projector, pairing-keyed source-set assemblers and materialization of the virtual workspace."
},
{
- "path": "openhcs/microscopes/source_bindings_handler.py",
- "sha256": "88f7c624310ea31a4cf5de884fdae295e4675a578f8a2821a837140e4e4b07b2",
- "role": "Arbitrary-folder ingestion and declared source-binding projection."
+ "path": "openhcs/core/dataset_sources/source_bindings_source.py",
+ "sha256": "63e4a3f849ab5122ab7af8767a6288135b7330027dff962c80add93d5fd820f1",
+ "role": "SourceBindingsSource: recursive folder inventory and declared source-binding projection."
},
{
- "path": "external/PolyStore/src/polystore/filemanager.py",
- "sha256": "877a2b10731b12904b1784a5b42c9e39688e3a84f7f86018c1c2beee96ea2296",
- "role": "Generic FileManager storage and backend composition."
+ "path": "openhcs/core/dataset_sources/choice.py",
+ "sha256": "497eb028aabb80283be8a621ea94e09881dfcd51b2e34e2bbecafa09e004c1f6",
+ "role": "AutoDetectedSource falls back to the registered declared-file source only when detection fails and bindings are non-empty."
+ },
+ {
+ "path": "openhcs/core/source_projection.py",
+ "sha256": "a922f3e66d44bc60ad0a31039128c72321f324ae9ed81070e3efe175e2e12414",
+ "role": "SourcePlaneProjection, SourceArtifactProjection, SourceProjectionSet and typed SourceDatasetDiagnostic."
+ },
+ {
+ "path": "external/PolyStore/src/polystore/base.py",
+ "sha256": "8f2082ade15072bc8e8eba363c75a1d1cff7c67c74845e6485f69eab244634dc",
+ "role": "BackendBase.resolve_listed_address and optional physical_source_path versus DataSource.source_path."
}
],
- "findings": [
- "The source explanation did not distinguish raw selector coordinates from projected component identity or state that the binding tuple, rather than a mirrored enabled flag, owns compiled-plan activity."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/source_model.rst in full and compared it against the changed authorities: openhcs/core/config.py, openhcs/core/source_bindings.py, openhcs/core/source_binding_workspace.py, openhcs/core/source_projection.py; E1 turned SourceProjectionRole, SourceSetRole and MaterializationBackend into class families (PrimaryPlaneRole, SourceArtifactRole, BroadcastSource, MaterializeToZarr and siblings) and source roots now use absolute_source_path; the page names only SourceProjectionRole as the declared role a reader reconstructs projections from, which still holds, and every other named class, method and field was grepped and exists; no page change.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/source_model.rst in full and compared it against the changed authorities: core/source_bindings.py (K4: config classes moved to core/config.py, SourceBindingMatchPlan/SourceBindingMatchMethod replaced by SourceSetPairing, plans never None), source_binding_workspace.py (pairing-keyed assemblers, path identity), dataset_sources/source_bindings_source.py and choice.py (import moves only), source_projection.py (source_path_posix); grepped every other named declaration, projection, diagnostic, serializer and PolyStore method in the current tree; replaced the SourceBindingMatchPlan entry with SourceSetPairing and added core/config.py as the config-class authority.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/source_model.rst against B6's later changes to openhcs/core/source_binding_workspace.py, openhcs/core/dataset_sources/source_bindings_source.py, openhcs/core/dataset_sources/choice.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/source_model.rst against the later B6 changes to openhcs/core/source_bindings.py, openhcs/core/source_binding_workspace.py, openhcs/core/dataset_sources/source_bindings_source.py, openhcs/core/dataset_sources/choice.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/source_model.rst in full and compared it against the changed authorities: openhcs/core/source_bindings.py, openhcs/core/source_binding_workspace.py, openhcs/microscopes/source_bindings_handler.py (deleted, G4) and PolyStore filemanager.py; checked every named declaration, compiled plan, projection, diagnostic and PolyStore method exists, corrected the arbitrary-folder fallback to AutoDetectedSource/SourceBindingsSource, and confirmed CompiledStepPlan carries no separate source-binding enabled flag.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"tests/unit/test_source_bindings.py, tests/unit/test_source_projection.py, and tests/unit/test_source_binding_workspace.py cover declarations, projections, and generic-folder ingestion",
"On 2026-08-22, re-read the complete explanation against the current source-binding declarations, workspace projector, compiler grouping, and runtime projection; the focused OpenHCS documentation-drift suite passed 238 tests."
@@ -1306,7 +1484,7 @@
},
{
"path": "docs/source/architecture/special_io_system.rst",
- "source_sha256": "e61acca7a6cb7e2c7cf41948d399b850695c9c245c46d0d5c85d1d5b0d0bfaf1",
+ "source_sha256": "3e48a170133fa5154e6cbb7fe7466855a28064eb30e45ecf0fd42d0e212e651e",
"audience": [
"OpenHCS maintainers migrating old special-input and special-output code"
],
@@ -1315,30 +1493,41 @@
"authority": [
{
"path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
- "role": "Callable ABI including input/output/execution memory roles, runtime-owned parameter projection, and artifact contracts."
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "CallableContract input/output declarations and runtime-owned parameter projection."
+ },
+ {
+ "path": "openhcs/core/pipeline/function_contracts.py",
+ "sha256": "67a654102b1d60c0a0f90e64ccaf3a8c177035cd11a0346734f872b329783d74",
+ "role": "special_inputs decorator: records parameter names as runtime-managed callable metadata and excludes them from user parameters; no special_outputs decorator exists."
},
{
"path": "openhcs/core/pipeline/artifact_planning.py",
- "sha256": "8f2a9fe778df5699218185b2b52f9537a57a929f64ae2a43915d14e04a2e7135",
- "role": "Exact ArtifactSpecRef planning and nominal artifact strategy selection."
+ "sha256": "377558d738198216e9f240af6e19dfd71b5a81d826a2044ec957fa410abcdf92",
+ "role": "ArtifactGraph and ArtifactSpecRef planning during compilation."
},
{
"path": "openhcs/core/runtime_stores.py",
- "sha256": "5f323ec8edd4221ee30e34ed0c9c09e0f4e45ef502749c1a2d331b7a5452af99",
- "role": "RuntimeValueStore recording, replacement, query, and observation semantics."
+ "sha256": "35fe71906bfd16842de347776a0c5cc846550ad490cc3628885129d746ec6f31",
+ "role": "RuntimeValueStore recording, replacement, query, and observation during execution."
},
{
"path": "openhcs/interop/cellprofiler/module_artifact_contracts.py",
- "sha256": "bf2e8f6c66a181218c8fd9afa9132922e86295f65d6102d976e1b36e41e17ee5",
- "role": "Invocation-specific CellProfiler artifact contract derivation."
+ "sha256": "cefc83a740f917730f336b7283c6ea2d9280d27bf077e030d41b7a8a9add41af",
+ "role": "CellProfilerModuleArtifactContracts: per-invocation artifact bindings from CellProfiler setting declarations."
}
],
"findings": [
- "The string-keyed special-I/O architecture is superseded; this URL now exists only to route legacy terminology."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/special_io_system.rst in full and compared it against the changed authorities: openhcs/core/callable_contract.py, openhcs/core/pipeline/function_contracts.py, openhcs/core/pipeline/artifact_planning.py, openhcs/core/runtime_stores.py, openhcs/interop/cellprofiler/module_artifact_contracts.py; the diffs are imports from the openhcs/core/artifacts package (K2), artifact_type.value replacing require_value(), and step-input dependency classes; CallableContract, CellProfilerModuleArtifactContracts, ArtifactGraph, RuntimeValueStore and the special_inputs decorator still exist, and artifact_contract_system.rst, the redirect target, exists.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/special_io_system.rst in full and compared it against the changed authorities: callable_contract.py, function_contracts.py, artifact_planning.py, runtime_stores.py, module_artifact_contracts.py; K5 replaced CallableMetadata with CallableDeclaration (CallableContract now subclasses it); CallableContract, special_inputs, ArtifactGraph, RuntimeValueStore and CellProfilerModuleArtifactContracts all still exist with the roles the page assigns, and no special_outputs decorator exists. No correction needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/special_io_system.rst against B6's later changes to openhcs/core/pipeline/function_contracts.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/special_io_system.rst against B6's later changes to openhcs/core/callable_contract.py, openhcs/core/pipeline/function_contracts.py, openhcs/core/runtime_stores.py, openhcs/interop/cellprofiler/module_artifact_contracts.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/special_io_system.rst against the later B6 changes to openhcs/core/callable_contract.py, openhcs/core/pipeline/function_contracts.py, openhcs/core/runtime_stores.py, openhcs/interop/cellprofiler/module_artifact_contracts.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/special_io_system.rst in full, including its unreviewed special_outputs removal, and compared it against the changed authorities: callable_contract.py, pipeline/artifact_planning.py, runtime_stores.py, interop/cellprofiler/module_artifact_contracts.py; confirmed each named class and the special_inputs decorator exist with the roles the page assigns and that no special_outputs declaration remains; fixed the edited sentence.",
"Re-read docs/source/architecture/special_io_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/callable_contract.py; openhcs/core/pipeline/artifact_planning.py; openhcs/core/runtime_stores.py; openhcs/interop/cellprofiler/module_artifact_contracts.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -1359,23 +1548,30 @@
"authority": [
{
"path": "external/pyqt-reactive/src/pyqt_reactive/forms/parameter_form_manager.py",
- "sha256": "a561e62e29af42630fa107b0929fc87b74007f7ed14ec8f0a6881c49070dc923",
+ "sha256": "2c59c3dc2f95a349970815a33c2afcf5158c97f4627973a8a0c6826f8ca9ae6c",
"role": "Reusable parameter-form lifecycle and value projection."
},
{
"path": "openhcs/pyqt_gui/widgets/step_parameter_editor.py",
- "sha256": "54e0bdd74f8d781c2bfdf1da559bbe7e2393f19ef74826d374a2281e8eb96258",
- "role": "OpenHCS FunctionStep editing actions and domain-specific parameter policy."
+ "sha256": "534f33acc5e295bfda4fbd73cbec49c4fbf662953e14842bf04ce59550f2dea3",
+ "role": "FunctionStep editor widget: config-tree, code-document round trip, and OpenHCS parameter policy over the pyqt-reactive form."
+ },
+ {
+ "path": "openhcs/authoring/session/step_scopes.py",
+ "sha256": "6caab26c963c958bf65aec69c03e49e1a4e84a46005c6d61a5301e67618be875",
+ "role": "StepEditorScope: FunctionStep scope identity used by the editor."
},
{
"path": "openhcs/pyqt_gui/services/window_handlers.py",
- "sha256": "5080a81191a8cfc78ce826db7e241bd41d3df7607deea32128a06449e04a3aa8",
- "role": "OpenHCS domain window-route registration over pyqt-reactive, with configuration-window presentation derived from its stable UI identity."
+ "sha256": "6f05c7945f41b25e0b783231d51b508729ba0713911224bf46f84f89ea24823f",
+ "role": "OpenHCS window-route registration, including definition-mutation guards and compiled-inspection actions through the plate session."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/step-editor-generalization.rst in full and compared it against the changed authorities: parameter_form_manager.py, step_parameter_editor.py, step_scopes.py, window_handlers.py; the diffs move CodeEditorFormUpdater, use_external_code_editor and ConfigWindow into pyqt-reactive (OpenHCSConfigWindow subclasses it), take SourceBindingsConfig from openhcs.core.config, and replace '::' joins with objectstate.ScopeKey. The page already assigns editor-window lifecycle to pyqt-reactive and keeps FunctionStep scope registration, code documents and workflow composition in OpenHCS, which still holds; both linked pyqt-reactive owner documents exist. No correction needed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/step-editor-generalization.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/widgets/step_parameter_editor.py and openhcs/pyqt_gui/services/window_handlers.py; confirmed scope registration, code-document actions and compile/inspection routes still live in OpenHCS while form mechanics stay in pyqt-reactive; no prose change was needed.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"Current imports place generic form/window mechanics in pyqt-reactive and OpenHCS actions in the named adapters",
"Re-reviewed the public form-build completion, failure, and signal lifecycle against the complete dependent page and the updated pyqt-reactive owner documentation.",
@@ -1398,7 +1594,7 @@
"authority": [
{
"path": "external/PolyStore/src/polystore/filemanager.py",
- "sha256": "877a2b10731b12904b1784a5b42c9e39688e3a84f7f86018c1c2beee96ea2296",
+ "sha256": "a9e9f3a8205216efc769accad06ab64d5373e6726733b2a1f2e3881613d1880e",
"role": "Generic FileManager storage and backend composition."
},
{
@@ -1408,16 +1604,17 @@
},
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
- "role": "Current Python support, dependency, package discovery, and entry-point authority."
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
+ "role": "Declares polystore and arraybridge as separate runtime dependencies of OpenHCS."
}
],
- "findings": [
- "The transition page linked only PolyStore, grammatically assigned two packages to one owner, and duplicated its OpenHCS boundary link.",
- "The redirect used raw Read the Docs URLs instead of the configured first-party PolyStore and ArrayBridge intersphinx inventories."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/storage_and_memory_system.rst in full and compared it against the changed authorities: pyproject.toml (current file read; no reviewed version in history); polystore and arraybridge remain declared dependencies, and external_foundations.rst, the page this one points to, exists; no claim changed.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/storage_and_memory_system.rst in full and compared it against the changed authorities: pyproject.toml; the diff only raises dependency pins (pyqt-reactive 0.5, pycodify 0.2, objectstate 2.1, zmqruntime 0.6.2, python-introspect 0.2.7); polystore and arraybridge remain separate runtime dependencies, and FileManager and the arraybridge converters still exist. No correction needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/storage_and_memory_system.rst against the later B6 changes to pyproject.toml: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/storage_and_memory_system.rst in full and compared it against the changed authorities: external/PolyStore/src/polystore/filemanager.py (submodule advance) and pyproject.toml (polystore 0.6, arraybridge 0.4.1, JAX and openhcs-basicpy dependency changes); the redirect makes no version, backend or API claim those changes affect, and both packages remain separate dependencies with intersphinx targets in docs/source/conf.py; no prose change was needed.",
"Re-read docs/source/architecture/storage_and_memory_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: external/PolyStore/src/polystore/filemanager.py; external/arraybridge/src/arraybridge/converters.py; pyproject.toml.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -1443,7 +1640,7 @@
},
{
"path": "docs/source/architecture/streaming_boundary_and_wrappers.rst",
- "source_sha256": "b4943eeac6a59501b60ebfa16b0b093bc415b5bf8e4124409371b0d3641cbbfd",
+ "source_sha256": "f5c2031f28cff9e7b61a4b0c3ba0afb467568fa48c2e0c52468f6a83ec092242",
"audience": [
"OpenHCS streaming, viewer, and runtime maintainers"
],
@@ -1452,105 +1649,103 @@
"authority": [
{
"path": "openhcs/core/viewer_streaming_service.py",
- "sha256": "46982fd46f47b962033e02d82afb6c3ce2abbafdabfbce99e591d9c6dff2fbab",
- "role": "Compiled output streaming, viewer settlement, state evidence, and lifecycle cleanup."
+ "sha256": "50742cdcb589dc9fba5a177c0107c046dd63c518c5aa77bd8a80e4f69e898e07",
+ "role": "Compiled output streaming through StreamComponentMessageExtraAuthority and viewer settlement."
},
{
"path": "openhcs/core/streaming_config_declarations.py",
- "sha256": "96ad52912439f8dd6b395b73b041313243f86f98f60eff8a2c3d43f97c7be340",
- "role": "Registered viewer streaming configuration declarations compiled by OpenHCS."
+ "sha256": "fc6178165f7ddf80ce7f213774993fa16bbc85ec3538f7daa06ad9c4c17f6c1d",
+ "role": "ViewerFamily classes (NapariViewer, FijiViewer) and the ViewerType boundary enum derived from them."
},
{
- "path": "openhcs/runtime/viewer_protocol.py",
- "sha256": "30441eab97da9ffcb5b9dc33d65c2ecc56c426e79bb2b27eedc78a68aaf13bc3",
- "role": "OpenHCS viewer persistence policy, exact process ownership, platform policy, readiness, and settlement contracts."
+ "path": "openhcs/core/compiled_step_plan.py",
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "CompiledStepPlan.producer_identity_for_main_flow and producer_identity_for_artifact."
},
{
- "path": "openhcs/runtime/viewer_controls.py",
- "sha256": "3227e6bf60e9b43fd792cc4299b6b50721741f012a0f7f856268b35022fd6062",
- "role": "Declares caller payload controls, runtime projection policy, global shape budgets, and one atomic layer-isolation request."
+ "path": "openhcs/core/runtime_object_labels.py",
+ "sha256": "0e6f92699f87013550e6426a70bd2e458fab881c4108aa1559cb17121110c284",
+ "role": "ObjectLabelValue.contextualize_image_output plane-axis and provenance rule for label-rendered images."
},
{
- "path": "openhcs/runtime/napari_viewer_server.py",
- "sha256": "64ccfef65a10e04878ef0806ae72d0446540efcbfc72af46c4e2ab785418a84c",
- "role": "Applies bounded shape projection and preflighted layer isolation inside the owning Napari Qt process."
+ "path": "openhcs/core/orchestrator/bundle_execution.py",
+ "sha256": "25446d6c6a43c22ff7104c6712c143f0276bc4749c2642fffac10db1455ef707",
+ "role": "Viewer bootstrap with rollback of the acquired set, settle-then-read-state order, and stopping of execution-owned viewers."
},
{
- "path": "openhcs/agent/services/viewer_window_service.py",
- "sha256": "257718ca7da768132be2283326730f65d9b399f0ef852b2c7e1e9e48263f9eed",
- "role": "Projects one typed bulk viewer response into agent results without client-side state mirroring or per-layer round trips."
+ "path": "openhcs/runtime/viewer_protocol.py",
+ "sha256": "347a65b63c149f2f437014d537cfaf481a33801d860742f4083b7377494e792f",
+ "role": "ManagedViewerLifecycleMixin, ViewerProcessPlatform Linux policy, ViewerSettleProgress phases, and the execution-owned EndpointProcessGroup."
},
{
- "path": "openhcs/runtime/zmq_execution_server.py",
- "sha256": "812ac9fc2363afa892410dbfebb461d4bc6f44c663e3ac4490e8edf41cf0dee2",
- "role": "Releases execution-session process resources when the execution server stops."
+ "path": "openhcs/runtime/viewer_controls.py",
+ "sha256": "df0ac0aee66ba52a8b1706a1a99e03acc1300cfaf5cc74c2f93614a1c77465f6",
+ "role": "ViewerPayloadControlOptions, ViewerPayloadProjectionOptions, SUMMARY shape projection and ViewerLayerIsolationControlOptions."
},
{
- "path": "openhcs/core/execution_visualizer.py",
- "sha256": "88d4332fbf10d63107d56675753b16a7afab49d0f16571949ae0bf2eda2be23c",
- "role": "Declares the nominal application viewer contract consumed by compiled execution."
+ "path": "openhcs/runtime/napari_viewer_server.py",
+ "sha256": "ac54f8bfdce33150c31b38bcd5cf6069b5fd66fb7c05d5a6951610ec3d895bfd",
+ "role": "NapariImagePayload intake validation, bounded shape projection and preflighted layer isolation in the Napari Qt process."
},
{
- "path": "openhcs/core/orchestrator/compiled_plate_execution.py",
- "sha256": "80998f355c3f4be9027cb91cabf581f51efd870fb86960f5f6ecd615bcdc89ef",
- "role": "Acquires required viewers transactionally and rolls back the exact acquired set on launch or readiness failure."
+ "path": "openhcs/runtime/napari_streaming_handlers.py",
+ "sha256": "b43f29163cef420c77f30e46e92d94145520f7534077d54636a0ab141da32e7e",
+ "role": "Accepted intake-cycle and route error retention through settlement."
},
{
- "path": "external/PolyStore/src/polystore/filemanager.py",
- "sha256": "877a2b10731b12904b1784a5b42c9e39688e3a84f7f86018c1c2beee96ea2296",
- "role": "Generic FileManager storage and backend composition."
+ "path": "openhcs/agent/services/viewer_window_service.py",
+ "sha256": "cfe222586a221cb317099fc245216778f2ce15589604e5ce03410998dec4bb1a",
+ "role": "Projects one typed bulk viewer response into agent results without per-layer round trips."
},
{
- "path": "external/zmqruntime/src/zmqruntime/execution/client.py",
- "sha256": "1b9453ea2be540b29849770cc3fe82f284f501e0e140797dd48c2c257ec36f12",
- "role": "Generic execution client submit, wait, progress, and connection lifecycle."
+ "path": "openhcs/runtime/zmq_execution_server.py",
+ "sha256": "709193dcb39298d3ad8bdc7908150740993da161547b6499be9923cb2317f3f1",
+ "role": "Releases execution-session process resources when the execution server stops."
+ },
+ {
+ "path": "external/PolyStore/src/polystore/streaming/identity.py",
+ "sha256": "a8e48d76c8fd222515121e2ebc879dccc820e0ebf5b576a8728c1a7ffd35dc4e",
+ "role": "StreamProducerIdentity and its projection_key receiver route."
},
{
"path": "external/zmqruntime/src/zmqruntime/client.py",
- "sha256": "fc8bdf68455673921d79742204bf6c88e42b52bbe407d10ef4a831e084ca86a8",
- "role": "Provides exact viewer-process ownership, terminal observation, automatic child reaping, and the generic group used for execution-owned viewers."
+ "sha256": "7d3cce31d5abaa070927f02fe87d919687619e2cf4adbc37ab82483b6b8fc212",
+ "role": "EndpointProcessGroup, exact viewer-process ownership and terminal child reaping."
},
{
"path": "tests/unit/test_viewer_protocol.py",
- "sha256": "f2226d3674b56cd46d2f93ad6764df96616104485d5f8ae15469882854492eaa",
- "role": "Proves execution cleanup stops exact non-persistent processes while persistent viewers remain available for typed lifecycle shutdown."
+ "sha256": "3aaf1fea097807f3bcd82c11abf58c134303c499bda49206ad9328900b68f0af",
+ "role": "Proves execution cleanup stops exact non-persistent processes while persistent viewers remain available."
},
{
"path": "tests/unit/test_viewer_result_element_selection.py",
- "sha256": "58480f4da61b427ec1c55560a77686e74df70404fbbf77e701ba386ad9f89a03",
- "role": "Proves bulk isolation validates every route and navigation before any visibility, selection, or axis mutation."
+ "sha256": "b1dae580f0291f53be9453df6f6c6df5458046bd3ff947b550f7ddea94b29f96",
+ "role": "Proves bulk isolation validates every route and navigation before any mutation."
},
{
"path": "tests/unit/test_napari_streaming_handlers.py",
- "sha256": "a338d6f2ef3094fa429f3d640709c7231eb741a7bf2859318bf4bbfa196afc07",
- "role": "Proves one global ROI-member budget and metadata-only shape projection across payload records."
- },
- {
- "path": "tests/unit/test_orchestrator_lane_planning.py",
- "sha256": "09c39f5cba68f8c15a0def8a09843b62d6da5c5a01e7558a9263ce4d311c078d",
- "role": "Proves multi-viewer bootstrap rollback after partial acquisition and readiness failure."
+ "sha256": "c08363837db3efd13534c966a6192b785cb85ccca742b574ac9bd0e74600b80e",
+ "role": "Proves one global ROI-member budget and metadata-only shape projection."
},
{
- "path": "openhcs/runtime/napari_streaming_handlers.py",
- "sha256": "86f4ab319c7c68b626e7e2a1adef81eb3d7244f044b5b3e4dddd78bbee2aa4b6",
- "role": "Owns accepted intake-cycle and route error retention through settlement."
+ "path": "tests/unit/test_napari_accepted_work_settlement.py",
+ "sha256": "32c52a4807fddceb25839b75c6a937dcc0c7212437c2c3072066b976cf894306",
+ "role": "Tests accepted display failures, settlement reporting and recovery."
},
{
- "path": "tests/unit/test_napari_accepted_work_settlement.py",
- "sha256": "a6248305f0d4b27b92b3eb81fe5b75a31073695ad3b5ebfab1d76071d9d305e9",
- "role": "Tests actual accepted display failures, settlement reporting and recovery."
+ "path": "tests/unit/test_orchestrator_lane_planning.py",
+ "sha256": "4af8c5e5a6d68a64f862a0bcb98f398a0a898948e6f8b5e462a4a11381f19524",
+ "role": "Proves multi-viewer bootstrap rollback after partial acquisition and readiness failure."
}
],
- "findings": [
- "Link checking showed that the viewer-streaming deep Read the Docs page is not deployed; the owner link now follows the maintained ZMQRuntime source instead of hard-coding a stale package release.",
- "Execution-server cleanup registered persistent viewers in its transient process group and stopped them before post-run inspection; persistence policy now limits that exact group to non-persistent viewers.",
- "A terminated retained endpoint process previously depended on later caller polling for child reaping; ZMQRuntime now retains one terminal observer per exact process.",
- "ROI summaries transported full shape geometry and applied the per-record maximum independently; the runtime now composes one response-wide budget with metadata-only summary projection.",
- "Layer isolation required state inspection followed by one navigation round trip per mounted layer; the viewer now preflights and applies the complete typed projection in one control operation.",
- "Multi-viewer bootstrap did not retain incremental acquisition evidence, allowing a later launch or readiness failure to leak an earlier owned viewer."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/streaming_boundary_and_wrappers.rst in full and compared it against the changed authorities: openhcs/core/viewer_streaming_service.py, compiled_step_plan.py, runtime_object_labels.py, orchestrator/bundle_execution.py, openhcs/runtime/viewer_protocol.py, napari_viewer_server.py, napari_streaming_handlers.py, zmq_execution_server.py and the four changed tests; E1 turned ViewerSettlePhase (SettlementRunning/Complete/Failed), the viewer lifecycle (ViewerStopped/ViewerConnectedExternal/ViewerOwnedProcess), the launch session, NapariLayerKind and OrchestratorState into class families; the page states settlement by its wire phase names complete and failed, which the phase classes keep, and describes owned versus external viewer rollback, which the lifecycle classes implement; confirmed ViewerType is still the enum derived from the ViewerFamily registry, and that ViewerShapePayloadProjection.SUMMARY, ViewerProcessPlatform, ViewerSlotFamily, ViewerControlAction, DeclaredAxis and NapariImagePayload exist; no page change.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/streaming_boundary_and_wrappers.rst in full and compared it against the changed authorities: compiled_step_plan.py (CompiledSourceBindingPlan/CompiledSourceUniversePlan default factories), orchestrator/bundle_execution.py (K6 ProgressPhase/ProgressStatus classes, lane results ordered by compiled axis), runtime/zmq_execution_server.py (K6 debug replay predicate) and test_orchestrator_lane_planning.py; verified StreamComponentMessageExtraAuthority, StepOutputManifestStore, ArtifactMaterializationTargetPlan, producer_identity_for_main_flow/_for_artifact, StreamProducerIdentity.projection_key, ObjectLabelValue.contextualize_image_output, ViewerFamily/ViewerType, ViewerControlAction, ViewerProcessPlatform, ViewerSettleProgress, EndpointProcessGroup, the viewer control/projection option classes, settle-then-read-state order, server stop cleanup and the bootstrap rollback test all still exist as described; no claim needed correction.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/streaming_boundary_and_wrappers.rst against B6's later changes to openhcs/core/viewer_streaming_service.py, openhcs/core/compiled_step_plan.py, openhcs/core/runtime_object_labels.py, openhcs/core/orchestrator/bundle_execution.py, openhcs/runtime/viewer_protocol.py, openhcs/runtime/zmq_execution_server.py, tests/unit/test_viewer_result_element_selection.py, tests/unit/test_napari_streaming_handlers.py, tests/unit/test_napari_accepted_work_settlement.py, tests/unit/test_orchestrator_lane_planning.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/streaming_boundary_and_wrappers.rst against the later B6 changes to openhcs/core/viewer_streaming_service.py, openhcs/core/compiled_step_plan.py, openhcs/core/runtime_object_labels.py, openhcs/core/orchestrator/bundle_execution.py, openhcs/runtime/viewer_protocol.py, openhcs/runtime/zmq_execution_server.py, tests/unit/test_viewer_result_element_selection.py, tests/unit/test_napari_streaming_handlers.py, tests/unit/test_napari_accepted_work_settlement.py, tests/unit/test_orchestrator_lane_planning.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/streaming_boundary_and_wrappers.rst in full and compared it against the changed authorities: viewer_streaming_service.py, streaming_config_declarations.py, viewer_protocol.py, viewer_controls.py, napari_viewer_server.py, viewer_window_service.py, zmq_execution_server.py, compiled_plate_execution.py (renamed bundle_execution.py, G7), zmqruntime client.py and the two viewer tests; checked every named class exists, corrected the producer-identity and label-image context owners, and confirmed the bootstrap rollback, settle/read-state/stop order, Linux XCB/MIT-SHM/vblank policy and settle phases in current code.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"tests/unit/test_streaming_service.py and tests/unit/test_viewer_protocol.py exercise the compiled stream, readiness, settlement, and platform policies",
"Sphinx linkcheck resolved the then-pinned zmqruntime viewer-streaming owner-source link",
@@ -1588,25 +1783,30 @@
"authority": [
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
- "role": "PipelineDocument construction and atomic configuration-step identity."
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
+ "role": "PipelineDocument and PipelineDocumentCodec: the declaration entry point of the end-to-end flow."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compilation order, declaration resolution, callable warm-up, framework-local device assignment, and bundle construction."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "PipelineCompiler phases from declaration resolution to framework device assignment."
},
{
"path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
- "role": "CompiledExecutionBundle and runtime-environment authority; execution-wide worker facts remain separate from per-step framework devices."
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "CompiledExecutionBundle, worker start and runtime-environment plans handed to execution."
}
],
"findings": [
- "The previous broad integration narrative was replaced by the current typed system overview."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/system_integration.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py; the diff swaps enum members for the E1 class families (OrchestratorCompiled, MaterializeToZarr, PrimaryPlaneRole); the page states nothing about them, and system_overview.rst, the redirect target, exists.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/system_integration.rst in full and compared it against the changed authorities: openhcs/core/pipeline_document.py, openhcs/core/pipeline/compiler.py; PipelineDocument now renders through pycodify generate_python_source and the compiler uses ScopeKey and the K6 progress classes; the redirect to system_overview still describes the current flow. No correction needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/system_integration.rst against B6's later changes to openhcs/core/pipeline/compiler.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/system_integration.rst against the later B6 changes to openhcs/core/pipeline/compiler.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/system_integration.rst in full and compared it against the changed authorities: openhcs/core/pipeline_document.py (PipelineDocumentAuthority renamed PipelineDocumentCodec), openhcs/core/pipeline/compiler.py and openhcs/core/compiled_execution.py; the redirect states no API detail and its target system_overview.rst exists; no prose change was needed.",
"Re-read docs/source/architecture/system_integration.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline_document.py; openhcs/core/pipeline/compiler.py; openhcs/core/compiled_execution.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -1617,7 +1817,7 @@
},
{
"path": "docs/source/architecture/system_overview.rst",
- "source_sha256": "87f6eec65645b2fd4a01de961126c765230ae0eb3480a1e3b4a7b3fb3618b28f",
+ "source_sha256": "30b4399b81c0557f010d3723c24aac7752551b3fc03b9b3e3d250679b6cfb1da",
"audience": [
"OpenHCS users, maintainers, and integrators"
],
@@ -1626,63 +1826,83 @@
"authority": [
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
- "role": "PipelineDocument construction and atomic configuration-step identity."
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
+ "role": "PipelineDocument: PipelineConfig plus the FunctionStep list that all front ends converge on."
},
{
"path": "openhcs/core/pipeline/compilation_session.py",
- "sha256": "3c840a89baccc8665d415175458280fd904c1a837b7fd879e303d7dbca129a1c",
- "role": "Resolved step/session identity and typed plan assembly invariants."
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "ResolvedPipelineDefinition (resolved steps, scope ids, field provenance) and the per-axis CompilationSession."
+ },
+ {
+ "path": "openhcs/core/compiled_step_plan.py",
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "CompiledStepPlan typed fields, including framework-local device assignment."
+ },
+ {
+ "path": "openhcs/core/pipeline/framework_device_assignment.py",
+ "sha256": "cdfde10f996ab682952a1d4948d3de2d23d48353eae41f3124770e19fc28a193",
+ "role": "Declaration-derived framework footprint and per-step device selection."
},
{
"path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
- "role": "CompiledExecutionBundle and runtime-environment authority; execution-wide worker facts remain separate from per-step framework devices."
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "CompiledExecutionBundle contexts, transport contexts, worker assignments and runtime-environment plan."
},
{
"path": "openhcs/core/runtime_stores.py",
- "sha256": "5f323ec8edd4221ee30e34ed0c9c09e0f4e45ef502749c1a2d331b7a5452af99",
+ "sha256": "35fe71906bfd16842de347776a0c5cc846550ad490cc3628885129d746ec6f31",
"role": "RuntimeValueStore recording, replacement, query, and observation semantics."
},
{
- "path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
- "role": "Current Python support, dependency, package discovery, and entry-point authority."
+ "path": "openhcs/core/runtime_plane_projection.py",
+ "sha256": "cf644451b6649dcec398fecbf961a4a29ea74f0bb2e0bdab052ec1be8a427f95",
+ "role": "RuntimeSliceProjectableValue: value families own their slice projection."
+ },
+ {
+ "path": "external/python-introspect/src/python_introspect/jsonable.py",
+ "sha256": "c845361d0d14914798d9a1b5329d681300511fc7e95c67508943425716930ebd",
+ "role": "python-introspect's to_jsonable JSON encoder."
},
{
"path": "external/python-introspect/README.md",
- "sha256": "d861f8aff2e79d9e77fbb2286b2f353c5b568412482a57efce9a488e58d543fe",
- "role": "States python-introspect ownership of callable/signature analysis and signature-derived declaration projection."
+ "sha256": "059dcfaafc0b2222e8e64b7fe072d24bddfb5a347a6f25113e715ed7946e44a1",
+ "role": "States python-introspect ownership of callable/signature analysis and declaration projection."
},
{
- "path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
- "role": "Typed step-plan authority including framework-local device bindings."
+ "path": "external/arraybridge/src/arraybridge/types.py",
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
+ "role": "Declaration-derived child-process environments for optional array frameworks."
},
{
- "path": "openhcs/core/pipeline/framework_device_assignment.py",
- "sha256": "f77f3fb2d30c4af8cab6210dbe294f9fb5e246dad0a6bbf88bcaf1e2b58dead7",
- "role": "Declaration-derived framework footprint and per-step device selection."
+ "path": "external/metaclass-registry/src/metaclass_registry/discovery.py",
+ "sha256": "757958272d11633e7b854c7104c07f0d90845c37fee6ab0c3646fb14c2ed8daa",
+ "role": "Registry discovery imports that preserve host-owned root logging."
},
{
- "path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
- "role": "Owns declaration-derived child-process environments for optional array frameworks."
+ "path": "external/ObjectState/src/objectstate/scope_key.py",
+ "sha256": "d0dd6e56cebf413d6869b07c7b9d8668ba954ed5aeead36c70288920a5b412b2",
+ "role": "ScopeKey: the one scope-key grammar and separator."
},
{
- "path": "external/metaclass-registry/src/metaclass_registry/discovery.py",
- "sha256": "f92c94a448701a8dec278c8c6ce4b0a48c49ffc7df48620c5f2a51bc1734d24b",
- "role": "Owns registry discovery imports that preserve host-owned root logging."
+ "path": "external/ObjectState/src/objectstate/codegen.py",
+ "sha256": "347d2697c061a35e6e440e373fe4e8bfce1ad92b500e98da07faa26f6a3a1356",
+ "role": "Lazy-configuration source formatters used when pipelines are saved as Python."
+ },
+ {
+ "path": "external/pyqt-reactive/src/pyqt_reactive/windows/config_window.py",
+ "sha256": "2ee4f5c173b30b0a94a4288243d82ae66b6d65d12aa59493ad9363559966c1cd",
+ "role": "Generic configuration window that OpenHCSConfigWindow subclasses."
}
],
- "findings": [
- "Execution-wide GPU registry ownership was removed; framework-local bindings now remain on the compiled step plans that derive them.",
- "The extracted-package owner summary understated ArrayBridge's callable memory-role, framework-device, and execution-scope mechanics.",
- "The extracted-package summary now includes python-introspect's callable-declaration projection boundary.",
- "The system overview omitted declaration-derived child import environments and logging-safe discovery from the extracted-package boundary."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/system_overview.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compilation_session.py, compiled_step_plan.py, runtime_stores.py, runtime_plane_projection.py, external/python-introspect/src/python_introspect/jsonable.py; the diffs are imports from the openhcs/core/artifacts package, UnresolvedStepInput as the default step-input dependency, and new slice-projection helper methods; CompilationSession, the CompiledStepPlan fields, CompiledExecutionBundle, RuntimeValueStore and to_jsonable/dataclass_from_mapping match the page; no page change.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/system_overview.rst in full and compared it against the changed authorities: pipeline_document.py, compilation_session.py, compiled_step_plan.py, runtime_stores.py, python_introspect/jsonable.py; the flow, ResolvedPipelineDefinition, CompilationSession, CompiledStepPlan typed fields and RuntimeValueStore claims still hold (diffs: generate_python_source, graph.validate_invocation_declarations, CompiledSourceBindingPlan() defaults). Updated the package-owner list for U3/L6: ObjectState now owns ScopeKey and objectstate.codegen, and pyqt-reactive owns configuration windows, editable tables, grid selection and time travel.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/system_overview.rst against B6's later changes to openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py, openhcs/core/runtime_stores.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/system_overview.rst against the later B6 changes to openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py, openhcs/core/runtime_stores.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/system_overview.rst in full and compared it against the changed authorities: pipeline_document.py, pipeline/compilation_session.py, compiled_execution.py, runtime_stores.py, compiled_step_plan.py, framework_device_assignment.py, pyproject.toml and the python-introspect/arraybridge/metaclass-registry owners; replaced StepSnapshot with ResolvedPipelineDefinition, restated the session invariant to what CompilationSession enforces, moved slice projection to value families, added the python-introspect JSON codec, and confirmed the CompiledStepPlan fields and bundle environment facts.",
"Re-read docs/source/architecture/system_overview.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline_document.py; openhcs/core/pipeline/compilation_session.py; openhcs/core/compiled_execution.py; openhcs/core/runtime_stores.py; pyproject.toml; openhcs/core/compiled_step_plan.py; openhcs/core/pipeline/framework_device_assignment.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -1719,25 +1939,31 @@
"authority": [
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
- "role": "Current Python support, dependency, package-discovery, and installed entry-point authority."
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
+ "role": "No Textual dependency or entry point; openhcs-gui gui-script points at the PyQt application."
},
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
- "role": "Desktop composition and live UIConfig application boundary."
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
+ "role": "PyQt desktop main window composition."
},
{
"path": "tests/unit/pyqt_gui/test_ui_config_lifecycle.py",
- "sha256": "eeb7c260d3ae8bc7b5e6d94917b1e5e9a214feac9e2a0c2651552950cfe3a179",
- "role": "Prevents the removed Textual package authority from returning alongside the supported PyQt lifecycle."
+ "sha256": "78c9d42d91a384d0b9bce1139dfcd171a2d87a8781be4c078e2f9703bf302043",
+ "role": "Guards that openhcs/textual_tui does not return."
}
],
"findings": [
- "The transition page still described deprecated Textual sources as retained after their implementation had been removed."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/architecture/tui_system.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/tui_system.rst in full and compared it against the changed authorities: pyproject.toml, openhcs/pyqt_gui/main.py; pyproject.toml declares no Textual dependency, main.py only swapped selection and update-check enum members for class families, and docs/source/user_guide/index.rst and docs/archive exist.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/tui_system.rst in full and compared it against the changed authorities: pyproject.toml, openhcs/pyqt_gui/main.py, tests/unit/pyqt_gui/test_ui_config_lifecycle.py; pyproject.toml still has no Textual dependency and the openhcs-gui gui-script points at openhcs.pyqt_gui.__main__:main; main.py changes are U3 moves of generic Qt machinery; the lifecycle test still guards that openhcs/textual_tui does not return (its shortcut tests moved to pyqt-reactive); docs/archive still exists. No correction needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/tui_system.rst against B6's later changes to openhcs/pyqt_gui/main.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/tui_system.rst against the later B6 changes to pyproject.toml, openhcs/pyqt_gui/main.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/tui_system.rst in full and compared it against the changed authorities: pyproject.toml, openhcs/pyqt_gui/main.py and tests/unit/pyqt_gui/test_ui_config_lifecycle.py; confirmed no Textual dependency, package or entry point exists, openhcs-gui still launches openhcs.pyqt_gui, the textual_tui guard test remains and docs/archive still exists; no prose change was needed.",
"Re-read docs/source/architecture/tui_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: pyproject.toml; openhcs/pyqt_gui/main.py; tests/unit/pyqt_gui/test_ui_config_lifecycle.py.",
"On 2026-08-22, revised the transition page after deleting the excluded Textual package and passed 29 focused website and UI-lifecycle tests.",
@@ -1766,7 +1992,7 @@
},
{
"path": "docs/source/architecture/ui_services_architecture.rst",
- "source_sha256": "f398733f3501923efcbd7e41fc8da2681c9ffe1e78c2d1997fc65df5e731de3a",
+ "source_sha256": "abc8843ed2629474f2d541424a237731f8da1ebd38eb21348e9740264e3c149b",
"audience": [
"OpenHCS desktop, ObjectState, and UI-service maintainers"
],
@@ -1775,17 +2001,17 @@
"authority": [
{
"path": "openhcs/pyqt_gui/config.py",
- "sha256": "a3eece2e5b2926fc6fc0edd197221da451ab7c0d0d6f4ffc8bb2ff865ad74bd4",
+ "sha256": "d092cb20206127484fbbc80a38a5152462b9b8991e848a814c85f564d51f4db8",
"role": "Embeds the persisted generic list-preview policy in UIConfig."
},
{
"path": "openhcs/runtime/zmq_config.py",
- "sha256": "842b25ffd542f99923dafb2f67d23bac0263f38d85e64ef1bdbaba3da671b792",
- "role": "OpenHCS-owned ZMQConfig subtype and process-level transport fields."
+ "sha256": "7c96c39d684d450ae045d1de1186dd27c5d67e042996857d5cdfcda6c2d4d069",
+ "role": "OpenHCSZMQConfig: ExecutionTransportConfig subtype that sets OpenHCS application names."
},
{
"path": "openhcs/pyqt_gui/services/window_handlers.py",
- "sha256": "5080a81191a8cfc78ce826db7e241bd41d3df7607deea32128a06449e04a3aa8",
+ "sha256": "6f05c7945f41b25e0b783231d51b508729ba0713911224bf46f84f89ea24823f",
"role": "Registers OpenHCS domain window routes, including declaration-owned config ObjectState scopes and stable configuration-window presentation, over pyqt-reactive."
},
{
@@ -1795,12 +2021,12 @@
},
{
"path": "tests/unit/pyqt_gui/test_ui_config_lifecycle.py",
- "sha256": "eeb7c260d3ae8bc7b5e6d94917b1e5e9a214feac9e2a0c2651552950cfe3a179",
- "role": "Proves one absolute environment-selected UIConfig cache path and rejects relative paths."
+ "sha256": "78c9d42d91a384d0b9bce1139dfcd171a2d87a8781be4c078e2f9703bf302043",
+ "role": "Proves one absolute environment-selected UIConfig cache path and that UIConfig reaches the session's progress interval and transport."
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/services/ui_thread_dispatch.py",
- "sha256": "f2dbe7e8ce8ac0ad3116001d7dfcd91affd516a34615775ce4ce9921480e40bd",
+ "sha256": "f6daf3e8588888fff7490775fa048bac9daca189e16afe25188791b2985b8535",
"role": "Owns bounded callable dispatch, outcomes, cancellation, and shutdown on the Qt application thread."
},
{
@@ -1810,7 +2036,7 @@
},
{
"path": "openhcs/pyqt_gui/app.py",
- "sha256": "3257f148dfddac3f1ff7409881db8100273adac5c3564b9cd3745e8f75313086",
+ "sha256": "3816c79689a29c22d2e4098622c0d3242c334517b201f159f30a728ea0fb322f",
"role": "Owns application execution and native window-tree destruction before QApplication teardown."
},
{
@@ -1819,19 +2045,24 @@
"role": "Exercises real QApplication shutdown in a subprocess and asserts native parent and child destruction while Python references remain alive."
},
{
- "path": "openhcs/agent/ui_bridge_actions.py",
- "sha256": "28c494cc9335ac84482787f1949d9937fd818864407d56ca23ba585a3b0156a1",
- "role": "Owns main-window action behaviour, admission, effects and recovery warnings."
+ "path": "openhcs/authoring/session/operations/application.py",
+ "sha256": "628fb1dd209d62d1d5830e710cf6e0ea6804f576569489da7df42f974652ffd8",
+ "role": "CheckForUpdates, RestartApplication and ExitApplication session operations with availability, side effects and reconnect warnings."
+ },
+ {
+ "path": "openhcs/pyqt_gui/services/ui_bridge_session_actions.py",
+ "sha256": "033aee9296f6f1c75f01d1fc3a39e5aeb0e7c13c15a7e4396872d3d014673123",
+ "role": "SessionOperationActionProvider projects session operation classes as UI-bridge actions."
},
{
- "path": "openhcs/pyqt_gui/services/desktop_restart.py",
- "sha256": "24ca24c3afc0038a268280a73ae7615045eb107ee541a59c13fcdf07005603a2",
- "role": "Owns explicit session-preserving restart admission and detached relaunch."
+ "path": "openhcs/desktop/update.py",
+ "sha256": "23ffce8fe2362d8651ea90d2ef9601d193658e4fcef6aef8964e888f43014a3c",
+ "role": "DesktopRestartSession capture and restoration shared by restart, update and version-mismatch recovery."
},
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
- "role": "Applies the same preview policy to both managers at startup and after configuration save."
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
+ "role": "OpenHCSMainWindow.set_ui_config consumer reconciliation and rollback, and preview-policy propagation to both managers."
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/strategies/preview_formatting.py",
@@ -1840,7 +2071,7 @@
},
{
"path": "tests/unit/pyqt_gui/test_main_config_propagation.py",
- "sha256": "291dc45ca20ead6025c255a8109a2e6c55d90bd7858e80ad12484dafa733d7c5",
+ "sha256": "303d90b43bee7bd531a1c7664fbf9707a2e7be1696a5147614f93c40e5c7021f",
"role": "Verifies live preview policy propagation through the main configuration lifecycle."
},
{
@@ -1882,17 +2113,37 @@
"path": "external/pyqt-reactive/tests/test_deferred_callback.py",
"sha256": "1c59913d1b19a9725e5735bbd342dc115fdf4b555863427169b5001324e83b1c",
"role": "Real Qt regressions cover callback retirement, owner destruction, widget cancellation and persistent row identity."
+ },
+ {
+ "path": "openhcs/pyqt_gui/widgets/plate_manager.py",
+ "sha256": "b6e56da089cff746c9f0f41ca4b4d947942d4a71d1b290a1d8b76c3b7732c31e",
+ "role": "Plate Manager renders the Session dataset list, holds no session state, and forwards transport and progress settings to the session."
+ },
+ {
+ "path": "external/pyqt-reactive/src/pyqt_reactive/windows/config_window.py",
+ "sha256": "2ee4f5c173b30b0a94a4288243d82ae66b6d65d12aa59493ad9363559966c1cd",
+ "role": "ConfigWindow.save_config: validation and mutation guard before checkpoint, participant compensation in reverse, global-context then ObjectState restoration, post-commit notifications."
+ },
+ {
+ "path": "openhcs/pyqt_gui/windows/config_window.py",
+ "sha256": "f72c0e481e35819f49749a7b82729e2f99ff200ebfd5bd6031efdb6f0585b24b",
+ "role": "OpenHCSConfigWindow supplies only the OpenHCS configuration-document format and window ids."
+ },
+ {
+ "path": "external/pyqt-reactive/src/pyqt_reactive/services/application_shortcuts.py",
+ "sha256": "73ccd1ddebc60e90514c0b2b561e8e84cb3b8b5743a50318911a947823b0b2a5",
+ "role": "ShortcutLifecycle rejects duplicate keys before rebinding QActions and the application shortcut filter."
}
],
- "findings": [
- "The page named the removed ScopeWindowFactory instead of ScopeWindowRegistry and incorrectly assigned the OpenHCSZMQConfig subtype itself to the generic transport package.",
- "The UI configuration explanation did not record the single absolute process-local cache override used by isolated launches.",
- "The window-composition explanation did not record that process configuration scopes share one stable window while the requested ObjectState scope and field select the owning tab.",
- "Added list-preview policy to the existing UI configuration lifecycle without duplicating generic settings.",
- "Clarified row-arrow overrides and repeated graphical guides, distinguishing shared wrapping defaults from row-owned display choices."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/architecture/ui_services_architecture.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/ui_services_architecture.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/config.py, openhcs/pyqt_gui/app.py, openhcs/desktop/update.py, openhcs/pyqt_gui/main.py, openhcs/pyqt_gui/widgets/plate_manager.py; E1 turned the startup-readiness state, the update-check origin (ExplicitUpdateCheck/StartupUpdateCheck), the selection scope (SelectedItems/AllItems) and OrchestratorState into class families; the page names none of them and its statements on OpenHCSPyQtApp shutdown, UIConfig fields, set_ui_config, the update-check preference and Plate Manager's session rendering are unaffected; every named class was grepped and exists; no page change.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/ui_services_architecture.rst in full and compared it against the changed authorities: pyqt_gui/services/window_handlers.py (ScopeKey separator, OpenHCSConfigWindow over pyqt-reactive ConfigWindow, session event connection), tests/unit/pyqt_gui/test_ui_config_lifecycle.py (shortcut tests removed), pyqt_gui/main.py (pyqt-reactive ShortcutLifecycle, DockLayoutStore/DockPane, TimeTravelCommands/TimeTravelNavigator) and widgets/plate_manager.py (GuiEventBusBroadcaster removed, PathCacheKey and external-editor policy from pyqt-reactive); corrected the shortcut-lifecycle and configuration-window transaction owners and checked the save transaction order in pyqt-reactive ConfigWindow.save_config against the page's rollback description.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/ui_services_architecture.rst against B6's later changes to openhcs/pyqt_gui/services/ui_bridge_session_actions.py, openhcs/pyqt_gui/main.py, tests/unit/pyqt_gui/test_main_config_propagation.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/ui_services_architecture.rst against the later B6 changes to openhcs/pyqt_gui/services/ui_bridge_session_actions.py, openhcs/pyqt_gui/main.py, tests/unit/pyqt_gui/test_main_config_propagation.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/ui_services_architecture.rst in full and compared it against the changed authorities: runtime/zmq_config.py, services/window_handlers.py, pyqt_gui/main.py, ui_thread_dispatch.py, the two UI config tests, agent/ui_bridge_actions.py (deleted, U1) and services/desktop_restart.py (deleted); verified every named class exists, replaced MainWindowAction with session operations, moved DesktopRestartSession to openhcs/desktop/update.py, corrected the OpenHCSZMQConfig base, and restated Plate Manager as a renderer of Session state; UIConfig fields, set_ui_config ordering and application shutdown were confirmed in code.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"tests/unit/test_ui_zmq_config_authority.py passed; source inspection confirmed ScopeWindowRegistry routing and OpenHCSZMQConfig inheritance",
"On 2026-08-22, re-read the complete explanation against UIConfig cache ownership and lifecycle tests; the focused OpenHCS documentation-drift suite passed 238 tests.",
@@ -1920,7 +2171,7 @@
"authority": [
{
"path": "external/pyqt-reactive/src/pyqt_reactive/forms/parameter_form_manager.py",
- "sha256": "a561e62e29af42630fa107b0929fc87b74007f7ed14ec8f0a6881c49070dc923",
+ "sha256": "2c59c3dc2f95a349970815a33c2afcf5158c97f4627973a8a0c6826f8ca9ae6c",
"role": "Reusable parameter-form lifecycle and value projection."
},
{
@@ -1930,10 +2181,11 @@
}
],
"findings": [
- "The transition page duplicated the OpenHCS boundary link and omitted the extracted owner document."
+ "Redirect still correct after U3: the pyqt-reactive owner document exists; no page change needed."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/widget_protocol_system.rst in full and compared it against the changed authorities: pyqt_reactive/forms/parameter_form_manager.py; the page states only that pyqt-reactive owns widget protocols; the linked owner document external/pyqt-reactive/docs/architecture/widget_protocol_system.rst still exists. No correction needed.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"Sphinx resolved the current pyqt-reactive owner target",
"Re-reviewed the public form-build completion, failure, and signal lifecycle against the complete dependent page and the updated pyqt-reactive owner documentation.",
@@ -1943,7 +2195,7 @@
},
{
"path": "docs/source/architecture/zmq_execution_service_extracted.rst",
- "source_sha256": "dcaf4e63eb604312edadcb3442479ac6e369842231bb5caeb3e5404ecf693155",
+ "source_sha256": "a30938b8339fc2a6941b9164dd917257d7dff9250c4ce1139ec24a962361315f",
"audience": [
"OpenHCS contributors following the former local ZMQ execution-service URL"
],
@@ -1952,47 +2204,47 @@
"authority": [
{
"path": "external/zmqruntime/src/zmqruntime/execution/client.py",
- "sha256": "1b9453ea2be540b29849770cc3fe82f284f501e0e140797dd48c2c257ec36f12",
+ "sha256": "dec1edbc173d93d20d07564e4487a58e0c714588b6e2d01a4b75b5b03d59b57c",
"role": "Generic execution client submit, wait, progress, and connection lifecycle."
},
{
"path": "openhcs/runtime/zmq_execution_server.py",
- "sha256": "812ac9fc2363afa892410dbfebb461d4bc6f44c663e3ac4490e8edf41cf0dee2",
+ "sha256": "709193dcb39298d3ad8bdc7908150740993da161547b6499be9923cb2317f3f1",
"role": "OpenHCS compiled-execution adapter over the generic execution server."
},
{
"path": "openhcs/runtime/zmq_execution_observation.py",
- "sha256": "e52b2e9784daa396b493f1f569fbcbb0d394e7e3c03d1a0c67d6594778ce64d3",
+ "sha256": "db40a0d1e82bdbe778f948619e62a93d74c16d3155ebc701a067bbba164f0efa",
"role": "Owns the ordinary value/outcome export schemas, producing execution identity, and exclusive evidence-file creation."
},
{
"path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
"role": "CompiledExecutionBundle and runtime-environment authority; execution-wide worker facts remain separate from per-step framework devices."
},
{
"path": "external/zmqruntime/src/zmqruntime/messages.py",
- "sha256": "0df6261b31d3e2758ced4c7ee5933dde0e866b8e30d7927c3fcb2641a8d97d44",
+ "sha256": "fb257a7ac25a47d8c604fbfbd88c96b92fac16f914a61dab81c784fe99d95378",
"role": "Declares endpoint application identity, generic exact compatibility, and member-owned execution lifecycle semantics."
},
{
"path": "external/zmqruntime/src/zmqruntime/execution/lifecycle.py",
- "sha256": "23596c31b9efe812f1ab66b4300ee1bb19125ea9bef6e51ddcd8acaafb0f4ed6",
+ "sha256": "5e984c7a5b154a2ed67028871451a8f8a1343633510d7b8ad1cf2ad5468272b3",
"role": "Owns thread-safe terminal transitions and targeted cancellation outcomes."
},
{
"path": "external/zmqruntime/tests/test_execution.py",
- "sha256": "eadc940b9c04b221b993892442bcc71bd489de0687696bc037b3de931c0fffe5",
+ "sha256": "8d4bf962418d8338c165a583d391a9691aa5a167a76e41542adb8b20f3a21a17",
"role": "Proves immutable per-execution progress observations alongside terminal cancellation, readiness cleanup, and lifecycle guarantees."
},
{
"path": "external/zmqruntime/src/zmqruntime/client.py",
- "sha256": "fc8bdf68455673921d79742204bf6c88e42b52bbe407d10ef4a831e084ca86a8",
+ "sha256": "7d3cce31d5abaa070927f02fe87d919687619e2cf4adbc37ab82483b6b8fc212",
"role": "Retains the exact readiness handshake as the authoritative connected-endpoint observation while preserving the established client readiness extension point."
},
{
"path": "external/zmqruntime/src/zmqruntime/transport_modes.py",
- "sha256": "a4287d0045e69506b4f8b570aeaa14fd6b6b25a61c86467f67ac2ee8bdc74d83",
+ "sha256": "5f97b45a78b759e454e2dad260579af32fbb3be42a1dff8b724220709911507f",
"role": "Owns transport-specific endpoint operations, including IPC deadlines and data/control pair availability."
},
{
@@ -2007,76 +2259,96 @@
},
{
"path": "openhcs/runtime/zmq_execution_client.py",
- "sha256": "31081f2a323a81c060aede1201a4bf1a1b39a51f37928c281a419ffaceadfe60",
+ "sha256": "ad7ea01599493dbefce1f3c91e6b082e932c6a5183d0a85a186f7bd7b62779a9",
"role": "Spawns the execution server with the ArrayBridge-projected child environment and carries typed execution transport."
},
{
"path": "external/zmqruntime/src/zmqruntime/timeouts.py",
- "sha256": "bb5121a03070f0f2a0ed76170a2324512ed4a4d64536ecc7ba8bdc6a4b8961d0",
+ "sha256": "192f0e5ad6905cf60a197ba1a207c30eb3c9a98e6a45425ae20a7711f4b46c92",
"role": "Declares the reusable monotonic operation deadline consumed by connection and control boundaries."
},
{
- "path": "openhcs/agent/services/execution_session_service.py",
- "sha256": "1a9a70d2924b1854338612b212f00888bacba5cd33eb23608be294bb06c76399",
- "role": "Retains the exact submitting client, projects its authoritative progress, caches terminal status, and releases the client once."
+ "path": "openhcs/authoring/session/submission.py",
+ "sha256": "e88dba60a818ce44d0ecf744b918f8a82421ccd0d9a6a5aebce7c15bb331f1dc",
+ "role": "ExecutionSubmission records each accepted submission with its request and endpoint and follows status to terminal."
+ },
+ {
+ "path": "openhcs/authoring/session/session.py",
+ "sha256": "ced615b580e4625e09873449b29ac67cdea87c8b8065c205f32e6d5921cf31fc",
+ "role": "Session owns the one execution client and keeps FinishedExecution (request, endpoint, ExecutionRecord) per execution."
+ },
+ {
+ "path": "openhcs/authoring/session/execution_control.py",
+ "sha256": "40e8e6bf11dc90707ea8f11ebbae4f54d2cec4721ee341fdcad394db24dddf5e",
+ "role": "Session stop: endpoint shutdown (graceful or forced) and cancellation of active datasets."
+ },
+ {
+ "path": "openhcs/runtime/zmq_orchestrator_environment.py",
+ "sha256": "5301811d7a37fa500f4c511ce9e57ac3620b172331f0ffc76a29e3a63797bf85",
+ "role": "Opens execution_subject_id when declared, otherwise subject_id."
+ },
+ {
+ "path": "external/zmqruntime/src/zmqruntime/execution/config.py",
+ "sha256": "a3f3ba1f16fbedeb282825fbc381d4cc788fa8ac1600fd50fa1a27b5c65421db",
+ "role": "ExecutionTransportConfig submission and control timeout budgets inherited by OpenHCSZMQConfig."
},
{
"path": "tests/unit/test_zmq_submission_timeout.py",
- "sha256": "b525a1cc113bdae987c7f202369cc251ca1129188bd8d3adb834aec44f4c8393",
+ "sha256": "acea5dcb72c820ed8c1f225dc1960dfd615ad3f002ef6aec8e78b75bf8e50b3e",
"role": "Proves a total submit deadline prevents execute dispatch after slow startup and preserves the declared connection inactivity timeout."
},
{
"path": "openhcs/runtime/function_catalog_preparation.py",
- "sha256": "63abd2cc72de8baacff723e4a574a74f0f6ef422b17eb32ca5fabead689505fc",
+ "sha256": "c4710d17e92924c685e7b1f41817de0b3ae6656f5a0ea70d12b8a3308d0cb512",
"role": "Owns persistent registry preparation and the live endpoint's single asynchronous catalogue-preparation operation."
},
{
"path": "openhcs/runtime/zmq_execution_server_launcher.py",
- "sha256": "a9110cb65bfe8119e8f60816305f9d5a4bc7e94377eb78c2d13c073cba16eaf5",
+ "sha256": "09c3dd01d5bfd0fe2d08d6d8ffe0c9c806a817940d2df2d176ae5108cf0a1708",
"role": "Executes dedicated persistent catalogue preparation before importing or constructing an execution server."
},
{
"path": "tests/unit/test_zmq_execution_server_process.py",
- "sha256": "710f6f723e90679c21a87e10710db8faaf8556ef8376cb93b7ba891f01855e83",
+ "sha256": "a3c9a5c03de440b49dd8575879d01514dffa0da32cbbfe15c0d363711685d964",
"role": "Proves capability-preparation mode does not construct or bind the execution server."
},
{
"path": "openhcs/processing/backends/lib_registry/registry_service.py",
- "sha256": "046d3d088f685a993ddad6b5923cf63beef84b201127b72ae32af6721dd5ad63",
+ "sha256": "f2e981157632adb5020828720ec26bfa6444ab52f545e1e71b1524242eaed6a3",
"role": "Prepares the canonical catalogue in a child environment projected by ArrayBridge declarations."
},
{
"path": "openhcs/processing/backends/lib_registry/unified_registry.py",
- "sha256": "70462cddcad1bfee447ce0786a9e4a85abbd297726d59a1208156af0ef3a3743",
+ "sha256": "b1e9253021b9c3ab14a070b7d0f10909f641ba07784c794ad95d2ce36041b1d3",
"role": "Processing-contract execution, registry-owned catalogue admission and cache semantics, and external-signature authoring exclusions."
},
{
"path": "openhcs/processing/backends/lib_registry/openhcs_registry.py",
- "sha256": "aa3079341cb2af8788938e14efee6f9f6c4b6c01874227e74c6340850d525884",
+ "sha256": "2a1de7f8af9ade2cba450cbbcc1abd6510a8e3edb58217c6c08fa067bde21de2",
"role": "Derives the public native callable catalogue while retaining explicit reconstruction from each callable declaration."
},
{
"path": "tests/unit/test_library_registry_discovery.py",
- "sha256": "a7ee2d3a2002ea291876c27aa2dce51a889cea6be9a3055602c76f1e9cf76f1b",
+ "sha256": "a67eb8d91a44a3eb347b46608679f2f8632abfd5b54c6f85315d1ea71a164460",
"role": "Proves source-revision cache invalidation, stable optional-runtime admission, declaration-owned projection, runtime-owned external-signature exclusions, and no partial catalogue after failure."
},
{
"path": "tests/unit/test_cellprofiler_processing_backend.py",
- "sha256": "42c779c73969d25cff9b48700599b2af1366cec1cfa57c94a8c0b950b9487c63",
+ "sha256": "111733bb0df819bb8bd9d170a1a6e2b48bd3acd343750a299937ead4d15539a8",
"role": "Proves distinct persistent cache identities for CPU-only and GPU-capable function-catalogue admission contexts."
},
{
- "path": "openhcs/pyqt_gui/services/desktop_restart.py",
- "sha256": "24ca24c3afc0038a268280a73ae7615045eb107ee541a59c13fcdf07005603a2",
+ "path": "openhcs/desktop/restart.py",
+ "sha256": "53028fa6380292520ca42945b12c6844019dd91a77aef3d15e20108830a18c0b",
"role": "Carries the saved desktop session and ObjectState history across a detached process restart."
},
{
- "path": "openhcs/pyqt_gui/services/zmq_version_restart.py",
- "sha256": "86d4bf273c75417b2c4f5c38ac83a66e3487bb233be88a608140a0bdb4c8bc67",
+ "path": "openhcs/desktop/zmq_version_restart.py",
+ "sha256": "84b4f13432862bc9d61dbbcd19d4b54a0240fb243f9ddd2fb94a7b68f5808acb",
"role": "Owns the one state-preserving mismatch decision, endpoint replacement, and desktop restart transaction."
},
{
- "path": "openhcs/pyqt_gui/services/desktop_restart_worker.py",
+ "path": "openhcs/desktop/restart_worker.py",
"sha256": "52d8a8f223d49dbd9ef42951fe507dfbea803b33ff4dfce3e198f4b1a9fe4ed7",
"role": "Waits for the current UI process to exit before launching the captured restart command."
},
@@ -2086,38 +2358,38 @@
"role": "Proves the execution server advertises the OpenHCS version declaration in its handshake."
},
{
- "path": "tests/unit/pyqt_gui/test_desktop_restart.py",
- "sha256": "aafaa6e7a46859f50b44232e12f8db606e0dd4631340a834694a0fe795b5ff70",
+ "path": "tests/unit/desktop/test_desktop_restart.py",
+ "sha256": "50425f39e481a6fff384b9732bc0f2cd68f223d3ac980234a9142b6db981b48f",
"role": "Proves the themed mismatch decision and state-preserving version-restart transaction."
},
{
- "path": "tests/unit/pyqt_gui/test_zmq_version_restart.py",
- "sha256": "052699f393497ab23ec49d23fa136d969245c95c0973d6e9898aedfb860c43f9",
+ "path": "tests/unit/desktop/test_zmq_version_restart.py",
+ "sha256": "f1968ec55295c577e118ada34ba80d987f50d69df30105b8b3a65a1a774c0018",
"role": "Proves success, replacement failure, executor shutdown, capture failure, and restart-launch failure preserve one transaction owner."
},
{
- "path": "tests/unit/pyqt_gui/test_desktop_restart_worker.py",
- "sha256": "a9fbda7d6c78972d6300cd9fe67c2e88497b6b802a9b48057568007bb33b0e1c",
+ "path": "tests/unit/desktop/test_desktop_restart_worker.py",
+ "sha256": "e20c6d3f3122bd09a304b7896245c35ba4fc14bb71943afbd68c9428c5374a90",
"role": "Proves parent-exit gating, Windows handle cleanup, exact restart arguments, and fail-closed launch outcomes."
},
{
"path": "scripts/smoke_installed_desktop.py",
- "sha256": "f745f93e245e5c1b02f6e0ece3a4a0e2af086c3cab77282e3907a167f5ce0a7f",
+ "sha256": "d5ce5e2a2b3590be2101b487b2957b270f386dcf47b4c197f1ce4c2ab6aa9d65",
"role": "Executes the GUI acceptance probe and packaged restart worker from the installed environment while deriving the outer process ceiling from the probe's timing authority."
},
{
"path": "tests/installer/test_installed_desktop_smoke.py",
- "sha256": "5ac9f0e9e0e2ffe73cbab940da132421424e355c354b7476a8a072090c5aad00",
+ "sha256": "bf6fa5db1ff253c4ee3df16b3d46bdc64e87d54171770287baeed3516d44bb05",
"role": "Proves platform smoke orchestration passes the exact installed Python, install root, environment, and declaration-derived GUI operation and process-exit budgets to the installed verifiers."
},
{
"path": "openhcs/processing/custom_functions/runtime_registry.py",
- "sha256": "ee935ccc09f3a967d84c9ebd08511ad729d53e73d5bceb52074b39a1cd6ead9c",
+ "sha256": "d2311617cad39d96ceb3ea6e13b6ad89f3b846e95d5dc3d692e7a9a46dd9c739",
"role": "Atomic process-local projection of persisted custom-function declarations without a copied global catalogue."
},
{
"path": "external/zmqruntime/src/zmqruntime/transport.py",
- "sha256": "6ddaf01cb423565391186fb26e5d0826c6e41aead7dce09ee0d6c8270df0bcd1",
+ "sha256": "e486a4215d4b977553030e897589bccac637dfe4e9abb28cfb7594139b2196c2",
"role": "Owns typed transport endpoints, configured data/control pairs, exact occupancy, proof-gated stale cleanup, and forced local release."
},
{
@@ -2127,37 +2399,25 @@
},
{
"path": "external/zmqruntime/src/zmqruntime/execution/progress_stream.py",
- "sha256": "b2dd8e27f8fcac010beef3f8532fd9d584d7bae8c3f5591dcee0e5a235d6213a",
+ "sha256": "f01e1b98768b288bb921b757f1e51359e7fb2eddb2dbda18f54e4b11bbc4ccea",
"role": "Owns the immutable latest event and monotonic sequence wire observation retained per execution."
},
- {
- "path": "tests/unit/agent/test_agent_services.py",
- "sha256": "0c3a5225c1e832d9fbd5a423ef0cfd495451c0872b6b718d5fd66726f0758fdf",
- "role": "Proves same-client status reuse, progress serialization, terminal caching, and one-time client release."
- },
{
"path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
"role": "Owns the framework child environment consumed by catalogue preparation and server startup."
}
],
- "findings": [
- "The transition page now records generic compatibility ownership, the retained handshake, and the OpenHCS restart projection without introducing a second connection-state authority.",
- "The page did not previously describe the generic terminal cancellation authority or the exact OpenHCS interruption hook.",
- "The transition page lacked the total submission-deadline boundary and the distinction between known pre-dispatch and unknown post-dispatch timeout outcomes.",
- "The page did not record that cold persistent catalogue preparation must run before execution-server import to prevent recursive preparer launch.",
- "The page did not record that optional-backend admission must be proven once and remain stable across cache preparation and later catalogue reads.",
- "The catalogue transition needed the nominal module owner, source-revision cache context, atomic failure, and complete derived-view clearing boundaries.",
- "The transition reference did not record the generic endpoint owner for data/control port-pair projection and exact occupancy.",
- "OpenHCS still selected IPC-versus-TCP cleanup behavior locally; pair cleanup and forced release now descend through the ZMQRuntime transport declaration.",
- "The endpoint-version restart transaction lacked direct failure-path evidence for executor shutdown, saved-session capture, and detached worker launch.",
- "Owner-requested connection-attempt cancellation was not a typed result, so GUI teardown could only surface it as a generic asynchronous failure; ZMQRuntime now owns the explicit cancellation outcome.",
- "The execution reference omitted the shared MemoryType child environment and the public-only native catalogue projection.",
- "The catalogue cache used one physical document for distinct framework-admission contexts, allowing concurrent CPU-only and GPU-capable preparation to replace each other's valid result.",
- "Cancelling a cold catalogue request could leave its isolated capability-discovery child alive; the server-owned preparation now carries one cancellation authority to the exact process and joins it before backend cleanup."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/architecture/zmq_execution_service_extracted.rst in full and compared it against the changed authorities: openhcs/runtime/zmq_execution_server.py, zmq_execution_observation.py, zmq_execution_client.py, openhcs/authoring/session/submission.py, session.py, execution_control.py, openhcs/processing/custom_functions/runtime_registry.py; E1 made the runtime-observation export scope a class family that builds its own export, and execution status, terminal status and custom-function lifetime became families; the page describes the scope as a typed value-or-outcome scope carried by ZMQAuxiliaryExecutionParams and both exports as carrying the execution ID, which still holds; no page change.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/zmq_execution_service_extracted.rst in full and compared it against the changed authorities: runtime/zmq_execution_server.py and zmq_execution_client.py (K6 debug request codecs and replay predicate), external/zmqruntime messages.py (current file: EndpointApplication.compatibility_with, ExecutionRecord, ExecutionStatus), authoring/session/session.py (debug command classes), lib_registry/registry_service.py (contract read from reference.declaration) and test_cellprofiler_processing_backend.py (runtime_bound_parameter_names); verified EndpointConnectionAttempt, EndpointConnectionCancelledError, TransportEndpoint, OperationDeadline, ExecutionSubmission, ZMQAuxiliaryExecutionParams, FunctionCatalogPreparation, --prepare-capabilities, OperationCancellation and execution_subject_id still exist as described; no claim needed correction.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/zmq_execution_service_extracted.rst against B6's later changes to openhcs/authoring/session/session.py: the writability probe makes one exclusive create attempt, the installed demo reports ViewerType.value, the unit session pre-creates its QApplication, and the GUI journey waits for each batch to end. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/zmq_execution_service_extracted.rst against B6's later changes to openhcs/authoring/session/session.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/zmq_execution_service_extracted.rst against B6's later changes to openhcs/runtime/zmq_execution_server.py, openhcs/runtime/zmq_execution_client.py, openhcs/authoring/session/submission.py, openhcs/authoring/session/session.py, openhcs/runtime/zmq_orchestrator_environment.py, tests/unit/test_zmq_submission_timeout.py, openhcs/processing/backends/lib_registry/registry_service.py, openhcs/processing/backends/lib_registry/unified_registry.py, openhcs/processing/backends/lib_registry/openhcs_registry.py, tests/unit/desktop/test_desktop_restart.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/zmq_execution_service_extracted.rst against the later B6 changes to openhcs/runtime/zmq_execution_server.py, external/zmqruntime/src/zmqruntime/transport_modes.py, openhcs/runtime/zmq_execution_client.py, openhcs/authoring/session/submission.py, openhcs/authoring/session/session.py, openhcs/runtime/zmq_orchestrator_environment.py, tests/unit/test_zmq_submission_timeout.py, openhcs/processing/backends/lib_registry/registry_service.py, openhcs/processing/backends/lib_registry/unified_registry.py, openhcs/processing/backends/lib_registry/openhcs_registry.py, tests/unit/test_library_registry_discovery.py, tests/unit/desktop/test_desktop_restart.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/zmq_execution_service_extracted.rst in full and compared it against the changed authorities: zmq_execution_server.py, zmq_execution_observation.py, zmq_execution_client.py, compiled_execution.py, function_catalog_preparation.py, the zmqruntime execution/messages/client/transport/timeout owners, tests/unit/test_zmq_submission_timeout.py and agent/services/execution_session_service.py (deleted, U1); verified every named class and the --prepare-capabilities mode exist, rewrote the headless job, cancellation and execution-subject paragraphs for Session/ExecutionSubmission and subject_id/execution_subject_id, moved the submission budget to ExecutionTransportConfig, and confirmed the compatibility check before submit and the benchmark execution-ID check.",
"Re-read docs/source/architecture/zmq_execution_service_extracted.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: external/zmqruntime/src/zmqruntime/execution/client.py; openhcs/runtime/zmq_execution_server.py; openhcs/core/compiled_execution.py; external/zmqruntime/src/zmqruntime/messages.py; external/zmqruntime/src/zmqruntime/execution/lifecycle.py; external/zmqruntime/tests/test_execution.py; external/zmqruntime/src/zmqruntime/client.py; external/zmqruntime/src/zmqruntime/transport_modes.py; external/zmqruntime/tests/test_transport.py; openhcs/runtime/zmq_application.py; openhcs/runtime/zmq_execution_client.py; external/zmqruntime/src/zmqruntime/timeouts.py; openhcs/agent/services/execution_session_service.py; tests/unit/test_zmq_submission_timeout.py; openhcs/runtime/function_catalog_preparation.py; openhcs/runtime/zmq_execution_server_launcher.py; tests/unit/test_zmq_execution_server_process.py; openhcs/processing/backends/lib_registry/registry_service.py; openhcs/processing/backends/lib_registry/unified_registry.py; openhcs/processing/backends/lib_registry/openhcs_registry.py; tests/unit/test_library_registry_discovery.py; openhcs/pyqt_gui/services/desktop_restart.py; tests/unit/test_zmq_application.py; tests/unit/pyqt_gui/test_desktop_restart.py; openhcs/processing/custom_functions/runtime_registry.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -2200,7 +2460,7 @@
},
{
"path": "docs/source/architecture/zmq_server_browser_system.rst",
- "source_sha256": "2ceb609f8b720a6862577d5fb1e18658d1040bc7e7507c03642a3b8471a9fa6e",
+ "source_sha256": "aaf89e33f9fae47a31e0ea5e55cc466d1b9f91159d90209d1eda9e5e5f8a01dc",
"audience": [
"OpenHCS server-browser and progress UI maintainers"
],
@@ -2214,7 +2474,7 @@
},
{
"path": "openhcs/pyqt_gui/widgets/shared/server_browser/progress_projection.py",
- "sha256": "4b9c4bd90fbe6ed4ac55f5fe1d23cf2a46c3a7f743218d685df8ac2f27be7998",
+ "sha256": "d49ef50d9bebca37c25abd5d603ddce77680fe21757c2bbd3b02533042c11bb9",
"role": "Browser-facing typed execution progress projection."
},
{
@@ -2229,17 +2489,17 @@
},
{
"path": "openhcs/pyqt_gui/widgets/shared/server_browser/live_tree_sync.py",
- "sha256": "81dbee5b7f471082bcfeb8ca63e753840ad7134aff7101fd14f1c821a121d1bb",
+ "sha256": "dfb301b3e3e465e40205c4f3cf29eb7b47333751f70cc6d26a3838c5eea848c0",
"role": "Renders responsive, startup, and launching-viewer rows from current lifecycle snapshots without retaining parallel endpoint state."
},
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
"role": "Projects the configured endpoint snapshot into its dedicated right-hand indicator, and routes endpoint termination to the client lifecycle owner."
},
{
"path": "tests/unit/pyqt_gui/test_main_zmq_window_routing.py",
- "sha256": "d02fe132fda83540f26ed4b7a79fc1409fabbf0ff6ce72f3d1ed8681258e9f6a",
+ "sha256": "8568dcd39ed44cc1e065c0d7389b9f92dacc80e984f13ca2944d102baf679027",
"role": "Proves startup callbacks do not write presentation directly, endpoint snapshots leave workflow messages untouched, and endpoint termination descends to the client lifecycle owner."
},
{
@@ -2253,23 +2513,30 @@
"role": "Owns typed multi-endpoint shutdown outcomes and generic progress-tracker retirement."
},
{
- "path": "openhcs/pyqt_gui/widgets/shared/services/zmq_client_service.py",
- "sha256": "8bb3b33ee64869a225e7ec23be3dcf4f5bf3622dd801f7e77bdd7826980cbd8e",
- "role": "Owns the persistent OpenHCS execution-client lifecycle and retires its exact session when that endpoint terminates."
+ "path": "openhcs/authoring/session/execution_client.py",
+ "sha256": "f56f23fc860dab554584cb5fe44d29332c73f7bb7a650c167702efeb986363b7",
+ "role": "ZMQClientService: the session-owned execution client, its EndpointClientSession, and retirement when its exact endpoint terminates."
},
{
"path": "external/zmqruntime/src/zmqruntime/client.py",
- "sha256": "fc8bdf68455673921d79742204bf6c88e42b52bbe407d10ef4a831e084ca86a8",
+ "sha256": "7d3cce31d5abaa070927f02fe87d919687619e2cf4adbc37ab82483b6b8fc212",
"role": "Declares EndpointClientSession as the authoritative transport-client and application-admission proof."
+ },
+ {
+ "path": "openhcs/core/progress/runtime_tree.py",
+ "sha256": "75599ae64cf21ebbc2496c0a77b2e3b40b465f867cebc290066fc65c4513dff3",
+ "role": "RuntimeExecutionTopology, RuntimeTreeProjectionBuilder and the subject/worker/partition/step/compilation node kinds with mean or explicit percent policies."
}
],
- "findings": [
- "The component inventory named a deleted ServerTreePopulation class and described responsibilities now split between LiveServerTreeSync and ServerRowPresenter.",
- "The reference treated the browser only as an execution-progress consumer and omitted its generic endpoint-snapshot authority and the OpenHCS status projections derived from it.",
- "The launching-viewer projection retained stale row payloads and rows after the authoritative viewer lifecycle no longer contained the endpoint."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/architecture/zmq_server_browser_system.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/zmq_server_browser_system.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py; the diff swaps selection and update-check enum members for class families and touches no endpoint-snapshot, status-bar or server-browser code; the listed module paths exist and every named class was grepped; no page change.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/zmq_server_browser_system.rst in full and compared it against the changed authorities: pyqt_gui/main.py (shortcut, dock and time-travel machinery moved to pyqt-reactive; endpoint indicator projection unchanged) and core/progress/runtime_tree.py (K6 ProgressStream classes replace channel helpers); verified every named class (ZMQServerBrowserWidgetABC, EndpointObservationSnapshot, RuntimeExecutionTopology, RuntimeTreeProjectionBuilder, ProgressTreeBuilder, ExecutionProgressProjection, ServerRowPresenter, LiveServerTreeSync, EndpointShutdownService, ZMQClientService, ProgressUIConfig, EndpointClientSession), the primary module paths, and mean/explicit percent node policies on subject/worker vs partition/step/compilation nodes; no claim needed correction.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/zmq_server_browser_system.rst against B6's later changes to openhcs/pyqt_gui/main.py, openhcs/core/progress/runtime_tree.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/architecture/zmq_server_browser_system.rst against the later B6 changes to openhcs/pyqt_gui/main.py, openhcs/core/progress/runtime_tree.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/architecture/zmq_server_browser_system.rst in full and compared it against the changed authorities: server_browser/progress_projection.py, server_browser/live_tree_sync.py, pyqt_gui/main.py, tests/unit/pyqt_gui/test_main_zmq_window_routing.py, zmqruntime client.py and services/zmq_client_service.py (moved, U1); verified every named class and module path exists, corrected the progress subscriber owner and the subject/worker/partition/step tree path and aggregation kinds from core/progress/runtime_tree.py, and confirmed the main window still projects the endpoint snapshot into its indicator and manager button states.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"The complete pyqt-reactive suite passed 320 tests, including shared non-overlapping browser and log-viewer scan lifecycle coverage.",
"tests/unit/pyqt_gui/test_progress_tree_aggregation.py passed; tests/unit/test_configuration_mirror_deletion.py proves the obsolete server_tree_population.py remains absent",
@@ -2297,45 +2564,49 @@
"authority": [
{
"path": "openhcs/mcp/client_registration.py",
- "sha256": "28459b95d3b155778ce178f36302cb830958b74f2603980641ec492d81c41a9a",
- "role": "Nominal MCP client registration target registry."
+ "sha256": "4d6fc5f744617ab1d0e9b8317d07965496aaa6b098a4d179ef95cc6baec35671",
+ "role": "Declares McpClientRegistrationTarget.__registry__ keyed by target_id."
},
{
"path": "openhcs/agent/services/synthetic_plate_service.py",
- "sha256": "2860714a9f1f2e66df403bc72331195d912cb180600889a1b4f4207642cc776b",
- "role": "Production synthetic-plate request service delegating deterministic fixture generation to the installed demo package."
+ "sha256": "eb9e9ef693a877d65280004f8f85f1b6d6ab9be1c51aa3d2a0e1ed96630faa2d",
+ "role": "Production synthetic-plate service delegating generation to openhcs.demo.synthetic_data."
},
{
- "path": "website/assets/agent/cold-start-workflow-record.json",
- "sha256": "52d126027b39a05cd5e799e4aca28e45fd8edd251c7b523204c33c14fa7de65c",
- "role": "Retained baseline schema, client and model identity, operation mode, trace hashes, evidence, and acceptance-gate results."
+ "path": "openhcs/demo/synthetic_data.py",
+ "sha256": "be80973ecd87b4b3b36b7a7283d962a5d7cafd491cfa16eafe4622e09391463a",
+ "role": "Installed synthetic-plate generator; writes a time-derived ImageXpress UniquePlateIdentifier."
},
{
- "path": "tests/unit/test_installed_mcp_demo.py",
- "sha256": "f5d4a719a7902b364b551a3cbc4a816ccd7f46e5d9abbc87bf2d53cb0180053b",
- "role": "Proves the installed MCP workflow, progress-aware inactivity watchdog, hard ceiling, and terminal completion."
+ "path": "openhcs/mcp/installed_demo.py",
+ "sha256": "f89ba094072c46c1ed5504c7b4e786aa3806776615330f4e32f2b07f19555100",
+ "role": "Declares the deterministic fixture request (1x1 grid, 192 px tiles, 10% overlap, 1 px stage error, 2 wavelengths, 1 Z, 12 cells, 0.95 shared, A01, ImageXpress, seed 1)."
},
{
- "path": "openhcs/demo/synthetic_data.py",
- "sha256": "7a8f4a79e716ad944d5ceb1ed14e62cab9211aee65b1c0dc571e097e0f4d3c09",
- "role": "Installed deterministic synthetic-plate implementation used by the production service and MCP demo."
+ "path": "website/assets/agent/cold-start-workflow-record.json",
+ "sha256": "52d126027b39a05cd5e799e4aca28e45fd8edd251c7b523204c33c14fa7de65c",
+ "role": "Retained baseline record with the schema groups, nullable registration_target_id and operation mode."
},
{
- "path": "openhcs/mcp/installed_demo.py",
- "sha256": "4ea6c0730db6d3938eea1cac558dd46ea5c956ef151a5d68b8ea956e8bf57655",
- "role": "Implements the installed workflow rehearsal and derives activity from exact execution progress observations."
+ "path": "tests/unit/test_installed_mcp_demo.py",
+ "sha256": "8babc443f7ec59cebebd3772633dde9ae9c49b125066cd5e7c7d527dc74c2a09",
+ "role": "Proves the installed demo runs the session journey and validates settled viewer state."
},
{
"path": "tests/unit/test_synthetic_generator_randomness.py",
"sha256": "6ab3efd7f4e083744dc4dda26d97c60b6a7649dd44432362020027b92271717f",
- "role": "Proves seeded synthetic image equivalence and isolated randomness across interleaved and concurrent generation."
+ "role": "Proves seeded synthetic image equivalence and isolated randomness."
}
],
- "findings": [
- "The production synthetic service now delegates to the installed openhcs.demo package rather than an importable tests package."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/agent_workflow_validation.rst in full and compared it against the changed authorities: openhcs/mcp/client_registration.py (E1: ClientRegistrationStatus and the runtime platform became class families and the report is projected with to_jsonable; McpClientRegistrationTarget.__registry__ is still keyed by target_id) and openhcs/mcp/installed_demo.py (compares the terminal status with ExecutionCompleted.name; the deterministic fixture request is unchanged); the fixture table, registry rule and evidence schema still hold.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/agent_workflow_validation.rst against B6's later changes to openhcs/mcp/installed_demo.py: the writability probe makes one exclusive create attempt, the installed demo reports ViewerType.value, the unit session pre-creates its QApplication, and the GUI journey waits for each batch to end. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/agent_workflow_validation.rst against B6's later changes to openhcs/mcp/installed_demo.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/agent_workflow_validation.rst against B6's later changes to tests/unit/test_installed_mcp_demo.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/agent_workflow_validation.rst against the later B6 changes to tests/unit/test_installed_mcp_demo.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/agent_workflow_validation.rst in full and compared it against the changed authorities: openhcs/mcp/client_registration.py, synthetic_plate_service.py, tests/unit/test_installed_mcp_demo.py, openhcs/demo/synthetic_data.py, openhcs/mcp/installed_demo.py; the client skill sync and session-based execute path do not touch the protocol, and the fixture request values, the registry the page names, the time-derived HTD identifier, the ZMQ Server Manager and Live Results windows and the retained baseline record all still match; updated the test's role because its old progress-polling watchdog tests were replaced by the session journey.",
"Re-read docs/source/development/agent_workflow_validation.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/mcp/client_registration.py; openhcs/agent/services/synthetic_plate_service.py; website/assets/agent/cold-start-workflow-record.json; tests/unit/test_installed_mcp_demo.py; openhcs/demo/synthetic_data.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -2350,7 +2621,7 @@
},
{
"path": "docs/source/development/ast_refactoring_workflow.rst",
- "source_sha256": "79299114e82cb13d549d24dce90abc381cd249e2c2d4e10c116d2086b73e4686",
+ "source_sha256": "723e0d6cc10e795822010d36c73319567e263ecb31a8ba5264368996a15fdf25",
"audience": [
"OpenHCS documentation and source maintainers"
],
@@ -2359,20 +2630,17 @@
"authority": [
{
"path": "scripts/validate_docs.py",
- "sha256": "f048e8c882ad701ae7f238cda02526292e82c0ee317e2f817e75d75db3b9e882",
- "role": "Executable documentation and audit validation contract."
- },
- {
- "path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compilation order, declaration resolution, callable warm-up, framework-local device assignment, and bundle construction."
+ "sha256": "d48be3462998b8277bff6f7e9eea8785e01d937a6d1a300127772c680a66f094",
+ "role": "Parses RST and Markdown Python blocks with ast and checks imports, literalincludes and repository paths, the validation step 6 names."
}
],
"findings": [
- "The integrated setup procedure was duplicated in docs/development_setup.md; the Markdown entry now points to this canonical how-to and unique optional-extra and packaging guidance was consolidated here."
+ "Step 6 began with a lowercase \"run\"; capitalised to match the other steps.",
+ "openhcs/core/pipeline/compiler.py supports no claim on this page (it only refers generically to the compiler/declaration authority) and was dropped from the authorities."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/ast_refactoring_workflow.rst in full and compared it against the changed authorities: scripts/validate_docs.py (repository-path pattern now also matches .jsonl and rejects trailing identifier characters) and openhcs/core/pipeline/compiler.py; neither change affects the workflow, the validate_docs.py step still holds, and the compiler authority was dropped as irrelevant.",
"Re-read docs/source/development/ast_refactoring_workflow.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: scripts/validate_docs.py; openhcs/core/pipeline/compiler.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -2383,7 +2651,7 @@
},
{
"path": "docs/source/development/callable_artifact_authoring.rst",
- "source_sha256": "60006087b96c5e1c84063923e457fabbd54a040db4bad4e9e96c2fc6bfeb62b3",
+ "source_sha256": "6b216b639ff38eeebcf8bd72e40e75fe8508d445689fbd55f0dbfcfb7e6b869f",
"audience": [
"OpenHCS callable and artifact extension authors"
],
@@ -2392,47 +2660,81 @@
"authority": [
{
"path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
- "role": "Callable ABI including input/output/execution memory roles, runtime-owned parameter projection, and artifact contracts."
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "Declares CallableDeclaration (inherited by CallableContract) with required_axis_roles, allowed_group_by_roles, memory roles and prepared signatures, processing_prepare, resolve_returned_output, the return partition and the artifact-parameter binding errors quoted by the page."
},
{
- "path": "openhcs/core/artifacts.py",
- "sha256": "bb958c74c98cb96c97c5ac1ea68ac997673aac6afcad57bdf0c9496065e895fa",
- "role": "Artifact type, specification, reference, relation, and plan declarations."
+ "path": "openhcs/core/artifacts/specs.py",
+ "sha256": "c4c50309a195bf85e1c0641413544d9581fc0ce1197043130126f7a4c70d8c4e",
+ "role": "Declares ArtifactSpec with its input() and output() constructors and MainFlowStackOutputSpec."
},
{
- "path": "openhcs/interop/cellprofiler/module_artifact_contracts.py",
- "sha256": "bf2e8f6c66a181218c8fd9afa9132922e86295f65d6102d976e1b36e41e17ee5",
- "role": "Invocation-specific CellProfiler artifact contract derivation."
+ "path": "openhcs/core/artifacts/kinds.py",
+ "sha256": "216b913efad9ec76707e9f61be445fb6fa1c6601c36ef53e296acd4928604515",
+ "role": "Declares the ArtifactType members used in the examples (image, object labels, measurements, special) and runtime_parameter_types()."
},
{
- "path": "tests/unit/test_artifact_spec_collection.py",
- "sha256": "09d66f7b67e2b281c911ba935d6a1b3ff2c790da3977184256302d24b7a6d548",
- "role": "Regression proof for role-neutral artifact specs, exact refs, relations, and collection validation."
+ "path": "openhcs/core/artifacts/relations.py",
+ "sha256": "b1ae8be613fbabc372bd6692b41c5f925d4f968291f0a715bd1473d85cbed000",
+ "role": "Declares ObjectMeasurementSubjectRelation and MaterializationSourceIdentityRelation."
},
{
- "path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
- "role": "Memory role taxonomy and framework execution-device scopes."
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "Declares Pure2DContract and Pure3DContract used in the examples."
+ },
+ {
+ "path": "openhcs/core/pipeline/function_contracts.py",
+ "sha256": "67a654102b1d60c0a0f90e64ccaf3a8c177035cd11a0346734f872b329783d74",
+ "role": "Declares artifact_inputs, artifact_outputs, special_inputs (which writes the artifact-fed parameter names), execution_scope and runtime_bound_parameters."
+ },
+ {
+ "path": "openhcs/core/runtime_stores.py",
+ "sha256": "35fe71906bfd16842de347776a0c5cc846550ad490cc3628885129d746ec6f31",
+ "role": "Declares RuntimeArtifactBatch.records and StoredRuntimeValue for plate-scope callables."
+ },
+ {
+ "path": "openhcs/core/orchestrator/bundle_execution.py",
+ "sha256": "25446d6c6a43c22ff7104c6712c143f0276bc4749c2642fffac10db1455ef707",
+ "role": "Requires a plate-scope callable to declare exactly one SpecialArtifactType output."
+ },
+ {
+ "path": "openhcs/processing/materialization/core.py",
+ "sha256": "79db4b008483dfb5ac752f3a5da692a941cba7caae25a6ec26085bbdb4bf278f",
+ "role": "Point ROI writer: rejects missing fields, duplicate labels, non-finite coordinates and an empty archive."
+ },
+ {
+ "path": "openhcs/interop/cellprofiler/module_artifact_contracts.py",
+ "sha256": "cefc83a740f917730f336b7283c6ea2d9280d27bf077e030d41b7a8a9add41af",
+ "role": "Declares CellProfilerModuleArtifactContracts resolving setting-backed specs into the invocation CallableContract."
},
{
"path": "external/arraybridge/src/arraybridge/decorators.py",
- "sha256": "99823105b0ae59054df1d84013633b8b6628d0a6dc3326383199ac7c65c69bed",
- "role": "Decorators that declare input, output, and execution framework roles."
+ "sha256": "28f9bf4770bd4cc1ef34045fdef28065063ede029296f62b8a6c4140cc88e40e",
+ "role": "Declares the input, output and execution framework roles written by the memory decorators."
+ },
+ {
+ "path": "tests/unit/test_artifact_spec_collection.py",
+ "sha256": "46b3367620866438509896fc33bf196f262078f84a6b976612f104ce228148e4",
+ "role": "Proves role-neutral artifact specs, exact refs, relations and collection validation."
},
{
"path": "tests/unit/test_callable_contract.py",
- "sha256": "493f76a54f5fe024ca8f10c53b1f810b8c369626ad7141205b42ef3a78282137",
+ "sha256": "9170f8694f388dbed5bfd36d882a5c0a4cdf28f50f51f7fdeaf4a955a9f3b412",
"role": "Proves exact enum validation and contract-derived runtime-owned parameter exclusions."
}
],
"findings": [
- "The callable ABI guide omitted the execution-memory role and its distinction from input/output conversion boundaries.",
- "The callable-authoring how-to did not state that an enum-annotated public kwarg requires a member of the exact declared enum.",
- "The how-to did not state that contract-owned runtime parameters project into the shared authoring exclusion boundary."
+ "K2 replaced the module openhcs/core/artifacts.py with the package openhcs/core/artifacts/. The four code blocks that imported from the old module were repointed by K2 to openhcs.core.artifacts.kinds, .specs and .relations after the last review, and the audit record still named the deleted module as an authority; the record now names specs.py, kinds.py and relations.py."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/callable_artifact_authoring.rst in full and compared it against the changed authorities: openhcs/core/artifacts.py (deleted by K2; replaced as authority by openhcs/core/artifacts/specs.py, kinds.py and relations.py), openhcs/core/callable_contract.py, openhcs/core/pipeline/function_contracts.py, openhcs/core/runtime_stores.py, openhcs/core/orchestrator/bundle_execution.py, openhcs/processing/materialization/core.py, openhcs/interop/cellprofiler/module_artifact_contracts.py and tests/unit/test_artifact_spec_collection.py; executed all six code blocks in order against this tree (imports from the artifacts package modules, the direct-call assertions, the points, diagnostic, input and plate declarations) and they pass; confirmed the quoted 'does not accept object_labels artifact payloads', 'no exact artifact declaration' and 'trailing return count' messages, canonical_return_output_specs, trailing_return_output_specs, runtime_parameter_types and the plate rule of exactly one SpecialArtifactType output still exist. No prose edit was needed in this round.",
+ "On 2026-10-11 (B6), re-read docs/source/development/callable_artifact_authoring.rst in full and compared it against the changed authorities: callable_contract.py (K5 CallableDeclaration, prepare removed, repeated_return_slots), processing_contracts.py, function_contracts.py (special_inputs now writes artifact_input_parameter_names), runtime_stores.py (K6 codec removal), bundle_execution.py (progress classes, lane results in compiled axis order), module_artifact_contracts.py (UniqueReturnSlots) and tests/unit/test_callable_contract.py; corrected the preparation-hook claim, confirmed by a direct call that special_inputs('labels') alone still fails with 'has no exact artifact declaration binding', and re-checked canonical/trailing_return_output_specs, RuntimeArtifactBatch.records, StoredRuntimeValue and the single SpecialArtifactType plate output.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/callable_artifact_authoring.rst against B6's later changes to openhcs/core/pipeline/function_contracts.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/callable_artifact_authoring.rst against B6's later changes to openhcs/core/callable_contract.py, openhcs/core/artifacts.py, openhcs/core/processing_contracts.py, openhcs/core/pipeline/function_contracts.py, openhcs/core/runtime_stores.py, openhcs/core/orchestrator/bundle_execution.py, openhcs/processing/materialization/core.py, openhcs/interop/cellprofiler/module_artifact_contracts.py, tests/unit/test_callable_contract.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/callable_artifact_authoring.rst against the later B6 changes to openhcs/core/callable_contract.py, openhcs/core/artifacts.py, openhcs/core/processing_contracts.py, openhcs/core/pipeline/function_contracts.py, openhcs/core/runtime_stores.py, openhcs/core/orchestrator/bundle_execution.py, openhcs/processing/materialization/core.py, openhcs/interop/cellprofiler/module_artifact_contracts.py, tests/unit/test_callable_contract.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/callable_artifact_authoring.rst in full and compared it against the changed authorities: the page, openhcs/core/callable_contract.py, openhcs/core/artifacts.py, module_artifact_contracts.py, tests/unit/test_artifact_spec_collection.py, the ArrayBridge types and decorators, tests/unit/test_callable_contract.py; grepped every API the page names (SettingToKeywordBinding, SelectedPlaneImageOutput, canonical/trailing return specs, RuntimeArtifactBatch.records, PointROIOptions features, the custom-function capability and guide names) and the quoted error texts, confirmed the plate executor's single SpecialArtifactType rule and the empty-Points rejection, corrected the axis-role and processing-contract names, and added processing_contracts.py, function_contracts.py, runtime_stores.py, bundle_execution.py and materialization/core.py as owners while dropping arraybridge/types.py.",
"Re-read docs/source/development/callable_artifact_authoring.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/callable_contract.py; openhcs/core/artifacts.py; openhcs/interop/cellprofiler/module_artifact_contracts.py; tests/unit/test_artifact_spec_collection.py; external/arraybridge/src/arraybridge/types.py; external/arraybridge/src/arraybridge/decorators.py; tests/unit/test_callable_contract.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -2458,22 +2760,22 @@
"authority": [
{
"path": "openhcs/interop/cellprofiler/module_declarations.py",
- "sha256": "89dd65abc003c464ba6c489f52d3a4fa47be1d5f8b87d26ad72b7537f188cf8b",
+ "sha256": "6ff4f4bc3eaf6b4c9fc42752493c2b9ba2e11c8994d7edaea81f586ac57c3b89",
"role": "CellProfiler module registry, catalogue placement, and headless-import eligibility."
},
{
"path": "openhcs/interop/cellprofiler/module_artifact_contracts.py",
- "sha256": "bf2e8f6c66a181218c8fd9afa9132922e86295f65d6102d976e1b36e41e17ee5",
+ "sha256": "cefc83a740f917730f336b7283c6ea2d9280d27bf077e030d41b7a8a9add41af",
"role": "Invocation-specific CellProfiler artifact contract derivation."
},
{
"path": "openhcs/interop/cellprofiler/pipeline_import.py",
- "sha256": "b547c838b2c86fe7cc5fe12d6cbf6a6715e83055c2d8c1a109ac22566d22c51e",
+ "sha256": "5aa8895c9f533603b7ef972202141e2439473f0604b97d66f5a027b8b3cd1c83",
"role": "Public CellProfiler pipeline lowering and declaration-owned headless-import validation."
},
{
"path": "tests/unit/test_cellprofiler_pipeline_import.py",
- "sha256": "353f0c1d8e0bc3552d2e418d792794ab0631daa16c78a3e10191b1cc1ea07d73",
+ "sha256": "67576f37b7cbe0595acea633265f8e14677ca3c1b8204630413330273dc4e58a",
"role": "Proves interactive CellProfiler modules fail headless import instead of disappearing from the lowered pipeline."
},
{
@@ -2483,7 +2785,7 @@
},
{
"path": "openhcs/processing/backends/lib_registry/openhcs_registry.py",
- "sha256": "aa3079341cb2af8788938e14efee6f9f6c4b6c01874227e74c6340850d525884",
+ "sha256": "2a1de7f8af9ade2cba450cbbcc1abd6510a8e3edb58217c6c08fa067bde21de2",
"role": "Derives the public native callable catalogue while retaining explicit reconstruction from each callable declaration."
},
{
@@ -2492,14 +2794,14 @@
"role": "Lazy package projection derived from CellProfilerModule declared function names."
}
],
- "findings": [
- "CellProfilerModule now also owns callable catalogue placement; the backend package lazily projects its declared names.",
- "The how-to did not explicitly require active output identity keywords to survive public source transport instead of observation-driven pruning.",
- "The catalogue-authoring workflow did not state that only the public declared union is browsable.",
- "The module-authoring workflow omitted declaration-owned headless-import validation."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/cellprofiler_module_authoring.rst in full and compared it against the changed authorities: openhcs/interop/cellprofiler/module_declarations.py and pipeline_import.py (E1: the main-flow anchor is an InputSource class, ReadPreviousStep or ReadPipelineStart), module_artifact_contracts.py (K2: artifact_type.value in derived names and messages) and tests/unit/test_cellprofiler_pipeline_import.py (same renames); for_callable_contract, declared_function_names, validate_pipeline_import, module_blocks_for_invocation and SettingToKeywordBinding.input/output still exist as the page describes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/cellprofiler_module_authoring.rst in full and compared it against the changed authorities: module_declarations.py, module_artifact_contracts.py, pipeline_import.py and tests/unit/test_cellprofiler_pipeline_import.py (K4 moved SourceBindingsConfig/StepSourceBindingsConfig imports to openhcs.core.config and replaced SourceBindingMatchMethod.ORDER with PairByOrder; K5 put artifact_inputs/outputs directly on CallableContract and added repeated_return_slots through UniqueReturnSlots); the page names only SourceBindingsConfig, CellProfilerModule hooks, CellProfilerModuleArtifactContracts, SettingToKeywordBinding.input()/output(), module_blocks_for_invocation() and ModuleBlock, all re-checked in the current tree, and its claim that outputs are declared independently of return position still holds.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/cellprofiler_module_authoring.rst against B6's later changes to openhcs/interop/cellprofiler/module_declarations.py, openhcs/interop/cellprofiler/module_artifact_contracts.py, openhcs/interop/cellprofiler/pipeline_import.py, openhcs/processing/backends/lib_registry/openhcs_registry.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/cellprofiler_module_authoring.rst against the later B6 changes to openhcs/interop/cellprofiler/module_declarations.py, openhcs/interop/cellprofiler/module_artifact_contracts.py, openhcs/interop/cellprofiler/pipeline_import.py, openhcs/processing/backends/lib_registry/openhcs_registry.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/cellprofiler_module_authoring.rst in full and compared it against the changed authorities: module_declarations.py (axis-family imports, MeasurementDialect, lazy registry preparation, private _require_declared_callable with object-identity check, prepare_declared_callable), module_artifact_contracts.py (target_units_for_invocation replaces target_contract_for_invocation), pipeline_import.py, tests/unit/test_cellprofiler_pipeline_import.py and openhcs_registry.py (LazyDiscoveryDict declaration discovery, declared_callable_contract). Confirmed every name the page relies on still exists at its owner: AutoRegisterMeta, CellProfilerModule.__registry__, for_callable_contract (import identity then object identity), declared_function_names and the lazy CellProfilerBackendModule projection, validate_pipeline_import called by pipeline_import before lowering, CellProfilerModuleArtifactContracts, SettingToKeywordBinding.input/output, module_blocks_for_invocation, SourceBindingsConfig, ModuleBlock and CallableContract; no page change was needed.",
"Re-read docs/source/development/cellprofiler_module_authoring.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/interop/cellprofiler/module_declarations.py; openhcs/interop/cellprofiler/module_artifact_contracts.py; openhcs/interop/cellprofiler/pipeline_import.py; tests/unit/test_cellprofiler_pipeline_import.py; tests/unit/test_cellprofiler_public_boundary_contracts.py; openhcs/processing/backends/lib_registry/openhcs_registry.py; openhcs/processing/backends/cellprofiler/__init__.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -2513,7 +2815,7 @@
},
{
"path": "docs/source/development/compiler_extension.rst",
- "source_sha256": "4bb46f10ebfdab22de8f3f5b833e7f74b78d4d846ee50c671296e4aad33953ff",
+ "source_sha256": "f6374ec2abb6d45312729e9ac0f26e8e7ef562119c99bdd779091059d3a56a8b",
"audience": [
"OpenHCS compiler extension authors"
],
@@ -2522,37 +2824,37 @@
"authority": [
{
"path": "openhcs/core/pipeline/compilation_session.py",
- "sha256": "3c840a89baccc8665d415175458280fd904c1a837b7fd879e303d7dbca129a1c",
- "role": "Resolved step/session identity and typed plan assembly invariants."
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "ResolvedPipelineDefinition and CompilationSession fields, one-time artifact admission before axis fan-out, and the missing-plan failure of plan(index)."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
"role": "Compilation order, declaration resolution, callable warm-up, framework-local device assignment, and bundle construction."
},
{
"path": "openhcs/core/pipeline/path_planner.py",
- "sha256": "0ed8bb8d843ff41382f4de25367a74c9535a39425c6fc328091ad3025d526151",
+ "sha256": "cf31311ffa462d0cbd8eb8cf426d75d5ee2f5668de7b7291ca6787b5d5be118a",
"role": "Compiled source satisfaction, artifact edge, path, grouping, and plan decisions."
},
{
"path": "tests/unit/test_compilation_session.py",
- "sha256": "4224c87a6b67e4d8dde2a5c6b786ea7998474819de56da2b9d954aa2b479feb4",
- "role": "Regression proof for one-to-one step, state, snapshot, context, and plan identity."
+ "sha256": "f7d5483997bf604f2c4fccaf08c165ae1a7c881079a250add3c98ce02250e2c0",
+ "role": "Regression proof that axis sessions share one resolved pipeline definition while owning their own plans."
},
{
"path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
"role": "Framework declarations, device discovery, and device-scope ownership."
},
{
"path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
"role": "Typed per-step framework-device binding authority."
},
{
"path": "openhcs/core/pipeline/framework_device_assignment.py",
- "sha256": "f77f3fb2d30c4af8cab6210dbe294f9fb5e246dad0a6bbf88bcaf1e2b58dead7",
+ "sha256": "cdfde10f996ab682952a1d4948d3de2d23d48353eae41f3124770e19fc28a193",
"role": "Compiler projection from declared framework roles to per-step bindings."
},
{
@@ -2566,11 +2868,14 @@
"role": "Regression proof for independent framework namespaces and exact selection."
}
],
- "findings": [
- "Framework extensions needed an explicit declaration-to-CompiledStepPlan path after deletion of the global GPU scheduler and validator."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/compiler_extension.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compilation_session.py (K2 import only), openhcs/core/pipeline/compiler.py (E1 class families), openhcs/core/pipeline/path_planner.py and openhcs/core/compiled_step_plan.py (E1: StepInputDependency is a class family, UnresolvedStepInput/PipelineStartInput/StepOutputInput, whose members answer the directory defaults) and tests/unit/test_compilation_session.py (same renames); the session invariants, plan(index) failure and exact ArtifactSpecRef planning are unchanged, and the page names no dependency-kind member.",
+ "On 2026-10-11 (B6), re-read docs/source/development/compiler_extension.rst in full and compared it against the changed authorities: compilation_session.py (disabled items dropped by normalisation; invocation declarations validated by the artifact graph), compiler.py (ScopeKey scope ids, progress phase/status classes, an empty CompiledSourceBindingPlan instead of None), path_planner.py (inject_metadata over the ArtifactGraph, source_bindings.compiled_plan), compiled_step_plan.py (empty plan defaults) and tests/unit/test_compilation_session.py (PairByOrder, admitted_graph); the page's ResolvedPipelineDefinition/CompilationSession invariants, plan(index) failure, typed-plan guidance and ArtifactSpecRef identity still hold.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/compiler_extension.rst against B6's later changes to openhcs/core/pipeline/compilation_session.py, openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/path_planner.py, tests/unit/test_compilation_session.py, openhcs/core/compiled_step_plan.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/compiler_extension.rst against the later B6 changes to openhcs/core/pipeline/compilation_session.py, openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/path_planner.py, tests/unit/test_compilation_session.py, openhcs/core/compiled_step_plan.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/compiler_extension.rst in full and compared it against the changed authorities: compilation_session.py (StepSnapshot and snapshot validation deleted; ResolvedPipelineDefinition and CompilationDatasetScope added), compiler.py, path_planner.py, tests/unit/test_compilation_session.py, arraybridge types.py, compiled_step_plan.py and framework_device_assignment.py. Rewrote the Invariants section for the current session model; confirmed CompiledStepPlan, ArtifactSpecRef, assign_framework_devices, FrameworkDeviceResolver and the absence of a global GPU scheduler.",
"Re-read docs/source/development/compiler_extension.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline/compilation_session.py; openhcs/core/pipeline/compiler.py; openhcs/core/pipeline/path_planner.py; tests/unit/test_compilation_session.py; external/arraybridge/src/arraybridge/types.py; openhcs/core/compiled_step_plan.py; openhcs/core/pipeline/framework_device_assignment.py; openhcs/core/framework_device_resolver.py; tests/unit/test_framework_device_resolver.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -2592,27 +2897,22 @@
"authority": [
{
"path": "openhcs/agent/services/object_state_field_help_service.py",
- "sha256": "d1f130b63e7b36aaee23b6333048de777b21fb40b7ecd85dcc7687c79dc74ce6",
+ "sha256": "5f8bbcd9ebc9d4361a7907900ce1df578ec489fe4ccc8907ea4fdb0f987158da",
"role": "Declaration-reflected ObjectState field help and configuration schema projection."
},
{
"path": "openhcs/agent/services/knowledge_base_service.py",
- "sha256": "88e10e18c66eb3a8bc2ec5dd33493a9473510c0b0a9d355502d668042f32fdd9",
+ "sha256": "dfc26135b16f3827ced85b122cc1555c1325da1c5e805b421afcee9cdd7d91f3",
"role": "Source-backed knowledge catalogue, search, section reads, and lazy Official30 projection."
},
{
"path": "scripts/validate_docs.py",
- "sha256": "f048e8c882ad701ae7f238cda02526292e82c0ee317e2f817e75d75db3b9e882",
- "role": "Executable documentation and audit validation contract."
- },
- {
- "path": "docs/audits/README.md",
- "sha256": "aeb08ec4e4a0e8cc7dd6e0be9d0de9c1dc159793f5643e91aa6c543cac145c4b",
- "role": "Audit-ledger schema and evidence rules only; it is not used as product-fact authority."
+ "sha256": "d48be3462998b8277bff6f7e9eea8785e01d937a6d1a300127772c680a66f094",
+ "role": "Executable documentation and audit validation contract, including the project.readme entry and digest gate."
},
{
"path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
"role": "Owns nominal gallery scenario identity, publication targets, representative images, copy, accessibility text, dimensions, contextual and stable-window capture targets, and derivative contracts."
},
{
@@ -2622,16 +2922,15 @@
},
{
"path": "tests/unit/test_build_website.py",
- "sha256": "4a8952ba0f51590ffd9f1d787986653802506807079a7e0d118343264252e0e0",
+ "sha256": "e88fc8ae73c5610dfb0a9948271a016e666451228ecade92aa1b2892da106059",
"role": "Proves every scenario owns a valid representative static projection, stable-window figures derive from registered UI identities, documentation-only scenarios do not expand the website cards, and Sphinx branding resolves from package authority."
}
],
- "findings": [
- "The guide did not state that material RST changes must refresh the page-level audit record and source digest.",
- "The root README becomes the package-index long description but was outside both the documented review workflow and the executable audit gate."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/documentation_authoring.rst in full and compared it against the changed authorities: openhcs/agent/services/object_state_field_help_service.py (E1: the parameter supplier is a class family, SuppliedByAgent/SuppliedAtRuntime, replacing FunctionParameterSource); field help is still reflected from the declarations, and the four verification test files and openhcs_describe_config_schema still exist.",
+ "On 2026-10-11 (B6), re-read docs/source/development/documentation_authoring.rst in full and compared it against the changed authorities: object_state_field_help_service.py (UI-bridge scope listing through UiBridgeService.invoke; JsonValue from python_introspect), knowledge_base_service.py (comparison-manifest parsing moved to openhcs.agent.knowledge_manifest_schema), scripts/validate_docs.py (path regex now accepts .jsonl), scripts/gallery_catalog.py (SessionOperation actions) and tests/unit/test_build_website.py. Confirmed openhcs_describe_config_schema, the openhcs-gallery directive, the napari-roi-navigation scenario, the documentation publication target, the project.readme gate, reference/configuration.rst and all four listed test files exist; no page claim changed. Dropped the docs/audits/README.md authority as a documentation file.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"Config-help and knowledge-service tests provide the listed projection gates; the audit validator enforces exact active-source coverage and SHA-256 freshness.",
"On 2026-08-24, re-read the complete guide after deriving the package-index README from project.readme and requiring it in the audit; 10 focused audit regressions passed.",
@@ -2645,7 +2944,7 @@
},
{
"path": "docs/source/development/extension_workflows.rst",
- "source_sha256": "02e13f4611803605d430da15b4a1fa875a16a708b4b167fa0efc19f03df672eb",
+ "source_sha256": "8fc4f9ad9a36c88d51a8e41285e7e8ccb5a6af7ec0c349c41bf1295f0f0cbcac",
"audience": [
"OpenHCS extension authors"
],
@@ -2653,13 +2952,13 @@
"diataxis": "how-to",
"authority": [
{
- "path": "openhcs/core/registry_strategies.py",
- "sha256": "a41186665abbd308f224d0bb41d6618cd9d89b08acaf54ec2819f2024a6e0804",
- "role": "OpenHCS enum, nominal-type, and most-derived strategy selection implementations."
+ "path": "external/metaclass-registry/src/metaclass_registry/strategies.py",
+ "sha256": "5c00cd4d80f9f0ceffbc2ab95ad8c6abd44a8a66a974959445ef93df08dd4f0d",
+ "role": "Enum-keyed, nominal-type, and most-derived-context strategy family mixins used when behaviour varies by key or context."
},
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
"role": "Current Python support, dependency, package discovery, and entry-point authority."
},
{
@@ -2668,59 +2967,61 @@
"role": "Orders supplied parameters by signature with optional default omission and caller-owned equality."
},
{
- "path": "openhcs/core/pipeline/compilation_session.py",
- "sha256": "3c840a89baccc8665d415175458280fd904c1a837b7fd879e303d7dbca129a1c",
- "role": "Resolved step/session identity and typed plan assembly invariants."
+ "path": "external/python-introspect/src/python_introspect/jsonable.py",
+ "sha256": "c845361d0d14914798d9a1b5329d681300511fc7e95c67508943425716930ebd",
+ "role": "singledispatch to_jsonable projection for dataclasses, mappings, sequences, callables, registered types, enums, paths and scalars, paired with dataclass_from_mapping."
},
{
- "path": "openhcs/serialization/json.py",
- "sha256": "fa1e2aeae1e5d6896944d1516c17db0ac486157b335d7be06fcb553ab3203f1f",
- "role": "Bounded JSON singledispatch projection for callables, registered types, dataclasses, paths, enums, and collections."
+ "path": "openhcs/core/pipeline/compilation_session.py",
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "ResolvedPipelineDefinition and CompilationSession consumed by compiler phases."
},
{
"path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
"role": "Declares framework capabilities, callable memory metadata, and the child-process import environment owned by each MemoryType member."
},
{
"path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
"role": "OpenHCS callable declaration projection for input, output, and execution frameworks."
},
{
"path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
"role": "Typed per-step framework-device binding authority."
},
{
"path": "openhcs/core/pipeline/framework_device_assignment.py",
- "sha256": "f77f3fb2d30c4af8cab6210dbe294f9fb5e246dad0a6bbf88bcaf1e2b58dead7",
+ "sha256": "cdfde10f996ab682952a1d4948d3de2d23d48353eae41f3124770e19fc28a193",
"role": "Compiler projection from declared framework roles to per-step bindings."
},
{
"path": "benchmark/cellprofiler_benchmark_cli.py",
- "sha256": "2b92755c556c1d66d28ffa0af6e2ac61cac5af4631514382290a2af0f907c26b",
+ "sha256": "56a9b4e8bb7d8195095eb99a849f717e79ca37aa429c612804584cbd7939f420",
"role": "Owns registered benchmark command declarations and the derived installed parser."
},
{
"path": "benchmark/contracts/run_receipt.py",
- "sha256": "1704e1ab47411d25d382b95871c96c1e60c94db14b8ffd55f4dc6c8a917758b9",
+ "sha256": "4cd61bd33f8f6199b3cc4130ed091dddde9c3a23975e132b1bee06c3c6a43cd7",
"role": "Owns the common comparison-run declaration and typed persisted lifecycle receipt."
},
{
"path": "tests/unit/test_benchmark_control.py",
- "sha256": "ee75209799216c90cfa3a585dd9b7af99c99405f93e9fb5e4f900ea26d9d3ea0",
+ "sha256": "093b584eeb37cbb12089d5af082ad45acd779e9d2bdd5b3feaea08234fc22183",
"role": "Proves registered CLI derivation and declaration-derived receipt construction."
}
],
- "findings": [
- "The owner map lacked ArrayBridge framework/device discovery and OpenHCS per-step binding compilation.",
- "The extension map omitted python-introspect's ownership of generic callable-declaration projection.",
- "The extension map did not assign child framework import environments to ArrayBridge MemoryType declarations.",
- "The owner map omitted the registered benchmark command family and the shared comparison-run declaration/receipt boundary."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/development/extension_workflows.rst against benchmark/cellprofiler_benchmark_cli.py: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/extension_workflows.rst in full and compared it against the changed authorities: pyproject.toml (foundation pins raised; same eight distributions), external/python-introspect/src/python_introspect/jsonable.py (read in full at 0.2.8: singledispatch to_jsonable still registers dataclasses, mappings, sequences, callables, AutoRegisterMeta classes, enums, paths and scalars, paired with dataclass_from_mapping), openhcs/core/pipeline/compilation_session.py and openhcs/core/callable_contract.py (K2 imports), openhcs/core/compiled_step_plan.py (E1 StepInputDependency classes) and benchmark/cellprofiler_benchmark_cli.py (E1: observation scope choices come from the ZMQRuntimeObservationExportScope registry; BenchmarkCliCommand unchanged); every owner named in the table still exists and metaclass-registry still ships the enum-, type- and context-keyed strategy mixins.",
+ "On 2026-10-11 (B6), re-read docs/source/development/extension_workflows.rst in full and compared it against the changed authorities: pyproject.toml (first-party floors only), external/python-introspect/src/python_introspect/jsonable.py (current file: singledispatch registrations for dataclasses, mappings, sequences, callables, AutoRegisterMeta families, enums, paths and scalars, plus registered_class_from_key), openhcs/core/pipeline/compilation_session.py (validation moved onto ArtifactGraph), openhcs/core/callable_contract.py (K5: CallableMetadata folded into CallableContract declared fields; input/output/execution memory types remain) and openhcs/core/compiled_step_plan.py (CompiledSourceBindingPlan() default). Rechecked every class the owner table and prose names (CallableContract, CellProfilerModule, ImageFileFormat, ResolvedPipelineDefinition, CompilationSession, CompiledExecutionBundle, CompiledStepPlan device bindings, BenchmarkCliCommand, ComparisonSuiteRunDeclaration/Receipt, MeasuredPipelineRunReceipt) and that pycodify still owns Python source generation after L6 (ObjectState adds only the lazy-config formatter); no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/extension_workflows.rst against B6's later changes to benchmark/cellprofiler_benchmark_cli.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/extension_workflows.rst against B6's later changes to openhcs/core/pipeline/compilation_session.py, openhcs/core/callable_contract.py, openhcs/core/compiled_step_plan.py, benchmark/cellprofiler_benchmark_cli.py, tests/unit/test_benchmark_control.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/extension_workflows.rst against the later B6 changes to pyproject.toml, openhcs/core/pipeline/compilation_session.py, openhcs/core/callable_contract.py, openhcs/core/compiled_step_plan.py, benchmark/cellprofiler_benchmark_cli.py, tests/unit/test_benchmark_control.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/extension_workflows.rst in full and compared it against the changed authorities: deleted openhcs/core/registry_strategies.py (strategy mixins moved to metaclass-registry, b221f8491), deleted openhcs/serialization/json.py (python-introspect codec, c0a53a1e0), compilation_session.py (StepSnapshot removed), arraybridge types.py, callable_contract.py, compiled_step_plan.py, framework_device_assignment.py, the benchmark CLI and run-receipt modules, and pyproject.toml. Corrected the StepSnapshot reference and the to_jsonable owner; confirmed ImageFileFormat, BenchmarkCliCommand, ComparisonSuiteRunDeclaration, ComparisonSuiteRunReceipt, MeasuredPipelineRunReceipt, ArtifactKey, CompiledExecutionBundle and MemoryType.import_environment exist at their owners.",
"Re-read docs/source/development/extension_workflows.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/registry_strategies.py; pyproject.toml; openhcs/core/pipeline/compilation_session.py; openhcs/serialization/json.py; external/arraybridge/src/arraybridge/types.py; openhcs/core/callable_contract.py; openhcs/core/compiled_step_plan.py; openhcs/core/pipeline/framework_device_assignment.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -2749,7 +3050,7 @@
},
{
"path": "docs/source/development/git_worktree_testing.rst",
- "source_sha256": "5a2487f59d2befe94a05219b3a382f2c60e2c773054cb90f8ac2bfdde0ab9e63",
+ "source_sha256": "53191366b967995617ea27cc7bb1a44dcd095f57826effd69ff6d3513b5a7e79",
"audience": [
"OpenHCS contributors comparing or testing branches"
],
@@ -2763,20 +3064,29 @@
},
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
"role": "Current Python support, dependency, package discovery, and entry-point authority."
},
{
"path": "tests/pytest_integration_options.py",
"sha256": "de36c47ead5e45020fcd6f72cf5ab20bab195962d22ab8e69cde604a324a9ab8",
"role": "Executable authority for accepted integration-test option names and values."
+ },
+ {
+ "path": "openhcs/utils/environment.py",
+ "sha256": "1985ffbe42b5dfb283b78b6282f9b56e08cd51d5a9e79f2dd4eebffcb6da0bf5",
+ "role": "OpenHCSProcessEnvironment.flag_enabled, which tests/conftest.py uses to read OPENHCS_CPU_ONLY (1, true, yes or on) before deciding whether to load pytest-qt."
}
],
- "findings": [
- "The old guide recommended shared editable environments, incorrectly claimed tracked test data was shared, duplicated the integration option catalogue, and suggested broad process/temp deletion plus manual lock-file removal."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/git_worktree_testing.rst in full and compared it against the changed authorities: pyproject.toml (foundation pins raised; the dev and gui extras used by the install command still exist) and openhcs/utils/environment.py (AgentRuntimePlatformAuthority renamed AgentRuntimePlatform in the Numba cache helper; flag_enabled still accepts 1, true, yes or on); the install order, OPENHCS_CPU_ONLY usage and --it-* option names are unchanged.",
+ "On 2026-10-11 (B6), re-read docs/source/development/git_worktree_testing.rst in full and compared it against the changed authorities: pyproject.toml (first-party floors moved to zmqruntime 0.6.2, pycodify 0.2, objectstate 2.1.1, python-introspect 0.2.7, pyqt-reactive 0.5); the page states no version floors. Rechecked the eight editable installs against .gitmodules, the dev and gui extras, and the integration options file; corrected the stale conftest role (OPENHCS_CPU_ONLY parsing now lives in OpenHCSProcessEnvironment).",
+ "On 2026-10-11 (B6 final), compared docs/source/development/git_worktree_testing.rst against B6's later changes to tests/conftest.py: the writability probe makes one exclusive create attempt, the installed demo reports ViewerType.value, the unit session pre-creates its QApplication, and the GUI journey waits for each batch to end. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/git_worktree_testing.rst against B6's later changes to tests/conftest.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/git_worktree_testing.rst against the later B6 changes to pyproject.toml: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/git_worktree_testing.rst in full and compared it against the changed authorities: pyproject.toml (first-party floors moved to zmqruntime 0.6, objectstate 1.3.1, python-introspect 0.2.5, metaclass-registry 0.3, arraybridge 0.4.1, polystore 0.6, pyqt-reactive 0.4; JAX extra now jax[cuda12-local]; openhcs-basicpy added); the page states no version floors. Checked the eight editable installs against .gitmodules, the dev and gui extras, tests/integration/test_main.py, and every --it-* option and value against tests/pytest_integration_options.py; corrected OPENHCS_CPU_ONLY=1 to OPENHCS_CPU_ONLY=true to match tests/conftest.py.",
"Re-read docs/source/development/git_worktree_testing.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: .gitmodules; pyproject.toml; tests/pytest_integration_options.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -2811,23 +3121,22 @@
"authority": [
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
"role": "Current Python support, dependency, package discovery, and entry-point authority."
},
{
- "path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
- "role": "CompiledExecutionBundle and runtime-environment authority; execution-wide worker facts remain separate from per-step framework devices."
- },
- {
- "path": "docs/source/development/index.rst",
- "sha256": "fd425732b5660df4f8f110174f6e773f070160f73828bb84f7dd641965184fdd",
- "role": "Navigation or authoring-policy evidence only; product claims are also grounded in non-documentation authorities."
+ "path": ".gitmodules",
+ "sha256": "b70034bf0b9661f2528f002e08c03042d6cece29de79bd96c11d2d5a943684bd",
+ "role": "Recorded first-party submodule set behind the recursive-clone and install-extracted-packages-first instruction."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/index.rst in full and compared it against the changed authorities: pyproject.toml (read the current file: foundation pins raised to zmqruntime 0.6.2, objectstate 2.1.1, python-introspect 0.2.8 and polystore 0.6.1, mahotas kept in the core dependencies); the page gives no version and every toctree target still exists.",
+ "On 2026-10-11 (B6), re-read docs/source/development/index.rst in full and compared it against the changed authorities: pyproject.toml (first-party dependency floors only); the page states no versions. Confirmed every toctree target and related-architecture link exists and the recursive-clone guidance matches .gitmodules; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/index.rst against the later B6 changes to pyproject.toml: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/index.rst in full and compared it against the changed authorities: pyproject.toml (first-party dependency floors, JAX extra, openhcs-basicpy) and openhcs/core/compiled_execution.py; the page states neither versions nor compiled-bundle facts. Confirmed every toctree target exists and the recursive-clone guidance matches .gitmodules; dropped the unrelated compiled_execution.py and self-referential page authorities.",
"Re-read docs/source/development/index.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: pyproject.toml; openhcs/core/compiled_execution.py; docs/source/development/index.rst.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -2864,12 +3173,12 @@
"authority": [
{
"path": "scripts/capture_media_gallery.py",
- "sha256": "987f0b3fe09f98fff0e7878053867311af7b65004569bd75937f627464e063b6",
+ "sha256": "f4808f79d960a5cfcdb40edd37780120189f1c21e833e51f5a4878af36fec9f1",
"role": "Owns the typed capture manifest, host-tool discovery, trusted UI-bridge session, declaration-selected contextual fixture preparation, X11 capture, deterministic derivative construction, validation, and command-line surface."
},
{
"path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
"role": "Owns the nominal gallery scenario lattice, stable-window and contextual capture targets, publication targets, derivative inventory, source evidence, card rendering, and generated release record."
},
{
@@ -2879,7 +3188,7 @@
},
{
"path": "tests/unit/test_capture_media_gallery.py",
- "sha256": "aabfe3defb657b72150caaa3737f06352056e8580060633999a4296818ee8dcd",
+ "sha256": "c1ee8fe70c63a4308c8c4b38b2733e73c4e9c7719695c305b723e7892c8dc04b",
"role": "Proves manifest bounds, path containment, no-overwrite rules, typed UI-bridge snapshot ingestion, contextual target and scope projection, flash-lifecycle waiting, X11 capture commands, deterministic FFmpeg projection, atomic builds, and independent derivative validation."
},
{
@@ -2888,12 +3197,12 @@
"role": "Consumes the same scenario declarations to produce individual RTD workflow figures and the complete stable-window reference without creating a documentation-only media registry."
}
],
- "findings": [
- "The active capture workflow lived outside docs/source, so Sphinx navigation and the documentation drift ledger did not own it.",
- "The original title and opening described a tool rather than leading a competent maintainer toward the publication goal."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/development/media_gallery_capture.rst against B6's later changes to scripts/capture_media_gallery.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/media_gallery_capture.rst against the later B6 changes to scripts/capture_media_gallery.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/media_gallery_capture.rst in full and compared it against the changed authorities: scripts/capture_media_gallery.py (UI-bridge calls now go through UiBridgeService.invoke with typed operation classes, to_jsonable moved to python_introspect), scripts/gallery_catalog.py (contextual actions are SessionOperation classes such as ShowDatasetImages and ShowLiveResults instead of PlateManagerAction values; StepEditorScope moved to openhcs.authoring.session.step_scopes) and tests/unit/test_capture_media_gallery.py (native-frame snapshot requests). None of these internals are named by the page; re-checked every subcommand and option (doctor, catalog, plan, build, validate, capture-still, capture-scenario-still, capture-ui-window-references, capture-ui-context-references with --fixture-root, record-window with --no-mouse), manifest schema_version 2, the 30-second trim limit, the documentation-fixture preparation path, the UI-bridge environment variables, the multi-plate-overview and lazy-inheritance scenario ids, and gallery_catalog --check; all remain accurate.",
"Re-read the complete how-to against scripts/capture_media_gallery.py, scripts/gallery_catalog.py, scripts/website_gallery_projection.py, and tests/unit/test_capture_media_gallery.py; every capture, transformation, ownership, validation, and no-overwrite claim matches its current owner.",
"Passed python -m scripts.capture_media_gallery catalog and verified the declaration-owned scenario inventory; python -m scripts.gallery_catalog --check confirmed the checked-in release projection.",
"Passed 42 focused capture and website tests, including a real FFmpeg derivative build and independent revalidation.",
@@ -2908,7 +3217,7 @@
},
{
"path": "docs/source/development/mcp_development.rst",
- "source_sha256": "829194108df1e11d2b8aaae97da3a0ce7cfd6c5a2842e58ef3ab3ad305a88de7",
+ "source_sha256": "f0c6b83bb855c6455138e2ade37b4afbc30bd2a71e0a50f549f6cdad1ea89893",
"audience": [
"OpenHCS MCP developers"
],
@@ -2917,149 +3226,147 @@
"authority": [
{
"path": "openhcs/mcp/dev_client.py",
- "sha256": "cb91fcaf3040ff9bc41d6145b71aac8249b42b0369ce8547531cccf7f76dcb3a",
- "role": "Current development-client parser, persistent shell entry point, and persistent-session selected-surface preflight."
+ "sha256": "c40f72cb9ba7f62b574d3f9e7b96470b5a78db60c87f6a4a99897449ae875714",
+ "role": "Current development-client parser, persistent shell entry point with --command and --stop-on-error, and the --surface option."
},
{
"path": "openhcs/mcp/dev_client_commanding.py",
- "sha256": "e1c987624c76be157a6524cda985498351da3c77ef19c99b85e05dd5c2e9b970",
+ "sha256": "abb1d9a82c4f36e171db61c0cf11eefa06fde10cc9b65a1ca031be138d46c234",
"role": "One-shot command orchestration and selected-surface preflight before subprocess construction."
},
{
"path": "openhcs/mcp/dev_client_core.py",
- "sha256": "6b05721536e92ed0f9ce4fdd0b6ebd4dbc75201277d1df1d625bf87456d5a57e",
- "role": "MCP dev-client session, declaration-derived command surface validation, receipt decoding, skip-reason-owned summary status, timeout, and workflow-owned terminal-state criteria."
+ "sha256": "1bb405ee541cdb48b27c355aeb71ad680ec8c14f9fd31089814e7ac01751b89b",
+ "role": "MCP dev-client session, selected-workflow argument projection, revision-token terminal-state criteria with idempotent initialization, and transport timeout."
},
{
"path": "openhcs/runtime/zmq_config.py",
- "sha256": "842b25ffd542f99923dafb2f67d23bac0263f38d85e64ef1bdbaba3da671b792",
- "role": "Declares the execution-submission deadline separately from short execution control deadlines."
+ "sha256": "7c96c39d684d450ae045d1de1186dd27c5d67e042996857d5cdfcda6c2d4d069",
+ "role": "OpenHCSZMQConfig, the OpenHCS execution transport configuration named by the page."
+ },
+ {
+ "path": "external/zmqruntime/src/zmqruntime/execution/config.py",
+ "sha256": "a3f3ba1f16fbedeb282825fbc381d4cc788fa8ac1600fd50fa1a27b5c65421db",
+ "role": "Declares execution_submission_timeout_ms separately from the shorter control_timeout_ms."
},
{
"path": "openhcs/mcp/dev_client_commands/knowledge_pipeline.py",
- "sha256": "91f04c09fbd3d3b83d962c221bee45b8f897a5cab236559abf964303a5fdb5cd",
+ "sha256": "dc64de05b47e7ce538431b67d55319b723ba637f54033ec6675395d7c1e61a3c",
"role": "Projects the declaration-owned execution submission deadline into the execute-source development command."
},
{
"path": "openhcs/mcp/control_timeout.py",
"sha256": "da74437a45c2c7bd3e249b202717c7adfd178433f667d60327fa513db233d0ee",
- "role": "Projects the UI-bridge and viewer declarations into bounded development-client control timeouts."
+ "role": "Projects the UI-bridge and viewer declarations into bounded development-client control timeouts whose default equals their maximum."
},
{
- "path": "openhcs/agent/dto/ui_bridge.py",
- "sha256": "585b1701b01b92bfce91437a60881afa769e36de9126807c3f4fcaf0ffc006fb",
- "role": "Declares the default UI bridge connection timeout."
+ "path": "openhcs/agent/dto/viewer.py",
+ "sha256": "24435279b0d31bfd62ea8c29c07cbadc564a3427fd2b06c5e4abf20992f27f12",
+ "role": "Declares the five-second default viewer control timeout and the layer-isolation request."
},
{
- "path": "openhcs/agent/dto/viewer.py",
- "sha256": "293ad9fcbd7570e74170e65f9e9d0c51eb78ead57f41213a2878ad6a95d055e3",
- "role": "Declares the default viewer control timeout."
+ "path": "openhcs/agent/services/ui_bridge_service.py",
+ "sha256": "a2d682ad7841a7d85173f792961cae2dc5546fb4fcf39caa85f0bbd6ed062b01",
+ "role": "Declares the default UI bridge connection spec whose timeout bounds UI development commands."
},
{
"path": "openhcs/mcp/dev_client_commands/ui.py",
- "sha256": "6c1142ce723a5d28ef14726b10868e11ede4cd043b7ea40a622fcdcc1eecc8a4",
+ "sha256": "e5eec92b43c20d91f83681e505afa94ca733e809b1c732f81952757b345d3cff",
"role": "Owns declaration-derived UI development commands, including selected-workflow ordering and the typed output-directory projection for window snapshots."
},
{
"path": "openhcs/mcp/dev_client_commands/viewer.py",
- "sha256": "9d854b05a76d67dc2ba4c4f0873a77b95630269f60d4564f4630ab1e4e3e572e",
+ "sha256": "b5e6551624d81a27507efe0553bafa4c20e6a533d00fd14146fb01b7a024e3de",
"role": "Declares the development-client viewer state, payload, ROI-summary, navigation, and isolation command surfaces."
},
+ {
+ "path": "openhcs/agent/capabilities.py",
+ "sha256": "87c8e2e5aeb425b9a2eb690d48bdd6f5af7a327c389deac261d7e4e0da5d609f",
+ "role": "Projects each SessionOperation as an openhcs_ tool and declares openhcs_session_events, openhcs_session_datasets and the local surface profiles."
+ },
+ {
+ "path": "openhcs/authoring/session/session.py",
+ "sha256": "ced615b580e4625e09873449b29ac67cdea87c8b8065c205f32e6d5921cf31fc",
+ "role": "Session submission budget and runtime-observation export validation (writable root, no existing path, outcomes require a path)."
+ },
+ {
+ "path": "openhcs/agent/dto/session.py",
+ "sha256": "5a261a14d3cd2880ecc14c2fc83f6e8664655799789f91a79e81f1fd40a8656d",
+ "role": "Typed run request fields runtime_observation_export_path, runtime_observation_export_scope and submit_timeout_ms, and event_sequence results."
+ },
{
"path": "tests/unit/agent/test_mcp_server.py",
- "sha256": "b99b6ca27be471f95ddf0852f8f94ce02797a459bf98bf6e0fa4851c0bb8c698",
+ "sha256": "7f47844b6fc0bf2461dfdb97e4b622a8d16f0f8c00004c1e6b7ea1a9997abe82",
"role": "Regression proof for selected-surface preflight, stale-row rejection, idempotent initialization, and every declared or malformed bridge-receipt continuation outcome."
},
{
"path": "openhcs/mcp/bootstrap.py",
- "sha256": "64b2ef9a4f4fe50154b4c921a7617b2e7c9dcf8bb475df7423e37e8395a8b250",
- "role": "Installed stdio bootstrap, surface selection, and stale-process boundary."
+ "sha256": "0e8e94cf54a32a1772e52a77d1c89a9ee02c6d2a58eb04f6e18fad2ce79d0394",
+ "role": "Installed stdio bootstrap, surface selection, OPENHCS_MCP_VERBOSE, and stale-process boundary."
},
{
"path": "openhcs/mcp/stdio.py",
- "sha256": "4a5299915413293e5c2492d4e3889614231a1286f93a976094e4d2eba77ea9c0",
+ "sha256": "ec2935e01e9dc7c0ea56781c174f490528002bd736ac19062bd3a848f7397e7e",
"role": "Reserves protocol stdin and stdout before server construction and owns process-lifetime application I/O isolation."
},
{
"path": "tests/unit/agent/test_mcp_stdio_transport.py",
- "sha256": "4f7c439168dacc350d7fc31618f10dfd7ce7478d52bd5ba13ed54e720291aff2",
+ "sha256": "ffad185f736a7ead7b6e2e7699fb3c9b034c24534af14ea8848e0eb6989cfe1a",
"role": "Exercises persistent sessions after application output, cold native imports after handshake, and process-handle restoration."
},
{
"path": "tests/unit/test_mcp_dev_client_persistent_session.py",
- "sha256": "043281c5b330e99fdb6e7941fd66655ee956256378b3676a393a11a44e61e65d",
+ "sha256": "5d3b517601b8a32b53a8519b65ee03fd1a7b51b1fb6e18203a58b50bd28f064f",
"role": "Regression proof for persistent dev-client parsing, reuse, errors, and stop-on-error semantics."
},
{
"path": "openhcs/runtime/zmq_execution_client.py",
- "sha256": "31081f2a323a81c060aede1201a4bf1a1b39a51f37928c281a419ffaceadfe60",
+ "sha256": "ad7ea01599493dbefce1f3c91e6b082e932c6a5183d0a85a186f7bd7b62779a9",
"role": "Owns the total headless submission budget and the pre-dispatch timeout boundary documented for MCP developers."
},
- {
- "path": "openhcs/agent/services/execution_session_service.py",
- "sha256": "1a9a70d2924b1854338612b212f00888bacba5cd33eb23608be294bb06c76399",
- "role": "Retains the submitting execution client, distinguishes dispatch outcomes, and projects its exact progress observation."
- },
{
"path": "tests/unit/test_zmq_submission_timeout.py",
- "sha256": "b525a1cc113bdae987c7f202369cc251ca1129188bd8d3adb834aec44f4c8393",
+ "sha256": "acea5dcb72c820ed8c1f225dc1960dfd615ad3f002ef6aec8e78b75bf8e50b3e",
"role": "Regression proof that deadline expiry after slow connection prevents execute dispatch."
},
{
"path": "openhcs/agent/services/ui_bridge_transport.py",
- "sha256": "32e759baf65b34989c99223d8fa9e64800ab566cd05160df2bc125b04dea5953",
+ "sha256": "1c2fb99aef62969cfabb9d22ccb954e8894ee72fd2e66e625304ffdb96a41b95",
"role": "Rejects response envelopes from an incompatible desktop application or bridge protocol."
},
{
"path": "openhcs/pyqt_gui/services/ui_bridge_server.py",
- "sha256": "667f582fffd4b489ca7274f8962a759c3d33d24b10ec8e6d1aa5cac01233b067",
+ "sha256": "f93a3570a834eca8596139676629dda48c3f22991fd8cb58cd03427af8b24a38",
"role": "Rejects stale MCP request contracts before they reach live UI state."
},
{
"path": "tests/unit/pyqt_gui/test_ui_agent_bridge.py",
- "sha256": "1feee4bb413738f04e81cbdf26244dc0d585a68921e6c7749a941a08518007a1",
+ "sha256": "422be9298e5120c898bd7110884ca770f1f033f07fd69be4ac0caa978d3014f4",
"role": "Proves stale protocol and application versions fail before UI operation dispatch."
},
- {
- "path": "external/zmqruntime/src/zmqruntime/execution/progress_stream.py",
- "sha256": "b2dd8e27f8fcac010beef3f8532fd9d584d7bae8c3f5591dcee0e5a235d6213a",
- "role": "Owns the immutable event and monotonic sequence wire observation described for asynchronous jobs."
- },
- {
- "path": "openhcs/agent/dto/execution.py",
- "sha256": "1fbdc008937e3a043cf077e2243b40f6a0efba34d9166716c423af9b85445684",
- "role": "Declares the public typed execution status and progress observation result."
- },
- {
- "path": "tests/unit/agent/test_agent_services.py",
- "sha256": "0c3a5225c1e832d9fbd5a423ef0cfd495451c0872b6b718d5fd66726f0758fdf",
- "role": "Proves exact progress projection from the submitting client and one-time release at terminal status."
- },
{
"path": "openhcs/mcp/server.py",
- "sha256": "ed65a008b52db505393413f0fbadf6b096c648ff48340c7e2dda8584750f6c68",
- "role": "Owns declaration-derived progress execution and verbose delayed thread-stack diagnostics across the full invocation."
+ "sha256": "c3dd3a396a3c304341b06c7c813036e779c921fa915060d2d3d2ed594b00b697",
+ "role": "Owns the mcp_server_stale guard, declaration-derived progress execution, and verbose delayed thread-stack diagnostics across the full invocation."
},
{
- "path": "openhcs/pyqt_gui/config.py",
- "sha256": "a3eece2e5b2926fc6fc0edd197221da451ab7c0d0d6f4ffc8bb2ff865ad74bd4",
- "role": "Owns the isolated UI configuration cache override forwarded to the development MCP child."
+ "path": "openhcs/agent/ui_bridge_environment.py",
+ "sha256": "30f459623ed48c6d653725080453630ef9813f875dd8e63d171147f5e73caacf",
+ "role": "Declares OPENHCS_UI_BRIDGE_DESCRIPTOR, OPENHCS_UI_BRIDGE_DESCRIPTOR_DIR and the UIConfigCacheEnvironment override forwarded to the development MCP child."
}
],
- "findings": [
- "The development guide described bounded UI and viewer calls without identifying their connection declarations as the timeout authority or stating the five-second bound.",
- "The development workflow did not explain that submit_timeout_ms spans cold startup or distinguish a known pre-dispatch timeout from an unknown post-dispatch outcome.",
- "The workflow did not explain how to recover when a client-owned MCP process and desktop bridge were launched from incompatible OpenHCS environments.",
- "The selected-workflow waiter could accept a terminal Plate Manager row from an earlier operation without proving dispatch receipt or a state revision.",
- "Reusing a live UI exposed that idempotent initialization can complete without changing the Plate Manager revision; the workflow criterion now owns that exception while compile and run retain the stale-row guard.",
- "Capability-local stdout guards left delayed imports and background native output able to corrupt the persistent JSON-RPC stream; process stdout is now reserved once by the stdio transport.",
- "The command examples omitted the bounded ROI-summary and atomic viewer-isolation probes needed to validate current viewer agent workflows.",
- "The how-to did not state that the development client validates commands against the selected declaration-derived surface before starting or dispatching; the selected-profile workflow now fails early with compatible registered profiles.",
- "The UI window-snapshot command could not select the request DTO's declared output directory even though the underlying capability and viewer command supported it; the CLI now projects that field through the shared request-option authority.",
- "The guide called --timeout-seconds a generic client timeout even though standard progress renews it; it now distinguishes transport inactivity from operation-owned maximum duration."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/agent/dto/session.py changed by black formatting only (AST-equal) after docs/source/development/mcp_development.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/mcp_development.rst in full and compared it against the changed authorities: openhcs/mcp/dev_client_core.py, openhcs/mcp/dev_client_commands/ui.py, openhcs/agent/services/ui_bridge_service.py, openhcs/agent/dto/session.py, openhcs/authoring/session/session.py, openhcs/mcp/server.py, openhcs/runtime/zmq_execution_client.py, openhcs/pyqt_gui/services/ui_bridge_server.py and the two test modules (E1: poll status, orchestrator state, field filter, selection scope and export scope became class families); the page names no enum member; the selected-workflow wait policy, the session-operation tool names, the submit_timeout_ms budget, and the runtime-observation export checks (writable root, no existing path, `values`/`outcomes` spellings, scope.check_export_path before submission) still hold in the current files, so nothing was changed.",
+ "On 2026-10-11 (B6), re-read docs/source/development/mcp_development.rst in full and compared it against the changed authorities: openhcs/authoring/session/session.py, openhcs/runtime/zmq_execution_client.py and tests/unit/pyqt_gui/test_ui_agent_bridge.py (K6: debug commands, boundaries and progress phases/statuses became class families; debug control requests send request.to_dict() directly; U3: DockPane and ConfigCodeDocumentDriver moved to pyqt-reactive). The page names none of those types; rechecked the openhcs-mcp-dev entry point, every dev-client command and option it shows, the SessionOperation operation_ids behind the six named tools, openhcs_session_events/openhcs_session_datasets, execution_submission_timeout_ms on OpenHCSZMQConfig's zmqruntime base, UIConfigCacheEnvironment, the bridge/cache environment variables and OPENHCS_MCP_VERBOSE; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_development.rst against B6's later changes to openhcs/authoring/session/session.py: the writability probe makes one exclusive create attempt, the installed demo reports ViewerType.value, the unit session pre-creates its QApplication, and the GUI journey waits for each batch to end. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_development.rst against B6's later changes to openhcs/agent/dto/session.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_development.rst against B6's later changes to openhcs/authoring/session/session.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_development.rst against B6's later changes to openhcs/mcp/dev_client_core.py, openhcs/agent/services/ui_bridge_service.py, openhcs/mcp/dev_client_commands/ui.py, openhcs/authoring/session/session.py, tests/unit/agent/test_mcp_server.py, openhcs/runtime/zmq_execution_client.py, tests/unit/test_zmq_submission_timeout.py, tests/unit/pyqt_gui/test_ui_agent_bridge.py, openhcs/mcp/server.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_development.rst against the later B6 changes to openhcs/mcp/dev_client_core.py, openhcs/agent/services/ui_bridge_service.py, openhcs/mcp/dev_client_commands/ui.py, openhcs/agent/capabilities.py, openhcs/authoring/session/session.py, tests/unit/agent/test_mcp_server.py, openhcs/runtime/zmq_execution_client.py, tests/unit/test_zmq_submission_timeout.py, tests/unit/pyqt_gui/test_ui_agent_bridge.py, openhcs/mcp/server.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/mcp_development.rst in full and compared it against the changed authorities: the page's own U1 update (session-operation tools replacing the job-status paragraph; selected-workflow initialize_datasets), dev_client.py, dev_client_commanding.py, dev_client_core.py, zmq_config.py, knowledge_pipeline.py, agent/dto/ui_bridge.py, agent/dto/viewer.py, dev_client_commands/ui.py and viewer.py, bootstrap.py, stdio.py, zmq_execution_client.py, ui_bridge_transport.py, ui_bridge_server.py, progress_stream.py, agent/dto/execution.py, server.py, the deleted execution_session_service.py and their tests. Confirmed every dev-client command and option in the examples, the full/desktop/core/authoring surface names, initialize_datasets as a UiSelectedPlateWorkflowKind value, the revision-token and idempotent-initialization wait criteria, the 5000 ms UI and viewer control bounds, the 30000 ms submission default and pre-dispatch timeout error, the openhcs_ session tools, event_sequence, and the runtime-observation export checks; replaced the deleted and no-longer-relevant authorities.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"Source search confirmed every documented dev-client command; persistent-session and MCP-server parser tests cover the command shapes",
"Focused submission tests proved that slow startup cannot dispatch an execute request after the caller's budget expires",
@@ -3112,28 +3419,38 @@
"authority": [
{
"path": "openhcs/agent/services/knowledge_base_service.py",
- "sha256": "88e10e18c66eb3a8bc2ec5dd33493a9473510c0b0a9d355502d668042f32fdd9",
- "role": "Source-backed knowledge catalogue, search, section reads, and lazy Official30 projection."
+ "sha256": "dfc26135b16f3827ced85b122cc1555c1325da1c5e805b421afcee9cdd7d91f3",
+ "role": "Source-backed knowledge catalogue, search, section reads, the 12,000-character default bound, and on-demand uncached Official30 conversion through import_cellprofiler_pipeline."
},
{
"path": "openhcs/agent/knowledge_manifest.py",
- "sha256": "2136e24d452774f8ae92a2644940da547152bb7e2d995a9fdb476ba4bcea0107",
- "role": "Canonical allowlist mapping document identities to source-backed knowledge content."
+ "sha256": "2689e052a31a075475e48bcc288a5ce6ec43417a607486f67b892f794f61ff44",
+ "role": "Resolves the knowledge manifest and Python sources from the selected checkout or the packaged resources, and exposes manifest source paths to the MCP stale-source watchlist."
+ },
+ {
+ "path": "openhcs/agent/capabilities.py",
+ "sha256": "87c8e2e5aeb425b9a2eb690d48bdd6f5af7a327c389deac261d7e4e0da5d609f",
+ "role": "Declares the openhcs://knowledge resource and the three knowledge tools with their knowledge, knowledge-document, and knowledge-search dev-client commands."
},
{
"path": "tests/unit/agent/test_knowledge_base_service.py",
- "sha256": "7b4ab158ced3ec3355d314d04e9baf0c8a4e8b2e60ce84aa56b2723c44f563c1",
- "role": "Regression proof for canonical document IDs, search, bounded reads, and section selection."
+ "sha256": "7b30d1c1931efc1c380b2dacd743bcd4e509cafc949b01d24e156448681a0fc3",
+ "role": "Regression proof for document IDs, search, bounded reads, and section selection."
},
{
"path": "tests/unit/agent/test_official30_knowledge_examples.py",
- "sha256": "6b81450dc01a33370fdad7a9a3b88bc87c3f85eddcd8b872373da9a89172c7a4",
+ "sha256": "e8c5b917eceaed404e4a4858ede3acfa7cf3864255e27b82f3d6b8c4799ada9e",
"role": "Regression proof for lazy exact-case Official30 source conversion and structured failure."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/mcp_knowledge_base.rst in full and compared it against the changed authorities: tests/unit/agent/test_knowledge_base_service.py (MfdPresetKey renamed MfdPresetDefinition in the corpus assertions); the page does not name the preset class and its allowlist, search and section claims are untouched by the rename, so nothing was changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_knowledge_base.rst against B6's later changes to tests/unit/agent/test_knowledge_base_service.py: metadata documents are written only for backends that host local files (not virtual backends such as OMERO); the image-source guide test reads axis-family spellings. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_knowledge_base.rst against B6's later changes to tests/unit/agent/test_knowledge_base_service.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_knowledge_base.rst against the later B6 changes to openhcs/agent/capabilities.py, tests/unit/agent/test_knowledge_base_service.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/mcp_knowledge_base.rst in full and compared it against the changed authorities: knowledge_base_service.py (comparison-manifest parsing moved to openhcs.agent.knowledge_manifest_schema.ComparisonManifestSnapshot; per-case module inventories are now cached by source mtime), knowledge_manifest.py (comparison_manifest_type_for_root) and both test files. Confirmed the generated Python conversion is still recomputed per request and never stored (the new cache holds only module-name inventories), the 12,000 default bound, the four MCP surface names and dev-client commands with --limit, --max-chars and --section-id, the four document ids in mcp_knowledge_base_manifest.json, benchmark/manifests/official30_portable_axis1.json, the first-use capability names, and every cited documentation path; no page change was needed. Added openhcs/agent/capabilities.py for the surface names.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"tests/unit/agent/test_knowledge_base_service.py passed; source inspection confirmed lazy selected-section Official30 conversion",
"On 2026-08-24, re-read the complete page against the changed declaration, dependency, UI state, knowledge, and test authorities named by this record; the documented behavior and ownership boundaries remain accurate.",
@@ -3143,7 +3460,7 @@
},
{
"path": "docs/source/development/mcp_release.rst",
- "source_sha256": "df77e028492fbfa6856545a9eb138959a3ad7535de2878ede2583fa2b39f5ab1",
+ "source_sha256": "257396cda52f8bad5a3e32dc849007a939f4cd36ecbf03de298ba37083e2f9b7",
"audience": [
"OpenHCS release maintainers"
],
@@ -3152,7 +3469,7 @@
"authority": [
{
"path": "openhcs/__init__.py",
- "sha256": "4cb0632444fde9d3e430cc3ccce9c2b5c917e731e4b430356c5f48943de4abb1",
+ "sha256": "4ba9e81be71e75623f89835e547f37c621bf998264ffb9f6e9adc2895db14928",
"role": "Single OpenHCS package version authority."
},
{
@@ -3197,17 +3514,17 @@
},
{
"path": "tests/unit/test_validate_local_release_floors.py",
- "sha256": "c9016348d86f7585ee08a4e1432e10c8071c612736aaf171dcd7e495efe14ff1",
+ "sha256": "36b9e9aa7a4d7ac78c7eaa229750582a025ea8bffe43e3b2ccd2350fe496c242",
"role": "Proves metadata discovery and nominal requirement-to-candidate compatibility across stable and pre-1.0 release series."
},
{
"path": ".github/workflows/publish.yml",
- "sha256": "5039b3878de77647a1abf839923ffb0f675478263ffc9a8840139710d00a6ca9",
+ "sha256": "918ea1bbc262845ecb7e6bffefcf0d6af0cd2ddb598f7be9ee8635d9d3c34bb7",
"role": "Exact-tag installer, installed-GUI startup, PyPI, GitHub Release, recovery, and MCP Registry workflow consuming the central release requirements."
},
{
"path": "tests/unit/test_mcp_release_workflow.py",
- "sha256": "801c4b41931b1e725e8e42d8c0c30a78cd43cd63baeb6de5ed7bfb27d0273292",
+ "sha256": "5433d76ab87c00f0047e43b65c7a590aab7a8e397a0a1fd7cf6dc3b01e062854",
"role": "Proves immutable tag resolution, exact-SHA checkout, publication ordering, recovery, Registry policy, and publication-independent native candidate ownership."
},
{
@@ -3217,12 +3534,12 @@
},
{
"path": ".github/workflows/integration-tests.yml",
- "sha256": "c1ec94eab493bc46986c55b8b1428d9a0a5c0eaf5789f2dddbc06df2c0843ff7",
+ "sha256": "b2bcf966b13447f4f584dd27ea250d8c0adbf41faed37b61c5a1a30aec2d7aed",
"role": "Cross-platform source-candidate and published-dependency readiness, installed-wheel live GUI/MCP operation with retained native screenshots, three-stage native-installer workflow proof, signing-helper parse, and staged-update validation matrix."
},
{
"path": "scripts/resolve_successful_job_baseline.py",
- "sha256": "8e6541a53dac376176a63cb449e9166a425ef673954847cccd96fe444d60ad48",
+ "sha256": "be32836149414a8edf9ea73e1316907847376c86156b758b786b21fbd96a3283",
"role": "Derives the cumulative Python quality boundary from the latest successful code-quality job on an ancestor workflow run."
},
{
@@ -3237,7 +3554,7 @@
},
{
"path": "tests/installer/test_windows_simple_installer.py",
- "sha256": "3ab5b37fb4deee277f9deee41dc61deab5235e245e5ff37c98f32b06fde65697",
+ "sha256": "c83a5de13f423f7fd83624cabe38dec2901cb4ccfdc367cbca8e7c0460cbe6e7",
"role": "Proves the Windows native wizard waits for rendered controls, synchronizes window composition, and retains each declared installer evidence state."
},
{
@@ -3262,12 +3579,12 @@
},
{
"path": "scripts/smoke_installed_gui.py",
- "sha256": "a5af7f55eab45b7ab43772a9ff8cf758e4940c5ad58857611cc51dbe6004bd89",
+ "sha256": "c6cca9ad8a248e63949c3556562ec4760d6aaeeaba215bb21ef556c8a8cb726d",
"role": "Owns the installed-GUI acceptance timing and single terminal completion, applies the declared logging configuration, constructs the application on declaration-allocated endpoints, proves the background function catalogue and advertised endpoint log are usable, drives the GUI through packaged MCP, validates retained window snapshots, and cleans up exact owned resources outside the checkout."
},
{
"path": "tests/unit/test_smoke_installed_gui.py",
- "sha256": "b8d1a1fcd67c58e2b2749262b44c1dcccbb2e72a0d299d180823e94a0285ae9d",
+ "sha256": "2178b0b6cfcae83dc08574573e1b021b63e2390436fffb73cafea969ad415393",
"role": "Proves source-checkout rejection, one derived smoke timing policy, deadline-owner error preservation under late cancellation, declaration-owned endpoint allocation, function-catalogue readiness, advertised execution-log validation, snapshot CLI projection, and emitted PNG identity validation for the installed GUI/MCP probe."
},
{
@@ -3292,7 +3609,7 @@
},
{
"path": "tests/installer/test_installer_signing.py",
- "sha256": "c8f9d050c4337086766e732bb09b39bd7cd61d0f7c4866fd66f6417d99c4ca1b",
+ "sha256": "32622638bced1f620e21b3f251df8a70da27f5fc3cbafd98291c092d617fdb42",
"role": "Regression proof for workflow signing policy and both native signing-helper contracts."
},
{
@@ -3302,38 +3619,23 @@
},
{
"path": "tests/unit/test_ci_package_boundaries.py",
- "sha256": "691efcdafe66255c55ae11411b441930bdb58161cd74e2580c2531a486863303",
+ "sha256": "df85419ab722292f54a520bf61bd1a11ecc08b7637ffa2abdf70a16f16088e04",
"role": "Proves publication consumes central release requirements and that candidate and published GUI matrices retain their typed screenshot evidence."
}
],
"findings": [
- "The introduction called a version-specific 0.5.22 readiness plan the current checklist even though OpenHCS and the release workflow have advanced.",
- "The pinned Jaunch signing guide link used an anchor that no longer exists in the target document.",
- "Installer recovery now enforces an existing remote release tag before any build, and signing claims require the native helpers and integration matrix as authorities.",
- "The release-candidate matrix now has one published-dependency readiness gate backed by fetched submodule release tags and exact installer-visible wheel requirements.",
- "The PyPI installation matrix waited for the exact dependency wheels but then queried each runner's potentially stale package index instead of consuming that projection; every install lane now reuses the hash-pinned wheel requirements.",
- "The macOS disk-image helper retained only the backing device and could leave its synthesized APFS volume mounted; the attachment boundary now carries and releases both exact device identities.",
- "The release-candidate section did not identify the dedicated GUI source gate or distinguish it from installed-package evidence.",
- "The per-push Python quality comparison could forget a red change after an unrelated follow-up push; it now retains changes since the most recent ancestor whose code-quality job succeeded.",
- "Local tagging, manual publication, and installer recovery were independent fail-open paths; one readiness authority and one immutable tag-resolution job now bind them to the same exact commit and CI evidence.",
- "PyPI recovery used --skip-existing, which could hide independently rebuilt artifact conflicts; existing release files now stop publication.",
- "Workflow dependencies used movable major-version tags and deprecated Node 20 action releases; every external action now uses a current full-length commit pin maintained through grouped Dependabot updates.",
- "Release-candidate installer lanes inherited workstation pip configuration and package indexes; native installation, reinstall, and staged update now suppress those external settings while CI injects an unreachable index as proof.",
- "Published OpenHCS 0.7.26 could resolve a nominally valid but startup-incompatible pyqt-reactive version; installed-wheel publication gates and the scheduled published-release canary now exercise the real GUI ready boundary.",
- "Six first-party requirements had no breaking-series ceiling; the package metadata now bounds every extracted dependency, and the metadata-derived release validator rejects an unbounded future candidate.",
- "The installed-GUI probe reused the default execution endpoint, allowing an existing desktop server to influence an otherwise isolated package canary; it now allocates the endpoint through the selected transport declaration.",
- "Installed GUI and MCP package checks terminated before either operated the other; the source-wheel, publication, and scheduled canaries now keep the packaged GUI live while the packaged desktop MCP discovers it and invokes a declared UI action.",
- "The fixed desktop candidate was operated before publication only on Linux, while Windows and macOS evidence remained behind the unpublished-dependency gate; a complementary source-candidate matrix now proves Windows and both macOS architectures alongside the Linux wheel lane.",
- "The native source-candidate matrix still depended on published first-party tags, so the real installer and staged-update jobs were skipped exactly while those dependency releases were being prepared; the native lane now builds its complete wheelhouse from metadata-discovered submodules while the separate PyPI lane remains fail-closed on publication readiness.",
- "Cross-platform GUI canaries proved readiness and MCP actions but discarded the rendered UI, leaving native theming regressions outside retained evidence; candidate and published-package jobs now upload typed, hash-verified window snapshots.",
- "The installed-GUI canary constructed the application below the production logging boundary, so a successful endpoint could advertise an empty log; the probe now applies its declared logging configuration and fails unless the exact advertised log contains output.",
- "Native installer acceptance invoked worker mode without proving that either shipping wizard could start or drive installation; each supported lane now activates the shipping wizard and retains its welcome, progress, and completion states.",
- "The Windows installer evidence harness captured its welcome frame when only the top-level handle existed, before child controls painted; capture now requires the real primary control and synchronizes recursive rendering with desktop composition.",
- "Integration and Documentation workflows allowed superseded commits on the same event and ref to consume the full hosted matrix; their declarations now cancel predecessors while keeping manual dispatches independent.",
- "Post-PyPI recovery rebuilt the wheel and source distribution, so GitHub could receive semantically equivalent but byte-distinct archives; recovery now materializes the PyPI-owned files and verifies their declared hashes before attaching them."
+ "The tag and recovery workflow descriptions said publish.yml builds and smoke tests one OpenHCS wheel; publish.yml now builds stable-ABI wheels for Linux (x86_64, aarch64), Windows and macOS (x86_64, arm64) with cibuildwheel, smoke-tests each native extension, builds the sdist separately, validates every wheel, and smoke tests the Linux x86_64 wheel. The PyPI-file recovery path now materializes several wheels.",
+ "The installed-GUI smoke no longer invokes a PlateManagerAction code action; it invokes the show_dataset_code session operation (ShowDatasetCode) on the Plate Manager window."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6 final), scripts/smoke_installed_gui.py changed after docs/source/development/mcp_release.rst was reviewed: the GUI smoke compares the action status to ActionAccepted; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_release.rst against .github/workflows/integration-tests.yml, scripts/smoke_installed_gui.py: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_release.rst against .github/workflows/integration-tests.yml: the unit-tests job now runs three file-isolated xdist workers; no claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_release.rst against B6's later changes to .github/workflows/integration-tests.yml: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_release.rst against B6's later changes to tests/unit/test_mcp_release_workflow.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/mcp_release.rst against the later B6 changes to tests/unit/test_mcp_release_workflow.py, scripts/smoke_installed_gui.py, tests/unit/test_smoke_installed_gui.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/mcp_release.rst in full and compared it against the changed authorities: openhcs/__init__.py (version 0.8.7, microscopy axis-family activation; the version-authority claim holds), .github/workflows/publish.yml and tests/unit/test_mcp_release_workflow.py (new build-native-wheels job, sdist built separately, per-wheel validation, Linux x86_64 smoke selection), .github/workflows/integration-tests.yml (parity-scope gate, Official30 parity on Python 3.12 Linux and 3.14 on all three OSes, run_installed_tests.py coverage), scripts/resolve_successful_job_baseline.py (branch filter removed; ancestor rule unchanged), scripts/smoke_installed_gui.py (typed MCP payloads, ShowDatasetCode), tests/installer/test_windows_simple_installer.py, tests/installer/test_installer_signing.py, tests/unit/test_validate_local_release_floors.py and tests/unit/test_ci_package_boundaries.py. Corrected the single-wheel publication and recovery descriptions and the GUI smoke action name; confirmed the Python 3.14 wheel-candidate lane still excludes the Centrosome oracle and the remaining gate, recovery and signing claims.",
"Re-read docs/source/development/mcp_release.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/__init__.py; scripts/sync_mcp_release_metadata.py; scripts/release.py; scripts/release_readiness.py; scripts/validate_local_release_floors.py; scripts/install_ci_candidate.py; .github/workflows/publish.yml; tests/unit/test_mcp_release_workflow.py; tests/unit/test_release_readiness.py; .github/workflows/integration-tests.yml; .github/dependabot.yml; tests/unit/test_ci_action_pinning.py; packaging/installers/windows/Sign-Installer.ps1; packaging/installers/macos/Sign-Installer.sh; tests/installer/test_installer_signing.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -3412,7 +3714,7 @@
"authority": [
{
"path": "external/PolyStore/src/polystore/omero_local.py",
- "sha256": "35d39564317510234c2a3ded00bb06a09417d1b6f7359e52e74c7bd0a086f642",
+ "sha256": "e549d4811539cec9842ceac007eb666380ef00d6838a9b69d91eb02de64d1daa",
"role": "Generic OMERO addressing, persistence dispatch, and image-plane batching."
},
{
@@ -3442,7 +3744,7 @@
},
{
"path": "tests/integration/test_main.py",
- "sha256": "1a0c532eb631a89b77f92a4202a7b55033c86d7b872350156f106d276b061e9f",
+ "sha256": "ce4a32452cef8b8dadea26894b923bbda88f0f88635e979c83f81a8d6ac34f29",
"role": "Direct and ZMQ compile-before-execute integration harness with explicit OMERO lifecycle cleanup and no browser side effect."
},
{
@@ -3457,20 +3759,19 @@
},
{
"path": ".github/workflows/integration-tests.yml",
- "sha256": "c1ec94eab493bc46986c55b8b1428d9a0a5c0eaf5789f2dddbc06df2c0843ff7",
+ "sha256": "b2bcf966b13447f4f584dd27ea250d8c0adbf41faed37b61c5a1a30aec2d7aed",
"role": "Runs explicitly selected live OMERO variants and preserves component and table-service diagnostics on failure."
}
],
- "findings": [
- "The how-to omitted declaration-level tests for OMERO text, table-service, and image-plane semantics and did not require direct plus ZMQ live execution or a cold deployment.",
- "Legacy setup documentation inferred readiness from a hardcoded container name; the how-to now follows the instance manager and PolyStore table-service authorities.",
- "The how-to did not distinguish the packaged Compose lifecycle from host-owned Docker daemon startup.",
- "The address-testing boundary did not distinguish concrete plane identities from symbolic pattern-discovery templates.",
- "A live cold-start test could fail on a transient Docker daemon probe even though the same runner became ready moments later; the instance lifecycle now owns a bounded readiness wait.",
- "The Python 3.12 cold deployment exposed Tables-0 disabling itself before the managed repository existed; tests now distinguish generic repository readiness from packaged deployment recovery."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/development/omero_testing.rst against .github/workflows/integration-tests.yml: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/omero_testing.rst against .github/workflows/integration-tests.yml: the unit-tests job now runs three file-isolated xdist workers; no claim this page makes changes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/omero_testing.rst in full and compared it against the changed authorities: tests/integration/test_main.py (E1: ReadPipelineStart, PadToMaxSize and MaterializationBackend.__registry__ replace enum members); the page names none of those spellings; the harness still selects the omero_local materialization backend for OMERO runs and exercises direct and ZMQ execution, so nothing was changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/omero_testing.rst against B6's later changes to .github/workflows/integration-tests.yml: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/omero_testing.rst against the later B6 changes to external/PolyStore/src/polystore/omero_local.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/omero_testing.rst in full and compared it against the changed authorities: tests/integration/test_main.py (partition-filter config renames, Microscopy axis family, DatasetSourceChoice, per-test allocated ports, execute_compiled_bundle, subject_id, PipelineDocumentCodec) and .github/workflows/integration-tests.yml (parity-scope gate, run_installed_tests.py coverage). The page names none of the renamed test internals; confirmed the harness still registers OMEROLocalBackend and runs direct and ZMQ modes without opening a browser, the omero-tests-linux lane still builds submodule candidate wheels without a publication dependency and keeps Tables-0 and component diagnostics, and OMEROWellAddress, OMEROPlaneAddress, OMEROPlaneFilenameTemplate and FilenameParseResult exist at their owners.",
"Re-read docs/source/development/omero_testing.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: external/PolyStore/src/polystore/omero_local.py; external/PolyStore/tests/test_omero_tables.py; tests/integration/test_main.py; openhcs/omero/docker-compose.yml; openhcs/runtime/omero_instance_manager.py; .github/workflows/integration-tests.yml.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -3519,14 +3820,9 @@
"user_need": "Identify the archive status and reach the current signature/form extension boundaries.",
"diataxis": "reference",
"authority": [
- {
- "path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
- "role": "Current Python support, dependency, package discovery, and entry-point authority."
- },
{
"path": "external/python-introspect/src/python_introspect/signature_analyzer.py",
- "sha256": "73600e546753640964d4ba5308fdf808ed3a4a6f085417d8ac0745d9e2748ed7",
+ "sha256": "edbe51f6c92b2c334dc4533bbeaa252be649c1ac738d2ebfca4364f2708479dc",
"role": "Generic callable signature-analysis ownership."
},
{
@@ -3536,16 +3832,15 @@
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/forms/parameter_form_manager.py",
- "sha256": "a561e62e29af42630fa107b0929fc87b74007f7ed14ec8f0a6881c49070dc923",
+ "sha256": "2c59c3dc2f95a349970815a33c2afcf5158c97f4627973a8a0c6826f8ca9ae6c",
"role": "Reusable parameter-form lifecycle and value projection."
}
],
- "findings": [
- "The active orphan did not explicitly distinguish its compatibility-route status from current developer guidance.",
- "The current generic parameter-analysis owner now also projects callable declarations from signature defaults."
- ],
- "disposition": "redirect",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/parameter_analysis_audit.rst in full and compared it against the changed authority external/pyqt-reactive/src/pyqt_reactive/forms/parameter_form_manager.py (current file; ParameterFormManager and FormManagerConfig still own the generic Qt form lifecycle in pyqt-reactive). Confirmed signature and callable-declaration analysis remain in python-introspect, no Textual import remains in openhcs/, and the archived audit path exists; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/parameter_analysis_audit.rst in full and compared it against the changed authorities: pyproject.toml (dependency floors only) and external/python-introspect/src/python_introspect/signature_analyzer.py (current file); signature and docstring analysis still lives in python-introspect, generic forms in pyqt-reactive, no Textual UI remains in openhcs/, and the archived audit path exists. Dropped pyproject.toml because the orphan redirect states no package facts.",
"Re-read docs/source/development/parameter_analysis_audit.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: pyproject.toml; external/python-introspect/src/python_introspect/signature_analyzer.py; external/pyqt-reactive/src/pyqt_reactive/forms/parameter_form_manager.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -3584,28 +3879,33 @@
"authority": [
{
"path": "openhcs/core/debug.py",
- "sha256": "072259811bc50bfe71cce9a7cac3e9867bfd853bd34ab5a74b01d9fe14b68f36",
+ "sha256": "311b003ce50b69abbbdd934e1ff53cade27105605b6c3d7c980fc4ac81b8c2a4",
"role": "Nominal debug command, boundary, snapshot, replay, and paused-worker declarations."
},
{
"path": "openhcs/core/debug_session_projection.py",
- "sha256": "0a630eb03a83d741a4ed55838adb8ee65dd4fde9be07878aeba5e7a8c86c7378",
+ "sha256": "a088647748f3f3dace29ace81a6efacf3af32d66fd846417eedd5755b3545868",
"role": "Typed debug-session phase selection from exact runtime context."
},
{
"path": "openhcs/agent/services/runtime_server_service.py",
- "sha256": "34b61a154718e3e613245e84cdeca1c89a5fbbbddd6a5bb26f76a146d62c4f65",
+ "sha256": "f34c26b3c8110e85b4322591a43dff9d81da4c5cf3f48422c065b0d069ee6d6d",
"role": "Bounded runtime-server and paused-debug inspection service."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
"role": "Compilation order, declaration resolution, callable warm-up, framework-local device assignment, and bundle construction."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/pipeline_debugging_guide.rst in full and compared it against the changed authorities: openhcs/core/debug.py (K2 artifacts package imports), openhcs/core/debug_session_projection.py (E1: DebugSessionPhase is now the phase family itself) and openhcs/core/pipeline/compiler.py (E1: orchestrator state and materialization backend classes); the page names the debug phase only as state-surface content and names no removed class; CompiledStepPlan, CompilationSession, debug_pause and the inspection tool names are unchanged, so nothing was changed.",
+ "On 2026-10-11 (B6), re-read docs/source/development/pipeline_debugging_guide.rst in full and compared it against the changed authorities: openhcs/core/debug.py (K6: debug commands, event boundaries and replay modes became class families such as StepDebugCommand and AfterInvocationDebugBoundary; control payload wrappers deleted), openhcs/agent/services/runtime_server_service.py (command decoded through registered_class_from_key, worker state read from the typed status) and openhcs/core/pipeline/compiler.py (ScopeKey scope ids, progress phase/status classes, CompiledSourceBindingPlan() default). The page names none of the changed types; rechecked debug_pause on AbstractStep, the 'Run to Pause', Step and 'Inspect Runtime' action labels, pipeline_debug_toolbar.session, openhcs_inspect_pipeline_source_artifact_plan, openhcs_inspect_debug_runtime_values / runtime-debug-values, SettingToKeywordBinding and StepMaterializationConfig; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/pipeline_debugging_guide.rst against B6's later changes to openhcs/core/debug.py, openhcs/core/pipeline/compiler.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/pipeline_debugging_guide.rst against the later B6 changes to openhcs/core/debug.py, openhcs/core/pipeline/compiler.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/pipeline_debugging_guide.rst in full and compared it against the changed authorities: openhcs/core/debug.py (DebugJsonCodec removed, DebugProgressContext now a declared progress-context payload, plate_id renamed subject_id), openhcs/agent/services/runtime_server_service.py and openhcs/core/pipeline/compiler.py (CompilationPlateScope and plate_path renamed to dataset scope and dataset_root). The page names none of the renamed fields; confirmed openhcs_inspect_pipeline_source_artifact_plan, openhcs_inspect_debug_runtime_values and its runtime-debug-values command, debug_session_id, pipeline_debug_toolbar, FunctionStep.debug_pause, the Run to Pause, Step and Inspect Runtime actions, StepMaterializationConfig, RuntimeValueStore, CompiledStepPlan, CompilationSession and CompiledExecutionBundle still exist with the described roles.",
"Re-read docs/source/development/pipeline_debugging_guide.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/debug.py; openhcs/core/debug_session_projection.py; openhcs/agent/services/runtime_server_service.py; openhcs/core/pipeline/compiler.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -3616,7 +3916,7 @@
},
{
"path": "docs/source/development/pyclesperanto_simple_implementation.rst",
- "source_sha256": "5cecda2276f76c744ad6a215f8ecd58c82c2ce6ce4859d6d5990f39ba67ffc3c",
+ "source_sha256": "14d112b12e6984faed5c19c022a14cab430c5157a110c949ed0fd3bc0598da13",
"audience": [
"OpenHCS GPU-processing callable authors"
],
@@ -3631,31 +3931,36 @@
{
"path": "openhcs/processing/backends/lib_registry/pyclesperanto_registry.py",
"sha256": "9049165370dbea917e643395c516e08e94509777d9bf8edf9476cf1e75bccc77",
- "role": "Declaration-owned pyclesperanto callable-library discovery."
+ "role": "Declaration-owned pyclesperanto callable-library discovery and the runtime-owned output_image, output_image_matrix and device parameters."
},
{
"path": "openhcs/processing/backends/processors/pyclesperanto_processor.py",
- "sha256": "0504e1434a489005a1e0c3d7edeeb306acf1b4f9aba463e45ab0c817970a9652",
+ "sha256": "ae6a2507ecfea6e592f4c9ec874e761ef82eb9a958c09e56c17ff9e3b3ef7390",
"role": "OpenHCS processing callables using the pyclesperanto integration."
},
{
"path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
"role": "Pyclesperanto framework device discovery, execution scope, and cleanup authority."
},
{
"path": "external/arraybridge/src/arraybridge/decorators.py",
- "sha256": "99823105b0ae59054df1d84013633b8b6628d0a6dc3326383199ac7c65c69bed",
+ "sha256": "28f9bf4770bd4cc1ef34045fdef28065063ede029296f62b8a6c4140cc88e40e",
"role": "Memory decorator that declares pyclesperanto input, output, and execution roles."
},
{
"path": "openhcs/core/memory/decorators.py",
- "sha256": "d9f9cf0be7593dc0ebfc57f258aebed75bcf9cfa508ed1e942137366d819766a",
- "role": "OpenHCS metadata-preserving wrapper around ArrayBridge memory decorators."
+ "sha256": "76f234785f4865257fd19c935e0aace82d1f5586198737fec619ee243b79cbf6",
+ "role": "OpenHCS metadata-preserving wrapper around ArrayBridge memory decorators, accepting a ProcessingContract class as contract."
+ },
+ {
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "ProcessingContract class family including Pure3DContract used in the example."
},
{
"path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
"role": "Compiler-visible projection of all declared callable memory roles."
},
{
@@ -3665,7 +3970,7 @@
},
{
"path": "openhcs/core/steps/function_runtime.py",
- "sha256": "b1449120bebe7cf4898c055d841dc258c7d91d976d9e5ff7396aed81ad5011e7",
+ "sha256": "0b5b3b025811df3b8075e51d605474b8be1e4b7f6fe873da82882e0232bf7dc2",
"role": "Runtime conversion and invocation inside the compiled execution-framework device scope."
},
{
@@ -3675,15 +3980,19 @@
},
{
"path": "tests/unit/test_unified_registry_runtime_payloads.py",
- "sha256": "e731379d3e48fd5c98709203668e09baa195bb0c9301a2b041e1c799fa8140e3",
+ "sha256": "a817850fc2d84f82da3c5e60d2ae33d461d795b36d65816955a4c8b18bd9b852",
"role": "Proves pyclesperanto output buffers and device handles remain runtime-owned while algorithm parameters stay authored."
}
],
- "findings": [
- "The how-to lacked an exact decorator import, a minimal declaration example, framework-local device behavior, and a navigable ArrayBridge owner link."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/core/steps/function_runtime.py changed by black formatting only (AST-equal) after docs/source/development/pyclesperanto_simple_implementation.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/pyclesperanto_simple_implementation.rst in full and compared it against the changed authorities: openhcs/processing/backends/processors/pyclesperanto_processor.py and openhcs/core/callable_contract.py (K2: artifact imports moved to openhcs.core.artifacts.specs/kinds/plans) and the current openhcs/core/steps/function_runtime.py; the example imports only openhcs.core.memory and openhcs.core.processing_contracts, both unchanged; Pure3DContract, PyclesperantoRegistry's runtime-owned output_image/output_image_matrix/device parameters and the runtime execution_device_scope still exist, so nothing was changed.",
+ "On 2026-10-11 (B6), re-read docs/source/development/pyclesperanto_simple_implementation.rst in full and compared it against the changed authorities: openhcs/core/processing_contracts.py and openhcs/core/callable_contract.py (K5: CallableMetadata replaced by CallableDeclaration, which CallableContract inherits), openhcs/core/steps/function_runtime.py (K6 debug boundary classes; main-flow source-binding plan no longer None) and tests/unit/test_unified_registry_runtime_payloads.py. The page names none of the changed types; rechecked the pyclesperanto decorator re-exported from openhcs.core.memory, Pure3DContract, and PyclesperantoRegistry's runtime-owned output_image, output_image_matrix and device parameters; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/pyclesperanto_simple_implementation.rst against B6's later changes to openhcs/processing/backends/processors/pyclesperanto_processor.py, openhcs/core/memory/decorators.py, openhcs/core/processing_contracts.py, openhcs/core/callable_contract.py, openhcs/core/steps/function_runtime.py, tests/unit/test_unified_registry_runtime_payloads.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/pyclesperanto_simple_implementation.rst against the later B6 changes to openhcs/processing/backends/processors/pyclesperanto_processor.py, openhcs/core/memory/decorators.py, openhcs/core/processing_contracts.py, openhcs/core/callable_contract.py, openhcs/core/steps/function_runtime.py, tests/unit/test_unified_registry_runtime_payloads.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/pyclesperanto_simple_implementation.rst in full and compared it against the changed authorities: the page itself (example already moved from ProcessingContract.PURE_3D to the Pure3DContract class, G5), pyclesperanto_processor.py (shared operations moved to image_stack_operations), arraybridge types.py and decorators.py, openhcs/core/memory/decorators.py, callable_contract.py, function_runtime.py and test_unified_registry_runtime_payloads.py. Confirmed the example imports (openhcs.core.memory.pyclesperanto, openhcs.core.processing_contracts.Pure3DContract), that the OpenHCS decorator accepts a ProcessingContract subclass as contract, and the three runtime-owned PyclesperantoRegistry parameters; added processing_contracts.py as the owner of the example contract.",
"Re-read docs/source/development/pyclesperanto_simple_implementation.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: external/arraybridge/src/arraybridge/converters.py; openhcs/processing/backends/lib_registry/pyclesperanto_registry.py; openhcs/processing/backends/processors/pyclesperanto_processor.py; external/arraybridge/src/arraybridge/types.py; external/arraybridge/src/arraybridge/decorators.py; openhcs/core/memory/decorators.py; openhcs/core/callable_contract.py; openhcs/core/framework_device_resolver.py; openhcs/core/steps/function_runtime.py; tests/unit/test_framework_device_resolver.py; tests/unit/test_unified_registry_runtime_payloads.py.",
"On 2026-08-27, re-read and revised the complete how-to after PyclesperantoRegistry began owning external output-buffer and device exclusions; the page now states which signature parameters stay out of authored forms and code, and the focused runtime-payload regression passed.",
@@ -3712,15 +4021,17 @@
},
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
"role": "Current Python support, dependency, package discovery, and entry-point authority."
}
],
- "findings": [
- "The packaging paragraph attributed runtime dependency resolution to python -m build; it now distinguishes recording requirements in the wheel from resolving them during installation."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/repository_setup.rst in full and compared it against the changed authorities: pyproject.toml (python-introspect requirement raised to 0.2.8); the page quotes no dependency version; the Python 3.11-3.14 range, the centrosome python_version < 3.14 marker, the dev/dev-gui/gui/viz/gpu/docs/cellprofiler-compat extras, the CUDA 12 gpu pins and the eight external/ submodule directories all match, so nothing was changed.",
+ "On 2026-10-11 (B6), re-read docs/source/development/repository_setup.rst in full and compared it against the changed authorities: pyproject.toml (first-party floors moved to zmqruntime 0.6.2, pycodify 0.2, objectstate 2.1.1, python-introspect 0.2.7, pyqt-reactive 0.5); the page states no version floors. Rechecked the eight submodules against .gitmodules, requires-python >=3.11,<3.15, the named extras (dev, gui, dev-gui, cellprofiler-compat, viz, gpu, docs), and that OPENHCS_CPU_ONLY=1 is accepted by OpenHCSProcessEnvironment.flag_enabled; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/repository_setup.rst against the later B6 changes to pyproject.toml: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/repository_setup.rst in full and compared it against the changed authorities: pyproject.toml (first-party floors moved to zmqruntime 0.6, objectstate 1.3.1, python-introspect 0.2.5, metaclass-registry 0.3, arraybridge 0.4.1, polystore 0.6, pyqt-reactive 0.4; JAX GPU extra now jax[cuda12-local]; openhcs-basicpy added as an ordinary dependency); challenged the eight-submodule count against .gitmodules, the 3.11-3.14 range against requires-python, the centrosome parity oracle's python_version < 3.14 marker, the named extras (dev, gui, dev-gui, cellprofiler-compat, viz, gpu, docs), setup.py deferring metadata to pyproject.toml, and the CUDA 12 statement for the gpu extra; all remain accurate and the page states no version floors.",
"Re-read docs/source/development/repository_setup.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: .gitmodules; pyproject.toml.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -3746,7 +4057,7 @@
},
{
"path": "docs/source/development/respecting_codebase_architecture.rst",
- "source_sha256": "2f9665a3a063e62b401f3d44c24f596baf16d3496d0953efc269e563bda9d48b",
+ "source_sha256": "7db323adce801d8a8a38d8ba8d77493486870ba9a573a161f234ec1e4fdc1465",
"audience": [
"OpenHCS maintainers and reviewers"
],
@@ -3754,9 +4065,14 @@
"diataxis": "how-to",
"authority": [
{
- "path": "openhcs/core/registry_strategies.py",
- "sha256": "a41186665abbd308f224d0bb41d6618cd9d89b08acaf54ec2819f2024a6e0804",
- "role": "OpenHCS enum, nominal-type, and most-derived strategy selection implementations."
+ "path": "external/metaclass-registry/src/metaclass_registry/strategies.py",
+ "sha256": "5c00cd4d80f9f0ceffbc2ab95ad8c6abd44a8a66a974959445ef93df08dd4f0d",
+ "role": "Declares EnumKeyedStrategyMixin, NominalTypeKeyedStrategyMixin and MostDerivedContextStrategyMixin and their registry-derived selection, which the selection rules section names."
+ },
+ {
+ "path": "external/metaclass-registry/src/metaclass_registry/core.py",
+ "sha256": "72c8284e8a32dd193f13f36a425289d5293a32f4a381046db523aa87182e0be8",
+ "role": "Declares AutoRegisterMeta, RegistryConfig and RegistryFamily and the __registry__ catalogue that the search command and registry discipline section rely on."
},
{
"path": "external/python-introspect/src/python_introspect/callable_declaration.py",
@@ -3765,25 +4081,28 @@
},
{
"path": "openhcs/core/pipeline/compilation_session.py",
- "sha256": "3c840a89baccc8665d415175458280fd904c1a837b7fd879e303d7dbca129a1c",
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
"role": "Resolved step/session identity and typed plan assembly invariants."
},
{
"path": "openhcs/core/runtime_stores.py",
- "sha256": "5f323ec8edd4221ee30e34ed0c9c09e0f4e45ef502749c1a2d331b7a5452af99",
+ "sha256": "35fe71906bfd16842de347776a0c5cc846550ad490cc3628885129d746ec6f31",
"role": "RuntimeValueStore recording, replacement, query, and observation semantics."
},
{
"path": "tests/unit/test_configuration_mirror_deletion.py",
- "sha256": "26002ddfbbf89bf06ae54888645c6b8c90154536a16e93feee1a2fae713c4963",
+ "sha256": "8f9da50e930925ed3ecdd57add9cc919e008a9202c9e405ff922dcd1c9a418cc",
"role": "Deletion gate proving obsolete parallel configuration and browser modules remain absent."
}
],
- "findings": [
- "The package-ownership summary omitted python-introspect's callable-declaration projection responsibility."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/respecting_codebase_architecture.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compilation_session.py and openhcs/core/runtime_stores.py (K2: artifact imports moved into the openhcs.core.artifacts package); the page names no artifact module path; EnumKeyedStrategyMixin, NominalTypeKeyedStrategyMixin, MostDerivedContextStrategyMixin and RegistryFamily still exist in metaclass_registry and EnumKeyedStrategyMixin still has OpenHCS users, so nothing was changed.",
+ "On 2026-10-11 (B6), re-read docs/source/development/respecting_codebase_architecture.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compilation_session.py (per-invocation validation moved onto ArtifactGraph.validate_invocation_declarations; strip_disabled_functions no longer applied) and openhcs/core/runtime_stores.py (hand-written to_dict/from_dict transport on RuntimeArtifactLocation and RuntimeArtifactAddress deleted). Both moves agree with the page's single-owner guidance; rechecked that the three strategy mixins, AutoRegisterMeta, RegistryConfig and RegistryFamily still exist in metaclass-registry; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/respecting_codebase_architecture.rst against B6's later changes to openhcs/core/pipeline/compilation_session.py, openhcs/core/runtime_stores.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/respecting_codebase_architecture.rst against the later B6 changes to openhcs/core/pipeline/compilation_session.py, openhcs/core/runtime_stores.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/respecting_codebase_architecture.rst in full and compared it against the changed authorities: openhcs/core/registry_strategies.py (deleted in b221f8491), openhcs/core/pipeline/compilation_session.py, openhcs/core/runtime_stores.py, tests/unit/test_configuration_mirror_deletion.py; confirmed the three strategy mixins and the registry types named in the search command exist in metaclass-registry, replaced the deleted openhcs.core.registry_strategies reference with metaclass_registry.strategies, and found the typed-plan, RuntimeValueStore and mirror-deletion claims still accurate.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"Source search confirmed the three named strategy mixins and direct typed-plan consumers; mirror-deletion tests protect removed parallel surfaces",
"On 2026-08-21, re-read the guide against callable_declaration.py and the updated python-introspect owner documentation.",
@@ -3793,7 +4112,7 @@
},
{
"path": "docs/source/development/runtime_system_assembly_rules.rst",
- "source_sha256": "a2275aad184f08bd2966a26e44463909c92c5151b79936f1ce76f83766fc7164",
+ "source_sha256": "1a4cf1b603e199ed6dac4e5646530eb8e5dfb58f7939dbaf57a3f2e7b71b75ab",
"audience": [
"OpenHCS compiler, adapter, and runtime maintainers"
],
@@ -3802,45 +4121,48 @@
"authority": [
{
"path": "openhcs/core/pipeline/compilation_session.py",
- "sha256": "3c840a89baccc8665d415175458280fd904c1a837b7fd879e303d7dbca129a1c",
- "role": "Resolved step/session identity and typed plan assembly invariants."
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "Declares ResolvedPipelineDefinition and CompilationSession, the resolved declaration view and per-context compiler boundary named in the assembly order."
},
{
"path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
- "role": "CompiledExecutionBundle and runtime-environment authority; execution-wide worker facts remain separate from per-step framework devices."
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "CompiledExecutionBundle fields: runtime and transport contexts, worker assignments and runtime environment plan."
},
{
"path": "openhcs/core/runtime_stores.py",
- "sha256": "5f323ec8edd4221ee30e34ed0c9c09e0f4e45ef502749c1a2d331b7a5452af99",
+ "sha256": "35fe71906bfd16842de347776a0c5cc846550ad490cc3628885129d746ec6f31",
"role": "RuntimeValueStore recording, replacement, query, and observation semantics."
},
{
"path": "openhcs/core/runtime_slice_projection.py",
- "sha256": "8a1cb65d3025ed8a87ee59af26deac9765a8f59b21bf2cd61b6f96d82999641d",
- "role": "Nominal runtime slice projection registry and aggregation behaviour."
+ "sha256": "f59785d901d0d7bcc3ac1fc1be606bd1728a727ea7347cdc1dd4f549cc3193fa",
+ "role": "RuntimeSliceProjection boundary between owned projectable values and foreign values, with source-provenance checks on stacks."
},
{
"path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
- "role": "Callable ABI and input/output/execution framework declaration authority."
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "Callable ABI and input/output/execution memory-type declaration authority."
},
{
"path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
- "role": "Typed per-step semantic and framework-device decisions."
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "Typed per-step source-binding plan and framework-device decisions."
},
{
"path": "openhcs/core/pipeline/framework_device_assignment.py",
- "sha256": "f77f3fb2d30c4af8cab6210dbe294f9fb5e246dad0a6bbf88bcaf1e2b58dead7",
+ "sha256": "cdfde10f996ab682952a1d4948d3de2d23d48353eae41f3124770e19fc28a193",
"role": "Compiler projection from declared framework footprint to exact step bindings."
}
],
- "findings": [
- "The assembly owner map omitted callable execution-memory roles and per-step framework-local device bindings."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/runtime_system_assembly_rules.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compilation_session.py, openhcs/core/runtime_stores.py, openhcs/core/callable_contract.py, openhcs/core/compiled_step_plan.py (K2 artifact package imports; UnresolvedStepInput default) and openhcs/core/runtime_slice_projection.py (new stored_slices/match_alignment/contextualized_output delegating to the value); every owner in the owner map still exists with the stated responsibility, and the RuntimeSliceProjection entry stays true because the new methods delegate to RuntimeSliceProjectableValue, so nothing was changed.",
+ "On 2026-10-11 (B6), re-read docs/source/development/runtime_system_assembly_rules.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compilation_session.py (per-invocation validation moved onto ArtifactGraph), openhcs/core/runtime_stores.py (hand-written address/location to_dict/from_dict deleted), openhcs/core/callable_contract.py (K5: CallableContract now inherits CallableDeclaration and its declared fields directly; memory roles, artifact inputs/outputs and execution scope remain on the contract) and openhcs/core/compiled_step_plan.py (CompiledSourceBindingPlan() default). Rechecked CellProfilerModuleArtifactContracts, CompiledExecutionBundle's runtime/transport contexts, worker assignments and runtime environment, SourceBindingsConfig, ArtifactSpecRef and ArtifactKey; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/runtime_system_assembly_rules.rst against B6's later changes to openhcs/core/pipeline/compilation_session.py, openhcs/core/runtime_stores.py, openhcs/core/callable_contract.py, openhcs/core/compiled_step_plan.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/runtime_system_assembly_rules.rst against the later B6 changes to openhcs/core/pipeline/compilation_session.py, openhcs/core/runtime_stores.py, openhcs/core/callable_contract.py, openhcs/core/compiled_step_plan.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/runtime_system_assembly_rules.rst in full and compared it against the changed authorities: compilation_session.py, compiled_execution.py, runtime_stores.py, runtime_slice_projection.py, callable_contract.py, compiled_step_plan.py, framework_device_assignment.py; replaced the deleted StepSnapshot with ResolvedPipelineDefinition, corrected the CompiledExecutionBundle field summary and the RuntimeSliceProjection description, and confirmed CallableContract memory roles, CellProfilerModuleArtifactContracts, SourceBindingsConfig, CompiledSourceUniversePlan, ArtifactSpecRef and ArtifactKey still exist as described.",
"Re-read docs/source/development/runtime_system_assembly_rules.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline/compilation_session.py; openhcs/core/compiled_execution.py; openhcs/core/runtime_stores.py; openhcs/core/runtime_slice_projection.py; openhcs/core/callable_contract.py; openhcs/core/compiled_step_plan.py; openhcs/core/pipeline/framework_device_assignment.py.",
"Focused feature-lattice QA passed 159 tests covering the N-Z architecture and developer workflow authorities.",
@@ -3852,7 +4174,7 @@
},
{
"path": "docs/source/development/runtime_value_extension.rst",
- "source_sha256": "eb0f4bdd7eac7fff1ded970acbe29db94f76e06d1bb61d8e25d564f43ea9e249",
+ "source_sha256": "73aaa60318f73e06b27473e7beaaad533a99f9665be7c017ae480e506063b4cb",
"audience": [
"OpenHCS runtime-value extension authors"
],
@@ -3861,28 +4183,38 @@
"authority": [
{
"path": "openhcs/core/runtime_artifact_values.py",
- "sha256": "9ac62ec4c4a6a6f7f375026ea112cc2e43f09ee6e6ca270a3d955be8ef5347cf",
- "role": "RuntimeValue, ArtifactKey, location, and nominal payload wrapping."
+ "sha256": "ce42e38f893459b9cb0ed6d779fabd5818204301ab1acc13ab0a7df32e6e3a2d",
+ "role": "RuntimeValue and ArtifactKey construction from compiled output plans."
},
{
- "path": "openhcs/core/runtime_stores.py",
- "sha256": "5f323ec8edd4221ee30e34ed0c9c09e0f4e45ef502749c1a2d331b7a5452af99",
- "role": "RuntimeValueStore recording, replacement, query, and observation semantics."
+ "path": "openhcs/core/runtime_plane_projection.py",
+ "sha256": "cf644451b6649dcec398fecbf961a4a29ea74f0bb2e0bdab052ec1be8a427f95",
+ "role": "Declares RuntimeSliceProjectableValue and its indexed, invariant and identity-stamped subclasses that payload authors join."
},
{
"path": "openhcs/core/runtime_slice_projection.py",
- "sha256": "8a1cb65d3025ed8a87ee59af26deac9765a8f59b21bf2cd61b6f96d82999641d",
- "role": "Nominal runtime slice projection registry and aggregation behaviour."
+ "sha256": "f59785d901d0d7bcc3ac1fc1be606bd1728a727ea7347cdc1dd4f549cc3193fa",
+ "role": "RuntimeSliceProjection foreign-value list and rejection of owned values without slice declarations."
+ },
+ {
+ "path": "openhcs/core/runtime_stores.py",
+ "sha256": "35fe71906bfd16842de347776a0c5cc846550ad490cc3628885129d746ec6f31",
+ "role": "RuntimeValueStore recording, replacement, query, and observation semantics."
},
{
"path": "tests/unit/test_runtime_value_store.py",
- "sha256": "8900a7b83b59d0a40f9698dbafaaba778df982f9d7a0b628f80eab97335aeab0",
+ "sha256": "17e15a64a4e84437f6523f872bad11ea7d813c50c1c41fc86ca7822379a7ce27",
"role": "Regression proof for typed runtime recording, replacement, queries, and failure behaviour."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/runtime_value_extension.rst in full and compared it against the changed authorities: openhcs/core/runtime_artifact_values.py (payload_of), openhcs/core/runtime_plane_projection.py (stored_slices, match_alignment, contextualized_output and axis-size checks on the value classes), openhcs/core/runtime_slice_projection.py, openhcs/core/runtime_stores.py and tests/unit/test_runtime_value_store.py (K2 artifact package imports); RuntimeSliceProjectableValue, RuntimeSliceIndexedValue and RuntimeSliceInvariantValue still exist, the added behaviour sits on the payload classes as the page says, and RuntimeSliceProjection.require_foreign_value still rejects undeclared values, so nothing was changed.",
+ "On 2026-10-11 (B6), re-read docs/source/development/runtime_value_extension.rst in full and compared it against the changed authorities: openhcs/core/runtime_stores.py and tests/unit/test_runtime_value_store.py (hand-written to_dict/from_dict on RuntimeArtifactLocation and RuntimeArtifactAddress deleted; round trips now use python_introspect to_jsonable and dataclass_from_mapping; CompiledSourceBindingPlan() replaces .empty()). The page names none of those methods and its 'add transport only where needed' guidance still holds; rechecked RuntimeSliceProjectableValue and its indexed/invariant subclasses, RuntimeSliceProjection and RuntimeValueStore; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/runtime_value_extension.rst against B6's later changes to openhcs/core/runtime_stores.py, tests/unit/test_runtime_value_store.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/runtime_value_extension.rst against the later B6 changes to openhcs/core/runtime_stores.py, tests/unit/test_runtime_value_store.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/runtime_value_extension.rst in full and compared it against the changed authorities: runtime_artifact_values.py, runtime_stores.py, runtime_slice_projection.py, tests/unit/test_runtime_value_store.py; replaced the deleted RuntimeSliceProjectionStrategy registration step with the RuntimeSliceProjectableValue base classes from runtime_plane_projection.py, removed the MRO-selection and aggregation claims, and confirmed RuntimeValue, ArtifactKey, separate RuntimeArtifactLocation and fail-closed store queries remain as described.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"tests/unit/test_runtime_value_store.py and tests/unit/test_runtime_slice_projection.py passed",
"On 2026-08-24, re-read the complete page against the changed declaration, dependency, UI state, knowledge, and test authorities named by this record; the documented behavior and ownership boundaries remain accurate."
@@ -3890,7 +4222,7 @@
},
{
"path": "docs/source/development/source_binding_extension.rst",
- "source_sha256": "a1b709b00dca507f7183aa847d208192d6f79f2eba8cf5f452cec37b9510a63d",
+ "source_sha256": "9f62eb7718bf83ef1bb7ef5207e4ccbf60667b35ea30934442b5a1b443a39f12",
"audience": [
"OpenHCS source-ingestion and compiler extension authors"
],
@@ -3899,30 +4231,50 @@
"authority": [
{
"path": "openhcs/core/source_bindings.py",
- "sha256": "e0afbbe40cce3847bcc2314910d0083ecc579a5a0d6e4278c3a5df4c62e7eb01",
- "role": "Public typed source-binding declaration vocabulary."
+ "sha256": "890473715373d1adc6b12fec2b57eab6f179cbca7d837920c0388d620ce8b8c2",
+ "role": "Declares SourceFilterClause, MetadataExtractionRule, NamedSourceBinding, ImagePlaneSource, the SourceFilterMatchType predicate family, the SourceSetPairing family (PairByOrder, PairByMetadata) and the compiled source universe and binding plans."
},
{
- "path": "openhcs/core/source_binding_workspace.py",
- "sha256": "7dce6e45b9e846713b246561340d43de378e251e35f6ef68d5527fccc1b31ad3",
- "role": "Compiled virtual source workspace and binding projection."
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares SourceBindingsConfig and StepSourceBindingsConfig as global_pipeline_config sections, where the step config compiles its own binding plan."
},
{
- "path": "openhcs/core/pipeline/path_planner.py",
- "sha256": "0ed8bb8d843ff41382f4de25367a74c9535a39425c6fc328091ad3025d526151",
- "role": "Compiled source satisfaction, artifact edge, path, grouping, and plan decisions."
+ "path": "openhcs/core/source_binding_selection.py",
+ "sha256": "e837296accf4dd3353956a8e5f267d3fdd4581fb01db1227bc592a2ed6b4d0d3",
+ "role": "SourceUniverseRequest family, keyed by SourceBindingOrigin class, that selects source universes."
},
{
- "path": "external/PolyStore/src/polystore/filemanager.py",
- "sha256": "877a2b10731b12904b1784a5b42c9e39688e3a84f7f86018c1c2beee96ea2296",
- "role": "Generic FileManager storage and backend composition."
+ "path": "openhcs/core/source_matching.py",
+ "sha256": "cea83c632056420b4e2affac25f9da37c3a4ee41878e2a1fb18b6747d8573dcf",
+ "role": "Applies filter clauses and metadata extraction rules to candidate paths and derives image-set identity from source declarations."
+ },
+ {
+ "path": "openhcs/core/source_workspace_projection.py",
+ "sha256": "1f8785fec7919f52b69578bbd6f6b5f94be8b2f9e97d11d00f639f3f70bcfe23",
+ "role": "VirtualWorkspaceSourceProjection mapping virtual paths to SourcePixelRef and source metadata."
+ },
+ {
+ "path": "openhcs/core/source_binding_workspace.py",
+ "sha256": "e242d9b932d52c1099756881870f926a273f5ec08663938347580cb4a70e8283",
+ "role": "Compiled virtual source workspace and the source-set assemblers keyed by SourceSetPairing class."
+ },
+ {
+ "path": "external/PolyStore/src/polystore/virtual_workspace.py",
+ "sha256": "b19fcda85369d812e70bfc1a0efc5d8861ae256f69e94372e2e9515c591d36af",
+ "role": "PolyStore declaration of SourcePixelRef and generic virtual-workspace addressing."
}
],
"findings": [
- "The how-to did not require proof that a raw component selector may differ from the projected biological component identity."
+ "E1 deleted the SourceFilterMatcher strategy family from openhcs/core/source_matching.py; filter predicates are now the SourceFilterMatchType class family in openhcs/core/source_bindings.py. The strategy-selection paragraph still named SourceFilterMatcher as a family to extend, so it now names SourceFilterMatchType."
],
"disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/development/source_binding_extension.rst in full and compared it against the changed authorities: openhcs/core/source_bindings.py, openhcs/core/source_matching.py, openhcs/core/source_binding_selection.py, openhcs/core/source_binding_workspace.py, openhcs/core/source_workspace_projection.py and openhcs/core/config.py (E1: the source-binding kinds became class families); replaced the deleted SourceFilterMatcher with SourceFilterMatchType in the strategy-selection paragraph and re-wrapped it; confirmed SourceUniverseRequest (now keyed by SourceBindingOrigin class), SourceSetPairing, the compiled plan classes and VirtualWorkspaceSourceProjection still exist at their owners.",
+ "On 2026-10-11 (B6), re-read docs/source/development/source_binding_extension.rst in full and compared it against the changed authorities: openhcs/core/source_bindings.py, source_binding_selection.py, source_matching.py, source_workspace_projection.py and source_binding_workspace.py (K4: config classes moved to core/config.py, SourceSetPairing family replaces the match-method enum, CompiledSourceBindingPlan()/CompiledSourceUniversePlan() replace .empty(), one lexical path identity). Confirmed every class the page names exists at its current owner, added SourceSetPairing to the strategy families, and added core/config.py as the config-class authority.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/source_binding_extension.rst against B6's later changes to openhcs/core/source_binding_selection.py, openhcs/core/source_workspace_projection.py, openhcs/core/source_binding_workspace.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/development/source_binding_extension.rst against the later B6 changes to openhcs/core/source_bindings.py, openhcs/core/source_binding_selection.py, openhcs/core/source_workspace_projection.py, openhcs/core/source_binding_workspace.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/development/source_binding_extension.rst in full and compared it against the changed authorities: source_bindings.py (runtime-context and path-lookup classes removed), source_binding_workspace.py, pipeline/path_planner.py, PolyStore filemanager.py; confirmed every named declaration and compiled plan class still exists, rewrote the strategy-selection paragraph around the current SourceUniverseRequest / SourceFilterMatcher families, changed microscope metadata to dataset source metadata, and moved the PolyStore authority to virtual_workspace.py where SourcePixelRef is declared; path_planner.py and filemanager.py were dropped as the page states no fact they own.",
"scripts/validate_docs.py validated the final RST source and its concrete repository paths",
"tests/unit/test_source_bindings.py and tests/unit/test_source_projection.py passed; source inspection confirmed both compiled source plan types",
"On 2026-08-22, re-read the complete how-to against the current source-binding and workspace owners; the focused OpenHCS documentation-drift suite passed 238 tests.",
diff --git a/docs/audits/project_surface.json b/docs/audits/project_surface.json
index ab29a11a7f..3df00aad34 100644
--- a/docs/audits/project_surface.json
+++ b/docs/audits/project_surface.json
@@ -1,7 +1,7 @@
[
{
"path": "README.md",
- "source_sha256": "1a600010deb7ddc3fe141b5efe3528bbe4a4e119846157e935e56784628445ed",
+ "source_sha256": "6eda2fe873aaecfb28e60fa4a7c3ba4f6a33bf3f19d79961149b735d21572b7b",
"audience": [
"imaging scientists evaluating OpenHCS",
"pipeline authors",
@@ -12,8 +12,8 @@
"authority": [
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
- "role": "Declares the published README, Python range, package dependencies, optional surfaces, console commands, and project links."
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
+ "role": "Declares the published README, Python range, package dependencies, optional extras, console commands, and project links."
},
{
"path": ".gitmodules",
@@ -21,93 +21,106 @@
"role": "Declares the eight extracted package checkouts composed by the source repository."
},
{
- "path": "openhcs/desktop_installation.py",
- "sha256": "244ade0711356f68df9f376e2517dfe1c9fb77cc960c517f7c4e8ee418b5711f",
- "role": "Nominally declares the CPU-safe desktop extras and native installation policy without copying project or brand declarations."
+ "path": "openhcs/desktop/installation.py",
+ "sha256": "b2124aaa3e4ef4a310f91a51c1879de41de1818b25fc619cc7cdad888c4202b6",
+ "role": "Declares the CPU-safe desktop package extras selected by the graphical installers."
},
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
"role": "Owns the validated Python pipeline document shared by visual, code, and agent authoring."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Owns compile-time resolution across selected axes and construction of the execution bundle."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Owns compile-time resolution across selected axes, compile progress emission, and construction of the execution bundle."
},
{
- "path": "openhcs/core/artifacts.py",
- "sha256": "bb958c74c98cb96c97c5ac1ea68ac997673aac6afcad57bdf0c9496065e895fa",
- "role": "Declares image, object, measurement, relationship, table, and file artifact semantics used by materialization."
+ "path": "openhcs/core/pipeline/compilation_session.py",
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "Declares ResolvedPipelineDefinition and CompilationSession named in the compilation diagram."
},
{
- "path": "openhcs/constants/constants.py",
- "sha256": "4cd6c29abd8b8e3ecc396b424d2b8b61e819a215fc1d770d3f6852b260a0691d",
- "role": "Declares supported microscope source identities, processing axes, memory types, and file formats."
+ "path": "openhcs/core/progress/types.py",
+ "sha256": "c46e825cd32801caae9fc107e0759976da054eadc56adc0d13354b3a8b7db209",
+ "role": "Declares set_progress_queue, which compilation requires before it emits progress events."
+ },
+ {
+ "path": "openhcs/core/dataset_sources/source.py",
+ "sha256": "b3758a878fdf8ec28b6d9818ebfd96c5c470895575f7a79500c85f1490679281",
+ "role": "Declares the auto-registered DatasetSource family that the ImageXpress, Opera Phenix, Bio-Formats, OMERO and OpenHCS-format sources join."
},
{
"path": "openhcs/processing/backends/lib_registry/unified_registry.py",
- "sha256": "70462cddcad1bfee447ce0786a9e4a85abbd297726d59a1208156af0ef3a3743",
- "role": "Owns declaration-based callable discovery, execution memory contracts, dimensional execution, and registry caching."
+ "sha256": "b1e9253021b9c3ab14a070b7d0f10909f641ba07784c794ad95d2ce36041b1d3",
+ "role": "Owns declaration-based callable discovery, execution memory contracts, processing contracts, and registry caching."
},
{
"path": "openhcs/core/viewer_streaming_service.py",
- "sha256": "46982fd46f47b962033e02d82afb6c3ce2abbafdabfbce99e591d9c6dff2fbab",
+ "sha256": "50742cdcb589dc9fba5a177c0107c046dd63c518c5aa77bd8a80e4f69e898e07",
"role": "Owns manual and orchestrated image and ROI streaming through registered viewer declarations."
},
{
"path": "openhcs/agent/capabilities.py",
- "sha256": "7fe9ab91490dedd6f750a57386e91835cf1067a27d10079966510aa9821adaf0",
- "role": "Declares the MCP discovery, authoring, execution, UI, and viewer-review capabilities and their surface policies."
+ "sha256": "87c8e2e5aeb425b9a2eb690d48bdd6f5af7a327c389deac261d7e4e0da5d609f",
+ "role": "Declares the MCP capability profiles, including the full profile and governed custom-function registration."
},
{
"path": "openhcs/mcp/client_registration.py",
- "sha256": "28459b95d3b155778ce178f36302cb830958b74f2603980641ec492d81c41a9a",
- "role": "Declares supported local MCP client targets and stable-launcher registration behaviour."
+ "sha256": "4d6fc5f744617ab1d0e9b8317d07965496aaa6b098a4d179ef95cc6baec35671",
+ "role": "Declares supported local MCP client targets such as ChatGPT desktop/Codex and Claude Desktop."
},
{
"path": "openhcs/interop/cellprofiler/pipeline_import.py",
- "sha256": "b547c838b2c86fe7cc5fe12d6cbf6a6715e83055c2d8c1a109ac22566d22c51e",
- "role": "Owns lowering CellProfiler cppipe modules into ordinary OpenHCS pipeline declarations."
+ "sha256": "5aa8895c9f533603b7ef972202141e2439473f0604b97d66f5a027b8b3cd1c83",
+ "role": "Declares import_cellprofiler_pipeline and its returned FunctionStep list and PipelineConfig."
},
{
"path": "tests/integration/test_cellprofiler_official30_zmq.py",
- "sha256": "bc5abfddf076996a050b0601c46b6039d2d812ab57cd303847b241e477176f53",
+ "sha256": "e9f346e25a9fbb1ec7edf9e6e19c6d51c7d2000347ee61ef41b3017244322589",
"role": "Exercises all 30 declared CellProfiler comparison cases over ZMQ and validates live Napari and Fiji projections."
},
{
"path": "benchmark/cellprofiler_benchmark_cli.py",
- "sha256": "2b92755c556c1d66d28ffa0af6e2ac61cac5af4631514382290a2af0f907c26b",
- "role": "Owns the installed registry-derived benchmark execution and report command surface."
+ "sha256": "56a9b4e8bb7d8195095eb99a849f717e79ca37aa429c612804584cbd7939f420",
+ "role": "Declares the openhcs-benchmark subcommands and options, including run-measured through the OpenHCS session."
},
{
- "path": "benchmark/contracts/run_receipt.py",
- "sha256": "1704e1ab47411d25d382b95871c96c1e60c94db14b8ffd55f4dc6c8a917758b9",
- "role": "Owns typed run provenance, lifecycle status, exact rerun arguments, and receipt validation."
+ "path": "benchmark/mcp_extension.py",
+ "sha256": "056ab5834fa3c37df1a36a37d80f2a821fb4b59c39892d7737e62fa6a57cb53b",
+ "role": "Declares the expert benchmark MCP tool names, including the session-backed measured-run finalizer."
},
{
- "path": "benchmark/contracts/control.py",
- "sha256": "0bce24c29ea9dfb7b50c88937b616118078505db6f497312fff87766397e5ce9",
- "role": "Declares the typed benchmark inspection request and result exposed through MCP."
+ "path": "benchmark/contracts/run_receipt.py",
+ "sha256": "4cd61bd33f8f6199b3cc4130ed091dddde9c3a23975e132b1bee06c3c6a43cd7",
+ "role": "Owns typed run provenance, lifecycle status, exact rerun arguments, and receipt validation."
},
{
"path": "benchmark/control.py",
- "sha256": "4706078a8fbeb516a52bc51600751ae86c39c3ef7e397a2f8cf13d49204db941",
+ "sha256": "b10dd87d7bc44063566e0c53d849091c0fc43a345b434c13467df1ede86ac892",
"role": "Owns read-only inspection of receipts, observation progress, and structured artifacts."
},
{
"path": "tests/unit/test_benchmark_control.py",
- "sha256": "ee75209799216c90cfa3a585dd9b7af99c99405f93e9fb5e4f900ea26d9d3ea0",
+ "sha256": "093b584eeb37cbb12089d5af082ad45acd779e9d2bdd5b3feaea08234fc22183",
"role": "Proves the installed command derivation, typed receipts, MCP projection, path policy, and inspect-only import boundary."
+ },
+ {
+ "path": "openhcs/core/artifacts/kinds.py",
+ "sha256": "216b913efad9ec76707e9f61be445fb6fa1c6601c36ef53e296acd4928604515",
+ "role": "Declares the image, object-label, measurement, relationship, table, spatial and metadata artifact kinds used by materialization."
}
],
- "findings": [
- "The agent-workflow capability still claimed that the removed embedded UI assistant could use Ollama or remote LLM endpoints.",
- "The root README is the package-index long description but was outside the editorial drift gate that covered active RTD pages.",
- "The package overview mentioned Official30 evidence but did not identify the installed benchmark command, typed run receipt, or the expert MCP inspect-only boundary."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared README.md against benchmark/cellprofiler_benchmark_cli.py: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6), re-read README.md in full and compared it against the changed authorities: pyproject.toml (python-introspect floor 0.2.8), openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/artifacts.py (K2 split it into the openhcs/core/artifacts package), openhcs/core/dataset_sources/source.py, openhcs/core/viewer_streaming_service.py, openhcs/mcp/client_registration.py, openhcs/interop/cellprofiler/pipeline_import.py, benchmark/cellprofiler_benchmark_cli.py, benchmark/control.py; E1 turned OrchestratorState, MaterializationBackend, InputSource, the client-registration status and the observation export scope into class families, but the README names none of the old enum members and the `--observation-scope outcomes` spelling is still the registered name, so no claim changed. Replaced the deleted artifacts.py authority with openhcs/core/artifacts/kinds.py.",
+ "On 2026-10-11 (B6), re-read README.md in full and compared it against the changed authorities: pyproject.toml (zmqruntime 0.6.2, pycodify 0.2, objectstate 2.1.1, python-introspect 0.2.7, pyqt-reactive 0.5 floors), openhcs/core/pipeline_document.py (renders through pycodify.generate_python_source), openhcs/core/pipeline/compiler.py and compilation_session.py (ScopeKey ids, progress classes, disabled-function stripping removed), openhcs/core/progress/types.py (K6: ProgressPhase/ProgressStatus enums became class families; set_progress_queue unchanged and still exported from openhcs.core.progress), openhcs/core/dataset_sources/source.py (SourceBindingsConfig import moved) and pipeline_import.py (K4 import moves). The README states no dependency version or phase name; the CellProfiler example's imports (ensure_global_config_context, set_progress_queue, import_cellprofiler_pipeline with source_root, PipelineOrchestrator.initialize/compile_pipelines) and the compilation diagram's class names all still resolve, so no claim changed.",
+ "On 2026-10-11 (B6 final), compared README.md against B6's later changes to tests/integration/test_cellprofiler_official30_zmq.py, benchmark/cellprofiler_benchmark_cli.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared README.md against B6's later changes to openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/progress/types.py, openhcs/core/artifacts.py, openhcs/core/dataset_sources/source.py, openhcs/processing/backends/lib_registry/unified_registry.py, openhcs/core/viewer_streaming_service.py, openhcs/interop/cellprofiler/pipeline_import.py, benchmark/cellprofiler_benchmark_cli.py, tests/unit/test_benchmark_control.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared README.md against the later B6 changes to pyproject.toml, openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/progress/types.py, openhcs/core/artifacts.py, openhcs/core/dataset_sources/source.py, openhcs/processing/backends/lib_registry/unified_registry.py, openhcs/core/viewer_streaming_service.py, openhcs/agent/capabilities.py, openhcs/interop/cellprofiler/pipeline_import.py, tests/integration/test_cellprofiler_official30_zmq.py, benchmark/cellprofiler_benchmark_cli.py, tests/unit/test_benchmark_control.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read README.md in full and compared it against the changed authorities: pyproject.toml, openhcs/desktop_installation.py (moved to openhcs/desktop/installation.py), openhcs/core/pipeline_document.py, openhcs/core/pipeline/compiler.py, openhcs/core/artifacts.py, openhcs/constants/constants.py, unified_registry.py, viewer_streaming_service.py, openhcs/agent/capabilities.py, openhcs/mcp/client_registration.py, pipeline_import.py, the Official30 integration test and the benchmark CLI/receipt/control modules; installed a progress queue in the CellProfiler example and renamed its root to dataset_root, rewrote the run-measured and finalizer paragraphs for the session-backed run, replaced microscope-handler wording with dataset sources, replaced the deleted StepSnapshot with ResolvedPipelineDefinition in the validation card and compilation diagram, and confirmed extras, console scripts, benchmark subcommands/options, MCP tool names, the full profile, client targets, and the eight submodules against current source.",
"Re-read README.md in full and challenged its explanation role, audience, capability claims, installation routes, package boundaries, flow, and links against the hash-pinned declaration owners.",
"Removed the obsolete embedded-assistant claim while preserving the MCP-first agent workflow.",
"Passed 12 focused documentation-audit tests, including missing, changed, and Python-block-validated project README regressions.",
diff --git a/docs/audits/user_docs.json b/docs/audits/user_docs.json
index 7a7ea449d9..7b30caffa3 100644
--- a/docs/audits/user_docs.json
+++ b/docs/audits/user_docs.json
@@ -1,7 +1,7 @@
[
{
"path": "docs/source/api/index.rst",
- "source_sha256": "68401481faeeb4745afa97a50248df8533cfdb6ca0aa09a94557bae2cc4ef57c",
+ "source_sha256": "0d2ac4119c086b8fde3f738f0937145feba52c6a0c8d18b1fbce7f1f51012fa2",
"audience": [
"Python integrators",
"application developers"
@@ -11,45 +11,53 @@
"authority": [
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares PipelineConfig, GlobalPipelineConfig, ProcessingConfig and the lazy processing configuration used by the step example."
},
{
"path": "openhcs/core/steps/function_step.py",
"sha256": "8e1243404095691030e9ebb95bb5611679f96c66c6b6819ae71e4b41249e74bc",
"role": "Declares FunctionStep and its callable, chain, and dictionary composition forms."
},
+ {
+ "path": "openhcs/domains/microscopy/axes.py",
+ "sha256": "531695809259324032e0b88632813323b4fe6a9a176ad6f636aeef8dec332399",
+ "role": "Declares the microscopy axis family members such as Microscopy.Site used as variable components."
+ },
{
"path": "openhcs/core/orchestrator/orchestrator.py",
- "sha256": "843e994d7b16592f43a15ce36c33a51ead534cf22df792838ce2b800621114b7",
- "role": "Owns execution orchestration, worker submission, progress handling, and cleanup of compiled bundles."
+ "sha256": "a7097eec86c5966552bcb546a2161d1a08e6a4c630e6b156de3b8354b0026b54",
+ "role": "Declares the PipelineOrchestrator constructor, compile_pipelines, progress_context and execute_compiled_bundle signatures."
},
{
- "path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
- "role": "Declares the frozen compiled execution bundle consumed by workers after compilation."
+ "path": "openhcs/core/progress/types.py",
+ "sha256": "c46e825cd32801caae9fc107e0759976da054eadc56adc0d13354b3a8b7db209",
+ "role": "Declares ProgressQueue, set_progress_queue and ProgressExecutionContext, which compilation and execution emit through."
},
{
- "path": "tests/unit/test_compiled_execution.py",
- "sha256": "a7ef4c3f011e0770d8e7a48928aa25c6ca550095c1f80146a25170d449ecc31f",
- "role": "Proves compilation produces immutable step plans and execution bundles with the expected resolved values."
+ "path": "openhcs/core/compiled_execution.py",
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "Declares the CompiledExecutionBundle returned by compilation."
},
{
"path": "openhcs/interop/cellprofiler/pipeline_import.py",
- "sha256": "b547c838b2c86fe7cc5fe12d6cbf6a6715e83055c2d8c1a109ac22566d22c51e",
- "role": "Owns the supported CellProfiler pipeline import boundary demonstrated by this API orientation."
+ "sha256": "5aa8895c9f533603b7ef972202141e2439473f0604b97d66f5a027b8b3cd1c83",
+ "role": "Declares import_cellprofiler_pipeline and its returned steps and PipelineConfig."
},
{
"path": "tests/unit/test_cellprofiler_pipeline_import.py",
- "sha256": "353f0c1d8e0bc3552d2e418d792794ab0631daa16c78a3e10191b1cc1ea07d73",
+ "sha256": "67576f37b7cbe0595acea633265f8e14677ca3c1b8204630413330273dc4e58a",
"role": "Proves direct import rejects modules requiring interactive desktop input instead of omitting them."
}
],
- "findings": [
- "The API reference did not state that CellProfiler modules requiring interactive desktop input are rejected instead of being silently omitted from the imported pipeline."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/api/index.rst in full and compared it against the changed authorities: openhcs/core/config.py, openhcs/core/orchestrator/orchestrator.py, openhcs/interop/cellprofiler/pipeline_import.py, tests/unit/test_cellprofiler_pipeline_import.py; E1 made MaterializationBackend, InputSource and OrchestratorState class families, but the examples use only ProcessingConfig(variable_components=...), LazyProcessingConfig.from_config, the PipelineOrchestrator constructor, initialize, compile_pipelines, progress_context and execute_compiled_bundle, and import_cellprofiler_pipeline, whose signatures and import locations are unchanged, so no claim changed.",
+ "On 2026-10-11 (B6), re-read docs/source/api/index.rst in full and compared it against the changed authorities: openhcs/core/config.py (K4 source-binding classes moved in, PartitionFilterMode became a class family), orchestrator.py (source_bindings_defaults_to_base now imported from config), progress/types.py (K6 phase/status/stream class families), pipeline_import.py and its test (K4 import moves, PairByOrder). The page's two examples still match: ProcessingConfig(variable_components=(Microscopy.Site,)), LazyProcessingConfig.from_config, import_cellprofiler_pipeline(..., source_root=...), set_progress_queue, compile_pipelines and execute_compiled_bundle(execution_bundle=, progress_queue=, progress_context=) with ProgressQueue and ProgressExecutionContext unchanged, so no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/api/index.rst against B6's later changes to openhcs/core/config.py, openhcs/domains/microscopy/axes.py, openhcs/core/orchestrator/orchestrator.py, openhcs/core/progress/types.py, openhcs/interop/cellprofiler/pipeline_import.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/api/index.rst against the later B6 changes to openhcs/core/config.py, openhcs/domains/microscopy/axes.py, openhcs/core/orchestrator/orchestrator.py, openhcs/core/progress/types.py, openhcs/interop/cellprofiler/pipeline_import.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/api/index.rst in full and compared it against the changed authorities: openhcs/core/config.py, openhcs/core/orchestrator/orchestrator.py, openhcs/core/compiled_execution.py, tests/unit/test_compiled_execution.py, openhcs/interop/cellprofiler/pipeline_import.py, tests/unit/test_cellprofiler_pipeline_import.py; rewrote the compile/execute example to install a progress queue, call execute_compiled_bundle with orchestrator.progress_context(), and name the constructor argument dataset_root, matching tests/unit/remote_sensing_witness_pipeline.py; confirmed the FunctionStep/ProcessingConfig example, the import_cellprofiler_pipeline signature and the interactive-module rejection against current source.",
"Re-read docs/source/api/index.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/config.py; openhcs/core/steps/function_step.py; openhcs/core/orchestrator/orchestrator.py; openhcs/core/compiled_execution.py; tests/unit/test_compiled_execution.py; openhcs/interop/cellprofiler/pipeline_import.py; tests/unit/test_cellprofiler_pipeline_import.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -63,7 +71,7 @@
},
{
"path": "docs/source/appendices/glossary.rst",
- "source_sha256": "0d2911da50a8546e893ffbf0091415ae995700bef3f271170cd8eafb7abffb85",
+ "source_sha256": "09220b13b10a0ec98237a505e09077d6c41ae85e7fa5ad4b57a61673867008ee",
"audience": [
"OpenHCS users",
"contributors"
@@ -72,44 +80,64 @@
"diataxis": "reference",
"authority": [
{
- "path": "openhcs/core/artifacts.py",
- "sha256": "bb958c74c98cb96c97c5ac1ea68ac997673aac6afcad57bdf0c9496065e895fa",
- "role": "Declares the nominal artefact types exchanged by compiled steps and runtime consumers."
+ "path": "openhcs/core/compiled_step_plan.py",
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "Declares the per-step compiled plan read by runtime code."
},
{
- "path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
- "role": "Declares typed per-step memory, conversion, framework-device assignment, and runtime decisions."
+ "path": "openhcs/core/compiled_execution.py",
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "Declares CompiledExecutionBundle, the worker-safe compiled contexts consumed by bundle execution."
},
{
"path": "openhcs/core/pipeline/compilation_session.py",
- "sha256": "3c840a89baccc8665d415175458280fd904c1a837b7fd879e303d7dbca129a1c",
- "role": "Owns compilation-scoped state and resolves declarations into frozen plans without leaking runtime values."
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "Owns compilation-scoped source, artifact and plan state."
},
{
"path": "openhcs/core/runtime_stores.py",
- "sha256": "5f323ec8edd4221ee30e34ed0c9c09e0f4e45ef502749c1a2d331b7a5452af99",
+ "sha256": "35fe71906bfd16842de347776a0c5cc846550ad490cc3628885129d746ec6f31",
"role": "Implements scoped typed runtime-value storage and lookup for compiled execution."
},
{
"path": "openhcs/core/source_bindings.py",
- "sha256": "e0afbbe40cce3847bcc2314910d0083ecc579a5a0d6e4278c3a5df4c62e7eb01",
- "role": "Declares source bindings that map logical pipeline inputs to microscope-backed workspaces."
+ "sha256": "890473715373d1adc6b12fec2b57eab6f179cbca7d837920c0388d620ce8b8c2",
+ "role": "Declares NamedSourceBinding and the source-assignment family that connect callable inputs to sources, plus CompiledSourceUniversePlan."
},
{
"path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
- "role": "Declares the compiler-visible callable metadata and contract terms defined by the glossary."
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "Declares CallableDeclaration and CallableContract fields: memory types, artifact inputs/outputs, runtime-bound parameters, required axis roles and execution scope."
},
{
- "path": "openhcs/processing/backends/lib_registry/unified_registry.py",
- "sha256": "70462cddcad1bfee447ce0786a9e4a85abbd297726d59a1208156af0ef3a3743",
- "role": "Declares the nominal processing-contract members defined by the glossary."
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "Declares the ProcessingContract family with Pure2DContract and Pure3DContract."
+ },
+ {
+ "path": "openhcs/core/axes.py",
+ "sha256": "8d93133639c576050f089fb8aab99937c8982aff07e742fea44f8a12e62bb7b2",
+ "role": "Declares the PartitionAxis role and the axis family that names the partition axis."
+ },
+ {
+ "path": "openhcs/core/dataset_sources/source.py",
+ "sha256": "b3758a878fdf8ec28b6d9818ebfd96c5c470895575f7a79500c85f1490679281",
+ "role": "Declares DatasetSource with detection, parsing, metadata, virtual-workspace and backend behaviour."
+ },
+ {
+ "path": "openhcs/core/artifacts/kinds.py",
+ "sha256": "216b913efad9ec76707e9f61be445fb6fa1c6601c36ef53e296acd4928604515",
+ "role": "Declares the ArtifactType family: image, object-label, measurement, relationship, table and spatial-graph kinds."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/appendices/glossary.rst in full and compared it against the changed authorities: openhcs/core/artifacts.py (K2 split it into the openhcs/core/artifacts package), openhcs/core/compiled_step_plan.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/runtime_stores.py, openhcs/core/source_bindings.py (E1 source-binding kinds became families), openhcs/core/callable_contract.py, openhcs/core/dataset_sources/source.py; every artifact kind the Artifact entry lists is still a registered ArtifactType, NamedSourceBinding and CompiledSourceUniversePlan still exist, and no entry names a removed enum member, so no definition changed. Replaced the deleted artifacts.py authority with openhcs/core/artifacts/kinds.py.",
+ "On 2026-10-11 (B6), re-read docs/source/appendices/glossary.rst in full and compared it against the changed authorities: compiled_step_plan.py and runtime_stores.py (source plans default-construct; RuntimeArtifactLocation/Address dict codecs removed), compilation_session.py, source_bindings.py (K4: SourceBindingsConfig moved to config.py, match methods became the SourceSetPairing family), callable_contract.py (K5: metadata wrapper flattened into CallableDeclaration fields), processing_contracts.py and dataset_sources/source.py. The glossary defines terms without naming moved classes; Pure2DContract, Pure3DContract, PartitionAxis, DatasetSource, CompiledSourceUniversePlan and the callable-contract fields it lists still exist, so no claim changed. Sharpened the source_bindings and callable_contract roles to what they now declare.",
+ "On 2026-10-11 (B6 final), compared docs/source/appendices/glossary.rst against B6's later changes to openhcs/core/artifacts.py, openhcs/core/compiled_step_plan.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/runtime_stores.py, openhcs/core/callable_contract.py, openhcs/core/processing_contracts.py, openhcs/core/axes.py, openhcs/core/dataset_sources/source.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/appendices/glossary.rst against the later B6 changes to openhcs/core/artifacts.py, openhcs/core/compiled_step_plan.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/runtime_stores.py, openhcs/core/source_bindings.py, openhcs/core/callable_contract.py, openhcs/core/processing_contracts.py, openhcs/core/axes.py, openhcs/core/dataset_sources/source.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/appendices/glossary.rst in full and compared it against the changed authorities: openhcs/core/artifacts.py, compiled_step_plan.py, pipeline/compilation_session.py, runtime_stores.py, source_bindings.py, callable_contract.py, openhcs/processing/backends/lib_registry/unified_registry.py, plus the G1/G4/G5/G7 owners; rewrote the Processing contract, Dataset source (was Microscope handler), Partition axis (was Execution axis), Compiled step plan, Compiled execution bundle and Source workspace projection entries, and confirmed the remaining terms (FunctionStep, CompiledExecutionBundle, SourceUniverse, RuntimeAdapterSpec, CellProfilerModuleArtifactContracts, variable_components, group_by) against current source.",
"On 2026-08-27, re-read docs/source/appendices/glossary.rst in full against callable-owned form exclusions and registry-owned non-authorable controls; challenged its reference definitions for accuracy, completeness, owner boundaries, and navigation, and found no prose drift.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/artifacts.py; openhcs/core/compiled_step_plan.py; openhcs/core/pipeline/compilation_session.py; openhcs/core/runtime_stores.py; openhcs/core/source_bindings.py; openhcs/core/callable_contract.py; openhcs/processing/backends/lib_registry/unified_registry.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -133,19 +161,22 @@
"diataxis": "reference",
"authority": [
{
- "path": "openhcs/core/artifacts.py",
- "sha256": "bb958c74c98cb96c97c5ac1ea68ac997673aac6afcad57bdf0c9496065e895fa",
- "role": "Declares the nominal artefact types exchanged by compiled steps and runtime consumers."
+ "path": ".readthedocs.yaml",
+ "sha256": "bd5346a882747235c8202c2de4ecfc8c9902accc1cf1534a55d1d1c7a93c373c",
+ "role": "Declares the Read the Docs build that publishes the appendix navigation from docs/source/conf.py."
},
{
"path": "docs/source/conf.py",
"sha256": "37c6c130d75662601d1e0fece5e0f206b7bf366ae04b596ae39aa9f691ad1c94",
- "role": "Registers the active Sphinx extensions and documentation build configuration that expose the appendix navigation."
+ "role": "Registers the Sphinx configuration whose toctree resolution exposes the glossary entry."
}
],
- "findings": [],
+ "findings": [
+ "The record cited openhcs/core/artifacts.py, which states no fact on this navigation-only page; replaced it with the Read the Docs build declaration."
+ ],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/appendices/index.rst in full and compared it against the changed authorities: openhcs/core/artifacts.py; the page is a single-entry toctree to the glossary and makes no artefact claim, so the text stays and the artefact module was dropped from its authorities in favour of .readthedocs.yaml.",
"Reviewed docs/source/appendices/index.rst in full; the recorded findings and disposition are the resulting page-specific editorial decision.",
"Compared its factual claims directly with the hash-pinned files listed in authority: openhcs/core/artifacts.py; docs/source/conf.py.",
"Passed `.venv/bin/python scripts/validate_docs.py docs/source/appendices`: documentation validation passed: 3 files, 0 Python blocks.",
@@ -164,42 +195,42 @@
"user_need": "Understand why unsupported impact and performance claims are not published as current product facts.",
"diataxis": "explanation",
"authority": [
- {
- "path": "benchmark/contracts/pipeline.py",
- "sha256": "54857ee11404bdc510478e53873b359d05f4a1a103d915626c27acfb4884946c",
- "role": "Declares the benchmark pipeline specification used to represent reproducible workload definitions."
- },
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
- "role": "Declares the package version, Python requirement, dependency extras, scripts, and distribution metadata."
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
+ "role": "Declares the packaged openhcs-benchmark console script entry point."
},
{
"path": "benchmark/cellprofiler_benchmark_cli.py",
- "sha256": "2b92755c556c1d66d28ffa0af6e2ac61cac5af4631514382290a2af0f907c26b",
+ "sha256": "56a9b4e8bb7d8195095eb99a849f717e79ca37aa429c612804584cbd7939f420",
"role": "Owns installed benchmark execution and report commands."
},
{
"path": "benchmark/contracts/run_receipt.py",
- "sha256": "1704e1ab47411d25d382b95871c96c1e60c94db14b8ffd55f4dc6c8a917758b9",
+ "sha256": "4cd61bd33f8f6199b3cc4130ed091dddde9c3a23975e132b1bee06c3c6a43cd7",
"role": "Owns typed comparison-run provenance and lifecycle receipts."
},
{
"path": "benchmark/control.py",
- "sha256": "4706078a8fbeb516a52bc51600751ae86c39c3ef7e397a2f8cf13d49204db941",
- "role": "Owns read-only progress and structured-artifact inspection."
+ "sha256": "b10dd87d7bc44063566e0c53d849091c0fc43a345b434c13467df1ede86ac892",
+ "role": "Owns read-only measured-run inspection and bounded report rendering."
},
{
"path": "tests/unit/test_benchmark_control.py",
- "sha256": "ee75209799216c90cfa3a585dd9b7af99c99405f93e9fb5e4f900ea26d9d3ea0",
+ "sha256": "093b584eeb37cbb12089d5af082ad45acd779e9d2bdd5b3feaea08234fc22183",
"role": "Proves receipt validation and the inspect-only MCP projection."
}
],
- "findings": [
- "The retired-impact notice did not distinguish reproducibility receipts and read-only run inspection from scientific comparison proof or execution authority."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/appendices/research_impact.rst against benchmark/cellprofiler_benchmark_cli.py: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6), re-read docs/source/appendices/research_impact.rst in full and compared it against the changed authorities: pyproject.toml (python-introspect floor 0.2.8), benchmark/cellprofiler_benchmark_cli.py, benchmark/control.py; the observation export scope became a class family read by name, while the openhcs-benchmark entry point, typed receipts, read-only inspection and report, and the archive path the page names are unchanged, so no claim changed.",
+ "On 2026-10-11 (B6), re-read docs/source/appendices/research_impact.rst in full and compared it against the changed authorities: pyproject.toml (dependency floors for zmqruntime, pycodify 0.2, objectstate 2.1, python-introspect, pyqt-reactive 0.5); the openhcs-benchmark entry point, benchmark CLI, run receipts, read-only inspection service and the archived page path all still exist, and the page states no version, so no claim changed.",
+ "On 2026-10-11 (B6 final), compared docs/source/appendices/research_impact.rst against B6's later changes to benchmark/cellprofiler_benchmark_cli.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/appendices/research_impact.rst against B6's later changes to benchmark/cellprofiler_benchmark_cli.py, tests/unit/test_benchmark_control.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/appendices/research_impact.rst against the later B6 changes to pyproject.toml, benchmark/cellprofiler_benchmark_cli.py, tests/unit/test_benchmark_control.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/appendices/research_impact.rst in full and compared it against the changed authorities: pyproject.toml, benchmark/cellprofiler_benchmark_cli.py, benchmark/contracts/run_receipt.py, benchmark/control.py, tests/unit/test_benchmark_control.py; the CLI now runs through the OpenHCS session and receipts name a subject_id, but the openhcs-benchmark entry point, typed receipts, read-only inspect/report service and the archive path the page names all still exist, so no claim changed. Dropped benchmark/contracts/pipeline.py, which the page does not rely on.",
"On 2026-08-27, re-read docs/source/appendices/research_impact.rst in full after the python-introspect dependency floor advanced to 0.1.12; challenged its explanation boundary and confirmed that retiring unsupported impact claims remains accurate.",
"Compared every factual claim directly with the current hash-pinned authorities: benchmark/contracts/pipeline.py; pyproject.toml.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -228,7 +259,7 @@
},
{
"path": "docs/source/concepts/building_intuition.rst",
- "source_sha256": "f4eb04f78f1e52241fa1b7f79b39609142f106832a0bf38b953a170592cf8f78",
+ "source_sha256": "8827a6443b6df28347af61ad301226dbcf3396fbf45c35a8127c086f15185ce9",
"audience": [
"pipeline authors",
"new contributors"
@@ -238,35 +269,48 @@
"authority": [
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
"role": "Compiles pipeline declarations into validated step plans and execution bundles."
},
{
"path": "openhcs/core/source_bindings.py",
- "sha256": "e0afbbe40cce3847bcc2314910d0083ecc579a5a0d6e4278c3a5df4c62e7eb01",
- "role": "Declares source bindings that map logical pipeline inputs to microscope-backed workspaces."
+ "sha256": "890473715373d1adc6b12fec2b57eab6f179cbca7d837920c0388d620ce8b8c2",
+ "role": "Declares NamedSourceBinding and the SourceSetPairing family that turn files and metadata into named inputs."
},
{
- "path": "openhcs/core/artifacts.py",
- "sha256": "bb958c74c98cb96c97c5ac1ea68ac997673aac6afcad57bdf0c9496065e895fa",
- "role": "Declares the nominal artefact types exchanged by compiled steps and runtime consumers."
+ "path": "openhcs/core/dataset_sources/source.py",
+ "sha256": "b3758a878fdf8ec28b6d9818ebfd96c5c470895575f7a79500c85f1490679281",
+ "role": "DatasetSource family that owns recognized dataset layouts."
},
{
- "path": "openhcs/processing/backends/lib_registry/unified_registry.py",
- "sha256": "70462cddcad1bfee447ce0786a9e4a85abbd297726d59a1208156af0ef3a3743",
- "role": "Defines canonical callable metadata and derives the unified catalogue from registry declarations."
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "ProcessingContract family deciding per-plane versus whole-stack execution."
+ },
+ {
+ "path": "openhcs/core/artifacts/kinds.py",
+ "sha256": "216b913efad9ec76707e9f61be445fb6fa1c6601c36ef53e296acd4928604515",
+ "role": "Nominal artifact types exchanged by compiled steps and runtime consumers."
},
{
"path": "external/arraybridge/README.md",
- "sha256": "b108a144e2f797425a1734f745a73ac7a215bd5e58b3f6fe4342b18e7d16b892",
- "role": "States ArrayBridge ownership of reusable array conversion, callable memory metadata, and framework device mechanics."
+ "sha256": "2fe1a330ca101dadb6cea5ac6f0482a5ffe8c7e0e4b9bd7bc8bc2220e106415a",
+ "role": "States ArrayBridge ownership of array conversion, callable memory metadata, and framework device mechanics."
+ },
+ {
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares SourceBindingsConfig, the pipeline-level source-binding configuration (moved here by K4)."
}
],
- "findings": [
- "The owner checklist previously reduced ArrayBridge to conversion and omitted its callable memory metadata and framework-local device mechanics."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/concepts/building_intuition.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, source_bindings.py, dataset_sources/source.py, openhcs/core/artifacts.py (now the openhcs/core/artifacts package, K2; the authority is its kinds.py), openhcs/core/config.py; the diffs are E1 class families replacing enum members and DatasetSource losing get_required_backend; the page states none of them, and force_disk_output is still absent from the tree as the page says; no page change.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/building_intuition.rst in full and compared it against the changed authorities: compiler.py (ScopeKey scope ids, progress classes, empty CompiledSourceBindingPlan), source_bindings.py (K4: config class moved to config.py, SourceSetPairing family replacing SourceBindingMatchMethod), dataset_sources/source.py and processing_contracts.py (CallableDeclaration rename); the page's owner checklist (DatasetSource, PolyStore, ArrayBridge, pyqt-reactive) and its no force_disk_output claim still hold.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/building_intuition.rst against B6's later changes to openhcs/core/pipeline/compiler.py, openhcs/core/dataset_sources/source.py, openhcs/core/processing_contracts.py, openhcs/core/artifacts.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/building_intuition.rst against the later B6 changes to openhcs/core/pipeline/compiler.py, openhcs/core/source_bindings.py, openhcs/core/dataset_sources/source.py, openhcs/core/processing_contracts.py, openhcs/core/artifacts.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/building_intuition.rst in full and compared it against the changed authorities: pipeline/compiler.py, source_bindings.py, artifacts.py, lib_registry/unified_registry.py and external/arraybridge/README.md; confirmed FunctionStep, variable_components, group_by and ProcessingContract exist, that force_disk_output is absent, and renamed the source-integration owner to DatasetSource.",
"On 2026-08-27, re-read docs/source/concepts/building_intuition.rst in full against the full-catalogue editor and owner-declared exclusion changes; challenged its explanation flow and ownership model and found no prose drift.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline/compiler.py; openhcs/core/source_bindings.py; openhcs/core/artifacts.py; openhcs/processing/backends/lib_registry/unified_registry.py; external/arraybridge/README.md.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -281,35 +325,43 @@
},
{
"path": "docs/source/concepts/component_system_concepts.rst",
- "source_sha256": "e3816c371c55821e36e5012787776092beee9650ea638f0006673808e3e81539",
+ "source_sha256": "7bd02c6e5e670bfc02d4e90586a136647c7f6a384226aa7dc7e6edfd872c1be1",
"audience": [
"pipeline authors",
"compiler contributors"
],
- "user_need": "Understand how microscopy component identities differ from grouping and runtime scope.",
+ "user_need": "Understand how dataset axis identities differ from grouping and runtime scope.",
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/constants/constants.py",
- "sha256": "4cd6c29abd8b8e3ecc396b424d2b8b61e819a215fc1d770d3f6852b260a0691d",
- "role": "Declares the nominal microscopy dimensions, grouping identities, and shared component enums used by pipelines."
+ "path": "openhcs/core/axes.py",
+ "sha256": "8d93133639c576050f089fb8aab99937c8982aff07e742fea44f8a12e62bb7b2",
+ "role": "Declares Axis, AxisFamily, the axis roles, Ungrouped, AxisFamily.active() and with_role()."
+ },
+ {
+ "path": "openhcs/domains/microscopy/axes.py",
+ "sha256": "531695809259324032e0b88632813323b4fe6a9a176ad6f636aeef8dec332399",
+ "role": "Declares the Microscopy family with Site, Channel, ZIndex, Timepoint and the well partition axis."
},
{
"path": "openhcs/core/component_group_scope.py",
- "sha256": "16e7f8f05223f7f9d65d1b96ac0418899f82710389768fa0e063d700e368f409",
- "role": "Declares how component groups map nominal dimensions to execution scopes."
+ "sha256": "1d95c82d935a2441c69b4e0046eaa078dc1add7befa7f707b5e76867cf1dce39",
+ "role": "Declares ComponentGroupScope and RuntimeExecutionAxisScope."
},
{
- "path": "tests/unit/test_configuration_mirror_deletion.py",
- "sha256": "26002ddfbbf89bf06ae54888645c6b8c90154536a16e93feee1a2fae713c4963",
- "role": "Proves configuration consumers use declaration-owned metadata without parallel field registries."
+ "path": "tests/unit/test_axis_family_guards.py",
+ "sha256": "699c109686040b978d9007f60b6124d3698a794711212b292348d61dd1d5fa2a",
+ "role": "Proves the component enums are deleted and kernel modules name no microscopy member."
}
],
- "findings": [
- "The previous audit named an unused dynamic-interface scaffold as the component-enum authority; the actual declaration owner is openhcs/constants/constants.py."
- ],
+ "findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/concepts/component_system_concepts.rst in full and compared it against the changed authorities: openhcs/core/component_group_scope.py, tests/unit/test_axis_family_guards.py; ComponentGroupScope gained is_grouped, is_static, excludes and one_per_component, and the guard test's allow-list follows the artifacts package move; ComponentGroupScope and RuntimeExecutionAxisScope still carry the roles the page gives them; no page change.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/component_system_concepts.rst in full and compared it against the changed authorities: tests/unit/test_axis_family_guards.py (U3 deleted openhcs/core/path_cache.py, so the kernel microscopy-reference ceiling dropped from 72 to 70); the page names no pinned reference, and Axis, AxisFamily.active()/with_role(), the seven roles, Ungrouped, ComponentGroupScope, RuntimeExecutionAxisScope and LazyProcessingConfig.from_config were re-checked against the current tree.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/component_system_concepts.rst against B6's later changes to openhcs/core/axes.py, openhcs/domains/microscopy/axes.py, tests/unit/test_axis_family_guards.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/component_system_concepts.rst against the later B6 changes to openhcs/core/axes.py, openhcs/domains/microscopy/axes.py, tests/unit/test_axis_family_guards.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/component_system_concepts.rst in full and compared it against the changed authorities: openhcs/constants/constants.py, openhcs/core/component_group_scope.py, tests/unit/test_configuration_mirror_deletion.py; the page had already been rewritten for the G1 axis family, so checked every named class, role, module path and the ProcessingConfig/LazyProcessingConfig.from_config example against openhcs/core/axes.py, openhcs/domains/microscopy/axes.py, openhcs/core/config.py and the ObjectState lazy factory, corrected the Site sentence, and moved the authorities to the axis owners.",
"Re-read docs/source/concepts/component_system_concepts.rst in full against repository base 32ca17c17 and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared its factual claims directly with the hash-pinned files listed in authority: openhcs/constants/constants.py; openhcs/core/component_group_scope.py; tests/unit/test_configuration_mirror_deletion.py.",
"On 2026-08-25, traced component-enum construction to openhcs/constants/constants.py and removed the unreferenced parallel dynamic-interface registry from the authority record.",
@@ -318,7 +370,7 @@
},
{
"path": "docs/source/concepts/core_model.rst",
- "source_sha256": "abf8f245abf9ca3526a63840d54dbc5f9677604422f027c0d838ff2256800830",
+ "source_sha256": "8b4e5a46e424780bc2bf6e8d1609c667b24f42f6dd946b89e146d3c5eb45aca1",
"audience": [
"imaging scientists",
"integrators",
@@ -329,38 +381,58 @@
"authority": [
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
- "role": "Declares the public pipeline document, step collection, and serialisable declaration boundary."
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
+ "role": "PipelineDocument holds a PipelineConfig plus an ordered step list; no Pipeline wrapper class exists."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compiles pipeline declarations into validated step plans and execution bundles."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Creates one CompilationSession per partition-axis context and emits the compiled bundle after validation."
+ },
+ {
+ "path": "openhcs/core/pipeline/compilation_session.py",
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "Declares CompilationSession with the ResolvedPipelineDefinition, source workspace projection and CompiledStepPlan map."
},
{
"path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
- "role": "Declares the frozen compiled execution bundle consumed by workers after compilation."
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "Declares CompiledExecutionBundle."
},
{
"path": "openhcs/core/runtime_stores.py",
- "sha256": "5f323ec8edd4221ee30e34ed0c9c09e0f4e45ef502749c1a2d331b7a5452af99",
- "role": "Implements scoped typed runtime-value storage and lookup for compiled execution."
+ "sha256": "35fe71906bfd16842de347776a0c5cc846550ad490cc3628885129d746ec6f31",
+ "role": "Declares RuntimeValueStore keyed by typed artifact keys and execution scope."
+ },
+ {
+ "path": "openhcs/core/callable_contract.py",
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "Declares CallableDeclaration (inherited by CallableContract) with the memory types, required axis roles, allowed group-by roles, processing contract, execution scope, runtime adapter and artifact spec fields."
},
{
"path": "openhcs/interop/cellprofiler/pipeline_import.py",
- "sha256": "b547c838b2c86fe7cc5fe12d6cbf6a6715e83055c2d8c1a109ac22566d22c51e",
- "role": "Imports CellProfiler modules into ordinary OpenHCS pipeline declarations through the interop boundary."
+ "sha256": "5aa8895c9f533603b7ef972202141e2439473f0604b97d66f5a027b8b3cd1c83",
+ "role": "Lowers parsed .cppipe modules into ordinary source bindings and FunctionStep declarations."
},
{
"path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
- "role": "Declares the typed source, artifact, memory, framework-device, materialisation, and streaming plan fields summarised by the core model."
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "Declares the source, artifact, memory, materialisation, streaming and worker fields of CompiledStepPlan."
+ },
+ {
+ "path": "openhcs/core/dataset_sources/source.py",
+ "sha256": "b3758a878fdf8ec28b6d9818ebfd96c5c470895575f7a79500c85f1490679281",
+ "role": "Declares the DatasetSource family that interprets plate layouts and metadata."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/concepts/core_model.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, compilation_session.py, runtime_stores.py, callable_contract.py, openhcs/interop/cellprofiler/pipeline_import.py, compiled_step_plan.py, dataset_sources/source.py; the diffs are imports from the openhcs/core/artifacts package and E1 class families (input source, materialization backend, orchestrator state) replacing enum members; the page's diagram, declaration, compilation and runtime statements name none of the replaced members and every named class exists; no page change.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/core_model.rst in full and compared it against the changed authorities: pipeline_document.py (pycodify generate_python_source), compiler.py (ScopeKey, progress classes, empty source plans), compilation_session.py (graph-level declaration validation, disabled items dropped by normalisation), runtime_stores.py (K6 codec removal), callable_contract.py (K5 CallableDeclaration with artifact fields and repeated_return_slots), pipeline_import.py (K4 imports) and compiled_step_plan.py (empty plan defaults); corrected the microscope-handler and snapshot wording and re-checked the callable-contract field list, CompilationSession per partition value, CompiledExecutionBundle and RuntimeValueStore claims.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/core_model.rst against B6's later changes to openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/runtime_stores.py, openhcs/core/callable_contract.py, openhcs/interop/cellprofiler/pipeline_import.py, openhcs/core/compiled_step_plan.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/core_model.rst against the later B6 changes to openhcs/core/pipeline/compiler.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/runtime_stores.py, openhcs/core/callable_contract.py, openhcs/interop/cellprofiler/pipeline_import.py, openhcs/core/compiled_step_plan.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/core_model.rst in full and compared it against the changed authorities: openhcs/core/pipeline_document.py, openhcs/core/pipeline/compiler.py, openhcs/core/compiled_execution.py, openhcs/core/runtime_stores.py, openhcs/interop/cellprofiler/pipeline_import.py, openhcs/core/compiled_step_plan.py; grepped every named class (CompilationSession, CompiledExecutionBundle, RuntimeValueStore, CellProfilerModule, SourceBindingsConfig, ArtifactSpec, CompiledStepPlan, ProcessingContract), replaced the deleted StepSnapshot, corrected session scope and contract axis wording, and added compilation_session.py and callable_contract.py as owners.",
"On 2026-08-27, re-read docs/source/concepts/core_model.rst in full against the explicit CellProfiler import-validation hook; challenged its declaration-to-runtime explanation and confirmed that its supported lowering model remains accurate.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline_document.py; openhcs/core/pipeline/compiler.py; openhcs/core/compiled_execution.py; openhcs/core/runtime_stores.py; openhcs/interop/cellprofiler/pipeline_import.py; openhcs/core/compiled_step_plan.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -373,7 +445,7 @@
},
{
"path": "docs/source/concepts/data_dimensions.rst",
- "source_sha256": "09ab2832cfb480cc532867a118850e12242f2fc9951df718f95317bd746ee564",
+ "source_sha256": "75be1966bb36c44edcd081e1cc2adfa23a929661d6565c04919521e9f2e58037",
"audience": [
"pipeline authors",
"imaging scientists"
@@ -382,61 +454,54 @@
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/constants/constants.py",
- "sha256": "4cd6c29abd8b8e3ecc396b424d2b8b61e819a215fc1d770d3f6852b260a0691d",
- "role": "Declares the nominal microscopy dimensions, grouping identities, and shared component enums used by pipelines."
+ "path": "openhcs/core/axes.py",
+ "sha256": "8d93133639c576050f089fb8aab99937c8982aff07e742fea44f8a12e62bb7b2",
+ "role": "Declares AxisFamily, its single PartitionAxis and the variable axes a step may stack along."
},
{
- "path": "openhcs/core/variable_component_stack_requirement.py",
- "sha256": "0f34c6ecb44cae29266e6c572b80c5393ef588a144e215fa3fe135ea29f737a2",
- "role": "Declares which nominal stack components a callable requires and how those requirements are validated."
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "Declares the Pure2DContract, Pure3DContract, FlexibleContract and VolumetricToSliceContract subclasses of ProcessingContract."
},
{
"path": "openhcs/core/component_group_scope.py",
- "sha256": "16e7f8f05223f7f9d65d1b96ac0418899f82710389768fa0e063d700e368f409",
- "role": "Declares how component groups map nominal dimensions to execution scopes."
- },
- {
- "path": "openhcs/processing/backends/lib_registry/unified_registry.py",
- "sha256": "70462cddcad1bfee447ce0786a9e4a85abbd297726d59a1208156af0ef3a3743",
- "role": "Defines canonical callable metadata and derives the unified catalogue from registry declarations."
+ "sha256": "1d95c82d935a2441c69b4e0046eaa078dc1add7befa7f707b5e76867cf1dce39",
+ "role": "Declares ComponentGroupScope and RuntimeExecutionAxisScope carried by artifact keys."
},
{
"path": "openhcs/core/runtime_object_label_domains.py",
- "sha256": "32824678741c1caf3fbabe621619d6d8d463936a28bc2d689916ed69fa7582ed",
- "role": "Declares payload-scoped and plane-scoped object identity domains without inferring them from array shape."
+ "sha256": "cb56a2b3a391cfd5081e1487a04e2e95b88628879abc42a2ec3b69ab327ab0cf",
+ "role": "Declares the PAYLOAD and PLANE object-label domain scopes."
},
{
"path": "openhcs/processing/backends/cellprofiler/watershed.py",
- "sha256": "9bd009be9881a88e295e1c5ca6fbc02b4402bc97d207cfd20848ff685fe64bde",
- "role": "Declares the full-stack image payload mode used by CellProfiler-compatible volumetric Watershed."
+ "sha256": "7fb797d16fc740c381d7dc9add3a0ff79ed2d82515850597c7995857bdacc206",
+ "role": "Declares Watershed with FullStackExecution so it consumes the complete payload."
},
{
"path": "openhcs/processing/backends/cellprofiler/primary_objects.py",
- "sha256": "6710b507fcdead2954e8a1038c91e2a99572d67b6d4ded8a7d41100fb229aa29",
- "role": "Declares IdentifyPrimaryObjects as a plane-local processing callable."
+ "sha256": "4d28a02d5c25e4700df1affdbf414e587cde7b7d348917ee29e862e5135466cc",
+ "role": "Declares IdentifyPrimaryObjects with Pure2DContract."
},
{
"path": "openhcs/processing/backends/cellprofiler/secondary.py",
- "sha256": "9a032a11e51c3f950538f89f4e4312094496fd6408e0fdceebc7dc8b91be43de",
- "role": "Declares IdentifySecondaryObjects and IdentifyTertiaryObjects as plane-local processing callables."
- },
- {
- "path": "tests/unit/test_callable_contract.py",
- "sha256": "493f76a54f5fe024ca8f10c53b1f810b8c369626ad7141205b42ef3a78282137",
- "role": "Proves callable contract derivation for dimensions, memory types, dtype policy, and outputs."
+ "sha256": "e84db4b2bfa56492801e24fc9471e09c4ada4daf5b3541463cf3424ed227af2b",
+ "role": "Declares IdentifySecondaryObjects and IdentifyTertiaryObjects with Pure2DContract."
},
{
"path": "tests/unit/test_documented_dimensionality.py",
- "sha256": "83bef8dcad91750c9acda63b8a4293ccbd0904dd5aea7a32be197f9d51f300a7",
- "role": "Binds the published dimensionality boundary to the current callable and feature declarations."
+ "sha256": "45762e74bf5cecbbada509f378ddbfa47c20fa6a9f5bd668d05d003e59bb38e1",
+ "role": "Binds the published dimensionality boundary to the current callable declarations."
}
],
- "findings": [
- "The previous explanation did not distinguish a Z stack from a payload-scoped volumetric object domain, leaving true 3D support ambiguous."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/concepts/data_dimensions.rst in full and compared it against the changed authorities: openhcs/core/component_group_scope.py, openhcs/processing/backends/cellprofiler/watershed.py, primary_objects.py, secondary.py; Watershed's runtime family became WatershedRuntimeStrategy subclasses and IdentifySecondaryObjects declares its source artifact type as a class attribute; neither touches the callable declarations that make Watershed consume a complete 3D payload and the Identify modules run plane by plane, and the four ProcessingContract subclasses and the PAYLOAD/PLANE label-domain scopes still exist; no page change.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/data_dimensions.rst in full and compared it against the changed authorities: openhcs/core/processing_contracts.py (CallableMetadata renamed CallableDeclaration at three call sites), openhcs/processing/backends/cellprofiler/watershed.py (StepSourceBindingsConfig imported from openhcs.core.config after K4) and openhcs/processing/backends/cellprofiler/secondary.py (repeated_return_slots for identical tertiary inputs); the page names none of these, and the four contract classes, the PAYLOAD/PLANE label domains, the per-plane Identify modules and the ProcessingConfig example still hold.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/data_dimensions.rst against B6's later changes to openhcs/core/axes.py, openhcs/core/processing_contracts.py, openhcs/processing/backends/cellprofiler/watershed.py, openhcs/processing/backends/cellprofiler/primary_objects.py, openhcs/processing/backends/cellprofiler/secondary.py, tests/unit/test_documented_dimensionality.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/data_dimensions.rst against the later B6 changes to openhcs/core/axes.py, openhcs/core/processing_contracts.py, openhcs/processing/backends/cellprofiler/watershed.py, openhcs/processing/backends/cellprofiler/primary_objects.py, openhcs/processing/backends/cellprofiler/secondary.py, tests/unit/test_documented_dimensionality.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/data_dimensions.rst in full and compared it against the changed authorities: openhcs/constants/constants.py, openhcs/core/component_group_scope.py, openhcs/processing/backends/lib_registry/unified_registry.py, openhcs/core/runtime_object_label_domains.py, the three CellProfiler backends, tests/unit/test_callable_contract.py and tests/unit/test_documented_dimensionality.py; confirmed Watershed still declares FullStackExecution and the Identify modules Pure2DContract, the PAYLOAD/PLANE scopes are unchanged, and corrected the processing-contract names and partition-axis wording.",
"On 2026-08-27, re-read docs/source/concepts/data_dimensions.rst in full against registry form exclusions, explicit secondary-backend default selection, and expanded contract tests; challenged its dimensional explanation and found no semantic drift.",
"On 2026-08-27, re-read the complete data-dimensions explanation after library registries gained declaration-owned runtime-parameter exclusions; dimensional and runtime-identity claims remain unchanged.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/constants/constants.py; openhcs/core/variable_component_stack_requirement.py; openhcs/core/component_group_scope.py; openhcs/processing/backends/lib_registry/unified_registry.py; openhcs/core/runtime_object_label_domains.py; openhcs/processing/backends/cellprofiler/watershed.py; openhcs/processing/backends/cellprofiler/primary_objects.py; openhcs/processing/backends/cellprofiler/secondary.py; tests/unit/test_callable_contract.py; tests/unit/test_documented_dimensionality.py.",
@@ -449,7 +514,7 @@
},
{
"path": "docs/source/concepts/domain_fundamentals.rst",
- "source_sha256": "9f4a6f7aa270b5814397a0e8227f44064cc6f5e4cf2219609da187ae7557341e",
+ "source_sha256": "741c1cca9905d47ae0170570bdc473f503d68ec526bc07f309368d6b37f0e185",
"audience": [
"imaging scientists new to OpenHCS"
],
@@ -457,24 +522,29 @@
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/microscopes/microscope_interfaces.py",
- "sha256": "a603fa1c58ba005f8ba22713e46efe4e33aee0f762ba188467482ae3ae429200",
- "role": "Declares the microscope source interface, plate dimensions, metadata, and acquisition access contract."
+ "path": "openhcs/core/dataset_sources/source.py",
+ "sha256": "b3758a878fdf8ec28b6d9818ebfd96c5c470895575f7a79500c85f1490679281",
+ "role": "Declares the registered DatasetSource family that projects each microscope format into axis values, backends and metadata."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compiles pipeline declarations into validated step plans and execution bundles."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Resolves inputs, axes, locality, memory conversion and artifact dependencies before emitting compiled plans."
},
{
"path": "openhcs/core/runtime_artifact_values.py",
- "sha256": "9ac62ec4c4a6a6f7f375026ea112cc2e43f09ee6e6ca270a3d955be8ef5347cf",
- "role": "Declares the typed runtime values carried for nominal artefact kinds."
+ "sha256": "ce42e38f893459b9cb0ed6d779fabd5818204301ab1acc13ab0a7df32e6e3a2d",
+ "role": "Declares ArtifactKey and RuntimeValue, the typed runtime result carrier."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/concepts/domain_fundamentals.rst in full and compared it against the changed authorities: openhcs/core/dataset_sources/source.py (E1: get_required_backend deleted, virtual-workspace root compared by absolute path), openhcs/core/pipeline/compiler.py (E1: OrchestratorState, MaterializationBackend and projection-role members are now classes) and openhcs/core/runtime_artifact_values.py (K2: imports from the artifacts package, RuntimeValue.payload_of added); the page names none of these members and its account of dataset sources, compile-time resolution and typed results still holds.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/domain_fundamentals.rst in full and compared it against the changed authorities: openhcs/core/dataset_sources/source.py (SourceBindingsConfig now imported from openhcs.core.config after K4) and openhcs/core/pipeline/compiler.py (ScopeKey scope ids, CompileProgressPhase/RunningProgressStatus classes after K6, an empty CompiledSourceBindingPlan instead of None after K4); none changes the page's explanation of dataset sources, compile-time resolution or typed results.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/domain_fundamentals.rst against B6's later changes to openhcs/core/dataset_sources/source.py, openhcs/core/pipeline/compiler.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/domain_fundamentals.rst against the later B6 changes to openhcs/core/dataset_sources/source.py, openhcs/core/pipeline/compiler.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/domain_fundamentals.rst in full and compared it against the changed authorities: openhcs/microscopes/microscope_interfaces.py (deleted in G4), openhcs/core/pipeline/compiler.py, openhcs/core/runtime_artifact_values.py; replaced the deleted source interface with openhcs/core/dataset_sources/source.py, updated the dataset-source wording, and confirmed the compilation and typed-result claims still hold.",
"Re-read docs/source/concepts/domain_fundamentals.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/microscopes/microscope_interfaces.py; openhcs/core/pipeline/compiler.py; openhcs/core/runtime_artifact_values.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -495,28 +565,38 @@
"authority": [
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares DtypeConfig as a DtypeConversionConfig subclass with NATIVE_OUTPUT as its default_dtype_conversion."
+ },
+ {
+ "path": "openhcs/core/steps/abstract.py",
+ "sha256": "2bffdfe02664c4974797bb6cefbf7f84ebb6912fcc11e026a3f2d8b3da5262b8",
+ "role": "Declares the step-level dtype_config field separate from processing_config."
},
{
"path": "openhcs/core/memory/decorators.py",
- "sha256": "d9f9cf0be7593dc0ebfc57f258aebed75bcf9cfa508ed1e942137366d819766a",
- "role": "Exports OpenHCS memory and dtype decorators while delegating conversion semantics to ArrayBridge declarations."
+ "sha256": "76f234785f4865257fd19c935e0aace82d1f5586198737fec619ee243b79cbf6",
+ "role": "Exports the OpenHCS memory decorators that delegate dtype conversion to ArrayBridge."
},
{
"path": "external/arraybridge/src/arraybridge/decorators.py",
- "sha256": "99823105b0ae59054df1d84013633b8b6628d0a6dc3326383199ac7c65c69bed",
- "role": "Declares decorators that write independent input, output, and execution memory roles plus dtype policy."
+ "sha256": "28f9bf4770bd4cc1ef34045fdef28065063ede029296f62b8a6c4140cc88e40e",
+ "role": "Declares the DtypeConversion members and DtypeConversionConfig as a runtime-bound parameter declaration."
},
{
"path": "tests/unit/test_callable_contract.py",
- "sha256": "493f76a54f5fe024ca8f10c53b1f810b8c369626ad7141205b42ef3a78282137",
- "role": "Proves callable contract derivation for dimensions, memory types, dtype policy, and outputs."
+ "sha256": "9170f8694f388dbed5bfd36d882a5c0a4cdf28f50f51f7fdeaf4a955a9f3b412",
+ "role": "Proves dtype_config is the overridable runtime-bound parameter on a decorated callable's contract."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/concepts/dtype_config_system.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1: MaterializationBackend and InputSource became class families; DtypeConfig, its NATIVE_OUTPUT default and annotation_type unchanged) and openhcs/core/steps/abstract.py (docstring now names ReadPreviousStep/ReadPipelineStart; dtype_config is still a step field separate from processing_config); no claim changed and the DtypeConfig example imports still resolve.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/dtype_config_system.rst in full and compared it against the changed authorities: openhcs/core/config.py (K4 source-binding classes, PartitionFilterMode family; DtypeConfig unchanged with NATIVE_OUTPUT default), openhcs/core/steps/abstract.py (import regrouping only; dtype_config still a separate FunctionStep field) and tests/unit/test_callable_contract.py (CallableMetadata renamed CallableDeclaration, namespace round-trip test); dtype_config is still the one config-bound, overridable runtime parameter on a decorated callable's contract, and the DtypeConfig example still resolves.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/dtype_config_system.rst against B6's later changes to openhcs/core/config.py, openhcs/core/memory/decorators.py, tests/unit/test_callable_contract.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/dtype_config_system.rst against the later B6 changes to openhcs/core/config.py, openhcs/core/memory/decorators.py, tests/unit/test_callable_contract.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/dtype_config_system.rst in full and compared it against the changed authorities: openhcs/core/config.py, openhcs/core/memory/decorators.py, external/arraybridge/src/arraybridge/decorators.py, tests/unit/test_callable_contract.py; checked the DtypeConversion members, the NATIVE_OUTPUT default, the import paths in the example, the step-level dtype_config field (now owned by openhcs/core/steps/abstract.py, added as an authority) and the runtime-bound dtype_config contract parameter; no claim changed.",
"On 2026-08-27, re-read docs/source/concepts/dtype_config_system.rst in full after callable contracts began projecting runtime-owned inputs into generic form exclusions; challenged the dtype ownership boundary and found no prose drift.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/config.py; openhcs/core/memory/decorators.py; external/arraybridge/src/arraybridge/decorators.py; tests/unit/test_callable_contract.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -529,7 +609,7 @@
},
{
"path": "docs/source/concepts/function_library.rst",
- "source_sha256": "5f283828046aaa3e7c9e57ff0ef34cd77561031f110619b0d41aa9696047a769",
+ "source_sha256": "4af7d9f51f08b2ef9573882b50f1e5c7c2f70093ae1d37fcd8869b0ab8bb60ec",
"audience": [
"imaging scientists",
"callable authors"
@@ -538,70 +618,69 @@
"diataxis": "explanation",
"authority": [
{
- "path": "openhcs/processing/backends/lib_registry/unified_registry.py",
- "sha256": "70462cddcad1bfee447ce0786a9e4a85abbd297726d59a1208156af0ef3a3743",
- "role": "Defines canonical callable metadata and derives the unified catalogue from registry declarations."
+ "path": "openhcs/core/callable_contract.py",
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "Declares the CallableDeclaration fields CallableContract inherits (memory types, artifact specs, runtime-bound parameters, required_axis_roles, allowed_group_by_roles, processing contract, runtime adapter, execution scope, prepared signatures) and processing_prepare, which attaches the preparation hook to the callable."
},
{
- "path": "openhcs/processing/backends/lib_registry/registry_service.py",
- "sha256": "046d3d088f685a993ddad6b5923cf63beef84b201127b72ae32af6721dd5ad63",
- "role": "Owns backend discovery, availability filtering, and the canonical process catalogue projection."
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "Declares the ProcessingContract class family selecting per-slice, full-stack, flexible or volumetric-to-slice execution."
},
{
- "path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
- "role": "Derives callable input, output, execution-framework, dimensional, artifact, and runtime requirements from declarations."
+ "path": "openhcs/processing/backends/lib_registry/unified_registry.py",
+ "sha256": "b1e9253021b9c3ab14a070b7d0f10909f641ba07784c794ad95d2ce36041b1d3",
+ "role": "Derives catalogue metadata and form exclusions from library registry declarations."
},
{
- "path": "openhcs/agent/services/function_catalog_service.py",
- "sha256": "93cfc4431cff9ce182190dfd3c5cb265c532a38c78d2baa39153f972c5f97cc8",
- "role": "Projects callable contracts from the unified function registry for agent discovery and selection."
+ "path": "openhcs/processing/backends/lib_registry/registry_service.py",
+ "sha256": "f2e981157632adb5020828720ec26bfa6444ab52f545e1e71b1524242eaed6a3",
+ "role": "Owns backend discovery, availability filtering and the process catalogue projection."
},
{
- "path": "tests/unit/test_callable_contract.py",
- "sha256": "493f76a54f5fe024ca8f10c53b1f810b8c369626ad7141205b42ef3a78282137",
- "role": "Proves callable contract derivation for dimensions, memory types, dtype policy, and outputs."
+ "path": "openhcs/processing/backends/lib_registry/openhcs_registry.py",
+ "sha256": "2a1de7f8af9ade2cba450cbbcc1abd6510a8e3edb58217c6c08fa067bde21de2",
+ "role": "Derives the public native callable catalogue and excludes private decorated helpers."
},
{
- "path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
- "role": "Owns framework capabilities and the canonical callable memory-metadata keys used by function declarations."
+ "path": "openhcs/agent/services/function_catalog_service.py",
+ "sha256": "7ef506fe713be7a77d2a3eb6aa565cf313b43d7f79171014cb4e01543e63f4ae",
+ "role": "Serves catalogue search and callable detail to MCP and GUI authoring from the execution endpoint."
},
{
"path": "external/arraybridge/src/arraybridge/decorators.py",
- "sha256": "99823105b0ae59054df1d84013633b8b6628d0a6dc3326383199ac7c65c69bed",
- "role": "Writes the independent input, output, and execution framework declarations described by the page."
+ "sha256": "28f9bf4770bd4cc1ef34045fdef28065063ede029296f62b8a6c4140cc88e40e",
+ "role": "Writes the independent input, output and execution framework declarations."
},
{
- "path": "openhcs/processing/backends/lib_registry/openhcs_registry.py",
- "sha256": "aa3079341cb2af8788938e14efee6f9f6c4b6c01874227e74c6340850d525884",
- "role": "Derives the public native callable catalogue while retaining explicit reconstruction from each callable declaration."
+ "path": "external/arraybridge/src/arraybridge/types.py",
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
+ "role": "Owns the framework capabilities and callable memory-metadata keys."
},
{
"path": "tests/unit/test_library_registry_discovery.py",
- "sha256": "a7ee2d3a2002ea291876c27aa2dce51a889cea6be9a3055602c76f1e9cf76f1b",
+ "sha256": "a67eb8d91a44a3eb347b46608679f2f8632abfd5b54c6f85315d1ea71a164460",
"role": "Proves declaration-derived catalogue discovery and process policy filtering."
},
{
"path": "tests/unit/test_unified_registry_runtime_payloads.py",
- "sha256": "e731379d3e48fd5c98709203668e09baa195bb0c9301a2b041e1c799fa8140e3",
+ "sha256": "a817850fc2d84f82da3c5e60d2ae33d461d795b36d65816955a4c8b18bd9b852",
"role": "Proves external library runtime controls stay out of authored function parameters."
},
{
"path": "tests/unit/pyqt_gui/test_function_catalog_form_materialization.py",
"sha256": "711b69ff0b145b962f0f3518c7a7a77db38d8a545bb9f5ea7d2e259d70598667",
- "role": "Proves every locally available catalogue declaration materialises through the real desktop function form."
+ "role": "Proves every locally available catalogue declaration materialises through the desktop function form."
}
],
- "findings": [
- "The page did not explain that optional backend visibility requires a usable runtime and complete declared module inventory.",
- "The callable-contract explanation omitted the independent execution framework and did not state that catalogue admission checks every declared framework role.",
- "The function-pattern explanation did not state the nominal enum-member requirement enforced by callable keyword validation.",
- "The function-library explanation did not distinguish public catalogue declarations from private decorated implementation helpers.",
- "The explanation did not connect contract-owned exclusions and library-owned non-authorable controls to full-catalogue desktop form materialisation."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/concepts/function_library.rst in full and compared it against the changed authorities: openhcs/core/callable_contract.py (K2: artifact imports repointed to the package, artifact_type.value in one error message) and openhcs/agent/services/function_catalog_service.py (E1: search rank, signature view, summary view and parameter-supplier enums became class families); the page names none of them, and the contract field list, enum-member kwarg rule, catalogue and exclusion claims still hold.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/function_library.rst in full and compared it against the changed authorities: callable_contract.py (K5: CallableMetadata merged into CallableDeclaration, prepare left the declaration, runtime_bound_parameter_names), processing_contracts.py (CallableDeclaration rename), registry_service.py and function_catalog_service.py (declaration/runtime_bound_parameter_names renames) and tests/unit/test_unified_registry_runtime_payloads.py (admitted_graph helper); corrected the preparation-hook claim and re-checked the remaining contract fields, the ProcessingContract family, and the catalogue and form-exclusion claims.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/function_library.rst against B6's later changes to openhcs/core/callable_contract.py, openhcs/core/processing_contracts.py, openhcs/processing/backends/lib_registry/unified_registry.py, openhcs/processing/backends/lib_registry/registry_service.py, openhcs/processing/backends/lib_registry/openhcs_registry.py, openhcs/agent/services/function_catalog_service.py, tests/unit/test_unified_registry_runtime_payloads.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/function_library.rst against the later B6 changes to openhcs/core/callable_contract.py, openhcs/core/processing_contracts.py, openhcs/processing/backends/lib_registry/unified_registry.py, openhcs/processing/backends/lib_registry/registry_service.py, openhcs/processing/backends/lib_registry/openhcs_registry.py, openhcs/agent/services/function_catalog_service.py, tests/unit/test_library_registry_discovery.py, tests/unit/test_unified_registry_runtime_payloads.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/function_library.rst in full and compared it against the changed authorities: unified_registry.py, registry_service.py, openhcs/core/callable_contract.py, function_catalog_service.py, openhcs_registry.py, the ArrayBridge types and decorators, and the three registry/contract tests; the G5 processing-contract move and catalogue-service signature/doc projection changes leave the page's catalogue, exclusion and framework claims intact, corrected the contract field names, and added openhcs/core/processing_contracts.py as an owner.",
"Re-read docs/source/concepts/function_library.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/processing/backends/lib_registry/unified_registry.py; openhcs/processing/backends/lib_registry/registry_service.py; openhcs/core/callable_contract.py; openhcs/agent/services/function_catalog_service.py; tests/unit/test_callable_contract.py; external/arraybridge/src/arraybridge/types.py; external/arraybridge/src/arraybridge/decorators.py; openhcs/processing/backends/lib_registry/openhcs_registry.py; tests/unit/test_library_registry_discovery.py; tests/unit/test_unified_registry_runtime_payloads.py; tests/unit/pyqt_gui/test_function_catalog_form_materialization.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -620,7 +699,7 @@
},
{
"path": "docs/source/concepts/function_patterns.rst",
- "source_sha256": "2e60a937bc001bdcc961f378c5fb29d1829580351cbf87db4eec9eda02d77411",
+ "source_sha256": "5db317a1e98084d593e6b7ffc9cb514a7400a6c125d8296c9fda5ffadf361929",
"audience": [
"pipeline authors"
],
@@ -630,24 +709,32 @@
{
"path": "openhcs/core/steps/function_step.py",
"sha256": "8e1243404095691030e9ebb95bb5611679f96c66c6b6819ae71e4b41249e74bc",
- "role": "Declares FunctionStep composition forms and canonical callable-pattern declaration identity."
+ "role": "Declares the FunctionStep func forms and callable-pattern declaration identity."
+ },
+ {
+ "path": "openhcs/core/function_patterns.py",
+ "sha256": "c8004ae1e3f241c4ac6ce0a61c69e3c0d1ecfb5021e1bef5dd0fb1fd1251ca50",
+ "role": "Declares AuthoredFunctionPattern (the single walker over callable, chain and dictionary syntax) and normalises patterns into CompiledFunctionPattern, whose execution_scope requires one uniform scope across invocations."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compiles pipeline declarations into validated step plans and execution bundles."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Validates per-invocation callable contracts and dictionary keys against compiled grouping."
},
{
"path": "tests/unit/test_function_patterns.py",
- "sha256": "2b18e6f40f3f5e4a9673f954544b0113eba710675573fcda3fac6ef18d5eb725",
- "role": "Proves callable, sequential-chain, and dictionary function patterns compile with their declared semantics."
+ "sha256": "2eb64da61d0ae1ef2aedacddafc2f81b886515e9c500ac80b2cbcd49c48f5d9d",
+ "role": "Proves callable, chain and dictionary patterns compile with their declared semantics."
}
],
- "findings": [
- "The explanation omitted that wrappers and explicitly supplied signature defaults do not create distinct callable declarations."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/concepts/function_patterns.rst in full and compared it against the changed authorities: openhcs/core/function_patterns.py and tests/unit/test_function_patterns.py (K2: imports repointed to openhcs.core.artifacts.plans/specs/kinds/relations/input_projection only) and openhcs/core/pipeline/compiler.py (E1 class families for orchestrator state and materialization backend); the callable, chain and dictionary forms, the group_by example and the declaration-identity rule are unchanged.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/function_patterns.rst in full and compared it against the changed authorities: function_patterns.py (K5 replaced the seven raw pattern walkers, strip_disabled_functions, inject_kwargs_into_pattern and inject_artifact_input_values with AuthoredFunctionPattern and NormalizedFunctionPattern methods), compiler.py (ScopeKey, progress classes) and tests/unit/test_function_patterns.py (renamed tests for normalisation dropping disabled items and artifact input values); the page names none of the removed helpers, and the pattern forms, dictionary routing example, uniform execution scope (CompiledFunctionPattern.execution_scope) and declaration-identity claims still hold.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/function_patterns.rst against B6's later changes to openhcs/core/function_patterns.py, openhcs/core/pipeline/compiler.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/function_patterns.rst against the later B6 changes to openhcs/core/function_patterns.py, openhcs/core/pipeline/compiler.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/function_patterns.rst in full and compared it against the changed authorities: the page itself (example already moved to Microscopy axes), openhcs/core/pipeline/compiler.py, tests/unit/test_function_patterns.py; confirmed the example imports, CompiledFunctionPattern, and the grouping-versus-stacking claims, corrected the axis-role wording, and added openhcs/core/function_patterns.py as the pattern owner.",
"Re-read docs/source/concepts/function_patterns.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/steps/function_step.py; openhcs/core/pipeline/compiler.py; tests/unit/test_function_patterns.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -669,23 +756,28 @@
"authority": [
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
- "role": "Declares the public pipeline document, step collection, and serialisable declaration boundary."
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
+ "role": "Declares the pipeline document (config plus ordered steps) that the core-building-blocks pages explain."
},
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares the processing, dtype and storage configuration the processing-and-storage pages explain."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compiles pipeline declarations into validated step plans and execution bundles."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Compiles declarations into plans, the step the core-model and dimensions pages describe."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/concepts/index.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1: MaterializationBackend and InputSource class families) and openhcs/core/pipeline/compiler.py (E1: orchestrator-state and backend classes); the page is a reading-order index that names no member of either, and every toctree target still exists.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/index.rst in full and compared it against the changed authorities: openhcs/core/pipeline_document.py (source rendered through pycodify generate_python_source after L6), openhcs/core/config.py (source-binding config classes now declared there after K4) and openhcs/core/pipeline/compiler.py (ScopeKey scope ids, progress classes); the page only routes readers to the concept pages and every toctree entry still exists.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/index.rst against B6's later changes to openhcs/core/config.py, openhcs/core/pipeline/compiler.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/index.rst against the later B6 changes to openhcs/core/config.py, openhcs/core/pipeline/compiler.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/index.rst in full and compared it against the changed authorities: openhcs/core/pipeline_document.py, openhcs/core/config.py, openhcs/core/pipeline/compiler.py; the page is a reading-order toctree whose every entry still exists under docs/source/concepts and makes no product claim affected by the G1/G4/G5 renames; no change.",
"Re-read docs/source/concepts/index.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline_document.py; openhcs/core/config.py; openhcs/core/pipeline/compiler.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -697,7 +789,7 @@
},
{
"path": "docs/source/concepts/module_structure.rst",
- "source_sha256": "6537ec140390a40a617770c4c4c7691a568133bf9d72b0be41ee3acc7e61c404",
+ "source_sha256": "2dbec5d897d4384d25fed93a72d83c95ba527b9885dc82b4e7dd31643ca83a7f",
"audience": [
"contributors",
"integrators"
@@ -707,89 +799,103 @@
"authority": [
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
- "role": "Declares the package version, Python requirement, dependency extras, scripts, and distribution metadata."
- },
- {
- "path": "external/PolyStore/src/polystore/omero_address.py",
- "sha256": "8a3ae8789fceba0f099eef2ee983d858a8f83b764f19b70dfa7bb88f521c0c92",
- "role": "Owns canonical OMERO well, concrete plane-address, and symbolic filename-template semantics."
- },
- {
- "path": "external/python-introspect/README.md",
- "sha256": "d861f8aff2e79d9e77fbb2286b2f353c5b568412482a57efce9a488e58d543fe",
- "role": "States python-introspect ownership of callable/signature analysis and signature-derived declaration projection."
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
+ "role": "Declares the eight first-party foundation distributions as OpenHCS dependencies."
},
{
"path": ".gitmodules",
"sha256": "b70034bf0b9661f2528f002e08c03042d6cece29de79bd96c11d2d5a943684bd",
- "role": "Pins the repository paths and upstream URLs of the first-party submodules named in the ownership map."
+ "role": "Pins the external/ submodule paths named in the foundations list."
},
{
- "path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "path": "openhcs/domains/__init__.py",
+ "sha256": "544d3e1ba5c07915ecee96e1beb57f0d58843fbdf1324bdac0e7f539a3b19c0b",
+ "role": "States that openhcs/domains holds the declarations instantiating the domain-blind kernel."
},
{
- "path": "openhcs/processing/backends/lib_registry/unified_registry.py",
- "sha256": "70462cddcad1bfee447ce0786a9e4a85abbd297726d59a1208156af0ef3a3743",
- "role": "Defines canonical callable metadata and derives the unified catalogue from registry declarations."
+ "path": "openhcs/authoring/session/__init__.py",
+ "sha256": "8e1768912dbb8c23bc00463c9849b99e6a0b3027370f9299c0b48ed782e2ae83",
+ "role": "States that the session owns datasets, pipelines, compile, run and events rendered by the GUI and MCP clients."
},
{
- "path": "openhcs/interop/cellprofiler/pipeline_import.py",
- "sha256": "b547c838b2c86fe7cc5fe12d6cbf6a6715e83055c2d8c1a109ac22566d22c51e",
- "role": "Imports CellProfiler modules into ordinary OpenHCS pipeline declarations through the interop boundary."
+ "path": "external/PolyStore/src/polystore/omero_address.py",
+ "sha256": "8a3ae8789fceba0f099eef2ee983d858a8f83b764f19b70dfa7bb88f521c0c92",
+ "role": "Declares OMEROWellAddress, OMEROPlaneAddress and OMEROPlaneFilenameTemplate."
},
{
- "path": "openhcs/runtime/viewer_protocol.py",
- "sha256": "30441eab97da9ffcb5b9dc33d65c2ecc56c426e79bb2b27eedc78a68aaf13bc3",
- "role": "Declares the transport-independent viewer messages, stream configuration, and server contract."
+ "path": "openhcs/microscopes/omero.py",
+ "sha256": "a3ab5a257490f729e96ba413c3f11d12f10f68dae44a728206f81b850325da31",
+ "role": "Declares OMEROFilenameParser projecting PolyStore OMERO addresses into the parser family."
},
{
- "path": "external/zmqruntime/src/zmqruntime/client.py",
- "sha256": "fc8bdf68455673921d79742204bf6c88e42b52bbe407d10ef4a831e084ca86a8",
- "role": "Owns exact spawned viewer processes, terminal observation, automatic child reaping, and explicit application and test teardown."
+ "path": "external/python-introspect/README.md",
+ "sha256": "059dcfaafc0b2222e8e64b7fe072d24bddfb5a347a6f25113e715ed7946e44a1",
+ "role": "States python-introspect ownership of callable/signature analysis and declaration projection."
},
{
"path": "external/arraybridge/README.md",
- "sha256": "b108a144e2f797425a1734f745a73ac7a215bd5e58b3f6fe4342b18e7d16b892",
- "role": "States ArrayBridge ownership of reusable array conversion, callable memory metadata, and framework device mechanics."
+ "sha256": "2fe1a330ca101dadb6cea5ac6f0482a5ffe8c7e0e4b9bd7bc8bc2220e106415a",
+ "role": "States ArrayBridge ownership of array conversion, callable memory metadata and framework device mechanics."
},
{
"path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
- "role": "Declares framework capabilities, callable memory metadata, and the child-process import environment owned by each MemoryType member."
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
+ "role": "Declares framework capabilities and the child-process import environment per memory type."
},
{
"path": "external/metaclass-registry/src/metaclass_registry/discovery.py",
- "sha256": "f92c94a448701a8dec278c8c6ce4b0a48c49ffc7df48620c5f2a51bc1734d24b",
- "role": "Owns registry discovery imports that preserve host-owned root logging."
+ "sha256": "757958272d11633e7b854c7104c07f0d90845c37fee6ab0c3646fb14c2ed8daa",
+ "role": "Owns registry discovery imports that preserve host root logging."
},
{
- "path": "benchmark/cellprofiler_benchmark_cli.py",
- "sha256": "2b92755c556c1d66d28ffa0af6e2ac61cac5af4631514382290a2af0f907c26b",
- "role": "Owns registered benchmark command declarations and installed CLI construction."
+ "path": "external/zmqruntime/src/zmqruntime/client.py",
+ "sha256": "7d3cce31d5abaa070927f02fe87d919687619e2cf4adbc37ab82483b6b8fc212",
+ "role": "Owns the generic execution client and transport protocol."
},
{
- "path": "benchmark/contracts/run_receipt.py",
- "sha256": "1704e1ab47411d25d382b95871c96c1e60c94db14b8ffd55f4dc6c8a917758b9",
- "role": "Owns typed run declaration and receipt semantics inside the benchmark package."
+ "path": "benchmark/mcp_extension.py",
+ "sha256": "056ab5834fa3c37df1a36a37d80f2a821fb4b59c39892d7737e62fa6a57cb53b",
+ "role": "Declares the expert MCP capabilities to discover cases, finalize a measured run, and inspect or report run directories."
},
{
"path": "benchmark/control.py",
- "sha256": "4706078a8fbeb516a52bc51600751ae86c39c3ef7e397a2f8cf13d49204db941",
- "role": "Owns read-only inspection derived from benchmark contracts and artifacts."
+ "sha256": "b10dd87d7bc44063566e0c53d849091c0fc43a345b434c13467df1ede86ac892",
+ "role": "Implements read-only case discovery, inspection and reporting over benchmark contracts."
+ },
+ {
+ "path": "benchmark/contracts/run_receipt.py",
+ "sha256": "4cd61bd33f8f6199b3cc4130ed091dddde9c3a23975e132b1bee06c3c6a43cd7",
+ "role": "Declares the typed benchmark run receipts."
+ },
+ {
+ "path": "external/ObjectState/src/objectstate/codegen.py",
+ "sha256": "347d2697c061a35e6e440e373fe4e8bfce1ad92b500e98da07faa26f6a3a1356",
+ "role": "Registers the pycodify formatters that write lazy dataclasses from their raw field values."
+ },
+ {
+ "path": "external/ObjectState/src/objectstate/scope_key.py",
+ "sha256": "d0dd6e56cebf413d6869b07c7b9d8668ba954ed5aeead36c70288920a5b412b2",
+ "role": "Declares ScopeKey, the scope grammar ObjectState and pyqt-reactive share."
+ },
+ {
+ "path": "external/pycodify/src/pycodify/paths.py",
+ "sha256": "f74983cd801215505bbd94f33d42718fa8ba8efdebbbdbfa5174bc56496af2e1",
+ "role": "Implements document-level factoring of absolute paths in generated source."
+ },
+ {
+ "path": "external/pyqt-reactive/src/pyqt_reactive/widgets/__init__.py",
+ "sha256": "ddcf678b0dc5c03aaf11d2b6f12bb9cda681a29f0105925c51a5b9263d751716",
+ "role": "Exports the generic widget set, including the editable table, grid selector and time-travel widgets."
}
],
- "findings": [
- "The package ownership map reduced ArrayBridge to memory conversion and omitted its callable metadata and framework-local execution capabilities.",
- "The package ownership map omitted python-introspect's callable-declaration projection responsibility.",
- "The source-checkout note linked to a duplicate Markdown setup procedure instead of the canonical audited repository how-to.",
- "The package ownership reference omitted ArrayBridge child environments and metaclass-registry logging-safe discovery imports.",
- "The OpenHCS-owned package map omitted the newly distributed benchmark package and its execution-versus-inspection ownership boundary."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/concepts/module_structure.rst in full and compared it against the changed authorities: pyproject.toml (pins raised to zmqruntime 0.6.2, objectstate 2.1.1, python-introspect 0.2.8, polystore 0.6.1; mahotas moved within the core dependencies; still eight first-party foundation distributions) and benchmark/control.py (the report prints the observation scope's name instead of an enum value); the ownership map, foundation list and benchmark paragraph are unchanged.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/module_structure.rst in full and compared it against the changed authority pyproject.toml (zmqruntime 0.6.2, pycodify 0.2.0, objectstate 2.1.1, python-introspect 0.2.7, pyqt-reactive 0.5 floors) and the U3/L6 moves behind them; confirmed the openhcs/ area map, the eight foundations and the OMERO classes, and updated the ObjectState, pyqt-reactive and pycodify ownership lines.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/module_structure.rst against B6's later changes to openhcs/microscopes/omero.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/module_structure.rst against the later B6 changes to pyproject.toml, openhcs/microscopes/omero.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/module_structure.rst in full and compared it against the changed authorities: pyproject.toml, external/python-introspect/README.md, openhcs/core/config.py, unified_registry.py, pipeline_import.py, openhcs/runtime/viewer_protocol.py, external/zmqruntime client, ArrayBridge README and types, metaclass-registry discovery, benchmark CLI, run_receipt.py and control.py; listed the current openhcs/ top-level packages, confirmed the OMERO address and parser classes, the eight foundation distributions and the benchmark MCP capabilities (now session-backed), and added the domains and authoring areas.",
"On 2026-08-27, re-read docs/source/concepts/module_structure.rst in full against the python-introspect 0.1.12 floor, declaration-owned catalogue inputs, and CellProfiler import validation; challenged the owner map and found no prose drift.",
"Compared every factual claim directly with the current hash-pinned authorities: pyproject.toml; .gitmodules; openhcs/core/config.py; openhcs/processing/backends/lib_registry/unified_registry.py; openhcs/interop/cellprofiler/pipeline_import.py; openhcs/runtime/viewer_protocol.py; external/arraybridge/README.md; external/arraybridge/src/arraybridge/types.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -824,7 +930,7 @@
},
{
"path": "docs/source/concepts/pipelines_and_steps.rst",
- "source_sha256": "ae82cf8baa401e60948956fd26590ae6d32c0a08db147df5c7a70ab09398eed6",
+ "source_sha256": "396b99971bf876c9176f9e351fe9ca556838f9f111d79db736cad89d2dc0628c",
"audience": [
"pipeline authors"
],
@@ -833,33 +939,53 @@
"authority": [
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
- "role": "Declares the public pipeline document, step collection, and serialisable declaration boundary."
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
+ "role": "PipelineDocument holds a PipelineConfig and an ordered FunctionStep list."
},
{
"path": "openhcs/core/steps/function_step.py",
"sha256": "8e1243404095691030e9ebb95bb5611679f96c66c6b6819ae71e4b41249e74bc",
- "role": "Declares FunctionStep and its callable, chain, and dictionary composition forms."
+ "role": "Declares FunctionStep and its callable, tuple, chain and dictionary func forms."
},
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares ProcessingConfig (variable_components, group_by, input_source) and PipelineConfig."
},
{
- "path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compiles pipeline declarations into validated step plans and execution bundles."
+ "path": "openhcs/core/orchestrator/orchestrator.py",
+ "sha256": "a7097eec86c5966552bcb546a2161d1a08e6a4c630e6b156de3b8354b0026b54",
+ "role": "Declares PipelineOrchestrator.compile_pipelines over selected partition values, returning CompiledExecutionBundle."
+ },
+ {
+ "path": "openhcs/core/compiled_execution.py",
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "Declares the CompiledExecutionBundle fields: runtime and transport contexts, worker assignments and runtime environment plan."
+ },
+ {
+ "path": "openhcs/authoring/session/compile_batch.py",
+ "sha256": "3e7397caa5ea1b38c1abc47d363e37fc01b0b601ab1e6b37ed00226f7aa51f42",
+ "role": "Compiles the selected datasets through the session before execution for GUI and MCP clients."
+ },
+ {
+ "path": "openhcs/interop/cellprofiler/pipeline_import.py",
+ "sha256": "5aa8895c9f533603b7ef972202141e2439473f0604b97d66f5a027b8b3cd1c83",
+ "role": "import_cellprofiler_pipeline returns the lowered steps with a populated PipelineConfig."
},
{
"path": "tests/unit/test_pipeline_document.py",
- "sha256": "2c18222ac2a34ee267788063a374112b17874c64693bfe542669c4183bc90c36",
- "role": "Proves pipeline-document construction, step mutation, serialisation, and declaration invariants."
+ "sha256": "4fe05636d5a9eb93f576904a774d30653a3686c7c8f61031ec0d316854cf5303",
+ "role": "Proves pipeline-document construction and declaration invariants."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/concepts/pipelines_and_steps.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1: ProcessingConfig.input_source now holds an InputSource class, default ReadPreviousStep), openhcs/core/orchestrator/orchestrator.py and openhcs/authoring/session/compile_batch.py (E1: orchestrator states are classes) and openhcs/interop/cellprofiler/pipeline_import.py (same InputSource classes, K2 imports); the page describes previous-step and pipeline-start input in prose without naming the old members, its FunctionStep example still validates, and compile_pipelines still returns a CompiledExecutionBundle.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/pipelines_and_steps.rst in full and compared it against the changed authorities: openhcs/core/pipeline_document.py (pycodify generate_python_source after L6), openhcs/core/config.py and openhcs/core/orchestrator/orchestrator.py (K4 moved the source-binding config classes and source_bindings_defaults_to_base into config.py), openhcs/interop/cellprofiler/pipeline_import.py and tests/unit/test_pipeline_document.py; the FunctionStep example, the func pattern forms, PipelineConfig source declarations, CellProfiler import result and PipelineOrchestrator.compile_pipelines returning CompiledExecutionBundle still hold.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/pipelines_and_steps.rst against B6's later changes to openhcs/core/config.py, openhcs/core/orchestrator/orchestrator.py, openhcs/authoring/session/compile_batch.py, openhcs/interop/cellprofiler/pipeline_import.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/pipelines_and_steps.rst against the later B6 changes to openhcs/core/config.py, openhcs/core/orchestrator/orchestrator.py, openhcs/authoring/session/compile_batch.py, openhcs/interop/cellprofiler/pipeline_import.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/pipelines_and_steps.rst in full and compared it against the changed authorities: the page (example already on Microscopy axes), openhcs/core/pipeline_document.py, openhcs/core/config.py, openhcs/core/pipeline/compiler.py, tests/unit/test_pipeline_document.py; checked the example imports, compile_pipelines signature, CompiledExecutionBundle fields, the session compile path and the CellProfiler import return, and corrected the compile section and axis wording.",
"Re-read docs/source/concepts/pipelines_and_steps.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline_document.py; openhcs/core/steps/function_step.py; openhcs/core/config.py; openhcs/core/pipeline/compiler.py; tests/unit/test_pipeline_document.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -871,7 +997,7 @@
},
{
"path": "docs/source/concepts/storage_system.rst",
- "source_sha256": "6457c0c132bd45f195274e51b255f45713b5efda9ee7ada352de556ac4aa1dde",
+ "source_sha256": "7d868cabb7acf21d703b8f78c6df3b96ee45ac2e1e4d432edfe3aee75be27e96",
"audience": [
"pipeline authors",
"storage integrators"
@@ -881,56 +1007,64 @@
"authority": [
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares VFSConfig read/intermediate/materialization backends and the fieldless OpenHCS ZarrConfig and TiffConfig subtypes."
},
{
"path": "openhcs/core/runtime_stores.py",
- "sha256": "5f323ec8edd4221ee30e34ed0c9c09e0f4e45ef502749c1a2d331b7a5452af99",
- "role": "Implements scoped typed runtime-value storage and lookup for compiled execution."
+ "sha256": "35fe71906bfd16842de347776a0c5cc846550ad490cc3628885129d746ec6f31",
+ "role": "Declares RuntimeValueStore with explicit replace and retained observation history."
},
{
"path": "openhcs/core/steps/function_artifact_materialization.py",
- "sha256": "18c809a2d511501c4e308f6e92c18d5bb59744d4d3f59bb2bfc2507f2a2bdd03",
- "role": "Owns the rules that materialise function outputs through the configured storage backend."
+ "sha256": "dfa64f98f6033bb76d5a167da7d20c4bd531117dceb21a20bd1a8b14768462f6",
+ "role": "Materialises planned function outputs and forms filenames without changing artifact metadata."
},
{
- "path": "external/PolyStore/src/polystore/base.py",
- "sha256": "ae641affae21b61448816a14bd68b7f2f3fc0082f0f1fbe51079ed32520450b1",
- "role": "Declares the storage backend interface and lifecycle shared by local and remote materialisation backends."
+ "path": "openhcs/core/pipeline/function_contracts.py",
+ "sha256": "67a654102b1d60c0a0f90e64ccaf3a8c177035cd11a0346734f872b329783d74",
+ "role": "Declares the @artifact_outputs decorator named by the page."
},
{
"path": "external/PolyStore/src/polystore/config.py",
- "sha256": "62c8acad3da3abed8ccf7f1166fc6a0d094be1c3e842cec6446780541df292a0",
- "role": "Declares lossless disk-TIFF codec settings, defaults, and path-specific batching."
+ "sha256": "34313f3c12056a2a316f7f4712597444b269106b0f6786aca00c5a450d439daf",
+ "role": "Declares ZarrConfig, the compressor factories, and TiffConfig with NONE and DEFLATE codecs and compression_level."
+ },
+ {
+ "path": "external/PolyStore/src/polystore/base.py",
+ "sha256": "8f2082ade15072bc8e8eba363c75a1d1cff7c67c74845e6485f69eab244634dc",
+ "role": "Declares the storage backend interface and lifecycle."
},
{
"path": "external/PolyStore/src/polystore/disk.py",
- "sha256": "cf089b8c79f86099a7cea1b6cbe43ceaec6331196a44b43183f2e64b7746c219",
- "role": "Applies TIFF codec settings in the disk writer without changing other formats."
+ "sha256": "58733bc3890a9062572fedccb5643a382ce69d31486d496cd7f6c1ff9ac991c6",
+ "role": "Applies TIFF codec settings in the disk writer only."
},
{
"path": "external/arraybridge/src/arraybridge/decorators.py",
- "sha256": "99823105b0ae59054df1d84013633b8b6628d0a6dc3326383199ac7c65c69bed",
- "role": "Applies declared dtype conversion to a callable result before materialisation can persist it."
+ "sha256": "28f9bf4770bd4cc1ef34045fdef28065063ede029296f62b8a6c4140cc88e40e",
+ "role": "Applies declared dtype conversion to a callable result before storage."
},
{
"path": "tests/unit/test_runtime_value_store.py",
- "sha256": "8900a7b83b59d0a40f9698dbafaaba778df982f9d7a0b628f80eab97335aeab0",
- "role": "Proves scoped typed runtime-value insertion, lookup, isolation, and cleanup."
+ "sha256": "17e15a64a4e84437f6523f872bad11ea7d813c50c1c41fc86ca7822379a7ce27",
+ "role": "Proves scoped typed runtime-value insertion, replacement and lookup."
},
{
"path": "tests/unit/test_tiff_output_compression.py",
- "sha256": "458f576e545a033ed9e23b9dc349ab92c19d296ec7f0e269ea89884cd9b82942",
- "role": "Proves configured main-flow and named-artifact disk-TIFF writes round-trip exactly."
+ "sha256": "4db7b7c47ce17a9476c7af6984daaa14b2cea7c5ae972d32aa6e6ad9449d37da",
+ "role": "Proves main-flow and named-artifact disk-TIFF writes round-trip exactly."
}
],
- "findings": [
- "The storage explanation did not distinguish optional disk-TIFF compression from Zarr compression or semantic materialization.",
- "The compression explanation did not contrast lossless codec selection with the existing value-changing callable dtype policy."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/concepts/storage_system.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1: VFSConfig.materialization_backend now holds a MaterializationBackend class, default MaterializeToDisk; read_backend still defaults to Backend.AUTO and intermediate_backend to memory; TiffConfig and ZarrConfig still fieldless PolyStore subtypes), openhcs/core/runtime_stores.py and openhcs/core/pipeline/function_contracts.py (K2 imports only), openhcs/core/steps/function_artifact_materialization.py (K2: output_plan is required and viewer capture reads the artifact's streaming declaration; filename identity is still formed apart from artifact metadata), tests/unit/test_runtime_value_store.py and tests/unit/test_tiff_output_compression.py (K2 imports, NameByArtifactName class); the page names backends in prose (disk, Zarr) and no claim changed.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/storage_system.rst in full and compared it against the changed authorities: openhcs/core/config.py (PartitionFilterMode family and the source-binding config classes after K4; VFSConfig, ZarrConfig and TiffConfig unchanged), openhcs/core/runtime_stores.py and tests/unit/test_runtime_value_store.py (hand-written RuntimeArtifactLocation/RuntimeArtifactAddress to_dict/from_dict replaced by python-introspect to_jsonable/dataclass_from_mapping after K6), openhcs/core/pipeline/function_contracts.py (CallableDeclaration rename, artifact_input_parameter_names); the page names none of the removed methods, and its backend intents, RuntimeValueStore replace/history and @artifact_outputs claims still hold.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/storage_system.rst against B6's later changes to openhcs/core/pipeline/function_contracts.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/storage_system.rst against B6's later changes to openhcs/core/config.py, openhcs/core/runtime_stores.py, openhcs/core/steps/function_artifact_materialization.py, openhcs/core/pipeline/function_contracts.py, tests/unit/test_runtime_value_store.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/concepts/storage_system.rst against the later B6 changes to openhcs/core/config.py, openhcs/core/runtime_stores.py, openhcs/core/steps/function_artifact_materialization.py, openhcs/core/pipeline/function_contracts.py, tests/unit/test_runtime_value_store.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/concepts/storage_system.rst in full and compared it against the changed authorities: the page, openhcs/core/config.py, openhcs/core/runtime_stores.py, function_artifact_materialization.py, PolyStore base/config/disk, ArrayBridge decorators, and the runtime-store and TIFF compression tests; confirmed the three VFSConfig backends and AUTO default, the fieldless ZarrConfig/TiffConfig subtypes, the TIFF NONE/DEFLATE codecs, the @artifact_outputs decorator and RuntimeValueStore replace/observation behaviour, and added openhcs/core/pipeline/function_contracts.py as the decorator owner.",
"Re-read docs/source/concepts/storage_system.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/config.py; openhcs/core/runtime_stores.py; openhcs/core/steps/function_artifact_materialization.py; external/PolyStore/src/polystore/base.py; tests/unit/test_runtime_value_store.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -947,7 +1081,7 @@
},
{
"path": "docs/source/getting_started/getting_started.rst",
- "source_sha256": "151b7d49018e4ef31c21bd96729c1ccd3af746a82a5d239bd27bf163474c067c",
+ "source_sha256": "93d8e1a92b8d9fdda4f505d1565bc4118dc4688d5ac555cdd3ec3c3a8f88c45b",
"audience": [
"new desktop users",
"Python installers"
@@ -957,107 +1091,97 @@
"authority": [
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
"role": "Declares the package version, Python requirement, dependency extras, scripts, and distribution metadata."
},
{
"path": "openhcs/cli.py",
- "sha256": "51c2570a5a49c83e826c3a2e85943a2c39593e5e30e1ce8af396ec4a4c724df1",
+ "sha256": "59025ea51309dbae40416d75fc203287657a109379f65072d77cff41c712e11e",
"role": "Declares the installed OpenHCS command entry points and their launch routing."
},
{
"path": "openhcs/mcp/installed_demo.py",
- "sha256": "4ea6c0730db6d3938eea1cac558dd46ea5c956ef151a5d68b8ea956e8bf57655",
+ "sha256": "f89ba094072c46c1ed5504c7b4e786aa3806776615330f4e32f2b07f19555100",
"role": "Implements the installed-package MCP demonstration used by the getting-started verification flow."
},
{
- "path": "openhcs/desktop_installation.py",
- "sha256": "244ade0711356f68df9f376e2517dfe1c9fb77cc960c517f7c4e8ee418b5711f",
- "role": "Nominally owns only the desktop extras, Python, binary-wheel, and uv policy shared by native installation and staged updates."
+ "path": "openhcs/desktop/installation.py",
+ "sha256": "b2124aaa3e4ef4a310f91a51c1879de41de1818b25fc619cc7cdad888c4202b6",
+ "role": "Desktop install profile: package extras, Python minor, binary-only packages, uv release and suppressed pip/uv package-source variables shared by native installation and staged updates."
},
{
"path": "scripts/render_installer_contract.py",
- "sha256": "da9045e1012cb0a71ff0dfd2f2d9fc98232299f5a2f019b01228e344cf3b40e7",
+ "sha256": "1be39dfcc514e2f6f544e6907b3af0992485d3752877502fc761f8da760a7785",
"role": "Projects installer policy together with package entry points and brand identity into an exact-version native contract."
},
{
"path": "packaging/installers/windows/Install-OpenHCS.ps1",
- "sha256": "f94eb1c0460cab7dca770a600d889bf55ae98f1aa652b2755de0ecc906ee83d8",
+ "sha256": "ca368082f3d7a414f9e9136b2c5ae459bfb2a9a2a37c096d06eeb93d5c403358",
"role": "Implements the Windows installer, short managed paths, package installation, and shortcut creation."
},
{
"path": "packaging/installers/macos/install-openhcs.sh",
- "sha256": "b78b6b5849265926f83f77f392f7f4ac74ce5968f62e0b6972c6ce7976498203",
+ "sha256": "3a0f43c57c14bd7f8ceeb38464e174728f903e0ec524d980dbdb2b5f9bc11544",
"role": "Implements the macOS application installer, managed environment creation, package installation, and launcher setup."
},
{
- "path": "packaging/installers/macos/README.md",
- "sha256": "f7e7a45c5e0b2a80cd2360f9dced3717738c103181c148330437f951de54be3a",
- "role": "Defines the exact actionable Gatekeeper recovery sequence for the unsigned bootstrap period."
+ "path": "tests/installer/test_macos_simple_installer.py",
+ "sha256": "c7127f024a17e2408842522969a3a5626c6aa070a23fc209df045996537849cf",
+ "role": "Checks that the user-facing Gatekeeper instructions keep the exact Open Anyway control label for the unsigned bootstrap."
},
{
"path": "scripts/smoke_installed_desktop.py",
- "sha256": "f745f93e245e5c1b02f6e0ece3a4a0e2af086c3cab77282e3907a167f5ce0a7f",
- "role": "Exercises the installed distribution, launchers, MCP/runtime path, viewers, and packaged restart worker outside the source checkout while deriving the GUI subprocess ceiling from the installed-GUI smoke timing authority."
- },
- {
- "path": "tests/installer/test_installed_desktop_smoke.py",
- "sha256": "5ac9f0e9e0e2ffe73cbab940da132421424e355c354b7476a8a072090c5aad00",
- "role": "Proves platform smoke orchestration passes the exact installed Python, install root, environment, and declaration-derived GUI operation and process-exit budgets to the installed verifiers."
+ "sha256": "d5ce5e2a2b3590be2101b487b2957b270f386dcf47b4c197f1ce4c2ab6aa9d65",
+ "role": "Exercises the installed distribution, launchers, MCP/runtime path, viewers, and packaged restart worker outside the source checkout."
},
{
"path": "tests/installer/test_installer_contract.py",
- "sha256": "42a551a7806f043ff464402795bc493ab8ee750fa0dcfa1e1822a4b6e79f8405",
+ "sha256": "cc7273eec242a0374d38f8aef9c6bb38f29b1ec5ba5141e2a648606a2b3db598",
"role": "Proves both native installers and staged updates consume the same declaration-owned package and binary-wheel policy."
},
{
- "path": "openhcs/pyqt_gui/services/desktop_update.py",
- "sha256": "42910339c4f11afe8d413dfbe024910a20ac6b275c563d1c6947452f8baec908",
+ "path": "openhcs/desktop/update.py",
+ "sha256": "23ffce8fe2362d8651ea90d2ef9601d193658e4fcef6aef8964e888f43014a3c",
"role": "Builds a staged desktop-update plan from the active managed environment."
},
{
- "path": "openhcs/pyqt_gui/services/desktop_update_worker.py",
- "sha256": "c8d157ea5d61b5cb52f09f3835b430fe1ef3358822b988451e4ee2a6832729bf",
+ "path": "openhcs/desktop/update_worker.py",
+ "sha256": "917c5d2d4fb7693147f03c14096535d198151c65123b6d256a8b871cbecb54a7",
"role": "Creates and verifies a sibling replacement environment before publishing it and preserves the previous environment."
},
{
- "path": "openhcs/desktop_deployment.py",
- "sha256": "d5393386a14b693af3356484bdcb560e8a3cc0faea061352f46854c0cfead051",
+ "path": "openhcs/desktop/deployment.py",
+ "sha256": "aafb7c77756cd3cd9de71bec7a6360058cd14e3aebc27b5e014c3b74271307cf",
"role": "Owns platform-specific managed environment paths, launchers, shortcuts, and atomic desktop publication."
},
{
- "path": "tests/unit/test_desktop_deployment.py",
- "sha256": "258a657c79a5ad96eacc06a9d4436018ffa10d7ca6d4ae56d5630e0a81c52238",
- "role": "Proves Windows launcher deferral is limited to a sharing violation against a valid live GUI launcher."
- },
- {
- "path": "tests/unit/pyqt_gui/test_desktop_update.py",
- "sha256": "b79c4c2620f271ad3a2c08f3002ec9d2c17c8f80b0b72b8ac6b1baba9dd8815c",
- "role": "Proves the running environment is not mutated while a sibling update candidate is prepared."
- },
- {
- "path": "tests/unit/pyqt_gui/test_desktop_update_worker.py",
- "sha256": "68dfc122f38164cf6eb47e856bfe369002e03e3b0699ad6bb2fda9f45db2dc8b",
+ "path": "tests/unit/desktop/test_desktop_update_worker.py",
+ "sha256": "b6b1a1d1e232c3de86db74b0ef7695a8af6225ddc48f33e2eb0a665a3dc5af93",
"role": "Proves staged installation, verification, publication, rollback, and restart behaviour."
},
{
"path": ".github/workflows/integration-tests.yml",
- "sha256": "c1ec94eab493bc46986c55b8b1428d9a0a5c0eaf5789f2dddbc06df2c0843ff7",
- "role": "Exercises Windows and macOS native installers and their staged-update transition in installed environments."
+ "sha256": "b2bcf966b13447f4f584dd27ea250d8c0adbf41faed37b61c5a1a30aec2d7aed",
+ "role": "Exercises Windows and macOS native installers, staged updates, and cellprofiler-compat installation on Python 3.14."
},
{
"path": "tests/unit/test_installed_mcp_demo.py",
- "sha256": "f5d4a719a7902b364b551a3cbc4a816ccd7f46e5d9abbc87bf2d53cb0180053b",
- "role": "Proves the documented installed MCP verification flow, including progress-aware stall detection."
+ "sha256": "8babc443f7ec59cebebd3772633dde9ae9c49b125066cd5e7c7d527dc74c2a09",
+ "role": "Proves the documented installed MCP verification flow."
}
],
- "findings": [
- "The update migration needed staged-update implementation and CI evidence; the macOS recovery sequence now preserves the actionable Open Anyway control label while linking Apple's current guidance.",
- "The Windows update path now preserves a valid live stable launcher, publishes the verified sibling environment, and retries the deferred launcher cache after the process releases it.",
- "Workstation pip configuration could redirect or stall native installation and staged updates; each managed mutation path now suppresses pip configuration files and inherited primary and extra index overrides, and native CI injects an unreachable index to prove that boundary."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/getting_started/getting_started.rst against .github/workflows/integration-tests.yml: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/getting_started/getting_started.rst against .github/workflows/integration-tests.yml: the unit-tests job now runs three file-isolated xdist workers; no claim this page makes changes.",
+ "On 2026-10-11 (B6), re-read docs/source/getting_started/getting_started.rst in full and compared it against the changed authorities: pyproject.toml (python-introspect 0.2.8), openhcs/mcp/installed_demo.py, openhcs/desktop/update.py, openhcs/desktop/update_worker.py, openhcs/desktop/deployment.py and tests/unit/desktop/test_desktop_update_worker.py (E1: update-check origin, failed-update response and runtime platform became class families); the page names none of those types; the extras, Python range, openhcs and openhcs-mcp-demo scripts, and the verified-replacement update with the previous environment preserved are unchanged, so nothing was changed.",
+ "On 2026-10-11 (B6), re-read docs/source/getting_started/getting_started.rst in full and compared it against the changed authorities: pyproject.toml (first-party floors moved to zmqruntime 0.6.2, pycodify 0.2, objectstate 2.1.1, python-introspect 0.2.7, pyqt-reactive 0.5). The page states no dependency floors; rechecked requires-python (3.11-3.14) and every named extra (gui, viz, bioformats, mcp, cellprofiler-compat, napari, fiji, gpu) against pyproject.toml; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/getting_started/getting_started.rst against B6's later changes to openhcs/mcp/installed_demo.py: the writability probe makes one exclusive create attempt, the installed demo reports ViewerType.value, the unit session pre-creates its QApplication, and the GUI journey waits for each batch to end. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/getting_started/getting_started.rst against B6's later changes to openhcs/mcp/installed_demo.py, .github/workflows/integration-tests.yml: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/getting_started/getting_started.rst against B6's later changes to tests/unit/test_installed_mcp_demo.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/getting_started/getting_started.rst against the later B6 changes to pyproject.toml, tests/unit/test_installed_mcp_demo.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/getting_started/getting_started.rst in full and compared it against the changed authorities: pyproject.toml, openhcs/cli.py, openhcs/mcp/installed_demo.py, render_installer_contract.py, both native installers, smoke_installed_desktop.py, installer tests, integration-tests.yml, test_installed_mcp_demo.py, and the desktop modules moved into openhcs/desktop by 73a2aece2; confirmed the extras, entry points (openhcs, openhcs-mcp-demo), --log-level handling in pyqt_gui/launch.py, the demo's generated plate / neurite preset / nonzero Napari payload check / output_root report, the desktop profile extras and pip/uv override suppression, and sibling-environment update with a preserved previous environment; removed the false Python 3.13 limit on cellprofiler-compat and replaced the moved authorities.",
"On 2026-08-27, re-read docs/source/getting_started/getting_started.rst in full after the python-introspect dependency floor advanced to 0.1.12; challenged the installation how-to flow and confirmed that the supported procedure is unchanged.",
"On 2026-08-27, re-read the complete installation how-to after the shared workflow made its Python quality baseline job-owned; native installation, staged update, and package-index isolation claims remain unchanged.",
"Compared every factual claim directly with the current hash-pinned authorities: pyproject.toml; openhcs/cli.py; openhcs/mcp/installed_demo.py; openhcs/desktop_installation.py; scripts/render_installer_contract.py; packaging/installers/windows/Install-OpenHCS.ps1; packaging/installers/macos/install-openhcs.sh; packaging/installers/macos/README.md; scripts/smoke_installed_desktop.py; tests/installer/test_installed_desktop_smoke.py; tests/installer/test_installer_contract.py; openhcs/pyqt_gui/services/desktop_update.py; openhcs/pyqt_gui/services/desktop_update_worker.py; openhcs/desktop_deployment.py; tests/unit/test_desktop_deployment.py; tests/unit/pyqt_gui/test_desktop_update.py; tests/unit/pyqt_gui/test_desktop_update_worker.py; .github/workflows/integration-tests.yml.",
@@ -1127,7 +1251,7 @@
},
{
"path": "docs/source/guide_for_biologists/basic_interface.rst",
- "source_sha256": "2478a75261b5e9ab12e01f897561485fb004da645f15bf030870fda628162b41",
+ "source_sha256": "8e9ad410dfedb59a70fa07c8f806be298b136fb89027d5c53e9cc18cc6bd680b",
"audience": [
"desktop users",
"MCP-assisted operators"
@@ -1137,72 +1261,67 @@
"authority": [
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
- "role": "Applies the same preview policy to both managers at startup and after configuration save."
- },
- {
- "path": "tests/unit/pyqt_gui/test_ui_config_lifecycle.py",
- "sha256": "eeb7c260d3ae8bc7b5e6d94917b1e5e9a214feac9e2a0c2651552950cfe3a179",
- "role": "Prevents the removed Textual package authority and local UI mirrors from returning."
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
+ "role": "Dock layout (system monitor above, Datasets and ZMQ server manager left, Pipeline Editor right), View/Tools/Help menus, and preview-policy application to both panes."
},
{
"path": "openhcs/pyqt_gui/widgets/plate_manager.py",
- "sha256": "6fa48fbcd58e47eb7132d5d9f86a002c6a0e8ff077d9654a03a791629e135378",
+ "sha256": "b6e56da089cff746c9f0f41ca4b4d947942d4a71d1b290a1d8b76c3b7732c31e",
"role": "Implements plate import, selection, pipeline assignment, and execution controls."
},
{
"path": "openhcs/pyqt_gui/widgets/pipeline_editor.py",
- "sha256": "450d311712d032d2f811de28a618915bc7e8a7f389e9f23c8a12c3e6ae273206",
+ "sha256": "f60e195031b99bd02d8e4cdf1b42ca213fc5669526bc30348ce3ddff14f6876b",
"role": "Implements step editing, pipeline compilation controls, and declaration feedback in the main UI."
},
{
"path": "openhcs/agent/services/ui_bridge_service.py",
- "sha256": "37015a88828820e7ae98bf804f42c37f1fa26009b1ad28b3c537f65a656e943a",
+ "sha256": "a2d682ad7841a7d85173f792961cae2dc5546fb4fcf39caa85f0bbd6ed062b01",
"role": "Owns agent-to-UI declaration inspection and mutation, including code and form synchronisation."
},
{
"path": "tests/unit/pyqt_gui/test_ui_agent_bridge.py",
- "sha256": "1feee4bb413738f04e81cbdf26244dc0d585a68921e6c7749a941a08518007a1",
+ "sha256": "422be9298e5120c898bd7110884ca770f1f033f07fd69be4ac0caa978d3014f4",
"role": "Proves bidirectional UI-bridge inspection and mutation of live declarations, plus truthful rejection of unsupported window-navigation targets."
},
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
- "role": "Owns the Pipeline Editor document rule that missing pipeline_config selects PipelineConfig()."
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
+ "role": "PipelineDocumentCodec rule that pipeline_steps is required and a missing pipeline_config selects PipelineConfig()."
},
{
"path": "tests/unit/pyqt_gui/test_pipeline_code_document_transactionality.py",
- "sha256": "b7e546e904b897f13d76f0c523e751afa541a75c627349ba79a3a87313d30f4c",
+ "sha256": "6604f4cc9ff8d1f3824f0479c4947c7d264aff59c7b3e139d670b659b63b39ec",
"role": "Proves Pipeline Editor code validates and applies pipeline_steps without an explicit pipeline_config."
},
{
"path": "openhcs/pyqt_gui/widgets/shared/services/pipeline_editor_workflows.py",
- "sha256": "6664126ac4daa851cacff5014e04a0c09a56bbc76f62661ff97d713e9137d447",
+ "sha256": "06e54377bc594beef653baa9cfaf4de0031e754da1e7b1ab3a22ac67a922b68f",
"role": "Applies Pipeline Editor documents and commits the reconciled editor state graph."
},
{
"path": "openhcs/agent/services/endpoint_function_catalog_service.py",
- "sha256": "977a74003819f2547548d322f4c5f0cb148b23fec20f31be4fa462bfcfb7ea04",
+ "sha256": "bc232090472554ab075f7800bbcc238e27a646a2d8225c87e5e443fb3c5d9f78",
"role": "Owns asynchronous preparation and reuse of the endpoint callable catalogue across desktop and local MCP authoring."
},
{
"path": "tests/unit/pyqt_gui/test_pipeline_editor_widget.py",
- "sha256": "8f28719c5bd9eb5681e67af8e1f7aceb445bcae97c12a13348f943c8b6bafa35",
+ "sha256": "6761f5c3526167053b22edf5271f3413730d7798e8db45dbc57ec1baa52515aa",
"role": "Proves Pipeline Editor code apply leaves the reconciled root, step, and nested function states saved."
},
{
"path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
"role": "Owns the documentation-enabled Function Selector capture declaration, accessibility text, capture target, and generated visual projection."
},
{
"path": "openhcs/pyqt_gui/services/ui_bridge_windows.py",
- "sha256": "edb992542dffb88a9cc4557dbd6647b76d5b68584dca27a4c9486ef545be74e9",
+ "sha256": "a10489e1bae296bc224b339ee32e2e99537175f891b86f901b4d7503d6f38f13",
"role": "Projects embedded manager navigation through the generic declared driver."
},
{
"path": "openhcs/pyqt_gui/config.py",
- "sha256": "a3eece2e5b2926fc6fc0edd197221da451ab7c0d0d6f4ffc8bb2ff865ad74bd4",
+ "sha256": "d092cb20206127484fbbc80a38a5152462b9b8991e848a814c85f564d51f4db8",
"role": "Embeds the persisted generic list-preview policy in UIConfig."
},
{
@@ -1212,7 +1331,7 @@
},
{
"path": "tests/unit/pyqt_gui/test_main_config_propagation.py",
- "sha256": "291dc45ca20ead6025c255a8109a2e6c55d90bd7858e80ad12484dafa733d7c5",
+ "sha256": "303d90b43bee7bd531a1c7664fbf9707a2e7be1696a5147614f93c40e5c7021f",
"role": "Verifies live preview policy propagation through the main configuration lifecycle."
},
{
@@ -1244,19 +1363,34 @@
"path": "external/pyqt-reactive/tests/test_list_preview_wrapping.py",
"sha256": "60bfe5b16e9f17f3fed1f88213e69644ad2bcf278d7012b446689fbc75e181a2",
"role": "Native Qt regressions prove row wrapping, disclosure, refresh/reorder preservation, graphical guides, structural action parity, and delegate paint acknowledgement."
+ },
+ {
+ "path": "openhcs/authoring/session/views.py",
+ "sha256": "296adbceb7db7a7f7ac3cb09dbe8bb6620dec588a3b593ef2239108298bee5c8",
+ "role": "DatasetListView and PipelineStepsView titles and the operations each pane binds."
+ },
+ {
+ "path": "openhcs/authoring/session/operations/datasets.py",
+ "sha256": "630a74c9e4fb026a7c47e78b3a07427c4d16c9630447a523b36dd8c73e17e430",
+ "role": "Compile/Connect/Connecting slot, Run availability (compiled, endpoint accepting requests) and Stop while a batch runs."
+ },
+ {
+ "path": "openhcs/agent/ui_bridge_identities.py",
+ "sha256": "66dcf838d08cac2004ab8f73db777f73270cc520697281cf4f94a5fc6af4dc64",
+ "role": "plate_manager, pipeline_editor and main_window widget identities and stable window titles used by MCP navigation."
}
],
- "findings": [
- "The desktop map still described the removed Textual implementation as retained.",
- "The Pipeline Editor reference previously implied an explicit PipelineConfig assignment; it now records the supported steps-only source form.",
- "The interface reference omitted selected-plate code-document scope and complete pipeline replacement semantics.",
- "The reference omitted the Pipeline Editor's code-apply commit semantics and the non-blocking endpoint-catalogue loading state.",
- "The agent-navigation reference did not distinguish successful window focus from declaration-owned field navigation or explain unsupported targets.",
- "Explained list-preview wrapping and detail settings, immediate two-panel updates, and unchanged full editor values.",
- "Clarified row-arrow overrides and repeated graphical guides, distinguishing shared wrapping defaults from row-owned display choices."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/basic_interface.rst against openhcs/authoring/session/operations/datasets.py: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/guide_for_biologists/basic_interface.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/basic_interface.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py, widgets/plate_manager.py, widgets/pipeline_editor.py, services/ui_bridge_windows.py, openhcs/pyqt_gui/config.py, openhcs/agent/services/ui_bridge_service.py, openhcs/authoring/session/views.py and the three test modules (E1: selection scope, orchestrator state, action status and update-check origin became class families; the error-dialog marker became a boolean); the page describes user-visible behaviour and names no changed type; all-plates versus selected code documents, Connect/Compile/Run/Stop control behaviour and the pane identities are unchanged, so nothing was changed.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/basic_interface.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py, widgets/plate_manager.py, widgets/pipeline_editor.py, shared/services/pipeline_editor_workflows.py, services/ui_bridge_windows.py, openhcs/core/pipeline_document.py and their tests (U3: DockPane, DockLayoutStore, ShortcutLifecycle, time-travel navigation, path cache and the external-editor switch moved to pyqt-reactive, GlobalEventBus broadcasts replaced by session events, ScopeKey scope ids; L6: pipeline documents render through pycodify generate_python_source; K6 debug command classes). The page describes behaviour, not those classes; rechecked the dock arrangement (system monitor above, Datasets and ZMQ server manager stacked left, Pipeline Editor right), the plate_manager window identity, list_previews on UIConfig, and that PipelineDocument still requires pipeline_steps; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/basic_interface.rst against B6's later changes to openhcs/authoring/session/views.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/basic_interface.rst against B6's later changes to openhcs/pyqt_gui/main.py, openhcs/pyqt_gui/widgets/pipeline_editor.py, openhcs/agent/services/ui_bridge_service.py, tests/unit/pyqt_gui/test_ui_agent_bridge.py, tests/unit/pyqt_gui/test_pipeline_code_document_transactionality.py, openhcs/pyqt_gui/widgets/shared/services/pipeline_editor_workflows.py, tests/unit/pyqt_gui/test_pipeline_editor_widget.py, openhcs/pyqt_gui/services/ui_bridge_windows.py, tests/unit/pyqt_gui/test_main_config_propagation.py, openhcs/authoring/session/views.py, openhcs/authoring/session/operations/datasets.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/basic_interface.rst against the later B6 changes to openhcs/pyqt_gui/main.py, openhcs/pyqt_gui/widgets/pipeline_editor.py, openhcs/agent/services/ui_bridge_service.py, tests/unit/pyqt_gui/test_ui_agent_bridge.py, tests/unit/pyqt_gui/test_pipeline_code_document_transactionality.py, openhcs/pyqt_gui/widgets/shared/services/pipeline_editor_workflows.py, tests/unit/pyqt_gui/test_pipeline_editor_widget.py, openhcs/pyqt_gui/services/ui_bridge_windows.py, tests/unit/pyqt_gui/test_main_config_propagation.py, openhcs/authoring/session/views.py, openhcs/authoring/session/operations/datasets.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/basic_interface.rst in full and compared it against the changed authorities: pyqt_gui/main.py, plate_manager.py, pipeline_editor.py, ui_bridge_service.py, ui_bridge_windows.py, pipeline_document.py, pipeline_editor_workflows.py, endpoint_function_catalog_service.py, gallery_catalog.py and their tests; confirmed the dock layout, menus (Image Browser, Log Viewer, Global Configuration, Custom Functions, Analysis Consolidation), Connect/Connecting/Compile/Run/Stop rules in the session operations, selected/all dataset code documents, steps-only pipeline documents, background catalogue preparation, the ui-function-selector gallery scenario and unsupported-navigation reporting; renamed the Plate Manager pane to its current Datasets title and pointed agent step 6 at session events.",
"Re-read docs/source/guide_for_biologists/basic_interface.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/pyqt_gui/main.py; tests/unit/pyqt_gui/test_ui_config_lifecycle.py; openhcs/pyqt_gui/widgets/plate_manager.py; openhcs/pyqt_gui/widgets/pipeline_editor.py; openhcs/agent/services/ui_bridge_service.py; tests/unit/pyqt_gui/test_ui_agent_bridge.py; openhcs/core/pipeline_document.py; tests/unit/pyqt_gui/test_pipeline_code_document_transactionality.py; openhcs/pyqt_gui/widgets/shared/services/pipeline_editor_workflows.py; openhcs/agent/services/endpoint_function_catalog_service.py; tests/unit/pyqt_gui/test_pipeline_editor_widget.py.",
"On 2026-08-22, removed the stale retained-Textual wording after the deprecated implementation and its local UI mirrors were deleted.",
@@ -1289,7 +1423,7 @@
},
{
"path": "docs/source/guide_for_biologists/configuration_reference.rst",
- "source_sha256": "ba3c4ef923c4f643c06accc8641c462b2f63a8e46694ef984dde96ef49a0d9c7",
+ "source_sha256": "023ca1f76200be284520e2f118005a7e0f131f88141d3693271be0782deafa95",
"audience": [
"imaging scientists",
"pipeline authors"
@@ -1299,67 +1433,73 @@
"authority": [
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
"role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
},
{
"path": "external/ObjectState/src/objectstate/object_state.py",
- "sha256": "735ce860ab650b281534e24e3c7354f37b64bb06832cf186d6b1f626f87108fe",
+ "sha256": "413a7bcd27695a823805cbc16a393569e30293a70c8d60d66dc6d56d3b589a4a",
"role": "Implements ObjectState provenance, inheritance resolution, and field-change propagation used by nested configuration objects."
},
{
"path": "openhcs/agent/services/config_service.py",
- "sha256": "c4dc3860e1720fe6b383aec8b0ddcb498eb219da74da6ccd6709547205193f22",
+ "sha256": "27a1d5d841b147e3ce35c3a358ca30b6d6efe6ed08a7db874648410b122a717f",
"role": "Resolves and mutates configuration through the registered ObjectState-backed configuration services."
},
{
"path": "external/python-introspect/src/python_introspect/annotation_types.py",
- "sha256": "dc126879a8a581cea0758eec4df18ec20c47ca4b1e135abde23699b65b790bde",
+ "sha256": "004a3ce2e214a184aaa3230ceaa89ccaa5f188c1c3d885aacad5222726c34b7a",
"role": "Owns declaration-derived enum discovery, accepted input strings, and member coercion across transparent annotation wrappers."
},
{
"path": "openhcs/pyqt_gui/services/ui_bridge_object_state.py",
- "sha256": "747c284c37e420092ffda9abbb8275fc7deaaff7f3676cecf3f5b1f324314744",
+ "sha256": "f615718186e4aaebdc12eec6bec6a97d47953322075c5b067974e5a16ecc8e80",
"role": "Projects declaration-derived accepted enum inputs through live ObjectState field help."
},
{
"path": "tests/unit/pyqt_gui/test_ui_agent_bridge.py",
- "sha256": "1feee4bb413738f04e81cbdf26244dc0d585a68921e6c7749a941a08518007a1",
+ "sha256": "422be9298e5120c898bd7110884ca770f1f033f07fd69be4ac0caa978d3014f4",
"role": "Proves live field help reports the exact accepted inputs for enum-backed configuration fields."
},
{
"path": "tests/unit/agent/test_mcp_self_description_contracts.py",
- "sha256": "67c7f8052c852beef3dd875b5e96c0c7635e001df13a585589e320955667a59d",
+ "sha256": "d569b936f38c66bbef3d0142518720669c3859f4a42945b42ebd7dbb153da73d",
"role": "Proves MCP self-description exposes the declared configuration and capability contracts."
},
{
"path": "tests/unit/test_configuration_mirror_deletion.py",
- "sha256": "26002ddfbbf89bf06ae54888645c6b8c90154536a16e93feee1a2fae713c4963",
+ "sha256": "8f9da50e930925ed3ecdd57add9cc919e008a9202c9e405ff922dcd1c9a418cc",
"role": "Proves configuration consumers use declaration-owned metadata without parallel field registries."
},
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
"role": "Owns default PipelineConfig construction for source documents that provide pipeline_steps only."
},
{
"path": "tests/unit/test_pipeline_document.py",
- "sha256": "2c18222ac2a34ee267788063a374112b17874c64693bfe542669c4183bc90c36",
+ "sha256": "4fe05636d5a9eb93f576904a774d30653a3686c7c8f61031ec0d316854cf5303",
"role": "Proves the default-config compatibility rule and the remaining source-document validation constraints."
},
{
"path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
"role": "Owns the documentation-enabled global, pipeline, step, and nested function configuration capture declarations, accessibility text, capture targets, and generated visual projections."
+ },
+ {
+ "path": "openhcs/domains/microscopy/config.py",
+ "sha256": "79b8baefdbf22b85c199807f62b8234daca6904633a574886661a1c2e3d03a9b",
+ "role": "Declares AnalysisConsolidationConfig, PlateMetadataConfig and the standalone ExperimentalAnalysisConfig named in the post-run section."
}
],
- "findings": [
- "The configuration explanation now distinguishes source-document omission, which selects PipelineConfig(), from normal lazy-field inheritance.",
- "The explanation did not tell agents that live field help projects accepted enum input strings, including member names for non-string values.",
- "The practitioner configuration map did not name the new optional TIFF compression family or distinguish its output scope from Zarr and viewer streams."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/configuration_reference.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1: MaterializationBackend and InputSource are registered class families selected through RegisteredClassChoices), openhcs/agent/services/config_service.py (family-valued fields decode from their registry key), openhcs/pyqt_gui/services/ui_bridge_object_state.py and tests/unit/pyqt_gui/test_ui_agent_bridge.py; the page already says the schema reports enum and registry values and names no member of the changed families; the previous-step versus pipeline-start description of input_source and the config family list still hold, so nothing was changed.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/configuration_reference.rst in full and compared it against the changed authorities: openhcs/core/config.py (K4: SourceBindingsConfig and StepSourceBindingsConfig now declared here; PartitionFilterMode became the IncludePartitions/ExcludePartitions class family offered through RegisteredClassChoices), external/ObjectState/src/objectstate/object_state.py (current file), openhcs/pyqt_gui/services/ui_bridge_object_state.py (field inspection and provenance moved to objectstate.inspection), openhcs/core/pipeline_document.py (pycodify generate_python_source) and their tests. The page states include/exclude selection without naming enum members, and still-accurate omitted pipeline_config -> PipelineConfig() behaviour; rechecked every config class it names, TiffConfig's PolyStore DEFLATE codec, the microscopy-domain AnalysisConsolidationConfig/PlateMetadataConfig/ExperimentalAnalysisConfig, and openhcs_describe_config_schema; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/configuration_reference.rst against B6's later changes to openhcs/core/config.py, openhcs/pyqt_gui/services/ui_bridge_object_state.py, tests/unit/pyqt_gui/test_ui_agent_bridge.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/configuration_reference.rst against the later B6 changes to openhcs/core/config.py, openhcs/pyqt_gui/services/ui_bridge_object_state.py, tests/unit/pyqt_gui/test_ui_agent_bridge.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/configuration_reference.rst in full and compared it against the changed authorities: the page's own unreviewed WellFilter-to-PartitionFilter rename, openhcs/core/config.py, ObjectState object_state.py, config_service.py, python-introspect annotation_types.py, ui_bridge_object_state.py, pipeline_document.py, gallery_catalog.py and their tests; imported GlobalPipelineConfig/PipelineConfig and AbstractStep to confirm every named config field and the step-overridable families, checked the PartitionFilterConfig inheritance tree, InputSource values, main_flow_axis_persistence_enabled, openhcs_describe_config_schema and the four gallery scenarios; replaced the step-snapshot wording, corrected the consolidation field summary and realigned the diagram.",
"Re-read docs/source/guide_for_biologists/configuration_reference.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/config.py; external/ObjectState/src/objectstate/object_state.py; openhcs/agent/services/config_service.py; tests/unit/agent/test_mcp_self_description_contracts.py; tests/unit/test_configuration_mirror_deletion.py; openhcs/core/pipeline_document.py; tests/unit/test_pipeline_document.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -1379,7 +1519,7 @@
},
{
"path": "docs/source/guide_for_biologists/domain_expert_onboarding.rst",
- "source_sha256": "99af4a6bf51653532d929d7008e20f515bf4ff1303a1eccfec7e7dd79be5ca4d",
+ "source_sha256": "53919654c9cb3da65d16f83a2d689ca59735968f3a7101f71b60920e573e2377",
"audience": [
"imaging scientists",
"scientists working with an MCP agent"
@@ -1389,61 +1529,76 @@
"authority": [
{
"path": "openhcs/agent/capabilities.py",
- "sha256": "7fe9ab91490dedd6f750a57386e91835cf1067a27d10079966510aa9821adaf0",
+ "sha256": "87c8e2e5aeb425b9a2eb690d48bdd6f5af7a327c389deac261d7e4e0da5d609f",
"role": "Declares the agent capability catalogue and safety metadata exposed through MCP self-description."
},
- {
- "path": "openhcs/agent/services/architecture_projection_service.py",
- "sha256": "b209098d78c5520fd34d744d17b10c1ef0375a9bc2655449f6f3f9908decce0e",
- "role": "Projects maintained architecture sources into agent-readable results without copying an architecture catalogue."
- },
{
"path": "openhcs/agent/services/knowledge_base_service.py",
- "sha256": "88e10e18c66eb3a8bc2ec5dd33493a9473510c0b0a9d355502d668042f32fdd9",
+ "sha256": "dfc26135b16f3827ced85b122cc1555c1325da1c5e805b421afcee9cdd7d91f3",
"role": "Discovers and serves the maintained knowledge and example corpus used by MCP clients."
},
{
"path": "openhcs/interop/cellprofiler/pipeline_import.py",
- "sha256": "b547c838b2c86fe7cc5fe12d6cbf6a6715e83055c2d8c1a109ac22566d22c51e",
+ "sha256": "5aa8895c9f533603b7ef972202141e2439473f0604b97d66f5a027b8b3cd1c83",
"role": "Imports CellProfiler modules into ordinary OpenHCS pipeline declarations through the interop boundary."
},
{
"path": "tests/unit/agent/test_architecture_projection_service.py",
- "sha256": "e820969a0499995097af17fbde9b4694f1626c0ca6d70c75fd20bd4d2cdd7be9",
+ "sha256": "78c3851d3b95bfd4518b82d5c1ff20c70822dcabb10da0257e8d1c794eed1c12",
"role": "Proves that architecture projections come from maintained sources and preserve requested scope."
},
{
"path": "tests/unit/test_cellprofiler_pipeline_import.py",
- "sha256": "353f0c1d8e0bc3552d2e418d792794ab0631daa16c78a3e10191b1cc1ea07d73",
+ "sha256": "67576f37b7cbe0595acea633265f8e14677ca3c1b8204630413330273dc4e58a",
"role": "Proves direct import rejects modules requiring interactive desktop input instead of omitting them."
},
{
"path": "benchmark/cellprofiler_benchmark_cli.py",
- "sha256": "2b92755c556c1d66d28ffa0af6e2ac61cac5af4631514382290a2af0f907c26b",
+ "sha256": "56a9b4e8bb7d8195095eb99a849f717e79ca37aa429c612804584cbd7939f420",
"role": "Owns packaged comparison execution and reporting commands."
},
{
"path": "benchmark/contracts/run_receipt.py",
- "sha256": "1704e1ab47411d25d382b95871c96c1e60c94db14b8ffd55f4dc6c8a917758b9",
+ "sha256": "4cd61bd33f8f6199b3cc4130ed091dddde9c3a23975e132b1bee06c3c6a43cd7",
"role": "Owns typed comparison-run provenance, lifecycle status, and rerun arguments."
},
{
"path": "benchmark/control.py",
- "sha256": "4706078a8fbeb516a52bc51600751ae86c39c3ef7e397a2f8cf13d49204db941",
+ "sha256": "b10dd87d7bc44063566e0c53d849091c0fc43a345b434c13467df1ede86ac892",
"role": "Owns read-only inspection of existing benchmark directories."
},
{
"path": "tests/unit/test_benchmark_control.py",
- "sha256": "ee75209799216c90cfa3a585dd9b7af99c99405f93e9fb5e4f900ea26d9d3ea0",
+ "sha256": "093b584eeb37cbb12089d5af082ad45acd779e9d2bdd5b3feaea08234fc22183",
"role": "Proves exact receipt, progress, artifact, path-policy, and MCP projection behavior."
+ },
+ {
+ "path": "openhcs/agent/services/architecture_projection_service.py",
+ "sha256": "66e19059af19bdbd9e0f06b8bb5dad43e34d57210b3fb230f6c998538d2fea4d",
+ "role": "Declares the cellprofiler_translation architecture topic served by openhcs_explain_architecture."
+ },
+ {
+ "path": "openhcs/interop/cellprofiler/module_artifact_declarations.py",
+ "sha256": "5442b56c7dd7c913d38d18c5a76660ad6006e147ffc8f790712fb79b301ff035",
+ "role": "Raises the import error for CellProfiler modules that require interactive desktop input."
+ },
+ {
+ "path": "openhcs/authoring/session/views.py",
+ "sha256": "296adbceb7db7a7f7ac3cb09dbe8bb6620dec588a3b593ef2239108298bee5c8",
+ "role": "DatasetListView title for the desktop pane that UI-owned work populates."
}
],
- "findings": [
- "The onboarding how-to did not tell scientists how to handle CellProfiler modules that require interactive desktop input.",
- "The Official30 guidance did not tell readers where reviewed comparison execution lives or distinguish it from read-only MCP result inspection."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/domain_expert_onboarding.rst against benchmark/cellprofiler_benchmark_cli.py: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/domain_expert_onboarding.rst in full and compared it against the changed authorities: openhcs/interop/cellprofiler/pipeline_import.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, tests/unit/test_cellprofiler_pipeline_import.py (E1 input-source classes, K2 artifact package), benchmark/cellprofiler_benchmark_cli.py, benchmark/control.py (export scope family) and openhcs/authoring/session/views.py; the page names none of the changed types; the CellProfiler translation list, the unsupported-interaction report and the read-only benchmark inspection routes are unchanged, so nothing was changed.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/domain_expert_onboarding.rst in full and compared it against the changed authorities: openhcs/interop/cellprofiler/pipeline_import.py, module_artifact_declarations.py, openhcs/agent/services/architecture_projection_service.py and tests/unit/test_cellprofiler_pipeline_import.py (K4: SourceBindingsConfig and StepSourceBindingsConfig now imported from openhcs.core.config, order matching is PairByOrder). The page names neither import location; rechecked the cellprofiler_translation topic id, the interactive-input import error, and the DatasetListView 'Datasets' title; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/domain_expert_onboarding.rst against B6's later changes to openhcs/authoring/session/views.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/domain_expert_onboarding.rst against B6's later changes to benchmark/cellprofiler_benchmark_cli.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/domain_expert_onboarding.rst against B6's later changes to openhcs/interop/cellprofiler/pipeline_import.py, benchmark/cellprofiler_benchmark_cli.py, tests/unit/test_benchmark_control.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/authoring/session/views.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/domain_expert_onboarding.rst against the later B6 changes to openhcs/agent/capabilities.py, openhcs/interop/cellprofiler/pipeline_import.py, benchmark/cellprofiler_benchmark_cli.py, tests/unit/test_benchmark_control.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/authoring/session/views.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/domain_expert_onboarding.rst in full and compared it against the changed authorities: agent/capabilities.py, knowledge_base_service.py, interop/cellprofiler/pipeline_import.py, test_architecture_projection_service.py, test_cellprofiler_pipeline_import.py, benchmark/cellprofiler_benchmark_cli.py, benchmark/contracts/run_receipt.py, benchmark/control.py, test_benchmark_control.py; confirmed openhcs_explain_architecture with topic cellprofiler_translation, the 50,000-character knowledge document bound, the interactive-module import rejection, the full MCP profile, the openhcs-benchmark entry point and read-only receipt/artifact inspection in benchmark/control.py; renamed the Plate Manager reference to the Datasets pane.",
"Reviewed docs/source/guide_for_biologists/domain_expert_onboarding.rst in full; the recorded findings and disposition are the resulting page-specific editorial decision.",
"Compared its factual claims directly with the hash-pinned files listed in authority: openhcs/agent/capabilities.py; openhcs/agent/services/architecture_projection_service.py; openhcs/agent/services/knowledge_base_service.py; openhcs/interop/cellprofiler/pipeline_import.py; tests/unit/agent/test_architecture_projection_service.py; tests/unit/test_cellprofiler_pipeline_import.py.",
"Passed `.venv/bin/python scripts/validate_docs.py docs/source/guide_for_biologists`: documentation validation passed: 10 files, 2 Python blocks.",
@@ -1477,24 +1632,29 @@
"diataxis": "reference",
"authority": [
{
- "path": "openhcs/constants/constants.py",
- "sha256": "4cd6c29abd8b8e3ecc396b424d2b8b61e819a215fc1d770d3f6852b260a0691d",
- "role": "Declares the nominal microscopy dimensions, grouping identities, and shared component enums used by pipelines."
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "ProcessingConfig declares variable_components as the stack-axis authority and group_by as post-assembly partitioning."
},
{
- "path": "openhcs/processing/backends/lib_registry/unified_registry.py",
- "sha256": "70462cddcad1bfee447ce0786a9e4a85abbd297726d59a1208156af0ef3a3743",
- "role": "Defines canonical callable metadata and derives the unified catalogue from registry declarations."
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "ProcessingContract family (for example Pure2DContract and WholeStackInputContract) declares whether a callable runs per plane or on the whole stack."
},
{
- "path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "path": "openhcs/core/axes.py",
+ "sha256": "8d93133639c576050f089fb8aab99937c8982aff07e742fea44f8a12e62bb7b2",
+ "role": "AxisFamily and Axis declarations that variable_components and group_by take their values from."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/glossary.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1: MaterializationBackend and InputSource class families); ProcessingConfig still declares variable_components and group_by with the meaning the three bullets give, and the page names neither changed family, so nothing was changed.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/glossary.rst in full and compared it against the changed authorities: openhcs/core/config.py (PartitionFilterMode subclass family, source-binding configs moved in by K4) and openhcs/core/processing_contracts.py (CallableDeclaration rename); variable_components and group_by remain on the processing config and the ProcessingContract family (Pure2DContract, WholeStackInputContract) still states plane-local versus whole-stack, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/glossary.rst against B6's later changes to openhcs/core/config.py, openhcs/core/processing_contracts.py, openhcs/core/axes.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/glossary.rst against the later B6 changes to openhcs/core/config.py, openhcs/core/processing_contracts.py, openhcs/core/axes.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/glossary.rst in full and compared it against the changed authorities: openhcs/constants/constants.py (component enums removed by the axis-family refactor), lib_registry/unified_registry.py, openhcs/core/config.py; confirmed variable_components and group_by docstrings in ProcessingConfig and that ProcessingContract still exists as the per-plane versus whole-stack family in processing_contracts.py; the three distinctions remain accurate. Replaced constants.py and unified_registry.py, which no longer own these facts, with processing_contracts.py and core/axes.py.",
"On 2026-08-27, re-read docs/source/guide_for_biologists/glossary.rst in full against the declaration-owned catalogue input changes; challenged its concise reference boundary and confirmed that the three axis-processing distinctions remain accurate.",
"On 2026-08-27, re-read the complete biologist glossary after library registries gained declaration-owned runtime-parameter exclusions; its three axis-processing distinctions remain unchanged.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/constants/constants.py; openhcs/processing/backends/lib_registry/unified_registry.py; openhcs/core/config.py.",
@@ -1507,7 +1667,7 @@
},
{
"path": "docs/source/guide_for_biologists/image_sources.rst",
- "source_sha256": "59e171378f8827620c00e6cc6407ea2ec2d13c18e51afea57dfb67a0c19db418",
+ "source_sha256": "fec4bfc289273a852837ae9adbbfa5a9edad775d9d0af74770f662d64969479e",
"audience": [
"imaging scientists",
"source-integration operators"
@@ -1515,54 +1675,39 @@
"user_need": "Load, name, subset, and diagnose microscopy sources without bypassing source declarations.",
"diataxis": "how-to",
"authority": [
- {
- "path": "openhcs/microscopes/microscope_interfaces.py",
- "sha256": "a603fa1c58ba005f8ba22713e46efe4e33aee0f762ba188467482ae3ae429200",
- "role": "Declares the microscope source interface, plate dimensions, metadata, and acquisition access contract."
- },
- {
- "path": "openhcs/microscopes/source_bindings_handler.py",
- "sha256": "88f7c624310ea31a4cf5de884fdae295e4675a578f8a2821a837140e4e4b07b2",
- "role": "Resolves configured source bindings through the selected microscope handler."
- },
{
"path": "openhcs/core/source_bindings.py",
- "sha256": "e0afbbe40cce3847bcc2314910d0083ecc579a5a0d6e4278c3a5df4c62e7eb01",
- "role": "Declares source bindings that map logical pipeline inputs to microscope-backed workspaces."
+ "sha256": "890473715373d1adc6b12fec2b57eab6f179cbca7d837920c0388d620ce8b8c2",
+ "role": "Declares NamedSourceBinding (required defaults to True, component_identity), SourceSelector, ComponentSelector, SourceFilterClause and the SourceFileName and TextEquals family members passed by the code examples."
},
{
"path": "openhcs/core/source_workspace_projection.py",
- "sha256": "eeb629edcf353e46e125762b85084744723eafa98b7b2b8cd7ce925a578d3304",
+ "sha256": "1f8785fec7919f52b69578bbd6f6b5f94be8b2f9e97d11d00f639f3f70bcfe23",
"role": "Projects source bindings and persisted semantic metadata into the concrete workspace identities compiled for execution."
},
- {
- "path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compiles pipeline declarations into validated step plans and execution bundles."
- },
{
"path": "tests/unit/test_source_bindings.py",
- "sha256": "c7cfdd8015abc6948f0db3b3277a75d015f19a4cad68032badda99756f257942",
+ "sha256": "adcd46644c5005b9106a527b0c47d3c7d6821f4923213213c3a8f4c04c4934db",
"role": "Proves source-binding validation and projection into concrete workspace identities."
},
{
"path": "openhcs/core/source_binding_workspace.py",
- "sha256": "7dce6e45b9e846713b246561340d43de378e251e35f6ef68d5527fccc1b31ad3",
+ "sha256": "e242d9b932d52c1099756881870f926a273f5ec08663938347580cb4a70e8283",
"role": "Selects raw candidates and projects binding-owned component identity."
},
{
"path": "tests/unit/test_source_binding_workspace.py",
- "sha256": "223c51f0915ee8012ef39b085efe5fa43b446a11e33ba10f4844aee894957995",
+ "sha256": "98461001b494da536b8bd13e139e3ee01263b65b430b69bec8ce33ad32100dec",
"role": "Proves raw selector coordinates are retained as original metadata while the projected address uses semantic component identity."
},
{
"path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
"role": "Owns the documentation-enabled source-image browser capture, caption, accessibility text, dimensions, and source evidence."
},
{
"path": "openhcs/microscopes/bioformats_adapter.py",
- "sha256": "8ed1ae0589533e80ee4eaa2738c790c3249c4579274cb5d4d5996d474332d8d0",
+ "sha256": "e81247a7088748fed07ed290f33fe49194fd894c446d287138d000c2a91213f3",
"role": "Projects native OME-Zarr metadata into source planes, validates named axes and single-resolution images, and keeps Java-backed CZI and OME-TIFF decoding separate."
},
{
@@ -1572,12 +1717,12 @@
},
{
"path": "external/PolyStore/src/polystore/zarr.py",
- "sha256": "29310bc988689fa20b0d11fe6da0fdbd474eee26881e759b513f3d1e0feb875a",
+ "sha256": "f6b4c7eea3db8c3bb250caf548aecb9a7d2ee1a63e93b66f53c5ce0be89a542c",
"role": "Owns Zarr output format through upstream FormatV04, declared batch coordinates and chunks, and reads image arrays through their NGFF declarations."
},
{
"path": "external/PolyStore/src/polystore/config.py",
- "sha256": "62c8acad3da3abed8ccf7f1166fc6a0d094be1c3e842cec6446780541df292a0",
+ "sha256": "34313f3c12056a2a316f7f4712597444b269106b0f6786aca00c5a450d439daf",
"role": "Owns the nominal Zarr compressor factories, Numcodecs creation, chunk strategy, and backend configuration."
},
{
@@ -1587,17 +1732,17 @@
},
{
"path": "tests/unit/test_plane_store_sources.py",
- "sha256": "792a832934e9e25ddccf08796bab4ed5c519212f158af9915226f84cea08120c",
+ "sha256": "619a8bc276c116bc0e92ef9268e59fc57f9abd0f5819dffecb598fe3cdf1510c",
"role": "Proves both physical NGFF formats retain axis cardinalities, pixels, source identities, bindings, materialisation, and reinitialisation through OpenHCS."
},
{
"path": "tests/integration/test_source_store_code_mode_zmq.py",
- "sha256": "cd4bfc193471f6c99dfd899dc9092db49db67af12d725e18dd33648fb3f5032c",
+ "sha256": "9edac8c05b1682e688825bb5b3c1a69c12cb42a46a27b0fc3dc41a917378628c",
"role": "Exercises mixed-store code documents, source projection and the opt-in compile-then-execute ZMQ path for both NGFF formats."
},
{
"path": "external/PolyStore/tests/test_zarr_backend.py",
- "sha256": "7896159c33ee7fefb89976291e35db507acf282e92361c1abd86eca54160e2fa",
+ "sha256": "9a74e35521552391e1b33ea06b985debe2efaa3429299793c784db4f28104c57",
"role": "Proves format-2 output, every configured codec, array dtypes, chunks, metadata, historical precedence, batch mappings, and non-overwriting storage operations."
},
{
@@ -1606,43 +1751,94 @@
"role": "Proves format-independent store discovery, legacy metadata precedence, declared resolution lookup, malformed-metadata rejection, and dimension-name validation."
},
{
- "path": "openhcs/core/artifacts.py",
- "sha256": "c80450105c2acf01cb8cf2fbb0a0de0653b0f7171a744608fe7e43f879298d53",
+ "path": "openhcs/core/artifacts/relations.py",
+ "sha256": "b1ae8be613fbabc372bd6692b41c5f925d4f968291f0a715bd1473d85cbed000",
"role": "Owns InputImageSetContextSourceRelation and its exact input-image and context-bearing target constraints."
},
{
"path": "openhcs/core/runtime_stores.py",
- "sha256": "d9987379e3729720d0efcc32bff113d4a583f5b97bb5bfb1a5abd45c57acb8ea",
+ "sha256": "35fe71906bfd16842de347776a0c5cc846550ad490cc3628885129d746ec6f31",
"role": "RuntimeArtifactInput resolves exact artifact addresses and checks execution context through the declared image-set relation; it does not select arbitrary same-shape labels."
},
{
"path": "openhcs/core/source_matching.py",
- "sha256": "128e3b8be9ef3c24ebe357a385a1ca6685cacdc83545bd2dd362849c1dcf95b3",
+ "sha256": "cea83c632056420b4e2affac25f9da37c3a4ee41878e2a1fb18b6747d8573dcf",
"role": "SourceImageSetIdentityPolicy derives plane membership and retained sample identity from source declarations."
},
{
"path": "openhcs/interop/cellprofiler/module_artifact_declarations.py",
- "sha256": "ae3582a5d3252827c520ce14d15b0fd860e0d9486dd103e9178f3340805ff889",
+ "sha256": "5442b56c7dd7c913d38d18c5a76660ad6006e147ffc8f790712fb79b301ff035",
"role": "The existing object-measurement declaration ancestor derives label input image-set relations from its selected image specs."
- }
- ],
- "findings": [
- "The source-selection how-to did not explain or demonstrate the selector-to-semantic-identity projection boundary.",
- "The image-source how-to omitted the supported NGFF storage formats and unchanged output policy, and its decoder wording did not distinguish native OME-Zarr from Java-backed containers. The reviewed text now scopes NGFF inputs to single-resolution images and links generic format and codec policy to PolyStore."
- ],
- "disposition": "revise",
- "validation": [
- "Re-read docs/source/guide_for_biologists/image_sources.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
- "Compared every factual claim directly with the current hash-pinned authorities: openhcs/microscopes/microscope_interfaces.py; openhcs/microscopes/source_bindings_handler.py; openhcs/core/source_bindings.py; openhcs/core/source_workspace_projection.py; openhcs/core/pipeline/compiler.py; tests/unit/test_source_bindings.py.",
- "Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
- "Passed ./.venv/bin/python scripts/validate_docs.py docs/source: 154 files, 23 Python blocks, and 153 audited RST pages.",
- "Passed ./.venv/bin/python -m sphinx -E -W --keep-going -b html docs/source : 153 source files rendered without warnings.",
- "On 2026-08-22, re-read the complete how-to and validated its updated selector and component-identity example against source workspace, compiler, runtime, and viewer regressions in the 238-test focused OpenHCS suite.",
- "On 2026-08-28, re-read the complete how-to and inspected the live Image Browser capture at full resolution; it shows the initialized synthetic plate's 216-file source inventory without adding another source-model authority.",
- "On 2026-08-28, re-read the complete how-to after gallery dimensions and source evidence became generated projections; the Image Browser scenario and source-binding procedure remain unchanged and accurate.",
- "On 2026-09-07, re-read the complete source-loading how-to against OmeZarrStoreAdapter, OmeZarrLocation, the PolyStore writer and codec declarations, and the physical-store and code-mode/ZMQ regressions. Verified native Zarr 2/NGFF 0.4 and Zarr 3/NGFF 0.5 inputs, the single-resolution input boundary, unchanged FormatV04 output, coordinate preservation, and the separate Java decoding boundary without duplicating implementation policy in the how-to.",
- "On 2026-09-07, the minimum-stack PolyStore environment resolved the declared development install on Python 3.11 with Zarr 3.0.10 and ome-zarr 0.12.2 and passed dependency compatibility checks; the frozen bd5a616 candidate passed 486 tests with two deliberate skips. Physical-store tests also exercised the current Zarr 3.3.0 and ome-zarr 0.19.1 stack.",
- "On 2026-09-07, re-read the complete source-loading how-to after moving NGFF runtime imports from adapter/backend registration to actual discovery and read-only store opening. The documented formats, source identities, and separate native/Java decoding boundaries remain unchanged. Upstream location.create preserves subclass, mode, and format for nested wells and images. The unchanged declaration-import regression and real fresh-process discovery across both formats passed in the 27-test OpenHCS source suite; PolyStore passed 487 tests with two skips on the minimum runtime and 488 with one skip on the latest GPU-enabled runtime. Only the three directly reviewed changed authority hashes were refreshed.",
+ },
+ {
+ "path": "openhcs/core/dataset_sources/source.py",
+ "sha256": "b3758a878fdf8ec28b6d9818ebfd96c5c470895575f7a79500c85f1490679281",
+ "role": "DatasetSource family with format-specific, broad structured-store and declared-file selection roles and their detection order."
+ },
+ {
+ "path": "openhcs/core/dataset_sources/source_bindings_source.py",
+ "sha256": "63e4a3f849ab5122ab7af8767a6288135b7330027dff962c80add93d5fd820f1",
+ "role": "SourceBindingsSource, the declared-file fallback for unrecognized folders driven by source_bindings_config."
+ },
+ {
+ "path": "openhcs/agent/dto/plate.py",
+ "sha256": "a54d0252c8c48db29e976222b5e8136943426c3081ed53cda06ea6eaf9826253",
+ "role": "PlatePathInspectionRequest source_format field and format_specific_handler_candidates result field."
+ },
+ {
+ "path": "openhcs/domains/microscopy/axes.py",
+ "sha256": "531695809259324032e0b88632813323b4fe6a9a176ad6f636aeef8dec332399",
+ "role": "Microscopy axis family (Well, Site, Channel, ZIndex, Timepoint) used for component selectors, variable_components and group_by."
+ },
+ {
+ "path": "openhcs/core/axes.py",
+ "sha256": "8d93133639c576050f089fb8aab99937c8982aff07e742fea44f8a12e62bb7b2",
+ "role": "Ungrouped grouping declaration that overlapping non-dict group_by normalizes to."
+ },
+ {
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares SourceBindingsConfig and StepSourceBindingsConfig with their lazy forms LazySourceBindingsConfig and LazyStepSourceBindingsConfig, and PipelineConfig.source_bindings_config, used by the code-mode examples."
+ },
+ {
+ "path": "openhcs/core/artifacts/specs.py",
+ "sha256": "c4c50309a195bf85e1c0641413544d9581fc0ce1197043130126f7a4c70d8c4e",
+ "role": "ArtifactSpec.input, the constructor the input-contract fragment calls with parameter_name and relations."
+ },
+ {
+ "path": "openhcs/core/artifacts/kinds.py",
+ "sha256": "216b913efad9ec76707e9f61be445fb6fa1c6601c36ef53e296acd4928604515",
+ "role": "ImageArtifactType and ObjectLabelsArtifactType, the artifact kinds the input-contract fragment declares."
+ },
+ {
+ "path": "openhcs/constants/input_source.py",
+ "sha256": "4a7929e66d11d5ad81c40f080ba8cd23d466797bfa780d59eae0e1aea3ffd9a4",
+ "role": "InputSource family; ReadPipelineStart is the member the neurite and code-mode examples pass as input_source."
+ }
+ ],
+ "findings": [
+ "E1 turned SourceFilterSubject, SourceFilterMatchType and InputSource into class families, so the mixed-store code example failed at SourceFilterSubject.FILE, SourceFilterMatchType.EQUALS and InputSource.PIPELINE_START; it now imports and passes SourceFileName, TextEquals and ReadPipelineStart.",
+ "The prose told readers to set input_source=PIPELINE_START in two places and named PIPELINE_START as a re-entry point in a third; the first two now say ReadPipelineStart and the third says the pipeline start.",
+ "K2 made openhcs/core/artifacts a package; the page's imports were already updated, but the audit still named the deleted openhcs/core/artifacts.py as the owner of InputImageSetContextSourceRelation. The authority is now openhcs/core/artifacts/relations.py."
+ ],
+ "disposition": "revise",
+ "validation": [
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/image_sources.rst in full and compared it against the changed authorities: openhcs/core/source_bindings.py, openhcs/core/source_matching.py, openhcs/core/source_binding_workspace.py, openhcs/core/source_workspace_projection.py, openhcs/core/config.py and openhcs/agent/dto/plate.py (E1 class families), openhcs/core/artifacts.py (K2: now the openhcs/core/artifacts package), openhcs/core/runtime_stores.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/core/dataset_sources/source.py, openhcs/microscopes/bioformats_adapter.py and the four test modules; rewrote the mixed-store code example to pass SourceFileName, TextEquals and ReadPipelineStart, replaced the three prose uses of PIPELINE_START, and confirmed the K2 artifact imports in the input-contract fragment resolve to specs.py, kinds.py and relations.py; all three Python blocks were then executed against the worktree and ran.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/image_sources.rst in full and compared it against the changed authorities: openhcs/core/source_bindings.py (K4: SourceBindingsConfig/StepSourceBindingsConfig moved to openhcs/core/config.py, match plan became the SourceSetPairing family PairByOrder/PairByMetadata), openhcs/core/source_workspace_projection.py, openhcs/core/source_binding_workspace.py, openhcs/core/runtime_stores.py, openhcs/core/source_matching.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/core/dataset_sources/source.py, openhcs/core/dataset_sources/source_bindings_source.py and their tests; confirmed every symbol the code blocks import still exists at its import path (validate_docs reports no import or code-block finding for the page), NamedSourceBinding.required defaults to True, and InputImageSetContextSourceRelation and SourceImageSetIdentityPolicy are unchanged; replaced the 'failed receipt' wording and added openhcs/core/config.py as an authority.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/image_sources.rst against B6's later changes to openhcs/core/source_workspace_projection.py, tests/unit/test_source_bindings.py, openhcs/core/source_binding_workspace.py, tests/unit/test_source_binding_workspace.py, tests/unit/test_plane_store_sources.py, openhcs/core/artifacts.py, openhcs/core/runtime_stores.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/core/dataset_sources/source.py, openhcs/core/dataset_sources/source_bindings_source.py, openhcs/agent/dto/plate.py, openhcs/domains/microscopy/axes.py, openhcs/core/axes.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/image_sources.rst against the later B6 changes to openhcs/core/source_bindings.py, openhcs/core/source_workspace_projection.py, tests/unit/test_source_bindings.py, openhcs/core/source_binding_workspace.py, tests/unit/test_source_binding_workspace.py, tests/unit/test_plane_store_sources.py, openhcs/core/artifacts.py, openhcs/core/runtime_stores.py, openhcs/interop/cellprofiler/module_artifact_declarations.py, openhcs/core/dataset_sources/source.py, openhcs/core/dataset_sources/source_bindings_source.py, openhcs/agent/dto/plate.py, openhcs/domains/microscopy/axes.py, openhcs/core/axes.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/image_sources.rst in full and compared it against the changed authorities: the page's own unreviewed axis-family edit, microscope_interfaces.py and source_bindings_handler.py (deleted in c6d9a52ce), source_bindings.py, source_workspace_projection.py, pipeline/compiler.py, source_binding_workspace.py, bioformats_adapter.py, PolyStore zarr.py and config.py, artifacts.py, runtime_stores.py, source_matching.py, module_artifact_declarations.py and their tests; replaced the deleted handler classes with DatasetSource / SourceBindingsSource, microscope_type with source_format, deleted component enum spellings with Microscopy axes and Ungrouped, and Plate Manager with the Datasets pane; confirmed source_stack_components, ImportedMetadataTable, InputImageSetContextSourceRelation, SourceFilterSubject/MatchType, viewer route keys and both code blocks' imports still exist.",
+ "Re-read docs/source/guide_for_biologists/image_sources.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
+ "Compared every factual claim directly with the current hash-pinned authorities: openhcs/microscopes/microscope_interfaces.py; openhcs/microscopes/source_bindings_handler.py; openhcs/core/source_bindings.py; openhcs/core/source_workspace_projection.py; openhcs/core/pipeline/compiler.py; tests/unit/test_source_bindings.py.",
+ "Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
+ "Passed ./.venv/bin/python scripts/validate_docs.py docs/source: 154 files, 23 Python blocks, and 153 audited RST pages.",
+ "Passed ./.venv/bin/python -m sphinx -E -W --keep-going -b html docs/source : 153 source files rendered without warnings.",
+ "On 2026-08-22, re-read the complete how-to and validated its updated selector and component-identity example against source workspace, compiler, runtime, and viewer regressions in the 238-test focused OpenHCS suite.",
+ "On 2026-08-28, re-read the complete how-to and inspected the live Image Browser capture at full resolution; it shows the initialized synthetic plate's 216-file source inventory without adding another source-model authority.",
+ "On 2026-08-28, re-read the complete how-to after gallery dimensions and source evidence became generated projections; the Image Browser scenario and source-binding procedure remain unchanged and accurate.",
+ "On 2026-09-07, re-read the complete source-loading how-to against OmeZarrStoreAdapter, OmeZarrLocation, the PolyStore writer and codec declarations, and the physical-store and code-mode/ZMQ regressions. Verified native Zarr 2/NGFF 0.4 and Zarr 3/NGFF 0.5 inputs, the single-resolution input boundary, unchanged FormatV04 output, coordinate preservation, and the separate Java decoding boundary without duplicating implementation policy in the how-to.",
+ "On 2026-09-07, the minimum-stack PolyStore environment resolved the declared development install on Python 3.11 with Zarr 3.0.10 and ome-zarr 0.12.2 and passed dependency compatibility checks; the frozen bd5a616 candidate passed 486 tests with two deliberate skips. Physical-store tests also exercised the current Zarr 3.3.0 and ome-zarr 0.19.1 stack.",
+ "On 2026-09-07, re-read the complete source-loading how-to after moving NGFF runtime imports from adapter/backend registration to actual discovery and read-only store opening. The documented formats, source identities, and separate native/Java decoding boundaries remain unchanged. Upstream location.create preserves subclass, mode, and format for nested wells and images. The unchanged declaration-import regression and real fresh-process discovery across both formats passed in the 27-test OpenHCS source suite; PolyStore passed 487 tests with two skips on the minimum runtime and 488 with one skip on the latest GPU-enabled runtime. Only the three directly reviewed changed authority hashes were refreshed.",
"On 2026-09-08, re-read this page against the compiler saved-scope resolution fix: worker assignment and runtime environment consume one concrete resolved configuration; public pipeline declarations remain unchanged.",
"On 2026-09-16, re-read the complete how-to against persisted semantic metadata replay. Added the procedure for reopening generated output plates without recreating collapsed axes from filename coordinates; 277 focused source, output, materialization, and provenance tests passed.",
"2026-10-05: re-read the complete guide and the existing authoring routes against source bindings, RuntimeArtifactInput, SourceImageSetIdentityPolicy, and the CellProfiler measurement declaration ancestor. Added a conditional pre-authoring distinction between assembled image scope and declared label image-set context; preserved grouping, broadcast, sample identity and wrong-source checks. Focused qualification is recorded in docs/validation/source-scoped-photometry-authoring/RECEIPT.rst; it is documentation/contract delivery, not biological or live-author acceptance."
@@ -1659,23 +1855,29 @@
"authority": [
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
"role": "Composes the main desktop window, menus, task windows, and registered UI services."
},
{
- "path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
- "role": "Declares the public pipeline document, step collection, and serialisable declaration boundary."
+ "path": "openhcs/pyqt_gui/windows/synthetic_plate_generator_window.py",
+ "sha256": "66ddba05885106acab8f5f9219665e8b611f50984b09c6a4e3408ab783685a9c",
+ "role": "Generates the synthetic plate and loads openhcs.demo.synthetic_plate_pipeline for the first-workflow tutorial."
},
{
- "path": "openhcs/microscopes/microscope_interfaces.py",
- "sha256": "a603fa1c58ba005f8ba22713e46efe4e33aee0f762ba188467482ae3ae429200",
- "role": "Declares the microscope source interface, plate dimensions, metadata, and acquisition access contract."
+ "path": "openhcs/demo/synthetic_plate_pipeline.py",
+ "sha256": "c175dfae6455690c554952a5f7202569fc0aa2c0aec0999c2d8112475587f80d",
+ "role": "The included eight-step pipeline the Learn by doing entry promises."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/guide_for_biologists/index.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/index.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py and openhcs/demo/synthetic_plate_pipeline.py; both diffs are E1 enum-to-family renames (SelectedItems/AllItems, update-check origins, ReadPipelineStart, PadToMaxSize); the demo pipeline still declares eight steps and the page names no renamed symbol, so no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/index.rst in full and compared it against the changed authority openhcs/pyqt_gui/main.py (U3: dock layout, shortcut lifecycle and time-travel navigation now come from pyqt-reactive); the page only routes readers and names no main-window mechanism, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/index.rst against B6's later changes to openhcs/pyqt_gui/main.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/index.rst against the later B6 changes to openhcs/pyqt_gui/main.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/index.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py, openhcs/core/pipeline_document.py, openhcs/microscopes/microscope_interfaces.py (deleted in c6d9a52ce); the page states no fact owned by the deleted microscope interface or by the pipeline document, so both were replaced by the synthetic-plate generator window and the eight-step synthetic_plate_pipeline.py that back its only product claim; the toctree and links remain correct.",
"Reviewed docs/source/guide_for_biologists/index.rst in full; the recorded findings and disposition are the resulting page-specific editorial decision.",
"Compared its factual claims directly with the hash-pinned files listed in authority: openhcs/pyqt_gui/main.py; openhcs/core/pipeline_document.py; openhcs/microscopes/microscope_interfaces.py.",
"Passed `.venv/bin/python scripts/validate_docs.py docs/source/guide_for_biologists`: documentation validation passed: 10 files, 2 Python blocks.",
@@ -1697,30 +1899,34 @@
"authority": [
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
"role": "Declares the package version, Python requirement, dependency extras, scripts, and distribution metadata."
},
{
"path": "openhcs/cli.py",
- "sha256": "51c2570a5a49c83e826c3a2e85943a2c39593e5e30e1ce8af396ec4a4c724df1",
+ "sha256": "59025ea51309dbae40416d75fc203287657a109379f65072d77cff41c712e11e",
"role": "Declares the installed OpenHCS command entry points and their launch routing."
},
{
"path": "packaging/installers/windows/Install-OpenHCS.ps1",
- "sha256": "f94eb1c0460cab7dca770a600d889bf55ae98f1aa652b2755de0ecc906ee83d8",
+ "sha256": "ca368082f3d7a414f9e9136b2c5ae459bfb2a9a2a37c096d06eeb93d5c403358",
"role": "Implements the Windows installer, short managed paths, package installation, and shortcut creation."
},
{
"path": "packaging/installers/macos/install-openhcs.sh",
- "sha256": "b78b6b5849265926f83f77f392f7f4ac74ce5968f62e0b6972c6ce7976498203",
+ "sha256": "3a0f43c57c14bd7f8ceeb38464e174728f903e0ec524d980dbdb2b5f9bc11544",
"role": "Implements the macOS application installer, managed environment creation, package installation, and launcher setup."
}
],
"findings": [
- "A second installation procedure would duplicate volatile package and installer facts; the compatibility page correctly redirects to the canonical task."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/installation_and_setup.rst in full and compared it against the changed authorities: pyproject.toml; the python-introspect floor moved to 0.2.8; the page states no dependency fact and docs/source/getting_started/getting_started.rst still exists, so no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/installation_and_setup.rst in full and compared it against the changed authorities: pyproject.toml (dependency floors only); the page restates no version or installer fact, so the redirect disposition stands.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/installation_and_setup.rst against the later B6 changes to pyproject.toml: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/installation_and_setup.rst in full and compared it against the changed authorities: pyproject.toml, openhcs/cli.py, packaging/installers/windows/Install-OpenHCS.ps1, packaging/installers/macos/install-openhcs.sh; the page contains only the redirect sentence and its target getting_started.rst was re-reviewed in this batch, so the redirect disposition stands.",
"On 2026-08-27, re-read docs/source/guide_for_biologists/installation_and_setup.rst in full after the python-introspect dependency floor advanced to 0.1.12; confirmed that its redirect still reaches the single maintained installation how-to.",
"On 2026-08-26, re-read the complete compatibility page after native installer transcript and evidence handling changed; retaining one redirect to the canonical installation how-to remains accurate and avoids duplicated guidance.",
"Compared every factual claim directly with the current hash-pinned authorities: pyproject.toml; openhcs/cli.py; packaging/installers/windows/Install-OpenHCS.ps1; packaging/installers/macos/install-openhcs.sh.",
@@ -1753,7 +1959,7 @@
},
{
"path": "docs/source/guide_for_biologists/intro.rst",
- "source_sha256": "40fac7417c9d8920edb90f3b3dbae55b8e05f333ea58271ef2cce2514717fde4",
+ "source_sha256": "2bae4f9921e6da0170c52daf3fb66c62ffcdc1cdfd8a7a1fd857cfa54a45cd0f",
"audience": [
"prospective imaging-scientist users"
],
@@ -1762,28 +1968,38 @@
"authority": [
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
"role": "Compiles pipeline declarations into validated step plans and execution bundles."
},
{
"path": "openhcs/interop/cellprofiler/pipeline_import.py",
- "sha256": "b547c838b2c86fe7cc5fe12d6cbf6a6715e83055c2d8c1a109ac22566d22c51e",
+ "sha256": "5aa8895c9f533603b7ef972202141e2439473f0604b97d66f5a027b8b3cd1c83",
"role": "Imports CellProfiler modules into ordinary OpenHCS pipeline declarations through the interop boundary."
},
{
"path": "openhcs/processing/backends/lib_registry/registry_service.py",
- "sha256": "046d3d088f685a993ddad6b5923cf63beef84b201127b72ae32af6721dd5ad63",
+ "sha256": "f2e981157632adb5020828720ec26bfa6444ab52f545e1e71b1524242eaed6a3",
"role": "Owns backend discovery, availability filtering, and the canonical process catalogue projection."
},
{
"path": "openhcs/pyqt_gui/widgets/plate_manager.py",
- "sha256": "6fa48fbcd58e47eb7132d5d9f86a002c6a0e8ff077d9654a03a791629e135378",
- "role": "Implements plate import, selection, pipeline assignment, and execution controls."
+ "sha256": "b6e56da089cff746c9f0f41ca4b4d947942d4a71d1b290a1d8b76c3b7732c31e",
+ "role": "Implements dataset import, selection, initialization, compilation and run controls in the Datasets pane."
+ },
+ {
+ "path": "openhcs/core/dataset_sources/choice.py",
+ "sha256": "497eb028aabb80283be8a621ea94e09881dfcd51b2e34e2bbecafa09e004c1f6",
+ "role": "DatasetSourceChoices projects the registered dataset sources, plus auto, as the dataset_source field's choices."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/intro.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, openhcs/interop/cellprofiler/pipeline_import.py and openhcs/pyqt_gui/widgets/plate_manager.py; the diffs replace OrchestratorState, MaterializationBackend, InputSource and selection-mode enum members with subclass families and move artifact imports into the openhcs.core.artifacts package; compile-before-run, .cppipe import and the dataset_source choices are unchanged, so no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/intro.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py (ScopeKey scope ids, K6 progress classes), openhcs/interop/cellprofiler/pipeline_import.py and openhcs/core/dataset_sources/choice.py (source-binding config imports from openhcs.core.config), openhcs/processing/backends/lib_registry/registry_service.py (reference.declaration replaces reference.metadata) and openhcs/pyqt_gui/widgets/plate_manager.py (U3: path cache and config window from pyqt-reactive, GlobalEventBus broadcast removed); the page's capability, dataset_source choice and compile-before-run claims still hold, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/intro.rst against B6's later changes to openhcs/core/pipeline/compiler.py, openhcs/interop/cellprofiler/pipeline_import.py, openhcs/processing/backends/lib_registry/registry_service.py, openhcs/core/dataset_sources/choice.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/intro.rst against the later B6 changes to openhcs/core/pipeline/compiler.py, openhcs/interop/cellprofiler/pipeline_import.py, openhcs/processing/backends/lib_registry/registry_service.py, openhcs/core/dataset_sources/choice.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/intro.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, openhcs/interop/cellprofiler/pipeline_import.py, lib_registry/registry_service.py, pyqt_gui/widgets/plate_manager.py; the fit list, CellProfiler import claim and compile-before-run flow remain accurate (Run requires a compiled selection in the session operations); replaced the Plate Manager format-catalogue pointer with the dataset_source choices declared in openhcs/core/config.py and dataset_sources/choice.py.",
"On 2026-08-27, re-read docs/source/guide_for_biologists/intro.rst in full against the explicit CellProfiler import-validation hook; challenged its explanation of supported imports and found no prose drift.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline/compiler.py; openhcs/interop/cellprofiler/pipeline_import.py; openhcs/processing/backends/lib_registry/registry_service.py; openhcs/pyqt_gui/widgets/plate_manager.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -1800,7 +2016,7 @@
},
{
"path": "docs/source/guide_for_biologists/intro_stitching.rst",
- "source_sha256": "fde83b50eb661c94d16d4d8986b86f565f6db1bdf7f57668293af7b1612e29d2",
+ "source_sha256": "3bcb8fa804b88f764b4d0c52e81b336815fd4a9a1a984baefca0a20f861341ba",
"audience": [
"first-time desktop users"
],
@@ -1814,30 +2030,40 @@
},
{
"path": "openhcs/demo/synthetic_data.py",
- "sha256": "7a8f4a79e716ad944d5ceb1ed14e62cab9211aee65b1c0dc571e097e0f4d3c09",
+ "sha256": "be80973ecd87b4b3b36b7a7283d962a5d7cafd491cfa16eafe4622e09391463a",
"role": "Generates the packaged deterministic synthetic microscopy plate consumed by the introductory workflow."
},
{
"path": "openhcs/demo/synthetic_plate_pipeline.py",
- "sha256": "3f8916ff71938ac5b53c7f55329b037653519e9592cf564257f78dde158fbd11",
- "role": "Provides the packaged example pipeline used to process the generated synthetic plate."
+ "sha256": "c175dfae6455690c554952a5f7202569fc0aa2c0aec0999c2d8112475587f80d",
+ "role": "Declares the eight supplied steps, from Image Enhancement Processing to Cell Counting, that the tutorial tells readers to expect."
+ },
+ {
+ "path": "openhcs/authoring/session/operations/datasets.py",
+ "sha256": "630a74c9e4fb026a7c47e78b3a07427c4d16c9630447a523b36dd8c73e17e430",
+ "role": "Declares the Init, Compile, Run, Viewer, and Results dataset operations and their button labels named in the tutorial steps."
},
{
- "path": "tests/pyqt_gui/integration/test_end_to_end_workflow_foundation.py",
- "sha256": "db2f98b9878565df935d78aaf8d8365cdac5fe160cd4fcee2d6246502dc355d5",
- "role": "Proves synthetic plate import and the desktop configuration lifecycle, including form readiness, inheritance, persistence, reset, and teardown."
+ "path": "openhcs/pyqt_gui/main.py",
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
+ "role": "Places Generate Synthetic Plate in the View menu."
},
{
"path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
"role": "Owns the documentation-enabled synthetic generator, plate metadata, and live measurement result capture declarations used by the first-workflow tutorial."
}
],
- "findings": [
- "The previous evidence role overstated the GUI foundation as proving pipeline execution; the generator and preset declarations remain the authorities for the tutorial's compile-and-run path."
- ],
+ "findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/intro_stitching.rst against openhcs/authoring/session/operations/datasets.py: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/guide_for_biologists/intro_stitching.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/intro_stitching.rst in full and compared it against the changed authorities: openhcs/demo/synthetic_plate_pipeline.py and openhcs/pyqt_gui/main.py; the supplied pipeline still has eight steps running from Image Enhancement Processing to Cell Counting (only its InputSource and variable-size spellings became ReadPipelineStart and PadToMaxSize) and main.py still places Generate Synthetic Plate under View; no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/intro_stitching.rst in full and compared it against the changed authority openhcs/pyqt_gui/main.py (U3 dock, shortcut and time-travel moves to pyqt-reactive); the View menu still holds the Generate Synthetic Plate action, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/intro_stitching.rst against B6's later changes to openhcs/authoring/session/operations/datasets.py, openhcs/pyqt_gui/main.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/intro_stitching.rst against the later B6 changes to openhcs/authoring/session/operations/datasets.py, openhcs/pyqt_gui/main.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/intro_stitching.rst in full and compared it against the changed authorities: openhcs/demo/synthetic_data.py, openhcs/demo/synthetic_plate_pipeline.py, tests/pyqt_gui/integration/test_end_to_end_workflow_foundation.py, scripts/gallery_catalog.py; confirmed the eight step names, the View menu entry, the generator dialog labels, the Init/Compile/Run/Results labels and the four gallery scenario ids, and replaced the unnamed metadata viewer with the Viewer button and Metadata tab.",
"Re-read docs/source/guide_for_biologists/intro_stitching.rst in full against repository base bb4218fae and the current shared worktree; challenged its tutorial user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/pyqt_gui/windows/synthetic_plate_generator_window.py; openhcs/demo/synthetic_data.py; openhcs/demo/synthetic_plate_pipeline.py; tests/pyqt_gui/integration/test_end_to_end_workflow_foundation.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -1851,7 +2077,7 @@
},
{
"path": "docs/source/guide_for_biologists/troubleshooting_FAQ.rst",
- "source_sha256": "0c2fb0dbb43ec18efb8d4aeff2a728a35630516a6e506de677de5d070089f428",
+ "source_sha256": "7499a25552e6f1fed5ef0d669970b4363a80e96f676279b6218ede90a39e8191",
"audience": [
"desktop operators diagnosing a failed workflow"
],
@@ -1859,29 +2085,39 @@
"diataxis": "how-to",
"authority": [
{
- "path": "openhcs/pyqt_gui/widgets/plate_manager.py",
- "sha256": "6fa48fbcd58e47eb7132d5d9f86a002c6a0e8ff077d9654a03a791629e135378",
- "role": "Implements plate import, selection, pipeline assignment, and execution controls."
+ "path": "openhcs/authoring/session/operations/datasets.py",
+ "sha256": "630a74c9e4fb026a7c47e78b3a07427c4d16c9630447a523b36dd8c73e17e430",
+ "role": "Declares the Init, Compile, Run, Connect, and Code dataset operations and the submit_timeout_ms request field that the recovery steps name."
+ },
+ {
+ "path": "openhcs/authoring/session/operations/application.py",
+ "sha256": "628fb1dd209d62d1d5830e710cf6e0ea6804f576569489da7df42f974652ffd8",
+ "role": "Declares the Restart OpenHCS and restore session operation and its warning that the old process's operation status does not survive the restart."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compiles pipeline declarations into validated step plans and execution bundles."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Stops before execution on unsatisfied declarations and applies materialize_runtime_artifacts to automatic terminal outputs."
},
{
"path": "openhcs/utils/environment.py",
- "sha256": "d408e5c8bd86f9b7d26ce23595fb75200a9320d9f3ee9bfca8d29861cecb9c9d",
- "role": "Detects runtime environment and optional backend availability, including CPU-only operation."
+ "sha256": "1985ffbe42b5dfb283b78b6282f9b56e08cd51d5a9e79f2dd4eebffcb6da0bf5",
+ "role": "Reads OPENHCS_CPU_ONLY to hide GPU-backed declarations."
+ },
+ {
+ "path": "openhcs/pyqt_gui/launch.py",
+ "sha256": "fc018b94dc0aeaea4d1d7592973f5635419da5b53b85ecf07448bae975bb6e14",
+ "role": "Defines the openhcs --no-gpu launch option."
},
{
"path": "openhcs/runtime/viewer_protocol.py",
- "sha256": "30441eab97da9ffcb5b9dc33d65c2ecc56c426e79bb2b27eedc78a68aaf13bc3",
- "role": "Declares the transport-independent viewer messages, stream configuration, and server contract."
+ "sha256": "347a65b63c149f2f437014d537cfaf481a33801d860742f4083b7377494e792f",
+ "role": "Declares the viewer server contract whose readiness and dependency failures the viewer section diagnoses."
},
{
"path": "openhcs/runtime/zmq_execution_client.py",
- "sha256": "31081f2a323a81c060aede1201a4bf1a1b39a51f37928c281a419ffaceadfe60",
- "role": "Implements execution-server connection, request submission, progress consumption, and connection-state reporting."
+ "sha256": "ad7ea01599493dbefce1f3c91e6b082e932c6a5183d0a85a186f7bd7b62779a9",
+ "role": "Implements execution-server connection and retains the .startup.jsonl journal for a server that fails before readiness."
},
{
"path": "openhcs/runtime/zmq_application.py",
@@ -1890,86 +2126,70 @@
},
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
- "role": "Wires the endpoint compatibility observation to the owning version-restart workflow."
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
+ "role": "Wires endpoint compatibility to the version-restart workflow and gates the restart operation until initialisation, compilation and execution have finished."
},
{
- "path": "openhcs/pyqt_gui/services/zmq_version_restart.py",
- "sha256": "86d4bf273c75417b2c4f5c38ac83a66e3487bb233be88a608140a0bdb4c8bc67",
- "role": "Owns the mismatch prompt, one endpoint-replacement transaction, and its success or recovery outcome."
+ "path": "openhcs/desktop/zmq_version_restart.py",
+ "sha256": "84b4f13432862bc9d61dbbcd19d4b54a0240fb243f9ddd2fb94a7b68f5808acb",
+ "role": "Owns the mismatch prompt, its cancel path, one endpoint-replacement transaction, and its success or recovery outcome."
},
{
- "path": "openhcs/pyqt_gui/services/desktop_restart.py",
- "sha256": "24ca24c3afc0038a268280a73ae7615045eb107ee541a59c13fcdf07005603a2",
- "role": "Preserves the current desktop session and ObjectState history across the version restart."
+ "path": "openhcs/desktop/restart.py",
+ "sha256": "53028fa6380292520ca42945b12c6844019dd91a77aef3d15e20108830a18c0b",
+ "role": "Preserves the current desktop session and edit history across a restart."
},
{
- "path": "openhcs/pyqt_gui/services/desktop_restart_worker.py",
+ "path": "openhcs/desktop/restart_worker.py",
"sha256": "52d8a8f223d49dbd9ef42951fe507dfbea803b33ff4dfce3e198f4b1a9fe4ed7",
"role": "Launches the saved restart command only after the current UI process exits."
},
{
- "path": "tests/unit/pyqt_gui/test_desktop_restart.py",
- "sha256": "aafaa6e7a46859f50b44232e12f8db606e0dd4631340a834694a0fe795b5ff70",
- "role": "Proves the mismatch dialog, saved session, and version-specific restart behavior."
- },
- {
- "path": "tests/unit/pyqt_gui/test_zmq_version_restart.py",
- "sha256": "052699f393497ab23ec49d23fa136d969245c95c0973d6e9898aedfb860c43f9",
+ "path": "tests/unit/desktop/test_zmq_version_restart.py",
+ "sha256": "f1968ec55295c577e118ada34ba80d987f50d69df30105b8b3a65a1a774c0018",
"role": "Proves expected replacement and restart failures leave the current UI usable and discard stale saved sessions."
},
{
- "path": "tests/unit/pyqt_gui/test_desktop_restart_worker.py",
- "sha256": "a9fbda7d6c78972d6300cd9fe67c2e88497b6b802a9b48057568007bb33b0e1c",
- "role": "Proves parent-exit gating, Windows handle cleanup, exact restart arguments, and fail-closed launch outcomes."
- },
- {
- "path": "scripts/smoke_installed_desktop.py",
- "sha256": "f745f93e245e5c1b02f6e0ece3a4a0e2af086c3cab77282e3907a167f5ce0a7f",
- "role": "Executes the GUI acceptance probe and packaged restart worker from the installed environment while deriving the outer process ceiling from the probe's timing authority."
- },
- {
- "path": "tests/installer/test_installed_desktop_smoke.py",
- "sha256": "5ac9f0e9e0e2ffe73cbab940da132421424e355c354b7476a8a072090c5aad00",
- "role": "Proves platform smoke orchestration passes the exact installed Python, install root, environment, and declaration-derived GUI operation and process-exit budgets to the installed verifiers."
+ "path": "tests/unit/desktop/test_desktop_restart.py",
+ "sha256": "50425f39e481a6fff384b9732bc0f2cd68f223d3ac980234a9142b6db981b48f",
+ "role": "Proves the mismatch dialog, saved session, and version-specific restart behavior."
},
{
"path": "openhcs/agent/services/endpoint_function_catalog_service.py",
- "sha256": "977a74003819f2547548d322f4c5f0cb148b23fec20f31be4fa462bfcfb7ea04",
+ "sha256": "bc232090472554ab075f7800bbcc238e27a646a2d8225c87e5e443fb3c5d9f78",
"role": "Owns asynchronous execution-endpoint catalogue loading, failure projection, revision state, and reuse."
},
{
- "path": "tests/unit/pyqt_gui/test_function_catalog_projection.py",
- "sha256": "b16c69e8290aa7c74dd142288b16b7e0975652fb227384f0abb50fc156387a60",
- "role": "Proves one shared non-blocking catalogue request, endpoint invalidation, and safe background-client lifecycle."
+ "path": "openhcs/mcp/dev_client.py",
+ "sha256": "c40f72cb9ba7f62b574d3f9e7b96470b5a78db60c87f6a4a99897449ae875714",
+ "role": "Defines the MCP development client's --timeout-seconds inactivity limit renewed by progress."
+ },
+ {
+ "path": "openhcs/agent/ui_bridge_environment.py",
+ "sha256": "30f459623ed48c6d653725080453630ef9813f875dd8e63d171147f5e73caacf",
+ "role": "Reads OPENHCS_UI_CONFIG_CACHE_FILE to select the UI configuration shared by the desktop and MCP client."
},
{
"path": "openhcs/agent/services/ui_bridge_transport.py",
- "sha256": "32e759baf65b34989c99223d8fa9e64800ab566cd05160df2bc125b04dea5953",
+ "sha256": "1c2fb99aef62969cfabb9d22ccb954e8894ee72fd2e66e625304ffdb96a41b95",
"role": "Rejects UI bridge responses from an incompatible OpenHCS application or bridge protocol."
},
{
"path": "openhcs/pyqt_gui/services/ui_bridge_server.py",
- "sha256": "667f582fffd4b489ca7274f8962a759c3d33d24b10ec8e6d1aa5cac01233b067",
+ "sha256": "f93a3570a834eca8596139676629dda48c3f22991fd8cb58cd03427af8b24a38",
"role": "Advertises the current application identity and rejects incompatible MCP requests before UI dispatch."
- },
- {
- "path": "tests/unit/pyqt_gui/test_ui_agent_bridge.py",
- "sha256": "1feee4bb413738f04e81cbdf26244dc0d585a68921e6c7749a941a08518007a1",
- "role": "Proves stale protocol and OpenHCS versions cannot reach running UI operations."
}
],
- "findings": [
- "The server recovery guidance now explains the version mismatch decision and its state-preserving endpoint and desktop restart.",
- "The guide lacked a recovery path for first-start function-catalogue loading; it now distinguishes asynchronous progress from endpoint failure.",
- "The guide did not tell operators how to distinguish a known pre-dispatch MCP timeout from an execute request with an unknown acceptance outcome.",
- "The guide did not distinguish execution-server replacement from recovery after an MCP/UI bridge version mismatch.",
- "The documented version-restart recovery lacked direct evidence for capture, executor-shutdown, endpoint-replacement, and detached-launch failure outcomes.",
- "The selector recovery section did not explain that progress renews client inactivity or that closing the server reaps the exact cold-catalogue preparation process.",
- "Clarified current endpoint catalogue reads after remote registration and retained asynchronous/cache ownership."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/troubleshooting_FAQ.rst against openhcs/authoring/session/operations/datasets.py: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/guide_for_biologists/troubleshooting_FAQ.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/troubleshooting_FAQ.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, openhcs/utils/environment.py, openhcs/pyqt_gui/launch.py, openhcs/runtime/viewer_protocol.py, openhcs/runtime/zmq_execution_client.py, openhcs/pyqt_gui/main.py and openhcs/pyqt_gui/services/ui_bridge_server.py; confirmed --no-gpu, OPENHCS_CPU_ONLY, the .startup.jsonl journal, materialize_runtime_artifacts and submit_timeout_ms still exist; the diffs are enum-to-family renames (orchestrator states, settle phases, viewer lifecycle, update-check origins), the AgentRuntimePlatform rename and the removed QtPlatformSystem enum, none of which the page names, so no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/troubleshooting_FAQ.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py (ScopeKey scope ids, K6 progress classes), openhcs/runtime/zmq_execution_client.py (K6: debug requests serialize themselves, command types are DebugCommandType classes; the .startup.jsonl journal beside the server log is unchanged) and openhcs/pyqt_gui/main.py (U3 moves to pyqt-reactive); the restart-and-restore action, OPENHCS_UI_CONFIG_CACHE_FILE, submit_timeout_ms and materialize_runtime_artifacts still exist, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/troubleshooting_FAQ.rst against B6's later changes to openhcs/authoring/session/operations/datasets.py, openhcs/core/pipeline/compiler.py, openhcs/runtime/viewer_protocol.py, openhcs/runtime/zmq_execution_client.py, openhcs/pyqt_gui/main.py, tests/unit/desktop/test_desktop_restart.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guide_for_biologists/troubleshooting_FAQ.rst against the later B6 changes to openhcs/authoring/session/operations/datasets.py, openhcs/core/pipeline/compiler.py, openhcs/runtime/viewer_protocol.py, openhcs/runtime/zmq_execution_client.py, openhcs/pyqt_gui/main.py, tests/unit/desktop/test_desktop_restart.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guide_for_biologists/troubleshooting_FAQ.rst in full and compared it against the changed authorities: plate_manager.py, compiler.py, environment.py, viewer_protocol.py, zmq_execution_client.py, pyqt_gui/main.py, the moved desktop restart modules and tests, smoke_installed_desktop.py, the catalogue service and UI bridge transport/server; confirmed every named option, environment variable, journal file, button and action label in current source, replaced the receipt wording of the restart paragraph, and repointed restart authorities to openhcs/desktop.",
"Re-read docs/source/guide_for_biologists/troubleshooting_FAQ.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/pyqt_gui/widgets/plate_manager.py; openhcs/core/pipeline/compiler.py; openhcs/utils/environment.py; openhcs/runtime/viewer_protocol.py; openhcs/runtime/zmq_execution_client.py; openhcs/runtime/zmq_application.py; openhcs/pyqt_gui/main.py; openhcs/pyqt_gui/services/desktop_restart.py; tests/unit/pyqt_gui/test_desktop_restart.py; openhcs/agent/services/endpoint_function_catalog_service.py; tests/unit/pyqt_gui/test_function_catalog_projection.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -2005,7 +2225,7 @@
},
{
"path": "docs/source/guides/complete_examples.rst",
- "source_sha256": "ad07bdc258a85e22c4b67c3d4694c84550dc8e0c1e136998d174e558bc9682d7",
+ "source_sha256": "8344b2d8b15319bc08668fc37e5376701e101c71cfb43b61e3ea28e9d34dac28",
"audience": [
"pipeline authors",
"demo operators"
@@ -2015,33 +2235,60 @@
"authority": [
{
"path": "openhcs/agent/services/knowledge_base_service.py",
- "sha256": "88e10e18c66eb3a8bc2ec5dd33493a9473510c0b0a9d355502d668042f32fdd9",
- "role": "Discovers and serves the maintained knowledge and example corpus used by MCP clients."
+ "sha256": "dfc26135b16f3827ced85b122cc1555c1325da1c5e805b421afcee9cdd7d91f3",
+ "role": "Generates the Official30 recipe sections lazily from the manifest-resolved .cppipe and serves them through KnowledgeBaseService."
+ },
+ {
+ "path": "tests/unit/agent/test_official30_knowledge_examples.py",
+ "sha256": "e8c5b917eceaed404e4a4858ede3acfa7cf3864255e27b82f3d6b8c4799ada9e",
+ "role": "Proves the openhcs_official30_benchmark_recipes document id and the examplehuman-openhcs-python section reached by an 'OpenHCS Python' search."
+ },
+ {
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "Declares Pure2DContract used in the minimal declaration example."
},
{
"path": "openhcs/processing/presets/mfd_specs.py",
- "sha256": "6f1808c7cecab58e3317bfa020db7d087947ed2e3b3d11838f11d47924e3a18c",
- "role": "Declares the maintained MFD example pipeline specifications exposed through the example corpus."
+ "sha256": "a3de70444a7945a42907ffbf7da988cccdd508334f1b67747093040371073486",
+ "role": "Declares the MfdPresetDefinition values, build_mfd_preset and the relative whole-device template path."
},
{
"path": "openhcs/processing/presets/pipelines/loose_operaphenix_neurite_outgrowth.py",
- "sha256": "46fb1ff75071febae663e5efa088590f579ccdd60e81a997476c32b07ddea491",
- "role": "Declares the maintained multi-step Operetta/Opera Phenix neurite-outgrowth example pipeline."
+ "sha256": "5fddd2fbd70f778a21bea6ead99d88a9b2d8962897756536a163e11619f93300",
+ "role": "Declares the example_inputs fields, including source_voxel_spacing, and the UnifiedNeurons result of the loose Opera Phenix example."
+ },
+ {
+ "path": "openhcs/microscopes/opera_phenix.py",
+ "sha256": "0af8f549988b0fb919d7b58121a3c260a47249e6cca6bf6d7e1a854171f2de38",
+ "role": "Declares OperaPhenixHandler, which requires Index.xml for native plate semantics."
},
{
"path": "scripts/master_multi_plate_demo.py",
- "sha256": "8115619d0dd4aeedfd8a4ce1b20f7eabd8f3c1f0ffbdc20e4dc8b2231cff7ffb",
- "role": "Implements the maintained end-to-end multi-plate example referenced by the example guide."
+ "sha256": "e77acafb6b10996be8daf46d7612ebc39df6cf1f3e79d590567068c8372c3516",
+ "role": "Implements the master showcase's command-line options, base port 5900, contributor loading and output root."
},
{
- "path": "tests/unit/agent/test_official30_knowledge_examples.py",
- "sha256": "6b81450dc01a33370fdad7a9a3b88bc87c3f85eddcd8b872373da9a89172c7a4",
- "role": "Proves that the official example corpus remains discoverable and structurally valid."
+ "path": "scripts/mcp_assay_showcase.py",
+ "sha256": "1afc72337ca6767f8430a3751be05dd4aeef258b409455d276f52fcba0228710",
+ "role": "Declares the seven built-in scenario blueprints the master showcase composes."
+ },
+ {
+ "path": "openhcs/interop/cellprofiler/pipeline_import.py",
+ "sha256": "5aa8895c9f533603b7ef972202141e2439473f0604b97d66f5a027b8b3cd1c83",
+ "role": "Defines import_cellprofiler_pipeline, which lowers a .cppipe into steps and a pipeline config."
}
],
- "findings": [],
- "disposition": "keep",
+ "findings": [
+ "Typed native presets said the MFD variants are declared by ``MfdPresetKey``; E1 deleted that enum and openhcs/processing/presets/mfd_specs.py now declares each variant as an ``MfdPresetDefinition`` value passed to ``build_mfd_preset``. Corrected."
+ ],
+ "disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/guides/complete_examples.rst in full and compared it against the changed authorities: openhcs/processing/presets/mfd_specs.py, openhcs/processing/presets/pipelines/loose_operaphenix_neurite_outgrowth.py, scripts/master_multi_plate_demo.py, scripts/mcp_assay_showcase.py and openhcs/interop/cellprofiler/pipeline_import.py; corrected the Typed native presets paragraph, which named the deleted MfdPresetKey enum, to name MfdPresetDefinition; the loose Opera Phenix example, showcase scripts and importer changed only enum spellings and artifact import paths, and their example_inputs, port 5900, seven scenarios and import_cellprofiler_pipeline claims still hold.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/complete_examples.rst in full and compared it against the changed authorities: openhcs/core/processing_contracts.py (CallableMetadata renamed CallableDeclaration internally), openhcs/processing/presets/pipelines/loose_operaphenix_neurite_outgrowth.py and openhcs/interop/cellprofiler/pipeline_import.py (source-binding configs now imported from openhcs.core.config, AuthoredFunctionPattern.first_callable replaces get_core_callable); the page names none of the moved symbols, and Pure2DContract, the openhcs.core.memory.decorators numpy decorator, source_voxel_spacing and import_cellprofiler_pipeline still exist, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/complete_examples.rst against B6's later changes to openhcs/core/processing_contracts.py, openhcs/processing/presets/pipelines/loose_operaphenix_neurite_outgrowth.py, openhcs/microscopes/opera_phenix.py, scripts/master_multi_plate_demo.py, scripts/mcp_assay_showcase.py, openhcs/interop/cellprofiler/pipeline_import.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/complete_examples.rst against the later B6 changes to openhcs/core/processing_contracts.py, openhcs/processing/presets/pipelines/loose_operaphenix_neurite_outgrowth.py, openhcs/microscopes/opera_phenix.py, scripts/master_multi_plate_demo.py, scripts/mcp_assay_showcase.py, openhcs/interop/cellprofiler/pipeline_import.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/complete_examples.rst in full and compared it against the changed authorities: knowledge_base_service.py, mfd_specs.py, loose_operaphenix_neurite_outgrowth.py, master_multi_plate_demo.py, test_official30_knowledge_examples.py; confirmed the code block's imports, the Official30 document and section ids, the MFD owner names and template path, the master demo flags, port, blueprint count and contributor callables, replaced Microscope.OPERAPHENIX with OperaPhenixHandler and added the source_voxel_spacing input.",
"Reviewed docs/source/guides/complete_examples.rst in full; the recorded findings and disposition are the resulting page-specific editorial decision.",
"Compared its factual claims directly with the hash-pinned files listed in authority: openhcs/agent/services/knowledge_base_service.py; openhcs/processing/presets/mfd_specs.py; openhcs/processing/presets/pipelines/loose_operaphenix_neurite_outgrowth.py; scripts/master_multi_plate_demo.py; tests/unit/agent/test_official30_knowledge_examples.py.",
"Passed `.venv/bin/python scripts/validate_docs.py docs/source/guides`: documentation validation passed: 9 files, 2 Python blocks.",
@@ -2053,7 +2300,7 @@
},
{
"path": "docs/source/guides/example_corpus_map.rst",
- "source_sha256": "e3e4af860a98ec0b9412d66a45cef57837858e71dcf2e2c65dccd0ea43e11e6a",
+ "source_sha256": "dcf4d0b8271ef5a9bed149bce8b29586c0ced5d65558d8b85f2f7ee2d1257956",
"audience": [
"pipeline authors",
"MCP agents assisting scientists"
@@ -2063,28 +2310,46 @@
"authority": [
{
"path": "openhcs/agent/services/knowledge_base_service.py",
- "sha256": "88e10e18c66eb3a8bc2ec5dd33493a9473510c0b0a9d355502d668042f32fdd9",
- "role": "Discovers and serves the maintained knowledge and example corpus used by MCP clients."
+ "sha256": "dfc26135b16f3827ced85b122cc1555c1325da1c5e805b421afcee9cdd7d91f3",
+ "role": "Serves the openhcs_official30_benchmark_recipes sections and derives the Native Example Source Index from the Python paths this page declares."
},
{
"path": "openhcs/processing/presets/mfd_specs.py",
- "sha256": "6f1808c7cecab58e3317bfa020db7d087947ed2e3b3d11838f11d47924e3a18c",
- "role": "Declares the maintained MFD example pipeline specifications exposed through the example corpus."
+ "sha256": "a3de70444a7945a42907ffbf7da988cccdd508334f1b67747093040371073486",
+ "role": "Owns the MfdPresetDefinition values, build_mfd_preset and the plate-relative whole-device template path wrapped by the four 10x_mfd modules."
+ },
+ {
+ "path": "openhcs/processing/presets/pipelines/loose_operaphenix_neurite_outgrowth.py",
+ "sha256": "5fddd2fbd70f778a21bea6ead99d88a9b2d8962897756536a163e11619f93300",
+ "role": "Declares the loose Opera Phenix example's exact Hoechst, MAP2 and SMI312 sources, one-well bound and unified neuron result."
+ },
+ {
+ "path": "openhcs/agent/capabilities.py",
+ "sha256": "87c8e2e5aeb425b9a2eb690d48bdd6f5af7a327c389deac261d7e4e0da5d609f",
+ "role": "Declares openhcs_get_authoring_context and the inspect-plate, query-plate-files, selected-plate-images and selected-plate-files inspection tools."
},
{
"path": "benchmark/datasets/manifest.py",
- "sha256": "f3ab9ca4152d54bb29a7659a96ecf5762c08844b0f0caaf0f491807c2d924802",
+ "sha256": "9a556f7dddee400493369e88eef91c19faadd6ab967a1d256ee462280ec00325",
"role": "Declares benchmark dataset identities and the manifest metadata used to obtain their inputs."
},
{
"path": "tests/unit/agent/test_knowledge_base_service.py",
- "sha256": "7b4ab158ced3ec3355d314d04e9baf0c8a4e8b2e60ce84aa56b2723c44f563c1",
- "role": "Proves discovery and retrieval of maintained knowledge-base and example resources."
+ "sha256": "7b30d1c1931efc1c380b2dacd743bcd4e509cafc949b01d24e156448681a0fc3",
+ "role": "Proves discovery and retrieval of the example corpus map and other maintained knowledge documents."
}
],
- "findings": [],
- "disposition": "keep",
+ "findings": [
+ "Native OpenHCS Examples said openhcs/processing/presets/mfd_specs.py owns 'the typed preset keys, variant declarations'; E1 deleted MfdPresetKey and MfdPresetFamily, and the module now owns MfdPresetDefinition values. Corrected to 'the preset definitions'."
+ ],
+ "disposition": "revise",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/guides/example_corpus_map.rst in full and compared it against the changed authorities: openhcs/processing/presets/mfd_specs.py, openhcs/processing/presets/pipelines/loose_operaphenix_neurite_outgrowth.py and tests/unit/agent/test_knowledge_base_service.py; corrected Native OpenHCS Examples, which said mfd_specs.py owns typed preset keys, to say it owns the preset definitions (MfdPresetKey was deleted; the knowledge-base test now searches for MfdPresetDefinition); the loose Opera Phenix example changed only enum spellings and artifact import paths.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/example_corpus_map.rst in full and compared it against the changed authority openhcs/processing/presets/pipelines/loose_operaphenix_neurite_outgrowth.py (source-binding config imports moved to openhcs.core.config, AuthoredFunctionPattern.first_callable replaces get_core_callable); the page states only the example's sources, one-well bound and review evidence, which are unchanged, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/example_corpus_map.rst against B6's later changes to tests/unit/agent/test_knowledge_base_service.py: metadata documents are written only for backends that host local files (not virtual backends such as OMERO); the image-source guide test reads axis-family spellings. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/example_corpus_map.rst against B6's later changes to openhcs/processing/presets/pipelines/loose_operaphenix_neurite_outgrowth.py, tests/unit/agent/test_knowledge_base_service.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/example_corpus_map.rst against the later B6 changes to openhcs/processing/presets/pipelines/loose_operaphenix_neurite_outgrowth.py, openhcs/agent/capabilities.py, tests/unit/agent/test_knowledge_base_service.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/example_corpus_map.rst in full and compared it against the changed authorities: knowledge_base_service.py, mfd_specs.py, benchmark/datasets/manifest.py, test_knowledge_base_service.py; confirmed every repository path, the thirty scoped native reference rows, the four MFD wrapper modules, the cellprofiler_translation topic, the authoring-context and plate inspection tool names and the Native Example Source Index; replaced 'parity receipts' with 'parity evidence'.",
"Reviewed docs/source/guides/example_corpus_map.rst in full; the recorded findings and disposition are the resulting page-specific editorial decision.",
"Compared its factual claims directly with the hash-pinned files listed in authority: openhcs/agent/services/knowledge_base_service.py; openhcs/processing/presets/mfd_specs.py; benchmark/datasets/manifest.py; tests/unit/agent/test_knowledge_base_service.py.",
"Passed `.venv/bin/python scripts/validate_docs.py docs/source/guides`: documentation validation passed: 9 files, 2 Python blocks.",
@@ -2095,7 +2360,7 @@
},
{
"path": "docs/source/guides/fiji_viewer_management.rst",
- "source_sha256": "43f0e1f0effa40bf1a1efe18ac3efe8da0a184ad746da086b19f756f06178943",
+ "source_sha256": "0ab33853df56f236383b04988dce4497ec11a258059341604e62c38a27b28075",
"audience": [
"viewer integrators",
"pipeline authors"
@@ -2105,28 +2370,43 @@
"authority": [
{
"path": "openhcs/runtime/fiji_viewer_server.py",
- "sha256": "cde38de711dced10b8af10b3b0ca69b1111c234a2877f0aade1e762ccbb6777b",
- "role": "Implements Fiji stream reception, image-stack updates, ROI handling, and endpoint lifecycle."
+ "sha256": "352ef6b773da07b8103873606cf8a438b8ec70b64cfc57526cc9b18cf9b48b40",
+ "role": "Initializes Fiji through FIJI_IMAGEJ_RUNTIME, tracks debounced display-batch settlement, registers the run-macro action and delegates gateway shutdown."
+ },
+ {
+ "path": "openhcs/runtime/viewer_control_actions.py",
+ "sha256": "f19c8370dd9145f11ba7ceb91d2efff123a6473db9bc551474a47a37acb4041d",
+ "role": "Gives every viewer the shared shutdown, clear-state, process-launch and settle actions and answers unregistered control types with an unsupported-control error."
},
{
"path": "openhcs/runtime/viewer_protocol.py",
- "sha256": "30441eab97da9ffcb5b9dc33d65c2ecc56c426e79bb2b27eedc78a68aaf13bc3",
- "role": "Declares the transport-independent viewer messages, stream configuration, and server contract."
+ "sha256": "347a65b63c149f2f437014d537cfaf481a33801d860742f4083b7377494e792f",
+ "role": "Declares ViewerSettleProgress and the viewer control message types named on the page."
},
{
"path": "external/zmqruntime/src/zmqruntime/__init__.py",
- "sha256": "37f3e14f58b4c1dda3d338a3a2fb075aefea215f6f25b69f44091891341755ef",
+ "sha256": "394b1071d1bf74c6a4fc5d3785c868072a0da1d11f8bdb4b048d91638053b36c",
"role": "Exports the generic ZMQ runtime contracts used by viewer and execution endpoints."
},
+ {
+ "path": "pyproject.toml",
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
+ "role": "Declares the fiji and viz extras named in setup."
+ },
{
"path": "tests/unit/test_fiji_viewer_server.py",
- "sha256": "2d590f629243eec979531b0959a1006690befbd17b9778ec9f4fbefea72877f3",
- "role": "Proves Fiji endpoint lifecycle, streamed image updates, and ROI message handling."
+ "sha256": "b19316b843cc53c21bd3e00b22694fb645dcba062610295adf54abed7b4f51df",
+ "role": "Proves Fiji endpoint lifecycle, streamed image updates, settlement and control handling."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/guides/fiji_viewer_management.rst in full and compared it against the changed authorities: openhcs/runtime/fiji_viewer_server.py, openhcs/runtime/viewer_control_actions.py, openhcs/runtime/viewer_protocol.py, pyproject.toml and tests/unit/test_fiji_viewer_server.py; ViewerSettlePhase became a subclass family (SettlementRunning, SettlementComplete, SettlementFailed) with the same running/complete/failed spellings the page describes, ViewerSettleProgress remains, SETTLE, STATE and PAYLOADS are still control types, FIJI_IMAGEJ_RUNTIME still initializes Fiji and pyproject.toml still declares the fiji and viz extras; no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/fiji_viewer_management.rst in full and compared it against the changed authorities: external/zmqruntime/src/zmqruntime/__init__.py (current lazy export surface of client, messages and execution contracts) and pyproject.toml (dependency floors only; fiji and viz extras unchanged); the page's ZMQRuntime ownership and setup claims still hold, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/fiji_viewer_management.rst against B6's later changes to openhcs/runtime/fiji_viewer_server.py, openhcs/runtime/viewer_protocol.py, tests/unit/test_fiji_viewer_server.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/fiji_viewer_management.rst against the later B6 changes to openhcs/runtime/fiji_viewer_server.py, openhcs/runtime/viewer_protocol.py, pyproject.toml, tests/unit/test_fiji_viewer_server.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/fiji_viewer_management.rst in full and compared it against the changed authorities: fiji_viewer_server.py, viewer_protocol.py, zmqruntime __init__.py, test_fiji_viewer_server.py, plus the new shared viewer_control_actions.py; rewrote the settlement section for batch-tracked settle progress and the unsupported STATE/PAYLOADS answer, and confirmed the extras, FIJI_IMAGEJ_RUNTIME initialization and shutdown delegation.",
"Reviewed docs/source/guides/fiji_viewer_management.rst in full; the recorded findings and disposition are the resulting page-specific editorial decision.",
"Compared its factual claims directly with the hash-pinned files listed in authority: openhcs/runtime/fiji_viewer_server.py; openhcs/runtime/viewer_protocol.py; external/zmqruntime/src/zmqruntime/__init__.py; tests/unit/test_fiji_viewer_server.py.",
"Passed `.venv/bin/python scripts/validate_docs.py docs/source/guides`: documentation validation passed: 9 files, 2 Python blocks.",
@@ -2149,35 +2429,43 @@
"authority": [
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
"role": "Compiles pipeline declarations into validated step plans and execution bundles."
},
{
"path": "openhcs/core/memory/decorators.py",
- "sha256": "d9f9cf0be7593dc0ebfc57f258aebed75bcf9cfa508ed1e942137366d819766a",
+ "sha256": "76f234785f4865257fd19c935e0aace82d1f5586198737fec619ee243b79cbf6",
"role": "Exports OpenHCS memory and dtype decorators while delegating conversion semantics to ArrayBridge declarations."
},
+ {
+ "path": "external/arraybridge/src/arraybridge/types.py",
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
+ "role": "Owns the input, output and execution memory attributes and framework runtimes named by the memory-integration route."
+ },
+ {
+ "path": "openhcs/core/pipeline/framework_device_assignment.py",
+ "sha256": "cdfde10f996ab682952a1d4948d3de2d23d48353eae41f3124770e19fc28a193",
+ "role": "Plans framework-local worker device assignment named by the memory-integration route."
+ },
{
"path": "openhcs/runtime/viewer_protocol.py",
- "sha256": "30441eab97da9ffcb5b9dc33d65c2ecc56c426e79bb2b27eedc78a68aaf13bc3",
+ "sha256": "347a65b63c149f2f437014d537cfaf481a33801d860742f4083b7377494e792f",
"role": "Declares the transport-independent viewer messages, stream configuration, and server contract."
},
{
"path": "external/PolyStore/src/polystore/omero_local.py",
- "sha256": "35d39564317510234c2a3ded00bb06a09417d1b6f7359e52e74c7bd0a086f642",
+ "sha256": "e549d4811539cec9842ceac007eb666380ef00d6838a9b69d91eb02de64d1daa",
"role": "Implements the OMERO-backed PolyStore adapter, including connection and object materialisation behaviour."
- },
- {
- "path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
- "role": "Owns the callable memory metadata and framework capabilities named by the memory-integration route."
}
],
- "findings": [
- "The memory-integration route omitted ArrayBridge execution metadata and framework-local planning."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/guides/index.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py and openhcs/runtime/viewer_protocol.py; both diffs are enum-to-family renames (orchestrator states, materialization backends, settle phases, viewer launch session and lifecycle); the page only routes to the five explanations and names none of them, so no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/index.rst in full and compared it against the changed authority openhcs/core/pipeline/compiler.py (ScopeKey-built compiler scope ids from U3, CompileProgressPhase/RunningProgressStatus progress classes from K6, CompiledSourceBindingPlan() default); the page only routes to the integration explanations and states no compiler detail, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/index.rst against B6's later changes to openhcs/core/pipeline/compiler.py, openhcs/core/memory/decorators.py, openhcs/runtime/viewer_protocol.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/index.rst against the later B6 changes to openhcs/core/pipeline/compiler.py, openhcs/core/memory/decorators.py, openhcs/runtime/viewer_protocol.py, external/PolyStore/src/polystore/omero_local.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/index.rst in full and compared it against the changed authorities: compiler.py, core/memory/decorators.py, viewer_protocol.py, arraybridge types.py; the five toctree targets exist, ArrayBridge still declares input, output and execution memory attributes, framework device assignment still plans worker scope, and the viewer and OMERO routes name current owners; no correction was needed.",
"Re-read docs/source/guides/index.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline/compiler.py; openhcs/core/memory/decorators.py; openhcs/runtime/viewer_protocol.py; external/PolyStore/src/polystore/omero_local.py; external/arraybridge/src/arraybridge/types.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -2192,7 +2480,7 @@
},
{
"path": "docs/source/guides/memory_type_integration.rst",
- "source_sha256": "7890c784379b0de4ecc3871068f1acd13de522077d16a7589436ab94a8019adb",
+ "source_sha256": "8861e967f496aa32bc9976946d593aa6e2af606de66ec2de75fb44416feb58e9",
"audience": [
"callable authors",
"compiler contributors"
@@ -2202,52 +2490,42 @@
"authority": [
{
"path": "external/arraybridge/src/arraybridge/types.py",
- "sha256": "7b94760d57e54008d7adf4e4d37dca45d8a2c288a9dc8400be00c26148cf7624",
- "role": "Declares framework capabilities, callable memory metadata, and the child-process import environment owned by each MemoryType member."
+ "sha256": "08f7ff4df8807bb3340e00a02c6276b1c702a0f00a2eedacd701142736c5d804",
+ "role": "Declares MemoryContractAttribute's input, output and execution keys, framework runtimes, and MemoryType.subprocess_environment()."
},
{
"path": "openhcs/constants/constants.py",
- "sha256": "4cd6c29abd8b8e3ecc396b424d2b8b61e819a215fc1d770d3f6852b260a0691d",
- "role": "Provides the legacy OpenHCS constants import surface as identity-preserving compatibility re-exports."
+ "sha256": "00957394953071b0fff6ef8298648351d19877a408dff5f7b538e7249fcb9247",
+ "role": "Re-exports arraybridge.MemoryType as openhcs.constants.MemoryType without a translation."
},
{
"path": "openhcs/core/memory/__init__.py",
- "sha256": "9d5223bda2ace85b6dfb91b0d510fafcbfe6fc19c99f6e2f9c4325dc0d295f47",
- "role": "Provides the OpenHCS memory integration import surface over ArrayBridge."
+ "sha256": "94584daee1b57afe94a2b143901cc156cc947c44ce654fc8e52882e3ed7f464f",
+ "role": "Re-exports arraybridge.MemoryType as openhcs.core.memory.MemoryType."
},
{
"path": "openhcs/core/memory/decorators.py",
- "sha256": "d9f9cf0be7593dc0ebfc57f258aebed75bcf9cfa508ed1e942137366d819766a",
- "role": "Exports OpenHCS memory and dtype decorators while delegating conversion semantics to ArrayBridge declarations."
- },
- {
- "path": "openhcs/core/callable_contract.py",
- "sha256": "6a39b5e171901131ecb7280a78241e8ea72409a5003eabba3db34eb4851991d7",
- "role": "Derives callable input, output, execution-framework, dimensional, artifact, and runtime requirements from declarations."
- },
- {
- "path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
- "role": "Declares typed per-step memory, conversion, framework-device assignment, and runtime decisions."
+ "sha256": "76f234785f4865257fd19c935e0aace82d1f5586198737fec619ee243b79cbf6",
+ "role": "Exports the OpenHCS memory decorators used in the example while delegating conversion semantics to ArrayBridge."
},
{
- "path": "external/arraybridge/src/arraybridge/decorators.py",
- "sha256": "99823105b0ae59054df1d84013633b8b6628d0a6dc3326383199ac7c65c69bed",
- "role": "Declares decorators that write independent input, output, and execution memory roles plus dtype policy."
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "Declares the ProcessingContract family and Pure2DContract used in the example."
},
{
- "path": "tests/unit/test_callable_contract.py",
- "sha256": "493f76a54f5fe024ca8f10c53b1f810b8c369626ad7141205b42ef3a78282137",
- "role": "Proves callable contract derivation for dimensions, memory types, dtype policy, and outputs."
+ "path": "openhcs/core/callable_contract.py",
+ "sha256": "6089f63e72e1a47fc5f6bc7b443028ac8ec40d934898589fa03b5592c2273ab3",
+ "role": "Reads input, output and execution memory types through MemoryContractAttribute."
},
{
- "path": "tests/unit/test_memory_type_authority.py",
- "sha256": "0da705f6f5468c2bc8a866f44057c6790a2a679d80648f47c9b373ce1af64632",
- "role": "Proves enum and framework-set identity across the ArrayBridge and compatibility import surfaces, including legacy pickle loading."
+ "path": "openhcs/core/compiled_step_plan.py",
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "Declares FrameworkDeviceAssignment and the typed per-step conversion decisions recorded by compilation."
},
{
"path": "openhcs/core/pipeline/framework_device_assignment.py",
- "sha256": "f77f3fb2d30c4af8cab6210dbe294f9fb5e246dad0a6bbf88bcaf1e2b58dead7",
+ "sha256": "cdfde10f996ab682952a1d4948d3de2d23d48353eae41f3124770e19fc28a193",
"role": "Projects each compilation's declaration-derived framework footprint into exact per-step device assignments."
},
{
@@ -2257,31 +2535,39 @@
},
{
"path": "openhcs/core/steps/function_runtime.py",
- "sha256": "b1449120bebe7cf4898c055d841dc258c7d91d976d9e5ff7396aed81ad5011e7",
- "role": "Scopes a callable body through its declared execution framework during worker invocation."
+ "sha256": "0b5b3b025811df3b8075e51d605474b8be1e4b7f6fe873da82882e0232bf7dc2",
+ "role": "Enters the invocation's execution device scope around the function body."
},
{
"path": "openhcs/core/orchestrator/worker_execution.py",
- "sha256": "3c952465268e4b6c44676a853bb9c99d2d04a0490c7d398d2cdaf8b3b5f966d7",
+ "sha256": "83ade0271c9b24ca1ba61c708d9e6f60a432b630ec6a8a2fac6d1018458e6be5",
"role": "Cleans the exact compiled framework-device footprint during worker teardown."
},
{
- "path": "tests/unit/test_framework_device_resolver.py",
- "sha256": "ba1814ae8eae05a39b594a81b4fc0d1444bf1334574bf4efe49aa829eec43a65",
- "role": "Proves framework-local device resolution, scope selection, and cleanup behaviour."
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares variable_components and group_by on the step processing configuration."
+ },
+ {
+ "path": "tests/unit/test_memory_type_authority.py",
+ "sha256": "0da705f6f5468c2bc8a866f44057c6790a2a679d80648f47c9b373ce1af64632",
+ "role": "Proves MemoryType identity across the ArrayBridge and re-export import paths."
},
{
"path": "tests/unit/test_framework_execution_scope.py",
- "sha256": "888a7693d387b0d5007c9868f0b0344fa3df26620f35e40b45832f0931ef7126",
+ "sha256": "b61d2df837d570f99031b29cadf75d2ac7381617cdd845b2fb67914eb5015a3f",
"role": "Proves execution memory metadata selects the runtime framework device scope independently of boundary types."
}
],
- "findings": [
- "The explanation covered only input/output memory boundaries and the removed application-owned GPU model; it now documents execution ownership, framework-local assignment, scoping, and cleanup.",
- "The integration explanation omitted the declaration-derived environment used before optional frameworks are imported in fresh child processes."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/core/steps/function_runtime.py changed by black formatting only (AST-equal) after docs/source/guides/memory_type_integration.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/memory_type_integration.rst in full and compared it against the changed authorities: openhcs/constants/constants.py, openhcs/core/callable_contract.py, openhcs/core/compiled_step_plan.py, openhcs/core/steps/function_runtime.py, openhcs/core/orchestrator/worker_execution.py and openhcs/core/config.py; constants.py still re-exports arraybridge's MemoryType (its OrchestratorState enum became a class family, which the page does not mention), CallableContract still reads MemoryContractAttribute, FrameworkDeviceAssignment is still on CompiledStepPlan, function_runtime.py still enters execution_device_scope() around the function body, and variable_components and group_by remain on ProcessingConfig; no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/memory_type_integration.rst in full and compared it against the changed authorities: openhcs/core/processing_contracts.py and openhcs/core/callable_contract.py (K5: CallableMetadata became CallableDeclaration, which CallableContract inherits and which reads the input, output and execution keys through MemoryContractAttribute), openhcs/core/compiled_step_plan.py (empty-plan defaults), openhcs/core/steps/function_runtime.py (execution_device_scope still wraps the function body), openhcs/core/orchestrator/worker_execution.py (K6 progress classes; compiled GPU footprint cleanup unchanged), openhcs/core/config.py (variable_components and group_by still on the processing config) and tests/unit/test_framework_execution_scope.py; confirmed openhcs.constants.MemoryType and openhcs.core.memory.MemoryType are arraybridge.MemoryType and that MemoryType.subprocess_environment exists; no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/memory_type_integration.rst against B6's later changes to openhcs/core/memory/decorators.py, openhcs/core/processing_contracts.py, openhcs/core/callable_contract.py, openhcs/core/compiled_step_plan.py, openhcs/core/steps/function_runtime.py, openhcs/core/orchestrator/worker_execution.py, openhcs/core/config.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/memory_type_integration.rst against the later B6 changes to openhcs/core/memory/decorators.py, openhcs/core/processing_contracts.py, openhcs/core/callable_contract.py, openhcs/core/compiled_step_plan.py, openhcs/core/steps/function_runtime.py, openhcs/core/orchestrator/worker_execution.py, openhcs/core/config.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/memory_type_integration.rst in full and compared it against the changed authorities: arraybridge types.py and decorators.py, constants.py, core/memory/__init__.py and decorators.py, callable_contract.py, compiled_step_plan.py, test_callable_contract.py, framework_device_assignment.py, function_runtime.py, worker_execution.py, test_framework_execution_scope.py; confirmed the MemoryType re-exports, MemoryContractAttribute reads, FrameworkDeviceAssignment, execution device scope, subprocess_environment consumers and variable_components/group_by, and reworded the processing-contract paragraph for the subclass family.",
"On 2026-08-27, re-read docs/source/guides/memory_type_integration.rst in full after callable contracts began projecting runtime-owned inputs into generic form exclusions; challenged the ArrayBridge/OpenHCS ownership boundary and found no prose drift.",
"Compared every factual claim directly with the current hash-pinned authorities: external/arraybridge/src/arraybridge/types.py; openhcs/constants/constants.py; openhcs/core/memory/__init__.py; openhcs/core/memory/decorators.py; openhcs/core/callable_contract.py; openhcs/core/compiled_step_plan.py; external/arraybridge/src/arraybridge/decorators.py; tests/unit/test_callable_contract.py; tests/unit/test_memory_type_authority.py; openhcs/core/pipeline/framework_device_assignment.py; openhcs/core/framework_device_resolver.py; openhcs/core/steps/function_runtime.py; openhcs/core/orchestrator/worker_execution.py; tests/unit/test_framework_device_resolver.py; tests/unit/test_framework_execution_scope.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -2296,7 +2582,7 @@
},
{
"path": "docs/source/guides/omero_integration.rst",
- "source_sha256": "b900bea91d2eaecb90d593786b213830c6fd689b6cbc09c25f9b0c7ab2a6183e",
+ "source_sha256": "9ce2fd0bdbac524d6db0ff923ea53b8e13758d45c2bc271f7ec59edaac62424f",
"audience": [
"OMERO integrators",
"deployment contributors"
@@ -2306,19 +2592,24 @@
"authority": [
{
"path": "external/PolyStore/src/polystore/omero_local.py",
- "sha256": "35d39564317510234c2a3ded00bb06a09417d1b6f7359e52e74c7bd0a086f642",
- "role": "Implements the OMERO-backed PolyStore adapter, including connection and object materialisation behaviour."
+ "sha256": "e549d4811539cec9842ceac007eb666380ef00d6838a9b69d91eb02de64d1daa",
+ "role": "Implements OMEROLocalBackend, its virtual images_dir projection, PurePosixPath normalization and save_batch plane resolution."
},
{
"path": "external/PolyStore/src/polystore/omero_address.py",
"sha256": "8a3ae8789fceba0f099eef2ee983d858a8f83b764f19b70dfa7bb88f521c0c92",
- "role": "Owns strict concrete OMERO plane addresses and symbolic filename templates through one component grammar."
+ "role": "Owns OMEROPlaneAddress, OMEROPlaneFilenameTemplate and the multi-letter OMEROWellAddress."
},
{
"path": "external/PolyStore/src/polystore/filemanager.py",
- "sha256": "877a2b10731b12904b1784a5b42c9e39688e3a84f7f86018c1c2beee96ea2296",
+ "sha256": "a9e9f3a8205216efc769accad06ab64d5373e6726733b2a1f2e3881613d1880e",
"role": "Delegates contextual save arguments and persistence to the selected DataSink instance."
},
+ {
+ "path": "external/PolyStore/src/polystore/base.py",
+ "sha256": "8f2082ade15072bc8e8eba363c75a1d1cff7c67c74845e6485f69eab244634dc",
+ "role": "Declares the DataSink interface whose contextual save arguments generic materialization requests."
+ },
{
"path": "external/PolyStore/src/polystore/omero_tables.py",
"sha256": "910aaa6bea75f40bae82df71dd414415278b1387b80c7f1bc11e8d681874451e",
@@ -2330,29 +2621,24 @@
"role": "Declares supported text extensions, MIME types, and table parsers together."
},
{
- "path": "openhcs/core/orchestrator/analysis_consolidation.py",
- "sha256": "3ef9c20c347c1ac724353c84b24f9d7e13513aba7b9c5c01e194171b7e72bb52",
- "role": "Projects execution-ledger CSV content and writes summaries through the compiled persistent backend."
+ "path": "openhcs/domains/microscopy/analysis_consolidation.py",
+ "sha256": "292f7138a00dc22dba5b8d8af5de6140d247b955723cc807cc982ffec7a7dd2d",
+ "role": "Consolidates execution-ledger CSV content and writes summaries through FileManager and the compiled backend."
},
{
- "path": "external/PolyStore/src/polystore/base.py",
- "sha256": "ae641affae21b61448816a14bd68b7f2f3fc0082f0f1fbe51079ed32520450b1",
- "role": "Declares the storage backend interface and lifecycle shared by local and remote materialisation backends."
- },
- {
- "path": "openhcs/microscopes/microscope_interfaces.py",
- "sha256": "a603fa1c58ba005f8ba22713e46efe4e33aee0f762ba188467482ae3ae429200",
- "role": "Declares the microscope source interface, plate dimensions, metadata, and acquisition access contract."
+ "path": "openhcs/core/dataset_sources/source.py",
+ "sha256": "b3758a878fdf8ec28b6d9818ebfd96c5c470895575f7a79500c85f1490679281",
+ "role": "Declares the DatasetSource interface the OMERO source implements for workspace and metadata access."
},
{
"path": "openhcs/microscopes/omero.py",
- "sha256": "341733257cfb4c3bbe35dbc311c60e1666d357af6c6b7dc416399f77a92c570e",
- "role": "Projects PolyStore's concrete and symbolic OMERO filename boundaries into OpenHCS components."
+ "sha256": "a3ab5a257490f729e96ba413c3f11d12f10f68dae44a728206f81b850325da31",
+ "role": "Declares OMEROHandler and OMEROFilenameParser, which map PolyStore's concrete and symbolic OMERO filenames into FilenameParseResult."
},
{
"path": "tests/unit/test_omero_microscope_metadata.py",
- "sha256": "066e612d760bb5d0a75c5301cc6fb2d39ef460f1d7e84c67679505bec11ec7b9",
- "role": "Proves microscope metadata plus concrete and symbolic OMERO pattern projection through the common interfaces."
+ "sha256": "55adf5ec17e7e717c8b4a70828736743c878599ee3f70b55430bbd138316ea8f",
+ "role": "Proves OMERO metadata plus concrete and symbolic pattern projection."
},
{
"path": "openhcs/omero/plugin/README.md",
@@ -2367,11 +2653,11 @@
{
"path": "openhcs/runtime/omero_instance_manager.py",
"sha256": "af94c8b851bd4e1456e5c85b16f83ac8008944b05a71ed9d9db5574416704933",
- "role": "Owns OpenHCS instance lifecycle, packaged-only table-component recovery, exact Compose invocation, and complete readiness acceptance."
+ "role": "Owns OMEROInstanceManager lifecycle, derived local defaults, remote overrides, bounded Docker wait and packaged table-component recovery."
},
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
"role": "Includes the complete openhcs/omero deployment and plugin bundle in installed distributions."
},
{
@@ -2382,39 +2668,22 @@
{
"path": "tests/unit/test_omero_instance_manager.py",
"sha256": "c9341221f85069ebb465e67f28f240191a94bc690c2999d9de36212505dccda1",
- "role": "Proves canonical compose resolution, pinned images, packaged table recovery, external lifecycle preservation, and fail-closed context entry."
- },
- {
- "path": "tests/unit/test_validate_wheel_deployment.py",
- "sha256": "7909c30f7afc321f939aebf52c5c603e153cfa13e869492e0538f78dc632e145",
- "role": "Proves packaged deployment completeness and rejects parallel compose authorities, nested build output, nested package metadata, or developer-home paths."
- },
- {
- "path": "tests/integration/test_main.py",
- "sha256": "1a0c532eb631a89b77f92a4202a7b55033c86d7b872350156f106d276b061e9f",
- "role": "Exercises live OMERO instance and integration behaviour through the packaged OpenHCS boundary."
+ "role": "Proves compose resolution, pinned images, packaged table recovery, external lifecycle preservation, and fail-closed context entry."
},
{
"path": "openhcs/agent/ui_bridge_identities.py",
- "sha256": "5913d14b99340174af87b2a7cafa3cdd080a331553c0cf59392ade7d253f1234",
- "role": "Declares the complete stable desktop-window family used to verify that the current UI exposes no OMERO management or credential surface."
+ "sha256": "66dcf838d08cac2004ab8f73db777f73270cc520697281cf4f94a5fc6af4dc64",
+ "role": "Declares the desktop-window family, which contains no OMERO management or credential window."
}
],
- "findings": [
- "The page mirrored volatile alpha and legacy-client status from another package; it now delegates release compatibility to that owner and states only the OpenHCS declaration gate.",
- "The explanation treated omero_openhcs as a repository-root deployment project; it now distinguishes the bundled web application at openhcs/omero/plugin from the installed OpenHCS deployment and instance-lifecycle owner.",
- "A wheel built from a developer tree could absorb ignored OMERO build output and nested package metadata; package-data selection now names the canonical bundle, build output is reprojected from scratch, and the installed-wheel gate seeds both contamination forms.",
- "The explanation did not state that runtime analysis summaries consume execution-ledger content and persist through the compiled backend instead of reopening OMERO virtual paths as local files.",
- "Two compose declarations and hardcoded shell probes competed with the runtime lifecycle owner; the bundle now ships one pinned compose file and the wheel gate rejects parallel declarations.",
- "The default compose stack exposed an alpha panel that cannot submit the current execution contract; the upstream OMERO.web viewer remains available while the panel stays behind its owner-package integration gate.",
- "The manager mirrored local connection defaults from Compose; typed settings now project the canonical declaration and explicit values remain remote-instance overrides.",
- "Generic platform-string dispatch attempted to start Docker Desktop or a host service manager; the integration boundary now requires host-owned Docker availability.",
- "Pattern discovery passed symbolic fields through the strict concrete plane-address type; PolyStore now owns a separate template identity without weakening address validation.",
- "A one-shot Docker daemon probe made otherwise valid cold local deployments fail transiently; the instance manager now waits boundedly without taking ownership of daemon startup.",
- "A cold packaged deployment could leave Tables-0 disabled after it started before the managed repository; the explanation now distinguishes PolyStore readiness from OpenHCS-owned local recovery."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/guides/omero_integration.rst in full and compared it against the changed authorities: openhcs/core/dataset_sources/source.py and pyproject.toml; DatasetSource lost get_required_backend and now compares roots through absolute_source_path, neither of which the page describes; pyproject.toml still packages the openhcs/omero bundle and only moved the python-introspect floor; no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/omero_integration.rst in full and compared it against the changed authorities: openhcs/core/dataset_sources/source.py (SourceBindingsConfig type import now from openhcs.core.config, K4) and pyproject.toml (dependency floors only); the page names neither, and its DatasetSource and OMERO ownership claims still hold, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/omero_integration.rst against B6's later changes to openhcs/domains/microscopy/analysis_consolidation.py, openhcs/core/dataset_sources/source.py, openhcs/microscopes/omero.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/omero_integration.rst against the later B6 changes to external/PolyStore/src/polystore/omero_local.py, openhcs/domains/microscopy/analysis_consolidation.py, openhcs/core/dataset_sources/source.py, openhcs/microscopes/omero.py, pyproject.toml: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/omero_integration.rst in full and compared it against the changed authorities: PolyStore filemanager.py and base.py, microscopes/omero.py, test_omero_microscope_metadata.py, pyproject.toml, tests/integration/test_main.py, ui_bridge_identities.py, plus the moved analysis consolidation and dataset-source owners; confirmed OMEROFilenameParser, FilenameParseResult, the PolyStore address types, DataSink contextual saves, ledger-based consolidation through FileManager and OMEROInstanceManager, and dropped 'canonical'.",
"On 2026-08-27, re-read docs/source/guides/omero_integration.rst in full after the python-introspect dependency floor advanced to 0.1.12; challenged its OMERO ownership explanation and found no affected claim.",
"Compared every factual claim directly with the current hash-pinned authorities: external/PolyStore/src/polystore/omero_local.py; external/PolyStore/src/polystore/base.py; external/PolyStore/src/polystore/omero_tables.py; openhcs/microscopes/microscope_interfaces.py; tests/unit/test_omero_microscope_metadata.py; openhcs/omero/plugin/README.md; openhcs/omero/docker-compose.yml; openhcs/runtime/omero_instance_manager.py; pyproject.toml; scripts/validate_wheel_deployment.py; tests/unit/test_omero_instance_manager.py; tests/unit/test_validate_wheel_deployment.py; tests/integration/test_main.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -2451,7 +2720,7 @@
},
{
"path": "docs/source/guides/pipeline_compilation_workflow.rst",
- "source_sha256": "051f6d70831eb9939375063651d825604695186de950275b512f5bd8c64a6293",
+ "source_sha256": "c74f1449defb5d46a2537850d5cf7f0d3183bcea11be0f85883f29fba3cbf97f",
"audience": [
"pipeline integrators",
"compiler contributors"
@@ -2461,38 +2730,48 @@
"authority": [
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compiles pipeline declarations into validated step plans and execution bundles."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Defines compile_pipelines over an orchestrator and list[AbstractStep] and returns a CompiledExecutionBundle."
+ },
+ {
+ "path": "openhcs/core/orchestrator/orchestrator.py",
+ "sha256": "a7097eec86c5966552bcb546a2161d1a08e6a4c630e6b156de3b8354b0026b54",
+ "role": "Supplies the initialized orchestrator, its DatasetSource workspace and selected partition values to compilation."
},
{
"path": "openhcs/core/pipeline/compilation_session.py",
- "sha256": "3c840a89baccc8665d415175458280fd904c1a837b7fd879e303d7dbca129a1c",
- "role": "Owns compilation-scoped state and resolves declarations into frozen plans without leaking runtime values."
+ "sha256": "4946c221e34ec19957b5af2b1a54359d454ee840fb1f5e1fe2b3b306ac8e5426",
+ "role": "Declares CompilationSession, which owns cross-step compile state."
},
{
"path": "openhcs/core/compiled_step_plan.py",
- "sha256": "6d46571e6f2d069b98a92b41d711b2e20bfab9f0493cf73d3f150d5be08e7f96",
- "role": "Declares typed per-step memory, conversion, framework-device assignment, and runtime decisions."
+ "sha256": "6525348d097bda8d0265bc7c3f312dd8839ca9f65dd05f797fc82ba873e04547",
+ "role": "Declares CompiledStepPlan and its typed per-step memory, conversion, device and runtime fields."
},
{
"path": "openhcs/core/compiled_execution.py",
- "sha256": "941115f2dca2e53538d3141a59f3ae1f928a87cf741fcfa177f7cd64a5f41aea",
- "role": "Declares the frozen compiled execution bundle consumed by workers after compilation."
- },
- {
- "path": "tests/unit/test_compiled_execution.py",
- "sha256": "a7ef4c3f011e0770d8e7a48928aa25c6ca550095c1f80146a25170d449ecc31f",
- "role": "Proves compilation produces immutable step plans and execution bundles with the expected resolved values."
+ "sha256": "85972d418f0958e59f3185f07caf25a103bc9e101e481b2b63903377cc05e397",
+ "role": "Declares the frozen CompiledExecutionBundle consumed by workers after compilation."
},
{
"path": "openhcs/core/pipeline/framework_device_assignment.py",
- "sha256": "f77f3fb2d30c4af8cab6210dbe294f9fb5e246dad0a6bbf88bcaf1e2b58dead7",
+ "sha256": "cdfde10f996ab682952a1d4948d3de2d23d48353eae41f3124770e19fc28a193",
"role": "Owns declaration-derived framework device assignment during compilation."
+ },
+ {
+ "path": "tests/unit/test_compiled_execution.py",
+ "sha256": "906c9f7eadb2faf92244e6ce22ce98cc5e0f6bd1d1b967458bbedc799e92e465",
+ "role": "Proves compilation produces immutable step plans and execution bundles with the expected resolved values."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/guides/pipeline_compilation_workflow.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py, openhcs/core/orchestrator/orchestrator.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py and tests/unit/test_compiled_execution.py; CompilationSession, CompiledStepPlan and CompiledExecutionBundle keep their names and roles; the diffs replace OrchestratorState, MaterializationBackend and RuntimeObservationMode enum members with subclass families and move artifact imports to openhcs.core.artifacts.plans and .kinds, none of which the page names, so no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/pipeline_compilation_workflow.rst in full and compared it against the changed authorities: openhcs/core/pipeline/compiler.py (ScopeKey scope ids, K6 progress classes), openhcs/core/orchestrator/orchestrator.py (source_bindings_defaults_to_base imported from openhcs.core.config), openhcs/core/pipeline/compilation_session.py (strip_disabled_functions removed, per-graph invocation validation moved onto the graph) and openhcs/core/compiled_step_plan.py (empty-plan defaults now plain constructors); CompilationSession, CompiledStepPlan and CompiledExecutionBundle still own the stated responsibilities, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/pipeline_compilation_workflow.rst against B6's later changes to openhcs/core/pipeline/compiler.py, openhcs/core/orchestrator/orchestrator.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py, tests/unit/test_compiled_execution.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/pipeline_compilation_workflow.rst against the later B6 changes to openhcs/core/pipeline/compiler.py, openhcs/core/orchestrator/orchestrator.py, openhcs/core/pipeline/compilation_session.py, openhcs/core/compiled_step_plan.py, tests/unit/test_compiled_execution.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/pipeline_compilation_workflow.rst in full and compared it against the changed authorities: compiler.py, compilation_session.py, compiled_step_plan.py, compiled_execution.py, test_compiled_execution.py, framework_device_assignment.py; confirmed CompilationSession, CompiledStepPlan, CompiledExecutionBundle and the compile_pipelines inputs, and renamed the microscope/source workspace to the dataset-source workspace.",
"Re-read docs/source/guides/pipeline_compilation_workflow.rst in full against repository base bb4218fae and the current shared worktree; challenged its explanation user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline/compiler.py; openhcs/core/pipeline/compilation_session.py; openhcs/core/compiled_step_plan.py; openhcs/core/compiled_execution.py; tests/unit/test_compiled_execution.py; openhcs/core/pipeline/framework_device_assignment.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -2504,7 +2783,7 @@
},
{
"path": "docs/source/guides/testing_guide.rst",
- "source_sha256": "82d14d7b3163bd6c7313922d7509cd80325e1021d83e80e50aa8775ac902e59b",
+ "source_sha256": "7e1836870d03c13b0bc04c38e699e09b4ffbc5ae485667ac76f262f6e3187241",
"audience": [
"contributors",
"integration-test operators"
@@ -2514,7 +2793,7 @@
"authority": [
{
"path": "pytest.ini",
- "sha256": "abeb2cd3d7ac83c5988690e5b991c0989a96b46d62febc48ba1c7664b6a30d1f",
+ "sha256": "d00f34c3bddc54800f38769af84f43dc2e91497fe7a7891eb06706bb257326be",
"role": "Declares the repository's pytest markers, collection rules, and default test configuration."
},
{
@@ -2524,12 +2803,12 @@
},
{
"path": ".github/workflows/integration-tests.yml",
- "sha256": "c1ec94eab493bc46986c55b8b1428d9a0a5c0eaf5789f2dddbc06df2c0843ff7",
- "role": "Defines the operating-system integration matrix, dependency setup, installed-wheel live GUI/MCP operation, retained application and three-stage installer screenshot artifacts, pytest commands, and source-gate coverage artifacts used by CI."
+ "sha256": "b2bcf966b13447f4f584dd27ea250d8c0adbf41faed37b61c5a1a30aec2d7aed",
+ "role": "Defines the operating-system integration matrix, the Python 3.14 core job, the scoped four-platform Official30 parity lane and its required check, installed-wheel GUI/MCP operation, installer screenshot artifacts, and source-gate coverage artifacts used by CI."
},
{
"path": "scripts/resolve_successful_job_baseline.py",
- "sha256": "8e6541a53dac376176a63cb449e9166a425ef673954847cccd96fe444d60ad48",
+ "sha256": "be32836149414a8edf9ea73e1316907847376c86156b758b786b21fbd96a3283",
"role": "Resolves the cumulative changed-Python boundary from the latest successful code-quality job on an ancestor workflow run."
},
{
@@ -2544,7 +2823,7 @@
},
{
"path": "tests/installer/test_windows_simple_installer.py",
- "sha256": "3ab5b37fb4deee277f9deee41dc61deab5235e245e5ff37c98f32b06fde65697",
+ "sha256": "c83a5de13f423f7fd83624cabe38dec2901cb4ccfdc367cbca8e7c0460cbe6e7",
"role": "Proves the Windows native wizard waits for rendered controls, synchronizes window composition, and retains each declared installer evidence state."
},
{
@@ -2564,8 +2843,8 @@
},
{
"path": ".github/workflows/website-pages.yml",
- "sha256": "94fd5634ace8d7f138446a120acb49de27d0ea7230b09b1e958ef8b8fcd85b37",
- "role": "Validates gallery generation against metadata-resolved source candidates before deploying the checked-in dependency-free website projection."
+ "sha256": "d4fdfdf27f02a9c9cab67fa876f7a06844fa8b7298acce0e25a352682a34925f",
+ "role": "Installs the OpenHCS candidate from submodule sources with install_ci_candidate before validating gallery generation, then deploys the checked-in dependency-free website projection."
},
{
"path": "tests/unit/test_ci_action_pinning.py",
@@ -2577,6 +2856,11 @@
"sha256": "840eb6612bb41758979104cab1165d7e2297011fc6ea907ab67b9112f349b3e2",
"role": "Builds the metadata-discovered first-party source wheelhouse or installs the hash-pinned public dependency projection selected by each CI evidence lane."
},
+ {
+ "path": "scripts/run_installed_tests.py",
+ "sha256": "db4991c7d7fa1cb1cfd5918525f26b42fce799fb4f8136d8740001a9e8ac6b56",
+ "role": "Runs the checkout's tests with checkout source paths removed and fails when OpenHCS would import from the checkout instead of the installed wheel."
+ },
{
"path": "tests/unit/test_install_ci_candidate.py",
"sha256": "43b636293b30767c8040d7da77d440090089d097df75dfcf0649efc07fcddcb0",
@@ -2594,43 +2878,43 @@
},
{
"path": "tests/unit/test_ci_package_boundaries.py",
- "sha256": "691efcdafe66255c55ae11411b441930bdb58161cd74e2580c2531a486863303",
+ "sha256": "df85419ab722292f54a520bf61bd1a11ecc08b7637ffa2abdf70a16f16088e04",
"role": "Proves source-gate coverage fails closed, website validation uses local candidate owners, and cross-platform candidate and published GUI canaries retain their typed screenshot evidence."
},
{
"path": "scripts/smoke_installed_desktop.py",
- "sha256": "f745f93e245e5c1b02f6e0ece3a4a0e2af086c3cab77282e3907a167f5ce0a7f",
+ "sha256": "d5ce5e2a2b3590be2101b487b2957b270f386dcf47b4c197f1ce4c2ab6aa9d65",
"role": "Runs installed GUI readiness and screenshot evidence, launch, MCP/runtime, viewer, and packaged restart-worker acceptance outside the source checkout while deriving the outer GUI process ceiling from the probe's timing authority."
},
{
"path": "tests/installer/test_installed_desktop_smoke.py",
- "sha256": "5ac9f0e9e0e2ffe73cbab940da132421424e355c354b7476a8a072090c5aad00",
+ "sha256": "bf6fa5db1ff253c4ee3df16b3d46bdc64e87d54171770287baeed3516d44bb05",
"role": "Proves platform smoke orchestration passes the exact installed Python, isolated runtime, evidence directory, and declaration-derived GUI timing to the installed verifiers."
},
{
"path": "scripts/smoke_installed_gui.py",
- "sha256": "a5af7f55eab45b7ab43772a9ff8cf758e4940c5ad58857611cc51dbe6004bd89",
+ "sha256": "c6cca9ad8a248e63949c3556562ec4760d6aaeeaba215bb21ef556c8a8cb726d",
"role": "Owns the installed-GUI acceptance timing and single terminal completion, applies the declared logging configuration, constructs the application on declaration-allocated endpoints, proves the background function catalogue and advertised endpoint log are usable, drives the GUI through packaged MCP, validates retained window snapshots, and cleans up exact owned resources outside the checkout."
},
{
"path": "tests/unit/test_smoke_installed_gui.py",
- "sha256": "b8d1a1fcd67c58e2b2749262b44c1dcccbb2e72a0d299d180823e94a0285ae9d",
+ "sha256": "2178b0b6cfcae83dc08574573e1b021b63e2390436fffb73cafea969ad415393",
"role": "Proves source-checkout rejection, one derived smoke timing policy, deadline-owner error preservation under late cancellation, declaration-owned endpoint allocation, function-catalogue readiness, advertised execution-log validation, snapshot CLI projection, and emitted PNG identity validation for the installed GUI/MCP probe."
},
{
"path": "openhcs/pyqt_gui/config.py",
- "sha256": "a3eece2e5b2926fc6fc0edd197221da451ab7c0d0d6f4ffc8bb2ff865ad74bd4",
+ "sha256": "d092cb20206127484fbbc80a38a5152462b9b8991e848a814c85f564d51f4db8",
"role": "Declares UI configuration, including logging, theming, viewer, and execution-server settings."
},
{
"path": "openhcs/core/xdg_paths.py",
- "sha256": "c0ecf7576243107dbd1a76fd74f1162ab31cb98634e458294b0210cf88774513",
+ "sha256": "f7aa72330de80a1a4dce33a56f3f9d2cdb57a4257c03db4e3e641950fe146d3b",
"role": "Resolves OpenHCS data, cache, state, and log locations from the platform and XDG environment."
},
{
"path": "tests/integration/conftest.py",
- "sha256": "7397b869b835c535a8707412e35f5f4b25aa92eec5b7fe9300c83dbce0bc987a",
- "role": "Composes the integration parameter axes and parametrises collected integration cases."
+ "sha256": "d618490a1a01c26190e264e311e188b4c21ffbbaeb42b8173c9948602052d664",
+ "role": "Composes the integration parameter axes, parametrises collected integration cases, and allocates per-test endpoint ports."
},
{
"path": ".vscode/settings.json",
@@ -2648,31 +2932,18 @@
"role": "Owns the table-service readiness contract required before OMERO storage tests proceed."
}
],
- "findings": [
- "Failure guidance named an unsupported ~/.local/state log tree and implied Unix-only paths; it now follows the configured log owner and launch-reported paths.",
- "The guide attributed integration composition to the root conftest instead of tests/integration/conftest.py and omitted the authority for the VS Code OMERO argument.",
- "The Windows installer section now records the dual-lock staged-update smoke and the subsequent deferred launcher refresh.",
- "The CI section omitted the maintained GUI source gate and described live Napari/Fiji validation as only local or ad hoc despite the dedicated viewer smoke.",
- "The changed-Python quality gate could forget a failed file on the next push; the guide now records the cumulative successful-job ancestor boundary.",
- "The combined report omitted the foundational unit/core and maintained GUI source gates, and its description implied coverage from uninstrumented jobs; both source gates now emit fail-closed artifacts and the guide names the report's exact evidence boundary.",
- "The native installer section did not record that each supported Windows/macOS architecture now exercises the packaged restart worker against a real parent exit and detached command.",
- "The generated coverage README rendered its update command literally instead of recording when the report was produced; the workflow now appends an ISO 8601 UTC timestamp.",
- "Native installer acceptance did not exercise hostile workstation pip index configuration; Windows installation and reinstall, macOS installation, and the staged updater now run while an unreachable index is injected.",
- "The release matrix proved dependency resolution and importability but did not construct the installed GUI, and no cross-platform canary re-resolved a published release after publication; both boundaries now exercise painted application readiness.",
- "The OMERO failure guide did not describe the cold repository race exposed by Python 3.12 CI or the boundary that prevents OpenHCS from restarting externally managed components.",
- "The installed-GUI probe reused the default execution endpoint, allowing an existing desktop server to influence an otherwise isolated package canary; it now allocates the endpoint through the selected transport declaration.",
- "Installed GUI and MCP package checks terminated before either operated the other; the source-wheel, publication, native-installer, and scheduled canaries now keep the packaged GUI live while the packaged desktop MCP discovers it and invokes a declared UI action.",
- "The fixed desktop candidate was operated before publication only on Linux, while Windows and macOS evidence remained behind the unpublished-dependency gate; a complementary source-candidate matrix now proves Windows and both macOS architectures alongside the Linux wheel lane.",
- "The native installer source job still depended on public first-party release tags and therefore skipped the real Windows and macOS installer paths while dependency candidates were unpublished; it now builds the complete metadata-discovered source wheelhouse independently, while the PyPI installation lane remains publication-gated.",
- "Cross-platform GUI canaries proved readiness and MCP actions but discarded the rendered UI, so native theming regressions had no retained evidence; candidate and published-package jobs now upload typed, hash-verified window snapshots.",
- "The installed-GUI canary bypassed the production logging setup and therefore could pass while its execution server advertised an empty log; the probe now applies LoggingConfig and verifies readable endpoint output.",
- "Native installer tests exercised worker mode without proving that the visible applications could start or drive installation; each supported lane now activates the shipping wizard and retains its welcome, progress, and completion states.",
- "The Windows installer evidence harness captured its welcome frame when only the top-level handle existed, before child controls painted; capture now requires the real primary control and synchronizes recursive rendering with desktop composition.",
- "Website validation attempted to install an unpublished first-party dependency from PyPI; it now resolves the required source candidates from the recursively recorded submodules while deployment continues to consume the dependency-free checked-in projection.",
- "Integration and Documentation workflows retained superseded same-ref matrices even though only exact-head evidence can support release readiness; both now declare event-and-ref concurrency."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), .github/workflows/website-pages.yml changed after docs/source/guides/testing_guide.rst was reviewed: the Website workflow's concurrency group is per ref; no claim changes.",
+ "On 2026-10-11 (B6 final), scripts/smoke_installed_gui.py changed after docs/source/guides/testing_guide.rst was reviewed: the GUI smoke compares the action status to ActionAccepted; no claim changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/testing_guide.rst against .github/workflows/integration-tests.yml, scripts/smoke_installed_gui.py: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/testing_guide.rst against .github/workflows/integration-tests.yml: the unit-tests job now runs three file-isolated xdist workers; no claim this page makes changes.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/testing_guide.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/config.py; the diff renames AgentRuntimePlatformAuthority to AgentRuntimePlatform and adds GuiLogLevel.emits_records; LoggingConfig and its default log directory, which the debugging section relies on, are unchanged, so no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/testing_guide.rst in full and compared it against the changed authority openhcs/core/xdg_paths.py (legacy ~/.openhcs migration helpers deleted; get_*_file_path now plain path joins); the page's log-location claim (get_openhcs_data_dir() / \"logs\", honouring XDG_DATA_HOME, default ~/.local/share/openhcs/logs) still holds and the page never described the deleted migration, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/testing_guide.rst against B6's later changes to .github/workflows/integration-tests.yml: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/testing_guide.rst against the later B6 changes to scripts/smoke_installed_gui.py, tests/unit/test_smoke_installed_gui.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/testing_guide.rst in full and compared it against the changed authorities: pytest.ini, integration-tests.yml, resolve_successful_job_baseline.py, test_windows_simple_installer.py, website-pages.yml, test_ci_package_boundaries.py, smoke_installed_desktop.py, test_installed_desktop_smoke.py, smoke_installed_gui.py, xdg_paths.py, tests/integration/conftest.py; confirmed the default discovery options, --it-* flags, artifact names, concurrency, actionlint, code-action probe and log-directory paths, and added the scoped Official30 parity lane and the installed-wheel test runner.",
"Re-read docs/source/guides/testing_guide.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: pytest.ini; tests/pytest_integration_options.py; .github/workflows/integration-tests.yml; scripts/install_ci_candidate.py; .github/workflows/coverage-pages.yml; tests/unit/test_ci_package_boundaries.py; scripts/smoke_installed_desktop.py; tests/installer/test_installed_desktop_smoke.py; openhcs/pyqt_gui/config.py; openhcs/core/xdg_paths.py; tests/integration/conftest.py; .vscode/settings.json.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -2739,7 +3010,7 @@
},
{
"path": "docs/source/guides/viewer_management.rst",
- "source_sha256": "cf6b38318a5464e6b0d30394f7a5c3bed5b3ca8af1a336486a9d4962bc104996",
+ "source_sha256": "d9187f4ee21b5aa5beacd73f2cb372fe314a420d0e791a9a3546c16defdd5000",
"audience": [
"viewer integrators",
"pipeline authors"
@@ -2747,10 +3018,15 @@
"user_need": "Understand viewer lifecycle, ROI ownership, settlement, and artefact boundaries.",
"diataxis": "explanation",
"authority": [
+ {
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares StreamingConfig, NapariStreamingConfig, FijiStreamingConfig and StepMaterializationConfig, including the partition filter inherited by streaming configuration."
+ },
{
"path": "openhcs/runtime/napari_viewer_server.py",
- "sha256": "64ccfef65a10e04878ef0806ae72d0446540efcbfc72af46c4e2ab785418a84c",
- "role": "Implements Napari stream reception, layer updates, ROI navigation, and endpoint lifecycle."
+ "sha256": "ac54f8bfdce33150c31b38bcd5cf6069b5fd66fb7c05d5a6951610ec3d895bfd",
+ "role": "Mounts the ROI Manager, owns the OpenHCS ROI selection toolbar and queued-route settlement, and applies layer isolation."
},
{
"path": "openhcs/napari_roi_manager/__init__.py",
@@ -2759,54 +3035,73 @@
},
{
"path": "openhcs/runtime/viewer_protocol.py",
- "sha256": "30441eab97da9ffcb5b9dc33d65c2ecc56c426e79bb2b27eedc78a68aaf13bc3",
- "role": "Declares the transport-independent viewer messages, stream configuration, and server contract."
+ "sha256": "347a65b63c149f2f437014d537cfaf481a33801d860742f4083b7377494e792f",
+ "role": "Declares the SHAPES streaming data type and the settle progress fields."
+ },
+ {
+ "path": "openhcs/runtime/viewer_control_actions.py",
+ "sha256": "f19c8370dd9145f11ba7ceb91d2efff123a6473db9bc551474a47a37acb4041d",
+ "role": "Answers settle requests on the control transport thread without waiting for Qt."
},
{
"path": "openhcs/runtime/viewer_controls.py",
- "sha256": "3227e6bf60e9b43fd792cc4299b6b50721741f012a0f7f856268b35022fd6062",
- "role": "Declares caller payload controls, response-wide shape projection policy, and one atomic layer-isolation request."
+ "sha256": "df0ac0aee66ba52a8b1706a1a99e03acc1300cfaf5cc74c2f93614a1c77465f6",
+ "role": "Declares caller payload controls, response-wide shape projection policy, and the atomic layer-isolation request."
},
{
"path": "openhcs/agent/services/viewer_window_service.py",
- "sha256": "257718ca7da768132be2283326730f65d9b399f0ef852b2c7e1e9e48263f9eed",
- "role": "Projects bounded ROI metadata and one bulk isolation response into viewer-agent results."
+ "sha256": "cfe222586a221cb317099fc245216778f2ce15589604e5ce03410998dec4bb1a",
+ "role": "Projects bounded ROI metadata under one max_rois budget and one bulk isolation response into viewer-agent results."
},
{
- "path": "openhcs/napari.yaml",
- "sha256": "48001da52bd77a2a436b021676c7d68a994b0d2a708d7ac6647f1fe0c8d8204a",
- "role": "Declares the Napari plugin contribution and dock-widget entry point installed with OpenHCS."
+ "path": "openhcs/agent/authoring_contexts.py",
+ "sha256": "a714fab8481102a5ab7800a465fcbdf7267d1ed4468cb3803b75e03f2f29ff33",
+ "role": "Targets the openhcs_viewer_qa knowledge document for viewer review."
},
{
- "path": "tests/unit/test_napari_roi_manager_integration.py",
- "sha256": "de66b3d7aeb49b100ac5bb3c3acdf1c14afc19fb3681071b4356f6d7949a52dc",
- "role": "Proves the packaged Napari ROI manager is discovered and integrated with the viewer contract."
+ "path": "packaging/codex/openhcs/skills/use-openhcs/references/viewer-qa.md",
+ "sha256": "6aff1f00a2cfb11a658850831c511298010339acba99c080cabed437c544e28b",
+ "role": "Bundles the viewer QA selective-retirement procedure the page defers to."
},
{
- "path": "tests/unit/test_napari_streaming_handlers.py",
- "sha256": "a338d6f2ef3094fa429f3d640709c7231eb741a7bf2859318bf4bbfa196afc07",
- "role": "Proves Napari streaming updates layers and carries ROI navigation through the declared protocol."
+ "path": "openhcs/processing/materialization/options.py",
+ "sha256": "e7487e9ad44276d4f2880f0d9b03fd6396f8053b9bda5775ab25caaf023554ca",
+ "role": "Declares SWCOptions and SpatialGraphROIOptions."
},
{
- "path": "tests/unit/test_viewer_result_element_selection.py",
- "sha256": "58480f4da61b427ec1c55560a77686e74df70404fbbf77e701ba386ad9f89a03",
- "role": "Proves isolation preflights every requested route and selected-route axis before changing live Napari state."
+ "path": "openhcs/core/artifacts/kinds.py",
+ "sha256": "216b913efad9ec76707e9f61be445fb6fa1c6601c36ef53e296acd4928604515",
+ "role": "Declares SpatialGraphArtifactType."
+ },
+ {
+ "path": "openhcs/napari.yaml",
+ "sha256": "a42e9d6288fd43d0855154e415eee0b998d120934754759c480e529c30bacafe",
+ "role": "Declares the ROI Manager widget and SWC reader contributed under Plugins > OpenHCS."
},
{
"path": "openhcs/runtime/napari_streaming_handlers.py",
- "sha256": "86f4ab319c7c68b626e7e2a1adef81eb3d7244f044b5b3e4dddd78bbee2aa4b6",
+ "sha256": "b43f29163cef420c77f30e46e92d94145520f7534077d54636a0ab141da32e7e",
"role": "Owns semantic display-axis alignment for images, points, shapes, translations, and exact-slice navigation."
+ },
+ {
+ "path": "tests/unit/test_napari_roi_manager_integration.py",
+ "sha256": "15b0417fc6388f932ac6cb8b35102721918b987b68e52caef04b1c6bc5ea9e81",
+ "role": "Proves the packaged Napari ROI manager is discovered and integrated with the viewer contract."
+ },
+ {
+ "path": "tests/unit/test_viewer_result_element_selection.py",
+ "sha256": "b1dae580f0291f53be9453df6f6c6df5458046bd3ff947b550f7ddea94b29f96",
+ "role": "Proves isolation preflights every requested route and selected-route axis before changing live Napari state."
}
],
- "findings": [
- "The viewer explanation did not state that every route shares the display declaration's axis slots or that reduced components occupy singleton slots.",
- "The viewer explanation did not state that the selected OpenHCS route owns dimension-label presentation or that non-OpenHCS selection clears stale route state.",
- "The persistence explanation treated execution-server teardown as owning persistent viewers, which contradicted their post-run reuse and inspection contract.",
- "The explanation did not distinguish bounded metadata-only ROI summaries from explicit raw geometry inspection.",
- "The explanation did not state that visibility, selection, and selected-route navigation are validated and applied as one viewer operation."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/guides/viewer_management.rst in full and compared it against the changed authorities: openhcs/core/config.py, openhcs/runtime/napari_viewer_server.py, openhcs/runtime/viewer_protocol.py, openhcs/runtime/viewer_control_actions.py, openhcs/processing/materialization/options.py, openhcs/runtime/napari_streaming_handlers.py, tests/unit/test_napari_roi_manager_integration.py and the deleted openhcs/core/artifacts.py; K2 split openhcs/core/artifacts.py into a package, so the SpatialGraphArtifactType authority is now openhcs/core/artifacts/kinds.py; the other diffs turn settle phases, Napari layer kinds, variable-size handling, dimension-label route sources and filename identities into subclass families without changing the streaming configs, SHAPES boundary, settle progress, SWCOptions or SpatialGraphROIOptions the page describes, so the prose needed no correction.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/viewer_management.rst in full and compared it against the changed authority openhcs/core/config.py (PartitionFilterMode became an IncludePartitions/ExcludePartitions subclass family, K4 moved SourceBindingsConfig/StepSourceBindingsConfig into config.py); StreamingConfig, NapariStreamingConfig, FijiStreamingConfig and StepMaterializationConfig still exist with partition selection inherited by streaming, and the page names no partition-mode member, so no correction was needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/viewer_management.rst against B6's later changes to openhcs/core/config.py, openhcs/runtime/viewer_protocol.py, openhcs/core/artifacts.py, tests/unit/test_napari_roi_manager_integration.py, tests/unit/test_viewer_result_element_selection.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/guides/viewer_management.rst against the later B6 changes to openhcs/core/config.py, openhcs/runtime/viewer_protocol.py, openhcs/core/artifacts.py, tests/unit/test_napari_roi_manager_integration.py, tests/unit/test_viewer_result_element_selection.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/guides/viewer_management.rst in full and compared it against the changed authorities: napari_viewer_server.py, viewer_protocol.py, viewer_controls.py, viewer_window_service.py, napari.yaml, the ROI manager, streaming handler and element-selection tests, and napari_streaming_handlers.py; confirmed the streaming and materialization config classes, SHAPES boundary, ROI toolbar labels, plugin menu entries, license file, spatial-graph options, max_rois, layer isolation and transport-thread settle, renamed well selection to partition selection, and replaced 'custody'.",
"Reviewed docs/source/guides/viewer_management.rst in full; the recorded findings and disposition are the resulting page-specific editorial decision.",
"Compared its factual claims directly with the hash-pinned files listed in authority: openhcs/runtime/napari_viewer_server.py; openhcs/napari_roi_manager/__init__.py; openhcs/runtime/viewer_protocol.py; openhcs/napari.yaml; tests/unit/test_napari_roi_manager_integration.py; tests/unit/test_napari_streaming_handlers.py.",
"Passed `.venv/bin/python scripts/validate_docs.py docs/source/guides`: documentation validation passed: 9 files, 2 Python blocks.",
@@ -2833,33 +3128,39 @@
"authority": [
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
- "role": "Declares the public pipeline document, step collection, and serialisable declaration boundary."
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
+ "role": "Declares PipelineDocument and PipelineDocumentCodec, the one pipeline config-plus-steps document shared by the GUI, Python and agents."
},
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
"role": "Composes the main desktop window, menus, task windows, and registered UI services."
},
{
"path": "openhcs/interop/cellprofiler/pipeline_import.py",
- "sha256": "b547c838b2c86fe7cc5fe12d6cbf6a6715e83055c2d8c1a109ac22566d22c51e",
+ "sha256": "5aa8895c9f533603b7ef972202141e2439473f0604b97d66f5a027b8b3cd1c83",
"role": "Imports CellProfiler modules into ordinary OpenHCS pipeline declarations through the interop boundary."
},
{
"path": "openhcs/mcp/bootstrap.py",
- "sha256": "64b2ef9a4f4fe50154b4c921a7617b2e7c9dcf8bb475df7423e37e8395a8b250",
+ "sha256": "0e8e94cf54a32a1772e52a77d1c89a9ee02c6d2a58eb04f6e18fad2ce79d0394",
"role": "Builds the installed MCP server, registers its tools and resources, and starts the selected transport."
},
{
"path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
- "role": "Owns the landing-page workspace scenario, representative image, caption, description, alt text, dimensions, and release evidence."
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
+ "role": "Owns the multi-plate-overview landing-page scenario, representative image, caption, description, alt text, dimensions, and release evidence."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/index.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/index.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py and openhcs/interop/cellprofiler/pipeline_import.py; both diffs are enum-to-family renames plus the artifacts-package import paths; the landing page routes readers and names no changed symbol, so no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/index.rst in full and compared it against the changed authorities: pipeline_document.py (render now via pycodify.generate_python_source, L6; PipelineDocument/Codec unchanged), pyqt_gui/main.py (dock layout, shortcuts and time-travel navigation moved to pyqt-reactive, U3), cellprofiler/pipeline_import.py (SourceBindingsConfig now imported from openhcs.core.config, K4); the landing page names no module, class or window behaviour these touched, and still routes to existing pages; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/index.rst against B6's later changes to openhcs/pyqt_gui/main.py, openhcs/interop/cellprofiler/pipeline_import.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/index.rst against the later B6 changes to openhcs/pyqt_gui/main.py, openhcs/interop/cellprofiler/pipeline_import.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/index.rst in full and compared it against the changed authorities: pipeline_document.py (PipelineDocumentAuthority renamed PipelineDocumentCodec), pyqt_gui/main.py, cellprofiler pipeline_import.py, mcp/bootstrap.py, gallery_catalog.py; the page names none of the renamed symbols, the multi-plate-overview scenario still exists, and the routing claims and toctree remain accurate; no correction was needed.",
"On 2026-08-27, re-read docs/source/index.rst in full against the explicit CellProfiler import-validation hook; challenged its Diataxis routing and supported-import overview and found no prose drift.",
"Compared its factual claims directly with the hash-pinned files listed in authority: openhcs/core/pipeline_document.py; openhcs/pyqt_gui/main.py; openhcs/interop/cellprofiler/pipeline_import.py; openhcs/mcp/bootstrap.py.",
"Passed `.venv/bin/python scripts/validate_docs.py docs/source/index.rst`: documentation validation passed: 1 files, 0 Python blocks.",
@@ -2875,7 +3176,7 @@
},
{
"path": "docs/source/installation.rst",
- "source_sha256": "05f5519d47c42adea4d67ef1e70d0eebab7819f0ea4fe66b293a5c78eb34f9ca",
+ "source_sha256": "67df29d4cb62e1bbef72a1a91d535c83fcd0a32f7f83c808f89cc8a857c09f1c",
"audience": [
"returning users following an old installation link"
],
@@ -2884,30 +3185,34 @@
"authority": [
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
- "role": "Declares the package version, Python requirement, dependency extras, scripts, and distribution metadata."
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
+ "role": "Declares the package version, Python requirement, dependency extras, scripts, and distribution metadata the redirected guide covers."
},
{
"path": "openhcs/cli.py",
- "sha256": "51c2570a5a49c83e826c3a2e85943a2c39593e5e30e1ce8af396ec4a4c724df1",
- "role": "Declares the installed OpenHCS command entry points and their launch routing."
+ "sha256": "59025ea51309dbae40416d75fc203287657a109379f65072d77cff41c712e11e",
+ "role": "Declares the installed OpenHCS command entry points, including the skills subcommand, and their launch routing."
},
{
"path": "packaging/installers/windows/Install-OpenHCS.ps1",
- "sha256": "f94eb1c0460cab7dca770a600d889bf55ae98f1aa652b2755de0ecc906ee83d8",
- "role": "Implements the Windows installer, short managed paths, package installation, and shortcut creation."
+ "sha256": "ca368082f3d7a414f9e9136b2c5ae459bfb2a9a2a37c096d06eeb93d5c403358",
+ "role": "Implements the Windows installer, short managed paths, package installation, skill sync, and shortcut creation."
},
{
"path": "packaging/installers/macos/install-openhcs.sh",
- "sha256": "b78b6b5849265926f83f77f392f7f4ac74ce5968f62e0b6972c6ce7976498203",
- "role": "Implements the macOS application installer, managed environment creation, package installation, and launcher setup."
+ "sha256": "3a0f43c57c14bd7f8ceeb38464e174728f903e0ec524d980dbdb2b5f9bc11544",
+ "role": "Implements the macOS application installer, managed environment creation, package installation, skill sync, and launcher setup."
}
],
"findings": [
- "Maintaining installation instructions here would duplicate package extras and installer behaviour; the page correctly redirects to the canonical task."
+ "The page is a redirect stub; its link target still exists and owns the content (checked 2026-10-11, B6)."
],
"disposition": "redirect",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/installation.rst in full and compared it against the changed authorities: pyproject.toml; the python-introspect floor moved to 0.2.8; the page states no dependency fact and docs/source/getting_started/getting_started.rst still exists, so no correction was needed.",
+ "On 2026-10-11 (B6), re-read docs/source/installation.rst in full and compared it against the changed authorities: pyproject.toml (zmqruntime, pycodify, objectstate, python-introspect and pyqt-reactive floor changes after U3/K4/K5/K6/L4/L6); the page states no version or extra and still redirects to getting_started; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/installation.rst against the later B6 changes to pyproject.toml: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/installation.rst in full and compared it against the changed authorities: pyproject.toml (dependency floors, JAX extra), openhcs/cli.py (skills subcommand), the Windows and macOS installers (skill sync); the page copies none of those facts and still redirects to getting_started; replaced 'canonical' with 'maintained'.",
"On 2026-08-27, re-read docs/source/installation.rst in full after the python-introspect dependency floor advanced to 0.1.12; confirmed that its compatibility redirect still reaches the canonical installation procedure.",
"On 2026-08-26, re-read the complete compatibility page after native installer transcript and evidence handling changed; retaining one redirect to the canonical installation how-to remains accurate and avoids duplicated guidance.",
"Compared every factual claim directly with the current hash-pinned authorities: pyproject.toml; openhcs/cli.py; packaging/installers/windows/Install-OpenHCS.ps1; packaging/installers/macos/install-openhcs.sh.",
@@ -2951,45 +3256,55 @@
"authority": [
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares GlobalPipelineConfig, PipelineConfig and the step configuration families with their defaults and field help."
},
{
"path": "openhcs/pyqt_gui/config.py",
- "sha256": "a3eece2e5b2926fc6fc0edd197221da451ab7c0d0d6f4ffc8bb2ff865ad74bd4",
- "role": "Declares UI configuration, including logging, theming, viewer, and execution-server settings."
+ "sha256": "d092cb20206127484fbbc80a38a5152462b9b8991e848a814c85f564d51f4db8",
+ "role": "Declares UIConfig with list previews, logging, shortcuts, progress, execution transport and agent-bridge settings."
+ },
+ {
+ "path": "openhcs/agent/services/config_service.py",
+ "sha256": "27a1d5d841b147e3ce35c3a358ca30b6d6efe6ed08a7db874648410b122a717f",
+ "role": "Resolves the global, pipeline, step and ui directive names to configuration declarations."
},
{
"path": "openhcs/agent/services/config_reference_service.py",
"sha256": "18b740aad750284c0aeaa4c51e81a31828c8e51e609f4997b6a1a50cabfb4e32",
- "role": "Builds configuration reference records from live dataclass fields, types, defaults, and help metadata."
+ "role": "Renders configuration reference records from live dataclass fields, types, defaults, and help metadata."
},
{
- "path": "docs/source/_ext/openhcs_config_reference.py",
- "sha256": "a45ccbd142745b0e636ea815134d6154c669f84b06a2d087fd76f993ee06aca3",
- "role": "Generates the Sphinx configuration reference from the live dataclass declarations instead of a copied field table."
+ "path": "openhcs/agent/capabilities.py",
+ "sha256": "87c8e2e5aeb425b9a2eb690d48bdd6f5af7a327c389deac261d7e4e0da5d609f",
+ "role": "Declares openhcs_describe_config_schema, the agent view of the same introspection."
+ },
+ {
+ "path": "openhcs/agent/dto/config.py",
+ "sha256": "b85b56d41b09feb826daf09d12427a1a78fb2d03705c4cb1177629e1711587dd",
+ "role": "Declares ConfigPatch, which the page excludes from UI configuration edits."
},
{
"path": "tests/unit/agent/test_config_reference_service.py",
- "sha256": "de18fb8ff76efe2bdbc46a3f46137622fe53fe6a2a28449d34728f4d04a32a1d",
- "role": "Proves that configuration reference output is derived from live declarations and field metadata."
+ "sha256": "43521f7fbad821905bd59fb231f8d96c4efcfa99d7a27b5655a70e9507199e01",
+ "role": "Proves that configuration reference output and inherited field help are derived from live declarations."
},
{
"path": "tests/unit/pyqt_gui/test_user_facing_help_surfaces.py",
- "sha256": "540d0fc2d796ff80543a7ef245d2450c1ecf988994e244c13472f9756cd904e1",
+ "sha256": "c7bfe985e0cf08cdab7de7f33aed443e697cbf7e181fbdc3ef4bb177c17ec897",
"role": "Proves user-facing field help is sourced from declaration metadata rather than copied UI text."
- },
- {
- "path": "external/pyqt-reactive/src/pyqt_reactive/strategies/preview_formatting.py",
- "sha256": "dc1a423246ca40f13633847d5d3ffde3916e53c1a8a8a92de893d3e80ad8fda7",
- "role": "Owns preview detail, collection bounds, and wrapping declarations."
}
],
"findings": [
- "Identified list-preview presentation in the UIConfig overview while retaining generated field reference as authority."
+ "Code issue, not edited: for fields whose value is a registered family class (vfs_config.materialization_backend, processing_config.input_source, napari variable_size_handling, partition filter mode), the generated reference prints the default as a Python class repr such as instead of the accepted spelling (disk). _default_value_repr in openhcs/agent/services/config_service.py has no branch for a registered class default; the same default_repr reaches openhcs_describe_config_schema. Accepted values render correctly."
],
- "disposition": "revise",
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/reference/configuration.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1: MaterializationBackend became a registered subclass family, MaterializeToDisk default; input_source holds an InputSource class, ReadPreviousStep default; K2 artifacts import), openhcs/pyqt_gui/config.py (AgentRuntimePlatform rename, GuiLogLevel.emits_records) and openhcs/agent/services/config_service.py (patch values coerced to registered family classes by key); the page copies no field, type or default; rendered the global root through ConfigReferenceRstRenderer and confirmed the family fields list their accepted values (auto/zarr/disk/omero_local, previous/start, separate_layers/pad_to_max) and that pipeline, step and ui roots still render; the rendered default of those fields is a class repr, a code defect recorded in findings; no page edit.",
+ "On 2026-10-11 (B6), re-read docs/source/reference/configuration.rst in full and compared it against the changed authority openhcs/core/config.py (PartitionFilterMode became a registered subclass family with RegisteredClassChoices, SourceBindingsConfig/StepSourceBindingsConfig declared in config.py, K4); the page copies no field, type or default and renders them through the openhcs-config-reference directive from live declarations, so the new family spellings appear automatically; confirmed UIConfig still holds list_previews, logging, shortcuts, progress, zmq transport and agent_bridge, openhcs_describe_config_schema and ConfigPatch still exist; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/reference/configuration.rst against B6's later changes to openhcs/core/config.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/reference/configuration.rst against the later B6 changes to openhcs/core/config.py, openhcs/agent/capabilities.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/reference/configuration.rst in full and compared it against the changed authorities: core/config.py, test_config_reference_service.py, test_user_facing_help_surfaces.py; the four directive names still resolve through config_service aliases, the named config classes, openhcs_describe_config_schema and ConfigPatch exist, and the UIConfig overview matches its fields; no correction was needed. The generated field tables carry the G1/G4 renames without page edits.",
"Re-read docs/source/reference/configuration.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/config.py; openhcs/pyqt_gui/config.py; openhcs/agent/services/config_reference_service.py; docs/source/_ext/openhcs_config_reference.py; tests/unit/agent/test_config_reference_service.py; tests/unit/pyqt_gui/test_user_facing_help_surfaces.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -3013,58 +3328,58 @@
"diataxis": "reference",
"authority": [
{
- "path": "openhcs/core/aligned_image_payload.py",
- "sha256": "086a6130abf3e0c759127aeacf4140157aee3b4bdc306c8d02067abce102acd0",
- "role": "Declares the runtime image payload execution modes used to preserve complete stacks."
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "Declares the ProcessingContract family: Pure2D, Pure3D, Flexible, and VolumetricToSlice contracts."
},
{
- "path": "openhcs/core/runtime_object_label_domains.py",
- "sha256": "32824678741c1caf3fbabe621619d6d8d463936a28bc2d689916ed69fa7582ed",
- "role": "Declares payload-scoped and plane-scoped object identity domains."
+ "path": "openhcs/core/image_payload_execution_mode.py",
+ "sha256": "7657995da0722cebfd0448c0cdce4cada1b00e0cff419f62e17dcc3654666ce0",
+ "role": "Declares the image payload execution modes, including FullStackExecution, that preserve a nested volume under an outer plane axis."
},
{
- "path": "openhcs/processing/backends/lib_registry/unified_registry.py",
- "sha256": "70462cddcad1bfee447ce0786a9e4a85abbd297726d59a1208156af0ef3a3743",
- "role": "Defines canonical callable metadata and derives the unified catalogue from registry declarations."
+ "path": "openhcs/core/runtime_object_label_domains.py",
+ "sha256": "cb56a2b3a391cfd5081e1487a04e2e95b88628879abc42a2ec3b69ab327ab0cf",
+ "role": "Declares the PAYLOAD and PLANE object-label domain scopes."
},
{
"path": "openhcs/processing/backends/cellprofiler/watershed.py",
- "sha256": "9bd009be9881a88e295e1c5ca6fbc02b4402bc97d207cfd20848ff685fe64bde",
+ "sha256": "7fb797d16fc740c381d7dc9add3a0ff79ed2d82515850597c7995857bdacc206",
"role": "Declares the full-stack payload route for CellProfiler-compatible Watershed."
},
{
"path": "openhcs/processing/backends/cellprofiler/primary_objects.py",
- "sha256": "6710b507fcdead2954e8a1038c91e2a99572d67b6d4ded8a7d41100fb229aa29",
+ "sha256": "4d28a02d5c25e4700df1affdbf414e587cde7b7d348917ee29e862e5135466cc",
"role": "Declares the current plane-local IdentifyPrimaryObjects route."
},
{
"path": "openhcs/processing/backends/cellprofiler/secondary.py",
- "sha256": "9a032a11e51c3f950538f89f4e4312094496fd6408e0fdceebc7dc8b91be43de",
+ "sha256": "e84db4b2bfa56492801e24fc9471e09c4ada4daf5b3541463cf3424ed227af2b",
"role": "Declares the current plane-local IdentifySecondaryObjects and IdentifyTertiaryObjects routes."
},
{
"path": "openhcs/processing/backends/cellprofiler/intensity.py",
- "sha256": "646e837759d6504e496b09fd903744c95e020ae02d0a424bbe408cb772d8b2f0",
+ "sha256": "35ea2c6c82b1451d1aee3accdb2e22cec2125ee6b2f19cf6655167d9a709025e",
"role": "Declares volumetric object-intensity execution and its Z measurement features."
},
{
"path": "openhcs/processing/backends/cellprofiler/shape.py",
- "sha256": "42e4f72dba460e5f32a5d1e70e4ca792a8e648158e509274dcc37762cdd08718",
+ "sha256": "1281d4aaa6adf5d431bea696b750832ca3b44aed0832257e60b3debf1e265a1b",
"role": "Declares full-stack object-shape input and the exact 3D feature schema."
},
{
"path": "openhcs/processing/backends/cellprofiler/area_occupied.py",
- "sha256": "5d6112d86c5df1e068d36e4c08fd9cc59cb2e91f5226c2bc2a017e204d2d4009",
+ "sha256": "c3463d217d1db31e713e350a6d2000dc76ffe33c73587a187721af74e4ad696d",
"role": "Declares 3D occupied-volume measurements and physical voxel-spacing input."
},
{
"path": "benchmark/manifests/official30_portable_axis1.json",
- "sha256": "7f11549c0bc870f9ce2792afc9176f52b6a2b83236e47b33cbedb4a2cac73267",
+ "sha256": "1be9bb67ea05d78fd1e0b8991b994ff5635da2fff0358302b8e0df888e62888e",
"role": "Declares the Official30 3D monolayer case and its compatibility evidence category."
},
{
"path": "tests/unit/test_documented_dimensionality.py",
- "sha256": "83bef8dcad91750c9acda63b8a4293ccbd0904dd5aea7a32be197f9d51f300a7",
+ "sha256": "45762e74bf5cecbbada509f378ddbfa47c20fa6a9f5bd668d05d003e59bb38e1",
"role": "Binds the published dimensionality claims to the current callable and feature declarations."
},
{
@@ -3074,25 +3389,28 @@
},
{
"path": "tests/unit/test_cellprofiler_library_loading.py",
- "sha256": "c756d24a1cd9469505d94de5cabe4cdb3129e67c4e1bd6c66ad02588274a8e5c",
+ "sha256": "316732ba044a62b80711a0466d8ce5812cb882ff91fbf0417ecae90afa3ff610",
"role": "Proves object intensity measures a 3D object as one volume and emits Z features."
},
{
"path": "tests/unit/test_cellprofiler_module_execution.py",
- "sha256": "f5be1bcae3be455df60ae80623e6cc170830b1754b9b435294f5fc5e1b75fc43",
+ "sha256": "93ee832a1da999d499afde2c08341f8da4f88e098fb27e4d9f3758b91b236b6c",
"role": "Proves payload-scoped size-and-shape rows use the 3D volume schema."
},
{
"path": "tests/unit/test_cellprofiler_pipeline_import.py",
- "sha256": "353f0c1d8e0bc3552d2e418d792794ab0631daa16c78a3e10191b1cc1ea07d73",
+ "sha256": "67576f37b7cbe0595acea633265f8e14677ca3c1b8204630413330273dc4e58a",
"role": "Proves CellProfiler import preserves 3D image, object, and morphology variants from declarations."
}
],
- "findings": [
- "Dimensionality and feature breadth were previously spread across implementation and conceptual pages, so prospective users could not look up the current boundary without reading architecture internals."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/reference/dimensionality_and_measurements.rst in full and compared it against the changed authorities: cellprofiler/watershed.py (WatershedRuntimeFamily enum replaced by WatershedRuntimeStrategy subclasses), primary_objects.py, secondary.py, intensity.py and area_occupied.py (K2 artifacts package imports; replacement-primary relation declares source_artifact_type), shape.py (K3: label-indexed rows, radius and Feret zero for empty labels) and tests/unit/test_cellprofiler_module_execution.py and test_cellprofiler_pipeline_import.py (family class spellings); watershed_cellprofiler4 and watershed_library still run under FullStackExecution, the Identify* routes and the 3D shape, intensity and occupied-volume schemas are unchanged, and tests/unit/test_documented_dimensionality.py still imports the declarations it binds; no edit needed.",
+ "On 2026-10-11 (B6), re-read docs/source/reference/dimensionality_and_measurements.rst in full and compared it against the changed authorities: processing_contracts.py (CallableMetadata -> CallableDeclaration), cellprofiler/watershed.py (StepSourceBindingsConfig import from config.py), cellprofiler/secondary.py (tertiary repeated_return_slots), tests/unit/test_cellprofiler_module_execution.py and test_cellprofiler_pipeline_import.py (flattened CallableDeclaration fields, admitted_graph helper); the segmentation table, volumetric measurement fields and validation boundary remain accurate; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/reference/dimensionality_and_measurements.rst against B6's later changes to openhcs/core/processing_contracts.py, openhcs/core/image_payload_execution_mode.py, openhcs/processing/backends/cellprofiler/watershed.py, openhcs/processing/backends/cellprofiler/primary_objects.py, openhcs/processing/backends/cellprofiler/secondary.py, openhcs/processing/backends/cellprofiler/intensity.py, openhcs/processing/backends/cellprofiler/shape.py, openhcs/processing/backends/cellprofiler/area_occupied.py, tests/unit/test_documented_dimensionality.py, tests/unit/test_cellprofiler_library_loading.py, tests/unit/test_cellprofiler_module_execution.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/reference/dimensionality_and_measurements.rst against the later B6 changes to openhcs/core/processing_contracts.py, openhcs/core/image_payload_execution_mode.py, openhcs/processing/backends/cellprofiler/watershed.py, openhcs/processing/backends/cellprofiler/primary_objects.py, openhcs/processing/backends/cellprofiler/secondary.py, openhcs/processing/backends/cellprofiler/intensity.py, openhcs/processing/backends/cellprofiler/shape.py, openhcs/processing/backends/cellprofiler/area_occupied.py, tests/unit/test_documented_dimensionality.py, tests/unit/test_cellprofiler_library_loading.py, tests/unit/test_cellprofiler_module_execution.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/reference/dimensionality_and_measurements.rst in full and compared it against the changed authorities: aligned_image_payload.py, runtime_object_label_domains.py, unified_registry.py, the six CellProfiler backends, the Official30 axis1 manifest, and the four tests; confirmed primary/secondary/tertiary still declare Pure2DContract, Watershed and intensity use FullStackExecution, shape and area-occupied use FullStackLabels, occupied-volume callables use Pure3DContract, PAYLOAD/PLANE scopes remain, and every listed measurement family has a backend module; no page change was needed.",
"On 2026-08-27, re-read docs/source/reference/dimensionality_and_measurements.rst in full against registry form exclusions, explicit secondary-backend default selection, and expanded import tests; challenged every dimensional capability claim and found no semantic drift.",
"On 2026-08-27, re-read the complete capability reference after library registries gained declaration-owned runtime-parameter exclusions; segmentation and measurement boundaries remain unchanged.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/aligned_image_payload.py; openhcs/core/runtime_object_label_domains.py; openhcs/processing/backends/lib_registry/unified_registry.py; openhcs/processing/backends/cellprofiler/watershed.py; openhcs/processing/backends/cellprofiler/primary_objects.py; openhcs/processing/backends/cellprofiler/secondary.py; openhcs/processing/backends/cellprofiler/intensity.py; openhcs/processing/backends/cellprofiler/shape.py; openhcs/processing/backends/cellprofiler/area_occupied.py; benchmark/manifests/official30_portable_axis1.json; tests/unit/test_documented_dimensionality.py; tests/unit/test_cellprofiler_watershed_3d.py; tests/unit/test_cellprofiler_library_loading.py; tests/unit/test_cellprofiler_module_execution.py; tests/unit/test_cellprofiler_pipeline_import.py.",
@@ -3108,7 +3426,7 @@
},
{
"path": "docs/source/reference/experimental_config_syntax.rst",
- "source_sha256": "730244294388f85e5e49fc46d9850cf8f38912fb4039ac45ab89da88d7825051",
+ "source_sha256": "b40850f9bdef7085e23271bead36af7ba1488a8f3c8afa0374990c09aaf1f225",
"audience": [
"experimental-analysis operators",
"analysis integrators"
@@ -3117,48 +3435,50 @@
"diataxis": "reference",
"authority": [
{
- "path": "openhcs/formats/experimental_layout_rows.py",
- "sha256": "69e1428d0aefe683f1ee1cf741f83915fbaa1318d9d3f19b8bf8120583d883d2",
- "role": "Declares workbook row roles and parses controls, exclusions, doses, well groups, and replicate identities."
+ "path": "openhcs/processing/backends/experimental_analysis/layout_rows.py",
+ "sha256": "9e1bd76a105c7673a1e0afb223eae262087563b5673ac05e540a40ff339ee5fe",
+ "role": "Declares the workbook row roles: replicate count, Wells and WellsN rows with their replicate numbers."
},
{
- "path": "openhcs/formats/experimental_analysis.py",
- "sha256": "1169fed585163a9e26376909b06675e35218ac459a11bbbe2e56e82014856a01",
- "role": "Parses experimental workbooks, assigns plate metadata, and prepares inputs for the analysis engine."
+ "path": "openhcs/processing/backends/experimental_analysis/analysis.py",
+ "sha256": "96b29239552724475c9fb010248bb266340fa0bbf06ae890f6074b8620c6183b",
+ "role": "Parses the drug_curve_map and plate_groups sheets, applies positional dose/well/plate-group mapping, controls, exclusions, and Per Well Datapoints."
},
{
- "path": "openhcs/formats/experimental_result_formats.py",
- "sha256": "ce46b895e4caafec7d026112cacfdfc496de0cb4cd5978cceb94db02b98fe288",
- "role": "Owns the CX5 workbook and MetaXpress CSV result contracts selected by the workbook scope."
+ "path": "openhcs/processing/backends/experimental_analysis/result_formats.py",
+ "sha256": "7581999983e5acc930733451dddc84f69bf5dfbb94cfd9954f35690b7a6dd885",
+ "role": "Declares the EDDU_CX5 and EDDU_metaxpress scope names on the CX5 workbook and MetaXpress CSV result readers and rejects any other scope."
},
{
- "path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "path": "openhcs/domains/microscopy/config.py",
+ "sha256": "79b8baefdbf22b85c199807f62b8234daca6904633a574886661a1c2e3d03a9b",
+ "role": "Declares ExperimentalAnalysisConfig with its five filenames, sheet names, and normalization method defaults."
},
{
"path": "openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py",
- "sha256": "316be268b761bfa74ed1df137f2ba014a1f1c273aa9abd598a8a2e10388afb79",
- "role": "Runs the unified experimental analysis pipeline and emits the documented summary outputs."
+ "sha256": "782f462451aec3f98fc09956492cf4882531cdc41a459ea3c2a539353ed244b9",
+ "role": "Exports ExperimentalAnalysisEngine, whose run_directory projects config filenames and returns the documented result dictionary."
},
{
"path": "tests/unit/test_experimental_layout_rows.py",
- "sha256": "d71b1e77aeb31a543e9da104a9dca2169aa7ea6c6e690a5c19302b26e76ae31b",
+ "sha256": "5ead5df21c529a3d29601e9d5eeec84e24ce0130bca0af5e114ee90793ded8d4",
"role": "Proves workbook row parsing, positional mapping, multi-digit groups, result-format leaves, normalization semantics, and conditional-format plate grids."
},
{
"path": "tests/unit/test_experimental_analysis_engine.py",
- "sha256": "23b5d73def871a4c1f1e60625faf56d4664a315f90ddbddff7fa1ceab2117c17",
+ "sha256": "641fadecf8c940cc2316a06771c74db5f3f2db9dafdfbfebdc535760d6d776ec",
"role": "Exercises declaration-owned paths and a complete MetaXpress directory workflow with all three outputs."
}
],
"findings": [
- "The page used Markdown headings in RST, mixed task advice into reference, documented unsupported validation, and showed a deprecated entry point; it was rewritten from the current parser and engine.",
- "Source verification exposed Cartesian dose/well expansion and single-digit replicate parsing; the owning declarations were corrected to positional mapping with cardinality validation and multi-digit identities before the reference was finalised.",
- "Result-format verification exposed three mirrored dispatch systems and filename-based format inference. The workbook scope now selects one nominal result-format leaf, and the reference distinguishes the MetaXpress desktop default from configured CX5 runs."
+ "E1 deleted the ExperimentalAnalysisScope enum from layout_rows.py; the accepted scope values EDDU_CX5 and EDDU_metaxpress are now each result format's scope_name in result_formats.py. The page's values still hold; the layout_rows.py and result_formats.py authority roles were corrected."
],
- "disposition": "revise",
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/reference/experimental_config_syntax.rst in full and compared it against the changed authorities: experimental_analysis/layout_rows.py (ExperimentalAnalysisScope enum deleted, E1), result_formats.py (each format subclass declares scope_name; ExperimentalResultFormatStrategy.for_scope raises 'microscope ... not known' for any other value), analysis.py and unified_analysis_engine.py (call for_scope) and tests/unit/test_experimental_layout_rows.py; EDDU_CX5 and EDDU_metaxpress remain the only accepted scopes, the result dictionary still carries the format name, and the example's imports (openhcs.domains.microscopy.config.ExperimentalAnalysisConfig, openhcs.processing.backends.experimental_analysis.ExperimentalAnalysisEngine) resolve; no page edit, authority roles updated.",
+ "On 2026-10-11 (B6 final), compared docs/source/reference/experimental_config_syntax.rst against B6's later changes to openhcs/processing/backends/experimental_analysis/analysis.py, openhcs/processing/backends/experimental_analysis/result_formats.py, openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py, tests/unit/test_experimental_analysis_engine.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/reference/experimental_config_syntax.rst against the later B6 changes to openhcs/processing/backends/experimental_analysis/analysis.py, openhcs/processing/backends/experimental_analysis/result_formats.py, openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py, tests/unit/test_experimental_analysis_engine.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/reference/experimental_config_syntax.rst in full and compared it against the changed authorities: the deleted openhcs/formats/experimental_{layout_rows,analysis,result_formats}.py (moved to processing/backends/experimental_analysis/), openhcs/core/config.py, unified_analysis_engine.py, and both experimental-analysis tests; checked the row roles, scope values, engine export and run_directory signature against the moved modules, and corrected the ExperimentalAnalysisConfig import to openhcs.domains.microscopy.config.",
"Re-read docs/source/reference/experimental_config_syntax.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/formats/experimental_layout_rows.py; openhcs/formats/experimental_analysis.py; openhcs/formats/experimental_result_formats.py; openhcs/core/config.py; openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py; tests/unit/test_experimental_layout_rows.py; tests/unit/test_experimental_analysis_engine.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -3178,28 +3498,35 @@
"authority": [
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares the global, pipeline, step, and desktop configuration dataclasses that the generated configuration reference lists."
},
{
- "path": "openhcs/formats/experimental_analysis.py",
- "sha256": "1169fed585163a9e26376909b06675e35218ac459a11bbbe2e56e82014856a01",
- "role": "Parses experimental workbooks, assigns plate metadata, and prepares inputs for the analysis engine."
+ "path": "openhcs/processing/backends/experimental_analysis/analysis.py",
+ "sha256": "96b29239552724475c9fb010248bb266340fa0bbf06ae890f6074b8620c6183b",
+ "role": "Parses the Excel experimental-design workbook that the syntax reference documents."
},
{
"path": "openhcs/agent/services/knowledge_base_service.py",
- "sha256": "88e10e18c66eb3a8bc2ec5dd33493a9473510c0b0a9d355502d668042f32fdd9",
- "role": "Discovers and serves the maintained knowledge and example corpus used by MCP clients."
+ "sha256": "dfc26135b16f3827ced85b122cc1555c1325da1c5e805b421afcee9cdd7d91f3",
+ "role": "Serves the example corpus map and its native example source projection."
},
{
- "path": "openhcs/core/artifacts.py",
- "sha256": "bb958c74c98cb96c97c5ac1ea68ac997673aac6afcad57bdf0c9496065e895fa",
- "role": "Declares the nominal artefact types exchanged by compiled steps and runtime consumers."
+ "path": "openhcs/core/artifacts/kinds.py",
+ "sha256": "216b913efad9ec76707e9f61be445fb6fa1c6601c36ef53e296acd4928604515",
+ "role": "Declares the artifact types named by the shared terminology the index points to."
}
],
- "findings": [],
+ "findings": [
+ "Recorded authority openhcs/core/artifacts.py was deleted when K2 split it into the openhcs/core/artifacts package; the artifact kinds named by the shared terminology are now declared in openhcs/core/artifacts/kinds.py, which replaces it. The page text needed no change."
+ ],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/reference/index.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1 MaterializationBackend and InputSource families), experimental_analysis/analysis.py (for_scope) and the deleted openhcs/core/artifacts.py (K2 split it into the openhcs/core/artifacts package); the index names no field, class or module and every listed reference page still exists; the artifact-kind authority was repointed to openhcs/core/artifacts/kinds.py; no page edit.",
+ "On 2026-10-11 (B6), re-read docs/source/reference/index.rst in full and compared it against the changed authority openhcs/core/config.py (PartitionFilterMode became a registered subclass family, IncludePartitions/ExcludePartitions; SourceBindingsConfig and StepSourceBindingsConfig declared directly in config.py, K4); the index names no field, class or mode, and every listed reference page still exists; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/reference/index.rst against B6's later changes to openhcs/core/config.py, openhcs/processing/backends/experimental_analysis/analysis.py, openhcs/core/artifacts.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/reference/index.rst against the later B6 changes to openhcs/core/config.py, openhcs/processing/backends/experimental_analysis/analysis.py, openhcs/core/artifacts.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/reference/index.rst in full and compared it against the changed authorities: openhcs/core/config.py, the deleted openhcs/formats/experimental_analysis.py, openhcs/agent/services/knowledge_base_service.py, and openhcs/core/artifacts.py; checked that every toctree target exists and each one-line description still matches its page and owner; no page change was needed.",
"Re-read docs/source/reference/index.rst in full against repository base bb4218fae and the current shared worktree; challenged its reference user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/config.py; openhcs/formats/experimental_analysis.py; openhcs/agent/services/knowledge_base_service.py; openhcs/core/artifacts.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -3225,22 +3552,27 @@
"authority": [
{
"path": "openhcs/agent/ui_bridge_identities.py",
- "sha256": "5913d14b99340174af87b2a7cafa3cdd080a331553c0cf59392ade7d253f1234",
- "role": "Declares the complete registered family of stable public UI window identities and their presentation titles."
+ "sha256": "66dcf838d08cac2004ab8f73db777f73270cc520697281cf4f94a5fc6af4dc64",
+ "role": "Declares the registered family of stable UI window identities; plate-manager and pipeline-editor identities take their titles from the session view they render."
+ },
+ {
+ "path": "openhcs/authoring/session/views.py",
+ "sha256": "296adbceb7db7a7f7ac3cb09dbe8bb6620dec588a3b593ef2239108298bee5c8",
+ "role": "Owns the session view titles that the plate-manager and pipeline-editor window identities present."
},
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
"role": "Composes the main desktop window and its stable docked surfaces from the registered identities."
},
{
"path": "openhcs/pyqt_gui/services/main_window_workflows.py",
- "sha256": "74d3e4fa2dda4a8223c8d878aad55de9df78a71ff359c0b83346be991d14a594",
+ "sha256": "8aca9e8a94b6a3ee36dbd454c2acd3147d96c767fccbf8149f0bf05287e66312",
"role": "Projects registered window identities into WindowManager specifications for stable task windows."
},
{
"path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
"role": "Projects every registered stable-window identity into its documentation scenario, capture target, filename, and accessibility text."
},
{
@@ -3250,18 +3582,25 @@
},
{
"path": "tests/unit/test_build_website.py",
- "sha256": "4a8952ba0f51590ffd9f1d787986653802506807079a7e0d118343264252e0e0",
+ "sha256": "e88fc8ae73c5610dfb0a9948271a016e666451228ecade92aa1b2892da106059",
"role": "Proves stable-window scenario identities, titles, filenames, and capture targets derive one-to-one from the identity registry."
},
{
"path": "tests/unit/test_capture_media_gallery.py",
- "sha256": "aabfe3defb657b72150caaa3737f06352056e8580060633999a4296818ee8dcd",
+ "sha256": "c1ee8fe70c63a4308c8c4b38b2733e73c4e9c7719695c305b723e7892c8dc04b",
"role": "Proves the stable-window capture batch processes every projected scenario and records its source evidence."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/reference/ui_windows.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/reference/ui_windows.rst in full and compared it against the changed authorities: openhcs/authoring/session/views.py (dataset rows report state, terminal status and debug phase by family name) and openhcs/pyqt_gui/main.py (SelectedItems/AllItems selection scopes, ExplicitUpdateCheck/StartupUpdateCheck); the view titles that the window identities present and the main-window composition are unchanged, and the page still lists no window itself; no edit needed.",
+ "On 2026-10-11 (B6), re-read docs/source/reference/ui_windows.rst in full and compared it against the changed authorities: pyqt_gui/main.py and main_window_workflows.py (U3 moved dock layout, shortcut lifecycle and time-travel navigation to pyqt-reactive DockLayoutStore/DockPanes, ShortcutLifecycle, TimeTravelNavigator); build_main_window_specs still projects UiStableWindowIdentityDeclaration subclasses into WindowManager specs and the openhcs-ui-window-reference directive still renders the registry, so the page (which keeps no window list of its own) remains accurate; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/reference/ui_windows.rst against B6's later changes to openhcs/authoring/session/views.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/reference/ui_windows.rst against B6's later changes to openhcs/authoring/session/views.py, openhcs/pyqt_gui/main.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/reference/ui_windows.rst against the later B6 changes to openhcs/authoring/session/views.py, openhcs/pyqt_gui/main.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/reference/ui_windows.rst in full and compared it against the changed authorities: openhcs/agent/ui_bridge_identities.py, openhcs/pyqt_gui/main.py, openhcs/pyqt_gui/services/main_window_workflows.py, scripts/gallery_catalog.py, tests/unit/test_build_website.py, and tests/unit/test_capture_media_gallery.py; the U1 session refactor moved two window titles onto session views but the inventory, titles, and capture targets are still projected from the registered identity declarations, no OMERO window is registered, and all linked pages exist; added openhcs/authoring/session/views.py as the title owner; no page change was needed.",
"On 2026-08-28, re-read docs/source/reference/ui_windows.rst in full and challenged its reference boundary, completeness, duplication, navigation, and current desktop claims against the registered identity and projection owners.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/agent/ui_bridge_identities.py; openhcs/pyqt_gui/main.py; openhcs/pyqt_gui/services/main_window_workflows.py; scripts/gallery_catalog.py; docs/source/_ext/openhcs_gallery.py; tests/unit/test_build_website.py; tests/unit/test_capture_media_gallery.py.",
"A current-source projection resolved 10 registered identities one-to-one to 10 scenarios, titles, declaration-derived filenames, and checked-in WebP figures; the registry contains no OMERO management window.",
@@ -3274,7 +3613,7 @@
},
{
"path": "docs/source/user_guide/analysis_consolidation.rst",
- "source_sha256": "ddb564adfdece91f7ea7223bbd8407b78492175b83fa500c2e4449ae82cfc50a",
+ "source_sha256": "d277f21a16284bdcd021959e98b2e7833661e7639db3a73247cd3acc43af2825",
"audience": [
"desktop operators with materialised analysis results"
],
@@ -3283,40 +3622,39 @@
"authority": [
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
- "role": "Composes the main desktop window, menus, task windows, and registered UI services."
- },
- {
- "path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
+ "role": "Declares the four Tools > Analysis Consolidation actions and the experimental-analysis directory check."
},
{
- "path": "openhcs/formats/experimental_analysis.py",
- "sha256": "1169fed585163a9e26376909b06675e35218ac459a11bbbe2e56e82014856a01",
- "role": "Parses experimental workbooks, assigns plate metadata, and prepares inputs for the analysis engine."
+ "path": "openhcs/domains/microscopy/config.py",
+ "sha256": "79b8baefdbf22b85c199807f62b8234daca6904633a574886661a1c2e3d03a9b",
+ "role": "Declares AnalysisConsolidationConfig fields (enabled, metaxpress_style, file_extensions, exclude_patterns, output names) and ExperimentalAnalysisConfig filenames."
},
{
"path": "openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py",
- "sha256": "316be268b761bfa74ed1df137f2ba014a1f1c273aa9abd598a8a2e10388afb79",
+ "sha256": "782f462451aec3f98fc09956492cf4882531cdc41a459ea3c2a539353ed244b9",
"role": "Projects the five configured filenames and writes normalized, raw, and heatmap outputs."
},
{
"path": "tests/unit/test_experimental_analysis_engine.py",
- "sha256": "23b5d73def871a4c1f1e60625faf56d4664a315f90ddbddff7fa1ceab2117c17",
+ "sha256": "641fadecf8c940cc2316a06771c74db5f3f2db9dafdfbfebdc535760d6d776ec",
"role": "Proves the documented directory contract and three output files end to end."
},
{
- "path": "openhcs/core/orchestrator/analysis_consolidation.py",
- "sha256": "3ef9c20c347c1ac724353c84b24f9d7e13513aba7b9c5c01e194171b7e72bb52",
- "role": "Owns automatic per-directory consolidation and the merged summary beneath the compiled output plate root."
+ "path": "openhcs/domains/microscopy/analysis_consolidation.py",
+ "sha256": "292f7138a00dc22dba5b8d8af5de6140d247b955723cc807cc982ffec7a7dd2d",
+ "role": "Owns the post-execute hook that consolidates execution-saved tables per directory and writes the merged summary beneath the compiled output plate root."
}
],
- "findings": [
- "The task omitted the desktop action's required filenames and produced outputs; those source-owned inputs and outputs are now explicit and link to reference syntax."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/user_guide/analysis_consolidation.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/analysis_consolidation.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py (selection-scope and update-check families, E1) and experimental_analysis/unified_analysis_engine.py (result format resolved with for_scope); the four Tools > Analysis Consolidation actions, the config.xlsx and metaxpress_style_summary.csv directory contract and the three output workbooks are unchanged; no edit needed.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/analysis_consolidation.rst in full and compared it against the changed authority openhcs/pyqt_gui/main.py (U3: dock layout, shortcuts and time-travel navigation moved to pyqt-reactive); the Tools > Analysis Consolidation submenu still declares the four actions (Consolidate Results Directory, Merge MetaXpress Summaries, Concatenate MetaXpress Summaries (Keep Headers), Run Experimental Analysis) and the experimental-analysis directory dialog; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/analysis_consolidation.rst against B6's later changes to openhcs/pyqt_gui/main.py, openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py, tests/unit/test_experimental_analysis_engine.py, openhcs/domains/microscopy/analysis_consolidation.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/analysis_consolidation.rst against the later B6 changes to openhcs/pyqt_gui/main.py, openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py, tests/unit/test_experimental_analysis_engine.py, openhcs/domains/microscopy/analysis_consolidation.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/analysis_consolidation.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py, openhcs/core/config.py, the deleted openhcs/formats/experimental_analysis.py and openhcs/core/orchestrator/analysis_consolidation.py (now openhcs/domains/microscopy/analysis_consolidation.py), unified_analysis_engine.py, and the engine test; checked the menu actions, directory filenames, consolidation config fields, and per-directory plus merged summary behaviour; corrected the config description.",
"Re-read docs/source/user_guide/analysis_consolidation.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/pyqt_gui/main.py; openhcs/core/config.py; openhcs/formats/experimental_analysis.py; openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py; tests/unit/test_experimental_analysis_engine.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -3332,7 +3670,7 @@
},
{
"path": "docs/source/user_guide/code_ui_editing.rst",
- "source_sha256": "7617d0594ee253c7a19ad940eee142a931b094171b76428ac83bb74861ad2c17",
+ "source_sha256": "cbc76b557c64d0c183b351c4e9c465777e465856e3e2c4954f4909bc4f2fd26e",
"audience": [
"desktop pipeline authors",
"MCP-assisted editors"
@@ -3342,93 +3680,83 @@
"authority": [
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
- "role": "Declares the public pipeline document, step collection, and serialisable declaration boundary."
- },
- {
- "path": "openhcs/pyqt_gui/services/config_window_code_document.py",
- "sha256": "bdbf33b254451945239bd1c1f256dedbed40d2a8c0e598c4426bcba939ab5d2d",
- "role": "Owns typed configuration source projection and applies parsed updates through ConfigDocumentAuthority."
- },
- {
- "path": "external/pyqt-reactive/src/pyqt_reactive/services/window_code_document.py",
- "sha256": "d0ab321af0eb2b14fac3ab2577c8c61672763d2f13a70a7ce8a35ee7bfab7c75",
- "role": "Owns generic window-document writability and declaration-normalised source comparison."
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
+ "role": "PipelineDocumentCodec validates, parses, and renders the pipeline_steps and optional pipeline_config document."
},
{
- "path": "openhcs/pyqt_gui/services/ui_bridge_object_state.py",
- "sha256": "747c284c37e420092ffda9abbb8275fc7deaaff7f3676cecf3f5b1f324314744",
- "role": "Projects driver capabilities and enforces them before applying a document to live ObjectState."
+ "path": "openhcs/authoring/session/dataset_document.py",
+ "sha256": "ddb55e52107819b22d7bb7d18e6310d98eb0600471a722cb789a5a68028d26f3",
+ "role": "Owns the selected and all-datasets document scopes, selected-scope ID enforcement, and the global-draft and per-dataset change checks on apply."
},
{
- "path": "openhcs/agent/services/ui_bridge_service.py",
- "sha256": "37015a88828820e7ae98bf804f42c37f1fa26009b1ad28b3c537f65a656e943a",
- "role": "Owns agent-to-UI declaration inspection and mutation, including code and form synchronisation."
+ "path": "openhcs/authoring/session/pipelines.py",
+ "sha256": "febf9f9f464b2600554256f16929c408d4bdcaedd6ca99c71810bcb7be519519",
+ "role": "PipelineObjectStateBinding updates and commits a dataset's step ObjectState graph after a complete pipeline replacement."
},
{
- "path": "external/pycodify/src/pycodify/__init__.py",
- "sha256": "b839bc2a7cd2afdfd8756eb39f22b791cb4f7aa55c0fc9c11099b7219c9c663b",
- "role": "Exports the PyCodify source-generation and reconstruction API used for declaration round-tripping."
+ "path": "openhcs/authoring/session/views.py",
+ "sha256": "296adbceb7db7a7f7ac3cb09dbe8bb6620dec588a3b593ef2239108298bee5c8",
+ "role": "Declares the Datasets and Pipeline Editor view titles shown for the two code-document owners."
},
{
- "path": "tests/unit/pyqt_gui/test_ui_agent_bridge.py",
- "sha256": "1feee4bb413738f04e81cbdf26244dc0d585a68921e6c7749a941a08518007a1",
- "role": "Proves bidirectional UI-bridge inspection, capability enforcement, semantic no-op handling, and mutation of live declarations."
+ "path": "openhcs/pyqt_gui/widgets/pipeline_editor.py",
+ "sha256": "f60e195031b99bd02d8e4cdf1b42ca213fc5669526bc30348ce3ddff14f6876b",
+ "role": "Makes the Pipeline Editor document writable only while a dataset is selected."
},
{
- "path": "openhcs/pyqt_gui/widgets/shared/services/plate_manager_workflows.py",
- "sha256": "d640bf4b2710009dc3f92719ec97cf09fe3d93ed7eb1bebaf2b02913e8fafbfd",
- "role": "Owns proof-bearing selected and all-plates code-document application."
+ "path": "openhcs/pyqt_gui/windows/config_window.py",
+ "sha256": "f72c0e481e35819f49749a7b82729e2f99ff200ebfd5bd6031efdb6f0585b24b",
+ "role": "OpenHCSConfigWindow plugs the OpenHCS configuration document format into pyqt-reactive's ConfigWindow, rendering and parsing typed config source."
},
{
- "path": "openhcs/pyqt_gui/services/pipeline_object_state_binding.py",
- "sha256": "afb190ac9ac38e800c8fe04497bbb78a2f1b6381a21eb14890e3061c61ae5b0b",
- "role": "Owns fresh identity graphs for complete pipeline replacement and stable occurrence identities for incremental edits."
+ "path": "openhcs/core/config_document.py",
+ "sha256": "9695a6f300d626ceddaf4e0cd18961fd5600b63becf9a133cfa7f0b654b32cfb",
+ "role": "ConfigDocumentAuthority renders a typed configuration as source and validates parsed source against the expected config type."
},
{
- "path": "openhcs/core/steps/function_step.py",
- "sha256": "8e1243404095691030e9ebb95bb5611679f96c66c6b6819ae71e4b41249e74bc",
- "role": "Owns complete FunctionStep declaration equality through canonical callable-pattern projection."
+ "path": "external/pyqt-reactive/src/pyqt_reactive/windows/config_window.py",
+ "sha256": "2ee4f5c173b30b0a94a4288243d82ae66b6d65d12aa59493ad9363559966c1cd",
+ "role": "ConfigCodeDocumentDriver applies parsed configuration source to the window's edit session."
},
{
- "path": "tests/unit/pyqt_gui/test_plate_manager_widget.py",
- "sha256": "b02e81c4a73213577df02a1effbd6843935f5c92f459d6c61ab2998bd489ad58",
- "role": "Proves selected code-document application preserves unselected plate graphs."
+ "path": "external/pyqt-reactive/src/pyqt_reactive/services/window_code_document.py",
+ "sha256": "56d92d665a7b3bf6acde28292679cad13388793e1acb5bea74926f4330150d42",
+ "role": "Owns generic window-document writability and declaration-normalised source comparison."
},
{
- "path": "tests/unit/test_pipeline_document.py",
- "sha256": "2c18222ac2a34ee267788063a374112b17874c64693bfe542669c4183bc90c36",
- "role": "Proves omitted pipeline_config becomes PipelineConfig() and canonical rendering restores the explicit assignment."
+ "path": "external/pyqt-reactive/src/pyqt_reactive/services/scope_token_service.py",
+ "sha256": "a4913835f35ae1b773eab817224410786cd569e459f633e9949ac4e3c3e724ba",
+ "role": "Reconciles submitted step occurrences with existing scopes, giving ambiguous duplicates new scopes."
},
{
- "path": "tests/unit/pyqt_gui/test_pipeline_code_document_transactionality.py",
- "sha256": "b7e546e904b897f13d76f0c523e751afa541a75c627349ba79a3a87313d30f4c",
- "role": "Proves Pipeline Editor validates and applies the abbreviated source through its normal transactional workflow."
+ "path": "openhcs/pyqt_gui/services/ui_bridge_object_state.py",
+ "sha256": "f615718186e4aaebdc12eec6bec6a97d47953322075c5b067974e5a16ecc8e80",
+ "role": "Enforces driver writability, returns unchanged for equivalent documents, and applies documents against revision tokens."
},
{
- "path": "openhcs/pyqt_gui/widgets/shared/services/pipeline_editor_workflows.py",
- "sha256": "6664126ac4daa851cacff5014e04a0c09a56bbc76f62661ff97d713e9137d447",
- "role": "Applies a complete Pipeline Editor document and commits the reconciled live state graph."
+ "path": "openhcs/pyqt_gui/services/ui_agent_bridge.py",
+ "sha256": "55a21b2746e3ba27eff1a3315604725d340dbddce6645c8457f707484a2f1f0c",
+ "role": "Rejects wildcard imports and resolves catalogue-listed external functions in submitted documents."
},
{
- "path": "tests/unit/pyqt_gui/test_pipeline_editor_widget.py",
- "sha256": "8f28719c5bd9eb5681e67af8e1f7aceb445bcae97c12a13348f943c8b6bafa35",
- "role": "Proves selected-plate writability and that code apply advances saved baselines for the editor, step, and nested function states."
+ "path": "openhcs/agent/services/ui_bridge_service.py",
+ "sha256": "a2d682ad7841a7d85173f792961cae2dc5546fb4fcf39caa85f0bbd6ed062b01",
+ "role": "Owns agent-to-UI document inspection and mutation and returns mutation receipts."
},
{
- "path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
- "role": "Owns the lazy-inheritance scenario, representative poster, caption, description, alt text, dimensions, and release evidence."
+ "path": "external/pycodify/src/pycodify/__init__.py",
+ "sha256": "083feeadebf55cc3437b44fa23aaccc51e1cef7b2cf169339031e7ca3a3e3abe",
+ "role": "Exports the PyCodify source-generation and reconstruction API used for declaration round-tripping."
},
{
- "path": "openhcs/pyqt_gui/widgets/step_parameter_editor.py",
- "sha256": "54e0bdd74f8d781c2bfdf1da559bbe7e2393f19ef74826d374a2281e8eb96258",
- "role": "Applies edited function patterns through generic reconciliation before rebuilding parameter panes."
+ "path": "openhcs/serialization/pycodify_formatters.py",
+ "sha256": "18b744cdeb8d9ce22801efe6f3f81fec346194019293e68f542dc13c11724a2d",
+ "role": "Formats function patterns and steps: signature-ordered parameters, semantic default omission in clean mode, and function-reference imports."
},
{
- "path": "openhcs/serialization/pycodify_formatters.py",
- "sha256": "316ff58e34dcb87816082ead03347cd280fe72b4e3ab8ea31affc1627e548c23",
- "role": "Supplies OpenHCS semantic equality and clean/full choices to shared parameter projection."
+ "path": "external/ObjectState/src/objectstate/codegen.py",
+ "sha256": "347d2697c061a35e6e440e373fe4e8bfce1ad92b500e98da07faa26f6a3a1356",
+ "role": "Writes raw lazy dataclass field values, omitting inherited and default fields in clean mode and writing every field in full mode."
},
{
"path": "external/python-introspect/src/python_introspect/callable_declaration.py",
@@ -3437,22 +3765,57 @@
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/services/function_pattern_code_document.py",
- "sha256": "bdf41b58bff009890e6e696cfc4f18313adfb1669566b2566f9e7b93d863c071",
- "role": "Projects child parameter states into function-pattern code documents, retaining a child only while its function authority and editable surface match."
+ "sha256": "77b0bc021efaaac47bc5fb8c29aa50536162a9630dafd48c6ece42790837b94d",
+ "role": "Retains a child parameter state only while its function and editable surface match."
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/protocols/widget_adapters.py",
"sha256": "1ae6653bf3f2786eafa9f53f246e145ebfa34b81c190aaa8f41c19a1463a42f9",
"role": "Owns locale-aware compact numeric text without reducing configured precision."
},
+ {
+ "path": "openhcs/pyqt_gui/widgets/shared/services/pipeline_editor_workflows.py",
+ "sha256": "06e54377bc594beef653baa9cfaf4de0031e754da1e7b1ab3a22ac67a922b68f",
+ "role": "Applies a complete Pipeline Editor document and commits the reconciled live state graph."
+ },
+ {
+ "path": "scripts/gallery_catalog.py",
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
+ "role": "Owns the lazy-inheritance gallery scenario, poster, caption, and alt text."
+ },
+ {
+ "path": "tests/unit/pyqt_gui/test_code_document_catalog_imports.py",
+ "sha256": "adfcec931eaad3efbde3495585bb8e184e840e107ce998ecc3a607be9fd80595",
+ "role": "Proves catalogue imports such as skimage.filters.edges.sobel keep the registry wrapper and wildcard or unlisted imports are rejected."
+ },
+ {
+ "path": "tests/unit/pyqt_gui/test_ui_agent_bridge.py",
+ "sha256": "422be9298e5120c898bd7110884ca770f1f033f07fd69be4ac0caa978d3014f4",
+ "role": "Proves bidirectional UI-bridge inspection, capability enforcement, semantic no-op handling, and mutation of live declarations."
+ },
+ {
+ "path": "tests/unit/pyqt_gui/test_plate_manager_widget.py",
+ "sha256": "96ffc41a2fe3ef6ad959e0527bbbec2a86ac64c0a3d627385b384539e4858f81",
+ "role": "Proves selected code-document application preserves unselected dataset graphs."
+ },
+ {
+ "path": "tests/unit/test_pipeline_document.py",
+ "sha256": "4fe05636d5a9eb93f576904a774d30653a3686c7c8f61031ec0d316854cf5303",
+ "role": "Proves omitted pipeline_config becomes PipelineConfig() and rendering restores the explicit assignment."
+ },
+ {
+ "path": "tests/unit/pyqt_gui/test_pipeline_editor_widget.py",
+ "sha256": "6761f5c3526167053b22edf5271f3413730d7798e8db45dbc57ec1baa52515aa",
+ "role": "Proves selected-dataset writability and that code apply advances saved baselines for the editor, step, and nested function states."
+ },
{
"path": "tests/unit/pyqt_gui/test_step_code_widget_roundtrip.py",
"sha256": "a67876053a5381e1767060d0ee5d54c79d96dfbaec8ef0e267bd767993a821cd",
- "role": "Verifies real step-code and widget round trips, including retained and hidden child states."
+ "role": "Verifies real step-code and widget round trips, including retained and rebuilt child states."
},
{
"path": "tests/unit/test_pycodify_formatters.py",
- "sha256": "445479f81fd70ce7f288d5b7537b9bbb85d1b5dd724d27baabf8e4c4e508128c",
+ "sha256": "78d695333a74b4a75a07ca7de87a5c685a6fd429c67412b43375d16a298a9c80",
"role": "Verifies clean/full parameter order and semantic default handling."
},
{
@@ -3461,17 +3824,15 @@
"role": "Verifies numeric value preservation and compact presentation."
}
],
- "findings": [
- "The code-mode procedure now records the steps-only compatibility form and canonical re-rendering of the default PipelineConfig assignment.",
- "The guide omitted Plate Manager selection authority and the identity semantics of complete pipeline replacement.",
- "The guide incorrectly implied a second save action after Pipeline Editor code apply; it now records the exact saved-baseline boundary.",
- "The guide omitted selected-plate writability and declaration-normalised no-op behaviour.",
- "Callable wrapper and explicit-default differences no longer create false occurrence replacements during code round trips.",
- "Explained signature order, compact decimal presentation, and retained parameter states after step-code application.",
- "The apply procedure previously implied every retained function occurrence kept its child ObjectState even when explicit keyword additions or removals changed the editable surface."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/code_ui_editing.rst in full and compared it against the changed authorities: authoring/session/dataset_document.py (document scopes keyed by SelectedItems/AllItems), session/views.py, pyqt_gui/widgets/pipeline_editor.py, services/ui_bridge_object_state.py (field filters are classes), services/ui_agent_bridge.py and agent/services/ui_bridge_service.py (operation and action statuses are classes; PlateManagerDocumentAssignments names the allowed document assignments) and the three pyqt_gui tests; selected and all-datasets document semantics, the writable check, the unchanged outcome for equivalent documents, wildcard-import rejection and revision-token apply are behaviourally unchanged; the page names none of the renamed enums; no edit needed.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/code_ui_editing.rst in full and compared it against the changed authorities: pipeline_document.py (generate_python_source), dataset_document.py (objectstate.retain_only_authored_paths), pipelines.py and pipeline_editor.py (ScopeKey child scopes, GlobalEventBus broadcast removed, use_external_code_editor), deleted config_window_code_document.py (U3), ui_bridge_object_state.py (field markers now from objectstate.inspection FieldInspection; '*' dirty and '_' signature-diff unchanged), pycodify_formatters.py (lazy-dataclass formatters moved to objectstate.codegen, L6), pipeline_editor_workflows.py (K6 debug command classes), pyqt-reactive window_code_document/scope_token_service/function_pattern_code_document (current files), and the changed tests; selected/all scope apply, writability, unchanged no-op, revision tokens, occurrence reconciliation, signature parameter order and clean/resolved views all still hold; replaced and added authorities; no page edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/code_ui_editing.rst against B6's later changes to openhcs/authoring/session/views.py: one-plane label projection now owned by every slice-aligned label mode, dataset rows carry the finished execution's error, the wheel job prewarms the Fiji bundle, the test session creates its QApplication first, and tracked files keep committed bytes. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/code_ui_editing.rst against B6's later changes to openhcs/authoring/session/dataset_document.py, openhcs/authoring/session/views.py, openhcs/pyqt_gui/widgets/pipeline_editor.py, openhcs/pyqt_gui/services/ui_bridge_object_state.py, openhcs/pyqt_gui/services/ui_agent_bridge.py, openhcs/agent/services/ui_bridge_service.py, openhcs/pyqt_gui/widgets/shared/services/pipeline_editor_workflows.py, tests/unit/pyqt_gui/test_ui_agent_bridge.py, tests/unit/pyqt_gui/test_plate_manager_widget.py, tests/unit/pyqt_gui/test_pipeline_editor_widget.py, tests/unit/test_pycodify_formatters.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/code_ui_editing.rst against the later B6 changes to openhcs/authoring/session/dataset_document.py, openhcs/authoring/session/views.py, openhcs/pyqt_gui/widgets/pipeline_editor.py, openhcs/pyqt_gui/services/ui_bridge_object_state.py, openhcs/pyqt_gui/services/ui_agent_bridge.py, openhcs/agent/services/ui_bridge_service.py, openhcs/pyqt_gui/widgets/shared/services/pipeline_editor_workflows.py, tests/unit/pyqt_gui/test_ui_agent_bridge.py, tests/unit/pyqt_gui/test_plate_manager_widget.py, tests/unit/pyqt_gui/test_pipeline_editor_widget.py, tests/unit/test_pycodify_formatters.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/code_ui_editing.rst in full and compared it against the changed authorities: pipeline_document.py, ui_bridge_object_state.py, ui_bridge_service.py, plate_manager_workflows.py and pipeline_editor_workflows.py, the deleted pipeline_object_state_binding.py (now authoring/session/pipelines.py), step_parameter_editor.py, pycodify_formatters.py, gallery_catalog.py, and the changed tests; checked selected/all document scopes, selected-dataset writability, global-draft protection while work runs, catalogue imports and wildcard rejection, unchanged no-op results, and saved-baseline commit; renamed Plate Manager to the Datasets window.",
"Re-read docs/source/user_guide/code_ui_editing.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/pipeline_document.py; openhcs/pyqt_gui/services/pycodified_window_code_document.py; openhcs/agent/services/ui_bridge_service.py; external/pycodify/src/pycodify/__init__.py; tests/unit/pyqt_gui/test_ui_agent_bridge.py; openhcs/pyqt_gui/widgets/shared/services/plate_manager_workflows.py; openhcs/pyqt_gui/services/pipeline_object_state_binding.py; tests/unit/pyqt_gui/test_plate_manager_widget.py; tests/unit/test_pipeline_document.py; tests/unit/pyqt_gui/test_pipeline_code_document_transactionality.py; openhcs/pyqt_gui/widgets/shared/services/pipeline_editor_workflows.py; tests/unit/pyqt_gui/test_pipeline_editor_widget.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -3502,28 +3863,38 @@
"authority": [
{
"path": "openhcs/utils/environment.py",
- "sha256": "d408e5c8bd86f9b7d26ce23595fb75200a9320d9f3ee9bfca8d29861cecb9c9d",
- "role": "Detects runtime environment and optional backend availability, including CPU-only operation."
+ "sha256": "1985ffbe42b5dfb283b78b6282f9b56e08cd51d5a9e79f2dd4eebffcb6da0bf5",
+ "role": "Declares OPENHCS_CPU_ONLY and projects the CPU-only import policy (including JAX_PLATFORMS=cpu) before dependencies import their runtimes."
+ },
+ {
+ "path": "openhcs/pyqt_gui/launch.py",
+ "sha256": "fc018b94dc0aeaea4d1d7592973f5635419da5b53b85ecf07448bae975bb6e14",
+ "role": "Declares the desktop --no-gpu flag and enables CPU-only mode before Qt and backend startup."
},
{
"path": "openhcs/processing/backends/lib_registry/registry_service.py",
- "sha256": "046d3d088f685a993ddad6b5923cf63beef84b201127b72ae32af6721dd5ad63",
- "role": "Owns backend discovery, availability filtering, and the canonical process catalogue projection."
+ "sha256": "f2e981157632adb5020828720ec26bfa6444ab52f545e1e71b1524242eaed6a3",
+ "role": "Skips registries that do not support CPU-only mode during function discovery."
},
{
"path": "tests/unit/test_library_registry_discovery.py",
- "sha256": "a7ee2d3a2002ea291876c27aa2dce51a889cea6be9a3055602c76f1e9cf76f1b",
+ "sha256": "a67eb8d91a44a3eb347b46608679f2f8632abfd5b54c6f85315d1ea71a164460",
"role": "Proves backend discovery and availability filtering produce the expected callable registry."
},
{
"path": "pyproject.toml",
- "sha256": "6217b5f029796edcd83aa47fb8632d37aa7552344755c578fa4f181a6b23282d",
- "role": "Declares the package version, Python requirement, dependency extras, scripts, and distribution metadata."
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
+ "role": "Declares the optional gpu extra and the openhcs console entry point."
}
],
"findings": [],
"disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/cpu_only_mode.rst in full and compared it against the changed authorities: openhcs/utils/environment.py (AgentRuntimePlatform rename), openhcs/pyqt_gui/launch.py (QtPlatformSystem enum replaced by a setup table) and pyproject.toml; OPENHCS_CPU_ONLY and --no-gpu are still declared, and pyproject.toml still declares the gpu extra and the openhcs entry point; no edit needed.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/cpu_only_mode.rst in full and compared it against the changed authorities: registry_service.py (processing contract now read from reference.declaration instead of reference.metadata, no change to CPU-only registry skipping) and pyproject.toml (dependency floors only; gpu extra and openhcs entry point unchanged); confirmed OPENHCS_CPU_ONLY and --no-gpu are still declared; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/cpu_only_mode.rst against B6's later changes to openhcs/processing/backends/lib_registry/registry_service.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/cpu_only_mode.rst against the later B6 changes to openhcs/processing/backends/lib_registry/registry_service.py, tests/unit/test_library_registry_discovery.py, pyproject.toml: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/cpu_only_mode.rst in full and compared it against the changed authorities: openhcs/utils/environment.py, registry_service.py, test_library_registry_discovery.py, and pyproject.toml; confirmed --no-gpu and OPENHCS_CPU_ONLY still select the same mode, discovery still filters registries by CPU-only support, and the gpu extra still exists; the new import-policy defaults (JAX platform, NumPy huge pages, worker profiling) do not contradict the page; no page change was needed.",
"On 2026-08-27, re-read docs/source/user_guide/cpu_only_mode.rst in full against declaration-owned catalogue inputs and the python-introspect 0.1.12 floor; challenged the CPU-only workflow and found no behaviour change.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/utils/environment.py; openhcs/processing/backends/lib_registry/registry_service.py; tests/unit/test_library_registry_discovery.py; pyproject.toml.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -3551,7 +3922,7 @@
},
{
"path": "docs/source/user_guide/custom_function_management.rst",
- "source_sha256": "a543658a18b61b83e0e516d59f13e543090213d904b7503c89ef479eb9a55f87",
+ "source_sha256": "949fb40f353aaeca3dab0272ec4e021166ab70f984e4acff6fe83ef73ea948ec",
"audience": [
"desktop callable authors"
],
@@ -3560,13 +3931,18 @@
"authority": [
{
"path": "openhcs/processing/custom_functions/manager.py",
- "sha256": "aa5aa5a7032cd38c170d29d800175022883f04df7ce2d3f2fb4f6decad43f97d",
- "role": "Owns persisted custom-function source and coordinates validated lifecycle transactions with the runtime projection owner."
+ "sha256": "77612ea5ed53888c36e8ed31f763d8eaf5e0af9e4f32b4beaaa7cff09012b130",
+ "role": "Owns persisted source under the data directory, requires one decorated processing function per source, rejects collisions, and coordinates create, load, replace, and delete."
+ },
+ {
+ "path": "openhcs/processing/custom_functions/runtime_registry.py",
+ "sha256": "d2311617cad39d96ceb3ea6e13b6ad89f3b846e95d5dc3d692e7a9a46dd9c739",
+ "role": "Atomically publishes process-local callable metadata and module exports, retaining ephemeral declarations when persisted source reloads."
},
{
"path": "openhcs/processing/custom_functions/events.py",
"sha256": "ed62fe7aef90d56ef4c3421d61257a272e9a54f7ec2005cc865ff0e605d8ea35",
- "role": "Publishes process-local custom-function changes through weak identity-preserving subscriptions without importing a GUI runtime."
+ "role": "Publishes process-local custom-function changes through weak subscriptions without importing a GUI runtime."
},
{
"path": "openhcs/processing/custom_functions/signals.py",
@@ -3574,80 +3950,70 @@
"role": "Adapts the headless domain event into Qt signal delivery for desktop consumers."
},
{
- "path": "tests/unit/test_custom_function_events.py",
- "sha256": "8cc545a424a3c5235dbca2c123ce7f1e266aaaa007b85082154cbf06ed78122c",
- "role": "Proves weak subscriber lifetimes, exact callback identity, duplicate bound-method suppression, and unsubscribe on Qt destruction even while its Python wrapper survives."
+ "path": "openhcs/pyqt_gui/main.py",
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
+ "role": "Declares the Tools > Custom Functions > Manage Functions action."
},
{
"path": "openhcs/pyqt_gui/dialogs/custom_function_manager_dialog.py",
- "sha256": "31b0073aa4e61966b3bb1020d8fe455f087563490dff0d78a47724fe0d677767",
+ "sha256": "fc67b871212b512661ebbf82641814194472bdc94febc67af85aa3f1e7eb2b4e",
"role": "Implements the desktop controls for inspecting, editing, reloading, and removing custom functions."
},
- {
- "path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
- "role": "Composes the main desktop window, menus, task windows, and registered UI services."
- },
- {
- "path": "tests/unit/test_function_catalog_zmq.py",
- "sha256": "f146a79c06ad1bec0db105ce88322a3a7158e0a6c60c8bac8e8608784fa7983c",
- "role": "Proves persisted custom functions propagate through registry discovery and the execution-server catalogue."
- },
{
"path": "openhcs/agent/services/endpoint_function_catalog_service.py",
- "sha256": "977a74003819f2547548d322f4c5f0cb148b23fec20f31be4fa462bfcfb7ea04",
- "role": "Owns endpoint registration, asynchronous invalidation, and refresh of the shared catalogue projection."
+ "sha256": "bc232090472554ab075f7800bbcc238e27a646a2d8225c87e5e443fb3c5d9f78",
+ "role": "Owns endpoint catalogue preparation, invalidation, cancellation, and custom-function registration on the execution endpoint."
},
{
"path": "openhcs/pyqt_gui/dialogs/function_selector_dialog.py",
"sha256": "e11c456a5322038408edd42df7d2674cda42e1992f7f6c6e567e7923bf3526c3",
- "role": "Projects catalogue loading and failure state without blocking the Qt thread."
+ "role": "Invalidates and reloads the endpoint catalogue without blocking the Qt thread."
},
{
- "path": "openhcs/processing/custom_functions/runtime_registry.py",
- "sha256": "ee935ccc09f3a967d84c9ebd08511ad729d53e73d5bceb52074b39a1cd6ead9c",
- "role": "Atomically publishes process-local custom callable metadata and public module exports from validated source declarations."
+ "path": "openhcs/mcp/context.py",
+ "sha256": "1292c9bb7173e6afe5aa6237b861c5f39bc2e854f364fd722c75a3c6b76f534e",
+ "role": "Supplies the local MCP tools with the session's endpoint function catalogue."
},
{
- "path": "tests/unit/test_custom_function_lifecycle.py",
- "sha256": "918326f0f530d833172b30f25a3e91c4f3fc402ea6645a515bd666a83816994d",
- "role": "Proves transactional create, load, replace, delete, collision, and source-revision behaviour."
+ "path": "external/pyqt-reactive/src/pyqt_reactive/core/future_completion.py",
+ "sha256": "7ba6ef203bae81319004ccc4ddfe4226e0230b177df663cfb0d02c364e1dbc47",
+ "role": "Owns future completion relay lifetime and Qt receiver disconnection."
},
{
"path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
- "role": "Owns the documentation-enabled Custom Function Manager capture declaration, accessibility text, capture target, and generated visual projection."
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
+ "role": "Owns the Custom Function Manager capture declaration, accessibility text, and capture target."
},
{
- "path": "external/pyqt-reactive/src/pyqt_reactive/core/future_completion.py",
- "sha256": "7ba6ef203bae81319004ccc4ddfe4226e0230b177df663cfb0d02c364e1dbc47",
- "role": "Owns future completion relay lifetime and Qt receiver disconnection."
+ "path": "tests/unit/test_custom_function_events.py",
+ "sha256": "8cc545a424a3c5235dbca2c123ce7f1e266aaaa007b85082154cbf06ed78122c",
+ "role": "Proves weak subscriber lifetimes, exact callback identity, and unsubscribe on Qt destruction."
},
{
- "path": "external/pyqt-reactive/tests/test_future_completion.py",
- "sha256": "fddee2a4c4b210e43c137a24f5730c0e8303a38ecc67afec7a4ac5f39d2ad860",
- "role": "Verifies native receiver destruction, delivery thread, garbage collection and cancellation."
+ "path": "tests/unit/test_custom_function_lifecycle.py",
+ "sha256": "81926f26051d5f40eead795c1c9edf60b84b31aab9d61bc0411929328b002de9",
+ "role": "Proves transactional create, load, replace, delete, collision, and source-revision behaviour."
},
{
- "path": "openhcs/mcp/context.py",
- "sha256": "7bb92e1357e02a0676243370179e5ff471d4e15c4c88b9a9f1d69b79f505c7cf",
- "role": "Projects the local MCP catalogue endpoint from the canonical UI configuration."
+ "path": "tests/unit/test_function_catalog_zmq.py",
+ "sha256": "9398fd1442b6f793f5b6d273b2965ececd5888f12c37fd11b1e71731c9a9fd93",
+ "role": "Proves persisted custom functions propagate through registry discovery and the execution-server catalogue."
},
{
- "path": "openhcs/pyqt_gui/config.py",
- "sha256": "a3eece2e5b2926fc6fc0edd197221da451ab7c0d0d6f4ffc8bb2ff865ad74bd4",
- "role": "Owns the explicit canonical UI configuration file selector."
+ "path": "external/pyqt-reactive/tests/test_future_completion.py",
+ "sha256": "fddee2a4c4b210e43c137a24f5730c0e8303a38ecc67afec7a4ac5f39d2ad860",
+ "role": "Verifies native receiver destruction, delivery thread, garbage collection and cancellation."
}
],
- "findings": [
- "The task guide did not explain that custom-function changes invalidate and asynchronously refresh the endpoint-owned catalogue projection.",
- "The guide assigned both persistence and runtime publication to CustomFunctionManager instead of distinguishing persisted source from the atomic process projection.",
- "The custom-function manager imported a global Qt signal as part of its headless lifecycle; change publication is now a domain event with a separate desktop Qt adapter.",
- "The domain event retained temporary Qt adapters after their C++ objects were destroyed, allowing a later custom-function update to invoke an invalid callback and crash the process.",
- "Clarified current endpoint catalogue reads after remote registration and retained asynchronous/cache ownership."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/user_guide/custom_function_management.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/custom_function_management.rst in full and compared it against the changed authorities: custom_functions/manager.py and runtime_registry.py (CustomFunctionLifetime became PersistedLifetime and EphemeralLifetime classes), openhcs/pyqt_gui/main.py (E1 families) and tests/unit/test_custom_function_lifecycle.py and test_function_catalog_zmq.py (K2 artifacts imports, parameter-source classes); reloading persisted functions still keeps declarations whose lifetime is EphemeralLifetime, CustomFunctionManager.register_from_code(persist=...) is unchanged and the Manage Functions action remains; no edit needed.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/custom_function_management.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py (U3 moved dock layout, shortcuts and time-travel navigation to pyqt-reactive; Tools > Custom Functions > Manage Functions unchanged) and tests/unit/test_custom_function_lifecycle.py (function references expose .declaration instead of .metadata, K5); CustomFunctionManager.register_from_code and its package export, and CustomFunctionRuntimeRegistry publishing FunctionMetadata, are unchanged, so the code example and prose remain accurate; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/custom_function_management.rst against B6's later changes to openhcs/processing/custom_functions/manager.py, openhcs/pyqt_gui/main.py, openhcs/mcp/context.py, tests/unit/test_custom_function_lifecycle.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/custom_function_management.rst against the later B6 changes to openhcs/processing/custom_functions/manager.py, openhcs/pyqt_gui/main.py, openhcs/mcp/context.py, tests/unit/test_custom_function_lifecycle.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/custom_function_management.rst in full and compared it against the changed authorities: custom_functions/manager.py and runtime_registry.py, custom_function_manager_dialog.py, openhcs/pyqt_gui/main.py, endpoint_function_catalog_service.py, openhcs/mcp/context.py, gallery_catalog.py, and the catalogue and lifecycle tests; checked the menu path, register_from_code signature and package export, source validation rules, ephemeral retention on reload, and catalogue invalidation; corrected the decorator requirement.",
"Re-read docs/source/user_guide/custom_function_management.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/processing/custom_functions/manager.py; openhcs/processing/custom_functions/events.py; openhcs/processing/custom_functions/signals.py; tests/unit/test_custom_function_events.py; openhcs/pyqt_gui/dialogs/custom_function_manager_dialog.py; openhcs/pyqt_gui/main.py; tests/unit/test_function_catalog_zmq.py; openhcs/agent/services/endpoint_function_catalog_service.py; openhcs/pyqt_gui/dialogs/function_selector_dialog.py; openhcs/processing/custom_functions/runtime_registry.py; tests/unit/test_custom_function_lifecycle.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -3672,7 +4038,7 @@
},
{
"path": "docs/source/user_guide/custom_functions.rst",
- "source_sha256": "6dec340f9cb1eb45e984513bb0ad3cdae229c546fc846df128564a0df0f61398",
+ "source_sha256": "b3116c2edcbfbfbe84ebe0874e453b74a50ae5a47cb5348dd2bfc6a442a6c174",
"audience": [
"Python-capable pipeline authors"
],
@@ -3681,45 +4047,59 @@
"authority": [
{
"path": "openhcs/core/memory/decorators.py",
- "sha256": "d9f9cf0be7593dc0ebfc57f258aebed75bcf9cfa508ed1e942137366d819766a",
- "role": "Exports OpenHCS memory and dtype decorators while delegating conversion semantics to ArrayBridge declarations."
+ "sha256": "76f234785f4865257fd19c935e0aace82d1f5586198737fec619ee243b79cbf6",
+ "role": "Exports one OpenHCS memory decorator per MemoryType, including numpy, accepting a contract argument."
},
{
- "path": "openhcs/processing/backends/lib_registry/unified_registry.py",
- "sha256": "70462cddcad1bfee447ce0786a9e4a85abbd297726d59a1208156af0ef3a3743",
- "role": "Defines canonical callable metadata and derives the unified catalogue from registry declarations."
+ "path": "openhcs/core/processing_contracts.py",
+ "sha256": "eb0f7fd99ded1c4d5957152ccf6abe1468436c13574a1d1bb83ce368d24b9f69",
+ "role": "Declares the ProcessingContract classes Pure2DContract, Pure3DContract, VolumetricToSliceContract, and FlexibleContract."
},
{
- "path": "openhcs/processing/custom_functions/manager.py",
- "sha256": "aa5aa5a7032cd38c170d29d800175022883f04df7ce2d3f2fb4f6decad43f97d",
- "role": "Owns persisted custom-function source and coordinates validated lifecycle transactions with the runtime projection owner."
+ "path": "external/arraybridge/src/arraybridge/decorators.py",
+ "sha256": "28f9bf4770bd4cc1ef34045fdef28065063ede029296f62b8a6c4140cc88e40e",
+ "role": "Declares the input, output, and execution framework metadata written by supported memory decorators."
},
{
- "path": "tests/unit/test_function_catalog_zmq.py",
- "sha256": "f146a79c06ad1bec0db105ce88322a3a7158e0a6c60c8bac8e8608784fa7983c",
- "role": "Proves persisted custom functions propagate through registry discovery and the execution-server catalogue."
+ "path": "openhcs/pyqt_gui/main.py",
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
+ "role": "Declares the Tools > Custom Functions > Create New Function action."
},
{
- "path": "external/arraybridge/src/arraybridge/decorators.py",
- "sha256": "99823105b0ae59054df1d84013633b8b6628d0a6dc3326383199ac7c65c69bed",
- "role": "Declares the input, output, and execution framework metadata written by supported memory decorators."
+ "path": "openhcs/processing/custom_functions/templates.py",
+ "sha256": "2e2b29c4ddf0d21464722aa918ea6ef176418dbc6d6b246af65e829157b291c7",
+ "role": "Supplies the starting template shown by the custom-function editor."
+ },
+ {
+ "path": "openhcs/processing/custom_functions/manager.py",
+ "sha256": "77612ea5ed53888c36e8ed31f763d8eaf5e0af9e4f32b4beaaa7cff09012b130",
+ "role": "Validates and registers saved custom-function source into the function catalogue."
},
{
"path": "openhcs/processing/custom_functions/runtime_registry.py",
- "sha256": "ee935ccc09f3a967d84c9ebd08511ad729d53e73d5bceb52074b39a1cd6ead9c",
+ "sha256": "d2311617cad39d96ceb3ea6e13b6ad89f3b846e95d5dc3d692e7a9a46dd9c739",
"role": "Projects a validated custom declaration into the process-local callable catalogue."
},
{
"path": "tests/unit/test_custom_function_lifecycle.py",
- "sha256": "918326f0f530d833172b30f25a3e91c4f3fc402ea6645a515bd666a83816994d",
+ "sha256": "81926f26051d5f40eead795c1c9edf60b84b31aab9d61bc0411929328b002de9",
"role": "Proves validated custom declarations publish atomically without mirrored catalogue state."
+ },
+ {
+ "path": "tests/unit/test_function_catalog_zmq.py",
+ "sha256": "9398fd1442b6f793f5b6d273b2965ececd5888f12c37fd11b1e71731c9a9fd93",
+ "role": "Proves persisted custom functions propagate through registry discovery and the execution-server catalogue."
}
],
- "findings": [
- "The authoring guide described one undifferentiated memory declaration and omitted the decorator's independent input, output, and execution framework roles."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/user_guide/custom_functions.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/custom_functions.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py (E1 families), custom_functions/templates.py (commented artifact example imports from the K2 artifacts package), manager.py and runtime_registry.py (lifetime classes) and the two custom-function tests; the page's example imports only openhcs.core.memory.decorators.numpy and Pure2DContract, both unchanged, and the Create New Function action remains; no edit needed.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/custom_functions.rst in full and compared it against the changed authorities: processing_contracts.py (signature lookup through CallableDeclaration, K5), pyqt_gui/main.py (U3; Tools > Custom Functions > Create New Function unchanged) and tests/unit/test_custom_function_lifecycle.py (reference.declaration); the example's numpy decorator still accepts contract= with ProcessingContract subclasses (default FlexibleContract), and Pure2DContract, Pure3DContract, VolumetricToSliceContract and FlexibleContract still exist; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/custom_functions.rst against B6's later changes to openhcs/core/memory/decorators.py, openhcs/core/processing_contracts.py, openhcs/pyqt_gui/main.py, openhcs/processing/custom_functions/manager.py, tests/unit/test_custom_function_lifecycle.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/custom_functions.rst against the later B6 changes to openhcs/core/memory/decorators.py, openhcs/core/processing_contracts.py, openhcs/pyqt_gui/main.py, openhcs/processing/custom_functions/manager.py, tests/unit/test_custom_function_lifecycle.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/custom_functions.rst in full and compared it against the changed authorities: the page's own earlier contract-import edit, openhcs/core/memory/decorators.py, unified_registry.py, custom_functions/manager.py and runtime_registry.py, external/arraybridge decorators.py, and the catalogue and lifecycle tests; checked the menu path, the decorator export, and the four contract classes in openhcs/core/processing_contracts.py; corrected the contract names in the list.",
"On 2026-08-27, re-read docs/source/user_guide/custom_functions.rst in full against callable- and registry-owned form exclusions; challenged the authoring steps and confirmed that custom runtime inputs remain declaration-owned.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/memory/decorators.py; openhcs/processing/backends/lib_registry/unified_registry.py; openhcs/processing/custom_functions/manager.py; tests/unit/test_function_catalog_zmq.py; external/arraybridge/src/arraybridge/decorators.py; openhcs/processing/custom_functions/runtime_registry.py; tests/unit/test_custom_function_lifecycle.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -3743,51 +4123,53 @@
"authority": [
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares DtypeConfig with default_dtype_conversion (NATIVE_OUTPUT default) and StepMaterializationConfig, from which the lazy step forms are generated."
},
{
"path": "openhcs/core/memory/decorators.py",
- "sha256": "d9f9cf0be7593dc0ebfc57f258aebed75bcf9cfa508ed1e942137366d819766a",
- "role": "Exports OpenHCS memory and dtype decorators while delegating conversion semantics to ArrayBridge declarations."
+ "sha256": "76f234785f4865257fd19c935e0aace82d1f5586198737fec619ee243b79cbf6",
+ "role": "Exports OpenHCS memory decorators while delegating conversion semantics to ArrayBridge declarations."
},
{
"path": "external/arraybridge/src/arraybridge/decorators.py",
- "sha256": "99823105b0ae59054df1d84013633b8b6628d0a6dc3326383199ac7c65c69bed",
- "role": "Declares decorators that write independent input, output, and execution memory roles plus dtype policy."
+ "sha256": "28f9bf4770bd4cc1ef34045fdef28065063ede029296f62b8a6c4140cc88e40e",
+ "role": "Declares DtypeConversion members and applies the conversion to a decorated callable's main result."
},
{
"path": "external/arraybridge/src/arraybridge/array_operations.py",
- "sha256": "7e9ebda3d058702cdf8662dc5ab1faf1254649f3cbde76fa0adcbe02677fa103",
+ "sha256": "ea8c4e21ffbef5b5d502f9e5b7779bd5a341cec4408337d16ba89613f4305636",
"role": "Defines NumPy float-to-integer scaling and integer-to-integer casting at the callable dtype boundary."
},
{
"path": "openhcs/processing/backends/processors/numpy_processor.py",
- "sha256": "311634ec55ec8ddefe05118329d208e4b33df86f81d54ca4e258e7f4d2dbc980",
- "role": "Declares the decorated projection function used in the 8-bit materialisation example."
+ "sha256": "fddb0447b60e30902a40b63d4e60ea52c44497807ab539cb03a9dde57766de39",
+ "role": "Declares the decorated create_projection function used in the 8-bit materialisation example."
},
{
"path": "external/PolyStore/src/polystore/disk.py",
- "sha256": "cf089b8c79f86099a7cea1b6cbe43ceaec6331196a44b43183f2e64b7746c219",
+ "sha256": "58733bc3890a9062572fedccb5643a382ce69d31486d496cd7f6c1ff9ac991c6",
"role": "Writes materialised TIFF pixels at the dtype supplied by the processing result."
},
{
"path": "tests/unit/test_callable_contract.py",
- "sha256": "493f76a54f5fe024ca8f10c53b1f810b8c369626ad7141205b42ef3a78282137",
+ "sha256": "9170f8694f388dbed5bfd36d882a5c0a4cdf28f50f51f7fdeaf4a955a9f3b412",
"role": "Proves callable contract derivation for dimensions, memory types, dtype policy, and outputs."
},
{
"path": "openhcs/core/steps/abstract.py",
- "sha256": "5b58b6af32470c0d3d3b80c33c80102de16db07d4050aa59d82c81571917f36a",
- "role": "Declares the step-level lazy dtype configuration override used by the task example."
+ "sha256": "2bffdfe02664c4974797bb6cefbf7f84ebb6912fcc11e026a3f2d8b3da5262b8",
+ "role": "Declares the step-level dtype_config and step_materialization_config lazy overrides used by the examples."
}
],
- "findings": [
- "The task guide told users to change a desktop or step setting without naming the control or showing an executable override.",
- "The callable override lacked a concrete 8-bit materialisation example and warnings about NumPy scaling, integer wrapping, downstream values, and auxiliary outputs."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/dtype_conversion.rst in full and compared it against the changed authorities: openhcs/core/config.py (E1 MaterializationBackend and InputSource families; DtypeConfig and the lazy configs unchanged), processors/numpy_processor.py (K2 artifacts imports) and openhcs/core/steps/abstract.py (docstring spellings ReadPreviousStep/ReadPipelineStart); LazyDtypeConfig, LazyStepMaterializationConfig, create_projection and the step dtype_config and step_materialization_config fields used by both examples are unchanged; no edit needed.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/dtype_conversion.rst in full and compared it against the changed authorities: openhcs/core/config.py (K4 source-binding classes moved in, PartitionFilterMode family, source-metadata field dtypes; DtypeConfig, LazyDtypeConfig and LazyStepMaterializationConfig unchanged), openhcs/core/steps/abstract.py (lazy config imports consolidated) and tests/unit/test_callable_contract.py (CallableMetadata folded into CallableDeclaration, K5; dtype_config remains a config-bound runtime parameter); both code examples' imports still resolve; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/dtype_conversion.rst against B6's later changes to openhcs/core/config.py, openhcs/core/memory/decorators.py, openhcs/processing/backends/processors/numpy_processor.py, tests/unit/test_callable_contract.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/dtype_conversion.rst against the later B6 changes to openhcs/core/config.py, openhcs/core/memory/decorators.py, openhcs/processing/backends/processors/numpy_processor.py, tests/unit/test_callable_contract.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/dtype_conversion.rst in full and compared it against the changed authorities: openhcs/core/config.py, openhcs/core/memory/decorators.py, external/arraybridge decorators.py and array_operations.py, numpy_processor.py, external/PolyStore disk.py, test_callable_contract.py, and openhcs/core/steps/abstract.py; confirmed DtypeConfig.default_dtype_conversion and its NATIVE_OUTPUT default, the PRESERVE_INPUT and UINT8 members, LazyDtypeConfig and LazyStepMaterializationConfig imports from openhcs.core.config, create_projection, and step dtype_config/step_materialization_config parameters; no page change was needed.",
"On 2026-08-27, re-read docs/source/user_guide/dtype_conversion.rst in full after callable contracts began projecting runtime-owned inputs into generic form exclusions; challenged the dtype task flow and found no behaviour change.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/config.py; openhcs/core/memory/decorators.py; external/arraybridge/src/arraybridge/decorators.py; tests/unit/test_callable_contract.py; openhcs/core/steps/abstract.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -3810,40 +4192,44 @@
"authority": [
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
- "role": "Composes the main desktop window, menus, task windows, and registered UI services."
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
+ "role": "Declares the Tools > Analysis Consolidation > Run Experimental Analysis action and its directory check for the config and results files."
},
{
- "path": "openhcs/formats/experimental_analysis.py",
- "sha256": "1169fed585163a9e26376909b06675e35218ac459a11bbbe2e56e82014856a01",
- "role": "Parses experimental workbooks, assigns plate metadata, and prepares inputs for the analysis engine."
+ "path": "openhcs/processing/backends/experimental_analysis/analysis.py",
+ "sha256": "96b29239552724475c9fb010248bb266340fa0bbf06ae890f6074b8620c6183b",
+ "role": "Parses the drug_curve_map and plate_groups sheets and applies replicate-local normalization and heatmap export."
},
{
- "path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the default directory filenames and replicate-local normalization method."
+ "path": "openhcs/domains/microscopy/config.py",
+ "sha256": "79b8baefdbf22b85c199807f62b8234daca6904633a574886661a1c2e3d03a9b",
+ "role": "Declares ExperimentalAnalysisConfig's default directory filenames and the normalization_method choices."
},
{
"path": "openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py",
- "sha256": "316be268b761bfa74ed1df137f2ba014a1f1c273aa9abd598a8a2e10388afb79",
- "role": "Runs the unified experimental analysis pipeline and emits the documented summary outputs."
+ "sha256": "782f462451aec3f98fc09956492cf4882531cdc41a459ea3c2a539353ed244b9",
+ "role": "Runs the directory workflow and writes the normalized, raw, and heatmap workbooks."
},
{
"path": "tests/unit/test_experimental_layout_rows.py",
- "sha256": "d71b1e77aeb31a543e9da104a9dca2169aa7ea6c6e690a5c19302b26e76ae31b",
+ "sha256": "5ead5df21c529a3d29601e9d5eeec84e24ce0130bca0af5e114ee90793ded8d4",
"role": "Proves workbook row parsing, positional mapping, multi-digit groups, exclusions, normalization, and heatmap output."
},
{
"path": "tests/unit/test_experimental_analysis_engine.py",
- "sha256": "23b5d73def871a4c1f1e60625faf56d4664a315f90ddbddff7fa1ceab2117c17",
+ "sha256": "641fadecf8c940cc2316a06771c74db5f3f2db9dafdfbfebdc535760d6d776ec",
"role": "Runs the documented MetaXpress directory task end to end."
}
],
- "findings": [
- "The page explained layout concepts but did not carry the operator through the actual file contract, menu action, or outputs; it now provides one complete desktop task flow."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/user_guide/experimental_layouts.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/experimental_layouts.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py (E1 families), experimental_analysis/analysis.py and unified_analysis_engine.py (for_scope) and tests/unit/test_experimental_layout_rows.py; the Run Experimental Analysis action, the directory filenames, the three output workbooks and ExperimentalAnalysisConfig.normalization_method are unchanged; no edit needed.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/experimental_layouts.rst in full and compared it against the changed authority openhcs/pyqt_gui/main.py (U3: dock layout, shortcuts and time-travel navigation moved to pyqt-reactive); the Run Experimental Analysis action and its directory dialog are unchanged, and ExperimentalAnalysisConfig.normalization_method still defaults to fold change in openhcs/domains/microscopy/config.py; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/experimental_layouts.rst against B6's later changes to openhcs/pyqt_gui/main.py, openhcs/processing/backends/experimental_analysis/analysis.py, openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py, tests/unit/test_experimental_analysis_engine.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/experimental_layouts.rst against the later B6 changes to openhcs/pyqt_gui/main.py, openhcs/processing/backends/experimental_analysis/analysis.py, openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py, tests/unit/test_experimental_analysis_engine.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/experimental_layouts.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py, the deleted openhcs/formats/experimental_analysis.py (moved to processing/backends/experimental_analysis/analysis.py), openhcs/core/config.py, unified_analysis_engine.py, and both experimental-analysis tests; checked the menu path, the five default filenames, normalization_method, and that variable_components and group_by remain step-config fields; no page change was needed.",
"Re-read docs/source/user_guide/experimental_layouts.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/pyqt_gui/main.py; openhcs/formats/experimental_analysis.py; openhcs/core/config.py; openhcs/processing/backends/experimental_analysis/unified_analysis_engine.py; tests/unit/test_experimental_layout_rows.py; tests/unit/test_experimental_analysis_engine.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
@@ -3868,30 +4254,39 @@
"authority": [
{
"path": "openhcs/pyqt_gui/main.py",
- "sha256": "a156a006a3c40f43107b9b1f6ecb97ed150879eb88ded8ef89e25214309ae33a",
- "role": "Composes the main desktop window, menus, task windows, and registered UI services."
+ "sha256": "817acfd0e44060440d57a2ada0ba75545dfa8dde009363143b2b86e831bd1dd7",
+ "role": "Composes the PyQt desktop main window that the openhcs command launches."
},
{
"path": "tests/unit/pyqt_gui/test_ui_config_lifecycle.py",
- "sha256": "eeb7c260d3ae8bc7b5e6d94917b1e5e9a214feac9e2a0c2651552950cfe3a179",
- "role": "Prevents the removed Textual package authority and local UI mirrors from returning."
+ "sha256": "78c9d42d91a384d0b9bce1139dfcd171a2d87a8781be4c078e2f9703bf302043",
+ "role": "Prevents the removed Textual package and local UI mirrors from returning."
},
{
"path": "openhcs/core/pipeline_document.py",
- "sha256": "0849800b3e6474b8176fe35bc4aaebc63cfa85bd77251000c50da5f4c928b1b2",
- "role": "Declares the public pipeline document, step collection, and serialisable declaration boundary."
+ "sha256": "9a77a08367f901594859c8b6cae9609c9230a551f4f4e9ec6ea6cd2ed233c0f4",
+ "role": "Declares the pipeline document as pipeline_steps plus PipelineConfig."
+ },
+ {
+ "path": "openhcs/core/config.py",
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares the nested processing, materialization, and streaming configuration dataclasses on PipelineConfig and steps."
},
{
"path": "openhcs/core/pipeline/compiler.py",
- "sha256": "ace8ae0c3e54bbe604b97cec7c3973a1abcf90e1dc517c2adda947dc2c35e882",
- "role": "Compiles pipeline declarations into validated step plans and execution bundles."
+ "sha256": "40b07b7f40aca39b19b95a8f61c1b0c8270f0ab0a29e8c36caa13f04d8fa539f",
+ "role": "Compiles pipeline declarations into typed step plans and a CompiledExecutionBundle before execution."
}
],
- "findings": [
- "The guide index still described the removed Textual interface as deprecated rather than removed."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), openhcs/pyqt_gui/main.py changed by black formatting only (AST-equal) after docs/source/user_guide/index.rst was reviewed; no claim changes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/index.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py (E1 families), openhcs/core/config.py (MaterializationBackend and InputSource families) and openhcs/core/pipeline/compiler.py (OrchestratorCompiled/OrchestratorCompileFailed state classes, MaterializeToZarr, PrimaryPlaneRole); the declaration model (list[FunctionStep] plus PipelineConfig, nested configs, compilation into typed plans and a bundle) is unchanged and every toctree page exists; no edit needed.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/index.rst in full and compared it against the changed authorities: pyqt_gui/main.py (U3 dock/shortcut/time-travel machinery moved to pyqt-reactive), tests/unit/pyqt_gui/test_ui_config_lifecycle.py (shortcut lifecycle tests moved with it; the textual_tui non-recurrence check remains), pipeline_document.py (generate_python_source), core/config.py (PartitionFilterMode family, K4 source-binding classes) and compiler.py (ScopeKey scope ids, K6 progress phase/status classes; still returns CompiledExecutionBundle); the PyQt-only interface, toctree and declaration-model claims remain accurate; no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/index.rst against B6's later changes to openhcs/pyqt_gui/main.py, openhcs/core/config.py, openhcs/core/pipeline/compiler.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/index.rst against the later B6 changes to openhcs/pyqt_gui/main.py, openhcs/core/config.py, openhcs/core/pipeline/compiler.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/index.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/main.py, test_ui_config_lifecycle.py, pipeline_document.py, and openhcs/core/pipeline/compiler.py; confirmed the Textual interface is still absent, the declaration is pipeline_steps plus PipelineConfig, nested step configuration still owns processing, input source, materialization and streaming options, and compilation still returns a CompiledExecutionBundle; all toctree targets exist; added openhcs/core/config.py for the nested-configuration claim; no page change was needed.",
"Re-read docs/source/user_guide/index.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/pyqt_gui/main.py; tests/unit/pyqt_gui/test_ui_config_lifecycle.py; openhcs/core/pipeline_document.py; openhcs/core/pipeline/compiler.py.",
"On 2026-08-22, corrected the interface status after the deprecated Textual implementation and its local UI mirrors were removed.",
@@ -3919,7 +4314,7 @@
{
"path": "external/pyqt-reactive/src/pyqt_reactive/widgets/log_viewer.py",
"sha256": "e68eda879c7cd3f5360dd1b4a08a481298cabf85cd72bdfda66b0efbd54d56f9",
- "role": "Implements log discovery, live-process filtering, record filtering, and viewer selection behaviour."
+ "role": "Implements log discovery, live-process filtering, level and source filtering, and log selection."
},
{
"path": "external/pyqt-reactive/src/pyqt_reactive/services/coalesced_scan_requests.py",
@@ -3928,30 +4323,33 @@
},
{
"path": "openhcs/pyqt_gui/services/reactor_providers.py",
- "sha256": "e937c310e6dd7f06bf8e2cfc227ce442310f6d249fc0abed4ed0e588b4121fe8",
- "role": "Registers the log, process, status, and execution reactors consumed by desktop widgets."
+ "sha256": "ca5fe97ba932805dd0f8cfa3bc85352cb428b12a46fcae83751382092d77475c",
+ "role": "Registers the OpenHCS log-discovery and server-scan providers that supply the Log Viewer's log files and live server list."
},
{
"path": "openhcs/pyqt_gui/config.py",
- "sha256": "a3eece2e5b2926fc6fc0edd197221da451ab7c0d0d6f4ffc8bb2ff865ad74bd4",
- "role": "Declares UI configuration, including logging, theming, viewer, and execution-server settings."
+ "sha256": "d092cb20206127484fbbc80a38a5152462b9b8991e848a814c85f564d51f4db8",
+ "role": "Declares UI configuration, including logging and execution-server settings."
},
{
"path": "openhcs/runtime/zmq_execution_client.py",
- "sha256": "31081f2a323a81c060aede1201a4bf1a1b39a51f37928c281a419ffaceadfe60",
- "role": "Implements execution-server connection, request submission, progress consumption, and connection-state reporting."
+ "sha256": "ad7ea01599493dbefce1f3c91e6b082e932c6a5183d0a85a186f7bd7b62779a9",
+ "role": "Spawns the execution server with its own per-port log file and startup status file and reports connection state."
},
{
"path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
- "role": "Owns the documentation-enabled execution-log capture, caption, accessibility text, dimensions, and source evidence."
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
+ "role": "Owns the execution-log-viewer capture, caption, accessibility text, and dimensions."
}
],
- "findings": [
- "The selector scanned only during construction, so an execution server that started later could never expose its dedicated live log; visible non-overlapping discovery now updates the catalogue only when it changes."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/log_viewer.rst in full and compared it against the changed authorities: pyqt_gui/services/reactor_providers.py (OrchestratorCreated state class), openhcs/pyqt_gui/config.py (AgentRuntimePlatform rename, GuiLogLevel.emits_records) and openhcs/runtime/zmq_execution_client.py (artifact inspection request sent directly); none touches log discovery, per-port server logs, level and source filtering or live server scanning; no edit needed.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/log_viewer.rst in full and compared it against the changed authorities: reactor_providers.py (codegen provider now calls pycodify.generate_python_source; external-editor env var) and zmq_execution_client.py (debug requests sent directly via request.to_dict(), K6 DebugCommandType class); neither touches log discovery, per-port server logs or live server scanning; confirmed the execution-log-viewer gallery scenario and the pipeline_debugging_guide link still exist; corrected the reactor_providers role.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/log_viewer.rst against B6's later changes to openhcs/pyqt_gui/services/reactor_providers.py, openhcs/runtime/zmq_execution_client.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/log_viewer.rst against the later B6 changes to openhcs/pyqt_gui/services/reactor_providers.py, openhcs/runtime/zmq_execution_client.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/log_viewer.rst in full and compared it against the changed authorities: openhcs/pyqt_gui/services/reactor_providers.py, openhcs/runtime/zmq_execution_client.py, and scripts/gallery_catalog.py; the reactor changes (axis-family grouping, session scope) and the execution server's per-port log and startup-status files are consistent with the page's claims that subprocesses write dedicated logs reported by the launching process and that readiness is recorded separately; no page change was needed.",
"Reviewed docs/source/user_guide/log_viewer.rst in full; the recorded findings and disposition are the resulting page-specific editorial decision.",
"Compared its factual claims directly with the hash-pinned files listed in authority: external/pyqt-reactive/src/pyqt_reactive/widgets/log_viewer.py; external/pyqt-reactive/src/pyqt_reactive/services/coalesced_scan_requests.py; openhcs/pyqt_gui/services/reactor_providers.py; openhcs/pyqt_gui/config.py; openhcs/runtime/zmq_execution_client.py.",
"Passed `.venv/bin/python scripts/validate_docs.py docs/source/user_guide`: documentation validation passed: 12 files, 2 Python blocks.",
@@ -3970,7 +4368,7 @@
},
{
"path": "docs/source/user_guide/mcp_clients.rst",
- "source_sha256": "f47e4de0a9e8653155f47898627953ad7635c2b72e05805483a72bb6c85f0e26",
+ "source_sha256": "2c38bbbf07310596c6cc95a102909162dbcb13f53f5b045e7fb2d025e31da46e",
"audience": [
"desktop users connecting local agent clients",
"MCP administrators"
@@ -3980,87 +4378,99 @@
"authority": [
{
"path": "openhcs/mcp/bootstrap.py",
- "sha256": "64b2ef9a4f4fe50154b4c921a7617b2e7c9dcf8bb475df7423e37e8395a8b250",
- "role": "Builds the installed MCP server, registers its tools and resources, and starts the selected transport."
+ "sha256": "0e8e94cf54a32a1772e52a77d1c89a9ee02c6d2a58eb04f6e18fad2ce79d0394",
+ "role": "Declares the openhcs-mcp --surface option with the desktop default and starts the stdio transport."
},
{
- "path": "openhcs/mcp/client_registration.py",
- "sha256": "28459b95d3b155778ce178f36302cb830958b74f2603980641ec492d81c41a9a",
- "role": "Owns discovery and registration of the OpenHCS MCP server in supported desktop clients."
+ "path": "openhcs/agent/capabilities.py",
+ "sha256": "87c8e2e5aeb425b9a2eb690d48bdd6f5af7a327c389deac261d7e4e0da5d609f",
+ "role": "Declares the full, desktop, authoring, and core surface profiles and derives the openhcs_ session tools from headless session operations."
},
{
- "path": "openhcs/mcp/lifecycle.py",
- "sha256": "671859916b91d5e2f4ce9c5cd329c6d088800d3b8282e0d9505c49c183c7c68b",
- "role": "Owns MCP process lifecycle, endpoint readiness, and shutdown coordination."
+ "path": "openhcs/authoring/session/operations/datasets.py",
+ "sha256": "630a74c9e4fb026a7c47e78b3a07427c4d16c9630447a523b36dd8c73e17e430",
+ "role": "Declares the add, set-pipeline, initialize, compile, run, and stop dataset operations behind the session tools."
},
{
- "path": "openhcs/agent/capabilities.py",
- "sha256": "7fe9ab91490dedd6f750a57386e91835cf1067a27d10079966510aa9821adaf0",
- "role": "Declares the agent capability catalogue and safety metadata exposed through MCP self-description."
+ "path": "openhcs/mcp/client_registration.py",
+ "sha256": "4d6fc5f744617ab1d0e9b8317d07965496aaa6b098a4d179ef95cc6baec35671",
+ "role": "Owns detection and registration of the stable launcher (and skill sync) in supported desktop clients."
},
{
- "path": "packaging/mcpb/openhcs/manifest.json",
- "sha256": "a51d5f32ad56d1ea22cf338b5b4eed1e343b7bbb62d9a1acde0ce5cf1fab20e5",
- "role": "Declares the packaged MCP server command, arguments, compatibility, and client-visible metadata."
+ "path": "openhcs/mcp/lifecycle.py",
+ "sha256": "4c2fb453798183bbc653b3ad1bf6d3e0ee0cf54383b6672cb745a325c92e6c3a",
+ "role": "Owns MCP process lifecycle, endpoint readiness, and shutdown coordination."
},
{
- "path": "tests/unit/agent/test_mcp_client_registration.py",
- "sha256": "0763752fc04c32d2812f509845886f10c366e1a67ec290d50e433096a8d4debe",
- "role": "Proves supported-client discovery, registration commands, and resulting MCP configuration."
+ "path": "openhcs/agent/path_policy.py",
+ "sha256": "cd01a3a0ab5e3c4a4e05b212f3df3d8c849586e80c6bc221532c1fbce76d8282",
+ "role": "Owns OPENHCS_AGENT_READ_ROOTS and OPENHCS_AGENT_WRITE_ROOTS and rejection of paths outside them."
},
{
- "path": "tests/unit/agent/test_mcp_server.py",
- "sha256": "b99b6ca27be471f95ddf0852f8f94ce02797a459bf98bf6e0fa4851c0bb8c698",
- "role": "Proves MCP server tool registration, request handling, and lifecycle behaviour."
+ "path": "packaging/mcpb/openhcs/manifest.json",
+ "sha256": "c4899e221db575ada4dc291c5e40c6b2c7ab52913177186b9546b061e56c6759",
+ "role": "Declares the packaged Claude Desktop server command, path-policy inputs, and client-visible metadata."
},
{
- "path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
- "role": "Owns the local-MCP endpoint-startup scenario, representative poster, caption, description, alt text, dimensions, and release evidence."
+ "path": "packaging/codex/openhcs/.codex-plugin/plugin.json",
+ "sha256": "56e6f69d55609d1f2c4e92d2ba758e71d37c99181d19e7b51d7abaa5c8168fd4",
+ "role": "Declares the Codex release plugin that ships the use-openhcs skill."
},
{
"path": "benchmark/cellprofiler_benchmark_cli.py",
- "sha256": "2b92755c556c1d66d28ffa0af6e2ac61cac5af4631514382290a2af0f907c26b",
- "role": "Owns the installed benchmark execution and report command surface."
+ "sha256": "56a9b4e8bb7d8195095eb99a849f717e79ca37aa429c612804584cbd7939f420",
+ "role": "Declares the list-cases, inspect-run, inspect-measured, and run-measured commands, including the required --wait-timeout-ms."
},
{
"path": "benchmark/contracts/run_receipt.py",
- "sha256": "1704e1ab47411d25d382b95871c96c1e60c94db14b8ffd55f4dc6c8a917758b9",
+ "sha256": "4cd61bd33f8f6199b3cc4130ed091dddde9c3a23975e132b1bee06c3c6a43cd7",
"role": "Owns typed lifecycle status, rerun provenance, and receipt validation."
},
{
"path": "benchmark/contracts/control.py",
- "sha256": "0bce24c29ea9dfb7b50c88937b616118078505db6f497312fff87766397e5ce9",
- "role": "Declares the expert MCP benchmark inspection request and result."
+ "sha256": "ce9c4a1146723e445abc7c9b54eff9e8808e115413cd2842efc742fd1a6b300e",
+ "role": "Declares the expert MCP benchmark inspection and measured-run finalization requests and results."
},
{
"path": "benchmark/control.py",
- "sha256": "4706078a8fbeb516a52bc51600751ae86c39c3ef7e397a2f8cf13d49204db941",
+ "sha256": "b10dd87d7bc44063566e0c53d849091c0fc43a345b434c13467df1ede86ac892",
"role": "Reads authorized existing run directories without invoking benchmark execution."
},
{
"path": "benchmark/control_service.py",
- "sha256": "bf2b39696a49d8c87cf74d39770d63013972b9c8cf884863c467ccdf46eb12d3",
- "role": "Requires manifest and every declared benchmark source location to satisfy the agent read policy."
+ "sha256": "a1256b349ffdc8d04cab927326d97821462b61f57b29e564b61cc627302be49c",
+ "role": "Requires the manifest and every declared benchmark source location to satisfy the agent read policy."
},
{
- "path": "openhcs/agent/path_policy.py",
- "sha256": "6ee0dc362b52d147586e9e61ea89499f5b35355ff697a2bfe5bbcc33febc45ba",
- "role": "Owns explicit MCP readable roots and rejection of paths outside them."
+ "path": "scripts/gallery_catalog.py",
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
+ "role": "Owns the zmq-startup-compile gallery scenario, poster, caption, and alt text."
+ },
+ {
+ "path": "tests/unit/agent/test_mcp_client_registration.py",
+ "sha256": "2b40d0188317828d6198e0787bb6560b490238bcc2c63a056fe54ac8fd37f8f0",
+ "role": "Proves supported-client discovery, registration commands, and resulting MCP configuration."
+ },
+ {
+ "path": "tests/unit/agent/test_mcp_server.py",
+ "sha256": "7f47844b6fc0bf2461dfdb97e4b622a8d16f0f8c00004c1e6b7ea1a9997abe82",
+ "role": "Proves MCP server tool registration, request handling, and lifecycle behaviour."
},
{
"path": "tests/unit/test_benchmark_control.py",
- "sha256": "ee75209799216c90cfa3a585dd9b7af99c99405f93e9fb5e4f900ea26d9d3ea0",
- "role": "Proves full-surface capability binding, enum projection, path policy, progress, artifacts, and lightweight imports."
+ "sha256": "093b584eeb37cbb12089d5af082ad45acd779e9d2bdd5b3feaea08234fc22183",
+ "role": "Proves benchmark capability binding, path policy, progress, artifacts, and lightweight imports."
}
],
- "findings": [
- "The desktop MCP profile description excluded headless execution; it now documents the declaration-owned headless group and distinguishes independent sessions from UI-owned state.",
- "The client how-to did not explain that benchmark inspection is expert-only and read-only, or direct users to the separately installed benchmark execution command.",
- "The measured-run inspection instructions now distinguish retained_evidence_valid from separate image and table output validation."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/mcp_clients.rst against openhcs/authoring/session/operations/datasets.py, benchmark/cellprofiler_benchmark_cli.py: the export scope crosses the request boundary as its enum view and reaches the session as the family, the GUI smoke reads the action-status family, and the unit job runs as three serial groups. No claim this page makes changes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/mcp_clients.rst in full and compared it against the changed authorities: openhcs/mcp/client_registration.py (registration statuses and platform keys became class families), openhcs/mcp/lifecycle.py (McpProcessRecoveryReason classes), openhcs/agent/path_policy.py, benchmark/cellprofiler_benchmark_cli.py (observation-scope choices read from the ZMQRuntimeObservationExportScope registry), benchmark/control.py and the two agent tests; the --observation-scope spellings outcomes and values (default values) are still the registered names, --wait-timeout-ms is still required, the dataset row still reports complete (ExecutionCompleted.name), and client registration and restart behaviour are unchanged; no edit needed. The --surface help text in openhcs/mcp/bootstrap.py still says the desktop surface hides headless tools although the desktop profile includes them (reported earlier, code not edited).",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/mcp_clients.rst against B6's later changes to benchmark/cellprofiler_benchmark_cli.py: the session decides in-place initialization by an actual file-creation attempt that leaves nothing behind, its debug-session methods moved into an inherited mixin, the measured CLI treats Windows Ctrl+Break like Ctrl+C, and the Official30 viewer test reads typed payload summaries. No claim this page makes changes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/mcp_clients.rst against B6's later changes to openhcs/authoring/session/operations/datasets.py, benchmark/cellprofiler_benchmark_cli.py, benchmark/control_service.py, tests/unit/agent/test_mcp_server.py, tests/unit/test_benchmark_control.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/mcp_clients.rst against the later B6 changes to openhcs/agent/capabilities.py, openhcs/authoring/session/operations/datasets.py, benchmark/cellprofiler_benchmark_cli.py, benchmark/control_service.py, tests/unit/agent/test_mcp_server.py, tests/unit/test_benchmark_control.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/mcp_clients.rst in full and compared it against the changed authorities: the page's own session-tool edit, openhcs/mcp/bootstrap.py, client_registration.py, openhcs/agent/capabilities.py, the mcpb manifest, test_mcp_server.py, gallery_catalog.py, the benchmark CLI, contracts, control and control_service, openhcs/agent/path_policy.py, and test_benchmark_control.py; confirmed every named session tool derives from a declared headless operation, the four surface names and desktop default, the benchmark subcommands and flags, the path-policy variables, and the packaging paths; restored the --wait-timeout-ms requirement and renamed the window.",
"Reviewed docs/source/user_guide/mcp_clients.rst in full; the recorded findings and disposition are the resulting page-specific editorial decision.",
"Compared its factual claims directly with the hash-pinned files listed in authority: openhcs/mcp/bootstrap.py; openhcs/mcp/client_registration.py; openhcs/mcp/lifecycle.py; openhcs/agent/capabilities.py; packaging/mcpb/openhcs/manifest.json; tests/unit/agent/test_mcp_client_registration.py; tests/unit/agent/test_mcp_server.py.",
"Passed `.venv/bin/python scripts/validate_docs.py docs/source/user_guide`: documentation validation passed: 12 files, 2 Python blocks.",
@@ -4104,7 +4514,7 @@
},
{
"path": "docs/source/user_guide/real_time_visualization.rst",
- "source_sha256": "c269dd225183533dbd30237661eb2f8926c9f42b46082023e457a6c9232f7e3d",
+ "source_sha256": "df6ba5fcf3861d895b456d2cfa47b7fe6670e2696f3c60e0c5d8fb49e5ba01d5",
"audience": [
"pipeline authors reviewing live scientific outputs"
],
@@ -4113,67 +4523,88 @@
"authority": [
{
"path": "openhcs/core/config.py",
- "sha256": "ef81b50ee22b8da69513fd50d6e45a929117fa4bfaa62a20ef08a4c02531c043",
- "role": "Declares the supported OpenHCS configuration dataclasses, nested scopes, defaults, and field help metadata."
+ "sha256": "9625ca054fac6c1e460216cfe0e85621f5306592a004d1bdc28a6b1ce2099366",
+ "role": "Declares StreamingDefaults (persistent, host, transport_mode, enabled), partition_filter, and the Napari and Fiji streaming configs with their ports."
+ },
+ {
+ "path": "openhcs/core/steps/abstract.py",
+ "sha256": "2bffdfe02664c4974797bb6cefbf7f84ebb6912fcc11e026a3f2d8b3da5262b8",
+ "role": "Declares the per-step napari_streaming_config, fiji_streaming_config, and step_materialization_config fields."
+ },
+ {
+ "path": "openhcs/runtime/viewer_display.py",
+ "sha256": "6af5ae4f4580a69ac2d55cc3d036aa7040173b51d86959959ccdccea7899f866",
+ "role": "Declares the Napari and Fiji display settings selectable on a streaming config."
+ },
+ {
+ "path": "pyproject.toml",
+ "sha256": "75f92010b3c18ede61cf7296064079590c143283871e372a1fd4546417d4e0f9",
+ "role": "Declares the gui, napari, fiji, and viz extras."
},
{
"path": "openhcs/runtime/viewer_protocol.py",
- "sha256": "30441eab97da9ffcb5b9dc33d65c2ecc56c426e79bb2b27eedc78a68aaf13bc3",
- "role": "Declares the transport-independent viewer messages, stream configuration, and server contract."
+ "sha256": "347a65b63c149f2f437014d537cfaf481a33801d860742f4083b7377494e792f",
+ "role": "Declares the transport-independent viewer messages and incremental settlement-progress fields."
},
{
"path": "openhcs/runtime/viewer_controls.py",
- "sha256": "3227e6bf60e9b43fd792cc4299b6b50721741f012a0f7f856268b35022fd6062",
- "role": "Declares the bounded payload and atomic layer-isolation controls used by focused agent review."
+ "sha256": "df0ac0aee66ba52a8b1706a1a99e03acc1300cfaf5cc74c2f93614a1c77465f6",
+ "role": "Declares bounded viewer-state payload limits and atomic layer-isolation controls."
+ },
+ {
+ "path": "openhcs/agent/dto/viewer.py",
+ "sha256": "24435279b0d31bfd62ea8c29c07cbadc564a3427fd2b06c5e4abf20992f27f12",
+ "role": "Declares the ROI-summary request with its max_rois bound and the geometry-free ROI summary records."
},
{
"path": "openhcs/agent/services/viewer_window_service.py",
- "sha256": "257718ca7da768132be2283326730f65d9b399f0ef852b2c7e1e9e48263f9eed",
+ "sha256": "cfe222586a221cb317099fc245216778f2ce15589604e5ce03410998dec4bb1a",
"role": "Projects viewer responses into bounded ROI summaries and one-operation isolation results."
},
{
"path": "openhcs/runtime/napari_viewer_server.py",
- "sha256": "64ccfef65a10e04878ef0806ae72d0446540efcbfc72af46c4e2ab785418a84c",
- "role": "Implements Napari stream reception, layer updates, ROI navigation, and endpoint lifecycle."
+ "sha256": "ac54f8bfdce33150c31b38bcd5cf6069b5fd66fb7c05d5a6951610ec3d895bfd",
+ "role": "Implements Napari stream reception, layer updates, selected-layer routing, and the dimension-label overlay."
+ },
+ {
+ "path": "openhcs/runtime/napari_streaming_handlers.py",
+ "sha256": "b43f29163cef420c77f30e46e92d94145520f7534077d54636a0ab141da32e7e",
+ "role": "Owns shared semantic axis slots, singleton alignment, and per-route forward-progress records for Napari layers."
},
{
"path": "openhcs/runtime/fiji_viewer_server.py",
- "sha256": "cde38de711dced10b8af10b3b0ca69b1111c234a2877f0aade1e762ccbb6777b",
+ "sha256": "352ef6b773da07b8103873606cf8a438b8ec70b64cfc57526cc9b18cf9b48b40",
"role": "Implements Fiji stream reception, image-stack updates, ROI handling, and endpoint lifecycle."
},
{
"path": "tests/unit/test_napari_streaming_handlers.py",
- "sha256": "a338d6f2ef3094fa429f3d640709c7231eb741a7bf2859318bf4bbfa196afc07",
+ "sha256": "c08363837db3efd13534c966a6192b785cb85ccca742b574ac9bd0e74600b80e",
"role": "Proves Napari streaming updates layers and carries ROI navigation through the declared protocol."
},
{
"path": "tests/unit/test_viewer_result_element_selection.py",
- "sha256": "58480f4da61b427ec1c55560a77686e74df70404fbbf77e701ba386ad9f89a03",
+ "sha256": "b1dae580f0291f53be9453df6f6c6df5458046bd3ff947b550f7ddea94b29f96",
"role": "Proves focused layer isolation is rejected before mutation when any route or axis is invalid."
},
{
"path": "tests/unit/test_fiji_viewer_server.py",
- "sha256": "2d590f629243eec979531b0959a1006690befbd17b9778ec9f4fbefea72877f3",
+ "sha256": "b19316b843cc53c21bd3e00b22694fb645dcba062610295adf54abed7b4f51df",
"role": "Proves Fiji endpoint lifecycle, streamed image updates, and ROI message handling."
},
- {
- "path": "openhcs/runtime/napari_streaming_handlers.py",
- "sha256": "86f4ab319c7c68b626e7e2a1adef81eb3d7244f044b5b3e4dddd78bbee2aa4b6",
- "role": "Owns shared semantic axis slots and route-local singleton alignment for Napari layers."
- },
{
"path": "scripts/gallery_catalog.py",
- "sha256": "7f493e0cb305e14ee4cf7d3e995c67df4200186a8a8aaf00eef0bc66d8d191cf",
- "role": "Owns the Napari ROI-navigation and Fiji ROI-review scenarios, representative images, captions, descriptions, alt text, dimensions, and release evidence."
+ "sha256": "7fd3fce2b8fdae1abf049bb4f8a3a310ab598a7f2c507578425be537903ccfca",
+ "role": "Owns the Napari ROI-navigation and Fiji ROI-review scenarios, captions, and alt text."
}
],
- "findings": [
- "The task guide did not explain how Napari preserves declaration-owned axis sizes across full and component-reduced step outputs.",
- "The task guide did not explain the selected-route dimension-label invariant or how to recognise stale overlay state.",
- "The task guide did not identify bounded ROI metadata summaries and one-operation layer isolation as the focused agent-review path."
- ],
- "disposition": "revise",
+ "findings": [],
+ "disposition": "keep",
"validation": [
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/real_time_visualization.rst in full and compared it against the changed authorities: openhcs/core/config.py and steps/abstract.py (E1 families), openhcs/runtime/viewer_display.py (NapariVariableSizeHandling became SeparateLayers/PadToMaxSize classes that own routing and padding), pyproject.toml, viewer_protocol.py (ViewerSettlePhase, launch-session and lifecycle classes; settle progress parsed as a WireView), napari_viewer_server.py, napari_streaming_handlers.py, fiji_viewer_server.py and their tests; settlement still reports completed/total progress and fails on a failed phase, the dimension-label overlay still follows the selected OpenHCS layer and clears for a non-OpenHCS layer, the per-step streaming fields, partition_filter and persistent are unchanged, and the gui, napari, fiji and viz extras are declared; no edit needed.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/real_time_visualization.rst in full and compared it against the changed authorities: openhcs/core/config.py (PartitionFilterMode became a subclass family, IncludePartitions/ExcludePartitions; partition_filter, persistent and the streaming configs unchanged; K4 source-binding classes moved in), openhcs/core/steps/abstract.py (lazy-config imports consolidated; napari_streaming_config, fiji_streaming_config and step_materialization_config still declared) and pyproject.toml (dependency floors only; gui, napari, fiji and viz extras unchanged); no edit needed.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/real_time_visualization.rst against B6's later changes to openhcs/core/config.py, openhcs/runtime/viewer_protocol.py, openhcs/runtime/fiji_viewer_server.py, tests/unit/test_napari_streaming_handlers.py, tests/unit/test_viewer_result_element_selection.py, tests/unit/test_fiji_viewer_server.py: black formatting (AST-equal) and the ratchet refactor (method groups moved into inherited mixins, unused main-window theme methods deleted, catalog policy moved to module functions); none changes a claim this page makes.",
+ "On 2026-10-11 (B6 final), compared docs/source/user_guide/real_time_visualization.rst against the later B6 changes to openhcs/core/config.py, pyproject.toml, openhcs/runtime/viewer_protocol.py, openhcs/runtime/fiji_viewer_server.py, tests/unit/test_napari_streaming_handlers.py, tests/unit/test_viewer_result_element_selection.py, tests/unit/test_fiji_viewer_server.py: Python changes that are not formatting-only (AST-unequal) were read (dependency floors, mahotas moved to core, startup-owner record through the held lock, OMERO is_dir, catalog exclusion by declared distribution, source-path filter matching, GUI catalog readiness, MCP application release); none changes a claim this page makes.",
+ "On 2026-10-11 (B6), re-read docs/source/user_guide/real_time_visualization.rst in full and compared it against the changed authorities: the page's own partition_filter edit, openhcs/core/config.py, viewer_protocol.py, viewer_controls.py, viewer_window_service.py, napari_viewer_server.py, fiji_viewer_server.py, napari_streaming_handlers.py, the three viewer tests, and gallery_catalog.py; confirmed the extras, the per-step streaming fields, persistent and partition_filter, max_rois, the selected-layer overlay controller, and forward-progress settlement; corrected the step-3 option list.",
"Re-read docs/source/user_guide/real_time_visualization.rst in full against repository base bb4218fae and the current shared worktree; challenged its how-to user need for accuracy, freshness, duplication, owner boundaries, flow, and navigation.",
"Compared every factual claim directly with the current hash-pinned authorities: openhcs/core/config.py; openhcs/runtime/viewer_protocol.py; openhcs/runtime/napari_viewer_server.py; openhcs/runtime/fiji_viewer_server.py; tests/unit/test_napari_streaming_handlers.py; tests/unit/test_fiji_viewer_server.py; openhcs/runtime/napari_streaming_handlers.py.",
"Focused cross-lattice QA passed 179 tests spanning callable contracts, framework-device scope, custom functions, catalogue discovery, installer/update policy, MCP release, and packaged OMERO deployment.",
diff --git a/docs/refactor/consolidation-20261009/B6-ci-green.md b/docs/refactor/consolidation-20261009/B6-ci-green.md
new file mode 100644
index 0000000000..301fca9327
--- /dev/null
+++ b/docs/refactor/consolidation-20261009/B6-ci-green.md
@@ -0,0 +1,130 @@
+# B6: main's CI green
+
+**Head audited:** `openhcs` `main` at `84e7a2904` (#1193). **Rules:** [00-RULES.md](00-RULES.md). **Input:** the last completed runs on main: Integration Tests 38116770859 (`b468e7359`), Website 38110269836 and Documentation 38116770933. Integration Tests last passed on main at `fc1f94632` (2026-09-24). Documentation had not passed in the last 1,000 main runs.
+
+## What was wrong
+
+**Three workflows were red on main because of about twenty independent causes.** Most of them only CI exposes: the installed wheel with `--import-mode=importlib`, Windows, Intel macOS, xcb under Xvfb, path filters, and jobs nobody watched. The table gives each cause, its fix, and its certificate where something was deleted.
+
+| Job(s) | First cause | Fix |
+|---|---|---|
+| Website | The job installed a five-package roster, then imported `openhcs`, whose import graph needs numba. The `paths:` filter only listed `openhcs/__init__.py`. | Install the candidate through `install_ci_candidate` with the `dev` extra, because `pytest.ini`'s addopts need pytest-xdist. Trigger on `openhcs/**` and `external/**`. |
+| Documentation (`validate_docs`) | 950 findings: 141 of 155 pages had stale editorial audits (828 changed and 78 deleted authorities), plus 10 false "missing path" findings. | The path regex now matches whole repository paths and covers `packaging/`. All 141 pages were re-read against the diff of each stale authority since its reviewed version, wrong claims were corrected (see the commit), and digests were pinned after review. B6's own later edits were compared and re-pinned. |
+| Documentation (sphinx `-W`) | PolyStore and pyqt-reactive had agent run logs outside every toctree. zmqruntime's API page listed members removed by L3. | Deleted the logs (**contract**: Q4, validation logs nothing links to). zmqruntime's page derives its members with plain `:members:`. |
+| unit-tests (collection) | 14 test modules imported a sibling test module by bare name, which fails under importlib mode. | Import through the `tests` package; guard in `test_ci_import_guards.py`. |
+| unit-tests (abort, exit 134) | An in-process MCP transport created a `QCoreApplication`, so a later `QWidget` aborted. | The session creates its `QApplication` first (`tests/conftest.py`). |
+| code-quality | 412 unformatted files since the last success. A test used Python 3.12-only f-string quote reuse, which is a syntax error on 3.11. | Black over this PR's changed files; quote fixed. Main's baseline becomes this PR's green head. |
+| structural ratchet | Black wrapping and B6 fixes grew six classes past their budget. | Cohesive method groups moved into inherited mixins: `MainOutputSliceProjectionMixin`, `FunctionCoreDebugMixin`, `CustomFunctionSourceFilesMixin`, `SessionDebugMixin`. Catalog policy moved to module functions. Four unused main-window theme methods and `SourceImageProvenance.source_component_identity` were deleted (**unused**: no caller, menu, bridge or string reference). |
+| Windows (every ZMQ job) | `tests/conftest.py` used `os.getpgid`. zmqruntime's `startup_owner` reopened the lock file that portalocker holds (mandatory locks give PermissionError). | Process-group reaping only on POSIX. zmqruntime reads and writes the owner record through the held handle (0.6.2). |
+| Windows | NumPy exports uint32 as format `'L'`; custom-function sources were written in text mode, which changes their hashes; SIGINT can't be sent to a child; chmod doesn't make a directory read-only and `os.access` ignores ACLs; autocrlf rewrote checksummed `.cppipe` files; packaged knowledge paths were longer than MAX_PATH. | Native granularity accepts both formats. Sources are persisted as exact bytes. The CLI treats Ctrl+Break as Ctrl+C. Writability is decided by an actual file-creation attempt, and the test denies writes with an ACL. `.gitattributes` keeps committed bytes (`* -text`). Recipes are placed by case name within a declared path budget. |
+| Intel macOS | `openhcs-basicpy` is excluded by marker, but the catalog imported its module. The kernel cache compiled serially because macOS has no `sched_getaffinity`. | Modules declare `REQUIRED_DISTRIBUTIONS`, and the catalog excludes those whose package markers leave them out. Workers come from `os.cpu_count()` where affinity is unavailable. |
+| python-314 | ObjectState read a class's own annotations through `__dict__`, which 3.14 no longer populates. mahotas sat only in an extra, but core catalog modules need it. | ObjectState uses `inspect.get_annotations` (2.1.1). mahotas is a core dependency. |
+| gui-tests (13) | Stale tests after U1/U5. | Rewritten against the session-backed plate manager and the current stubs. |
+| backend-microscope, boundary, OMERO | STEP_INPUT filters matched virtual names instead of stored source paths. Passthrough outputs dropped their producer's filename qualifier. BaSiC read `.metadata` off plain arrays. `OMEROLocalBackend` had no `is_dir`. The metadata document was written to local disk for a virtual backend. Viewer tests used `ViewerType` members. The GUI journey test ran in headless jobs. Several integration tests had gone stale. | Fixed at each owner: `SourceImageIdentity.filter_paths`, producer qualifiers, `ImagePayload.of`, `OMEROLocalBackend.is_dir`/`is_file` (PolyStore 0.6.1), `OpenHCSMetadataTarget.hosts_metadata_document` (not for a `VirtualBackend`), viewer families, and the GUI journey moved to gui-tests. |
+| Official30 real-viewer smoke | `required_scalar_components` pooled contributors across runtime planes and dropped `site`, so napari rejected the stack. | Contributors are grouped per plane (`contributor_groups`). |
+| Installed demo (Windows, Intel macOS) | The one-plane label projection (#477) lived only on `MatchImageStackLabels`; the skeleton step used `SliceAlignedLabels`. | The projection is owned by `ObjectLabelInputExecutionMode` (**derived**: `MatchImageStackLabels`' copy deleted). Dataset rows carry the finished execution's error. |
+| desktop live MCP | The smoke read the catalog before the GUI's endpoint was up. Requestless UI-bridge operations received the connection in the request slot. | `execution_services_prepared` future; connection passed by keyword. |
+| wheel-integration | The MCP smoke sent `wells` (G2 renamed it `partition_values`). Bio-Formats tests ran without the declared Fiji bundle. | `partition_values`; the job restores and prewarms the bundle. |
+| pypi-dependency-readiness | Pinned submodule sources differ from their release tags. | Version bumps: zmqruntime 0.6.2, PolyStore 0.6.1, ObjectState 2.1.1 (pycodify's change shipped in main's 0.2.0). Floors raised. **The job stays red until the owner merges and tags the three library PRs.** |
+
+## Guards
+
+- `tests/unit/test_ci_import_guards.py`: no test module imports a test-tree module by bare name.
+- `tests/unit/test_ci_package_boundaries.py`: the website job installs the candidate with the dev extra.
+- `tests/unit/test_build_website.py`: the website job triggers on `openhcs/**`.
+- `tests/unit/test_validate_documentation_audit.py`: repository paths match only whole paths.
+- `tests/unit/test_build_mcp_knowledge_assets.py`: every packaged resource fits the Windows path budget.
+- `tests/unit/test_library_registry_discovery.py`: marker-excluded distributions are left out of the catalog, while a required but missing one fails.
+- `tests/unit/test_dataset_access.py`; `external/zmqruntime` held-handle owner-record test; `external/PolyStore/tests/test_omero_virtual_paths.py`.
+
+## Done when
+
+Website, Documentation and Integration Tests are green on this PR's head, or each remaining red job names its exact cause and owner (see the PR's final status).
+
+## Lockstep library PRs (owner merges and tags)
+
+- OpenHCSDev/ZMQRuntime#29 (0.6.2; docs; startup lock)
+- OpenHCSDev/PolyStore#38 (0.6.1; OMERO `is_dir`; docs logs)
+- OpenHCSDev/ObjectState#26 (2.1.1)
+- OpenHCSDev/PyQT-reactive#21 (docs logs)
+
+## Handoff
+
+**Branch `refactor/B6-ci-green`, head `abf42ae1b` plus this receipt commit, merged with main `625150118`. PR #1195. GitHub Actions stalled before any job ran on this head, so nothing after `f419cb3bb` has a completed CI run.**
+
+### Last completed CI runs
+
+- **`f419cb3bb` (merged with main `eb64b3c13`): 36 jobs passed, 7 failed.** Website, Documentation and Refactor Guardrails were green. This is the last trustworthy picture.
+- **`2e0deafe6` (merged with main `44980dfe2`): 27 jobs failed.** Fixed since, unverified (see below).
+
+### Root causes fixed, with commits
+
+| Cause | Commit |
+|---|---|
+| Docs path regex matched inside longer paths | `c9de5fab0` |
+| Website installed a package roster; needs dev extra; path filter; shared `pages` concurrency group | `6bccc4d6d`, `5544c445f`, `ce529e459` |
+| Sibling test-module imports under importlib mode | `6bccc4d6d` |
+| `os.getpgid` on Windows; top-level import in GUI smoke | `62752cf33` |
+| 141/126/127 stale documentation pages (three rounds, after each main merge) | `e59901e76`, `b2fb1bcd2`, `297a423d5` |
+| Library docs: orphaned logs, removed zmqruntime members | `6669b3401`, `f3eb57aa9` |
+| zmqruntime startup-owner lock on Windows | `333d3e0ab` (library PR ZMQRuntime#29) |
+| Windows uint32 format; custom-function bytes | `47724ccd4`, `d5172f398` |
+| Stale GUI tests; catalog race; requestless UI-bridge call; `partition_values` | `f59da9b8f`, `03ca14cc9`, `c3ab99415`, `87e4df3ff` |
+| Marker-excluded distributions in the catalog (Intel macOS basicpy) | `c1013dd51` |
+| BaSiC `.metadata`; STEP_INPUT filter paths; viewer families; GUI journey job | `c00a652d4`, `35abb00d6`, `4b1dec1e2`, `2eb48fb9d`, `dbae3e0b4` |
+| mahotas core dependency; measured CLI child environment | `5e519401f`, `42f51b7a7` |
+| OMERO `is_dir` (PolyStore#38); metadata document for virtual backends | `54e87d5b4`, `956326e3e` |
+| Passthrough filename qualifier; adapter config; watchdog; stale synthetic tests | `89269b9a8`, `0f0ad9f7c`, `0d83e1e16`, `34556a1ac` |
+| ObjectState own annotations on 3.14 (ObjectState#26) | `fac0519e5`, re-based to 2.1.1 in the first main merge |
+| Ratchet: mixins, dead theme methods, catalog policy functions | `239953c4c`, `ab03ba7ba` |
+| Packaged recipe paths over MAX_PATH; macOS serial kernel preparation | `35833f3a5`, `ded24279e` |
+| Runtime-plane axes dropped from streams (napari settle) | `7066af9f3` |
+| Windows read-only datasets, Ctrl+Break, one-shot write probe | `433b26fef`, `e1a1719d3`, `c74b2bd55` |
+| Wheel job Fiji bundle; `.gitattributes` exact bytes | `e82ae3398`, `fbe17e02d` |
+| One-plane label projection; demo error surfaced; `ViewerType.value` | `5de70ef71`, `88e22432e` |
+| Unit session QApplication before MCP transports | `f322e395b`, `a5ce88b63`, `d43533f90` |
+| `PARTITION_COUNT` in observation-export test | `b14856fbd` |
+| E1 boundary: export scope and UI action status | `7379deb90`, `f0498a630` |
+| Unit job split into three serial groups | `542f7ffeb` |
+
+### Still red at the last completed run (`f419cb3bb`)
+
+| Job | First cause | Owner |
+|---|---|---|
+| pypi-dependency-readiness | Pinned ObjectState 2.1.1, PolyStore 0.6.1 and zmqruntime 0.6.2 have no release tags | owner: merge and tag ObjectState#26, PolyStore#38, ZMQRuntime#29 |
+| unit-tests | Cancelled at the 45-minute limit with 63% run. `test_custom_function_events.py` alone took 9 minutes (200 `gc.collect()` calls on a large heap). Of 48 failures before the cut, 29 are in `known.fail`; 18 pass locally on the merged tree; `test_declaration_selected_discovery.py::test_examplehuman_selected_source_uses_same_declaration_owner` fails on main `44980dfe2` too and is not in `known.fail` | job time: B6 (split unverified); the test: unowned, needs an owner |
+| official30-headless-parity (macOS 3.14) | ExampleHuman `cells/zernike_*` values differ from the native reference; Linux 3.12 and 3.14 pass | parity owner (Zernike numerics off Linux) |
+| official30-headless-parity (Windows 3.14) | ExampleImagingFlowCytometryObjectsInGrid `ssc/zernike_phase_*` values differ | same |
+| official30-parity-required | Aggregates the two above | same |
+| desktop-candidate-live-mcp and desktop-installer-source-test (macos-15-intel) | Cold function-catalog preparation runs past the smoke's 300-second deadline (it passed once at about 200 s) | owner decision: performance work or the budget |
+
+### Unverified because CI stalled
+
+- The E1 boundary fixes (`7379deb90`, `f0498a630`); locally the four integration tests and the GUI journey pass.
+- The three-group unit job (`542f7ffeb`). The earlier xdist attempt (`f8062d2cd`) lost a worker in `test_mcp_server.py` under xcb and was replaced.
+- The Website per-ref concurrency group (`ce529e459`).
+- The third documentation round and re-pins after `44980dfe2` and `625150118`; `validate_docs.py` passes locally on every target and the OpenHCS Sphinx `-W` build passed locally after round two.
+- Windows-only fixes confirmed by one run each but not on this head: read-only probe (`c74b2bd55` never ran on Windows), `.gitattributes`.
+- An earlier CI-only segfault in `tests/unit/pyqt_gui` teardown under xcb was not reproduced or root-caused; it has not recurred since `f322e395b`.
+
+### Why 27 jobs failed after merging main `44980dfe2`
+
+**E1 left two boundaries half-migrated on main, and every job that runs a dataset or the GUI smoke hit one of them:**
+
+- `DatasetRunRequest.runtime_observation_export_scope` is an enum-view field, but the measured CLI passed the family class into it, and `RunDatasets` passed the enum member to `Session.observation_export_params`, which expects the family. Every `run_datasets` call failed: four integration tests in all 17 backend, boundary and OMERO jobs, the wheel MCP smoke and the GUI journey.
+- `scripts/smoke_installed_gui.py` read `UiActionInvocationStatus.ACCEPTED`, which E1 replaced with the `ActionAccepted` class: all desktop installer and live-MCP jobs.
+- The unit job failed separately on the xdist worker crash described above.
+
+Main's own run at `625150118` had not started, so "main is red the same way" is inferred from the code being identical on main, not observed.
+
+### Standing problem for the owner
+
+**The documentation audit gate goes red on every surface merge.** Each merge changes authority files, which invalidates about 125 pages; B6 re-reviewed them three times in one day. Unless each surface re-reviews its dependent pages in its own PR, Documentation will be red again after the next merge.
+
+### Also found, not fixed
+
+- `openhcs/agent/services/config_service.py` `_default_value_repr` prints family-class defaults as `` in the generated config reference.
+- `openhcs/mcp/bootstrap.py` `--surface` help says the desktop surface hides headless tools; it does not.
+- `scripts/check_basicpy_source.py` expects a `jax_func` decorator the module no longer uses.
+- `polystore/atomic.py` `_try_acquire_lock` retries `PermissionError` until its 30-second timeout.
+- About 20 pages still say "Plate Manager" where the window title is now "Datasets".
diff --git a/docs/refactor/consolidation-20261009/U1-session-interface.md b/docs/refactor/consolidation-20261009/U1-session-interface.md
index 1f2929f8b5..1380f5346e 100644
--- a/docs/refactor/consolidation-20261009/U1-session-interface.md
+++ b/docs/refactor/consolidation-20261009/U1-session-interface.md
@@ -88,7 +88,7 @@ Measured against `origin/main` (`89b8fdbff`, Python plus the MCP contract fixtur
Corrections to the plan above:
- **MCP tools are derived, not subclassed.** `SessionOperation` does not subclass `AgentCapabilityDeclaration` (that import cycles through the DTOs); `capabilities.py` derives one `openhcs_` capability per `HeadlessOperation`. The guard checks the derivation is total.
-- **The headless journey runs a real execution server**, not a fake client; it and the GUI journey live in `tests/integration/test_session_journey.py`.
+- **The headless journey runs a real execution server**, not a fake client; it lives in `tests/integration/test_session_journey.py`; the GUI journey, which needs the GUI extras and a Qt platform, lives in `tests/pyqt_gui/test_session_gui_journey.py`.
- **The pipeline editor keeps `displayed_steps`**, a render projection of `PipelineStepsView`, never written back; the guard allows it.
- **500 ms polling advice remains on the UI bridge** (`recommended_poll_interval_ms`): bridge events belong to U2. The session's own event stream is push (`openhcs_session_events`).
- **The session package loads PyQt6 transitively** through `pyqt_reactive.protocols`; the guard checks direct imports. Owner: U4 (library moves).
diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst
index 392e57e4db..7b7ab3b22d 100644
--- a/docs/source/api/index.rst
+++ b/docs/source/api/index.rst
@@ -66,37 +66,36 @@ Compilation and execution
.. code-block:: python
+ import queue
from pathlib import Path
from objectstate import ensure_global_config_context
from openhcs.core.config import GlobalPipelineConfig
from openhcs.core.orchestrator.orchestrator import PipelineOrchestrator
+ from openhcs.core.progress import set_progress_queue
- plate_path = Path("/data/plate").resolve()
+ dataset_root = Path("/data/plate").resolve()
ensure_global_config_context(GlobalPipelineConfig, GlobalPipelineConfig())
orchestrator = PipelineOrchestrator(
- plate_path,
+ dataset_root,
pipeline_config=pipeline_config,
).initialize()
- execution_bundle = orchestrator.compile_pipelines(pipeline_steps)
-
- progress_context = {
- "execution_id": f"local::{plate_path}",
- "plate_id": str(plate_path),
- "axis_id": "",
- }
- progress_queue = (
- execution_bundle.runtime_environment.worker_start
- .multiprocessing_context()
- .Queue()
- )
-
- results = orchestrator.execute_compiled_plate(
- execution_bundle=execution_bundle,
- progress_queue=progress_queue,
- progress_context=progress_context,
- )
+ progress_queue = queue.Queue()
+ set_progress_queue(progress_queue)
+ try:
+ execution_bundle = orchestrator.compile_pipelines(pipeline_steps)
+ results = orchestrator.execute_compiled_bundle(
+ execution_bundle=execution_bundle,
+ progress_queue=progress_queue,
+ progress_context=orchestrator.progress_context(),
+ )
+ finally:
+ set_progress_queue(None)
+
+Compilation and execution emit typed progress events into the queue installed
+with ``set_progress_queue``; ``orchestrator.progress_context()`` names the
+execution those events report on.
Applications should normally let the GUI or an execution service own progress
queue lifecycle, cancellation, and result presentation. The explicit call above
diff --git a/docs/source/appendices/glossary.rst b/docs/source/appendices/glossary.rst
index c55eb509b4..3a3296482c 100644
--- a/docs/source/appendices/glossary.rst
+++ b/docs/source/appendices/glossary.rst
@@ -24,19 +24,19 @@ Glossary
Compiled execution bundle
The validated worker-safe steps, frozen processing contexts, and
- environment decisions consumed by plate execution.
+ environment decisions consumed by bundle execution.
Compiled step plan
- The typed compiler-owned plan for one step on one execution axis. Runtime
- code reads it but does not repeat its inference.
+ The typed compiler-owned plan for one step in one processing context.
+ Runtime code reads it but does not repeat its inference.
Compilation session
The cross-step authority for source, artifact, and plan state while a
pipeline is compiled.
- Execution axis
- The component that partitions independent processing contexts, normally
- well.
+ Partition axis
+ The axis role whose values partition independent processing contexts and
+ worker lanes; for microscopy, the well.
Function pattern
A callable, a callable plus keyword arguments, a chain, or a dictionary
@@ -58,9 +58,10 @@ Glossary
A typed quantitative result associated with images, objects, relationships,
wells, or a plate.
- Microscope handler
- A registered source-format declaration that owns detection, parsing,
- metadata projection, virtual-workspace behavior, and compatible backends.
+ Dataset source
+ A registered way to open a dataset root, such as a microscope format. It
+ owns detection, filename parsing, metadata, virtual-workspace behavior,
+ and compatible backends.
Module artifact contract
The artifact semantics declared by a module family, including a lowered
@@ -79,8 +80,9 @@ Glossary
``FunctionStep`` objects.
Processing contract
- The semantic locality of a callable, such as plane-local ``PURE_2D`` or
- whole-stack ``PURE_3D``. It is independent of array memory type.
+ The semantic locality of a callable, a member of the
+ ``ProcessingContract`` family such as plane-local ``Pure2DContract`` or
+ whole-stack ``Pure3DContract``. It is independent of array memory type.
Runtime adapter
A declared boundary that converts typed compiler/runtime values into the
@@ -102,7 +104,7 @@ Glossary
to satisfy a step.
Source workspace projection
- The virtual source view exposed by a microscope handler and PolyStore to
+ The virtual source view exposed by a dataset source and PolyStore to
initialization and compilation.
Variable components
diff --git a/docs/source/architecture/abstraction_lattices.rst b/docs/source/architecture/abstraction_lattices.rst
index 87bbdc21a0..a3af6cbdfb 100644
--- a/docs/source/architecture/abstraction_lattices.rst
+++ b/docs/source/architecture/abstraction_lattices.rst
@@ -21,7 +21,7 @@ Start with the semantic question an abstraction answers. Examples include:
- Which artifact does this callable produce?
- Which CellProfiler module owns this parsed module name?
- Which runtime value projection applies to this nominal value type?
-- Which microscope handler recognises and projects this source layout?
+- Which dataset source recognises and projects this source layout?
A valid owner contains facts that cannot be obtained from another declaration.
A mirror derives or copies its answer from an existing owner and then exposes
@@ -68,17 +68,18 @@ The following boundaries are verified by current declarations and tests:
nominal leaves. ``CompiledStepPlan`` adds execution consequences without
becoming a second declaration graph.
-``MicroscopeHandler`` and ``SourceBindingsConfig``
- These are distinct owners, not duplicates. A handler recognises and projects
- a physical or virtual source layout; source bindings select, name, and assign
- component identity to sources. ``SourceBindingsHandler`` composes both for
- arbitrary declared files.
+``DatasetSource`` and ``SourceBindingsConfig``
+ These are distinct owners, not duplicates. A dataset source recognises and
+ projects a physical or virtual source layout; source bindings select, name,
+ and assign component identity to sources. ``SourceBindingsSource`` composes
+ both for arbitrary declared files.
Strategy selection mixins
``EnumKeyedStrategyMixin``, ``NominalTypeKeyedStrategyMixin``, and
- ``MostDerivedContextStrategyMixin`` centralise selection by enum member,
- nearest nominal type, or most-derived matching context. Leaves own
- specialisation; consumers make one call through the root.
+ ``MostDerivedContextStrategyMixin``, provided by ``metaclass-registry``,
+ centralise selection by enum member, nearest nominal type, or most-derived
+ matching context. Leaves own specialisation; consumers make one call through
+ the root.
``AgentCapabilityDeclaration``
MCP tool identity, contracts, effects, security facts, and surface
@@ -119,11 +120,11 @@ declarations.
Source layouts and source bindings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-A microscope handler and source bindings sometimes mention the same files, but
-they answer different questions. The handler determines how a layout becomes a
-workspace and which storage backends it supports. Source bindings determine
-which declared files satisfy named semantic inputs. Combining them in
-``SourceBindingsHandler`` is therefore composition of two authorities, not a
+A dataset source and source bindings sometimes mention the same files, but
+they answer different questions. The dataset source determines how a layout
+becomes a workspace and which storage backends it supports. Source bindings
+determine which declared files satisfy named semantic inputs. Combining them in
+``SourceBindingsSource`` is therefore composition of two authorities, not a
mirror.
Collapsing every source concern into either owner would lose information. A
diff --git a/docs/source/architecture/analysis_consolidation_system.rst b/docs/source/architecture/analysis_consolidation_system.rst
index 4aca5a6f57..abf9836d25 100644
--- a/docs/source/architecture/analysis_consolidation_system.rst
+++ b/docs/source/architecture/analysis_consolidation_system.rst
@@ -5,20 +5,17 @@ Analysis consolidation is a post-plate operation over materialised tabular
artifacts. It is separate from callable execution and does not reopen an output
path to rediscover content already recorded by the runtime.
-``AnalysisConsolidationConfig`` is resolved into each processing context.
-After a compiled plate finishes successfully,
-``consolidate_analysis_outputs`` reads the configuration from the compiled
-contexts, projects the exact CSV content recorded in that execution's runtime
-observations, groups it by materialisation directory, and delegates to the
-analysis consolidation functions. Each directory retains its compiled persistent
-destination. Consolidation therefore neither scans a
-results directory nor treats a virtual backend address as a host file.
-
-Output selection is passed into runtime materialisation. A writer with a declared
-runtime-path projection can skip rendering when none of its outputs match the
-consolidation selection. ROI paths derive from the same target request used by
-the ROI writer, so consolidating tables does not regenerate unrelated contours.
-Writers without that projection filter their actual rendered outputs.
+Consolidation is a microscopy post-execute hook. ``AnalysisConsolidationHook``
+is a ``PostExecuteHook`` bound from the global ``AnalysisConsolidationConfig``
+and ``PlateMetadataConfig`` and carried by each compiled processing context.
+When a step saves CSV outputs, the hook records the exact text the writer saved,
+keeping only files the consolidation selection includes; explicitly reused debug
+outputs are read back through their compiled backend. These observations travel
+in each context's runtime observation. After every partition succeeds, they are
+combined, grouped by materialisation directory, and passed to the analysis
+consolidation functions. Each directory retains its compiled persistent
+destination. Consolidation therefore neither renders outputs again, scans a
+results directory, nor treats a virtual backend address as a host file.
The generic consolidation layer reads ``AnalysisTableSource`` values and writes
through an ``AnalysisSummaryWriter`` ABC. Local desktop consolidation uses a
@@ -41,7 +38,7 @@ Ownership
rendering, and summary filenames.
Consolidation never infers missing artifacts from step names and does not mutate
-compiled plans. A source or destination failure is reported as a plate execution
+compiled plans. A source or destination failure is raised as an execution
failure rather than logged and discarded. Manual desktop consolidation uses the
same analysis functions on an explicitly selected results directory.
diff --git a/docs/source/architecture/artifact_contract_system.rst b/docs/source/architecture/artifact_contract_system.rst
index f2f71b050d..cf7107385f 100644
--- a/docs/source/architecture/artifact_contract_system.rst
+++ b/docs/source/architecture/artifact_contract_system.rst
@@ -105,8 +105,9 @@ not present an ROI-shaped base path as though it were also the CSV output.
Callable ABI versus semantic artifacts
--------------------------------------
-The ``special_inputs`` decorator names callable ABI slots. They do not by themselves declare artifact types, producers,
-materialisation, or runtime-store identity.
+The ``special_inputs`` decorator names callable ABI slots. Those names do not
+by themselves declare artifact types, producers, materialisation, or
+runtime-store identity.
Semantic ownership comes from ``artifact_inputs`` and ``artifact_outputs`` on
the callable. A CellProfiler module derives those declarations through its
@@ -132,7 +133,7 @@ consumer inherit the exact producer and group scope without generated-name
prefix matching or a CellProfiler-specific branch.
The initial raw image flow has the compiler-owned ``PIPELINE_INPUT_ARTIFACT``
-identity. A first-step consumer or a step selecting ``PIPELINE_START`` can
+identity. A first-step consumer or a step selecting ``ReadPipelineStart`` can
therefore inherit its actual source scope without referring to an earlier step.
Explicit source bindings keep their own declared identities.
@@ -182,8 +183,8 @@ records:
- the exact declared ``ArtifactSpec``
- an optional prior-producer ``ArtifactInputPlan`` and runtime projection
-- whether the occurrence consumes ordinary main flow
-- which ``MainFlowInputProjection`` applies
+- the ``MainFlowInputProjection`` that applies when the occurrence consumes
+ ordinary main flow
This is why two callable parameters can consume the same semantic artifact
without collapsing into one edge. ``CompiledStepPlan.artifact_inputs`` remains
@@ -193,8 +194,8 @@ on the compiled invocation edges.
Implicit main-flow projection
-----------------------------
-An invocation edge with ``consumes_main_flow=True`` cannot also carry a storage
-plan. Its projection is one of two explicit modes:
+An invocation edge that consumes main flow carries a ``main_flow_projection``
+and no storage plan. Its projection is one of two explicit modes:
``MainFlowInputProjection.COMPLETE_PAYLOAD``
The invocation has one main-flow reference and consumes the current payload
@@ -207,9 +208,9 @@ plan. Its projection is one of two explicit modes:
The end-to-end regression in
``tests/unit/test_implicit_main_flow_compilation.py`` compiles grouped native
``percentile_normalize`` output into a CellProfiler ``Threshold`` consumer. It
-asserts the step dependency, channels ``1``/``2``/``4`` group scope,
-``consumes_main_flow=True``, absence of a storage plan, and axis execution
-scope.
+asserts the step dependency, channels ``1``/``2``/``4`` group scope, a
+main-flow projection on the input edge, absence of a storage plan, and axis
+execution scope.
Compiled plans
--------------
diff --git a/docs/source/architecture/cellprofiler_interop.rst b/docs/source/architecture/cellprofiler_interop.rst
index 1354a60a4f..5a5dd3caca 100644
--- a/docs/source/architecture/cellprofiler_interop.rst
+++ b/docs/source/architecture/cellprofiler_interop.rst
@@ -79,7 +79,7 @@ exports into exact callable artifact contracts. These are executable conversion
rules, not prose conventions.
That does not make the importer a universal native-pipeline intent oracle. The
-``InputSource`` enum has only previous-step and pipeline-start main flow; an
+``InputSource`` family has only previous-step and pipeline-start main flow; an
additional named image is represented by an artifact input plus source binding
or prior producer. A ``.cppipe`` also does not choose arbitrary OpenHCS viewer
streaming, native checkpoint, VFS, or user review policy that it never declared.
@@ -148,8 +148,9 @@ runtime retry, or silent substitution with an unrelated backend.
Exact compile-time reconstruction
---------------------------------
-``CellProfilerInvocationContractProviderFactory`` walks the resolved
-``StepSnapshot`` sequence and one forward ``ArtifactDeclarationStepContext``:
+``CellProfilerInvocationContractProviderFactory`` walks the steps of the
+``ResolvedPipelineDefinition`` with one forward
+``ArtifactDeclarationStepContext``:
.. code-block:: text
diff --git a/docs/source/architecture/code_ui_interconversion.rst b/docs/source/architecture/code_ui_interconversion.rst
index 371046e64f..18974fc53a 100644
--- a/docs/source/architecture/code_ui_interconversion.rst
+++ b/docs/source/architecture/code_ui_interconversion.rst
@@ -112,7 +112,8 @@ ObjectState stores UI-editable values in typed scopes. Field projections expose:
* dirty/default markers;
* provenance and scope identity;
* field descriptions from the underlying Python type;
-* accepted enum inputs derived from that type declaration.
+* accepted enum or registered-family choice inputs derived from that type
+ declaration.
ObjectState field mutation is the narrow projection for one field; a code
document is the revision-protected projection for a related configuration or
@@ -161,8 +162,10 @@ Round-trip fidelity means:
Function parameter order is projected by python-introspect from the function
signature for both clean and full code views. The OpenHCS formatter supplies
-its semantic equality rule and whether signature defaults should be omitted;
-extra keywords retain their supplied order after the declared parameters.
+ObjectState's semantic equality rule and whether signature defaults should be
+omitted; extra keywords retain their supplied order after the declared
+parameters. ObjectState's code generation decides which lazy configuration
+fields a clean view emits.
When a nested function ObjectState exists, pyqt-reactive projects its current
values into the function pattern, including groups whose panes are not visible.
diff --git a/docs/source/architecture/compilation_system_detailed.rst b/docs/source/architecture/compilation_system_detailed.rst
index 7e8f4904d9..e441f129b5 100644
--- a/docs/source/architecture/compilation_system_detailed.rst
+++ b/docs/source/architecture/compilation_system_detailed.rst
@@ -3,5 +3,5 @@ Detailed compiler transition
The former fixed-phase and string-keyed compiler narrative is retained only in
the documentation archive. See :doc:`pipeline_compilation_system` for the
-current ``StepSnapshot``, ``CompilationSession``, typed-plan, and
-``CompiledExecutionBundle`` architecture.
+current ``ResolvedPipelineDefinition``, ``CompilationSession``, typed-plan,
+and ``CompiledExecutionBundle`` architecture.
diff --git a/docs/source/architecture/component_system_integration.rst b/docs/source/architecture/component_system_integration.rst
index 62493f7a5e..d6c327d246 100644
--- a/docs/source/architecture/component_system_integration.rst
+++ b/docs/source/architecture/component_system_integration.rst
@@ -5,7 +5,7 @@ The microscope/source model discovers component values for a plate. The
compiler carries those values into source universes, execution-axis contexts,
stack construction, grouping, and filtering.
-The same component declarations have distinct jobs:
+The step, pipeline, and family declarations have distinct jobs:
``variable_components``
Determines what each position on the transported third array axis means.
@@ -16,8 +16,9 @@ The same component declarations have distinct jobs:
``sequential_components``
Chooses dimensions processed in bounded sequential slices.
-``multiprocessing_axis``
- Partitions independent compiled contexts, normally by well.
+The partition axis
+ The family's single ``PartitionAxis``; it partitions independent compiled
+ contexts, by well in the microscopy family.
Metadata caches and source projections are the authority for values available
on a particular plate. UI selectors, compiler stages, and runtime filters query
diff --git a/docs/source/architecture/component_validation_system.rst b/docs/source/architecture/component_validation_system.rst
index 2905286bb7..8089c6502b 100644
--- a/docs/source/architecture/component_validation_system.rst
+++ b/docs/source/architecture/component_validation_system.rst
@@ -1,22 +1,26 @@
Component validation
====================
-Component declarations describe semantic microscope dimensions such as well,
-site, channel, Z, and time point. Each is an ``Axis`` class declared by the
-active ``AxisFamily``; subsets such as the variable axes are family queries,
-and a step's absent grouping is the explicit ``Ungrouped`` declaration.
+Component declarations describe the data's dimensions; the microscopy family
+declares well, site, channel, Z, and time point. Each is an ``Axis`` class
+declared by the active ``AxisFamily``; subsets such as the variable axes are
+family queries, and a step's absent grouping is the explicit ``Ungrouped``
+declaration.
Validation decodes external names through ``AxisFamily.named`` and rejects
unknown or semantically invalid combinations. It checks, among other things,
that:
-- configured variable components exist in the source universe;
+- a step's variable and grouping axes are variable axes of the family (every
+ axis but the partition axis), and the grouping axis is not also a variable
+ axis;
+- dictionary-pattern keys exist among the dataset's values for the grouping
+ axis;
- grouping is allowed by every callable contract in the pattern;
-- required callable axes are present;
-- sequential and multiprocessing axes do not create an invalid execution
- topology.
+- the step's variable axes cover every axis role a callable requires;
+- no pipeline sequential axis is also a step's variable axis.
-Consumers should use the shared conversion/validation helpers and nominal enum
-members. They must not maintain copied component-name sets. See
+Consumers should use the shared validation helpers and the axis classes
+themselves. They must not maintain copied component-name sets. See
:doc:`../concepts/data_dimensions` and :doc:`processing_semantics`.
diff --git a/docs/source/architecture/concurrency_model.rst b/docs/source/architecture/concurrency_model.rst
index 491f8e843f..d76cc2eaec 100644
--- a/docs/source/architecture/concurrency_model.rst
+++ b/docs/source/architecture/concurrency_model.rst
@@ -3,33 +3,36 @@ Concurrency model
OpenHCS compiles the selected execution axes before starting workers. A
``CompiledExecutionBundle`` carries the worker-safe steps, frozen
-``ProcessingContext`` instances, and environment decisions for one plate.
+``ProcessingContext`` instances, and environment decisions for one dataset.
Execution units
---------------
-The configured multiprocessing axis normally yields one context per well.
-Source discovery determines the available axis values; compiler stages create
-the corresponding typed step plans. Workers receive one compiled context at a
-time and never share mutable compiler state.
+The active family's partition axis yields one context per value; in the
+microscopy family that is one context per well. Source discovery determines the
+available axis values; compiler stages create the corresponding typed step
+plans. Each worker executes one lane, the compiled contexts of the partition
+values it owns, one context at a time, and never shares mutable compiler state.
Each frozen ``ProcessingContext`` owns its multiprocessing ``axis_id``. Mapping
keys distinguish runtime contexts, including sequential combinations, but do
not encode an axis contract. Bundle projections and worker-lane grouping read
the context-owned identity instead of parsing those keys.
-``compiled_plate_execution`` owns the plate-level execution lifecycle.
+``bundle_execution`` owns the execution lifecycle of one compiled bundle.
``worker_execution`` owns executor construction, bundle installation, task
-submission, result collection, cancellation, and cleanup. ``worker_lanes``
-provides the process-local bundle authority used by worker code.
+submission, result collection, cancellation, and cleanup; process workers serve
+a typed ``WorkerCommand`` family over their connection to the parent.
+``worker_lanes`` provides the process-local bundle authority used by worker
+code.
At execution, each context binds its ``WorkerLaneExecutionContext`` directly.
The lane identity references the execution's worker-assignment mapping, without
carrying other workers' processing contexts. Progress projects the current
-lane's owned axes; streaming projects the complete assigned axis domain so that
+lane's owned partition values; streaming projects the complete assigned axis domain so that
every worker declares the same viewer coordinates. ``ProcessingContext`` does
-not retain separate copies of the lane's execution, plate, worker, or ownership
-fields.
+not retain separate copies of the lane's execution, dataset, worker, or
+ownership fields.
Start-method policy
-------------------
@@ -42,7 +45,8 @@ default to ``spawn`` unless an explicit unsafe override is allowed.
Progress and cancellation
-------------------------
-Workers emit typed progress events to the execution's queue. The parent and ZMQ
+Workers emit typed progress events to the execution's progress queue; process
+workers forward them over their parent connection. The parent and ZMQ
runtime project those events for UI consumers. ZMQRuntime owns the terminal
execution status and applies cancellation only to the requested execution.
OpenHCS owns an execution-scoped cancellation authority: inline and threaded
@@ -51,10 +55,11 @@ owned worker processes. A callable already in progress reaches its next safe
boundary before stopping. Cancellation never becomes success or failure when
late worker completion or an exception reaches the server, and every path still
runs cleanup.
-Worker ``AXIS_COMPLETED`` events close only the axis-scoped portion. Terminal
-plate steps, output consolidation, viewer settlement, and final state projection
-remain parent-owned; :doc:`progress_runtime_projection_system` defines the
-separate final ``SUCCESS`` boundary.
+Worker ``AxisCompletedProgressPhase`` events close only the axis-scoped portion. Terminal
+dataset-scoped steps, post-execute hooks such as the microscopy domain's output
+consolidation, viewer settlement, and final state projection remain
+parent-owned; :doc:`progress_runtime_projection_system` defines the
+separate final ``SuccessProgressPhase`` boundary.
Invariants
----------
diff --git a/docs/source/architecture/declarative_window_system.rst b/docs/source/architecture/declarative_window_system.rst
index a5bf624c76..4c4872757d 100644
--- a/docs/source/architecture/declarative_window_system.rst
+++ b/docs/source/architecture/declarative_window_system.rst
@@ -10,6 +10,8 @@ See :external+pyqt-reactive:doc:`window manager usage
`.
OpenHCS retains stable product window identities, domain factories, ObjectState
-scope selection, and pipeline/execution actions. Their cross-package composition
+scope selection, and the session views and operations behind the dataset,
+pipeline, and execution windows; a session-view window takes its title from the
+view it renders. Their cross-package composition
is documented in :doc:`ui_services_architecture`; package ownership and
migration policy are in :doc:`external_foundations`.
diff --git a/docs/source/architecture/experimental_analysis_system.rst b/docs/source/architecture/experimental_analysis_system.rst
index 9b42a4324f..a6c8481375 100644
--- a/docs/source/architecture/experimental_analysis_system.rst
+++ b/docs/source/architecture/experimental_analysis_system.rst
@@ -10,21 +10,21 @@ compilation or execution.
Current ownership
-----------------
-``ExperimentalAnalysisConfig`` owns the standalone engine options and all five
-filenames used by the directory workflow. It is deliberately not a global or
-pipeline configuration. ``ExperimentalAnalysisEngine`` owns format selection
+``ExperimentalAnalysisConfig``, declared by the microscopy domain, owns the
+standalone engine options and all five filenames used by the directory
+workflow. It is deliberately not a global or pipeline configuration section. ``ExperimentalAnalysisEngine`` owns format selection
and orchestration; its ``run_directory`` projection derives every input and
output path from that configuration. The desktop action constructs the default
configuration and calls the same engine boundary rather than restating those
-filenames or using the deprecated compatibility wrapper.
+filenames.
``NormalizationMethod`` owns the fold-change, control z-score, and
percentage-of-control leaf calculations. The shared analysis module traverses
the experiment once and applies that declaration against each biological
replicate's own control distribution; it does not infer control semantics from
condition-name strings. ``ExperimentalResultFormatStrategy`` leaves own the
-CX5 and MetaXpress result readers selected by the workbook's
-``ExperimentalAnalysisScope`` declaration. The shared analysis module owns
+CX5 and MetaXpress result readers; each leaf's ``scope_name`` is the scope a
+workbook declares to select it. The shared analysis module owns
layout parsing, exclusion projection, and table/plate-grid export.
Pipeline boundary
diff --git a/docs/source/architecture/external_foundations.rst b/docs/source/architecture/external_foundations.rst
index 9a3951b29e..aecd6e7e7d 100644
--- a/docs/source/architecture/external_foundations.rst
+++ b/docs/source/architecture/external_foundations.rst
@@ -11,15 +11,18 @@ Ownership catalog
ObjectState
Owns lazy configuration types, context resolution, ObjectState editing,
- snapshots, provenance, undo, and generic projection/composition. OpenHCS owns
+ snapshots, provenance, undo, generic projection/composition, the
+ ``ScopeKey`` scope grammar, field inspection, and the Python-source
+ formatting of lazy configurations (``objectstate.codegen``). OpenHCS owns
its configuration hierarchy, compiler resolution boundary, and UI/code use.
See :external+objectstate:doc:`ObjectState documentation `.
ArrayBridge
Owns memory-type taxonomy, callable memory metadata and decorators (including
input, output, and execution memory), device-neutral array geometry, array
- conversion, stack utilities, device streams, OOM behaviour, and the child-
- process environment required to import each framework consistently. OpenHCS
+ conversion, per-memory-type native array operations, stack utilities, device
+ streams, OOM behaviour, and the child-process environment required to import
+ each framework consistently. OpenHCS
projects those declarations into callable contracts and process launches, and
owns semantic image shape roles, compiled conversion and framework-device
plans, worker resource lifecycle, and resource policy. See
@@ -27,8 +30,9 @@ ArrayBridge
metaclass-registry
Owns ``AutoRegisterMeta``, ``RegistryConfig``, ``RegistryFamily``, discovery,
- generic caching, and discovery imports that preserve host-owned root logging.
- OpenHCS owns its nominal roots, declarations, and shared selection mixins. See
+ discovery imports that preserve host-owned root logging, the shared strategy
+ and selection mixins, and the bounded and process-local cache family.
+ OpenHCS owns its nominal roots and declarations. See
:external+metaclass-registry:doc:`metaclass-registry documentation `.
PolyStore
@@ -44,29 +48,37 @@ PolyStore
pyqt-reactive
Owns generic reactive forms, widget protocols and strategies, managers,
- previews, reusable services, live Qt-window discovery and infrastructure, and
- Qt-rendered window snapshots. OpenHCS owns concrete workflows, Plate Manager,
+ previews, reusable services, live Qt-window discovery and infrastructure,
+ Qt-rendered window snapshots, editable tables, grid selection, time-travel
+ widgets and navigation, configuration windows, and dock and shortcut layout.
+ OpenHCS owns concrete workflows, Plate Manager,
pipeline editors, application window identity and authorisation, and domain
transport adapters. See
:external+pyqt-reactive:doc:`pyqt-reactive documentation `.
python-introspect
Owns callable/signature analysis, wrapped-target resolution, parameter
- metadata, callable-declaration projection, and analyzer extensions. OpenHCS
+ metadata, field labels, callable-declaration projection, analyzer extensions, the
+ ``to_jsonable`` encoder paired with the ``dataclass_from_mapping`` decoder,
+ the registry-key spelling and field choices of registered class families
+ (``RegisteredClassChoices``), and declared public-API and lazy-export
+ helpers. OpenHCS
owns domain exclusions, callable contracts, and step-editor consumption. See
the `python-introspect repository
`_.
ZMQRuntime
Owns generic process lifecycle, exact spawned-process ownership groups,
- automatic child reaping, request/status/progress protocols, typed connection
- and operation cancellation, and viewer-control transport.
+ automatic child reaping, the background-process launch policy,
+ request/status/progress protocols, typed connection and operation
+ cancellation, and viewer-control transport.
OpenHCS owns mapping compiled execution, source axes, runtime observations,
and application services onto those protocols. See the `ZMQRuntime repository
`_.
pycodify
- Owns Python-source serialization, formatter registration, import construction,
+ Owns Python-source serialization, formatter registration, qualified-name
+ import construction, value-owned source literals, absolute-path factoring,
and collision handling. OpenHCS owns formatters and round-trip policies for its
domain declarations. See the
`pycodify repository `_.
diff --git a/docs/source/architecture/external_integrations_overview.rst b/docs/source/architecture/external_integrations_overview.rst
index 5acf8428c2..03817fcc86 100644
--- a/docs/source/architecture/external_integrations_overview.rst
+++ b/docs/source/architecture/external_integrations_overview.rst
@@ -8,7 +8,8 @@ parallel pipeline, source, artifact, or execution model.
CellProfiler
------------
-The ``.cppipe`` importer parses CellProfiler modules and lowers them to
+The ``.cppipe`` importer, registered from interop as a member of the kernel's
+``PipelineImporter`` family, parses CellProfiler modules and lowers them to
``PipelineConfig`` plus ``FunctionStep`` declarations. Module declarations own
their callable, artifact, and headless-import semantics; interactive-only
modules fail explicitly. See :doc:`cellprofiler_interop`.
@@ -16,8 +17,9 @@ modules fail explicitly. See :doc:`cellprofiler_interop`.
Microscope and storage sources
------------------------------
-Registered ``MicroscopeHandler`` implementations connect filename/metadata
-semantics to PolyStore source references and virtual workspaces. OpenHCS owns
+Registered ``DatasetSource`` implementations, such as the microscope handlers,
+connect filename/metadata semantics to PolyStore source references and virtual
+workspaces. OpenHCS owns
source selection and compiler bindings; PolyStore owns generic storage. See
:doc:`microscope_handler_integration` and :doc:`source_model`.
diff --git a/docs/source/architecture/gpu_resource_management.rst b/docs/source/architecture/gpu_resource_management.rst
index d3cfa8ab18..ec1a1052e2 100644
--- a/docs/source/architecture/gpu_resource_management.rst
+++ b/docs/source/architecture/gpu_resource_management.rst
@@ -21,8 +21,8 @@ instead of preparing the complete catalogue.
Fresh catalogue-preparation and execution-server processes receive
``MemoryType.subprocess_environment()``. That projection combines each member's
declared import defaults and member-owned native-library paths before the child
-imports any framework, so parent and child admission do not depend on which GPU
-library happened to be imported first in the parent.
+imports any framework, so parent and child framework imports do not depend on
+which GPU library happened to be imported first in the parent.
The desktop ``--no-gpu`` option and ``OPENHCS_CPU_ONLY=true`` reject a GPU
footprint before optional frameworks are imported.
diff --git a/docs/source/architecture/list_item_preview_system.rst b/docs/source/architecture/list_item_preview_system.rst
index 598e4c3c6b..888520f7a3 100644
--- a/docs/source/architecture/list_item_preview_system.rst
+++ b/docs/source/architecture/list_item_preview_system.rst
@@ -12,7 +12,7 @@ OpenHCS integration
OpenHCS registers one formatter for the ``PartitionFilterConfig`` declaration
family. Config classes own their preview labels and whether an enabled member
-remains visible without an explicit well selection. ``PartitionFilterMode`` subclasses
+remains visible without an explicit partition filter. ``PartitionFilterMode`` subclasses
own the compact include or exclude operator. The formatter resolves those
declarations through pyqt-reactive's preview API; it does not scan unrelated
registries or reproduce subtype tables.
diff --git a/docs/source/architecture/mcp_distribution.rst b/docs/source/architecture/mcp_distribution.rst
index a57c681288..2c885e892f 100644
--- a/docs/source/architecture/mcp_distribution.rst
+++ b/docs/source/architecture/mcp_distribution.rst
@@ -43,7 +43,10 @@ Local process lane
------------------
The local MCP server runs over stdio and is owned by the client process. It
-does not import or host the PyQt UI. GUI, viewer, and local runtime capabilities
+hosts no GUI: the transport keeps a Qt core application on the process main
+thread for main-thread-owned ObjectState work and runs the MCP SDK loop on a
+worker thread, so protocol notifications continue while main-thread work runs.
+GUI, viewer, and local runtime capabilities
remain local-only and attach through their authenticated process bridges.
UI-bridge and viewer control budgets remain facts of their respective
connection declarations. MCP timeout policies bound caller overrides from
@@ -57,8 +60,9 @@ does not import every processing library to rebuild a second catalogue. At MCP
process startup, the composition root snapshots the persisted desktop ZMQ
configuration so a non-default host, port, or transport remains the same
endpoint identity for both authoring surfaces. That snapshot is projected from
-the unique ``OpenHCSZMQConfig`` field in the canonical ``UIConfig`` Python
-document. The projection derives the field from the dataclass schema and does
+the unique ``OpenHCSZMQConfig`` field in the saved ``UIConfig`` Python
+document. The headless session that MCP session tools act on connects through
+that same endpoint configuration. The projection derives the field from the dataclass schema and does
not construct unrelated UI configuration or import Qt.
The stdio transport reserves process stdin and stdout before constructing the
@@ -106,8 +110,8 @@ measured-pipeline receipt is
produced only after an ordinary pipeline's selected value or outcome export
passes its validation. Outcome-only export retains per-axis status without
claiming value equivalence. The expert-only
-``openhcs_finalize_measured_pipeline_run`` capability consumes the exact
-completed ordinary job, validates the selected export, and retains the submitted
+``openhcs_finalize_measured_pipeline_run`` capability consumes the OpenHCS
+session's exact finished execution, validates the selected export, and retains the submitted
source, server result and timing receipt. It does not create another execution
or status authority. ``openhcs_inspect_measured_pipeline_run`` checks that
receipt, submitted-source digests, and retained observation/summary digests;
@@ -190,16 +194,16 @@ The shared registered JSON projection owns Cursor, Gemini CLI, and Windsurf
user configuration; Visual Studio Code is registered through its documented
command-line interface rather than a guessed profile path.
-``DesktopDeploymentAuthority`` owns the native launcher, shortcut, and
-application-icon projection. Its registered Windows and macOS leaves read the
-installed distribution entry points and packaged brand assets. Both initial
-setup and the in-application updater call this authority; the PowerShell and
+``DesktopDeployment`` in ``openhcs.desktop.deployment`` owns the native
+launcher, shortcut, and application-icon projection. Its registered Windows and
+macOS leaves read the installed distribution entry points and packaged brand
+assets. Both initial setup and the in-application updater call it; the PowerShell and
shell bootstrap adapters do not carry parallel launcher or app-bundle
templates.
On Windows, the stable GUI launcher is a forwarding cache over the separately
published current-environment pointer. If another launch still holds that
-executable without delete sharing during an update, the Windows authority
+executable without delete sharing during an update, the Windows deployment
retains the valid launcher, publishes the verified environment, and reports the
deferred launcher and fingerprint paths. A later installer or desktop refresh
retries those projections after the executable is released. Other publication
@@ -209,9 +213,12 @@ The projection owns only the ``openhcs`` server entry and preserves every other
client setting. Its command targets the stable launcher, never a
version-stamped private environment, so a verified installer update can switch
the environment without rewriting client configuration. Registration does not
-copy capability lists, path-policy roots, client credentials, or agent
+copy capability lists, path-policy roots, client credentials, or server
instructions. Those remain owned by the MCP initialisation handshake,
-``AgentPathPolicy``, and the client itself.
+``AgentPathPolicy``, and the client itself. A client target that declares a
+documented skill directory (currently Codex) also receives the installed
+OpenHCS skill bundle; the registration report records that synchronisation
+separately from the client's MCP connection result.
The launcher projects two lifecycle values into the MCP process: one JSON argv
for reconnecting through that same stable adapter and one installer-owned
@@ -232,8 +239,9 @@ they never contain MCP tool-name lists. The installed server defaults to
``desktop`` while the development client explicitly uses ``full`` for complete
surface audits. The desktop profile includes both UI-owned workflows and
independent headless execution. Those routes share pipeline declarations but
-retain separate state owners: a headless session does not create Plate Manager
-rows or ObjectState history. Capability discovery and bound tools/resources are
+retain separate state owners: the headless session keeps its datasets and
+pipelines in the MCP process's own ObjectState, so it does not create Plate
+Manager rows or ObjectState history in a running desktop application. Capability discovery and bound tools/resources are
filtered by the same ``AgentCapabilitySurfaceSelection`` instance.
The development client resolves each requested tool to its capability
@@ -326,20 +334,22 @@ invocation and writes a delayed stack diagnostic to stderr. This exposes a
blocked worker even when the event loop continues emitting liveness updates.
The diagnostic does not alter the operation's result or execution context.
-Source-backed orchestrator-session creation remains on the MCP process's main
-thread because its import and compiler-facing setup is thread-sensitive. The
-binder emits the standard ``started`` progress event before entering that
-synchronous section, and the service logs its source-parse start and
-completion. The event loop cannot emit periodic progress while the synchronous
-main-thread operation is blocked. None of those signals is scientific percent
-completion or success; the client must still wait for the actual typed result.
-
-Session creation and submitted execution are separate phases. The development
-``execute-source`` command first creates the source-backed session, then submits
-that exact session. Automation that must remain observable over a long run
-submits without an opaque aggregate wait and polls the typed execution-status
-capability until a terminal state. The initial progress event and the
-start/completion diagnostics expose the pre-submit phase; execution status,
+Capabilities whose work touches ObjectState or other thread-sensitive setup
+declare that they are not worker-thread safe. The binder emits the standard
+``started`` progress event and then runs them on the process main thread
+through the main-thread dispatcher; because the SDK loop runs on its own
+thread, periodic liveness updates continue meanwhile. None of those signals is
+scientific percent completion or success; the client must still wait for the
+actual typed result.
+
+Each headless session operation is one MCP tool on the OpenHCS session:
+adding datasets, setting a dataset's pipeline source, and initializing,
+compiling, or running it. An operation returns the event sequence it started
+at, and ``openhcs_session_events`` returns the events published after that
+sequence. The development ``execute-source`` command chains those operations
+and follows each stage through session events; ``--no-wait`` returns once the
+run is accepted. Automation that must remain observable over a long run follows
+session events instead of an opaque aggregate wait. Execution status,
cancellation, results, and viewer settlement remain runtime-service
responsibilities.
@@ -350,7 +360,7 @@ Distribution metadata
``scripts/sync_mcp_release_metadata.py`` projects it into the Codex plugin,
Claude MCPB, and ``server.json``. Its dependency-free phase validates only
directly readable package/version structure. After the built wheel is installed,
-``--capability-requirements`` imports the canonical selected capability registry
+``--capability-requirements`` imports the selected capability registry
and verifies that all declaration-owned required extras are projected. These
checked artifacts are install-surface metadata only; they do not own OpenHCS
behaviour.
@@ -368,7 +378,7 @@ manager. The same profile projects its binary-wheel policy into both paths, so
platform compatibility lines remain one package declaration and both native
consumers require the reviewed wheel-backed releases.
-Canonical MCP knowledge documents remain in the documentation tree. The wheel
+MCP knowledge documents remain in the documentation tree. The wheel
build deterministically projects the manifest-declared sources into package
resources, so installed servers do not depend on a source checkout and there is
no manually maintained second document corpus.
diff --git a/docs/source/architecture/measurement_equivalence_system.rst b/docs/source/architecture/measurement_equivalence_system.rst
index 2f4166221b..1d96b4255c 100644
--- a/docs/source/architecture/measurement_equivalence_system.rst
+++ b/docs/source/architecture/measurement_equivalence_system.rst
@@ -25,9 +25,12 @@ Measurement identity
--------------------
``RuntimeMeasurementFeatureKey`` separates subject, feature, source
-qualification, and aggregate identity. ``RuntimeMeasurementDialect`` declares
-how a producer encodes source names, qualifiers, aliases, and row layout. This
-lets two outputs be normalised without erasing meaningful distinctions.
+qualification, and aggregate identity. A registered ``MeasurementDialect``
+declares how a producer names sample and object fields, scopes, feature
+prefixes, aliases, and row qualifiers; ``PlainMeasurementDialect`` is the
+kernel's own spelling and CellProfiler declares its dialect in its interop
+package. This lets two outputs be normalised without erasing meaningful
+distinctions.
Row projection derives stable identities for image-, object-, and
relationship-scoped measurements. Wide and long-form tables project into the
@@ -65,12 +68,20 @@ Output
counts, measurement features/content, tables, and images. An empty difference
tuple means the compared outputs are semantically equivalent.
+The product package ``openhcs.core.equivalence`` keeps only the measurement
+semantics that production code uses: feature keys, semantic profiles, cell
+and array signatures, relationship aggregate features, and the policy. Output
+snapshots, row and fact projection, relationship plane alignment, comparison,
+and the report live in ``benchmark/equivalence`` beside the benchmark code that
+consumes them; no product module imports the benchmark package.
+
Official30 evidence boundary
----------------------------
The portable acceptance definition is
-``benchmark/manifests/official30_portable_axis1.json`` plus the two integration
-tests in ``tests/integration/test_cellprofiler_official30_zmq.py``. The headless
+``benchmark/manifests/official30_portable_axis1.json`` plus the headless and
+Napari integration tests in
+``tests/integration/test_cellprofiler_official30_zmq.py``. The headless
test loads exactly 30 cases, requires native references, runs every case with
``continue_on_error=True``, and requires every native/OpenHCS execution to
succeed and every observation to be equivalent. The Napari test exercises the
@@ -93,7 +104,7 @@ developer account:
Use the sibling
``test_official30_nonpersistent_napari_isolated_per_case`` target for the Napari
route. Different ZMQ/viewer ports avoid endpoint conflict, but overlapping runs
-remain diagnostic only and must not be reported as canonical acceptance timing.
+remain diagnostic only and must not be reported as acceptance timing.
Compared modalities and policy
------------------------------
@@ -110,9 +121,8 @@ exported files; the outcome receipt alone does not establish it.
Current exports also retain the compiler's exact axis membership: a missing
compiled axis or an outcome for an uncompiled axis invalidates the run even if
every retained outcome reports success. The measured-run finaliser refuses an
-empty execution. Archived outcome-only exports that predate this membership
-field remain readable, but their observed axes alone cannot prove complete
-compiled coverage and cannot be finalised as a new success receipt.
+empty execution. Outcome and observation exports written under an earlier
+schema version are rejected when read.
Repeated measurements can share one connected ordinary execution server while
each run compiles its own artifact and retains a distinct observation and
receipt. The benchmark does not create a second execution path to keep that
@@ -120,8 +130,7 @@ server warm.
Full-value observation expectations retain the compiled axis that owns each
artifact kind. A plate-scoped export is checked on its one owning axis rather
-than required independently on every image axis. Archived observation exports
-keep their earlier all-axis expectation when read.
+than required independently on every image axis.
A typed completed-run receipt derives execution identity, source/configuration
digests, runtime-observation and execution-summary digests, and output references
@@ -134,8 +143,8 @@ The CellProfiler adapter prepares one such submission and then applies its
optional native-reference equivalence policy. It does not own a second execution
engine.
An agent can finalise the same receipt after a normal headless job completes:
-the execution service supplies the exact submission, endpoint handshake and
-server completion record, while the benchmark extension validates and retains
+the OpenHCS session's finished execution supplies the exact request, endpoint
+handshake and server completion record, while the benchmark extension validates and retains
the observation. Both ordinary export scopes carry the producing server
execution ID. The shared finaliser rejects an export from a different job
before writing a success receipt; the runtime export writer also refuses to
@@ -151,7 +160,10 @@ server job, not progress events, supplies the execution-only duration. A missing
progress event leaves that diagnostic interval absent; client wait time is not
used to guess it. Server, progress, and client wait intervals overlap, so
additive phase totals count the client submit/wait phases but not their nested
-server or progress observations.
+server or progress observations. Benchmark validation phases (runtime
+validation, output snapshots, and equivalence comparison) are also excluded
+from the additive total because they check a run rather than operate either
+tool.
If finalisation stops between evidence files, inspection derives the
``unreceipted_artifacts`` list from the measured-artifact declarations. Their
@@ -175,6 +187,13 @@ runtime/output snapshots and compares:
- CPA SQLite table projections
- CellProfiler Analyst ``.properties`` values
+CellProfiler writes most AreaShape values by position among an image's
+non-empty objects, so an empty declared object shifts later values onto the
+wrong ``ObjectNumber``. OpenHCS keeps each value on the object it measured.
+The adapter therefore reads native reference tables through
+``benchmark/equivalence/native_shape_rows.py``, which moves those values back
+to their objects within each image without adding or dropping any.
+
The strict CellProfiler policy sets numeric and image absolute/relative
tolerances to ``1e-6``, permits zero differing image fraction, disables the
broad feature-specific relaxations used by less strict compatibility modes, and
diff --git a/docs/source/architecture/microscope_handler_integration.rst b/docs/source/architecture/microscope_handler_integration.rst
index 7164dec998..413d2473bb 100644
--- a/docs/source/architecture/microscope_handler_integration.rst
+++ b/docs/source/architecture/microscope_handler_integration.rst
@@ -1,75 +1,84 @@
-Microscope handler integration
-==============================
+Dataset source integration
+==========================
-``MicroscopeHandler`` is the nominal root for microscope/source-specific
-behaviour. Concrete handlers self-register through ``AutoRegisterMeta`` and are
-loaded by package discovery. ``create_microscope_handler`` selects an explicit
-handler or performs ordered detection through the registered family.
+``DatasetSource`` is the nominal root for format-specific dataset behaviour.
+Concrete sources self-register through ``AutoRegisterMeta`` under their
+``source_name``. The registry discovers the kernel's own members in
+``openhcs.core.dataset_sources`` and then imports the modules the active
+``AxisFamily`` lists in ``extension_modules``; for microscopy these include
+``openhcs.microscopes``. The kernel never imports a domain module by name.
Owned declarations
------------------
-A handler owns:
+A source owns:
-- its microscope identity and detection behaviour;
+- its ``source_name`` identity, selection role, and detection behaviour;
- filename parsing and metadata-handler type;
- virtual-workspace root and source projection;
- compatible storage backends;
-- viewer metadata required for component-aware display.
+- viewer metadata required for axis-aware display.
-The handler registry is the source of truth. Generic discovery, compiler, and
-UI code iterate ``MicroscopeHandler.__registry__.values()`` or the registry
-service. They must not maintain a copied microscope list or import concrete
-handler modules to inspect names.
+The registry is the source of truth. Generic discovery, compiler, and UI code
+iterate ``DatasetSource.__registry__`` or ``DatasetSourceChoice.choices()``.
+They must not maintain a copied source list or import concrete source modules
+to inspect names.
-The configurable ``Microscope`` members, excluding ``AUTO``, match the
-registered handler keys exactly. This keeps user-facing choices, agent
-discovery, and runtime construction on the same declaration set without hidden
-handlers or orphaned enum values.
+Selection
+---------
-The factory binds the root-declared ``plate_folder`` context uniformly after a
-registered leaf is created. Leaf-specific initialisation belongs on that
-leaf's ``create`` hook; the factory does not dispatch on concrete handler
-names.
+``PipelineConfig.dataset_source`` holds a ``DatasetSourceChoice`` class:
+``AutoDetectedSource`` (spelled ``auto``) or one registered source. Boundaries
+spell the choice by ``source_name``. Automatic detection walks
+``DatasetSource.detection_order()``: prepared OpenHCS workspaces first, then
+sources ranked by their selection-role mixin. Selection roles such as
+``FormatSpecificSource``, ``DeclaredFileSource``, and
+``PreparedWorkspaceSource`` are capability mixins on the source class.
+
+``DatasetSourceChoice.open`` resolves the source class, lets declared source
+bindings select the declared-file source when the role allows it, constructs
+the leaf through its ``create`` hook, and binds ``dataset_root`` uniformly.
+Leaf-specific initialisation belongs on that leaf's ``create`` hook; the opener
+does not dispatch on concrete source names.
Source and compiler boundary
----------------------------
-After initialisation, the handler supplies plate metadata and source files to
+After initialisation, the source supplies dataset metadata and source files to
the source-workspace projection. The compiler uses that projection to construct
source universes, execution-axis values, main-flow dependencies, and named
source bindings. PolyStore owns the underlying references, virtual paths, and
backend I/O.
-A handler may use a decoder-owned metadata handler while discovering exact
-source planes. Once it persists the canonical virtual workspace, its active
+A source may use a decoder-owned metadata handler while discovering exact
+source planes. Once it persists the virtual workspace, its active
``metadata_handler`` becomes the workspace metadata owner. Consumers therefore
-read one post-initialisation authority instead of conditionally combining raw
-store coordinates with projected workspace coordinates.
+read one post-initialisation metadata handler instead of conditionally
+combining raw store coordinates with projected workspace coordinates.
``MetadataHandler.component_value_set()`` returns one complete
``MetadataComponentValueSet`` keyed by the axes of the active ``AxisFamily``.
-Handlers implement that single projection; generic consumers
-iterate the family instead of calling one handler method per concrete
-axis. Viewer and serialized mappings receive string keys only at their explicit
-boundary.
+Metadata handlers implement that single projection; generic consumers iterate
+the family instead of calling one method per concrete axis. Viewer and
+serialized mappings receive string keys only at their explicit boundary.
-The persisted workspace records the handler declaration that produced its
-backing references. When the workspace is reopened, that registered handler
-class re-registers its decoder backends together with the plate-specific
-virtual-workspace backend. Replay therefore follows the original declaration
+The persisted workspace records the ``source_name`` of the source that produced
+its backing references. When the workspace is reopened, that registered source
+class re-registers its decoder backends together with the dataset-specific
+virtual-workspace backend. Replay therefore follows the recorded declaration
without reconstructing a configuration-dependent ingestion instance or
-requiring the original ``SourceBindingsConfig``.
+requiring the ``SourceBindingsConfig`` used at ingestion.
Extension boundary
------------------
-A new microscope integration is a registered handler leaf with its parser,
-metadata handler, identity, detection policy, and backend compatibility at the
-owning module. Dataset-specific ordinary-file layouts whose selection and axes
-can be declared with ``SourceBindingsConfig`` use the source-binding handler;
-the dataset name is not registered as a microscope type. Generic discovery and
-compilation consume the resulting declaration.
+A new format integration is a registered ``DatasetSource`` leaf, in a module
+listed by the active family's ``extension_modules``, with its parser, metadata
+handler, identity, selection role, and backend compatibility at the owning
+module. Dataset-specific ordinary-file layouts whose selection and axes can be
+declared with ``SourceBindingsConfig`` use the source-binding source
+(``source_bindings``); the dataset name is not registered as a source.
+Generic discovery and compilation consume the resulting declaration.
The task-oriented procedure and required tests are in
:doc:`../development/source_binding_extension`; the ownership rule is expanded
in :doc:`nominal_ownership`.
diff --git a/docs/source/architecture/multiprocessing_coordination_system.rst b/docs/source/architecture/multiprocessing_coordination_system.rst
index aa1f084c50..18e01cadd0 100644
--- a/docs/source/architecture/multiprocessing_coordination_system.rst
+++ b/docs/source/architecture/multiprocessing_coordination_system.rst
@@ -4,22 +4,25 @@ Multiprocessing coordination
Multiprocessing is coordinated from compiled execution facts rather than from
a second task-planning model.
-1. The microscope/source model discovers execution-axis values.
+1. The dataset source discovers the values of the active axis family's
+ partition axis.
2. ``PipelineCompiler`` produces a frozen ``ProcessingContext`` for each value.
-3. Bundle assembly validates the contexts and worker-safe steps.
+3. Bundle execution validates the contexts and worker-safe steps.
4. The worker-start policy chooses one multiprocessing context for the entire
execution.
-5. The executor submits one task per compiled axis and collects typed
- ``ExecutionResult`` values.
+5. The lane planner assigns the compiled axis contexts to deterministic worker
+ lanes. The executor submits one task per active lane and collects a typed
+ ``ExecutionResult`` for each axis.
-Worker processes install the ``CompiledExecutionBundle`` through the worker-lane
-authority. Runtime stores and adapters are process-local; cross-process state is
-limited to serialized bundle data, progress channels, and result messages.
-Cancellation remains parent-owned: process workers are interrupted through the
-execution server's worker ownership boundary, whereas inline and threaded lanes
-observe the execution-scoped cooperative cancellation signal directly.
+Worker processes install the ``CompiledExecutionBundle`` through
+``ForkInheritedWorkerExecutionState``, either inherited at fork or unpickled
+from the bundle shipped to a prepared worker. Runtime stores and adapters are
+process-local; cross-process state is limited to serialized bundle data,
+progress channels, and result messages. Cancellation remains parent-owned:
+process workers are terminated by the executor resources that own them, whereas
+inline and threaded lanes observe the execution-scoped cooperative cancellation
+signal directly.
-Generic code must not ask the orchestrator for hard-coded component names.
-Component identities come from the source model and nominal component
-declarations. See :doc:`component_system_integration` and
-:doc:`concurrency_model`.
+Generic code must not ask the orchestrator for hard-coded axis names. Axis
+identities come from the active ``AxisFamily`` declaration and the dataset
+source. See :doc:`component_system_integration` and :doc:`concurrency_model`.
diff --git a/docs/source/architecture/nominal_ownership.rst b/docs/source/architecture/nominal_ownership.rst
index 05ed756126..ef0d66c8bc 100644
--- a/docs/source/architecture/nominal_ownership.rst
+++ b/docs/source/architecture/nominal_ownership.rst
@@ -34,7 +34,8 @@ An ``AutoRegisterMeta`` family has a nominal root and an authoritative
use a shared selection mixin. ``RegistryConfig`` and ``RegistryFamily`` describe
generic registration mechanics and are owned by metaclass-registry.
-OpenHCS uses three shared selection models:
+OpenHCS uses three shared selection models, imported from
+``metaclass_registry.strategies``:
``EnumKeyedStrategyMixin``
A concrete strategy declares the enum member it owns. The mixin derives the
@@ -72,7 +73,9 @@ Examples in the current architecture
------------------------------------
- ``ArtifactType`` and module artifact partitions own artifact roles.
-- ``RuntimeSliceProjectionStrategy`` selects behavior by nominal runtime type.
+- owned runtime values subclass ``RuntimeSliceProjectableValue`` and implement
+ their own runtime-slice projection; ``RuntimeSliceProjection`` handles only
+ foreign values such as arrays, containers, and primitives.
- runtime equivalence feature profiles select the most-derived semantic owner
for a feature context.
- ``ProcessingContract`` subclasses own how a callable executes over its
diff --git a/docs/source/architecture/orchestrator_cleanup_guarantees.rst b/docs/source/architecture/orchestrator_cleanup_guarantees.rst
index 55d2a0d345..54cbb01d7c 100644
--- a/docs/source/architecture/orchestrator_cleanup_guarantees.rst
+++ b/docs/source/architecture/orchestrator_cleanup_guarantees.rst
@@ -1,7 +1,7 @@
Execution cleanup guarantees
============================
-The compiled plate execution boundary makes cleanup converge across success,
+The compiled-bundle execution boundary makes cleanup converge across success,
failure, cancellation, and a broken process pool.
Viewer acquisition is part of that execution transaction. If one required
diff --git a/docs/source/architecture/orchestrator_configuration_management.rst b/docs/source/architecture/orchestrator_configuration_management.rst
index 507ccb5dbd..4713b27ae5 100644
--- a/docs/source/architecture/orchestrator_configuration_management.rst
+++ b/docs/source/architecture/orchestrator_configuration_management.rst
@@ -18,12 +18,13 @@ through its declared ObjectState boundary. The desktop forms and code projection
edit the same state. Assignment or restoration changes the declaration; it does
not mutate already compiled contexts.
-A local plate path is declaration identity before it is a runtime input. Session
-restoration can therefore reconstruct and expose a plate whose directory is
-temporarily unavailable. The selected microscope handler's source role owns its
-availability contract, which ``PipelineOrchestrator.initialize`` enforces. A
-failed check leaves that orchestrator uninitialized, so the declaration remains
-editable without pretending that source discovery succeeded.
+A local dataset root is declaration identity before it is a runtime input.
+Session restoration can therefore reconstruct and expose a plate whose directory
+is temporarily unavailable. The selected ``DatasetSource`` class's
+``SourceSelectionRole`` owns its availability contract, which
+``PipelineOrchestrator.initialize`` enforces. A failed check leaves that
+orchestrator uninitialized, so the declaration remains editable without
+pretending that source discovery succeeded.
Ownership
---------
diff --git a/docs/source/architecture/pattern_grouping_and_special_outputs.rst b/docs/source/architecture/pattern_grouping_and_special_outputs.rst
index 19f907298b..8c2e47af0d 100644
--- a/docs/source/architecture/pattern_grouping_and_special_outputs.rst
+++ b/docs/source/architecture/pattern_grouping_and_special_outputs.rst
@@ -7,7 +7,7 @@ Grouping and output terminology (transition)
output-slot names, artifact declarations, and VFS paths. Those are independent
systems in the current architecture.
-Use these canonical pages:
+Use these pages:
- :doc:`processing_semantics` for ``variable_components``, ``group_by``,
dictionary pattern fan-out, and ``ProcessingContract``;
diff --git a/docs/source/architecture/pipeline_compilation_system.rst b/docs/source/architecture/pipeline_compilation_system.rst
index 821741d2e7..d618d44767 100644
--- a/docs/source/architecture/pipeline_compilation_system.rst
+++ b/docs/source/architecture/pipeline_compilation_system.rst
@@ -19,15 +19,10 @@ Output
Resolve once
------------
-The normal compiler path materializes the pipeline and each step from ObjectState
-once. ``ResolvedPipelineDefinition`` groups the resolved steps, their ObjectState
-map, and a ``StepSnapshot`` tuple shared by every axis compilation.
-
-A ``StepSnapshot`` contains:
-
-- stable step index
-- ObjectState scope identity
-- the already-resolved step object
+The normal compiler path resolves the pipeline and each saved step through
+ObjectState once, without registering editor states. ``ResolvedPipelineDefinition``
+holds the enabled resolved steps with each step's ObjectState scope identity and
+saved provenance, keyed by step index, and is shared by every axis compilation.
All step semantics remain on that resolved step and its authoritative callable
or module declarations. Later stages must not call ObjectState again to recover
@@ -46,19 +41,21 @@ wrapper identity rather than create a second callable projection.
Native module families may declare both their owned callable names and the
catalogue module that exposes them. Catalogue discovery projects those nominal
-claims, rejects duplicate canonical names, and does not maintain a parallel
+claims, rejects duplicate callable names, and does not maintain a parallel
module-to-function list.
-Compilation owns the deferred warmup. Callable leaves become typed
-``FunctionReference`` and ``CallableContract`` values. Resolving those
-references initializes the function catalog, and
-``prepare_compiled_context_callables()`` resolves and caches every compiled
-invocation and runs its declaration-owned module or callable preparation hook
-before the execution bundle is emitted. A first compilation may therefore pay
-catalog and backend preparation costs; function-step orchestration does not own
-another preparation phase in its hot path. A spawn worker may still resolve a
-reference and rebuild a process-local callable cache because parent-process
-caches cannot cross that process boundary.
+An execution endpoint prepares the registered catalogue before it reports
+readiness: ``RegistryService.prepare_in_current_process()`` populates kernel
+caches, runs each callable's declaration-owned preparation hook, and publishes
+prepared signatures. Compilation then binds the selected callables. Callable
+leaves become typed ``FunctionReference`` and ``CallableContract`` values;
+``CallableContract.from_prepared_callable()`` prepares an authored target that
+the catalogue did not already prepare, and each ``CompiledFunctionInvocation``
+resolves its runtime callable through the process-local contract cache when it
+is built, before the execution bundle is emitted. Function-step orchestration
+does not own another preparation phase in its hot path. A spawn worker may still
+resolve a reference and rebuild a process-local callable cache because
+parent-process caches cannot cross that process boundary.
This ordering is an ownership boundary, not merely a startup optimization.
Source inspection must stay safe and bounded, while compilation must fail if a
@@ -82,14 +79,14 @@ new ``ProcessingContext`` before constructing the narrower session boundary.
Each axis receives a ``CompilationSession`` joining:
- the axis ``ProcessingContext``
-- resolved steps and snapshots
-- the step ObjectState map
-- global and orchestrator configuration
+- the shared ``ResolvedPipelineDefinition``
+- the resolved global configuration and the orchestrator
- the virtual source-workspace projection
- the mutable ``CompiledStepPlan`` map
-- plate and transport execution facts
+- dataset scope, path resolution, and transport execution facts
-The session validates one-to-one step, state, snapshot, and plan identity. A
+``ResolvedPipelineDefinition`` rejects a step without scope and provenance
+facts, and the session reports a missing compiled plan for any step index. A
compiler stage accepts the session or a narrower typed request derived from it;
it does not pass loosely related dictionaries between phases.
@@ -100,12 +97,12 @@ Typed step plans
single plan authority for a step. Important fields include:
- input/output paths and storage backends
-- source binding, source universe, and source load plans
+- source binding and source universe plans
- main-flow dependency
- artifact input/output plans and execution group scope
- compiled function pattern and callable contracts
- variable components, grouping, and sequential filters
-- input, output, and execution memory roles plus framework-local device bindings
+- input and output memory types plus framework-local device bindings
- materialization plans and enabled streaming declarations
Stages mutate these fields directly. Code should not recreate
diff --git a/docs/source/architecture/plate_manager_services.rst b/docs/source/architecture/plate_manager_services.rst
index 72b628f62c..8aae1322bc 100644
--- a/docs/source/architecture/plate_manager_services.rst
+++ b/docs/source/architecture/plate_manager_services.rst
@@ -13,7 +13,7 @@ Ownership map
``session.py``
``Session``: dataset add/delete/split, initialisation, compile and run, stop,
- debug runs, finished-execution records, admission checks, and the ports a
+ debug runs, finished-execution records, work and mutation checks, and the ports a
client supplies (``MainThread``, ``DatasetAccess``, ``Renderer``).
``operations/``
diff --git a/docs/source/architecture/processing_semantics.rst b/docs/source/architecture/processing_semantics.rst
index 6378d413ad..0dc418a79f 100644
--- a/docs/source/architecture/processing_semantics.rst
+++ b/docs/source/architecture/processing_semantics.rst
@@ -1,9 +1,11 @@
Axes, grouping, and processing semantics
========================================
-OpenHCS transports image data as 3D arrays, including logical single-plane
-inputs. Array rank alone does not determine what the leading/third axis means or
-whether a callable has per-plane or whole-stack semantics.
+OpenHCS transports image data as tensor payloads whose dimensions are declared
+(``PayloadAxes``): a leading runtime plane axis, spatial dimensions named by the
+payload's spatial domain, and other declared axes such as colour samples. Array
+rank alone does not determine what the leading axis means or whether a callable
+has per-plane or whole-stack semantics.
Five questions, five owners
---------------------------
@@ -58,21 +60,25 @@ Related execution declarations
Processing contracts
--------------------
-``PURE_2D``
+Each contract is a ``ProcessingContract`` subclass that owns its execution
+algorithm; a callable declares one by class, for example
+``@numpy(contract=Pure2DContract)``.
+
+``Pure2DContract``
The result for one plane is semantically independent of every other plane.
- OpenHCS may still transport or batch a 3D array, but the contract executes and
+ OpenHCS may still transport or batch a stack, but the contract executes and
aggregates through nominal per-slice strategies.
-``PURE_3D``
+``Pure3DContract``
The callable depends on the full variable-component stack and executes once
with whole-stack semantics.
-``FLEXIBLE``
+``FlexibleContract``
The callable supports both modes through an explicit semantic-control
- parameter. The contract consumes that control and delegates to ``PURE_2D`` or
- ``PURE_3D``; callers do not infer the mode from shape.
+ parameter. The contract runs per plane when its slice-by-slice control is on
+ and on the whole stack otherwise; callers do not infer the mode from shape.
-``VOLUMETRIC_TO_SLICE``
+``VolumetricToSliceContract``
The callable consumes a real stack and produces a collapsed plane domain. The
contract updates payload provenance to reflect the consumed leading axis.
@@ -100,8 +106,10 @@ the resolved facts, including selected framework-local device bindings, on
Runtime projection
------------------
-When a contract requires per-slice execution, ``RuntimeSliceProjection`` selects
-a registered strategy for each nominal value family. Image payloads, object
+When a contract requires per-slice execution, each owned runtime value family
+projects itself: it subclasses ``RuntimeSliceProjectableValue`` and declares its
+slice count and per-slice value. ``RuntimeSliceProjection`` handles only foreign
+values such as arrays, containers, and primitives. Image payloads, object
labels, measurements, relationships, tables, and aligned collections retain
their semantic identity during projection and aggregation. A missing projection
declaration is an error; OpenHCS does not guess from an object's shape or fields.
@@ -110,9 +118,9 @@ Common mistakes
---------------
- Treating ``group_by`` as the definition of the stack axis.
-- Treating a named source binding as a third ``InputSource`` enum value.
+- Treating a named source binding as a third ``InputSource`` choice.
- Treating a value's presence in the runtime store as a persistence request.
-- Treating ``PURE_2D`` as “the callable only accepts rank-2 arrays.”
+- Treating ``Pure2DContract`` as “the callable only accepts rank-2 arrays.”
- Passing ``variable_components`` or ``group_by`` directly to
``FunctionStep`` instead of its processing configuration.
- Adding backend-specific dispatch to core rather than extending the callable or
diff --git a/docs/source/architecture/progress_runtime_projection_system.rst b/docs/source/architecture/progress_runtime_projection_system.rst
index e72a4aa7f7..00735b8d06 100644
--- a/docs/source/architecture/progress_runtime_projection_system.rst
+++ b/docs/source/architecture/progress_runtime_projection_system.rst
@@ -16,8 +16,9 @@ OpenHCS Implementation Path
OpenHCS-specific modules:
-- ``openhcs.core.progress.types`` owns the registered channel, phase, and status
- semantics carried by ``ProgressEvent``.
+- ``openhcs.core.progress.types`` owns the ``ProgressStream``, ``ProgressPhase``,
+ and ``ProgressStatus`` class families carried by ``ProgressEvent``; each phase
+ owns its stream and terminal semantics.
- ``openhcs.core.progress.registry`` owns OpenHCS semantic keying over the generic
registry.
- ``openhcs.core.progress.projection`` and
@@ -31,32 +32,35 @@ Consumption path:
1. Emit typed ``ProgressEvent`` values from compile/execute flows.
2. Route events through explicit queue wiring via ``set_progress_queue(...)``.
-3. Register events in ``ProgressRegistry`` with semantic channel keying.
+3. Register events in ``ProgressRegistry`` keyed by subject, axis, and stream.
4. Reconcile typed server-heartbeat running/queued entries by execution and
- plate identity in ``ExecutionRuntimeProjection``.
+ subject identity in ``ExecutionRuntimeProjection``.
5. Build runtime and execution-tree projections for status derivation.
-6. Coalesce Plate Manager registry mutations through its explicitly
- GUI-thread-owned timer.
-7. Render the same projection in Plate Manager and ZMQ server browser services.
+6. Coalesce registry mutations in the session's ``ExecutionProgress``: each
+ mutation marks the projection dirty, and the session's main thread rebuilds
+ it at most once per interval and publishes it as session events.
+7. Render the same projection in the Plate Manager (through session events) and
+ the ZMQ server browser.
OpenHCS Invariants
------------------
-- Canonical execution tree path is ``plate -> worker -> well -> step``.
-- ``PATTERN_GROUP`` is step-detail only and does not set well pipeline percent.
-- Plate/server status is derived from projection snapshots, not mutable cache.
+- The execution tree path is ``subject -> worker -> partition -> step``.
+- ``PatternGroupProgressPhase`` is step-detail only and does not set partition pipeline
+ percent.
+- Subject/server status is derived from projection snapshots, not mutable cache.
- Event retention is monotonic per semantic key. Stale or duplicate timestamps
are rejected before listeners and typed side-effect consumers run.
- Topology is reconstructed from the retained event snapshot. A late subscriber
may render a shallow lifecycle node when the initialization event is absent.
-- ``PlateRuntimeStateDeclarationBase`` owns state labels, count labels,
+- Each ``SubjectRuntimeState`` subclass owns its status label, count label,
terminal policy, and live-server transition policy. ``state_counts`` is the
only retained count projection; per-state count fields are forbidden.
-- Runtime identity is ``(execution_id, plate_id)``. A new queued execution may
- replace an older visible execution for the same plate without conflating their
- retained histories.
-- A background progress callback may only dirty the retained projection. Qt
- timers and row rendering are application-thread lifecycle responsibilities.
+- Runtime identity is ``SubjectRuntimeIdentity(execution_id, subject_id)``. A
+ new queued execution may replace an older visible execution for the same
+ subject without conflating their retained histories.
+- A background progress callback may only dirty the retained projection.
+ Rebuilding and rendering run on the client's main thread.
Debugger ownership
------------------
@@ -64,10 +68,11 @@ Debugger ownership
Debugger execution uses the normal progress transport but does not reduce its
retained state to status text:
-- ``DebugCommandDeclarationBase`` owns command semantics, while the registered
- execution and stop strategies implement command-specific runtime behavior.
-- ``DebugBoundaryEventDeclarationBase`` owns the progress status, boundary
- outcome, timeline state, and artifact-reporting policy for each boundary event.
+- Each ``DebugCommandType`` subclass is one command and owns what it does to a
+ paused worker; the execution and stop strategy families, keyed by command
+ class, implement command-specific bounded-run behaviour.
+- Each ``DebugBoundary`` subclass is one boundary event and owns its progress
+ status and whether it reports output artifacts or carries an exception.
- ``DebugCursor`` and ``DebugSession`` retain the exact invocation and controller
identity. ``DebugSnapshot`` retains boundary evidence, and ``DebugArtifactRef``
plus ``DebugArtifactIdentity`` retain typed artifact provenance.
@@ -85,12 +90,12 @@ Debugger projection path
``ProgressEvent``. ``RuntimeProjectionBuilder`` then builds the normal execution
projection and the debugger projection from the same immutable event snapshot.
``DebugRuntimeProjectionBuilder`` filters events by debug session, resolves their
-snapshots, and derives current, last, and timeline frames while retaining the
+snapshots, and derives the current and last frames while retaining the
normal execution projection beside them.
The runtime projection is not the session-state machine. Given the selected
target, active session, manager execution state, terminal summary, pause state,
-and snapshots, ``DebugSessionPhaseDeclarationBase`` selects the current debugger
+and snapshots, ``DebugSessionPhase.for_context`` selects the current debugger
phase. Toolbar, inspector, UI-bridge, agent, and MCP-facing adapters consume that
typed session context and projection instead of re-deriving phase semantics.
@@ -129,21 +134,22 @@ command declarations, debugger snapshots, or debugger session phase state.
Axis completion is not plate completion
---------------------------------------
-Workers execute only axis-scoped steps. ``AXIS_COMPLETED`` reports the number
-of completed axis positions and deliberately stops at the first terminal
-plate-scoped step. It is a successful terminal event for that worker/axis
-channel, but it does not assert that the plate execution has finished.
+Workers execute only axis-scoped steps. ``AxisCompletedProgressPhase`` reports
+the number of completed axis positions and deliberately stops at the first
+terminal plate-scoped step. It is a successful terminal event for that
+worker/axis key, but it does not assert that the plate execution has finished.
After every worker result succeeds, the parent merges runtime records and calls
-``execute_plate_scoped_steps()`` once for each terminal plate step. The parent
+``execute_dataset_scoped_steps()``, which runs each terminal plate-scoped step
+once. The parent
then materializes those outputs, runs analysis consolidation, settles viewer
state, and projects the orchestrator result. Only after those stages succeed
-does it emit the pipeline ``SUCCESS`` event at 100 percent. Failure,
-cancellation, viewer settlement, or terminal-step errors therefore cannot be
-hidden behind an earlier worker ``AXIS_COMPLETED`` event.
+does it emit the pipeline ``SuccessProgressPhase`` event at 100 percent.
+Failure, cancellation, viewer settlement, or terminal-step errors therefore
+cannot be hidden behind an earlier worker ``AxisCompletedProgressPhase`` event.
-Canonical Abstraction Docs
---------------------------
+Abstraction Docs
+----------------
See the published ``zmqruntime`` owner documentation for abstraction internals:
diff --git a/docs/source/architecture/quick_start.rst b/docs/source/architecture/quick_start.rst
index 288729e4de..9646e61c6d 100644
--- a/docs/source/architecture/quick_start.rst
+++ b/docs/source/architecture/quick_start.rst
@@ -39,7 +39,7 @@ Every supported authoring route produces the same public declaration shape:
PipelineConfig + ordered list[FunctionStep]
-> ObjectState resolves lazy configuration
- -> StepSnapshot + CompilationSession
+ -> ResolvedPipelineDefinition + CompilationSession
-> typed CompiledStepPlan objects
-> CompiledExecutionBundle
-> runtime values, artifacts, and materialized outputs
@@ -205,15 +205,16 @@ Keep these independent concepts separate:
function patterns. It does not define the stack axis.
``ProcessingContract``
- Declares callable locality. ``PURE_2D`` retains per-plane semantics even when
- planes travel in a batch; ``PURE_3D`` means the result depends on the stack.
+ Declares callable locality. ``Pure2DContract`` retains per-plane semantics
+ even when planes travel in a batch; ``Pure3DContract`` means the result
+ depends on the stack.
Unsupported microscope folders
-------------------------------
``SourceBindingsConfig`` is the supported generic input boundary when a folder
-does not match a registered microscope handler. A non-empty source-binding
-declaration makes auto-detection select ``SourceBindingsHandler``. The agent or
+does not match a registered dataset source. A non-empty source-binding
+declaration makes auto-detection select ``SourceBindingsSource``. The agent or
user describes the files; processing callables remain independent of physical
filenames.
@@ -221,6 +222,7 @@ For files such as ``A01_s1_DNA.tif`` and ``A01_s1_GFP.tif``:
.. code-block:: python
+ from openhcs.constants.input_source import ReadPipelineStart
from openhcs.core.config import (
LazyProcessingConfig,
LazySourceBindingsConfig,
@@ -229,13 +231,15 @@ For files such as ``A01_s1_DNA.tif`` and ``A01_s1_GFP.tif``:
)
from openhcs.core.source_bindings import (
ComponentSelector,
+ FromFileName,
MetadataExtractionRule,
- MetadataSource,
NamedSourceBinding,
+ PathIsImage,
+ SourceExtension,
+ SourceFileName,
SourceFilterClause,
- SourceFilterMatchType,
- SourceFilterSubject,
SourceSelector,
+ TextContains,
)
from openhcs.core.steps.function_step import FunctionStep
from openhcs.domains.microscopy.axes import Microscopy
@@ -249,8 +253,8 @@ For files such as ``A01_s1_DNA.tif`` and ``A01_s1_GFP.tif``:
selector=SourceSelector(
filters=(
SourceFilterClause(
- subject=SourceFilterSubject.FILE,
- match_type=SourceFilterMatchType.CONTAINS,
+ subject=SourceFileName,
+ match_type=TextContains,
value=token,
),
),
@@ -264,13 +268,13 @@ For files such as ``A01_s1_DNA.tif`` and ``A01_s1_GFP.tif``:
source_bindings_config=LazySourceBindingsConfig(
source_filters=(
SourceFilterClause(
- subject=SourceFilterSubject.EXTENSION,
- match_type=SourceFilterMatchType.IS_IMAGE,
+ subject=SourceExtension,
+ match_type=PathIsImage,
),
),
metadata_rules=(
MetadataExtractionRule(
- source=MetadataSource.FILE_NAME,
+ source=FromFileName,
pattern=(
r"^(?P[A-H][0-9]{2})_"
r"s(?P[0-9]+)_(?P[^.]+)"
@@ -289,7 +293,7 @@ For files such as ``A01_s1_DNA.tif`` and ``A01_s1_GFP.tif``:
name="Normalize DNA",
func=stack_percentile_normalize,
processing_config=LazyProcessingConfig(
- input_source=InputSource.PIPELINE_START,
+ input_source=ReadPipelineStart,
),
source_bindings=LazyStepSourceBindingsConfig(
enabled=True,
@@ -322,8 +326,8 @@ materialization, memory conversion, worker requirements, and execution scope.
Runtime workers consume the resulting ``CompiledExecutionBundle`` and typed
runtime values; they do not rediscover those contracts from strings or files.
-The desktop application and MCP execution services own progress, cancellation,
-and result presentation. For the explicit lower-level Python boundary, see
+The OpenHCS ``Session`` that both the desktop application and MCP operate owns
+progress, cancellation, and result presentation. For the explicit lower-level Python boundary, see
:doc:`../api/index`.
Current example authorities
diff --git a/docs/source/architecture/roi_system.rst b/docs/source/architecture/roi_system.rst
index dd15c0b5a5..74c74c35a8 100644
--- a/docs/source/architecture/roi_system.rst
+++ b/docs/source/architecture/roi_system.rst
@@ -38,16 +38,16 @@ type, producer, execution scope, source relations, materialization options, and
object-subject relations. Runtime materialization gives the registered writer
that complete plan.
-Object-label artifacts use their nominal materialization strategy to project
-label members into ROI shapes. Spatial graphs have two independent registered
-projections:
-
Projected materialization keeps semantic source identity separate from
filename identity. A semantic identity may intentionally omit an axis that was
collapsed by processing. The complete record-source identity may fill only the
missing parser coordinates needed to construct a filename; it does not restore
that axis to the artifact metadata.
+Object-label artifacts use their nominal materialization strategy to project
+label members into ROI shapes. Spatial graphs have two independent registered
+projections:
+
``SpatialGraphROIOptions``
Writes one feature-bearing polyline ROI per graph edge. It retains edge
identity and scalar branch features. ImageJ ROI geometry is two-dimensional,
diff --git a/docs/source/architecture/runtime_value_system.rst b/docs/source/architecture/runtime_value_system.rst
index dbf7cf8665..ee9c958077 100644
--- a/docs/source/architecture/runtime_value_system.rst
+++ b/docs/source/architecture/runtime_value_system.rst
@@ -13,8 +13,8 @@ Value families
Nominal array protocol with explicit NumPy interoperation.
image payloads
- Image data plus mask, plane-axis declaration, source metadata, spatial domain,
- and source-plane provenance.
+ ``ImagePayload``: image data plus mask, declared payload axes, plane-axis
+ declaration, source metadata, spatial domain, and source-plane provenance.
object-label values
Dense or sparse labeled-object domains, grouped sets, and storage/building
@@ -38,8 +38,8 @@ semantic identity. Persistence location is represented separately by
``RuntimeArtifactLocation``.
Invocation-selected output plans remain keyed by ``ArtifactSpecRef`` when they
-enter ``RuntimeAdapterRequest``. ``RuntimeReturnedOutputMatcher`` binds returned
-positions to the exact plan/spec refs, and adapter lookup accepts the
+enter ``RuntimeAdapterRequest``. ``CallableContract.resolve_returned_plan_values``
+binds returned positions to the exact plan/spec refs, and adapter lookup accepts the
declaration-derived ref whose key agrees with its plan. Two outputs may
therefore share a name when their artifact types differ: selection, matching,
and request lookup preserve both identities rather than collapsing to the name.
@@ -71,26 +71,32 @@ part of artifact identity rather than encoded into ad hoc path strings.
Runtime slice projection
------------------------
-``RuntimeSliceProjectionStrategy`` is a nominal-type registry family. A strategy
-declares the runtime value type it owns. Selection follows the value's MRO, and
-projection uses explicit plane-axis and source-identity declarations.
+Each owned runtime value family subclasses ``RuntimeSliceProjectableValue`` and
+implements its own slice behaviour: slice count, declared plane axis, per-slice
+values, and alignment slices. Projection uses explicit plane-axis and
+source-identity declarations.
-Registered strategies cover image payloads, aligned image collections, object
-labels, measurement tables, columnar rows, sparse labels, relationships, and
-identity-projectable semantic values. Primitive/pass-through types are declared
-explicitly. If a new value family can carry a runtime slice, its projection and
-aggregation behavior must be registered with the value type.
+Image payloads, aligned value sets, object labels, columnar rows (including
+measurement tables and sparse labels), relationships, and identity-projectable
+semantic values implement it. ``RuntimeSliceProjection`` handles only foreign
+values: it lists the array, mapping and primitive types that carry no
+runtime-slice axis, and projects tuples and lists item by item. Any other value
+without the ABC is an error. If a new value family can carry a runtime slice, it
+must subclass ``RuntimeSliceProjectableValue``.
Function-output contextualization
---------------------------------
A callable return does not inherit context from whichever runtime value happens
-to be nearby. ``FunctionOutputContextStrategy`` first selects behavior from the
-exact compiled output artifact type. For an image output,
-``ImageOutputSourceContextStrategy`` then selects behavior from the nominal type
-of the source chosen by the output's compiled ``source_context_source``
-relation. This is a two-stage owner dispatch, not a table of callable names or
-payload shapes.
+to be nearby. The exact compiled output artifact type first selects behaviour
+through its ``ArtifactType.contextualize_output`` method, which asks the
+returned value's ``contextualized_output`` which of the type's hooks applies:
+a plain value, an image payload, a source-projected image, or one value per
+runtime slice. For an image payload, ``ImageArtifactType`` then calls
+``contextualize_image_output`` on the source value chosen by the output's
+compiled ``source_context_source`` relation, so the source's nominal type
+decides. This is owner dispatch on the artifact type, the returned value, and
+the source, not a table of callable names or payload shapes.
An image result that already carries context may bypass derivation only when its
source identity is complete and, for a declared artifact output, its represented
@@ -103,7 +109,7 @@ Image results that select or reorder source planes implement
``SourceProjectedImageOutput``. Their leaf implementation validates the declared
selection and projects source metadata before generic image contextualization.
``SelectedPlaneImageOutput`` carries a cropped array and its ordered source
-indices, preserving channel identity even when only one channel remains.
+indices, preserving source-plane identity even when only one plane remains.
Object-label sources have a specialized image-output rule. A rendered label
volume with no selected plane must acquire the invocation's declared plane axis,
@@ -118,11 +124,10 @@ the artifact name and type, and the runtime store owns the resulting artifact
key. A contextualizer must not rename a value, select a same-named plan, or
coerce one artifact family into another.
-Artifact families without contextual image semantics use the root
-``UnchangedFunctionOutputContextStrategy``. It returns their values unchanged
-and does not invoke the image plane projector. Image, measurements, spatial
-graph, and object-label declarations select more-derived strategies through the
-artifact-type hierarchy. The artifact declaration therefore determines
+Artifact families without contextual image semantics inherit the root
+``ArtifactType`` methods. They return their values unchanged and do not invoke
+the image plane projector. Image, measurements, spatial grid, spatial graph,
+and object-label artifact types override them. The artifact declaration therefore determines
projection behaviour; individual backend result classes do not need a copied
exemption registry.
diff --git a/docs/source/architecture/serialization_boundaries.rst b/docs/source/architecture/serialization_boundaries.rst
index 995729ad35..ef835c2806 100644
--- a/docs/source/architecture/serialization_boundaries.rst
+++ b/docs/source/architecture/serialization_boundaries.rst
@@ -8,8 +8,11 @@ Python source
-------------
pycodify owns generic executable-Python generation, import construction,
-formatter registration, collision handling, and clean versus explicit output.
-OpenHCS owns formatters and round-trip policy for its domain declarations. Code
+formatter registration, collision handling, absolute-path factoring, and clean
+versus explicit output; a value that owns its source spelling subclasses
+pycodify's ``PythonSourceLiteral``. ``objectstate.codegen`` owns source
+formatting for lazy configuration dataclasses. OpenHCS owns formatters and
+round-trip policy for its domain declarations. Code
documents are user-editable source and must pass the normal validation boundary
before application.
diff --git a/docs/source/architecture/source_model.rst b/docs/source/architecture/source_model.rst
index d86cc30b59..be4966e911 100644
--- a/docs/source/architecture/source_model.rst
+++ b/docs/source/architecture/source_model.rst
@@ -10,11 +10,11 @@ Generic folders and unsupported microscopes
-------------------------------------------
Source bindings are the nominal microscope-independent ingestion path for
-arbitrary folders. When microscope auto-detection fails and a non-empty
-``SourceBindingsConfig`` is present, the handler factory selects
-``SourceBindingsHandler``. That handler recursively inventories the folder and
-projects only the ordinary files selected by the declarations into a virtual
-workspace.
+arbitrary folders. When dataset-source auto-detection fails and a non-empty
+``SourceBindingsConfig`` is present, ``AutoDetectedSource`` selects the
+registered declared-file source, ``SourceBindingsSource``. That source
+recursively inventories the folder and projects only the ordinary files selected
+by the declarations into a virtual workspace.
This fallback is distinct from semantic source selection on a recognized store.
Native HCS handlers retain ownership of recognized layouts; CZI, OME-TIFF, and
@@ -68,9 +68,10 @@ Declaration vocabulary
A regex over file or folder names with named capture groups. The captured
values become typed source metadata used for matching and grouping.
-``SourceBindingMatchPlan``
- The explicit order- or metadata-based rule for pairing multiple aliases into
- one logical source set.
+``SourceSetPairing``
+ The rule for pairing multiple aliases into one logical source set:
+ ``PairByOrder`` (the default when no method is declared) or
+ ``PairByMetadata`` over shared metadata keys.
Compilation
-----------
diff --git a/docs/source/architecture/special_io_system.rst b/docs/source/architecture/special_io_system.rst
index 312c842292..a24808863d 100644
--- a/docs/source/architecture/special_io_system.rst
+++ b/docs/source/architecture/special_io_system.rst
@@ -7,8 +7,9 @@ Special I/O terminology (transition)
generated-pickle-path architecture has been superseded by typed callable
contracts and compiled artifact plans. Read :doc:`artifact_contract_system`.
-``special_inputs`` is callable-ABI metadata. It names Python input parameters; they do not own artifact type, producer identity, materialization, or
-runtime storage.
+``special_inputs`` is callable-ABI metadata. It names Python input parameters
+and does not own artifact type, producer identity, materialization, or runtime
+storage. ``special_outputs`` no longer exists.
Current ownership is split across:
diff --git a/docs/source/architecture/streaming_boundary_and_wrappers.rst b/docs/source/architecture/streaming_boundary_and_wrappers.rst
index 2c0d2240c9..0d78a5b1e7 100644
--- a/docs/source/architecture/streaming_boundary_and_wrappers.rst
+++ b/docs/source/architecture/streaming_boundary_and_wrappers.rst
@@ -55,8 +55,9 @@ This path has two independent identities:
producer identity
Identifies the compiled step and exact output surface that emitted the value.
- ``FunctionStepOutputProducerIdentityAuthority`` derives it from the compiled
- plan and the returned-output or artifact context.
+ ``CompiledStepPlan.producer_identity_for_main_flow`` and
+ ``producer_identity_for_artifact`` derive it from the compiled step plan and
+ the main-flow output context or artifact output plan.
source identity
Identifies the source image plane or source domain represented by the value.
@@ -85,9 +86,9 @@ branch on a CellProfiler module name or infer labels from an ndarray dtype.
When a callable deliberately renders an image from labels, the compiled
``source_context_source`` relation selects the label artifact as the output's
-context source. Runtime contextualization then uses the registered
-``ObjectLabelImageOutputSourceContextStrategy``. For an unselected volumetric
-invocation, including a depth-one volume, that strategy requires the declared
+context source. Runtime contextualization then calls
+``ObjectLabelValue.contextualize_image_output`` on that label source. For an
+unselected volumetric invocation, including a depth-one volume, it requires the declared
plane axis, requires source-plane provenance cardinality to match it, and
validates the rendered output shape. A true scalar 2D label image remains
scalar. Array rank alone never creates source identity.
@@ -175,7 +176,7 @@ terminal progress for its synchronous update path. Every recorded route must
succeed before settlement completes, so an exception crosses the asynchronous
Qt boundary instead of becoming a silent log message or an unrelated timeout.
-Compiled plate execution follows this order on success:
+Compiled bundle execution follows this order on success:
1. settle every execution viewer;
2. read typed state from non-persistent viewers while their endpoints are live;
diff --git a/docs/source/architecture/system_overview.rst b/docs/source/architecture/system_overview.rst
index c5db47ad4c..6975aa95d2 100644
--- a/docs/source/architecture/system_overview.rst
+++ b/docs/source/architecture/system_overview.rst
@@ -17,7 +17,7 @@ End-to-end flow
| ObjectState resolves once
v
ResolvedPipelineDefinition
- + StepSnapshot tuple
+ + step scope ids and field provenance
|
| one CompilationSession per execution axis
v
@@ -55,18 +55,18 @@ Resolution boundary
ObjectState owns generic lazy configuration and live editing. Before compilation
continues, OpenHCS materializes one resolved step object for every declaration.
-``StepSnapshot`` binds compiler identity and ObjectState scope identity to that
-already-resolved step. Downstream compiler stages read semantics from the
-resolved step and its callable/module declarations; they do not repeatedly ask
-ObjectState to reinterpret fields.
+``ResolvedPipelineDefinition`` holds those resolved steps with each step's
+ObjectState scope id and per-field provenance. Downstream compiler stages read
+semantics from the resolved step and its callable/module declarations; they do
+not repeatedly ask ObjectState to reinterpret fields.
Compilation boundary
--------------------
-``CompilationSession`` ties together one ``ProcessingContext``, the resolved
-steps, snapshots, ObjectState map, source-workspace projection, and mutable typed
-plans for one execution axis. Its invariants prevent phases from silently
-mixing steps, scopes, or contexts.
+``CompilationSession`` ties together one ``ProcessingContext``, the
+``ResolvedPipelineDefinition``, source-workspace projection, and mutable typed
+plans for one execution axis. Phases read steps and plans from that one session
+by step index, and a missing plan is an error rather than a default.
Each ``CompiledStepPlan`` is the source of truth for the compiled step. It has
typed fields for source bindings, source universe, artifact inputs and outputs,
@@ -90,8 +90,8 @@ resolving declaration configuration again.
Runtime data is represented by nominal value families and stored under typed
artifact keys. ``RuntimeValueStore`` is the authority for validated values,
-locations, replacement, queries, and observation history. Projection strategies
-adapt values to declared slice or grouping scopes without guessing from raw
+locations, replacement, queries, and observation history. Each value family
+projects itself onto declared slice or grouping scopes without guessing from raw
array shape.
Owners and adapters
@@ -101,13 +101,17 @@ OpenHCS owns domain wiring: microscopy sources, pipeline declarations, compiler
plans, processing contracts, artifacts, runtime values, and integrations. The
extracted packages own their generic mechanisms:
-- ObjectState: lazy configuration and edit/snapshot machinery
+- ObjectState: lazy configuration, edit/snapshot machinery, the scope-key
+ grammar (``ScopeKey``), and lazy-configuration source formatting
+ (``objectstate.codegen``)
- ArrayBridge: memory types, callable memory-role metadata, conversion, child
import environments, and framework-local device/execution mechanics
- PolyStore: storage, formats, ROI, and virtual-workspace primitives
- metaclass-registry: generic nominal registration and logging-safe discovery
-- pyqt-reactive: generic reactive forms and widgets
-- python-introspect: callable/signature analysis and declaration projection
+- pyqt-reactive: generic reactive forms and widgets, configuration windows,
+ editable tables, grid selection, and time-travel views
+- python-introspect: callable/signature analysis, declaration projection, and
+ the JSON codec (``to_jsonable``/``dataclass_from_mapping``)
- ZMQRuntime: generic process and transport protocols
- pycodify: Python-source serialization
diff --git a/docs/source/architecture/ui_services_architecture.rst b/docs/source/architecture/ui_services_architecture.rst
index dc8f0214fc..0f2371e83c 100644
--- a/docs/source/architecture/ui_services_architecture.rst
+++ b/docs/source/architecture/ui_services_architecture.rst
@@ -45,11 +45,13 @@ generic registration, focus/reuse, parentage, navigation, and close cleanup.
Reopening a scoped editor therefore focuses the existing window; it does not
create a second domain state or a second window registry.
-``MainWindowAction`` members own their enablement, invocation, side effects and
-recovery warnings. The UI bridge projects those members directly. Session restart
-uses the same ``DesktopRestartSession`` capture and history restoration boundary
-as updates and version-mismatch recovery; its accepted receipt precedes old-UI
-shutdown and requires discovery of the new bridge afterwards.
+Main-window actions such as ``CheckForUpdates``, ``RestartApplication`` and
+``ExitApplication`` are ``SessionOperation`` classes that own their availability,
+invocation, side effects and warnings. The UI bridge projects those classes
+directly through ``SessionOperationActionProvider``. Session restart uses the
+same ``DesktopRestartSession`` capture and history restoration as updates and
+version-mismatch recovery; the action returns before the old UI exits, so the
+caller must rediscover the new bridge afterwards.
The process configuration ObjectState scopes share one stable configuration
window. OpenHCS registers those declared scopes as routes to that window, while
@@ -129,7 +131,8 @@ Process configuration topology
``UIConfig`` is the process-global desktop declaration. Its ``zmq`` field is an
OpenHCS-owned ``OpenHCSZMQConfig`` subtype of ZMQRuntime's generic
-``ZMQConfig``; it is not mirrored as a widget settings table.
+``ExecutionTransportConfig`` that only sets OpenHCS's application names; it is
+not mirrored as a widget settings table.
``PyQtGuiRuntimeContext`` carries the current
``UIConfig`` beside ``GlobalPipelineConfig``. The global configuration window
edits their registered ObjectState scopes. The declaration contains only
@@ -145,10 +148,10 @@ duplicated as editable UIConfig fields. The update-check preference is itself a
first reconciles every live consumer and only then publishes the runtime-context
value and ``ui_config_changed`` signal. The system monitor applies
``performance_monitor`` through its idempotent update owner. Plate Manager
-updates its execution connection declaration and the already-materialized
-progress timer; a not-yet-materialized progress service receives the same value
-when constructed. The main-window shortcut lifecycle validates the complete key
-set before rebinding its concrete QActions and time-travel event filter. ZMQ
+passes the execution transport declaration and progress-update interval to the
+session it renders. pyqt-reactive's ``ShortcutLifecycle`` validates the complete
+key set before rebinding the main window's QActions and its application shortcut
+filter, which carries the time-travel commands. ZMQ
Manager rebuilds its scan inputs from the current transport declaration and the
bridge's actual running binding, never from an unstarted configured port.
The logging lifecycle rebuilds its owned console and rotating-file handlers from
@@ -186,7 +189,9 @@ boundary before the ordinary XDG path is chosen; it does not add another
configuration value or persistence implementation.
The two-tab configuration window is one transaction, not two independent Save
-callbacks. It validates all materialized forms and runs mutation guards before
+callbacks. pyqt-reactive's ``ConfigWindow`` owns that transaction;
+``OpenHCSConfigWindow`` only supplies the OpenHCS configuration-document format
+and window identities. It validates all materialized forms and runs mutation guards before
reconstruction. It then checkpoints every affected ObjectState plus the
independent saved/live global contexts, commits all states, and invokes explicit
reversible persistence/live participants. A participant or saved-state failure
@@ -242,9 +247,11 @@ renderer, or fallback help system is retained.
Results and selected-plate ownership
------------------------------------
-Plate Manager owns the visible plate selection and delegates pipeline editing,
-compile/run actions, Results, and viewer launch to the orchestrator registered
-for that row. Output rows resolve through the declared source/output relation so
+Plate Manager renders the ``Session`` dataset list and holds no session state:
+rows, selection, pending work, compiled artifacts and execution state are read
+from the session, and its buttons are session operations. Pipeline editing,
+compile/run actions, Results, and viewer launch act on the orchestrator
+registered for the selected row. Output rows resolve through the declared source/output relation so
Results can use the producing orchestrator rather than treating the output plate
as an unrelated session.
diff --git a/docs/source/architecture/zmq_execution_service_extracted.rst b/docs/source/architecture/zmq_execution_service_extracted.rst
index a1919517ee..d15f9072aa 100644
--- a/docs/source/architecture/zmq_execution_service_extracted.rst
+++ b/docs/source/architecture/zmq_execution_service_extracted.rst
@@ -38,59 +38,57 @@ inline and threaded work then stops at the next cooperative boundary.
OpenHCS execution submission reuses ZMQRuntime's monotonic
``OperationDeadline`` across endpoint startup, progress registration, task
-serialisation, and the execute request. ``OpenHCSZMQConfig`` owns this dedicated
-submission budget separately from the shorter budget for status, stop, and
-other control requests. Startup activity may refresh the endpoint inactivity
+serialisation, and the execute request. ZMQRuntime's
+``ExecutionTransportConfig``, which ``OpenHCSZMQConfig`` extends with OpenHCS
+application names, declares this dedicated submission budget separately from
+the shorter budget for status, stop, and other control requests. Startup activity may refresh the endpoint inactivity
deadline, but it cannot extend the caller's total submit budget. OpenHCS reports
separately whether preparation expired before the execute request or the
request was sent without a reply, so callers do not invent an execution
identifier or retry an unknown outcome blindly.
-An accepted headless job retains the exact client that submitted it. Status
-polling reuses that client, projects its ZMQRuntime-owned latest progress
-observation, and disconnects it after caching a terminal response. It does not
-recreate clients for polling or mirror transport progress in an OpenHCS-owned
-registry. The job also retains the exact typed submission and accepting endpoint
-handshake. A successful completion projects those facts with the server's typed
-execution record, including its results and timing boundaries, so evidence
+Desktop and headless MCP clients submit through the same ``Session``, which
+owns one execution client. ``ExecutionSubmission`` records each accepted
+submission with its typed request and accepting endpoint handshake, and follows
+its status through that client until it is terminal. The session then keeps the
+submission, endpoint and the server's typed ``ExecutionRecord``, including its
+results and timing boundaries, together as one finished execution, so evidence
writers need not reconstruct the pipeline or parse a separate status source.
The ordinary ZMQ client checks the endpoint's OpenHCS application compatibility
before sending any compile or execution request. Benchmark callers use that same
admission method; they do not maintain a separate version rule.
-The headless cancellation capability addresses that retained client's exact
-job identifier. Its service delegates the bounded request to the ordinary
-execution client and returns the server's applied flag with the observed job
-status; a client timeout alone is not treated as successful cancellation.
-Optional runtime-observation export follows the same submission path: the
-agent path policy validates the destination, and the client/server shared
-auxiliary declaration carries it and its typed value-or-outcome scope to the
-existing execution server. Outcome-only export does not require runtime array
+Session stop shuts down the execution endpoint, gracefully or by force, and
+marks the active datasets cancelled. The ordinary execution client also keeps a
+bounded ``cancel_execution`` control request for one execution identifier.
+Optional runtime-observation export follows the ordinary submission path: the
+client/server shared ``ZMQAuxiliaryExecutionParams`` declaration carries the
+destination and its typed value-or-outcome scope to the existing execution
+server. Outcome-only export does not require runtime array
values to return to the parent when the compiled plan does not need them. It
retains file-export paths from the same compiled output contexts as a full
observation, without collecting intermediate runtime records. The
benchmark layer need not submit a second kind of job to collect that evidence.
Both export scopes carry the producing server execution ID, and the ordinary
export writer will not replace an existing file. The benchmark finaliser
-checks that ID against the completed ordinary job before retaining its receipt.
+checks that ID against the completed ordinary job before retaining its evidence.
The current value and outcome exports also compare execution-result membership
with the compiled axis set; a successful subset cannot become a completed
measurement merely because its reported axes succeeded.
The server-owned observation also carries a startup snapshot of its Python
-interpreter and installed distribution versions. Benchmark receipts project
+interpreter and installed distribution versions. Benchmark evidence projects
that observation without treating the client environment as the server's.
The snapshot does not establish the environment of a remote worker.
The ordinary compiled-run result also projects the server's completion summary
and typed output-plate metadata; benchmark adapters consume those projections
instead of decoding transport response keys themselves.
-A source-backed execution session may retain an original ``plate_path`` while
-declaring a different prepared ``execution_plate_path``. The shared ZMQ
-execution identity selects the prepared path when present, independently of a
-selected external pipeline file. The pycodified ``pipeline_source`` remains
-the pipeline authority; a second selected-pipeline path is rejected for that
-session. Both paths pass the ordinary agent read-path policy before submission.
+An execution request names its ``subject_id`` and may declare a different
+prepared ``execution_subject_id``. The shared ZMQ execution identity carries
+both, and the server's orchestrator environment opens the prepared subject when
+present, independently of a selected external pipeline file. The pycodified
+pipeline document remains the pipeline authority.
The execution server owns one ``FunctionCatalogPreparation``. On a cold cache,
``RegistryService`` launches the launcher's dedicated
diff --git a/docs/source/architecture/zmq_server_browser_system.rst b/docs/source/architecture/zmq_server_browser_system.rst
index 1d1fba6805..76ebd389a9 100644
--- a/docs/source/architecture/zmq_server_browser_system.rst
+++ b/docs/source/architecture/zmq_server_browser_system.rst
@@ -38,7 +38,7 @@ OpenHCS Browser Components
canonicalises responsive heartbeats and in-progress startup observations by
port, then derives endpoint status without persistent connection flags.
- ``RuntimeExecutionTopology``:
- derives worker/well ownership and step names from each retained event
+ derives worker/partition ownership and step names from each retained event
snapshot and validates worker claims without persistent browser state.
- ``RuntimeTreeProjectionBuilder``:
builds typed core runtime trees with recursive aggregation policies.
@@ -65,17 +65,17 @@ Canonical Tree Path
Execution subtree path is invariant:
-``plate -> worker -> well -> step``
+``subject -> worker -> partition -> step``
-Compilation path is plate -> compilation entries until execution events begin.
+Compilation path is subject -> compilation entries until execution events begin.
Aggregation Semantics
---------------------
Tree percentages are recursive and policy-driven:
-- ``mean`` for parent aggregates (plate, worker)
-- ``explicit`` for leaf/detail nodes (well, step, compilation)
+- ``mean`` for parent aggregates (subject, worker)
+- ``explicit`` for leaf/detail nodes (partition, step, compilation)
Policies are enforced per registered node type in
``RuntimeTreeProjectionBuilder``.
@@ -83,8 +83,8 @@ Policies are enforced per registered node type in
Refresh/State Preservation
--------------------------
-The Plate Manager's execution client is the sole subscriber that registers
-progress events. The browser observes accepted registry mutations, marshals the
+The ``Session`` execution client (``ZMQClientService``) is the sole subscriber
+that registers progress events. The browser observes accepted registry mutations, marshals the
notification through a queued Qt signal, and coalesces redraws at the interval
declared by ``ProgressUIConfig``. Server scans independently supply the current
typed running/queued heartbeat entries. Expansion/selection preservation is
diff --git a/docs/source/concepts/building_intuition.rst b/docs/source/concepts/building_intuition.rst
index 917b7f0fa8..037e65d715 100644
--- a/docs/source/concepts/building_intuition.rst
+++ b/docs/source/concepts/building_intuition.rst
@@ -55,7 +55,7 @@ selection.
Choose the owning layer
-----------------------
-- source/file semantics: source declarations or microscope integration
+- source/file semantics: source declarations or a ``DatasetSource`` integration
- callable behavior: callable contract or owning module declaration
- storage mechanism: PolyStore
- array conversion, framework-local device mechanics, and generic callable
diff --git a/docs/source/concepts/component_system_concepts.rst b/docs/source/concepts/component_system_concepts.rst
index 7b694af41a..8413f38ff4 100644
--- a/docs/source/concepts/component_system_concepts.rst
+++ b/docs/source/concepts/component_system_concepts.rst
@@ -50,7 +50,7 @@ Stack and grouping use
processing_config=LazyProcessingConfig.from_config(processing),
)
-Site declares the stack axis. Channel partitions those site stacks for
+Site is the axis the step assembles its stacks along. Channel partitions those site stacks for
dictionary routing. ``ProcessingContract`` separately declares whether each
callable has per-plane or whole-stack semantics.
diff --git a/docs/source/concepts/core_model.rst b/docs/source/concepts/core_model.rst
index 70b63d8d98..521a14e8b0 100644
--- a/docs/source/concepts/core_model.rst
+++ b/docs/source/concepts/core_model.rst
@@ -19,7 +19,7 @@ The model in one diagram
|
| resolve configuration once
v
- snapshots + compilation sessions
+ resolved pipeline + compilation sessions
|
| validate sources, artifacts,
| memory, scope, and workers
@@ -43,7 +43,8 @@ produce the same declarations.
Sources
-------
-Microscope handlers interpret plate layouts and metadata. Source bindings give
+Dataset sources (one ``DatasetSource`` per format) interpret plate layouts and
+metadata. Source bindings give
semantic aliases to selected images and metadata. The compiler resolves those
bindings against an explicit source universe and virtual-workspace projection;
runtime adapters do not rescan directories or reinterpret filenames.
@@ -70,7 +71,7 @@ the resolved declarations rather than repeatedly resolving individual fields.
Contracts and artifacts
-----------------------
-Callable contracts declare memory types, required axes, allowed grouping,
+Callable contracts declare memory types, required axis roles, allowed grouping,
processing contract, execution scope, runtime adapter, and artifact inputs and
outputs. CellProfiler module leaves derive invocation-specific ``ArtifactSpec``
inputs and outputs into that same callable contract. Compiler source plans,
@@ -84,8 +85,8 @@ addresses and materialization targets.
Compilation
-----------
-Each execution axis receives a ``CompilationSession`` with resolved steps,
-``StepSnapshot`` objects, source projection, and typed ``CompiledStepPlan``
+Each value of the partition axis receives a ``CompilationSession`` with the
+resolved pipeline definition, source projection, and typed ``CompiledStepPlan``
objects. Plans contain source, artifact, function, memory, materialization,
streaming, GPU, and worker facts. Compilation emits a
``CompiledExecutionBundle`` only after these facts satisfy runtime invariants.
diff --git a/docs/source/concepts/data_dimensions.rst b/docs/source/concepts/data_dimensions.rst
index baedf54635..fd4f69e87f 100644
--- a/docs/source/concepts/data_dimensions.rst
+++ b/docs/source/concepts/data_dimensions.rst
@@ -9,8 +9,9 @@ Plate and execution axis
------------------------
The orchestrator discovers component keys through the microscope/source model.
-One configured component—normally well—is the multiprocessing execution axis.
-Compilation creates one context and typed plan set for each selected axis value.
+The active axis family declares one partition axis (well, for microscopy) that
+splits work across processes. Compilation creates one context and typed plan
+set for each selected value on that axis.
Image stack axis
----------------
@@ -34,18 +35,19 @@ An array that contains several Z planes is not automatically treated as one
volumetric object domain. Component axes describe what the data contains;
callable declarations describe how a function consumes it.
-``ProcessingContract`` controls how a callable handles the assembled runtime
-stack independently of the component identities:
+A ``ProcessingContract`` subclass controls how a callable handles the
+assembled runtime stack independently of the component identities:
-- ``PURE_2D``: each plane is semantically independent;
-- ``PURE_3D``: the callable depends on the full stack;
-- ``FLEXIBLE``: an explicit control selects either semantic mode;
-- ``VOLUMETRIC_TO_SLICE``: a stack is consumed into a collapsed plane domain.
+- ``Pure2DContract``: each plane is semantically independent;
+- ``Pure3DContract``: the callable depends on the full stack;
+- ``FlexibleContract``: an explicit control selects either semantic mode;
+- ``VolumetricToSliceContract``: a stack is consumed into a collapsed plane
+ domain.
Other callable declarations can preserve a complete image payload or a
full-stack object-label input. The effective behaviour therefore comes from
-the callable's complete contract, not from array shape or one enum in
-isolation. For example, the CellProfiler-compatible Watershed route consumes a
+the callable's complete contract, not from array shape or one processing
+contract in isolation. For example, the CellProfiler-compatible Watershed route consumes a
complete 3D payload, whereas ``IdentifyPrimaryObjects``,
``IdentifySecondaryObjects``, and ``IdentifyTertiaryObjects`` currently operate
plane by plane.
diff --git a/docs/source/concepts/domain_fundamentals.rst b/docs/source/concepts/domain_fundamentals.rst
index c7f8dba20b..083ce29978 100644
--- a/docs/source/concepts/domain_fundamentals.rst
+++ b/docs/source/concepts/domain_fundamentals.rst
@@ -11,10 +11,10 @@ Why the data model matters
--------------------------
Microscope vendors differ in filename layout, metadata, and storage. OpenHCS
-uses registered microscope handlers and PolyStore source references to project
-those formats into a common source model. The original files remain the source;
-the projection supplies typed component identities and provenance to the
-compiler.
+uses registered dataset sources (one per microscope format) and PolyStore
+source references to project those formats into a common source model. The
+acquired files remain the source; the projection supplies typed component
+identities and provenance to the compiler.
Why compilation matters
-----------------------
diff --git a/docs/source/concepts/function_library.rst b/docs/source/concepts/function_library.rst
index abd010deeb..f5fbaffb6d 100644
--- a/docs/source/concepts/function_library.rst
+++ b/docs/source/concepts/function_library.rst
@@ -14,11 +14,14 @@ declare:
- input, output, and execution memory types;
- artifact inputs and outputs;
- runtime-bound parameters;
-- required variable components and allowed grouping;
-- ``ProcessingContract``;
+- required axis roles and allowed group-by roles;
+- a ``ProcessingContract`` subclass;
- runtime adapter and image execution mode;
- axis- or plate-level execution scope;
-- compiler preparation hooks.
+- the signatures a compiler preparation hook produced.
+
+The preparation hook itself is attached to the callable
+(``processing_prepare``), not carried in the contract.
Image transport
---------------
@@ -79,7 +82,7 @@ The desktop form surface materialises every locally available catalogue
declaration after applying its owner-declared exclusions; authorable parameters
become editable controls while runtime-owned inputs remain hidden.
-OpenHCS-native and custom functions are admitted only when every declared input,
+OpenHCS-native and custom functions are catalogued only when every declared input,
output, and execution framework is installed and allowed by the current process
policy. The browsable native catalogue contains public module declarations;
private decorated helpers remain internal unless an explicit pipeline transports
diff --git a/docs/source/concepts/function_patterns.rst b/docs/source/concepts/function_patterns.rst
index 6c90e2d698..8649da5038 100644
--- a/docs/source/concepts/function_patterns.rst
+++ b/docs/source/concepts/function_patterns.rst
@@ -68,7 +68,7 @@ Callable contracts
------------------
Every normalized invocation has a ``CallableContract``. All invocations in a
-step must agree on execution scope and satisfy declared axes, grouping, runtime
+step must agree on execution scope and satisfy declared axis roles, grouping, runtime
parameters, image mode, and artifacts. Compilation turns the public pattern into
a typed compiled pattern; workers consume that compiled form.
diff --git a/docs/source/concepts/module_structure.rst b/docs/source/concepts/module_structure.rst
index b67b87fc3a..53cd057f3d 100644
--- a/docs/source/concepts/module_structure.rst
+++ b/docs/source/concepts/module_structure.rst
@@ -9,19 +9,29 @@ OpenHCS-owned areas
-------------------
``openhcs/core``
- Pipeline declarations, source/artifact semantics, compiler plans, runtime
- values, contexts, orchestrator, progress, and equivalence.
+ The domain-blind kernel: axis families, dataset sources, pipeline
+ declarations, callable and processing contracts, source/artifact semantics,
+ compiler plans, runtime values, contexts, orchestrator, progress, and
+ equivalence.
+
+``openhcs/domains``
+ Domain declarations that instantiate the kernel; microscopy declares its axis
+ family and domain configuration here.
``openhcs/processing``
- Processing-library discovery, callable decorators/contracts, native and
- CellProfiler-compatible processing implementations.
+ Processing-library discovery, native and CellProfiler-compatible processing
+ implementations, presets, and custom functions.
``openhcs/interop``
Translation boundaries such as CellProfiler parsing, module declarations,
settings binding, and public lowering.
``openhcs/microscopes``
- Microscope-specific plate layout and metadata integration.
+ Microscope-specific dataset sources: plate layout and metadata integration.
+
+``openhcs/authoring``
+ The session (datasets, pipelines, compile, run, and their events) that the
+ Qt GUI and MCP clients render and invoke.
``openhcs/pyqt_gui``
OpenHCS screens, workflow services, editors, Plate Manager, and adapters over
@@ -43,16 +53,19 @@ Extracted foundations
The submodules under ``external/`` own reusable behavior:
-- ObjectState — lazy config and state/provenance
+- ObjectState — lazy config, state/provenance, scope keys, and Python source
+ for lazy dataclasses
- ArrayBridge — array conversion, callable memory metadata, child import
environments, and framework-local device and execution mechanics
- PolyStore — storage, formats, ROI, and virtual workspace
- metaclass-registry — generic nominal registry mechanics and logging-safe
discovery imports
-- pyqt-reactive — generic forms/widgets/services
+- pyqt-reactive — generic forms, widgets (editable tables, grid selector,
+ time travel), windows, and services
- python-introspect — callable/signature analysis and declaration projection
- ZMQRuntime — generic execution and transport protocols
-- pycodify — Python-source serialization
+- pycodify — Python-source serialization, including document-level path
+ factoring
The root ``pyproject.toml`` declares their published distributions. Development
checkouts install the submodules explicitly; see
diff --git a/docs/source/concepts/pipelines_and_steps.rst b/docs/source/concepts/pipelines_and_steps.rst
index 0f49040652..8d6cb75e1b 100644
--- a/docs/source/concepts/pipelines_and_steps.rst
+++ b/docs/source/concepts/pipelines_and_steps.rst
@@ -44,7 +44,7 @@ The ``func`` field accepts:
- a dictionary mapping compiled group keys to either form.
Callable contracts—not examples or UI labels—are the authority for accepted
-parameters, runtime-injected parameters, required axes, processing behavior,
+parameters, runtime-injected parameters, required axis roles, processing behavior,
memory types, and artifacts.
Data flow
@@ -70,10 +70,11 @@ Compile before execution
------------------------
``PipelineOrchestrator.compile_pipelines(pipeline_steps)`` resolves the pipeline
-once and compiles every selected execution axis. The returned
-``CompiledExecutionBundle`` contains runtime contexts, worker steps, and
-environment decisions. The GUI uses the same boundary and compiles the complete
-selected set before executing it.
+once and compiles every selected value of the partition axis. The returned
+``CompiledExecutionBundle`` contains runtime contexts, worker assignments, and
+the runtime environment plan. The GUI and MCP clients reach the same boundary
+through the OpenHCS session, which compiles the complete selected set of
+datasets before executing it.
Related pages
-------------
diff --git a/docs/source/concepts/storage_system.rst b/docs/source/concepts/storage_system.rst
index c2108e8589..53228ee44b 100644
--- a/docs/source/concepts/storage_system.rst
+++ b/docs/source/concepts/storage_system.rst
@@ -50,7 +50,8 @@ Callable and module contracts declare typed artifact inputs and outputs. The
artifact graph resolves producers and dependencies. Path planning then adds
backend addresses and materialization targets to typed plans.
-A Python output-slot name or ``@artifact_outputs`` declaration does not make a value persistent. Materialization is an explicit configuration and
+A Python output-slot name or ``@artifact_outputs`` declaration does not make a
+value persistent. Materialization is an explicit configuration and
artifact-plan decision.
Runtime storage
diff --git a/docs/source/development/ast_refactoring_workflow.rst b/docs/source/development/ast_refactoring_workflow.rst
index 89d08aa349..ff645c0f19 100644
--- a/docs/source/development/ast_refactoring_workflow.rst
+++ b/docs/source/development/ast_refactoring_workflow.rst
@@ -15,7 +15,7 @@ Workflow
changes.
5. Review every change that alters configuration nesting, ownership, or
runtime semantics.
-6. run ``scripts/validate_docs.py`` and Sphinx with warnings as errors.
+6. Run ``scripts/validate_docs.py`` and Sphinx with warnings as errors.
The AST pass is useful for import moves, renamed call sites, and detecting old
constructor keywords. It must not invent compatibility aliases, mirror
diff --git a/docs/source/development/callable_artifact_authoring.rst b/docs/source/development/callable_artifact_authoring.rst
index 727cdcac90..39ab12c8be 100644
--- a/docs/source/development/callable_artifact_authoring.rst
+++ b/docs/source/development/callable_artifact_authoring.rst
@@ -9,9 +9,11 @@ Callable contract
-----------------
The contract can carry input, output, and execution memory types, artifact
-inputs and outputs, runtime-bound parameters, required variable components,
-allowed grouping, processing contract, execution scope, runtime adapter,
-preparation hook, and a callable request binding. The execution memory role is
+inputs and outputs, runtime-bound parameters, required axis roles, allowed
+group-by roles, processing contract, execution scope, runtime adapter, the
+signatures a preparation hook produced, and a callable request binding. The
+hook itself stays on the callable (``processing_prepare``); it is not part of
+the transported contract. The execution memory role is
the framework whose device scope must be active while the callable runs; it may
differ from an input or output conversion boundary. Use the existing decorators
and declaration helpers; do not teach compiler phases to inspect backend names.
@@ -63,8 +65,8 @@ main-flow publication, and the active output subset are then represented by
compiled source, edge, and artifact plans; do not copy them into declaration
partitions.
-``special_inputs`` describes ABI parameters only. They are not a substitute for artifact types, producer edges,
-or materialization declarations.
+``special_inputs`` describes ABI parameters only. They are not a substitute
+for artifact types, producer edges, or materialization declarations.
``special_inputs("labels")`` alone does not bind a required ``labels`` parameter.
Compilation rejects that incomplete contract, even if authored kwargs contain a
@@ -211,7 +213,7 @@ still determine their return positions and materialisation. An output image
reference is not an input-qualified group-scope source.
``MaterializationSourceIdentityRelation`` is supported for image targets, not
labels or measurements, and is therefore not attached to those outputs here.
-``PURE_2D`` asks the runtime to dispatch planes independently;
+``Pure2DContract`` asks the runtime to dispatch planes independently;
the runtime projects the local ``slice_index=0`` rows onto the assembled stack's
plane axis. It does not create volumetric object identities.
@@ -254,7 +256,7 @@ where you saved it. For MCP custom registration, submit the complete declaration
block as ``source_code`` to the discovered ``openhcs_register_custom_function``
capability. Follow the ``openhcs_custom_function_workflow`` guide for explicit
endpoint, intended storage, preparation and uncertainty handling. Retain the
-registration receipt, then describe the deliberately authored public function
+registration result, then describe the deliberately authored public function
identifier to verify its ``function_id``, ``import_path`` and artifact contract
rather than assuming registration returns a function catalogue. Custom
callables are projected under ``openhcs.processing.custom_functions``. A local
@@ -342,7 +344,7 @@ On the existing centre-producing callable, declare
``@artifact_outputs(CENTRE_IMAGE, CENTRE_LABELS, CENTRE_ROWS)`` and return
``image, labels, DataclassMeasurementColumnarRows(rows, row_type=CentreRow)``.
Use the existing memory/processing decorators, as in the executable reference;
-``PURE_3D`` is appropriate only for an actual volumetric calculation. Retain
+``Pure3DContract`` is appropriate only for an actual volumetric calculation. Retain
the complete source bindings and compiled ZYX grouping: the decorator alone
does not establish axis order. The row's representative rule (peak, body centre
or another task-defined location) remains the analysis contract, not the writer.
@@ -464,7 +466,7 @@ the compatible producer, retaining the complete pipeline configuration/source
bindings. ``np.asarray(objects)`` is a local pixel view for this calculation,
not a replacement for the nominal input or its provenance. If the task needs
object/plane identity, use the value's domain and plane APIs rather than
-reconstructing them from dense pixels. ``PURE_2D`` here describes locality;
+reconstructing them from dense pixels. ``Pure2DContract`` here describes locality;
it does not select the pipeline's variable axis.
Repair the earliest failed declaration before trying execution. If compilation
diff --git a/docs/source/development/compiler_extension.rst b/docs/source/development/compiler_extension.rst
index 719e470049..05c69a604b 100644
--- a/docs/source/development/compiler_extension.rst
+++ b/docs/source/development/compiler_extension.rst
@@ -8,10 +8,12 @@ parallel dictionary plan.
Invariants
----------
-``StepSnapshot`` binds one resolved step, compiler index, and ObjectState scope
-identity. ``CompilationSession`` validates that steps, snapshots, states,
-context, plans, source projection, and plate scope describe the same execution
-axis.
+``ResolvedPipelineDefinition`` captures the enabled steps with their
+ObjectState scope identities and provenance, and admits their artifact
+contracts and forward topology once before axis fan-out. ``CompilationSession``
+binds that definition to one ``ProcessingContext`` (one axis or sequential
+combination), its compiled-plan map, source projection, and dataset scope;
+``plan(index)`` fails when a step has no compiled plan.
Adding a decision
-----------------
diff --git a/docs/source/development/extension_workflows.rst b/docs/source/development/extension_workflows.rst
index 53226a780a..69ee1d18f0 100644
--- a/docs/source/development/extension_workflows.rst
+++ b/docs/source/development/extension_workflows.rst
@@ -70,7 +70,7 @@ Compiler/runtime split
----------------------
Mutable ObjectState is resolved once. Compiler phases consume the resolved
-step through ``StepSnapshot`` and ``CompilationSession``. Workers consume
+steps through ``ResolvedPipelineDefinition`` and ``CompilationSession``. Workers consume
``CompiledExecutionBundle`` and must not reinterpret UI state or callable
attributes.
@@ -92,11 +92,13 @@ Serialization boundaries
Python source generation and JSON transport are different contracts.
pycodify owns executable Python source, imports, and formatter composition.
-OpenHCS ``to_jsonable`` owns the JSON-native projection used by agent
-capabilities and UI-bridge transport. It uses ``singledispatch`` for mappings,
-sequences, callables, registered nominal types, enums, paths, and scalars.
+python-introspect ``to_jsonable`` owns the JSON-native projection used by
+agent capabilities and UI-bridge transport, paired with the
+``dataclass_from_mapping`` decoder. It uses ``singledispatch`` for dataclasses,
+mappings, sequences, callables, registered nominal types, enums, paths, and
+scalars.
-Add a ``to_jsonable.register`` implementation at the OpenHCS value type only
+Add a ``to_jsonable.register`` implementation beside the OpenHCS value type only
when that type has a stable public transport representation. Do not make
pycodify formatters or generic JSON consumers guess domain identity, and never
use either projection to reconstruct compiler semantics that belong in typed
diff --git a/docs/source/development/git_worktree_testing.rst b/docs/source/development/git_worktree_testing.rst
index 5739c814af..333d018f2b 100644
--- a/docs/source/development/git_worktree_testing.rst
+++ b/docs/source/development/git_worktree_testing.rst
@@ -80,14 +80,14 @@ Run the smallest suite that exercises the change first:
.. code-block:: console
- OPENHCS_CPU_ONLY=1 python -m pytest tests/unit/path/to/test_owner.py -q
+ OPENHCS_CPU_ONLY=true python -m pytest tests/unit/path/to/test_owner.py -q
When the change crosses compilation or execution boundaries, run a bounded
integration configuration before widening the matrix:
.. code-block:: console
- OPENHCS_CPU_ONLY=1 python -m pytest tests/integration/test_main.py \
+ OPENHCS_CPU_ONLY=true python -m pytest tests/integration/test_main.py \
--it-backends disk \
--it-microscopes ImageXpress \
--it-dims 3d \
@@ -111,7 +111,7 @@ commit and recursive submodule revisions beside each result:
git rev-parse HEAD
git submodule status --recursive
- OPENHCS_CPU_ONLY=1 python -m pytest tests/unit -q > feature-results.txt 2>&1
+ OPENHCS_CPU_ONLY=true python -m pytest tests/unit -q > feature-results.txt 2>&1
Repeat from the comparison worktree, then compare the result files with
``diff``. Tracked fixtures are checked out independently in each worktree.
diff --git a/docs/source/development/mcp_knowledge_base_manifest.json b/docs/source/development/mcp_knowledge_base_manifest.json
index d79d1b0224..56012988d5 100644
--- a/docs/source/development/mcp_knowledge_base_manifest.json
+++ b/docs/source/development/mcp_knowledge_base_manifest.json
@@ -362,7 +362,7 @@
"document_id": "openhcs_pipeline_compilation_system",
"section_count": 0,
"source_path": "docs/source/architecture/pipeline_compilation_system.rst",
- "summary": "One-time ObjectState resolution, StepSnapshot and CompilationSession ownership, typed CompiledStepPlan fields, artifact/source planning, validation, and runtime bundle construction.",
+ "summary": "One-time ObjectState resolution, ResolvedPipelineDefinition and CompilationSession ownership, typed CompiledStepPlan fields, artifact/source planning, validation, and runtime bundle construction.",
"tags": [
"compiler",
"runtime",
diff --git a/docs/source/development/mcp_release.rst b/docs/source/development/mcp_release.rst
index cbea5463b6..dfb43cf60c 100644
--- a/docs/source/development/mcp_release.rst
+++ b/docs/source/development/mcp_release.rst
@@ -78,7 +78,7 @@ a complete, revisioned function catalogue, resolves one returned function
reference, and verifies that the ready endpoint's advertised log is readable
and contains output. A packaged desktop MCP session then verifies health and
version identity, discovers the live main and Plate Manager windows, and invokes
-the declared Plate Manager code action. The probe requires the resulting window
+the Plate Manager's ``show_dataset_code`` session operation. The probe requires the resulting window
to appear, then captures the main window and opened editor through the
production MCP snapshot capability. It verifies each PNG's path, dimensions,
byte size, and SHA-256 before closing the GUI, removing the bridge descriptor,
@@ -242,8 +242,11 @@ annotated ``v`` tag through the configured upstream remote. Do not
create a second MCP-specific or installer-specific tag.
That tag starts ``.github/workflows/publish.yml``. The workflow builds and
-validates the Windows and macOS installer assets first, then builds and smoke
-tests the OpenHCS wheel outside the checkout. After publishing the wheel and
+validates the Windows and macOS installer assets and, with ``cibuildwheel``,
+the stable-ABI OpenHCS wheels for Linux (x86_64 and aarch64), Windows, and
+macOS (x86_64 and arm64), smoke-testing each wheel's native extension. It then
+builds the source distribution, validates every wheel, and smoke tests the
+Linux x86_64 wheel outside the checkout. After publishing the wheels and
source distribution to PyPI, it creates one GitHub Release containing those
Python artifacts plus the directly runnable
``OpenHCS-Windows-Installer.exe`` and the single-file
@@ -273,8 +276,8 @@ complete release from that tag:
--field publish_python_package=true \
--field publish_desktop_installers=true
-This path validates and smoke-tests the built wheel, publishes the wheel and
-source distribution to PyPI, assembles the GitHub Release with both native
+This path builds and validates the native wheels, smoke-tests the Linux wheel,
+publishes the wheels and source distribution to PyPI, assembles the GitHub Release with both native
installers, then publishes the matching official MCP Registry entry. It reuses
the existing tag. The upload remains fail-closed when that PyPI version already
exists so independently rebuilt files cannot be mistaken for the published
@@ -291,7 +294,7 @@ again:
--field publish_python_package=false \
--field publish_desktop_installers=true
-This path downloads the exact wheel and source distribution already owned by
+This path downloads the exact wheels and source distribution already owned by
PyPI and verifies their declared SHA-256 digests. It builds both native
installers, attaches the complete asset set to the existing tag, and continues
to MCP Registry publication. The Python files on PyPI and GitHub are therefore
diff --git a/docs/source/development/respecting_codebase_architecture.rst b/docs/source/development/respecting_codebase_architecture.rst
index 592be234c9..a42f2c173e 100644
--- a/docs/source/development/respecting_codebase_architecture.rst
+++ b/docs/source/development/respecting_codebase_architecture.rst
@@ -30,7 +30,7 @@ Registry discipline
For a nominal family, the root registry is the catalog. Generic consumers
normally iterate ``Root.__registry__.values()`` or use the helpers in
-``openhcs.core.registry_strategies``.
+``metaclass_registry.strategies``.
Do not add a second dictionary, set, ordered list, priority table, copied field
list, compatibility alias, or fallback chain for the same fact. Such mirrors
diff --git a/docs/source/development/runtime_system_assembly_rules.rst b/docs/source/development/runtime_system_assembly_rules.rst
index 3eb6e19fb6..e0c667c865 100644
--- a/docs/source/development/runtime_system_assembly_rules.rst
+++ b/docs/source/development/runtime_system_assembly_rules.rst
@@ -11,7 +11,7 @@ Assembly order
PipelineConfig + FunctionStep declarations
-> one ObjectState resolution
- -> StepSnapshot + CompilationSession
+ -> ResolvedPipelineDefinition + CompilationSession
-> typed CompiledStepPlan objects
-> CompiledExecutionBundle
-> workers + RuntimeValueStore
@@ -43,14 +43,17 @@ Owner map
including the exact framework-local device bindings selected for each step.
``CompiledExecutionBundle``
- Contexts, worker-safe steps, plans, and the resolved runtime environment.
+ Runtime contexts, pickle-safe transport contexts, worker assignments, and the
+ resolved runtime environment.
``RuntimeValueStore``
Validated runtime values, artifact keys, locations, current bindings, and
observation history.
``RuntimeSliceProjection``
- Nominal, provenance-aware slice projection and aggregation.
+ Slice projection of runtime values. Owned value types declare their slice
+ behaviour through ``RuntimeSliceProjectableValue``; stack values are checked
+ against their source provenance.
Source and artifacts
--------------------
diff --git a/docs/source/development/runtime_value_extension.rst b/docs/source/development/runtime_value_extension.rst
index 904403092a..b379d1354f 100644
--- a/docs/source/development/runtime_value_extension.rst
+++ b/docs/source/development/runtime_value_extension.rst
@@ -9,22 +9,25 @@ Required decisions
1. Define the payload and its artifact type/spec boundary.
2. Decide how it is validated and wrapped in ``RuntimeValue``.
-3. Register ``RuntimeSliceProjectionStrategy`` behavior if it can be projected
- or aggregated across runtime slices.
+3. Subclass ``RuntimeSliceProjectableValue`` (or a narrower base such as
+ ``RuntimeSliceIndexedValue`` or ``RuntimeSliceInvariantValue``) if the
+ payload can be projected across runtime slices.
4. Add serialization, worker transport, materialization, equivalence, or viewer
strategies only where the value participates in those systems.
5. Keep persistence location separate from its ``ArtifactKey``.
-Projection strategies are selected by nominal type and MRO specificity. A value
-that preserves a runtime slice must carry explicit plane-axis and source
-identity declarations; raw shape is insufficient.
+Projection behaviour lives on the payload class itself.
+``RuntimeSliceProjection`` handles only foreign values such as arrays,
+mappings and primitives, and rejects other values that declare no slice
+behaviour. A value that preserves a runtime slice must carry explicit
+plane-axis and source identity declarations; raw shape is insufficient.
``RuntimeValueStore`` records validated values under an artifact key and
explicit backend location. Consumers resolve typed queries derived from
compiled artifact edges. Missing or ambiguous results must fail rather than
searching paths or fallback names.
-Verify store record/replace/query behavior, slice projection and aggregation,
+Verify store record/replace/query behavior, slice projection,
transport round trips, and materialization for every supported backend.
See :doc:`../architecture/runtime_value_system`.
diff --git a/docs/source/development/source_binding_extension.rst b/docs/source/development/source_binding_extension.rst
index 558eec463e..f53925f257 100644
--- a/docs/source/development/source_binding_extension.rst
+++ b/docs/source/development/source_binding_extension.rst
@@ -23,15 +23,18 @@ Strategy selection
------------------
Existing families select source universes, matching behavior, component
-projection, and workspace projection. Add a concrete strategy to the correct
-root and declare its enum/type/context key. Generic consumers should continue
-to use the root registry or its strategy mixin.
+projection, and workspace projection, for example ``SourceUniverseRequest``,
+``SourceFilterMatchType`` (whose subclasses own the filter predicates), and the
+``SourceSetPairing`` family (``PairByOrder``, ``PairByMetadata``) that decides
+how matched sources form source sets. Add a concrete subclass to the correct
+family root and declare its registry key. Generic consumers should continue to use the
+root registry or its strategy mixin.
Virtual workspaces
------------------
PolyStore owns ``SourcePixelRef`` and generic backend addressing. OpenHCS owns
-microscope metadata and ``VirtualWorkspaceSourceProjection``, which maps
+dataset source metadata and ``VirtualWorkspaceSourceProjection``, which maps
virtual paths to source references and provenance for one compilation session.
Verify declaration validation, strategy selection, physical and virtual path
@@ -41,7 +44,7 @@ folder metadata rules project the declared biological address without changing
the backend-owned source address, and that reopening the persisted workspace
reports the projected component values and labels. When a component selector
and component identity use different values, verify that the selector matches
-the original candidate while the projected address, compiler group keys, and
+the emitted candidate while the projected address, compiler group keys, and
runtime provenance use the declared component identity.
See :doc:`../architecture/source_model`.
diff --git a/docs/source/getting_started/getting_started.rst b/docs/source/getting_started/getting_started.rst
index ff2a9c5deb..31d7b31719 100644
--- a/docs/source/getting_started/getting_started.rst
+++ b/docs/source/getting_started/getting_started.rst
@@ -56,12 +56,6 @@ Manual Python installation
Create a virtual environment, then install the same CPU-safe capability set as
the desktop installer:
-.. note::
-
- The complete CellProfiler compatibility extra currently requires Python 3.13
- or earlier because its optional parity dependency does not publish Python
- 3.14 wheels. Python 3.14 supports the base package and core-service extras.
-
.. code-block:: bash
python -m pip install --upgrade pip
diff --git a/docs/source/guide_for_biologists/basic_interface.rst b/docs/source/guide_for_biologists/basic_interface.rst
index abb2c249ae..51624b6d3e 100644
--- a/docs/source/guide_for_biologists/basic_interface.rst
+++ b/docs/source/guide_for_biologists/basic_interface.rst
@@ -13,7 +13,7 @@ The desktop is a set of ObjectState-backed workflow windows, not one giant form:
.. code-block:: text
Main window
- |- Plate Manager datasets, selection, init/compile/run
+ |- Datasets datasets, selection, init/compile/run
|- Pipeline Editor PipelineConfig + ordered FunctionStep list
| `- Step editors callable pattern + step-local config
|- Global Configuration application/session defaults
@@ -21,19 +21,20 @@ The desktop is a set of ObjectState-backed workflow windows, not one giant form:
|- Log/progress/debug operation status and diagnostics
`- Analysis/tools consolidation, layouts, custom functions
-The Plate Manager is the operational hub. Other windows edit or inspect the
+The Datasets pane is the operational hub. Other windows edit or inspect the
selected plate, pipeline, step, source projection, or application config. A
Napari or Fiji viewer is a separate runtime window launched only when requested.
-In the supported PyQt desktop shell, Plate Manager and the ZMQ process manager
+In the supported PyQt desktop shell, the Datasets pane and the ZMQ server manager
occupy the left workspace, Pipeline Editor occupies the right workspace, and
the system monitor is docked above them. Managed editors,
configuration windows, logs, and tools open or focus around that embedded
workspace. The PyQt desktop shell is the supported MCP-attached UI.
-Plate Manager
+Datasets pane
-------------
-Plate Manager is the starting point for a dataset. Add a local or configured
+The **Datasets** pane (window identity ``plate_manager`` for MCP agents) is the
+starting point for a dataset. Add a local or configured
remote plate, initialize it, and select the plate before editing or running its
pipeline. Initialization discovers the microscope/source format and component
metadata. Compile and run actions are disabled until their prerequisites are
@@ -49,7 +50,7 @@ It is disabled during startup or disconnection, even if a compiled plate remains
selected. During execution, the same control provides **Stop**; stopping controls
remain available if the endpoint becomes unavailable.
-Plate Manager code mode records whether its document represents all plates or
+Datasets code mode records whether its document represents all plates or
only the current selection. Applying a selected document changes only those
plates and preserves every unselected plate. Read a new document if you need to
change which plates the edit covers.
@@ -92,7 +93,7 @@ reference to the authoring process; it does not rebuild the catalogue there.
List previews
-------------
-The ``list_previews`` section of UI configuration controls the Plate Manager
+The ``list_previews`` section of UI configuration controls the Datasets
and Pipeline Editor summaries. It sets the default wrapping behaviour, includes
or hides detail lines and modified settings, and shows collections compactly
or in detail. Saving applies the same display policy to both panels immediately.
@@ -136,16 +137,17 @@ Agent navigation
An attached MCP agent should navigate semantically:
1. list running windows and ObjectState scopes;
-2. read the Plate Manager state surface to identify selected/source/output rows;
+2. read the Datasets (``plate_manager``) state surface to identify selected/source/output rows;
3. open or focus the relevant window/scope;
4. list its code documents or fields rather than guessing widget labels;
5. validate code, then apply with the current revision token;
-6. dispatch semantic init/compile/run actions and poll operation/state surfaces.
+6. dispatch semantic init/compile/run actions and follow the work they start
+ through the session events and state surfaces.
To select a manager item through MCP, use that manager's window identity and
the item identity from its state surface, then check the current selection
before invoking an action. Opening a plate's configuration window does not
-select its Plate Manager row.
+select its Datasets row.
Window focus and field navigation are reported separately. A configuration
field request selects the tab whose declared ObjectState scope owns that path.
@@ -154,7 +156,7 @@ request reports that the target was unsupported instead of claiming it was
navigated.
The stable ``main_window`` snapshot captures the complete workspace and status
-bar. Pane identities capture a focused Plate Manager, Pipeline Editor, server
+bar. Pane identities capture a focused Datasets pane, Pipeline Editor, server
browser, or system-monitor view, while transient dialogs are listed when they
are open. Window snapshots and the widget tree are useful for orientation or
diagnosing a missing semantic surface. They are not configuration authorities.
diff --git a/docs/source/guide_for_biologists/configuration_reference.rst b/docs/source/guide_for_biologists/configuration_reference.rst
index 5152c26bd3..220a8c3b61 100644
--- a/docs/source/guide_for_biologists/configuration_reference.rst
+++ b/docs/source/guide_for_biologists/configuration_reference.rst
@@ -27,14 +27,14 @@ There are three declaration scopes, followed by one resolution boundary:
ObjectState resolution at compile time
|
v
- immutable step snapshots and typed compiled plans
+ resolved step declarations and typed compiled plans
.. openhcs-gallery:: ui-pipeline-configuration
``None`` in a lazy field normally means *inherit*, not *disable*. Enableable
configs have a separate ``enabled`` field; resetting a lazy field returns it to
inheritance. Compilation resolves the hierarchy once. Runtime workers consume
-the resolved snapshots and plans and never reinterpret the live forms.
+the resolved declarations and plans and never reinterpret the live forms.
Scope precedence is only one axis. Config dataclass inheritance is the other:
it determines which related policies share a default. The important well-filter
@@ -43,7 +43,7 @@ relationship is:
.. code-block:: text
PartitionFilterConfig pipeline execution domain
- |-- PathPlanningConfig automatic output-plate persistence
+ |-- PathPlanningConfig automatic output-plate persistence
`-- StepPartitionFilterConfig step-policy default
|-- StepMaterializationConfig
`-- StreamingDefaults
@@ -181,8 +181,8 @@ Post-run analysis and metadata
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``AnalysisConsolidationConfig``
- Optional post-plate consolidation, including well pattern, included
- extensions, exclusions, and output filenames.
+ Optional post-plate consolidation, including MetaXpress-style formatting,
+ included extensions, exclusions, and output filenames.
``PlateMetadataConfig``
Plate identity and acquisition metadata used by compatible consolidated
diff --git a/docs/source/guide_for_biologists/domain_expert_onboarding.rst b/docs/source/guide_for_biologists/domain_expert_onboarding.rst
index 636008e6f5..afd1d60843 100644
--- a/docs/source/guide_for_biologists/domain_expert_onboarding.rst
+++ b/docs/source/guide_for_biologists/domain_expert_onboarding.rst
@@ -87,8 +87,8 @@ to execution and result review.
Choose UI-visible or headless ownership before creating a pipeline. When the
scientist should see or continue editing the work in the desktop, use the
-UI-owned route; a headless draft or execution does not create Plate Manager
-state. Discover the capabilities exposed by the current profile before
+UI-owned route; a headless draft or execution does not create a row in the
+desktop Datasets pane. Discover the capabilities exposed by the current profile before
following a guide, and validate a representative bounded run before scaling up.
CellProfiler Mental Model
diff --git a/docs/source/guide_for_biologists/image_sources.rst b/docs/source/guide_for_biologists/image_sources.rst
index e0a0eb0544..3b3d2594a4 100644
--- a/docs/source/guide_for_biologists/image_sources.rst
+++ b/docs/source/guide_for_biologists/image_sources.rst
@@ -4,22 +4,22 @@ Loading and naming image sources
An OpenHCS plate can contain one image store or several stores together. Two
separate decisions are involved:
-1. a microscope or store handler owns **ingestion** and publishes addressable
- planes;
+1. a microscope or store dataset source owns **ingestion** and publishes
+ addressable planes;
2. source bindings optionally **select and name** those planes for pipeline
inputs.
.. openhcs-gallery:: source-image-browser
-For a recognized HCS layout, OpenHCS keeps the native microscope handler. CZI,
+For a recognized HCS layout, OpenHCS keeps the native microscope source. CZI,
OME-TIFF, and other supported rich containers are decoded by the Bio-Formats
-store handler using their embedded metadata. Add the containing directory to
-the Plate Manager and initialize with auto-detection; a source binding is not
+store source using their embedded metadata. Add the containing directory to
+the Datasets pane and initialize with auto-detection; a source binding is not
required merely to open or browse the store.
-``SourceBindingsHandler`` has a different role: it is the ingestion fallback for
+``SourceBindingsSource`` has a different role: it is the ingestion fallback for
an arbitrary image folder whose layout is not recognized. A non-empty
-``PipelineConfig.source_bindings_config`` tells that handler which ordinary
+``PipelineConfig.source_bindings_config`` tells that source which ordinary
TIFF, PNG, JPEG, or other registered files form the source universe and how
filename metadata should be interpreted.
@@ -30,7 +30,7 @@ their original file provenance and gives them a stable name such as ``DNA`` or
internals and do not select a storage backend.
This means ``SourceBindingsConfig`` can still name channels or samples from a
-CZI after Bio-Formats has decoded it. In that case the Bio-Formats handler remains
+CZI after Bio-Formats has decoded it. In that case the Bio-Formats source remains
the ingestion owner; the binding is semantic selection, not a replacement CZI
loader.
@@ -40,7 +40,7 @@ OME-Zarr input and output
OpenHCS reads OME-Zarr single-resolution images and plates stored as Zarr 2 /
NGFF 0.4 or Zarr 3 / NGFF 0.5. Discovery uses the declared image hierarchy and axes,
including time, channel and Z dimensions. Add the directory containing the
-store to the Plate Manager as for other rich image containers.
+store to the Datasets pane as for other rich image containers.
Selecting Zarr for pipeline output continues to produce Zarr 2 / NGFF 0.4
stores. Updating the installed Zarr library does not change that output format.
@@ -53,11 +53,11 @@ Native layout versus complete metadata
----------------------------------------
Filename/layout recognition and automatic detection are related but distinct.
-Run ``openhcs_inspect_plate_path`` with ``microscope_type="auto"`` first. Its
+Run ``openhcs_inspect_plate_path`` with ``source_format="auto"`` first. Its
``format_specific_handler_candidates`` are derived live from the registered
-``MicroscopeHandler`` parsers and detection contracts. If a broad store handler
+``DatasetSource`` parsers and detection contracts. If a broad store source
was selected but a format-specific candidate recognizes every tested physical
-filename under its declared ``root_dir``, do not assume the broad handler is the
+filename under its declared ``root_dir``, do not assume the broad source is the
better semantic owner merely because native metadata is missing.
Opera Phenix is a representative case. A complete plate export contains the
@@ -70,17 +70,17 @@ owner requires its ``Index.xml`` detection contract; matching filenames alone do
not make the subset a valid native plate. If those TIFFs are intentionally being
treated as loose ordinary images, select them with ``SourceBindingsConfig`` and
declare their Well/Site/Channel/Z/Time identities instead of explicitly selecting
-``microscope_type="opera_phenix"`` or accepting a broad decoder's inferred sample
+``source_format="opera_phenix"`` or accepting a broad decoder's inferred sample
layout.
-The folder-onboarding authoring context lists every currently registered handler
-and its handler-owned selection role. Use the format-specific owner for recognized
+The folder-onboarding authoring context lists every currently registered
+dataset source and its selection role. Use the format-specific owner for recognized
vendor layouts, the broad structured-store owner for supported rich containers
without a stronger native match, and the declared-file fallback for arbitrary
ordinary files whose schema you provide.
``source_stack_components`` describes axes physically present inside one selected
-file or store payload. Do not declare ``SITE`` or another source-stack axis when
+file or store payload. Do not declare ``Microscopy.Site`` or another source-stack axis when
each site is a separate ordinary 2-D file. Give each file its component identity,
then use ``processing_config.variable_components`` to assemble those selected files
along the callable's stack axis.
@@ -88,10 +88,10 @@ along the callable's stack axis.
Coordinates and source identity
-------------------------------
-Every resolved plane has one sample identity represented by ``WELL``. This is the
+Every resolved plane has one sample identity represented by ``Microscopy.Well``. This is the
embedded plate well when the image metadata declares one; otherwise it is the
-exact source-container identity. ``SITE``, ``CHANNEL``, ``Z_INDEX``, and
-``TIMEPOINT`` identify the plane within that sample. If a store declares an axis
+exact source-container identity. ``Microscopy.Site``, ``Microscopy.Channel``,
+``Microscopy.ZIndex``, and ``Microscopy.Timepoint`` identify the plane within that sample. If a store declares an axis
absent or singleton, its coordinate is ``"1"``. Embedded coordinates are never
replaced by filename guesses, and conflicting plane identities stop
initialization with an explicit error.
@@ -132,7 +132,7 @@ Keep three layers separate:
pipeline source universe
``PipelineConfig.source_bindings_config`` declares the full named physical
- source universe and the inputs consumed by the nominal handler projection. It
+ source universe and the inputs consumed by the dataset source projection. It
may correctly contain Hoechst channel 1, MAP2 channel 2, and SMI312 channel 4
even when one step needs only two of them.
Seeing all three in the source workspace inventory is valid.
@@ -145,7 +145,7 @@ per-step binding plan
callable stack positions
``processing_config.variable_components`` owns the meaning of the assembled
array axis. A callable ``channel_index`` is a zero-based position on that
- invocation's assembled axis, not a physical microscope ``CHANNEL`` value.
+ invocation's assembled axis, not a physical ``Microscopy.Channel`` value.
When viewer routes carry physical component identities from the first layer,
they retain the declared values; they do not renumber them to the third layer's
@@ -156,8 +156,8 @@ MAP2[channel=2], SMI312[channel=4])``. A MetaXpress neurite step has two useful
declaration choices:
simplest inherited full stack
- Set ``input_source=PIPELINE_START`` and
- ``variable_components=[CHANNEL]``, but omit ``FunctionStep.source_bindings``.
+ Set ``input_source=ReadPipelineStart`` and
+ ``variable_components=[Microscopy.Channel]``, but omit ``FunctionStep.source_bindings``.
The step inherits the complete pipeline order, so use
``nuclear_stain.channel_index=0``, ``cell_body.channel_index=1``, and
``neurite_channel_index=2``. Source provenance retains physical channels 1,
@@ -172,11 +172,11 @@ legacy shared-signal subset
not equivalent to MAP2-seeded analysis: retain MAP2 and use the inherited
three-channel form whenever MAP2 owns the neuronal bodies.
-In either option, ``variable_components=[CHANNEL]`` assembles the channel stack.
-Do not use ``group_by=CHANNEL`` for assembly. ``group_by`` partitions an already
+In either option, ``variable_components=[Microscopy.Channel]`` assembles the channel stack.
+Do not use ``group_by=Microscopy.Channel`` for assembly. ``group_by`` partitions an already
assembled value and selects branches only for a dictionary function pattern.
For this non-dictionary MetaXpress callable, an overlapping
-``group_by=CHANNEL`` is redundant and the compiler normalizes it to
+``group_by=Microscopy.Channel`` is redundant and the compiler normalizes it to
``Ungrouped``.
The explicit-subset regression is intentionally generic rather than a
@@ -210,7 +210,7 @@ step that selects MAP2 is valid evidence for that route. None of these cases
changes the zero-based positions of the selected callable stack.
Step source subsets and reordering select or re-enter original declared sources,
-especially at ``PIPELINE_START`` or on a branch that deliberately returns to the
+especially at the pipeline start or on a branch that deliberately returns to the
source universe. They do not reinterpret a previous step's output. Stitching,
Z projection, channel projection, or filtering may change that output's slice
count and axes; downstream code must use the current artifact provenance plus
@@ -222,11 +222,11 @@ Plan image stacks and source-bound labels before authoring
Naming two sources does not make both available in every invocation. Before
authoring a multi-image measurement, separate its current image input from its
-typed artifact inputs. ``input_source=PIPELINE_START`` returns to acquisition
+typed artifact inputs. ``input_source=ReadPipelineStart`` returns to acquisition
images; it does not remove the step's inherited variable-component scope.
When the invocation needs an assembled channel stack, declare
-``variable_components=[CHANNEL]`` with the intended step bindings/order. A
-``SITE`` stack or ``group_by=NONE`` alone does not assemble channels. Inspect the
+``variable_components=[Microscopy.Channel]`` with the intended step bindings/order. A
+``Microscopy.Site`` stack or ``group_by=Ungrouped`` alone does not assemble channels. Inspect the
resolved step configuration and artifact plan, not just the pipeline universe.
Object-label inputs also retain their producer's source context. Equal shape,
@@ -270,9 +270,9 @@ relation from their selected image inputs; a custom callable's input names and
Check the compiled image and artifact scopes before the first execution, then
read back each requested source's own values and label/object identity. Stack
assembly, image-set context and intensity-unit conversion are separate
-decisions: fixing one does not prove the others. A failed receipt remains
-preserved; validate a separately recorded declaration repair rather than replay
-an uncertain operation.
+decisions: fixing one does not prove the others. Keep the record of a failed
+run; validate a separately recorded declaration repair rather than replay an
+uncertain operation.
Executable code-mode declarations
---------------------------------
@@ -281,10 +281,10 @@ The following block constructs a complete Pipeline Editor code document for a
directory that mixes stores. The intermediate config values deliberately show
both source-binding roles:
-- ``tiff_png_config`` can drive ``SourceBindingsHandler`` when the directory is
+- ``tiff_png_config`` can drive ``SourceBindingsSource`` when the directory is
an otherwise unrecognized collection of ordinary files;
- the CZI, OME-TIFF, and OME-Zarr configs name/select planes after their store
- handlers decode them.
+ sources decode them.
Choose one as ``pipeline_config`` and keep the ``pipeline_steps`` assignment in
the same document. Filenames are exact examples; replace them with names present
@@ -296,7 +296,7 @@ source-binding semantics; do not pass that concrete class to this lazy field.
.. code-block:: python
- from openhcs.constants.input_source import InputSource
+ from openhcs.constants.input_source import ReadPipelineStart
from openhcs.core.config import (
LazyProcessingConfig,
LazySourceBindingsConfig,
@@ -305,10 +305,10 @@ source-binding semantics; do not pass that concrete class to this lazy field.
)
from openhcs.core.source_bindings import (
NamedSourceBinding,
+ SourceFileName,
SourceFilterClause,
- SourceFilterMatchType,
- SourceFilterSubject,
SourceSelector,
+ TextEquals,
)
from openhcs.core.steps.function_step import FunctionStep
from openhcs.processing.backends.processors.numpy_processor import (
@@ -322,8 +322,8 @@ source-binding semantics; do not pass that concrete class to this lazy field.
selector=SourceSelector(
filters=(
SourceFilterClause(
- subject=SourceFilterSubject.FILE,
- match_type=SourceFilterMatchType.EQUALS,
+ subject=SourceFileName,
+ match_type=TextEquals,
value=filename,
),
),
@@ -375,7 +375,7 @@ source-binding semantics; do not pass that concrete class to this lazy field.
name="Normalize DNA",
func=stack_percentile_normalize,
processing_config=LazyProcessingConfig(
- input_source=InputSource.PIPELINE_START,
+ input_source=ReadPipelineStart,
),
source_bindings=LazyStepSourceBindingsConfig(
enabled=True,
@@ -389,14 +389,14 @@ detection. OpenHCS first chooses the native, Bio-Formats/store, or arbitrary-
folder ingestion owner, then resolves every binding over the planes that owner
published. CZI and OME-TIFF inputs use the Bio-Formats runtime included by an
OpenHCS installation that enables those formats; OME-Zarr uses PolyStore
-directly. Do not force a rich image container through ``SourceBindingsHandler``
+directly. Do not force a rich image container through ``SourceBindingsSource``
when its structured decoder is
missing or unhealthy; repair that decoder instead.
Reopening OpenHCS outputs
-------------------------
-Add a generated output plate to the Plate Manager and initialize it normally.
+Add a generated output plate to the Datasets pane and initialize it normally.
OpenHCS reloads the semantic axes and source contributors saved with each
processed image. Do not add source bindings merely to recreate an axis that a
processing step collapsed: a coordinate retained in the filename identifies the
@@ -414,8 +414,8 @@ coordinate in a format-specific path rule. Use it as ``pipeline_config`` in the
complete document above; source-backed MCP execution and Pipeline Editor code
mode must also receive ``pipeline_steps``.
-``selector.components`` matches the coordinates published by the source
-handler. ``component_identity`` is optional and assigns the semantic coordinate
+``selector.components`` matches the coordinates published by the dataset
+source. ``component_identity`` is optional and assigns the semantic coordinate
used after selection. Here the selector matches physical channel ``2`` while
``component_identity`` assigns the projected channel value ``DNA``. The
original channel remains in original source metadata; compilation, grouping,
diff --git a/docs/source/guide_for_biologists/intro.rst b/docs/source/guide_for_biologists/intro.rst
index 8548e3b02d..036e644365 100644
--- a/docs/source/guide_for_biologists/intro.rst
+++ b/docs/source/guide_for_biologists/intro.rst
@@ -22,8 +22,8 @@ OpenHCS is useful when you need to:
The available microscope formats and function libraries depend on the installed
version and optional dependencies. The application discovers them from their
-registered declarations; use the Plate Manager and function browser as the
-current capability catalog.
+registered declarations; use the ``dataset_source`` choices in the
+configuration form and the function browser as the current capability catalog.
How a run works
---------------
diff --git a/docs/source/guide_for_biologists/intro_stitching.rst b/docs/source/guide_for_biologists/intro_stitching.rst
index acb74cb4d3..92fbd2030b 100644
--- a/docs/source/guide_for_biologists/intro_stitching.rst
+++ b/docs/source/guide_for_biologists/intro_stitching.rst
@@ -37,9 +37,9 @@ Initialise the source data
2. Select **Init** in Plate Manager.
3. Wait for initialisation to finish.
-When initialisation succeeds, **Compile** becomes available. Open the metadata
-viewer if you want to confirm the discovered wells, sites, channels, and Z
-planes before continuing.
+When initialisation succeeds, **Compile** becomes available. Select **Viewer**
+and open its **Metadata** tab if you want to confirm the discovered wells,
+sites, channels, and Z planes before continuing.
.. openhcs-gallery:: ui-plate-metadata
diff --git a/docs/source/guide_for_biologists/troubleshooting_FAQ.rst b/docs/source/guide_for_biologists/troubleshooting_FAQ.rst
index 4e3897435d..507a936249 100644
--- a/docs/source/guide_for_biologists/troubleshooting_FAQ.rst
+++ b/docs/source/guide_for_biologists/troubleshooting_FAQ.rst
@@ -59,9 +59,9 @@ the running UI.
An attached MCP client can request the main window's **Restart OpenHCS and
restore session** action once initialisation, compilation and execution have
finished. It saves the plate declarations and edit history before relaunching.
-The accepted action receipt precedes shutdown: rediscover the new UI bridge and
-check the restored session after restart. The old process's operation receipt
-does not persist across that boundary.
+The action returns before the old UI exits: rediscover the new UI bridge and
+check the restored session after restart. The old process's operation status
+does not survive the restart.
If a restored session refers to a local plate directory that is unavailable,
OpenHCS restores the plate and edit history but leaves that plate uninitialized.
@@ -100,7 +100,7 @@ configuration.
If a function registered through MCP is missing, confirm that the MCP client
and desktop use the same execution endpoint, then reopen the selector. Each
opening requests current server membership. For isolated development sessions,
-``OPENHCS_UI_CONFIG_CACHE_FILE`` selects the canonical UI configuration for
+``OPENHCS_UI_CONFIG_CACHE_FILE`` selects the shared UI configuration for
both processes; selecting a UI bridge descriptor alone does not select the
execution endpoint.
diff --git a/docs/source/guides/complete_examples.rst b/docs/source/guides/complete_examples.rst
index 2b8b491d1b..ff5ce6eede 100644
--- a/docs/source/guides/complete_examples.rst
+++ b/docs/source/guides/complete_examples.rst
@@ -14,7 +14,7 @@ the returned section from ``openhcs_official30_benchmark_recipes`` with
``max_chars=50000``. For example, section
``examplehuman-openhcs-python`` defines both ``pipeline_config`` and
``pipeline_steps`` using the public API. Sections are generated lazily from the
-manifest-resolved ``.cppipe`` through the canonical importer; they are not a
+manifest-resolved ``.cppipe`` through the public importer; they are not a
second checked-in set of generated scripts.
The same source is available to Python clients through
@@ -58,8 +58,8 @@ desktop application, which uses the same declarations and compiler.
Typed native presets
--------------------
-The current native MFD variants are declared once by ``MfdPresetKey`` and
-materialized by ``build_mfd_preset`` in
+The current native MFD variants are declared once as ``MfdPresetDefinition``
+values and materialized by ``build_mfd_preset`` in
``openhcs.processing.presets.mfd_specs``. The four corresponding
``10x_mfd_*.py`` modules are thin wrappers over that owner. Consult
:doc:`example_corpus_map` for their source index. Crop presets resolve
@@ -74,7 +74,8 @@ Loose Opera Phenix neurite outgrowth
is a complete, parameterized CellProfiler-backed example for selected Opera
Phenix TIFFs copied without ``Index.xml``. Edit its ``example_inputs`` boundary
for the plate path, exact Hoechst/MAP2/SMI312 filenames, well/site/Z/time
-identities, output root, and viewer port.
+identities, output root, and viewer port. Set its ``source_voxel_spacing`` when
+the copied TIFFs need a calibration; they lose it with ``Index.xml``.
The example uses MAP2 objects as neuronal seeds, enhances and skeletonizes
SMI312 neurites, measures topology per seed, and propagates seed identities into
@@ -91,8 +92,9 @@ It composes the registered CellProfiler-compatible leaves behind one
MetaXpress-style step while retaining typed measurements and unified neuron
labels.
-Use the native ``Microscope.OPERAPHENIX`` handler when the complete plate and
-``Index.xml`` are available. Source bindings are appropriate here because the
+Use the native Opera Phenix source (``OperaPhenixHandler``, selected by
+``dataset_source`` detection) when the complete plate and ``Index.xml`` are
+available. Source bindings are appropriate here because the
loose files no longer carry the plate-level metadata needed by that handler.
Master multi-plate lab-meeting showcase
diff --git a/docs/source/guides/example_corpus_map.rst b/docs/source/guides/example_corpus_map.rst
index cce8aef510..2a62f5dba9 100644
--- a/docs/source/guides/example_corpus_map.rst
+++ b/docs/source/guides/example_corpus_map.rst
@@ -62,8 +62,8 @@ Native OpenHCS Examples
-----------------------
The checked-in MFD preset authority is
-``openhcs/processing/presets/mfd_specs.py``. It owns the typed preset keys,
-variant declarations, shared step templates, materializers, and
+``openhcs/processing/presets/mfd_specs.py``. It owns the preset
+definitions, shared step templates, materializers, and
``build_mfd_preset``. These four small modules are current materialization
wrappers over that authority:
@@ -120,7 +120,7 @@ within that benchmark's source and measurement scope. It does not establish
biological acceptance for a new assay: review its raw pixels and output overlay
at matched coordinates, then preserve that new validation and any failures
before promoting an adapted recipe. The portable acceptance boundary and
-required parity receipts are described in
+required parity evidence are described in
:doc:`../architecture/measurement_equivalence_system` and
``benchmark/manifests/README.md``; do not infer a particular case passed from
its presence in the recipe catalogue alone.
diff --git a/docs/source/guides/fiji_viewer_management.rst b/docs/source/guides/fiji_viewer_management.rst
index 2dee62ad6d..522b2e774b 100644
--- a/docs/source/guides/fiji_viewer_management.rst
+++ b/docs/source/guides/fiji_viewer_management.rst
@@ -38,19 +38,21 @@ the bound process is compatible.
Settlement and inspection boundary
----------------------------------
-Fiji participates in the same typed settlement control contract as Napari, but
-its current display path is synchronous. A ``SETTLE`` request therefore returns
-terminal ``ViewerSettleProgress`` immediately; Fiji does not expose Napari's
-queue of deferred Qt layer-route updates. This still provides one common
-execution/lifecycle protocol without pretending the two viewer implementations
-have the same internal update model.
+Fiji answers the same typed ``SETTLE`` control as Napari through the shared
+viewer control actions. Its display path batches streamed items through
+PolyStore's debounced batch engine, and ``SETTLE`` reports
+``ViewerSettleProgress`` for the queued and active batches: running until every
+queued item has rendered, complete afterwards, or failed with the retained
+native rendering error. Fiji has no queue of deferred Qt layer-route updates;
+the two viewers share the settlement contract, not an internal update model.
Fiji does **not** currently provide Napari-style live viewer-state or payload
-projection. ``STATE`` and ``PAYLOADS`` control requests return explicit errors
-until a Fiji state projector exists. Inspection clients therefore cannot query
-Fiji layers, axes, labels, payload arrays, or payload summaries through those
-controls. This is a declared capability boundary, not a port/readiness failure;
-do not retry it as transport recovery or infer viewer state from window titles.
+projection. It registers no ``STATE`` or ``PAYLOADS`` control action, so those
+requests return an explicit unsupported-control error. Inspection clients
+therefore cannot query Fiji layers, axes, labels, payload arrays, or payload
+summaries through those controls. This is a declared capability boundary, not a
+port/readiness failure; do not retry it as transport recovery or infer viewer
+state from window titles.
Fiji macro execution, display construction, readiness, clear-state control, and
process lifecycle remain separate supported surfaces. Use Napari when automated
diff --git a/docs/source/guides/memory_type_integration.rst b/docs/source/guides/memory_type_integration.rst
index fc332f29ed..bf57c24909 100644
--- a/docs/source/guides/memory_type_integration.rst
+++ b/docs/source/guides/memory_type_integration.rst
@@ -55,8 +55,9 @@ projection. OpenHCS launchers consume the combined result; they do not mirror
framework package layouts or rely on a parent process importing frameworks in a
particular order.
-``ProcessingContract`` is independent of memory type. It says whether a
-callable is semantically plane-local or stack-dependent. Likewise,
+The ``ProcessingContract`` family, such as ``Pure2DContract`` above, is
+independent of memory type. Its subclass says whether a callable is
+semantically plane-local or stack-dependent. Likewise,
``variable_components`` defines the transported stack axis and ``group_by``
defines post-stack grouping. See :doc:`../architecture/processing_semantics`.
diff --git a/docs/source/guides/omero_integration.rst b/docs/source/guides/omero_integration.rst
index b332189f03..2f19ba1ecf 100644
--- a/docs/source/guides/omero_integration.rst
+++ b/docs/source/guides/omero_integration.rst
@@ -12,7 +12,7 @@ PolyStore
Owns generic storage backends, virtual paths, source references, and ROI
persistence primitives. Its OMERO declarations own text formats and MIME
types, table parsing and service readiness, image-plane batching, and the
- canonical OMERO well and plane addresses.
+ OMERO well and plane addresses.
OpenHCS
Owns microscope/source selection, source bindings, compilation, processing,
diff --git a/docs/source/guides/pipeline_compilation_workflow.rst b/docs/source/guides/pipeline_compilation_workflow.rst
index 7a1d9e0251..41fdce8248 100644
--- a/docs/source/guides/pipeline_compilation_workflow.rst
+++ b/docs/source/guides/pipeline_compilation_workflow.rst
@@ -12,7 +12,7 @@ The compiler accepts an initialized ``PipelineOrchestrator`` and an ordered
``list[AbstractStep]``. Together they provide:
- a ``PipelineConfig`` and ObjectState scope topology;
-- the microscope/source workspace and selected execution axes;
+- the dataset-source workspace and selected execution axes;
- callable, module, source, and artifact declarations;
- per-step processing, materialization, filtering, and streaming configuration.
diff --git a/docs/source/guides/testing_guide.rst b/docs/source/guides/testing_guide.rst
index 088654dd7c..b4eca62aaa 100644
--- a/docs/source/guides/testing_guide.rst
+++ b/docs/source/guides/testing_guide.rst
@@ -71,6 +71,19 @@ An uncaptured native Qt/OpenGL preflight proves that context before pytest,
so missing system libraries produce their own diagnostics rather than a
captured application-startup abort.
+The Official30 headless parity lane compares all thirty workflows against the
+committed native CellProfiler outputs on Python 3.12 Linux and on Python 3.14
+Linux, Windows, and macOS. A pull request that changes none of the processing,
+core, format, CellProfiler interop, submodule, benchmark, integration-test, or
+packaging paths skips that lane;
+the required parity check accepts the skip only when that scope check proved
+the change irrelevant.
+
+The test jobs install candidate wheels with ``scripts/install_ci_candidate.py``
+and run the checkout's tests through ``scripts/run_installed_tests.py``, which
+removes checkout source paths from the import path so OpenHCS is imported from
+the installed wheel.
+
The Python/OS boundary matrix also opens fresh MCP stdio sessions before
importing NumPy and SciPy inside a tool. These tests exercise cold native-library
loading while the protocol reader is active, plus protocol-channel isolation
diff --git a/docs/source/guides/viewer_management.rst b/docs/source/guides/viewer_management.rst
index fb485f7151..78a3759d0a 100644
--- a/docs/source/guides/viewer_management.rst
+++ b/docs/source/guides/viewer_management.rst
@@ -29,9 +29,10 @@ session's exact process group and are stopped when that session is cleaned up.
For resource-bound candidate review, retrieve the ``openhcs_viewer_qa`` knowledge
document, also bundled in the ``use-openhcs`` skill. Its conditional selective-
retirement procedure follows the installed typed capability and distinguishes
-scene/payload custody from visibility, durable evidence and measured memory.
-That is the operating procedure; this lifecycle overview does not maintain a
-second retirement recipe or assume that every installed viewer supports it.
+retained scene and payload state from visibility, durable evidence and measured
+memory. That is the operating procedure; this lifecycle overview does not
+maintain a second retirement recipe or assume that every installed viewer
+supports it.
Display-axis ownership
----------------------
@@ -176,7 +177,8 @@ These mechanisms answer different questions:
``NapariStreamingConfig`` / ``FijiStreamingConfig``
Show eligible outputs while the selected step executes. Display axes,
- batching, transport, viewer persistence, and well selection belong here.
+ batching, transport, viewer persistence, and partition (well) selection belong
+ here.
``StepMaterializationConfig``
Save the step's ordinary main-flow result as a persistent checkpoint. Set it
diff --git a/docs/source/installation.rst b/docs/source/installation.rst
index b5107b8746..67aa1306fa 100644
--- a/docs/source/installation.rst
+++ b/docs/source/installation.rst
@@ -1,7 +1,7 @@
Installation
============
-The canonical installation instructions live in the Getting Started guide.
+The maintained installation instructions live in the Getting Started guide.
See :doc:`getting_started/getting_started` for desktop installers, manual
Python installation, optional capabilities, launch verification, and updates.
diff --git a/docs/source/reference/experimental_config_syntax.rst b/docs/source/reference/experimental_config_syntax.rst
index 5bb8076a7d..d551023883 100644
--- a/docs/source/reference/experimental_config_syntax.rst
+++ b/docs/source/reference/experimental_config_syntax.rst
@@ -176,7 +176,7 @@ pipeline or step configuration.
.. code-block:: python
- from openhcs.core.config import ExperimentalAnalysisConfig
+ from openhcs.domains.microscopy.config import ExperimentalAnalysisConfig
from openhcs.processing.backends.experimental_analysis import (
ExperimentalAnalysisEngine,
)
diff --git a/docs/source/user_guide/analysis_consolidation.rst b/docs/source/user_guide/analysis_consolidation.rst
index dc251a0ae4..56137d3e7a 100644
--- a/docs/source/user_guide/analysis_consolidation.rst
+++ b/docs/source/user_guide/analysis_consolidation.rst
@@ -20,8 +20,9 @@ workbook contract is documented in
:doc:`../reference/experimental_config_syntax`.
Automatic post-plate consolidation is controlled by
-``AnalysisConsolidationConfig`` on the pipeline. It selects file extensions,
-well extraction, exclusions, and output names. Consolidation consumes
+``AnalysisConsolidationConfig`` in the global pipeline configuration. It
+selects the MetaXpress-style layout, file extensions, exclusion patterns, and
+output names. Consolidation consumes
materialized analysis artifacts; it is not an image-processing ``FunctionStep``
and does not reconstruct compiler state.
diff --git a/docs/source/user_guide/code_ui_editing.rst b/docs/source/user_guide/code_ui_editing.rst
index b0fd50a011..e8cd77ea8a 100644
--- a/docs/source/user_guide/code_ui_editing.rst
+++ b/docs/source/user_guide/code_ui_editing.rst
@@ -16,7 +16,7 @@ trip is:
Choose the document that owns the edited object. Pipeline Editor projects one
complete ``PipelineDocument``; a step editor projects one ``FunctionStep``; a
-configuration window projects its typed config; Plate Manager projects the
+configuration window projects its typed config; the Datasets window projects the
multi-plate aggregate. The available Code controls and MCP document catalog are
discovered from those nominal owners rather than a copied button list.
@@ -24,7 +24,7 @@ Select a plate before applying a Pipeline Editor document. Without a selected
plate, the document remains readable for inspection but reports
``writable=False`` and rejects apply requests without changing UI state.
-A Plate Manager document also carries its read-time selection scope. An
+A Datasets document also carries its read-time selection scope. An
all-plates document represents the complete visible collection, so applying it
synchronises that collection. A selected document may replace only the named
plates and leaves every unselected plate unchanged. Its payload must contain the
@@ -43,8 +43,8 @@ generated code includes the explicit assignment again. Provide
``pipeline_config`` when the pipeline needs non-default source, processing,
viewer, or materialisation settings.
-Reuse the exact imports shown in generated code. Plate Manager accepts external
-processing functions listed in the execution endpoint's catalogue, including
+Reuse the exact imports shown in generated code. The Datasets document accepts
+external processing functions listed in the execution endpoint's catalogue, including
``skimage.filters.edges.sobel``. Applying the document resolves the selected
catalogue wrapper, preserving its processing contract and runtime controls.
Wildcard imports and functions absent from that catalogue are rejected.
@@ -59,7 +59,7 @@ declared parameters in their supplied order. Decimal fields use compact,
locale-aware text, so a value such as ``99.8`` remains readable without changing
the field's configured numeric precision.
-Applying a complete Pipeline Editor or Plate Manager code document reconciles
+Applying a complete Pipeline Editor or Datasets code document reconciles
the submitted declaration with the live ObjectState graph. Unchanged and
unambiguously edited occurrences retain their history across reordering; added
occurrences receive new scopes and omitted occurrences are removed. Ambiguous
diff --git a/docs/source/user_guide/custom_function_management.rst b/docs/source/user_guide/custom_function_management.rst
index b5bfef6ca3..8259d684cf 100644
--- a/docs/source/user_guide/custom_function_management.rst
+++ b/docs/source/user_guide/custom_function_management.rst
@@ -18,8 +18,8 @@ shared request.
``CustomFunctionManager`` owns persisted source and coordinates create, load,
replace, and delete transactions. It stores source under the platform-specific
-OpenHCS data directory, validates code before execution, requires a supported
-memory decorator, and rejects name collisions. The process-local
+OpenHCS data directory, validates code before execution, requires exactly one
+decorated processing function per source, and rejects name collisions. The process-local
``CustomFunctionRuntimeRegistry`` atomically publishes the derived callable
metadata, source lifetime, and public module exports, so readers see either the
previous complete catalogue or the validated replacement. Reloading persisted
diff --git a/docs/source/user_guide/custom_functions.rst b/docs/source/user_guide/custom_functions.rst
index ea2299b488..ec6c66daa6 100644
--- a/docs/source/user_guide/custom_functions.rst
+++ b/docs/source/user_guide/custom_functions.rst
@@ -20,10 +20,11 @@ function in the OpenHCS function catalog.
Choose the processing contract deliberately:
-- ``PURE_2D`` means each plane is semantically independent;
-- ``PURE_3D`` means the result depends on the whole stack;
-- ``VOLUMETRIC_TO_SLICE`` consumes a volume and returns plane-local output;
-- ``FLEXIBLE`` is for callables that explicitly support either form.
+- ``Pure2DContract`` means each plane is semantically independent;
+- ``Pure3DContract`` means the result depends on the whole stack;
+- ``VolumetricToSliceContract`` consumes a volume and returns plane-local
+ output;
+- ``FlexibleContract`` is for callables that explicitly support either form.
Do not attach compiler fields manually or inspect array framework names inside
the function. Declare memory, locality, required axes, runtime parameters, and
diff --git a/docs/source/user_guide/mcp_clients.rst b/docs/source/user_guide/mcp_clients.rst
index 7a968d4b95..4b6197c79b 100644
--- a/docs/source/user_guide/mcp_clients.rst
+++ b/docs/source/user_guide/mcp_clients.rst
@@ -40,7 +40,7 @@ open PyQt windows automatically.
The installed stdio server defaults to the ``desktop`` capability surface. It
includes normal UI, selected-plate, viewer, plate-data, knowledge,
function-authoring, pipeline-authoring, and independent headless-execution
-tools. Headless sessions do not create or mirror desktop Plate Manager state.
+tools. Headless sessions do not create or mirror desktop Datasets window state.
The desktop profile hides runtime-server, fallback-widget, and expert-only
tools. The surface is projected from capability declaration metadata, not a
copied tool list. Advanced users can select a narrower or development-oriented
@@ -241,7 +241,8 @@ directory for a new run. These inspection routes do not submit a pipeline; use
the session tools above to run one.
The ``openhcs-benchmark run-measured`` CLI command accepts a normal Python
pipeline source file, plate and empty evidence directory, runs it on the
-ordinary execution runtime and retains the measured receipt;
+ordinary execution runtime and retains the measured receipt. It requires an
+explicit ``--wait-timeout-ms``; the session stops a run that exceeds it.
``--execution-plate`` can identify a prepared input while preserving the
original plate identity. For large pipelines,
``--observation-scope outcomes`` retains per-axis status, output roots, and
diff --git a/docs/source/user_guide/real_time_visualization.rst b/docs/source/user_guide/real_time_visualization.rst
index 8a65439154..db5327d83b 100644
--- a/docs/source/user_guide/real_time_visualization.rst
+++ b/docs/source/user_guide/real_time_visualization.rst
@@ -8,8 +8,8 @@ not artifact materialization, a special pipeline class, or a direct viewer call.
1. Install ``openhcs[gui,napari]``, ``openhcs[gui,fiji]``, or
``openhcs[gui,viz]``.
2. Open a step in the Pipeline Editor.
-3. Enable its viewer configuration and choose persistence, batching, well
- filters, and display options.
+3. Enable its viewer configuration and choose persistence, partition
+ filters, transport, and display options.
4. Compile. OpenHCS validates source metadata, transport, and viewer
requirements independently of artifact materialization.
5. Run the compiled plate and inspect the detached viewer.
diff --git a/external/ObjectState b/external/ObjectState
index 4eb8de1398..a44b90b08a 160000
--- a/external/ObjectState
+++ b/external/ObjectState
@@ -1 +1 @@
-Subproject commit 4eb8de1398428c539e1e14360c847ccea2fe4b34
+Subproject commit a44b90b08afab86b9d142fe59f9fa01cd61beab4
diff --git a/external/PolyStore b/external/PolyStore
index 7ac9d01110..25ee78d94a 160000
--- a/external/PolyStore
+++ b/external/PolyStore
@@ -1 +1 @@
-Subproject commit 7ac9d011100b18f4ad1a54635ee94f52a47efb30
+Subproject commit 25ee78d94aa31f3e12693c14be3de0d10e104086
diff --git a/external/pyqt-reactive b/external/pyqt-reactive
index 1bce7de2b3..c4f54b2917 160000
--- a/external/pyqt-reactive
+++ b/external/pyqt-reactive
@@ -1 +1 @@
-Subproject commit 1bce7de2b33edd5b054f596da2b321c9b7cf93fd
+Subproject commit c4f54b291787c2eed187238f0d3491d3e4c0f3ff
diff --git a/external/zmqruntime b/external/zmqruntime
index dc327e6ca2..3bb1b13098 160000
--- a/external/zmqruntime
+++ b/external/zmqruntime
@@ -1 +1 @@
-Subproject commit dc327e6ca2496aa4ad405479832f4e9e219750e4
+Subproject commit 3bb1b13098aaf022cc295e2949b5fbfb393c6f94
diff --git a/openhcs/agent/capabilities.py b/openhcs/agent/capabilities.py
index 27a03c7018..5cf4ac9586 100644
--- a/openhcs/agent/capabilities.py
+++ b/openhcs/agent/capabilities.py
@@ -461,7 +461,8 @@ class NonExpertCapabilitySurfaceMixin:
def includes(self, capability: type["AgentCapabilityDeclaration"]) -> bool:
return (
capability.exposition.visibility is not CapabilityVisibility.EXPERT
- and capability.exposition.role not in (CapabilityRole.EXPERT, CapabilityRole.FALLBACK)
+ and capability.exposition.role
+ not in (CapabilityRole.EXPERT, CapabilityRole.FALLBACK)
and super().includes(capability)
)
@@ -472,8 +473,9 @@ class WorkflowGroupCapabilitySurfaceMixin:
workflow_groups: ClassVar[frozenset[CapabilityWorkflowGroup]]
def includes(self, capability: type["AgentCapabilityDeclaration"]) -> bool:
- return capability.exposition.workflow_group in self.workflow_groups and super().includes(
- capability
+ return (
+ capability.exposition.workflow_group in self.workflow_groups
+ and super().includes(capability)
)
@@ -654,7 +656,9 @@ def require_agent_type_contract(contract: AgentContract | None) -> type:
def _keyword_parameter(name: str, annotation: object, default: object) -> Parameter:
- return Parameter(name, Parameter.KEYWORD_ONLY, default=default, annotation=annotation)
+ return Parameter(
+ name, Parameter.KEYWORD_ONLY, default=default, annotation=annotation
+ )
class AgentCapabilityInvocation(ABC):
@@ -786,7 +790,6 @@ class AgentResourceInvocationMixin:
kind: ClassVar[CapabilityKind] = CapabilityKind.RESOURCE
-
def bind_mcp(
self,
declaration: type[AgentCapabilityDeclaration],
@@ -1036,8 +1039,12 @@ def tool_parameters(self, declaration, binder):
)
def invoke(self, declaration, binder, arguments):
- return super().invoke(declaration, binder, arguments).as_jsonable(
- compact_actions=bool(arguments["compact_actions"]),
+ return (
+ super()
+ .invoke(declaration, binder, arguments)
+ .as_jsonable(
+ compact_actions=bool(arguments["compact_actions"]),
+ )
)
@@ -1152,8 +1159,6 @@ class AgentConfigPatchServiceInvocation(
__slots__ = ()
-
-
class AgentConnectionRequestServiceInvocation(
AgentUiConnectionMixin,
AgentFromFieldsInputMixin,
@@ -1162,7 +1167,6 @@ class AgentConnectionRequestServiceInvocation(
__slots__ = ()
-
class AgentViewerWindowConnectionServiceInvocation(
AgentViewerWindowConnectionMixin,
AgentServiceInvocation,
@@ -1195,6 +1199,7 @@ def invoke(self, declaration, binder, arguments):
*self.call_arguments(declaration, binder, arguments),
)
+
@dataclass(frozen=True, slots=True)
class AgentCapabilitySearchRequest:
"""Bounded registry query over declaration-owned capability metadata."""
@@ -1659,7 +1664,9 @@ class UiBridgeOperationExecution(AgentCapabilityInvocation):
def execute(self, context: object, *arguments: object) -> object:
*request, connection = arguments
- return context.ui_bridge_service.invoke(self.operation, *request, connection)
+ return context.ui_bridge_service.invoke(
+ self.operation, *request, connection=connection
+ )
@dataclass(frozen=True, slots=True)
@@ -2160,7 +2167,9 @@ class ObserveCustomFunctionRegistrationCapability(FunctionCatalogCapability):
output_contract = CustomFunctionRegistrationObservation
invocation = AgentDataclassRequestServiceInvocation(
service=lambda context: context.function_catalog,
- method=lambda service, request: service.observe_custom_function_registration(request),
+ method=lambda service, request: service.observe_custom_function_registration(
+ request
+ ),
)
@@ -2379,7 +2388,9 @@ class SamplePlateImageCapability(MainThreadProgressCapability, PlatePathCapabili
)
-class StreamPlateFilesToViewerCapability(MainThreadProgressCapability, PlatePathCapability):
+class StreamPlateFilesToViewerCapability(
+ MainThreadProgressCapability, PlatePathCapability
+):
name = "openhcs_stream_plate_files_to_viewer"
cli_command = "stream-plate-files"
title = "Stream plate files to viewer"
@@ -2508,7 +2519,9 @@ class UiSampleSelectedPlateImageCapability(UiSelectedPlateCapability):
)
-class UiStreamSelectedPlateFilesToViewerCapability(MainThreadProgressCapability, UiSelectedPlateCapability):
+class UiStreamSelectedPlateFilesToViewerCapability(
+ MainThreadProgressCapability, UiSelectedPlateCapability
+):
name = "openhcs_ui_stream_selected_plate_files_to_viewer"
cli_command = "selected-plate-stream"
title = "Stream selected plate files to viewer"
diff --git a/openhcs/agent/dto/session.py b/openhcs/agent/dto/session.py
index 45e37b84cc..65145850e8 100644
--- a/openhcs/agent/dto/session.py
+++ b/openhcs/agent/dto/session.py
@@ -27,7 +27,9 @@
It keeps the MCP schema's published name and description for the field.
"""
-RuntimeObservationExportScopeName.__doc__ = ZMQRuntimeObservationExportScope.__doc__.splitlines()[0]
+RuntimeObservationExportScopeName.__doc__ = (
+ ZMQRuntimeObservationExportScope.__doc__.splitlines()[0]
+)
RuntimeObservationExportScopeName.scope = property(
lambda member: ZMQRuntimeObservationExportScope.named(member.value)
)
@@ -164,6 +166,8 @@ class DatasetRowState:
debug_session_id: str | None = None
finished_execution_id: str | None = None
"""The dataset's latest finished ordinary execution (its evidence id)."""
+ finished_execution_error: str | None = None
+ """The server's error for that execution, when it did not complete."""
scope_accent_color: str | None = None
diff --git a/openhcs/agent/knowledge_manifest_schema.py b/openhcs/agent/knowledge_manifest_schema.py
index 9dd6751aaf..e71f892b53 100644
--- a/openhcs/agent/knowledge_manifest_schema.py
+++ b/openhcs/agent/knowledge_manifest_schema.py
@@ -4,6 +4,7 @@
import json
import os
+import re
from collections.abc import Mapping
from dataclasses import dataclass
from enum import Enum
@@ -13,6 +14,12 @@
"docs/source/development/mcp_knowledge_base_manifest.json"
)
PACKAGED_KNOWLEDGE_BASE_ROOT = Path("resources/knowledge")
+# Windows without long-path support rejects installed paths of 260 characters
+# or more. The installer places the knowledge root at
+# /env-XXXXXXXX/Lib/site-packages/openhcs/agent/resources/knowledge,
+# so every resource path relative to that root must stay within this budget.
+PACKAGED_KNOWLEDGE_PATH_BUDGET = 120
+_PORTABLE_CASE_NAME = re.compile(r"[A-Za-z0-9_](?:[A-Za-z0-9_ .-]*[A-Za-z0-9_])?")
class KnowledgeBaseManifestField(str, Enum):
@@ -145,21 +152,21 @@ def load(cls, path: Path, *, source_root: Path) -> ComparisonManifestSnapshot:
return manifest
def native_source_relative_path(self, case: Mapping[str, object]) -> Path:
- """Derive one placement from its original manifest/root/path identity."""
+ """Place one native recipe by its manifest-unique case name.
+
+ Upstream root/path layouts are deep; keying by case name keeps every
+ installed resource within ``PACKAGED_KNOWLEDGE_PATH_BUDGET``.
+ """
path = Path(str(case["cppipe_path"]))
- root = Path(str(case.get("cppipe_path_root", "unrooted")))
- if (
- path.is_absolute()
- or root.is_absolute()
- or ".." in path.parts
- or ".." in root.parts
- or path.suffix != ".cppipe"
- ):
+ name = str(case.get("name"))
+ if path.is_absolute() or ".." in path.parts or path.suffix != ".cppipe":
raise ValueError("Native knowledge source must be a relative .cppipe path.")
- return (
- self.path.relative_to(self.source_root).with_suffix(".sources")
- / root
- / path
+ if not _PORTABLE_CASE_NAME.fullmatch(name):
+ raise ValueError(
+ f"Native knowledge case name {name!r} is not a portable file name."
+ )
+ return self.path.relative_to(self.source_root).with_suffix(".sources") / (
+ f"{name}.cppipe"
)
def native_source_path(self, case: Mapping[str, object]) -> Path:
diff --git a/openhcs/authoring/session/operations/datasets.py b/openhcs/authoring/session/operations/datasets.py
index d9030c452f..af97138844 100644
--- a/openhcs/authoring/session/operations/datasets.py
+++ b/openhcs/authoring/session/operations/datasets.py
@@ -148,7 +148,9 @@ class DeleteDatasets(IdleTargetsOperation, HeadlessOperation):
operation_id = "delete_datasets"
label = "Del"
tooltip = "Delete selected datasets"
- description = "Removes datasets and their configuration and pipeline from the session."
+ description = (
+ "Removes datasets and their configuration and pipeline from the session."
+ )
side_effects = ("mutates_dataset_list",)
@classmethod
@@ -170,7 +172,9 @@ def available(cls, session, request) -> AgentError | None:
error = super().available(session, request)
if error is not None:
return error
- pending = [s for s in request.scope_ids if session.has_pending_definition_work(s)]
+ pending = [
+ s for s in request.scope_ids if session.has_pending_definition_work(s)
+ ]
if pending:
return AgentError(
code="dataset_definition_pending",
@@ -179,7 +183,9 @@ def available(cls, session, request) -> AgentError | None:
return None
-class InitializeDatasets(IdleTargetsOperation, DatasetWorkflowOperation, HeadlessOperation):
+class InitializeDatasets(
+ IdleTargetsOperation, DatasetWorkflowOperation, HeadlessOperation
+):
operation_id = "initialize_datasets"
workflow_name = "INIT"
label = "Init"
@@ -330,7 +336,9 @@ def available(cls, session, request) -> AgentError | None:
code="execution_batch_active",
message="An execution batch is already running.",
)
- pending = [s for s in request.scope_ids if session.has_pending_definition_work(s)]
+ pending = [
+ s for s in request.scope_ids if session.has_pending_definition_work(s)
+ ]
if pending:
return AgentError(
code="dataset_definition_pending",
@@ -357,7 +365,7 @@ def run(cls, session, request) -> SessionOperationResult:
tuple(request.scope_ids),
auxiliary_params=session.observation_export_params(
request.runtime_observation_export_path,
- request.runtime_observation_export_scope,
+ request.runtime_observation_export_scope.scope,
),
submit_timeout_ms=request.submit_timeout_ms,
wait_timeout_ms=request.wait_timeout_ms,
@@ -388,7 +396,9 @@ def button_label(cls, session) -> str:
def available(cls, session, request) -> AgentError | None:
state = session.execution_state
if not state.busy:
- return AgentError(code="no_execution_running", message="Nothing is running.")
+ return AgentError(
+ code="no_execution_running", message="Nothing is running."
+ )
if not state.run_button_enabled(True):
return AgentError(
code="execution_stopping",
@@ -461,4 +471,3 @@ class ShowDatasetImages(InitializedTargetsOperation, RendererOperation):
tooltip = "Browse dataset images and metadata"
description = "Opens the image and metadata browser of the selected datasets."
side_effects = ("opens_dataset_viewer_window",)
-
diff --git a/openhcs/authoring/session/session.py b/openhcs/authoring/session/session.py
index 90db36c345..01d2ed259f 100644
--- a/openhcs/authoring/session/session.py
+++ b/openhcs/authoring/session/session.py
@@ -16,6 +16,7 @@
import os
import threading
import time
+import uuid
from abc import ABC, abstractmethod
from dataclasses import dataclass
from collections.abc import Callable, Iterable
@@ -204,6 +205,25 @@ def call(self, callback: Callable[[], ResultT]) -> ResultT:
return self._dispatcher.call(callback)
+def _creates_files_in(directory: Path) -> bool:
+ """Whether the filesystem lets this process create a file in ``directory``.
+
+ Permission bits (``os.access``) do not see Windows ACLs or read-only
+ shares; an actual creation does. One attempt answers: ``tempfile`` on
+ Windows reads a refused creation as a name collision whenever ``os.access``
+ passes and retries up to ``os.TMP_MAX`` (2**31 - 1) times. The probe file is
+ removed at once, so it leaves nothing behind.
+ """
+
+ probe = Path(directory) / f".openhcs-write-probe-{uuid.uuid4().hex}"
+ try:
+ os.close(os.open(probe, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600))
+ except OSError:
+ return False
+ probe.unlink()
+ return True
+
+
class DatasetAccess(ABC):
"""Which dataset roots this session may read and initialize."""
@@ -218,7 +238,7 @@ def initializes_in_place(self, root: Path) -> bool:
"""
return all(
- os.access(directory, os.W_OK)
+ _creates_files_in(directory)
for directory in {
Path(root),
*(path.parent for path in METADATA_CONFIG.managed_paths(root)),
@@ -292,7 +312,204 @@ class FinishedExecution:
endpoint: "PongResponse | None"
-class Session:
+class SessionDebugMixin:
+ """Run, inspect and close one dataset's debug session."""
+
+ async def run_debug(
+ self,
+ scope_id: str,
+ *,
+ command_type: type[DebugCommandType] = RunDebugCommand,
+ snapshot_store_backend: str | None = None,
+ selected_source_group: str | None = None,
+ pause_step_indices: tuple[int, ...] = (),
+ start_step_index: int = 0,
+ start_after_invocation_key: str | None = None,
+ ) -> None:
+ """Start a debug run, or send a command to the dataset's debug worker."""
+
+ active = self.debug_sessions.get(scope_id)
+ if active is not None:
+ self.debug_sessions[scope_id] = active.with_command(command_type)
+ await self.debug_runs.send_worker_command(
+ debug_session_id=active.debug_session_id,
+ command_type=command_type,
+ )
+ if command_type is StopDebugCommand:
+ self.debug_sessions.pop(scope_id, None)
+ return
+
+ self.require_execution_allowed((scope_id,))
+ debug_session = DebugSession.create(
+ subject_id=scope_id, command_type=command_type
+ )
+ self.debug_sessions[scope_id] = debug_session
+ root = DatasetScope.parse(scope_id).root
+ snapshot_root = (root if root.is_dir() else root.parent) / ".openhcs_debug"
+ self._begin_batch((scope_id,))
+ try:
+ await self.connect_client()
+ self.progress.reset_for_new_batch()
+ self.publish(StatusReported(f"Compiling debug run for {scope_id}..."))
+ debug_request = DebugRunRequest(
+ debug_session_id=debug_session.debug_session_id,
+ snapshot_store_ref=str(snapshot_root),
+ snapshot_store_backend=snapshot_store_backend,
+ command_type=command_type,
+ selected_source_group=selected_source_group,
+ pause_step_indices=tuple(pause_step_indices),
+ start_step_index=start_step_index,
+ start_after_invocation_key=start_after_invocation_key,
+ replay_mode=PersistentPausedWorkerReplay,
+ )
+ request = dataset_pipeline_request(scope_id, self.global_config)
+ artifact_id = await self.debug_runs.compile_artifact_id(
+ request, debug_request
+ )
+ self.publish(StatusReported(f"Submitting debug run for {scope_id}..."))
+ await self.debug_runs.submit(
+ request,
+ compile_artifact_id=artifact_id,
+ debug_request=debug_request,
+ )
+ except Exception as error:
+ logger.error("Failed to execute debug run: %s", error, exc_info=True)
+ self.publish(ErrorReported(f"Failed to execute debug run: {error}"))
+ await self.execution_control.handle_execution_failure()
+
+ def record_debug_snapshot(
+ self,
+ notification: DebugSnapshotAvailableNotification,
+ ) -> None:
+ scope_id = notification.progress_event.subject_id
+ if notification.snapshot is not None:
+ retained = tuple(
+ snapshot
+ for snapshot in self.debug_snapshots.get(scope_id, ())
+ if snapshot.snapshot_id != notification.snapshot.snapshot_id
+ )
+ self.debug_snapshots[scope_id] = (*retained, notification.snapshot)
+ active = self.debug_sessions.get(scope_id)
+ if active is not None:
+ context = notification.debug_context
+ self.debug_sessions[scope_id] = active.with_snapshot_store(
+ snapshot_store_ref=context.snapshot_store_ref,
+ snapshot_store_backend=context.snapshot_store_backend,
+ axis_id=notification.progress_event.axis_id,
+ ).with_cursor(context.cursor)
+ self.publish(DebugSnapshotAvailable(notification))
+
+ def displayed_debug_session(self, scope_id: str) -> DebugSession | None:
+ """The running debug session, else the one loaded from a snapshot."""
+
+ active = self.debug_sessions.get(scope_id)
+ if active is not None:
+ return active
+ if scope_id in self.debug_terminal_summaries:
+ return None
+ return self.inspected_debug_sessions.get(scope_id)
+
+ def inspect_debug_snapshot(
+ self,
+ notification: DebugSnapshotAvailableNotification,
+ snapshot: DebugSnapshot | None,
+ ) -> None:
+ """Move the displayed debug cursor to a snapshot a client opened."""
+
+ event = notification.progress_event
+ context = notification.debug_context
+ scope_id = event.subject_id
+ active = self.debug_sessions.get(scope_id)
+ summary = self.debug_terminal_summaries.get(scope_id)
+ if active is not None:
+ self.debug_sessions[scope_id] = active.with_snapshot_store(
+ snapshot_store_ref=context.snapshot_store_ref,
+ snapshot_store_backend=context.snapshot_store_backend,
+ axis_id=event.axis_id,
+ ).with_cursor(context.cursor)
+ elif (
+ summary is not None and summary.debug_session_id == context.debug_session_id
+ ):
+ self.inspected_debug_sessions.pop(scope_id, None)
+ if snapshot is not None:
+ self.debug_terminal_summaries[scope_id] = summary.with_snapshot(
+ snapshot=snapshot,
+ snapshot_id=context.snapshot_id,
+ snapshot_store_ref=context.snapshot_store_ref,
+ snapshot_store_backend=context.snapshot_store_backend,
+ )
+ else:
+ self.inspected_debug_sessions[scope_id] = DebugSession(
+ debug_session_id=context.debug_session_id,
+ subject_id=scope_id,
+ axis_id=event.axis_id,
+ snapshot_store_ref=context.snapshot_store_ref,
+ snapshot_store_backend=context.snapshot_store_backend,
+ ).with_cursor(context.cursor)
+ self.refresh()
+
+ def _close_debug_session(self, scope_id: str) -> None:
+ active = self.debug_sessions.pop(scope_id, None)
+ if active is None or active.subject_id != scope_id:
+ return
+ terminal_status = self.batch.terminal_status(scope_id)
+ if terminal_status is None:
+ self.debug_terminal_summaries.pop(scope_id, None)
+ else:
+ summary = DebugTerminalSummary.from_session(
+ active,
+ terminal_status=terminal_status.name,
+ completed_at_unix=time.time(),
+ )
+ snapshots = self.debug_snapshots.get(scope_id, ())
+ latest = snapshots[-1] if snapshots else None
+ self.debug_terminal_summaries[scope_id] = (
+ summary
+ if latest is None
+ else summary.with_snapshot(
+ snapshot=latest,
+ snapshot_id=latest.snapshot_id,
+ snapshot_store_ref=active.snapshot_store_ref,
+ snapshot_store_backend=active.snapshot_store_backend,
+ )
+ )
+ self.publish(ExecutionStateChanged(self.execution_state))
+
+ def debug_context(self, scope_id: str) -> DebugSessionProjectionContext:
+ return DebugSessionProjectionContext(
+ target=DebugSessionTargetState(
+ current_plate_scope_id=scope_id,
+ pipeline_scope_id=PipelineScopeIdentity.from_plate_scope(
+ scope_id
+ ).scope_id,
+ initialized=self.is_initialized(scope_id),
+ compiled=scope_id in self.compiled,
+ terminal_status=(
+ None
+ if (status := self.batch.terminal_status(scope_id)) is None
+ else status.name
+ ),
+ ),
+ session=self.displayed_debug_session(scope_id),
+ terminal_summary=self.debug_terminal_summaries.get(scope_id),
+ pause_boundaries=DebugPauseBoundaryState(
+ pause_step_indices=tuple(
+ index
+ for index, step in enumerate(self.pipeline_steps(scope_id))
+ if step.debug_pause
+ )
+ ),
+ snapshots=self.debug_snapshots.get(scope_id, ()),
+ manager_execution_state=self.execution_state,
+ )
+
+ def current_debug_context(self) -> DebugSessionProjectionContext | None:
+ if not self.current_scope_id:
+ return None
+ return self.debug_context(self.current_scope_id)
+
+
+class Session(SessionDebugMixin):
"""Datasets, their pipelines and executions, and the events they emit."""
def __init__(
@@ -318,7 +535,9 @@ def __init__(
self.global_config = global_config
self.client = ZMQClientService(
config=transport_config,
- status_callback=lambda status: self.publish(ServerConnectionChanged(status)),
+ status_callback=lambda status: self.publish(
+ ServerConnectionChanged(status)
+ ),
compatibility_callback=lambda compatibility: self.publish(
ServerCompatibilityObserved(compatibility)
),
@@ -353,7 +572,9 @@ def __init__(
self.submission = ExecutionSubmission(self)
self.execution_control = ExecutionControl(self)
self.debug_runs = DebugRuns(self)
- self.progress = ExecutionProgress(self, interval_seconds=progress_interval_seconds)
+ self.progress = ExecutionProgress(
+ self, interval_seconds=progress_interval_seconds
+ )
self._work = AsyncOperationExecutor()
self._closed = False
@@ -365,7 +586,9 @@ def __init__(
def publish(self, event: SessionEvent) -> SessionEventRecord:
return self.event_log.publish(event)
- def subscribe(self, listener: Callable[[SessionEventRecord], None]) -> Callable[[], None]:
+ def subscribe(
+ self, listener: Callable[[SessionEventRecord], None]
+ ) -> Callable[[], None]:
return self.event_log.subscribe(listener)
def events_after(
@@ -515,7 +738,9 @@ def add_dataset_roots(
on that separately prepared root.
"""
- roots = tuple(self.dataset_access.require_readable(Path(root)) for root in roots)
+ roots = tuple(
+ self.dataset_access.require_readable(Path(root)) for root in roots
+ )
if execution_root is not None:
if len(roots) != 1:
raise ValueError("A prepared execution root needs exactly one root.")
@@ -530,7 +755,9 @@ def add_dataset_roots(
)
else:
offers = tuple(
- offer for root in roots for offer in DatasetScopeKind.offers_for_root(root)
+ offer
+ for root in roots
+ for offer in DatasetScopeKind.offers_for_root(root)
)
added = self._add_scopes(
tuple(offer.scope for offer in offers),
@@ -644,7 +871,9 @@ def _split_dataset_scopes(self) -> None:
def delete_datasets(self, scope_ids: Iterable[str]) -> None:
doomed = {str(scope_id) for scope_id in scope_ids}
- active = sorted(scope_id for scope_id in doomed if self.has_active_work(scope_id))
+ active = sorted(
+ scope_id for scope_id in doomed if self.has_active_work(scope_id)
+ )
if active:
raise RuntimeError(
"Cannot delete a dataset with active initialization, compilation, "
@@ -712,7 +941,9 @@ def set_pipeline_document(self, scope_id: str, document: PipelineDocument) -> No
PipelineObjectStateBinding.commit_plate_state(scope_id)
self.pipeline_changed(scope_id)
self.publish(
- StatusReported(f"Pipeline updated with {len(document.pipeline_steps)} steps")
+ StatusReported(
+ f"Pipeline updated with {len(document.pipeline_steps)} steps"
+ )
)
def set_pipeline_source(self, scope_id: str, source: str) -> None:
@@ -756,7 +987,9 @@ def replace_step(
edited_step: FunctionStep,
) -> None:
self.require_definition_mutation_allowed(scope_id)
- PipelineObjectStateBinding.replace_plate_step(scope_id, current_step, edited_step)
+ PipelineObjectStateBinding.replace_plate_step(
+ scope_id, current_step, edited_step
+ )
self.pipeline_changed(scope_id)
self.publish(StatusReported(f"Updated step: {edited_step.name}"))
@@ -788,7 +1021,9 @@ def delete_steps(self, scope_id: str, step_scope_ids: Iterable[str]) -> None:
doomed = set(step_scope_ids)
self.require_definition_mutation_allowed(scope_id)
steps = self.pipeline_steps(scope_id)
- kept = [step for step in steps if self.step_scope_id(scope_id, step) not in doomed]
+ kept = [
+ step for step in steps if self.step_scope_id(scope_id, step) not in doomed
+ ]
names = [step.name for step in steps if step not in kept]
with ObjectStateRegistry.atomic(f"delete steps {', '.join(names)}"):
for step_scope_id in doomed:
@@ -891,7 +1126,9 @@ def require_definition_mutation_allowed(self, scope_id: str | None = None) -> No
"active initialization or compilation. Other datasets remain editable."
)
- def require_definition_mutation_allowed_for_object_scope(self, scope_id: str) -> None:
+ def require_definition_mutation_allowed_for_object_scope(
+ self, scope_id: str
+ ) -> None:
"""Authorize an ObjectState mutation that may belong to a dataset."""
if scope_id == "":
@@ -1032,10 +1269,13 @@ async def initialize(scope_id: str) -> None:
self.publish(DatasetsChanged())
def run() -> InputWorkspacePreparationResult | None:
- ensure_global_config_context(GlobalPipelineConfig, self.global_config)
+ ensure_global_config_context(
+ GlobalPipelineConfig, self.global_config
+ )
workspace = scope.prepare_input_workspace()
- if workspace is None and not self.dataset_access.initializes_in_place(
- scope.root
+ if (
+ workspace is None
+ and not self.dataset_access.initializes_in_place(scope.root)
):
workspace = self.mirrored_workspace(scope.root)
if workspace is not None:
@@ -1063,9 +1303,7 @@ def run() -> InputWorkspacePreparationResult | None:
)
self.set_dataset_state(scope_id, OrchestratorReady)
if scope_id == self.current_scope_id or not self.current_scope_id:
- self.main_thread.call(
- lambda: self._reselect(scope_id)
- )
+ self.main_thread.call(lambda: self._reselect(scope_id))
done += 1
self.publish(ProgressAdvanced(done))
@@ -1130,7 +1368,9 @@ def _arm_run_deadline(
timer = threading.Timer(
wait_timeout_ms / 1000,
lambda: self.main_thread.post(
- lambda: self._run_deadline_passed(batch_number, scope_ids, wait_timeout_ms)
+ lambda: self._run_deadline_passed(
+ batch_number, scope_ids, wait_timeout_ms
+ )
),
)
timer.daemon = True
@@ -1214,7 +1454,9 @@ def observation_export_params(
return None
path = self.dataset_access.require_writable(Path(export_path))
if path.exists():
- raise FileExistsError(f"Runtime observation export path already exists: {path}")
+ raise FileExistsError(
+ f"Runtime observation export path already exists: {path}"
+ )
return ZMQAuxiliaryExecutionParams(
runtime_observation_export_path=path,
runtime_observation_export_scope=scope,
@@ -1248,7 +1490,9 @@ def finish_dataset_execution(
) -> None:
"""Record one dataset's terminal status (from any thread)."""
- self.main_thread.post(lambda: self._finish_dataset_execution(completion, scope_id))
+ self.main_thread.post(
+ lambda: self._finish_dataset_execution(completion, scope_id)
+ )
def _finish_dataset_execution(
self,
@@ -1280,7 +1524,9 @@ def _finish_dataset_execution(
if status.emit_failure:
detail = completion.traceback_text or completion.message
separator = ":\n\n" if completion.traceback_text else ": "
- self.publish(ErrorReported(f"Execution failed for {scope_id}{separator}{detail}"))
+ self.publish(
+ ErrorReported(f"Execution failed for {scope_id}{separator}{detail}")
+ )
def _add_output_dataset(
self,
@@ -1316,201 +1562,20 @@ def _add_output_dataset(
def finish_batch(self, completed_count: int, failed_count: int) -> None:
self.execution_state = ManagerExecutionState.IDLE
summary = f"All done: {completed_count} completed, {failed_count} failed"
- if completed_count > 1 and self.global_config.analysis_consolidation_config.enabled:
+ if (
+ completed_count > 1
+ and self.global_config.analysis_consolidation_config.enabled
+ ):
try:
consolidate_batch_results(self)
summary += ". Global summary created."
except Exception as error:
- logger.error("Failed to create global summary: %s", error, exc_info=True)
+ logger.error(
+ "Failed to create global summary: %s", error, exc_info=True
+ )
summary += ". Global summary failed."
self.publish(StatusReported(summary))
self.publish(BatchFinished(completed_count, failed_count))
self.refresh()
# -- debug ----------------------------------------------------------------
-
- async def run_debug(
- self,
- scope_id: str,
- *,
- command_type: type[DebugCommandType] = RunDebugCommand,
- snapshot_store_backend: str | None = None,
- selected_source_group: str | None = None,
- pause_step_indices: tuple[int, ...] = (),
- start_step_index: int = 0,
- start_after_invocation_key: str | None = None,
- ) -> None:
- """Start a debug run, or send a command to the dataset's debug worker."""
-
- active = self.debug_sessions.get(scope_id)
- if active is not None:
- self.debug_sessions[scope_id] = active.with_command(command_type)
- await self.debug_runs.send_worker_command(
- debug_session_id=active.debug_session_id,
- command_type=command_type,
- )
- if command_type is StopDebugCommand:
- self.debug_sessions.pop(scope_id, None)
- return
-
- self.require_execution_allowed((scope_id,))
- debug_session = DebugSession.create(subject_id=scope_id, command_type=command_type)
- self.debug_sessions[scope_id] = debug_session
- root = DatasetScope.parse(scope_id).root
- snapshot_root = (root if root.is_dir() else root.parent) / ".openhcs_debug"
- self._begin_batch((scope_id,))
- try:
- await self.connect_client()
- self.progress.reset_for_new_batch()
- self.publish(StatusReported(f"Compiling debug run for {scope_id}..."))
- debug_request = DebugRunRequest(
- debug_session_id=debug_session.debug_session_id,
- snapshot_store_ref=str(snapshot_root),
- snapshot_store_backend=snapshot_store_backend,
- command_type=command_type,
- selected_source_group=selected_source_group,
- pause_step_indices=tuple(pause_step_indices),
- start_step_index=start_step_index,
- start_after_invocation_key=start_after_invocation_key,
- replay_mode=PersistentPausedWorkerReplay,
- )
- request = dataset_pipeline_request(scope_id, self.global_config)
- artifact_id = await self.debug_runs.compile_artifact_id(request, debug_request)
- self.publish(StatusReported(f"Submitting debug run for {scope_id}..."))
- await self.debug_runs.submit(
- request,
- compile_artifact_id=artifact_id,
- debug_request=debug_request,
- )
- except Exception as error:
- logger.error("Failed to execute debug run: %s", error, exc_info=True)
- self.publish(ErrorReported(f"Failed to execute debug run: {error}"))
- await self.execution_control.handle_execution_failure()
-
- def record_debug_snapshot(
- self,
- notification: DebugSnapshotAvailableNotification,
- ) -> None:
- scope_id = notification.progress_event.subject_id
- if notification.snapshot is not None:
- retained = tuple(
- snapshot
- for snapshot in self.debug_snapshots.get(scope_id, ())
- if snapshot.snapshot_id != notification.snapshot.snapshot_id
- )
- self.debug_snapshots[scope_id] = (*retained, notification.snapshot)
- active = self.debug_sessions.get(scope_id)
- if active is not None:
- context = notification.debug_context
- self.debug_sessions[scope_id] = active.with_snapshot_store(
- snapshot_store_ref=context.snapshot_store_ref,
- snapshot_store_backend=context.snapshot_store_backend,
- axis_id=notification.progress_event.axis_id,
- ).with_cursor(context.cursor)
- self.publish(DebugSnapshotAvailable(notification))
-
- def displayed_debug_session(self, scope_id: str) -> DebugSession | None:
- """The running debug session, else the one loaded from a snapshot."""
-
- active = self.debug_sessions.get(scope_id)
- if active is not None:
- return active
- if scope_id in self.debug_terminal_summaries:
- return None
- return self.inspected_debug_sessions.get(scope_id)
-
- def inspect_debug_snapshot(
- self,
- notification: DebugSnapshotAvailableNotification,
- snapshot: DebugSnapshot | None,
- ) -> None:
- """Move the displayed debug cursor to a snapshot a client opened."""
-
- event = notification.progress_event
- context = notification.debug_context
- scope_id = event.subject_id
- active = self.debug_sessions.get(scope_id)
- summary = self.debug_terminal_summaries.get(scope_id)
- if active is not None:
- self.debug_sessions[scope_id] = active.with_snapshot_store(
- snapshot_store_ref=context.snapshot_store_ref,
- snapshot_store_backend=context.snapshot_store_backend,
- axis_id=event.axis_id,
- ).with_cursor(context.cursor)
- elif summary is not None and summary.debug_session_id == context.debug_session_id:
- self.inspected_debug_sessions.pop(scope_id, None)
- if snapshot is not None:
- self.debug_terminal_summaries[scope_id] = summary.with_snapshot(
- snapshot=snapshot,
- snapshot_id=context.snapshot_id,
- snapshot_store_ref=context.snapshot_store_ref,
- snapshot_store_backend=context.snapshot_store_backend,
- )
- else:
- self.inspected_debug_sessions[scope_id] = DebugSession(
- debug_session_id=context.debug_session_id,
- subject_id=scope_id,
- axis_id=event.axis_id,
- snapshot_store_ref=context.snapshot_store_ref,
- snapshot_store_backend=context.snapshot_store_backend,
- ).with_cursor(context.cursor)
- self.refresh()
-
- def _close_debug_session(self, scope_id: str) -> None:
- active = self.debug_sessions.pop(scope_id, None)
- if active is None or active.subject_id != scope_id:
- return
- terminal_status = self.batch.terminal_status(scope_id)
- if terminal_status is None:
- self.debug_terminal_summaries.pop(scope_id, None)
- else:
- summary = DebugTerminalSummary.from_session(
- active,
- terminal_status=terminal_status.name,
- completed_at_unix=time.time(),
- )
- snapshots = self.debug_snapshots.get(scope_id, ())
- latest = snapshots[-1] if snapshots else None
- self.debug_terminal_summaries[scope_id] = (
- summary
- if latest is None
- else summary.with_snapshot(
- snapshot=latest,
- snapshot_id=latest.snapshot_id,
- snapshot_store_ref=active.snapshot_store_ref,
- snapshot_store_backend=active.snapshot_store_backend,
- )
- )
- self.publish(ExecutionStateChanged(self.execution_state))
-
- def debug_context(self, scope_id: str) -> DebugSessionProjectionContext:
- return DebugSessionProjectionContext(
- target=DebugSessionTargetState(
- current_plate_scope_id=scope_id,
- pipeline_scope_id=PipelineScopeIdentity.from_plate_scope(scope_id).scope_id,
- initialized=self.is_initialized(scope_id),
- compiled=scope_id in self.compiled,
- terminal_status=(
- None
- if (status := self.batch.terminal_status(scope_id)) is None
- else status.name
- ),
- ),
- session=self.displayed_debug_session(scope_id),
- terminal_summary=self.debug_terminal_summaries.get(scope_id),
- pause_boundaries=DebugPauseBoundaryState(
- pause_step_indices=tuple(
- index
- for index, step in enumerate(self.pipeline_steps(scope_id))
- if step.debug_pause
- )
- ),
- snapshots=self.debug_snapshots.get(scope_id, ()),
- manager_execution_state=self.execution_state,
- )
-
- def current_debug_context(self) -> DebugSessionProjectionContext | None:
- if not self.current_scope_id:
- return None
- return self.debug_context(self.current_scope_id)
-
diff --git a/openhcs/authoring/session/views.py b/openhcs/authoring/session/views.py
index a736b56a43..abcbf101e7 100644
--- a/openhcs/authoring/session/views.py
+++ b/openhcs/authoring/session/views.py
@@ -174,7 +174,11 @@ def orchestrator_state(self):
if terminal is not None:
return terminal.orchestrator_state
orchestrator = ObjectStateRegistry.get_object(self.scope_id)
- return orchestrator.state if isinstance(orchestrator, PipelineOrchestrator) else None
+ return (
+ orchestrator.state
+ if isinstance(orchestrator, PipelineOrchestrator)
+ else None
+ )
@property
def execution_id(self) -> str | None:
@@ -253,7 +257,9 @@ def state_of(cls, session: "Session") -> DatasetListState:
selected = set(session.selected_scope_ids)
return DatasetListState(
rows=tuple(
- cls.row_state(session, row, relations[row.scope_id], row.scope_id in selected)
+ cls.row_state(
+ session, row, relations[row.scope_id], row.scope_id in selected
+ )
for row in rows
),
selected_scope_ids=session.selected_scope_ids,
@@ -279,6 +285,16 @@ def row_state(
terminal = activity.terminal_status
runtime = activity.runtime
debug_phase = activity.debug_phase
+ finished_id, finished = next(
+ (
+ (execution_id, finished)
+ for execution_id, finished in reversed(
+ session.finished_executions.items()
+ )
+ if finished.scope_id == row.scope_id
+ ),
+ (None, None),
+ )
return DatasetRowState(
scope_id=row.scope_id,
name=row.name,
@@ -305,15 +321,9 @@ def row_state(
source_root=relation.source_root,
debug_phase=None if debug_phase is None else debug_phase.name,
debug_session_id=activity.debug_session_id,
- finished_execution_id=next(
- (
- execution_id
- for execution_id, finished in reversed(
- session.finished_executions.items()
- )
- if finished.scope_id == row.scope_id
- ),
- None,
+ finished_execution_id=finished_id,
+ finished_execution_error=(
+ None if finished is None else finished.record.error
),
)
@@ -326,7 +336,9 @@ def row_state(
def _function_entries(function_spec) -> tuple:
if function_spec is None:
return ()
- entries = tuple(function_spec) if isinstance(function_spec, list) else (function_spec,)
+ entries = (
+ tuple(function_spec) if isinstance(function_spec, list) else (function_spec,)
+ )
functions = []
for entry in entries:
function = entry[0] if isinstance(entry, tuple) else entry
@@ -391,7 +403,9 @@ def state_of(
selected=step_scope_id in selected,
dirty=bool(state.dirty_fields) if state is not None else False,
default_diff=(
- bool(state.signature_diff_fields) if state is not None else False
+ bool(state.signature_diff_fields)
+ if state is not None
+ else False
),
description=step.description,
debug_pause=step.debug_pause,
diff --git a/openhcs/core/pipeline/function_contracts.py b/openhcs/core/pipeline/function_contracts.py
index 22edfc875d..826032e3be 100644
--- a/openhcs/core/pipeline/function_contracts.py
+++ b/openhcs/core/pipeline/function_contracts.py
@@ -76,9 +76,16 @@ def resolved_callable_type_hints(func: Callable) -> dict[str, Any]:
from typing import _strip_annotations
return {
- **{name: _strip_annotations(parameter.annotation) for name, parameter in signature.parameters.items()
- if parameter.annotation is not inspect.Parameter.empty},
- **({} if signature.return_annotation is inspect.Signature.empty else {"return": _strip_annotations(signature.return_annotation)}),
+ **{
+ name: _strip_annotations(parameter.annotation)
+ for name, parameter in signature.parameters.items()
+ if parameter.annotation is not inspect.Parameter.empty
+ },
+ **(
+ {}
+ if signature.return_annotation is inspect.Signature.empty
+ else {"return": _strip_annotations(signature.return_annotation)}
+ ),
}
@@ -171,16 +178,28 @@ def invocation_kwargs(
runtime_projection: RuntimePlaneAxisValueProjection | None,
semantic_controls: Mapping[str, Any],
) -> dict[str, Any]:
- """Return the invocation kwargs once the image's execution mode is known."""
- del execution_mode, image_projection, runtime_projection
+ """Match a scalar image to its declared singleton runtime root.
+
+ Labels can consume a singleton root only after the image's final
+ execution mode and retained plane projection have been resolved.
+ """
+ if (
+ execution_mode.per_plane
+ and image_projection is None
+ and runtime_projection is not None
+ and runtime_projection.axis_size == 1
+ ):
+ from openhcs.core.runtime_slice_projection import RuntimeSliceProjection
+
+ kwargs = RuntimeSliceProjection.kwargs_for_slice(
+ kwargs, runtime_projection.selected_plane(0)
+ )
return {**kwargs, **semantic_controls}
@classmethod
def semantic_label_payload(cls, source_projected_payload, completion_payload):
"""Return the label payload that represents this measurement domain."""
- raise TypeError(
- f"{cls.__name__} declares no object-measurement label domain."
- )
+ raise TypeError(f"{cls.__name__} declares no object-measurement label domain.")
@classmethod
def image_execution_mode(
@@ -243,16 +262,6 @@ def image_execution_mode(cls, labels, default, *, runtime_slice_count=None):
del labels, default, runtime_slice_count
return FullStackExecution
-
-class MatchImageStackLabels(ObjectLabelInputExecutionMode):
- """Labels follow the image: a stack when the image needs one."""
-
- key = "match_image_stack"
-
- @classmethod
- def preserves_full_stack(cls, *, image_stack_required: bool) -> bool:
- return image_stack_required
-
@classmethod
def invocation_kwargs(
cls,
@@ -263,23 +272,19 @@ def invocation_kwargs(
runtime_projection,
semantic_controls,
):
- """Match scalar images to their declared singleton runtime root.
+ """Full-stack label consumers keep their declared label domain."""
+ del execution_mode, image_projection, runtime_projection
+ return {**kwargs, **semantic_controls}
- Matching labels can consume a singleton root only after the image's
- final execution mode and retained plane projection have been resolved.
- """
- if (
- execution_mode.per_plane
- and image_projection is None
- and runtime_projection is not None
- and runtime_projection.axis_size == 1
- ):
- from openhcs.core.runtime_slice_projection import RuntimeSliceProjection
- kwargs = RuntimeSliceProjection.kwargs_for_slice(
- kwargs, runtime_projection.selected_plane(0)
- )
- return {**kwargs, **semantic_controls}
+class MatchImageStackLabels(ObjectLabelInputExecutionMode):
+ """Labels follow the image: a stack when the image needs one."""
+
+ key = "match_image_stack"
+
+ @classmethod
+ def preserves_full_stack(cls, *, image_stack_required: bool) -> bool:
+ return image_stack_required
def _artifact_spec_from_output_declaration(
@@ -395,7 +400,9 @@ def special_inputs(*parameter_names: str) -> Callable[[F], F]:
)
def decorator(func: F) -> F:
- vars(func)[FunctionContractAttribute.artifact_input_parameter_names] = normalized
+ vars(func)[
+ FunctionContractAttribute.artifact_input_parameter_names
+ ] = normalized
add_parameter_exclusions(func, normalized)
return func
diff --git a/openhcs/core/processing_preparation.py b/openhcs/core/processing_preparation.py
index 74c9c4d995..4ff7b08efb 100644
--- a/openhcs/core/processing_preparation.py
+++ b/openhcs/core/processing_preparation.py
@@ -312,6 +312,18 @@ def close(self) -> None:
self.result_connection.close()
+def _schedulable_cpu_count() -> int:
+ """CPUs this process may run on.
+
+ Linux exposes the scheduler's affinity set. macOS has no affinity API:
+ every online CPU is schedulable there.
+ """
+ try:
+ return len(os.sched_getaffinity(0))
+ except AttributeError:
+ return os.cpu_count() or 1
+
+
@dataclass(frozen=True, slots=True)
class PreparationCacheBatch:
"""Schedule derived cache operations without interpreting backend families."""
@@ -340,14 +352,7 @@ def populate_child_caches(
raise ValueError("Preparation worker budget must be positive.")
if "fork" not in multiprocessing.get_all_start_methods():
return
- try:
- worker_capacity = min(max_workers, len(os.sched_getaffinity(0)))
- except AttributeError:
- # Without an affinity witness there is no admitted parallel cache
- # work. The existing parent preparation still owns readiness.
- return
- if worker_capacity < 1:
- return
+ worker_capacity = min(max_workers, _schedulable_cpu_count())
operations = {
operation.identity: operation
for preparation in self.preparations
diff --git a/openhcs/core/source_bindings.py b/openhcs/core/source_bindings.py
index f0331be372..72c20f27da 100644
--- a/openhcs/core/source_bindings.py
+++ b/openhcs/core/source_bindings.py
@@ -905,7 +905,9 @@ class SourceSelector:
This filters candidates; it does not assign the output identity.
"""
- metadata: Annotated[tuple[MetadataSelector, ...], FieldLabel("Select Metadata")] = ()
+ metadata: Annotated[
+ tuple[MetadataSelector, ...], FieldLabel("Select Metadata")
+ ] = ()
"""Exact metadata field/value pairs required on selected sources, e.g. Well=A01.
This filters candidates; pairing sources into sets uses the match plan.
@@ -968,12 +970,15 @@ def metadata_candidates_match(
any(
source_metadata_values_equal(value, selector.value)
for metadata in candidates
- for value in source_component_metadata_values(metadata, selector.component)
+ for value in source_component_metadata_values(
+ metadata, selector.component
+ )
)
for selector in self.components
) and all(
any(
- value is not None and source_metadata_values_equal(value, selector.value)
+ value is not None
+ and source_metadata_values_equal(value, selector.value)
for metadata in candidates
for value in (semantic_source_metadata_value(metadata, selector.field),)
)
@@ -984,12 +989,12 @@ def matches_provenance(self, provenance: SourceImageProvenance) -> bool:
"""Apply the same selectors to identities carried by current pixels."""
identities = provenance.represented_source_identities
return (
- (not self.filters or self.path_filters_match(
- tuple(identity.path for identity in identities if identity.path is not None)
- ))
- and self.metadata_candidates_match(
- tuple(identity.component_metadata or {} for identity in identities)
+ not self.filters
+ or self.path_filters_match(
+ tuple(path for identity in identities for path in identity.filter_paths)
)
+ ) and self.metadata_candidates_match(
+ tuple(identity.component_metadata or {} for identity in identities)
)
@@ -1259,9 +1264,7 @@ def with_resolved_explicit_source(self, source_root: Path) -> Self:
if self.explicit_source is None:
return self
- return replace(
- self, explicit_source=self.explicit_source.resolved(source_root)
- )
+ return replace(self, explicit_source=self.explicit_source.resolved(source_root))
def source_channel_axis_for_shape(
self,
@@ -1397,34 +1400,39 @@ def project_step_input_payload(self, payload: RuntimeArrayData) -> RuntimeArrayD
metadata = payload.metadata
provenance = metadata.source_provenance
if not provenance.has_values:
- raise ValueError(f"Step-input binding {self.alias!r} requires source provenance.")
+ raise ValueError(
+ f"Step-input binding {self.alias!r} requires source provenance."
+ )
if not provenance.source_plane_count:
if not self.selector.matches_provenance(provenance):
- raise ValueError(f"Step-input binding {self.alias!r} selects no current planes.")
+ raise ValueError(
+ f"Step-input binding {self.alias!r} selects no current planes."
+ )
selected = payload
else:
named_planes = provenance.source_plane_selection(self.alias)
candidate_planes = (
range(provenance.source_plane_count)
- if named_planes is None else named_planes
+ if named_planes is None
+ else named_planes
)
selection = tuple(
- index for index in candidate_planes
+ index
+ for index in candidate_planes
if self.selector.matches_provenance(provenance.for_source_plane(index))
)
if not selection:
- raise ValueError(f"Step-input binding {self.alias!r} selects no current planes.")
+ raise ValueError(
+ f"Step-input binding {self.alias!r} selects no current planes."
+ )
selected = metadata.project_source_planes(payload, selection)
return self.apply_loaded_payload(selected, source_context=None)
@property
def requires_current_pixels(self) -> bool:
"""Whether this source projects the actual current main-flow pixels."""
- return (
- self.origin is FromStepInput
- and self.projection_role is PrimaryPlaneRole
- )
+ return self.origin is FromStepInput and self.projection_role is PrimaryPlaneRole
@staticmethod
def _monochrome_source_data(
@@ -1435,11 +1443,10 @@ def _monochrome_source_data(
"""Convert declared RGB channels using the source intensity domain."""
import numpy as np
-
- normalized = metadata.payload_with(data).normalize_intensity_payload(dtype=np.float32,)
- rgb = np.moveaxis(
- np.asarray(normalized.data), channel_axis, -1
- )[..., :3]
+ normalized = metadata.payload_with(data).normalize_intensity_payload(
+ dtype=np.float32,
+ )
+ rgb = np.moveaxis(np.asarray(normalized.data), channel_axis, -1)[..., :3]
if np.all(rgb == rgb[..., :1]):
return np.ascontiguousarray(rgb[..., 0])
@@ -1471,9 +1478,7 @@ def component_values(
values.extend(source_component_metadata_values(metadata, component))
return tuple(dict.fromkeys(values))
- def _declared_component_values(
- self, component: type[Axis]
- ) -> tuple[str, ...]:
+ def _declared_component_values(self, component: type[Axis]) -> tuple[str, ...]:
"""Resolve exact identity values before selector-derived coordinates."""
for declarations in (self.component_identity, self.selector.components):
values = tuple(
@@ -1497,7 +1502,9 @@ def component_domains(
for values in (self._declared_component_values(component),)
if values
}
- if realized_source_metadata is None or len(domains) == len(AxisFamily.active().axes):
+ if realized_source_metadata is None or len(domains) == len(
+ AxisFamily.active().axes
+ ):
return domains
realized: dict[type[Axis], list[str]] = {}
for metadata in realized_source_metadata:
@@ -1679,8 +1686,7 @@ def requires_step_input_component_stack(
def requires_pipeline_start_resolution(self) -> bool:
"""Whether any declaration resolves from the pipeline-start universe."""
return any(
- binding.origin is FromPipelineStart
- for binding in self.binding_declarations
+ binding.origin is FromPipelineStart for binding in self.binding_declarations
)
@property
@@ -1700,8 +1706,7 @@ def requires_pipeline_start_source_set(self) -> bool:
def requires_step_input_selector_resolution(self) -> bool:
"""Whether step-input declarations need selector-aware matching."""
return any(
- binding.origin is FromStepInput
- and binding.requires_selector_resolution
+ binding.origin is FromStepInput and binding.requires_selector_resolution
for binding in self.binding_declarations
)
@@ -2065,7 +2070,6 @@ def has_primary_content(self) -> bool:
"""Whether the subclass-specific binding payload is empty."""
-
def source_binding_group_keys_for_group_by(
source_bindings: SourceBindingDeclarationsMixin,
group_by: type[GroupingDeclaration],
@@ -2098,7 +2102,6 @@ def source_binding_group_keys_for_group_by(
)
-
@dataclass(frozen=True, slots=True)
class CompiledSourceBindingPlan(SourceBindingDeclarationsMixin, _SourceBindingPlanBase):
"""Immutable compile-time source binding plan for one step."""
@@ -2207,8 +2210,7 @@ def requires_step_input_selector_resolution(self) -> bool:
"""Whether any step-input binding needs selector-aware source matching."""
return any(
- binding.origin is FromStepInput
- and binding.requires_selector_resolution
+ binding.origin is FromStepInput and binding.requires_selector_resolution
for binding in self.bindings
)
@@ -2283,7 +2285,6 @@ def _coerce_component(value: Any, field_name: str) -> type[Axis]:
raise TypeError(f"{field_name}: {error}") from error
-
def _require_name(value: str, field_name: str) -> None:
if not value:
raise ValueError(f"{field_name} cannot be empty.")
diff --git a/openhcs/core/source_image_provenance.py b/openhcs/core/source_image_provenance.py
index 7a4d6a499c..dc4bed5564 100644
--- a/openhcs/core/source_image_provenance.py
+++ b/openhcs/core/source_image_provenance.py
@@ -126,6 +126,18 @@ def from_mapping(cls, values: Mapping[str, object]) -> "SourceImageIdentity":
def addressable(self) -> bool:
return self.path is not None or self.component_metadata is not None
+ @property
+ def filter_paths(self) -> tuple[str, ...]:
+ """Store-owned paths that source filters match.
+
+ A virtual workspace path names the plane; the stored source paths it
+ was admitted from are what a FILE filter selects.
+ """
+ stored = SourceMetadataFields.source_filter_paths(self.component_metadata or {})
+ if stored:
+ return stored
+ return () if self.path is None else (self.path,)
+
@property
def filename_extension(self) -> str | None:
"""Project the declared source filename fact, never a format default."""
@@ -238,7 +250,11 @@ def from_mapping(
raise ValueError(f"Unknown provenance plane fields: {sorted(unknown)!r}.")
kind = values.get(
"identity_kind",
- RuntimeSourceImageProvenancePlane.identity_kind if identities is None else None,
+ (
+ RuntimeSourceImageProvenancePlane.identity_kind
+ if identities is None
+ else None
+ ),
)
if not isinstance(kind, str) or kind not in cls.__registry__:
raise ValueError(f"Unknown source-image provenance identity kind {kind!r}.")
@@ -253,7 +269,9 @@ def from_mapping(
raise TypeError("Source provenance contributors must be a sequence.")
if identities is None:
facts = {
- key: values[key] for key in ("path", "component_metadata") if key in values
+ key: values[key]
+ for key in ("path", "component_metadata")
+ if key in values
}
else:
index = values.get("identity")
@@ -536,11 +554,14 @@ def from_mapping(cls, values: object) -> "SourceImageProvenancePlanes":
else:
identities = None
planes = values
- if not isinstance(planes, Sequence) or isinstance(planes, (str, bytes, bytearray)):
+ if not isinstance(planes, Sequence) or isinstance(
+ planes, (str, bytes, bytearray)
+ ):
raise TypeError("Source provenance planes must be a sequence.")
return cls(
tuple(
- SourceImageProvenancePlane.from_mapping(item, identities) for item in planes
+ SourceImageProvenancePlane.from_mapping(item, identities)
+ for item in planes
)
)
@@ -621,6 +642,25 @@ def contributors(self) -> tuple[SourceImageProvenanceContributor, ...]:
)
)
+ @property
+ def contributor_groups(
+ self,
+ ) -> tuple[tuple[SourceImageProvenanceContributor, ...], ...]:
+ """Contributors grouped by the pixels they reduce into: top-level ones
+ into the whole payload, nested ones into their own runtime plane."""
+ return (
+ tuple(
+ plane
+ for plane in self.planes
+ if isinstance(plane, SourceImageProvenanceContributor)
+ ),
+ *(
+ plane.contributors
+ for plane in self.planes
+ if not isinstance(plane, SourceImageProvenanceContributor)
+ ),
+ )
+
@property
def represented_source_image_names(self) -> tuple[str, ...]:
"""Return every alias owned by a runtime plane or pixel contributor."""
@@ -1046,32 +1086,30 @@ def required_scalar_components(
"""Exclude only coordinates proven reduced into pixel contributors.
Contributors do not own a projectable runtime axis. A component that
- varies across their complete source addresses therefore has no scalar
- address on the derived image. Absent or incomplete contributor evidence
- cannot exempt an acquired plane from its required coordinates.
+ varies across the contributors of one pixel group therefore has no
+ scalar address on the derived image; one that varies only between
+ runtime planes is that plane axis. Absent or incomplete contributor
+ evidence cannot exempt an acquired plane from its required coordinates.
"""
- contributors = self.source_image_provenance_planes.contributors
scalar_components = SourceMetadataFields.component_domains(
self.scalar_source_identity.component_metadata or {}
)
- contributor_components = tuple(
- SourceMetadataFields.component_domains(contributor.component_metadata or {})
- for contributor in contributors
- )
reduced = set()
- for component in AxisFamily.active().axes:
- if component in scalar_components:
- continue
- if len(contributor_components) < 2 or not all(
- len(values.get(component, ())) == 1 for values in contributor_components
- ):
- continue
- values = frozenset(
- value for values in contributor_components for value in values[component]
+ for contributors in self.source_image_provenance_planes.contributor_groups:
+ domains = tuple(
+ SourceMetadataFields.component_domains(item.component_metadata or {})
+ for item in contributors
)
- if len(values) > 1:
- reduced.add(component.name)
- return tuple(component for component in component_order if component not in reduced)
+ for component in AxisFamily.active().axes:
+ if component in scalar_components or len(domains) < 2:
+ continue
+ if not all(len(values.get(component, ())) == 1 for values in domains):
+ continue
+ if len({values[component][0] for values in domains}) > 1:
+ reduced.add(component.name)
+ return tuple(
+ component for component in component_order if component not in reduced
+ )
def image_set_identities(
self,
@@ -1096,11 +1134,9 @@ def image_set_plane_identities(
) -> tuple[frozenset[SourceImageSetIdentity], ...]:
"""Return one image-set identity entry for every runtime source plane."""
return tuple(
- self.source_image_provenance_planes.plane(
- index
- ).source_identity.with_missing_from(
- self.source_identity
- ).image_set_identities(policy)
+ self.source_image_provenance_planes.plane(index)
+ .source_identity.with_missing_from(self.source_identity)
+ .image_set_identities(policy)
for index in range(self.source_plane_count)
)
@@ -1577,10 +1613,6 @@ def with_common_scalar_identity_from_planes(self) -> Self:
source_image_names=self.source_image_names,
)
- @property
- def source_component_identity(self) -> tuple[tuple[str, str], ...] | None:
- return self.source_identity.identity[1]
-
@dataclass(frozen=True, slots=True)
class SourcePlaneIndexedMetadata:
diff --git a/openhcs/core/steps/function_outputs.py b/openhcs/core/steps/function_outputs.py
index 5a0a891d2b..feb924e67d 100644
--- a/openhcs/core/steps/function_outputs.py
+++ b/openhcs/core/steps/function_outputs.py
@@ -17,8 +17,13 @@
RegistryFamily,
extract_key_from_class_name,
)
+from polystore.backend_registry import STORAGE_BACKENDS
+from polystore.base import VirtualBackend
from polystore.streaming.identity import StreamProducerIdentity
-from polystore.streaming.viewer_transport import ViewerStreamProducer, ViewerStreamSourceMetadata
+from polystore.streaming.viewer_transport import (
+ ViewerStreamProducer,
+ ViewerStreamSourceMetadata,
+)
from polystore.virtual_workspace import SourcePixelRef
from openhcs.constants.constants import Backend
@@ -513,9 +518,7 @@ def partition_by_item_fields(
def project_item(
request: RuntimeProjectionSourceIdentityRequest,
) -> tuple[RuntimeProjectedPayloadItem, ...]:
- return (
- RequiredSourceComponentMetadata
- ).project_payload_items(request)
+ return (RequiredSourceComponentMetadata).project_payload_items(request)
@staticmethod
def stream_path_for_projected_item(
@@ -664,6 +667,15 @@ class OpenHCSMetadataTarget(ABC, metaclass=AutoRegisterMeta):
def from_plan(cls, plan: CompiledStepPlan) -> OpenHCSMetadataTarget | None:
"""Resolve this declaration's independently compiled storage target."""
+ @staticmethod
+ def hosts_metadata_document(backend: str) -> bool:
+ """The metadata document lives in the output root's local directory.
+
+ A virtual backend (for example OMERO) declares that its paths name
+ remote objects, so its output root has no directory to hold it.
+ """
+ return not issubclass(STORAGE_BACKENDS[backend], VirtualBackend)
+
@classmethod
def for_plan(cls, plan: CompiledStepPlan) -> tuple[OpenHCSMetadataTarget, ...]:
"""Derive targets from their declarations, without a consumer roster."""
@@ -693,7 +705,8 @@ def for_execution(
for declaration in cls.__registry__.values()
if (owner := declaration.from_execution(context, plan)) is not None
for target in replace(
- owner, artifact_materializations=artifact_materializations,
+ owner,
+ artifact_materializations=artifact_materializations,
create_openhcs_metadata=plan.create_openhcs_metadata,
).production_targets(context, plan)
)
@@ -711,7 +724,9 @@ def reconciliation_targets(
context: ProcessingContext,
*,
document: OpenHCSMetadataSubdirectories | None = None,
- accepted_entries: Mapping[str, VirtualWorkspaceSourceProjectionEntries] | None = None,
+ accepted_entries: (
+ Mapping[str, VirtualWorkspaceSourceProjectionEntries] | None
+ ) = None,
) -> tuple[OpenHCSMetadataTarget, ...]:
"""Resolve destinations after runtime values have been released."""
return (self,)
@@ -783,7 +798,9 @@ def write(
str(self.output_dir), self.backend
)
)
- return (metadata_writer or AtomicMetadataWriter()).publish_source_projection_metadata(
+ return (
+ metadata_writer or AtomicMetadataWriter()
+ ).publish_source_projection_metadata(
METADATA_CONFIG.metadata_path(self.dataset_root),
self.sub_dir,
projection_entries,
@@ -817,7 +834,8 @@ def produced_projection_entries(
raise ValueError("OpenHCS metadata requires a file manager.")
target = type(self).from_plan(plan)
if target is None or self not in replace(
- target, artifact_materializations=self.artifact_materializations,
+ target,
+ artifact_materializations=self.artifact_materializations,
create_openhcs_metadata=self.create_openhcs_metadata,
).production_targets(context, plan):
raise ValueError("Produced metadata plan does not own this output target.")
@@ -1064,11 +1082,19 @@ def finalize_completed_dataset(
for target in cls.for_plan(plan):
target_contexts.setdefault(target, context)
- produced_by_document: dict[Path, dict[OpenHCSMetadataTarget, list[VirtualWorkspaceSourceProjectionEntries]]] = {}
+ produced_by_document: dict[
+ Path,
+ dict[OpenHCSMetadataTarget, list[VirtualWorkspaceSourceProjectionEntries]],
+ ] = {}
for observation in runtime_observations:
for context_observation in observation.contexts:
context = compiled_contexts[context_observation.context_key]
- for target, entries in context_observation.outputs.source_projection_entries_by_target.items():
+ for (
+ target,
+ entries,
+ ) in (
+ context_observation.outputs.source_projection_entries_by_target.items()
+ ):
produced_by_document.setdefault(
METADATA_CONFIG.metadata_path(target.dataset_root), {}
).setdefault(target, []).append(entries)
@@ -1082,13 +1108,17 @@ def finalize_completed_dataset(
targets_by_document.setdefault(
METADATA_CONFIG.metadata_path(owner.dataset_root), {}
)[owner] = context
- for metadata_path in dict.fromkeys((*targets_by_document, *produced_by_document)):
+ for metadata_path in dict.fromkeys(
+ (*targets_by_document, *produced_by_document)
+ ):
AtomicMetadataWriter().reconcile_completed_dataset(
metadata_path,
targets_by_document.get(metadata_path, {}),
produced_entries_by_target={
target: VirtualWorkspaceSourceProjectionEntries.combine(entries)
- for target, entries in produced_by_document.get(metadata_path, {}).items()
+ for target, entries in produced_by_document.get(
+ metadata_path, {}
+ ).items()
},
)
@@ -1119,12 +1149,14 @@ def from_execution(
@classmethod
def from_plan(cls, plan: CompiledStepPlan) -> PrimaryImageMetadataTarget | None:
- if plan.write_backend in (Backend.OMERO_LOCAL.value, Backend.MEMORY.value):
+ if plan.write_backend == Backend.MEMORY.value:
return None
if plan.write_backend is None:
raise ValueError(
f"Step {plan.step_index} ({plan.step_name}) has no write backend."
)
+ if not cls.hosts_metadata_document(plan.write_backend):
+ return None
if plan.output_dir is None:
raise ValueError(
f"Step {plan.step_index} ({plan.step_name}) has no output directory."
@@ -1153,10 +1185,7 @@ def from_plan(
cls, plan: CompiledStepPlan
) -> MaterializedImageMetadataTarget | None:
output = plan.materialized_output
- if output is None or output.backend in (
- Backend.OMERO_LOCAL.value,
- Backend.MEMORY.value,
- ):
+ if output is None or not cls.hosts_metadata_document(output.backend):
return None
return cls(
output_dir=output.output_dir,
@@ -1193,17 +1222,22 @@ def reconciliation_targets(
context: ProcessingContext,
*,
document: OpenHCSMetadataSubdirectories | None = None,
- accepted_entries: Mapping[str, VirtualWorkspaceSourceProjectionEntries] | None = None,
+ accepted_entries: (
+ Mapping[str, VirtualWorkspaceSourceProjectionEntries] | None
+ ) = None,
) -> tuple[RuntimeArtifactMetadataTarget, ...]:
"""Use durable typed projections, without reloading cleaned artifact values."""
from openhcs.core.dataset_sources.openhcs_format import OpenHCSMetadataHandler
if (document is None) != (accepted_entries is None):
- raise ValueError("Reconciliation requires one document and its admitted entries.")
+ raise ValueError(
+ "Reconciliation requires one document and its admitted entries."
+ )
handler = OpenHCSMetadataHandler(context.filemanager)
directories = (
handler.reconciliation_directories(self.dataset_root, self.backend)
- if document is None else handler.reconciliation_directories_from_document(
+ if document is None
+ else handler.reconciliation_directories_from_document(
self.dataset_root, self.backend, document, accepted_entries
)
)
@@ -1227,7 +1261,12 @@ def for_directory(self, directory: Path) -> RuntimeArtifactMetadataTarget:
@classmethod
def from_plan(cls, plan: CompiledStepPlan) -> RuntimeArtifactMetadataTarget | None:
materialization = plan.runtime_artifact_materialization
- if not materialization.has_persistent_target:
+ if not (
+ materialization.has_persistent_target
+ and cls.hosts_metadata_document(
+ materialization.require_persistent_backend()
+ )
+ ):
return None
output_dir = plan.artifact_analysis_output_dir
dataset_root = plan.artifact_output_dataset_root
diff --git a/openhcs/core/steps/function_runtime.py b/openhcs/core/steps/function_runtime.py
index 499f108d68..c7855a0d55 100644
--- a/openhcs/core/steps/function_runtime.py
+++ b/openhcs/core/steps/function_runtime.py
@@ -269,7 +269,10 @@ def source_binding_plan(self) -> CompiledSourceBindingPlan:
source_refs = self.invocation_source_artifact_refs
if source_refs:
return declared_plan.for_artifact_refs(source_refs)
- if self.compiled_group.runtime_domain is RuntimeInvocationDomain.ARTIFACT_MANAGED:
+ if (
+ self.compiled_group.runtime_domain
+ is RuntimeInvocationDomain.ARTIFACT_MANAGED
+ ):
# The compiler has already traced stored artifact lineage to its
# source bindings. A produced name is not itself a raw alias.
return declared_plan
@@ -325,8 +328,139 @@ def selected_artifact_output_plans(self) -> ArtifactOutputPlans:
)
+class MainOutputSliceProjectionMixin:
+ """Project a function's main output into published slices and their producer contexts."""
+
+ __slots__ = ()
+
+ def _producer_output_contexts(
+ self,
+ matching_files: Sequence[str],
+ ) -> tuple[AlignedImageSliceContext, ...]:
+ """Resolve exact producer contexts for the loaded main-flow paths."""
+
+ return step_output_manifest(self.context).producer_output_contexts_for_paths(
+ self.execution_plan,
+ matching_files,
+ self.context.microscope_handler.parser,
+ )
+
+ def _producer_filename_qualifiers(
+ self,
+ matching_files: Sequence[str],
+ ) -> tuple[str | None, ...]:
+ """Resolve the filename qualifier each producer gave the loaded paths."""
+
+ records = step_output_manifest(self.context).producer_output_records_for_paths(
+ self.execution_plan,
+ matching_files,
+ self.context.microscope_handler.parser,
+ )
+ if records is None:
+ return (None,) * len(matching_files)
+ return tuple(record.filename_qualifier for record in records)
+
+ def _passes_producer_outputs_through(
+ self,
+ processed_stack: ImagePayload,
+ output_count: int,
+ input_count: int,
+ ) -> bool:
+ """Whether each published slice is the producer's aligned output, passed on.
+
+ A declared passthrough chain names no output of its own, so its slices
+ keep the exact producer declaration (context and filename qualifier)
+ of the input file they are aligned to.
+ """
+ if processed_stack.owns_output_surfaces:
+ return False
+ if self._unwrapped_main_flow_output_context() is not None:
+ return False
+ return output_count == input_count
+
+ def _project_output_slices(
+ self,
+ processed_stack: ImagePayload,
+ matching_files: Sequence[str],
+ ) -> tuple[tuple[ImagePayload, AlignedImageSliceContext | None], ...]:
+ """Split the function's main output into its published slices."""
+ if processed_stack.declares_whole_image_output:
+ output_context = self._unwrapped_main_flow_output_context()
+ if output_context is None and self._passes_producer_outputs_through(
+ processed_stack, 1, len(matching_files)
+ ):
+ output_context = self._producer_output_contexts(matching_files)[0]
+ if output_context is None:
+ output_context = AlignedImageSliceContext.anonymous_main_flow()
+ return ((processed_stack, output_context),)
+ unstack_started_at = time.perf_counter()
+ projected = processed_stack.main_output_slices(
+ lambda data: self._unstack_main_output(data, len(matching_files))
+ )
+ RuntimeProfileLogger.log(
+ logger,
+ "pattern_source_unstack",
+ time.perf_counter() - unstack_started_at,
+ step=self.execution_plan.step_index,
+ step_name=self.execution_plan.step_name,
+ slices=len(projected),
+ )
+ if processed_stack.owns_output_surfaces:
+ return projected
+ output_payloads = tuple(payload for payload, _context in projected)
+ if self._passes_producer_outputs_through(
+ processed_stack, len(output_payloads), len(matching_files)
+ ):
+ slice_contexts = self._producer_output_contexts(matching_files)
+ else:
+ output_context = self._unwrapped_main_flow_output_context()
+ if output_context is None:
+ output_context = AlignedImageSliceContext.anonymous_main_flow()
+ slice_contexts = (output_context,) * len(output_payloads)
+ return tuple(zip(output_payloads, slice_contexts, strict=True))
+
+ def _unstack_main_output(
+ self,
+ processed_data: Any,
+ expected_count: int,
+ ) -> tuple[Any, ...]:
+ """Split undeclared main-output pixels along their leading runtime-slice axis."""
+ try:
+ return unstack_runtime_slices(
+ processed_data,
+ self.execution_plan.output_memory_type,
+ self.execution_plan.device_id_for(
+ self.execution_plan.output_memory_type
+ ),
+ expected_count=expected_count,
+ )
+ except ValueError as exc:
+ output_shape = np.shape(processed_data)
+ logger.error("Function output is not an OpenHCS image stack.")
+ logger.error("Output shape: %s", output_shape)
+ logger.error("Output ndim: %s", np.ndim(processed_data))
+ raise ValueError(
+ "Main processing must result in an image stack shaped "
+ f"(N, H, W) or (N, H, W, C), got "
+ f"{output_shape}"
+ ) from exc
+
+ def _unwrapped_main_flow_output_context(
+ self,
+ ) -> AlignedImageSliceContext | None:
+ ArtifactInputPlan.require_exact_map(
+ self.execution_plan.artifact_inputs,
+ boundary="Component artifact input",
+ )
+ return self.compiled_group.unwrapped_main_flow_output_context(
+ self.selected_artifact_output_plans()
+ )
+
+
@dataclass(frozen=True, slots=True, kw_only=True)
-class PatternGroupExecutionRequest(PatternGroupExecutionScope):
+class PatternGroupExecutionRequest(
+ MainOutputSliceProjectionMixin, PatternGroupExecutionScope
+):
"""All runtime data needed to process one pattern group."""
pattern_group_info: JsonValue
@@ -349,7 +483,9 @@ def loaded_fixed_component_values(
payload: RuntimeArrayData,
) -> RuntimeFixedComponentValues:
"""Capture the source-selected cohort's common fixed coordinates."""
- source_provenance = payload.metadata.source_provenance.with_common_scalar_identity_from_planes()
+ source_provenance = (
+ payload.metadata.source_provenance.with_common_scalar_identity_from_planes()
+ )
common_source_metadata = source_provenance.source_component_metadata or {}
variable_components = ComponentSet.of(
self.execution_plan.variable_components or ()
@@ -418,7 +554,9 @@ def _input_relative_path(cls, input_dir: Path, matched_path: str) -> Path:
def run(self) -> None:
start_time = time.time() if logger.isEnabledFor(logging.DEBUG) else None
plan = self.execution_plan
- logger.debug("Processing pattern %s for axis %s", self.pattern_repr, plan.axis_id)
+ logger.debug(
+ "Processing pattern %s for axis %s", self.pattern_repr, plan.axis_id
+ )
try:
load_started_at = time.perf_counter()
@@ -506,7 +644,9 @@ def run(self) -> None:
)
except Exception as e:
logger.error(
- "Error processing pattern group %s: %s", self.pattern_repr, e,
+ "Error processing pattern group %s: %s",
+ self.pattern_repr,
+ e,
exc_info=True,
)
raise ValueError(
@@ -536,18 +676,6 @@ def _record_main_flow_passthrough(self, matching_files: Sequence[str]) -> None:
records = tuple(record.passed_through(plan) for record in producer_records)
manifest.record_outputs(plan, records)
- def _producer_output_contexts(
- self,
- matching_files: Sequence[str],
- ) -> tuple[AlignedImageSliceContext, ...]:
- """Resolve exact producer contexts for the loaded main-flow paths."""
-
- return step_output_manifest(self.context).producer_output_contexts_for_paths(
- self.execution_plan,
- matching_files,
- self.context.microscope_handler.parser,
- )
-
def load_input_stack(
self,
) -> tuple[list[str], RuntimeArrayData]:
@@ -762,7 +890,6 @@ def _filter_matching_files_for_group(
parser,
matching_files,
group_component,
-
component_value,
)
if not filtered:
@@ -779,9 +906,7 @@ def _filter_matching_files_for_source_bindings(
) -> list[str]:
"""Constrain the loaded main stack to declared image source bindings."""
- if (
- isinstance(self.execution_plan.main_input_dependency, StepOutputInput)
- ):
+ if isinstance(self.execution_plan.main_input_dependency, StepOutputInput):
return matching_files
source_binding_plan = self.main_flow_source_binding_plan
@@ -1001,94 +1126,6 @@ def _apply_source_binding_payload(
)
return binding.apply_loaded_payload(payload, source_context)
- def _project_output_slices(
- self,
- processed_stack: ImagePayload,
- matching_files: Sequence[str],
- ) -> tuple[tuple[ImagePayload, AlignedImageSliceContext | None], ...]:
- """Split the function's main output into its published slices."""
- if processed_stack.declares_whole_image_output:
- output_context = self._unwrapped_main_flow_output_context()
- contexts = (
- (output_context,)
- if output_context is not None
- else (
- self._producer_output_contexts(matching_files)
- if len(matching_files) == 1
- else ()
- )
- )
- context = (
- contexts[0]
- if contexts
- else AlignedImageSliceContext.anonymous_main_flow()
- )
- return ((processed_stack, context),)
- unstack_started_at = time.perf_counter()
- projected = processed_stack.main_output_slices(
- lambda data: self._unstack_main_output(data, len(matching_files))
- )
- RuntimeProfileLogger.log(
- logger,
- "pattern_source_unstack",
- time.perf_counter() - unstack_started_at,
- step=self.execution_plan.step_index,
- step_name=self.execution_plan.step_name,
- slices=len(projected),
- )
- if processed_stack.owns_output_surfaces:
- return projected
- output_payloads = tuple(payload for payload, _context in projected)
- output_context = self._unwrapped_main_flow_output_context()
- slice_contexts = (
- (output_context,) * len(output_payloads)
- if output_context is not None
- else ()
- )
- if not slice_contexts and len(output_payloads) == len(matching_files):
- slice_contexts = self._producer_output_contexts(matching_files)
- if not slice_contexts:
- slice_contexts = tuple(
- AlignedImageSliceContext.anonymous_main_flow()
- for _payload in output_payloads
- )
- return tuple(zip(output_payloads, slice_contexts, strict=True))
-
- def _unstack_main_output(
- self, processed_data: Any, expected_count: int,
- ) -> tuple[Any, ...]:
- """Split undeclared main-output pixels along their leading runtime-slice axis."""
- try:
- return unstack_runtime_slices(
- processed_data,
- self.execution_plan.output_memory_type,
- self.execution_plan.device_id_for(
- self.execution_plan.output_memory_type
- ),
- expected_count=expected_count,
- )
- except ValueError as exc:
- output_shape = np.shape(processed_data)
- logger.error("Function output is not an OpenHCS image stack.")
- logger.error("Output shape: %s", output_shape)
- logger.error("Output ndim: %s", np.ndim(processed_data))
- raise ValueError(
- "Main processing must result in an image stack shaped "
- f"(N, H, W) or (N, H, W, C), got "
- f"{output_shape}"
- ) from exc
-
- def _unwrapped_main_flow_output_context(
- self,
- ) -> AlignedImageSliceContext | None:
- ArtifactInputPlan.require_exact_map(
- self.execution_plan.artifact_inputs,
- boundary="Component artifact input",
- )
- return self.compiled_group.unwrapped_main_flow_output_context(
- self.selected_artifact_output_plans()
- )
-
def _save_outputs(
self,
processed_stack: RuntimeArrayData,
@@ -1125,6 +1162,13 @@ def _save_outputs(
output_slices = tuple(payload for payload, _context in projected_outputs)
num_outputs = len(output_slices)
num_inputs = len(matching_files)
+ passed_through_qualifiers = (
+ self._producer_filename_qualifiers(matching_files)
+ if self._passes_producer_outputs_through(
+ processed_stack, num_outputs, num_inputs
+ )
+ else (None,) * num_outputs
+ )
if num_outputs < num_inputs:
logger.debug(
@@ -1185,6 +1229,12 @@ def _save_outputs(
output_identity = output_identity.with_filename_qualifier(
output_context.output_key
)
+ elif passed_through_qualifiers[i] is not None:
+ # A passed-through surface keeps the qualifier its producer
+ # published it under; dropping it would merge sibling surfaces.
+ output_identity = output_identity.with_filename_qualifier(
+ passed_through_qualifiers[i]
+ )
output_path = output_identity.path_for_request(output_path_request)
output_path_text = str(output_path)
img_slice = output_context.contextualize_image_payload(img_slice)
@@ -1203,7 +1253,9 @@ def _save_outputs(
output_identity,
output_context=output_context,
image_metadata=output_metadata,
- main_flow_plane_axis=processed_stack.plane_axis_for_output_context(output_context),
+ main_flow_plane_axis=processed_stack.plane_axis_for_output_context(
+ output_context
+ ),
)
if output_directory_exists and context.filemanager.exists(
@@ -1563,12 +1615,62 @@ def _save_artifact_value(
return runtime_value.data
+class FunctionCoreDebugMixin:
+ """Expose one invocation's debug cursor, artifacts and events."""
+ __slots__ = ()
+ def debug_cursor(self) -> DebugCursor:
+ return DebugCursor.from_invocation(
+ step_index=self.group_data.execution_plan.step_index,
+ step_scope_id=self.group_data.execution_plan.step_scope_id,
+ invocation=self.invocation,
+ pattern_group_identity=str(self.group_data.runtime_plane_index),
+ )
+
+ def debug_artifacts(
+ self,
+ artifact_plans: ArtifactInputPlans | ArtifactOutputPlans,
+ artifact_values: Mapping[ArtifactSpecRef, object] | None = None,
+ ) -> DebugArtifactRefProjection:
+ return DebugArtifactRefProjection.from_artifact_plans(
+ artifact_plans=artifact_plans,
+ cursor=self.debug_cursor(),
+ artifact_values=artifact_values,
+ )
+
+ def debug_event(
+ self,
+ event_type: type[DebugBoundary],
+ *,
+ exception: Exception | None = None,
+ timing_seconds: float | None = None,
+ invocation_parameters: tuple[DebugInvocationParameter, ...] = (),
+ input_artifact_values: Mapping[ArtifactSpecRef, object] | None = None,
+ ) -> DebugEvent:
+ return DebugEvent.for_invocation(
+ event_type=event_type,
+ cursor=self.debug_cursor(),
+ step_name=self.group_data.execution_plan.step_name,
+ callable_name=self.invocation.key.function_name,
+ axis_id=self.group_data.execution_plan.axis_id,
+ input_artifacts=self.debug_artifacts(
+ {
+ edge.storage_plan.ref(): edge.storage_plan
+ for edge in self.artifact_inputs.values()
+ if edge.storage_plan is not None
+ },
+ input_artifact_values,
+ ),
+ output_artifacts=self.debug_artifacts(self.artifact_outputs),
+ exception=exception,
+ timing_seconds=timing_seconds,
+ invocation_parameters=invocation_parameters,
+ )
@dataclass(frozen=True, slots=True)
-class FunctionCoreExecutor:
+class FunctionCoreExecutor(FunctionCoreDebugMixin):
"""Execute one scoped callable invocation and route declared artifact I/O."""
group_data: PatternGroupData
@@ -1677,8 +1779,7 @@ def loaded_source_artifact_payload(
if (
binding is None
or self.invocation.key.position != 0
- or not self.group_data.execution_plan.main_input_dependency
- .uses_pipeline_start_anchors()
+ or not self.group_data.execution_plan.main_input_dependency.uses_pipeline_start_anchors()
or binding.origin is not FromPipelineStart
or request.context is not self.group_data.context
):
@@ -1689,21 +1790,24 @@ def loaded_source_artifact_payload(
or request.artifact_inputs.get(edge.key) is not edge
or edge.storage_plan is not None
or edge.main_flow_projection is None
- or self.group_data.main_flow_source_binding_plan.binding_for_artifact_ref(ref)
+ or self.group_data.main_flow_source_binding_plan.binding_for_artifact_ref(
+ ref
+ )
!= binding
):
return None
payload = self.invocation.convert_input(
- self.group_data.main_data_stack, self.source_memory_type,
+ self.group_data.main_data_stack,
+ self.source_memory_type,
)
if edge.main_flow_projection is MainFlowInputProjection.DECLARED_SOURCE_IMAGE:
payload = payload.project_declared_source(ref.name)
elif edge.main_flow_projection is not MainFlowInputProjection.COMPLETE_PAYLOAD:
return None
metadata = payload.metadata
- if (
- not metadata.persists_whole_image()
- and metadata.plane_axis not in (None, RuntimePlaneAxis.RUNTIME_SLICE)
+ if not metadata.persists_whole_image() and metadata.plane_axis not in (
+ None,
+ RuntimePlaneAxis.RUNTIME_SLICE,
):
# A remaining binding axis does not prove this source's ordered
# physical members. Resolve it through its declared origin owner.
@@ -1715,7 +1819,9 @@ def loaded_source_artifact_payload(
member_count=member_count if member_count is not None else 1,
)
projection = RuntimePlaneAxisValueProjection.from_projector(
- request.plane_projection, RuntimePlaneAxis.RUNTIME_SLICE, (),
+ request.plane_projection,
+ RuntimePlaneAxis.RUNTIME_SLICE,
+ (),
)
if projection is not None and projection.plane_index is not None:
payload = RuntimeSliceProjection.value_for_slice(payload, projection)
@@ -1738,7 +1844,8 @@ def declared_source_payload(
stored_payload = loaded_artifact_payloads.get(source_ref)
source_binding_plan = (
self.group_data.source_binding_plan
- if request is None else request.source_binding_plan
+ if request is None
+ else request.source_binding_plan
)
source_binding = source_binding_plan.binding_for_artifact_ref(source_ref)
main_flow_edges = tuple(
@@ -1769,7 +1876,8 @@ def declared_source_payload(
RuntimePayload,
(
self.runtime_adapter_request(primary_source_payload)
- if request is None else request
+ if request is None
+ else request
).source_artifact_payload(source_ref),
)
if len(main_flow_edges) != 1:
@@ -1880,54 +1988,6 @@ def load_artifact_input(
)
return loaded_values
- def debug_cursor(self) -> DebugCursor:
- return DebugCursor.from_invocation(
- step_index=self.group_data.execution_plan.step_index,
- step_scope_id=self.group_data.execution_plan.step_scope_id,
- invocation=self.invocation,
- pattern_group_identity=str(self.group_data.runtime_plane_index),
- )
-
- def debug_artifacts(
- self,
- artifact_plans: ArtifactInputPlans | ArtifactOutputPlans,
- artifact_values: Mapping[ArtifactSpecRef, object] | None = None,
- ) -> DebugArtifactRefProjection:
- return DebugArtifactRefProjection.from_artifact_plans(
- artifact_plans=artifact_plans,
- cursor=self.debug_cursor(),
- artifact_values=artifact_values,
- )
-
- def debug_event(
- self,
- event_type: type[DebugBoundary],
- *,
- exception: Exception | None = None,
- timing_seconds: float | None = None,
- invocation_parameters: tuple[DebugInvocationParameter, ...] = (),
- input_artifact_values: Mapping[ArtifactSpecRef, object] | None = None,
- ) -> DebugEvent:
- return DebugEvent.for_invocation(
- event_type=event_type,
- cursor=self.debug_cursor(),
- step_name=self.group_data.execution_plan.step_name,
- callable_name=self.invocation.key.function_name,
- axis_id=self.group_data.execution_plan.axis_id,
- input_artifacts=self.debug_artifacts(
- {
- edge.storage_plan.ref(): edge.storage_plan
- for edge in self.artifact_inputs.values()
- if edge.storage_plan is not None
- },
- input_artifact_values,
- ),
- output_artifacts=self.debug_artifacts(self.artifact_outputs),
- exception=exception,
- timing_seconds=timing_seconds,
- invocation_parameters=invocation_parameters,
- )
-
def execute(
self,
*,
diff --git a/openhcs/mcp/execution.py b/openhcs/mcp/execution.py
index 39b179072a..094fe20495 100644
--- a/openhcs/mcp/execution.py
+++ b/openhcs/mcp/execution.py
@@ -84,7 +84,9 @@ class McpTransportExecutor(AsyncOperationExecutor):
def __init__(self) -> None:
if threading.current_thread() is not threading.main_thread():
- raise RuntimeError("MCP transport must be started on the process main thread.")
+ raise RuntimeError(
+ "MCP transport must be started on the process main thread."
+ )
self._application = QCoreApplication.instance() or QCoreApplication([])
if QThread.currentThread() != self._application.thread():
raise RuntimeError("MCP transport requires the Qt application main thread.")
@@ -119,7 +121,9 @@ def close(self) -> None:
leaving it to interpreter finalization is not an MCP lifecycle.
"""
if threading.current_thread() is not threading.main_thread():
- raise RuntimeError("MCP transport must be closed on the process main thread.")
+ raise RuntimeError(
+ "MCP transport must be closed on the process main thread."
+ )
if self._closed:
return
try:
diff --git a/openhcs/mcp/installed_demo.py b/openhcs/mcp/installed_demo.py
index c3726b313f..3d713dc9ec 100644
--- a/openhcs/mcp/installed_demo.py
+++ b/openhcs/mcp/installed_demo.py
@@ -398,7 +398,8 @@ def _execute_pipeline(
(row,) = (row for row in datasets.rows if row.root == str(plate_path))
if row.terminal_status != ExecutionCompleted.name:
raise InstalledDemoFailure(
- f"Portable neurite execution ended with {row.terminal_status}: {row}"
+ f"Portable neurite execution ended with {row.terminal_status}: "
+ f"{row.finished_execution_error}; row={row}"
)
return row
@@ -609,7 +610,7 @@ def run_installed_demo(
viewer_type = (
None
if viewer_payload is None or viewer_payload.viewer is None
- else viewer_payload.viewer.viewer_type.wire_value
+ else viewer_payload.viewer.viewer_type.value
)
result = InstalledDemoResult(
openhcs_version=distribution("openhcs").version,
diff --git a/openhcs/processing/backends/cellprofiler/_granularity_native.cpp b/openhcs/processing/backends/cellprofiler/_granularity_native.cpp
index a49ac9118d..b875d7d539 100644
--- a/openhcs/processing/backends/cellprofiler/_granularity_native.cpp
+++ b/openhcs/processing/backends/cellprofiler/_granularity_native.cpp
@@ -110,9 +110,27 @@ class OwnedArrayBuffer {
return acquired_;
}
const Py_buffer& view() const { return view_; }
+ // One unsigned C width has several buffer codes: NumPy exports uint32 as
+ // 'I' on LP64 platforms and as 'L' on LLP64 Windows. Compare by width.
+ static Py_ssize_t unsigned_width(const char* format) {
+ if (format[0] == '\0' || format[1] != '\0') return 0;
+ switch (format[0]) {
+ case 'B': return sizeof(unsigned char);
+ case 'H': return sizeof(unsigned short);
+ case 'I': return sizeof(unsigned int);
+ case 'L': return sizeof(unsigned long);
+ case 'Q': return sizeof(unsigned long long);
+ default: return 0;
+ }
+ }
+ bool has_format(const char* format) const {
+ if (std::strcmp(view_.format, format) == 0) return true;
+ const Py_ssize_t width = unsigned_width(format);
+ return width != 0 && unsigned_width(view_.format) == width && view_.itemsize == width;
+ }
bool matches(const char* name, const char* format, int ndim, bool writable) const {
if (view_.ndim != ndim || view_.format == nullptr || format == nullptr ||
- std::strcmp(view_.format, format) != 0 ||
+ !has_format(format) ||
!PyBuffer_IsContiguous(&view_, 'C') || (writable && view_.readonly)) {
PyErr_Format(PyExc_ValueError,
"%s must be a C-contiguous %d-D array with format %s%s",
diff --git a/openhcs/processing/backends/enhance/basic_processor_jax.py b/openhcs/processing/backends/enhance/basic_processor_jax.py
index 3ec9de0485..f6808c704b 100644
--- a/openhcs/processing/backends/enhance/basic_processor_jax.py
+++ b/openhcs/processing/backends/enhance/basic_processor_jax.py
@@ -32,6 +32,10 @@
NameByArtifactName,
)
+# The OpenHCS requirement markers decide where this distribution is installed;
+# the function catalog leaves this module out wherever they exclude it.
+REQUIRED_DISTRIBUTIONS = ("openhcs-basicpy",)
+
def _fitted_field_output(name: str) -> ArtifactSpec:
"""Project group lineage; runtime field owns aggregate contributor context."""
@@ -57,7 +61,6 @@ def _fitted_field_output(name: str) -> ArtifactSpec:
@numpy_func(contract=Pure3DContract, dtype_config_default=DtypeConfig())
@allowed_group_by_roles(ColourAxis)
@required_axis_roles(FittedIlluminationFieldOutput.observation_role)
-
@artifact_outputs(CORRECTED_OUTPUT, FLATFIELD_OUTPUT, DARKFIELD_OUTPUT)
def basic_flatfield_correction_jax(
image: np.ndarray,
diff --git a/openhcs/processing/backends/enhance/flatfield.py b/openhcs/processing/backends/enhance/flatfield.py
index 4f049bb880..1b95dce858 100644
--- a/openhcs/processing/backends/enhance/flatfield.py
+++ b/openhcs/processing/backends/enhance/flatfield.py
@@ -6,6 +6,7 @@
from openhcs.core.projected_image_output import SourceProjectedImageOutput
from openhcs.core.runtime_array_values import RuntimeArrayData
+from openhcs.core.runtime_image_values import ImagePayload
from openhcs.core.runtime_plane_projection import RuntimePlaneAxisValueProjection
from openhcs.core.axes import AxisFamily, AxisRole, TileAxis
@@ -34,7 +35,7 @@ def __post_init__(self) -> None:
@classmethod
def validate_observation_domain(cls, source: RuntimeArrayData) -> None:
"""Reject mislabeled metadata-backed ensembles before fitting."""
- metadata = source.metadata
+ metadata = ImagePayload.of(source).metadata
family = AxisFamily.active()
retained_axes = tuple(
family.named(name) for name in metadata.retained_plane_component_values()
@@ -67,7 +68,7 @@ def resolve_source_context(
if source.shape != (self.observation_count, *self.data.shape):
raise ValueError("A fitted field must retain the source spatial grid.")
self.validate_observation_domain(source)
- metadata = source.metadata.collapse_leading_plane_axis()
+ metadata = ImagePayload.of(source).metadata.collapse_leading_plane_axis()
# Model parameters are not unit-interval input pixels. Contributor source
# dtypes/identities remain in provenance, not on the fitted field's range.
metadata = metadata.replace_fields(
diff --git a/openhcs/processing/backends/lib_registry/openhcs_registry.py b/openhcs/processing/backends/lib_registry/openhcs_registry.py
index e89c959654..20e5b6308d 100644
--- a/openhcs/processing/backends/lib_registry/openhcs_registry.py
+++ b/openhcs/processing/backends/lib_registry/openhcs_registry.py
@@ -9,6 +9,7 @@
import ast
import importlib
import importlib.abc
+import importlib.metadata
import inspect
import logging
from abc import ABC, abstractmethod
@@ -22,6 +23,8 @@
import numpy as np
from metaclass_registry import LazyDiscoveryDict, import_module_preserving_root_logging
+from packaging.requirements import Requirement
+from packaging.utils import canonicalize_name
from openhcs.constants import VALID_MEMORY_TYPES, MemoryType
from openhcs.core.callable_contract import (
@@ -293,6 +296,18 @@ def _catalog_memory_types(func: Callable) -> frozenset[MemoryType] | None:
return declared
+def _module_source(module_name: str) -> tuple[str, str] | None:
+ """Return loader-decoded module source and origin, or None without source."""
+
+ spec = importlib.util.find_spec(module_name)
+ if spec is None or not isinstance(spec.loader, importlib.abc.InspectLoader):
+ return None
+ source = spec.loader.get_source(module_name)
+ if source is None:
+ return None
+ return source, spec.origin or module_name
+
+
def _module_declares_allowed_memory_type(
module_name: str,
allowed_memory_types: frozenset[str],
@@ -300,15 +315,135 @@ def _module_declares_allowed_memory_type(
spec = importlib.util.find_spec(module_name)
if spec is not None and spec.submodule_search_locations is not None:
return True
- if spec is None or not isinstance(spec.loader, importlib.abc.InspectLoader):
+ module_source = _module_source(module_name)
+ if module_source is None:
return False
- source = spec.loader.get_source(module_name)
- if source is None:
+ return _source_declares_allowed_memory_type(*module_source, allowed_memory_types)
+
+
+_REQUIRED_DISTRIBUTIONS_NAME = "REQUIRED_DISTRIBUTIONS"
+
+
+def _openhcs_requirements() -> tuple[Requirement, ...]:
+ """Return the installed OpenHCS distribution's declared requirements."""
+
+ return tuple(
+ Requirement(value) for value in importlib.metadata.requires("openhcs") or ()
+ )
+
+
+def _distribution_excluded_by_package_metadata(name: str) -> bool:
+ """Return whether OpenHCS's own requirement markers leave ``name`` out here.
+
+ A distribution is legitimately absent only when no OpenHCS requirement for
+ it applies to this environment (markers, extras) and it is not installed.
+ A distribution OpenHCS requires here is never excluded, so its absence
+ surfaces as the module's import failure.
+ """
+
+ canonical = canonicalize_name(name)
+ requirements = tuple(
+ requirement
+ for requirement in _openhcs_requirements()
+ if canonicalize_name(requirement.name) == canonical
+ )
+ if not requirements:
+ raise ValueError(
+ f"Distribution {name!r} is not declared by the OpenHCS package "
+ "requirements; a module cannot depend on an undeclared distribution."
+ )
+ if any(
+ marker is None or marker.evaluate({"extra": ""})
+ for marker in (requirement.marker for requirement in requirements)
+ ):
return False
- return _source_declares_allowed_memory_type(
- source,
- spec.origin or module_name,
- allowed_memory_types,
+ try:
+ importlib.metadata.distribution(name)
+ except importlib.metadata.PackageNotFoundError:
+ return True
+ return False
+
+
+@lru_cache(maxsize=1024)
+def _source_required_distributions(source: str, origin: str) -> tuple[str, ...]:
+ """Read a module's literal ``REQUIRED_DISTRIBUTIONS`` declaration."""
+
+ for statement in ast.parse(source, filename=origin).body:
+ if isinstance(statement, ast.Assign):
+ targets = statement.targets
+ elif isinstance(statement, ast.AnnAssign):
+ targets = (statement.target,)
+ else:
+ continue
+ if not any(
+ isinstance(target, ast.Name) and target.id == _REQUIRED_DISTRIBUTIONS_NAME
+ for target in targets
+ ):
+ continue
+ value = ast.literal_eval(statement.value)
+ if not isinstance(value, tuple) or not all(
+ isinstance(item, str) and item for item in value
+ ):
+ raise ValueError(
+ f"{origin}: {_REQUIRED_DISTRIBUTIONS_NAME} must be a literal tuple "
+ "of distribution names."
+ )
+ return value
+ return ()
+
+
+def _module_in_this_catalog(
+ module_name: str,
+ allowed_memory_types: frozenset[str],
+) -> bool:
+ """Memory-type eligibility and the package requirement markers decide.
+
+ A module whose ``REQUIRED_DISTRIBUTIONS`` names a distribution those
+ markers leave out of this environment is not in this installation's catalog.
+ """
+
+ if not _module_declares_allowed_memory_type(module_name, allowed_memory_types):
+ logger.debug(
+ "Skipping OpenHCS module %s - no allowed memory decorators", module_name
+ )
+ return False
+ excluded = _module_distributions_excluded_by_package_metadata(module_name)
+ if excluded:
+ logger.info(
+ "Skipping OpenHCS module %s - package requirements exclude %s here",
+ module_name,
+ ", ".join(excluded),
+ )
+ return not excluded
+
+
+def _catalog_policy_context() -> dict[str, Any]:
+ """The declaration-import policy as catalogue cache identity."""
+
+ return {
+ "allowed_memory_types": sorted(_allowed_openhcs_memory_types()),
+ "distributions_excluded_by_package_metadata": sorted(
+ {
+ canonicalize_name(requirement.name)
+ for requirement in _openhcs_requirements()
+ if _distribution_excluded_by_package_metadata(requirement.name)
+ }
+ ),
+ }
+
+
+def _module_distributions_excluded_by_package_metadata(
+ module_name: str,
+) -> tuple[str, ...]:
+ """Return the module's declared distributions this installation excludes."""
+
+ module_source = _module_source(module_name)
+ if module_source is None:
+ return ()
+ return tuple(
+ name
+ for name in _source_required_distributions(*module_source)
+ if _distribution_excluded_by_package_metadata(name)
)
@@ -487,29 +622,19 @@ def _get_openhcs_modules(self) -> List[str]:
return modules
def get_modules_to_scan(self) -> List[Tuple[str, Any]]:
- """Import every admitted native declaration before catalog publication.
+ """Import every native declaration in this installation's catalog.
- Memory-type eligibility owns exclusions. An eligible OpenHCS module
- that cannot import is a broken installation, not an optional function
- inventory; its failure must prevent apparent catalog readiness.
+ ``_module_in_this_catalog`` owns exclusions; any other OpenHCS module
+ that cannot import is a broken installation and fails catalog readiness.
"""
self._ensure_module_inventory()
assert self.MODULES_TO_SCAN is not None
- modules = []
allowed_memory_types = _allowed_openhcs_memory_types()
- for module_name in self.MODULES_TO_SCAN:
- if not _module_declares_allowed_memory_type(
- module_name,
- allowed_memory_types,
- ):
- logger.debug(
- "Skipping OpenHCS module %s - no allowed memory decorators",
- module_name,
- )
- continue
- module = import_module_preserving_root_logging(module_name)
- modules.append((module_name, module))
- return modules
+ return [
+ (module_name, import_module_preserving_root_logging(module_name))
+ for module_name in self.MODULES_TO_SCAN
+ if _module_in_this_catalog(module_name, allowed_memory_types)
+ ]
# ===== ESSENTIAL ABC METHODS =====
def get_library_version(self) -> str:
@@ -540,7 +665,7 @@ def cache_source_mtimes(self) -> Dict[str, float]:
def cache_discovery_context(self) -> dict[str, Any]:
"""Project the declaration-import policy into catalogue cache identity."""
- return {"allowed_memory_types": sorted(_allowed_openhcs_memory_types())}
+ return _catalog_policy_context()
def is_library_available(self) -> bool:
"""OpenHCS is always available."""
@@ -826,9 +951,11 @@ def reconstruct_cached_callable(
"""Reconstruct cached metadata through the declaration's one wrapper."""
return _registered_wrapper(
- self, func, lambda: super(OpenHCSRegistry, self).reconstruct_cached_callable(
+ self,
+ func,
+ lambda: super(OpenHCSRegistry, self).reconstruct_cached_callable(
func, contract
- )
+ ),
)
def _processing_contract_for_function(
diff --git a/openhcs/processing/custom_functions/manager.py b/openhcs/processing/custom_functions/manager.py
index eea25e3ec7..2811fe1ef7 100644
--- a/openhcs/processing/custom_functions/manager.py
+++ b/openhcs/processing/custom_functions/manager.py
@@ -81,7 +81,122 @@ def backend_label(self) -> str:
return self.memory_type or self.contract.execution_scope.value
-class CustomFunctionManager:
+class CustomFunctionSourceFilesMixin:
+ """Persist custom-function sources as exact bytes and verify their revision snapshots."""
+
+ def _save_function_code(self, func_name: str, code: str) -> None:
+ """
+ Save function code to storage directory.
+
+ Args:
+ func_name: Name of function (used as filename)
+ code: Python source code
+ """
+ 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)
+ finally:
+ temp_path.unlink(missing_ok=True)
+ logger.info(f"Saved custom function to: {file_path}")
+
+ def _snapshot_source(
+ self,
+ source_path: Path,
+ ) -> CustomFunctionSourceSnapshot | None:
+ """Read one exact persisted source without executing it."""
+
+ try:
+ source_bytes = source_path.read_bytes()
+ except FileNotFoundError:
+ return None
+ try:
+ code = source_bytes.decode("utf-8")
+ except UnicodeDecodeError as exc:
+ raise ValidationError(
+ f"Persisted custom function '{source_path.name}' is not UTF-8."
+ ) from exc
+ return CustomFunctionSourceSnapshot(
+ source=CustomFunctionSource(
+ function_name=source_path.stem,
+ content_sha256=CustomFunctionSource.content_digest(source_bytes),
+ ),
+ code=code,
+ )
+
+ def _snapshot_all_sources(
+ self,
+ ) -> tuple[
+ CustomFunctionSourceRevision,
+ tuple[CustomFunctionSourceSnapshot, ...],
+ ]:
+ """Read the persisted source set that one reconciliation will prepare."""
+
+ snapshots = []
+ for source_path in sorted(self.storage_dir.glob("*.py")):
+ snapshot = self._snapshot_source(source_path)
+ if snapshot is None:
+ raise ValidationError(
+ "Persisted custom-function sources changed while being read; "
+ "retry reconciliation."
+ )
+ snapshots.append(snapshot)
+ revision = CustomFunctionSourceRevision(
+ sources=tuple(snapshot.source for snapshot in snapshots)
+ )
+ return revision, tuple(snapshots)
+
+ def _require_snapshot(
+ self,
+ source_path: Path,
+ expected: CustomFunctionSourceSnapshot,
+ ) -> None:
+ """Fail loudly when one prepared source no longer owns the same bytes."""
+
+ current = self._snapshot_source(source_path)
+ if current is None or current.source != expected.source:
+ raise ValidationError(
+ f"Persisted custom function '{source_path.name}' changed during "
+ "preparation; retry the lifecycle operation."
+ )
+
+ def _require_revision(self, expected: CustomFunctionSourceRevision) -> None:
+ """Fail loudly unless the prepared persisted source set is still exact."""
+
+ try:
+ current = self.source_revision()
+ except FileNotFoundError as exc:
+ raise ValidationError(
+ "Persisted custom-function sources changed during preparation; "
+ "retry reconciliation."
+ ) from exc
+ if current != expected:
+ raise ValidationError(
+ "Persisted custom-function sources changed during preparation; "
+ "retry reconciliation."
+ )
+
+ def _write_temporary_source(self, code: str) -> Path:
+ """Write source beside its destination for an atomic replacement."""
+
+ file_descriptor, raw_path = tempfile.mkstemp(
+ suffix=".py",
+ dir=self.storage_dir,
+ )
+ try:
+ # Persist the exact bytes the revision digest was computed from;
+ # text-mode newline translation would change them on Windows.
+ with os.fdopen(file_descriptor, "wb") as stream:
+ stream.write(code.encode("utf-8"))
+ stream.flush()
+ os.fsync(stream.fileno())
+ except BaseException:
+ Path(raw_path).unlink(missing_ok=True)
+ raise
+ return Path(raw_path)
+
+
+class CustomFunctionManager(CustomFunctionSourceFilesMixin):
"""
Manager for custom function lifecycle operations.
@@ -114,7 +229,9 @@ def default_storage_directory(cls) -> Path:
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.")
+ 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):
@@ -155,11 +272,18 @@ def register_from_code(
"""
if persist and write_admission is not None:
if expected_function_name is None:
- raise ValueError("Write admission requires an explicit custom function name.")
+ 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))
+ 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:
+ 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}."
@@ -208,7 +332,9 @@ def source_revision(self) -> CustomFunctionSourceRevision:
sources = tuple(
CustomFunctionSource(
function_name=source_path.stem,
- content_sha256=CustomFunctionSource.content_digest(source_path.read_bytes()),
+ content_sha256=CustomFunctionSource.content_digest(
+ source_path.read_bytes()
+ ),
)
for source_path in sorted(self.storage_dir.glob("*.py"))
)
@@ -367,7 +493,7 @@ def list_custom_functions(self) -> list[CustomFunctionInfo]:
for py_file in sorted(self.storage_dir.glob("*.py")):
try:
- metadata = self._prepare_source(py_file.read_text(encoding="utf-8"))
+ metadata = self._prepare_source(py_file.read_bytes().decode("utf-8"))
contract = CallableContract.from_callable(metadata.func)
functions.append(
CustomFunctionInfo(
@@ -401,7 +527,7 @@ def get_function_code(self, func_name: str) -> str:
if not file_path.exists():
raise ValueError(f"Custom function '{func_name}' not found")
- return file_path.read_text(encoding="utf-8")
+ return file_path.read_bytes().decode("utf-8")
def update_custom_function(self, old_name: str, new_code: str) -> str:
"""
@@ -490,7 +616,9 @@ def _prepare_source(
) -> "FunctionMetadata":
"""Validate and project one source without mutating runtime or disk state."""
- from openhcs.processing.backends.lib_registry.openhcs_registry import OpenHCSRegistry
+ from openhcs.processing.backends.lib_registry.openhcs_registry import (
+ OpenHCSRegistry,
+ )
validation_result = validate_code(code)
if not validation_result.is_valid:
@@ -562,115 +690,6 @@ def _check_name_collision(
"OpenHCS function. Please choose a different name."
)
- def _save_function_code(self, func_name: str, code: str) -> None:
- """
- Save function code to storage directory.
-
- Args:
- func_name: Name of function (used as filename)
- code: Python source code
- """
- 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)
- finally:
- temp_path.unlink(missing_ok=True)
- logger.info(f"Saved custom function to: {file_path}")
-
- def _snapshot_source(
- self,
- source_path: Path,
- ) -> CustomFunctionSourceSnapshot | None:
- """Read one exact persisted source without executing it."""
-
- try:
- source_bytes = source_path.read_bytes()
- except FileNotFoundError:
- return None
- try:
- code = source_bytes.decode("utf-8")
- except UnicodeDecodeError as exc:
- raise ValidationError(
- f"Persisted custom function '{source_path.name}' is not UTF-8."
- ) from exc
- return CustomFunctionSourceSnapshot(
- source=CustomFunctionSource(
- function_name=source_path.stem,
- content_sha256=CustomFunctionSource.content_digest(source_bytes),
- ),
- code=code,
- )
-
- def _snapshot_all_sources(
- self,
- ) -> tuple[
- CustomFunctionSourceRevision,
- tuple[CustomFunctionSourceSnapshot, ...],
- ]:
- """Read the persisted source set that one reconciliation will prepare."""
-
- snapshots = []
- for source_path in sorted(self.storage_dir.glob("*.py")):
- snapshot = self._snapshot_source(source_path)
- if snapshot is None:
- raise ValidationError(
- "Persisted custom-function sources changed while being read; "
- "retry reconciliation."
- )
- snapshots.append(snapshot)
- revision = CustomFunctionSourceRevision(
- sources=tuple(snapshot.source for snapshot in snapshots)
- )
- return revision, tuple(snapshots)
-
- def _require_snapshot(
- self,
- source_path: Path,
- expected: CustomFunctionSourceSnapshot,
- ) -> None:
- """Fail loudly when one prepared source no longer owns the same bytes."""
-
- current = self._snapshot_source(source_path)
- if current is None or current.source != expected.source:
- raise ValidationError(
- f"Persisted custom function '{source_path.name}' changed during "
- "preparation; retry the lifecycle operation."
- )
-
- def _require_revision(self, expected: CustomFunctionSourceRevision) -> None:
- """Fail loudly unless the prepared persisted source set is still exact."""
-
- try:
- current = self.source_revision()
- except FileNotFoundError as exc:
- raise ValidationError(
- "Persisted custom-function sources changed during preparation; "
- "retry reconciliation."
- ) from exc
- if current != expected:
- raise ValidationError(
- "Persisted custom-function sources changed during preparation; "
- "retry reconciliation."
- )
-
- def _write_temporary_source(self, code: str) -> Path:
- """Write source beside its destination for an atomic replacement."""
-
- file_descriptor, raw_path = tempfile.mkstemp(
- suffix=".py",
- dir=self.storage_dir,
- )
- try:
- with os.fdopen(file_descriptor, "w", encoding="utf-8") as stream:
- stream.write(code)
- stream.flush()
- os.fsync(stream.fileno())
- except BaseException:
- Path(raw_path).unlink(missing_ok=True)
- raise
- return Path(raw_path)
-
def _clear_caches(self) -> None:
"""
Clear function registry metadata caches.
diff --git a/openhcs/pyqt_gui/main.py b/openhcs/pyqt_gui/main.py
index fc933bcc43..a9acc0b179 100644
--- a/openhcs/pyqt_gui/main.py
+++ b/openhcs/pyqt_gui/main.py
@@ -5,6 +5,7 @@
"""
import logging
+from concurrent.futures import Future
from collections.abc import Callable
from functools import singledispatchmethod
from pathlib import Path
@@ -19,7 +20,6 @@
from PyQt6.QtWidgets import (
QApplication,
QDialog,
- QFileDialog,
QLabel,
QMainWindow,
QMessageBox,
@@ -212,6 +212,8 @@ def __init__(
# Core configuration
self.runtime_context = runtime_context
self.function_catalog_service = function_catalog_service
+ # Completes once the shared endpoint is attached and its catalog prewarmed.
+ self.execution_services_prepared: Future[None] | None = None
# Create shared components
self.storage_registry = storage_registry
@@ -362,7 +364,9 @@ def deferred_initialization(self):
def start_background_services(self) -> None:
"""Start non-visual services after the initialized window has painted."""
- self.service_adapter.execute_async_operation(self._prepare_execution_services)
+ self.execution_services_prepared = self.service_adapter.execute_async_operation(
+ self._prepare_execution_services
+ )
self._start_ui_bridge_if_enabled()
self._check_for_updates_on_startup()
@@ -857,7 +861,10 @@ def setup_status_bar(self):
# Add time-travel controls to the ordinary left lane. Transient
# QStatusBar messages hide ordinary widgets, so application status is
# rendered by a permanent right-lane label below instead.
- from pyqt_reactive.widgets.time_travel import TimeTravelCommands, TimeTravelWidget
+ from pyqt_reactive.widgets.time_travel import (
+ TimeTravelCommands,
+ TimeTravelWidget,
+ )
from openhcs.authoring.session.datasets import DATASET_LIST_SCOPE_ID
@@ -1130,7 +1137,9 @@ def _on_synthetic_plate_generated(self, output_dir: str, pipeline_path: str):
self.show_pipeline_editor()
for scope_id in self.session.add_dataset_roots((output_dir,)):
self.session.load_pipeline_file(scope_id, pipeline_path)
- logger.info("Added synthetic dataset and loaded its test pipeline: %s", output_dir)
+ logger.info(
+ "Added synthetic dataset and loaded its test pipeline: %s", output_dir
+ )
def _on_consolidate_results(self):
"""Open file dialog to select results directory and consolidate analysis results."""
@@ -1415,9 +1424,7 @@ def restart_session(self) -> None:
def check_for_updates(self) -> None:
"""Start an explicit, asynchronous stable-release check."""
- if not self.desktop_update_service.check_for_updates(
- ExplicitUpdateCheck
- ):
+ if not self.desktop_update_service.check_for_updates(ExplicitUpdateCheck):
return
self.check_for_updates_action.setEnabled(False)
self.status_message.emit("Checking for OpenHCS updates…")
@@ -1427,9 +1434,7 @@ def _check_for_updates_on_startup(self) -> None:
if not self.runtime_context.ui_config.check_for_updates_on_startup:
return
- if self.desktop_update_service.check_for_updates(
- StartupUpdateCheck
- ):
+ if self.desktop_update_service.check_for_updates(StartupUpdateCheck):
self.check_for_updates_action.setEnabled(False)
def _on_update_check_completed(
@@ -1545,59 +1550,6 @@ def _close_object_state_subscriptions(self) -> None:
self._object_state_unregister_subscription.release()
self.time_travel_widget.close()
- # ========== THEME MANAGEMENT METHODS ==========
-
- def switch_to_dark_theme(self):
- """Switch to dark theme variant."""
- self.service_adapter.theme_manager.switch_to_dark_theme()
- self.status_message.emit("Switched to dark theme")
-
- def switch_to_light_theme(self):
- """Switch to light theme variant."""
- self.service_adapter.theme_manager.switch_to_light_theme()
- self.status_message.emit("Switched to light theme")
-
- def load_theme_from_file(self):
- """Load theme from JSON configuration file."""
- file_path, _ = QFileDialog.getOpenFileName(
- self, "Load Theme Configuration", "", "JSON Files (*.json);;All Files (*)"
- )
-
- if file_path:
- theme_loaded = self.service_adapter.theme_manager.load_theme_from_config(
- file_path
- )
- if theme_loaded:
- self.status_message.emit(f"Loaded theme from {Path(file_path).name}")
- else:
- QMessageBox.warning(
- self,
- "Theme Load Error",
- f"Failed to load theme from {Path(file_path).name}",
- )
-
- def save_theme_to_file(self):
- """Save current theme to JSON configuration file."""
- file_path, _ = QFileDialog.getSaveFileName(
- self,
- "Save Theme Configuration",
- "pyqt6_color_scheme.json",
- "JSON Files (*.json);;All Files (*)",
- )
-
- if file_path:
- theme_saved = self.service_adapter.theme_manager.save_current_theme(
- file_path
- )
- if theme_saved:
- self.status_message.emit(f"Saved theme to {Path(file_path).name}")
- else:
- QMessageBox.warning(
- self,
- "Theme Save Error",
- f"Failed to save theme to {Path(file_path).name}",
- )
-
@singledispatchmethod
def on_session_event(self, event: SessionEvent) -> None:
del event
diff --git a/pyproject.toml b/pyproject.toml
index d6f3007b95..0a13b4fc05 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -63,6 +63,9 @@ dependencies = [
"numba>=0.61.0,<0.63; platform_system == 'Darwin' and platform_machine == 'x86_64'",
"numba>=0.61.0; platform_system != 'Darwin' or platform_machine != 'x86_64'",
"skan>=0.13.1",
+ # Every execution server prepares the CellProfiler watershed catalog entries,
+ # whose CellProfiler 4 paths run mahotas; it is a runtime requirement.
+ "mahotas>=1.4.18",
"threadpoolctl>=3.6.0",
# Image I/O and formats
@@ -99,13 +102,13 @@ dependencies = [
"psutil>=5.9.0",
# External dependencies (PyPI versions for production builds)
- "zmqruntime>=0.6.1,<0.7",
+ "zmqruntime>=0.6.2,<0.7",
"pycodify>=0.2.0,<0.3",
- "objectstate>=2.1,<3",
+ "objectstate>=2.1.1,<3",
"python-introspect>=0.2.8,<0.3",
"metaclass-registry>=0.3.0,<0.4",
"arraybridge>=0.4.1,<0.5",
- "polystore>=0.6,<0.7",
+ "polystore>=0.6.1,<0.7",
"pyqt-reactive>=0.5,<0.6",
]
@@ -132,7 +135,6 @@ dev-gui = [
# Production CellProfiler compatibility dependencies
cellprofiler-compat = [
- "mahotas>=1.4.18",
"matplotlib>=3.10.0",
]
diff --git a/scripts/build_mcp_knowledge_assets.py b/scripts/build_mcp_knowledge_assets.py
index 8ed94107b1..59f8660cb7 100644
--- a/scripts/build_mcp_knowledge_assets.py
+++ b/scripts/build_mcp_knowledge_assets.py
@@ -32,6 +32,7 @@ def _build_module(relative_path: str) -> SimpleNamespace:
ManifestRootAcquisitionKind = _UPSTREAM_SOURCES.ManifestRootAcquisitionKind
ManifestRootAcquisitionSpec = _UPSTREAM_SOURCES.ManifestRootAcquisitionSpec
KNOWLEDGE_MANIFEST_RELATIVE_PATH = _MANIFEST_SCHEMA.DEFAULT_KNOWLEDGE_BASE_MANIFEST_PATH
+PACKAGED_KNOWLEDGE_PATH_BUDGET = _MANIFEST_SCHEMA.PACKAGED_KNOWLEDGE_PATH_BUDGET
PACKAGED_KNOWLEDGE_ROOT_RELATIVE_PATH = (
Path("openhcs/agent") / _MANIFEST_SCHEMA.PACKAGED_KNOWLEDGE_BASE_ROOT
)
@@ -191,6 +192,35 @@ def project_knowledge_assets(
)
+def knowledge_asset_projections(
+ project_root: Path,
+ recipe_type: type[ComparisonManifestSnapshot] = ComparisonManifestSnapshot,
+) -> dict[Path, Path]:
+ """Packaged resource path -> canonical source, within the path budget."""
+ projections = knowledge_source_projections(
+ project_root / KNOWLEDGE_MANIFEST_RELATIVE_PATH,
+ source_root=project_root,
+ recipe_type=recipe_type,
+ )
+ plugin_manifest = project_root / AGENT_PLUGIN_MANIFEST_PATH
+ if plugin_manifest.is_file():
+ skill_sources = AgentSkillBundle.from_manifest(plugin_manifest).source_paths()
+ projections.update(
+ {source.relative_to(project_root): source for source in skill_sources}
+ )
+ over_budget = tuple(
+ relative.as_posix()
+ for relative in projections
+ if len(relative.as_posix()) > PACKAGED_KNOWLEDGE_PATH_BUDGET
+ )
+ if over_budget:
+ raise ValueError(
+ f"MCP knowledge resource paths exceed {PACKAGED_KNOWLEDGE_PATH_BUDGET} "
+ f"characters: {over_budget}"
+ )
+ return projections
+
+
def _project_knowledge_assets(
project_root: Path,
destination_root: Path,
@@ -207,17 +237,7 @@ def _project_knowledge_assets(
raise ValueError(
f"MCP knowledge destination must not own the project root: {destination}"
)
- projections = knowledge_source_projections(
- root / KNOWLEDGE_MANIFEST_RELATIVE_PATH,
- source_root=root,
- recipe_type=native_sources.recipe_type(),
- )
- plugin_manifest = root / AGENT_PLUGIN_MANIFEST_PATH
- if plugin_manifest.is_file():
- skill_sources = AgentSkillBundle.from_manifest(plugin_manifest).source_paths()
- projections.update(
- {source.relative_to(root): source for source in skill_sources}
- )
+ projections = knowledge_asset_projections(root, native_sources.recipe_type())
native_sources.materialize()
missing = tuple(source for source in projections.values() if not source.is_file())
if missing:
diff --git a/scripts/smoke_installed_gui.py b/scripts/smoke_installed_gui.py
index dcc5c5ba82..be1bb8abec 100644
--- a/scripts/smoke_installed_gui.py
+++ b/scripts/smoke_installed_gui.py
@@ -1,7 +1,6 @@
"""Operate an installed OpenHCS GUI through MCP outside the source checkout."""
from __future__ import annotations
-from openhcs.authoring.session.operations.datasets import ShowDatasetCode
import argparse
import faulthandler
@@ -35,6 +34,7 @@
JsonValue = str | int | bool | None | list["JsonValue"] | dict[str, "JsonValue"]
AgentDtoT = TypeVar("AgentDtoT")
+ResultT = TypeVar("ResultT")
@dataclass(frozen=True, slots=True)
@@ -497,20 +497,31 @@ def _capture_installed_gui_snapshot(
)
-def verify_installed_function_catalog(
- service: ZMQFunctionCatalogService,
- *,
- deadline: OperationDeadline,
-) -> InstalledFunctionCatalogSmokeResult:
- """Require the GUI's background endpoint catalogue and one usable reference."""
+def _result_within(future: Future[ResultT], deadline: OperationDeadline) -> ResultT:
+ """Wait inside the smoke deadline; an owner's own timeout stays its own."""
- future = service.prepare()
try:
- page = future.result(timeout=deadline.remaining_seconds())
+ return future.result(timeout=deadline.remaining_seconds())
except TimeoutError as error:
if not future.cancelled() and future.done() and future.exception() is error:
raise
raise deadline.timeout_error() from error
+
+
+def verify_installed_function_catalog(
+ service: ZMQFunctionCatalogService,
+ *,
+ execution_services_prepared: Future[None],
+ deadline: OperationDeadline,
+) -> InstalledFunctionCatalogSmokeResult:
+ """Require the GUI's background endpoint catalogue and one usable reference.
+
+ The catalog attaches only to an existing endpoint, so first wait for the
+ GUI's own endpoint start and catalog prewarm to finish.
+ """
+
+ _result_within(execution_services_prepared, deadline)
+ page = _result_within(service.prepare(), deadline)
if not page.revision.strip():
raise AssertionError("Installed function catalog omitted its revision.")
if page.total <= 0 or not page.items:
@@ -542,6 +553,7 @@ def run_installed_live_mcp_smoke(
evidence_directory: Path,
forbidden_root: Path,
function_catalog_service: ZMQFunctionCatalogService,
+ execution_services_prepared: Future[None],
execution_config: OpenHCSZMQConfig,
deadline: OperationDeadline,
journal: InstalledGuiSmokeEvidenceJournal,
@@ -554,7 +566,7 @@ def run_installed_live_mcp_smoke(
)
from openhcs.agent.dto.mcp import McpServerHealthResult
from openhcs.agent.dto.ui_bridge import (
- UiActionInvocationStatus,
+ ActionAccepted,
UiActionInvokeResult,
UiBridgeCatalog,
UiBridgeStatus,
@@ -565,6 +577,7 @@ def run_installed_live_mcp_smoke(
MainWindowWidgetIdentity,
PlateManagerWidgetIdentity,
)
+ from openhcs.authoring.session.operations.datasets import ShowDatasetCode
from openhcs.mcp.dev_client import McpDevClient
from openhcs.mcp.dev_client_commanding import CapabilityBackedCommandSpec
from openhcs.mcp.dev_client_commands.ui import (
@@ -575,6 +588,7 @@ def run_installed_live_mcp_smoke(
journal.record(InstalledGuiSmokePhase.WAITING_FOR_FUNCTION_CATALOG)
function_catalog = verify_installed_function_catalog(
function_catalog_service,
+ execution_services_prepared=execution_services_prepared,
deadline=deadline,
)
journal.record(
@@ -691,7 +705,7 @@ def run_installed_live_mcp_smoke(
capability=agent_capabilities.ui_invoke_action,
payload_type=UiActionInvokeResult,
)
- if action_result.status != UiActionInvocationStatus.ACCEPTED.value:
+ if action_result.status is not ActionAccepted:
raise AssertionError(
f"Installed UI action was not accepted: {action_result}"
)
@@ -868,6 +882,9 @@ def run_live_mcp() -> None:
evidence_directory=evidence_directory,
forbidden_root=forbidden_root,
function_catalog_service=application.function_catalog_service,
+ execution_services_prepared=(
+ main_window.execution_services_prepared
+ ),
execution_config=ui_config.zmq,
deadline=deadline,
journal=journal,
diff --git a/scripts/smoke_installed_mcp.py b/scripts/smoke_installed_mcp.py
index 5f3bd995f0..06c48c5286 100644
--- a/scripts/smoke_installed_mcp.py
+++ b/scripts/smoke_installed_mcp.py
@@ -280,7 +280,7 @@ async def _run_measured_execution_protocol_smoke(
"wavelengths": 1,
"z_stack_levels": 1,
"num_cells": 2,
- "wells": ["A01"],
+ "partition_values": ["A01"],
"format": "ImageXpress",
"random_seed": 7,
},
@@ -302,6 +302,7 @@ async def _run_measured_execution_protocol_smoke(
pipeline_source = PipelineDocumentCodec.render(document)
source_file = output_dir / "pipeline.py"
source_file.write_text(pipeline_source, encoding="utf-8")
+
async def call(name: str, arguments: dict, timeout: float = 90) -> dict:
return _tool_payload(
await asyncio.wait_for(session.call_tool(name, arguments), timeout=timeout)
@@ -330,7 +331,9 @@ async def call(name: str, arguments: dict, timeout: float = 90) -> dict:
"openhcs_run_datasets",
{
"runtime_observation_export_path": str(
- MeasuredPipelineRunArtifact.RUNTIME_OBSERVATION.path_in(evidence_dir)
+ MeasuredPipelineRunArtifact.RUNTIME_OBSERVATION.path_in(
+ evidence_dir
+ )
),
"runtime_observation_export_scope": "outcomes",
},
@@ -340,7 +343,9 @@ async def call(name: str, arguments: dict, timeout: float = 90) -> dict:
):
started = await call(tool, {"scope_ids": [scope_id], **arguments})
if started.get("status") != "accepted":
- raise AssertionError(f"Installed MCP rejected {tool}: {started} {connected}")
+ raise AssertionError(
+ f"Installed MCP rejected {tool}: {started} {connected}"
+ )
sequence = started["event_sequence"]
deadline = time.monotonic() + 180
while True:
diff --git a/scripts/validate_docs.py b/scripts/validate_docs.py
index 8205bb185d..1c36d3d499 100644
--- a/scripts/validate_docs.py
+++ b/scripts/validate_docs.py
@@ -65,7 +65,8 @@
MARKDOWN_FENCE = re.compile(r"^\s*```(?:python|py)\s*$", re.IGNORECASE)
LITERAL_INCLUDE = re.compile(r"^\s*\.\.\s+literalinclude::\s+(?P\S+)\s*$")
REPOSITORY_SOURCE_PATH = re.compile(
- r"(?P(?:benchmark|docs|external|openhcs|scripts)/[A-Za-z0-9_.\-/]+"
+ r"(?(?:benchmark|docs|external|openhcs|packaging|scripts)/[A-Za-z0-9_.\-/]+"
r"\.(?:jsonl|json|md|py|rst|toml|yaml|yml))(?![A-Za-z0-9_])"
)
EXTERNAL_URL = re.compile(r"https?://\S+")
diff --git a/tests/conftest.py b/tests/conftest.py
index 068fef6fc7..ea7aecaf00 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -8,6 +8,7 @@
from pathlib import Path
from openhcs._source_dependencies import ensure_source_checkout_external_paths
+from openhcs.utils.environment import OpenHCSProcessEnvironment
ensure_source_checkout_external_paths()
@@ -26,6 +27,7 @@
default_download_policy=ImageJArchiveDownloadPolicy(allow_download=False)
)
+
def _missing_native_extensions() -> tuple[str, ...]:
"""Native modules declared by their sources (``.cpp``) but not built.
@@ -63,7 +65,7 @@ def pytest_sessionstart(session):
# Conditionally import pytest-qt only when not in CPU-only mode
-CPU_ONLY_MODE = os.getenv("OPENHCS_CPU_ONLY", "false").lower() == "true"
+CPU_ONLY_MODE = OpenHCSProcessEnvironment.cpu_only_mode()
if not CPU_ONLY_MODE:
pytest_plugins = ["pytestqt"]
else:
@@ -151,25 +153,30 @@ def reap_spawned_processes(monkeypatch) -> Iterator[None]:
marker = uuid.uuid4().hex
monkeypatch.setenv(SPAWN_MARKER_VARIABLE, marker)
yield
- own_group = os.getpgid(0)
survivors = _marked_processes(marker)
for process in survivors:
- try:
- group = os.getpgid(process.pid)
- if group != own_group:
- os.killpg(group, signal.SIGTERM)
- else:
- process.terminate()
- except (ProcessLookupError, psutil.NoSuchProcess):
- continue
+ _stop_marked_process(process, force=False)
_gone, alive = psutil.wait_procs(survivors, timeout=10)
for process in alive:
- try:
- group = os.getpgid(process.pid)
- if group != own_group:
- os.killpg(group, signal.SIGKILL)
- else:
- process.kill()
- except (ProcessLookupError, psutil.NoSuchProcess):
- continue
+ _stop_marked_process(process, force=True)
psutil.wait_procs(alive, timeout=10)
+
+
+def _stop_marked_process(process: psutil.Process, *, force: bool) -> None:
+ """Signal a detached POSIX session's whole group, else the process itself.
+
+ Windows has no process groups; every descendant carries the marker, so
+ stopping each marked process covers the tree there.
+ """
+ try:
+ if os.name == "posix":
+ group = os.getpgid(process.pid)
+ if group != os.getpgid(0):
+ os.killpg(group, signal.SIGKILL if force else signal.SIGTERM)
+ return
+ if force:
+ process.kill()
+ else:
+ process.terminate()
+ except (ProcessLookupError, psutil.NoSuchProcess):
+ return
diff --git a/tests/integration/test_agent_observation_export_zmq.py b/tests/integration/test_agent_observation_export_zmq.py
index 57d1bd6252..784431bc3c 100644
--- a/tests/integration/test_agent_observation_export_zmq.py
+++ b/tests/integration/test_agent_observation_export_zmq.py
@@ -11,7 +11,7 @@
import pytest
from objectstate.object_state import ObjectStateRegistry
from zmqruntime import DataControlPortPairAuthority
-from zmqruntime.messages import ExecutionStatus
+from zmqruntime.messages import ExecutionStatus, MessageFields
from benchmark.cellprofiler_benchmark_cli import create_benchmark_argument_parser
from benchmark.contracts.measured_run_receipt import MeasuredPipelineRunReceipt
@@ -113,14 +113,21 @@ def _start_run(built, plate: Path, pipeline, export_path: Path) -> tuple[str, in
_call(
built,
"openhcs_set_dataset_pipeline",
- {"scope_id": scope_id, "pipeline_source": PipelineDocumentCodec.render(pipeline)},
- )
- _wait_for_row(built, _call(
- built, "openhcs_initialize_datasets", {"scope_ids": [scope_id]}
- ), lambda row: row["initialized"] and not row["init_pending"])
- _wait_for_row(built, _call(
- built, "openhcs_compile_datasets", {"scope_ids": [scope_id]}
- ), lambda row: row["compiled"] and not row["compile_pending"])
+ {
+ "scope_id": scope_id,
+ "pipeline_source": PipelineDocumentCodec.render(pipeline),
+ },
+ )
+ _wait_for_row(
+ built,
+ _call(built, "openhcs_initialize_datasets", {"scope_ids": [scope_id]}),
+ lambda row: row["initialized"] and not row["init_pending"],
+ )
+ _wait_for_row(
+ built,
+ _call(built, "openhcs_compile_datasets", {"scope_ids": [scope_id]}),
+ lambda row: row["compiled"] and not row["compile_pending"],
+ )
started = _call(
built,
"openhcs_run_datasets",
@@ -335,17 +342,18 @@ def test_ordinary_execution_can_export_outcomes_without_value_observation(
exported = ZMQRuntimeExecutionOutcomeExport.read(export_path)
exported.require_successful_axes()
- assert completed.results_summary["well_count"] == exported.axis_count == 1
+ assert (
+ completed.results_summary[MessageFields.PARTITION_COUNT]
+ == exported.axis_count
+ == 1
+ )
assert completed.results_summary["runtime_observation_export_scope"] == "outcomes"
assert exported.successful_axis_count == 1
assert exported.output_roots
assert exported.server_environment is not None
assert exported.server_environment.python_executable
assert completed.observation is None
- assert (
- completed.receipt.observation_export_scope
- is ExportRuntimeOutcomes
- )
+ assert completed.receipt.observation_export_scope is ExportRuntimeOutcomes
assert {
BenchmarkPhase.COMPILE_OPENHCS,
BenchmarkPhase.EXECUTE_OPENHCS,
@@ -393,9 +401,7 @@ def test_measured_repetitions_share_one_owned_ordinary_server(tmp_path: Path) ->
runtime_observation_export_path=(
evidence_dir / "observation.pkl.gz"
),
- runtime_observation_export_scope=(
- ExportRuntimeOutcomes
- ),
+ runtime_observation_export_scope=(ExportRuntimeOutcomes),
)
)
completed, _ = execute_measured_openhcs_pipeline_on_client(
diff --git a/tests/integration/test_basicpy_real_fit.py b/tests/integration/test_basicpy_real_fit.py
index 8c271094f9..4025252c49 100644
--- a/tests/integration/test_basicpy_real_fit.py
+++ b/tests/integration/test_basicpy_real_fit.py
@@ -17,6 +17,7 @@ def test_real_algorithm_uses_numpy_transport_and_is_cpu_catalog_eligible(monkeyp
from openhcs.processing.backends.lib_registry.openhcs_registry import (
_catalog_memory_types,
_module_declares_allowed_memory_type,
+ _module_distributions_excluded_by_package_metadata,
)
monkeypatch.setenv("OPENHCS_CPU_ONLY", "true")
@@ -28,6 +29,12 @@ def test_real_algorithm_uses_numpy_transport_and_is_cpu_catalog_eligible(monkeyp
assert _module_declares_allowed_memory_type(
basic_flatfield_correction_jax.__module__, frozenset({MemoryType.NUMPY.value})
)
+ assert (
+ _module_distributions_excluded_by_package_metadata(
+ basic_flatfield_correction_jax.__module__
+ )
+ == ()
+ )
def run_fit(observations):
diff --git a/tests/integration/test_benchmark_openhcs_adapter_cppipe.py b/tests/integration/test_benchmark_openhcs_adapter_cppipe.py
index 42621085c3..d2fb082d19 100644
--- a/tests/integration/test_benchmark_openhcs_adapter_cppipe.py
+++ b/tests/integration/test_benchmark_openhcs_adapter_cppipe.py
@@ -105,8 +105,9 @@ def test_openhcs_adapter_retains_declared_path_policy_and_explicit_exports(
for path in result.output_path.rglob("*")
if path.is_file() and path.suffix.lower() in {".tif", ".tiff"}
)
- assert tiff_outputs
- assert all(path.name.endswith(".labels.tif") for path in tiff_outputs)
+ # With automatic materialization disabled only declared exports persist:
+ # the spreadsheet. Retained labels and QA checkpoints stay in memory.
+ assert tiff_outputs == ()
def test_openhcs_adapter_resolves_dataset_reference_cppipe(
diff --git a/tests/integration/test_cellprofiler_official30_zmq.py b/tests/integration/test_cellprofiler_official30_zmq.py
index a298d1d30d..ced0397664 100644
--- a/tests/integration/test_cellprofiler_official30_zmq.py
+++ b/tests/integration/test_cellprofiler_official30_zmq.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from itertools import product
-
import json
import os
import shutil
@@ -51,7 +49,12 @@
runtime_artifact_viewer_component_identity,
)
from openhcs.core.source_spatial_domain import SourceSpatialDomain
-from openhcs.core.streaming_config_declarations import ViewerType
+from openhcs.core.streaming_config_declarations import (
+ FijiViewer,
+ NapariViewer,
+ ViewerFamily,
+ ViewerType,
+)
from openhcs.runtime.fiji_macro_runtime import FijiMacroExecutionRequest
from openhcs.runtime.viewer_protocol import (
ViewerControlMessageRequest,
@@ -75,20 +78,20 @@
for case_index, case in enumerate(_OFFICIAL30_CASES)
)
_OFFICIAL30_FIJI_VIEWER_VARIANTS = (
- (ViewerType.FIJI,),
- (ViewerType.FIJI, ViewerType.NAPARI),
+ (FijiViewer,),
+ (FijiViewer, NapariViewer),
)
_OFFICIAL30_FIJI_VIEWER_PARAMS = tuple(
pytest.param(
- viewer_types,
+ viewers,
case_index,
case,
id=(
- "+".join(viewer_type.value for viewer_type in viewer_types)
+ "+".join(viewer.wire_value for viewer in viewers)
+ f"-{case_index:02d}-{case.name}"
),
)
- for viewer_types in _OFFICIAL30_FIJI_VIEWER_VARIANTS
+ for viewers in _OFFICIAL30_FIJI_VIEWER_VARIANTS
for case_index, case in enumerate(_OFFICIAL30_CASES)
)
_FIJI_FRESH_PROCESS_IMAGE_PROBE = r"""#@ String Directory
@@ -236,13 +239,13 @@ def _free_zmq_port_pair(
def _registered_streaming_config_kwargs(
- viewer_types: Sequence[ViewerType],
- ports_by_viewer: Mapping[ViewerType, int],
+ viewers: Sequence[type[ViewerFamily]],
+ ports_by_viewer: Mapping[type[ViewerFamily], int],
) -> dict[str, StreamingConfig]:
"""Project one viewer selection through registered config owners."""
- selected_viewers = set(viewer_types)
- if len(selected_viewers) != len(viewer_types):
+ selected_viewers = set(viewers)
+ if len(selected_viewers) != len(viewers):
raise ValueError("Official30 viewer variants require unique declarations.")
if set(ports_by_viewer) != selected_viewers:
raise ValueError(
@@ -250,8 +253,8 @@ def _registered_streaming_config_kwargs(
)
config_kwargs: dict[str, StreamingConfig] = {}
- for viewer_type, config_type in StreamingConfig.__registry__.items():
- enabled = viewer_type in selected_viewers
+ for viewer, config_type in StreamingConfig.__registry__.items():
+ enabled = viewer in selected_viewers
init_kwargs: dict[str, object] = {
"enabled": enabled,
"persistent": enabled,
@@ -259,10 +262,10 @@ def _registered_streaming_config_kwargs(
if enabled:
init_kwargs.update(
host="127.0.0.1",
- port=ports_by_viewer[viewer_type],
+ port=ports_by_viewer[viewer],
transport_mode=TransportMode.TCP,
)
- config_kwargs[viewer_type.family.config_key] = config_type(**init_kwargs)
+ config_kwargs[viewer.config_key] = config_type(**init_kwargs)
if selected_viewers - set(StreamingConfig.__registry__):
raise ValueError("Official30 viewer variant selected an unregistered config.")
@@ -399,7 +402,7 @@ def _assert_napari_state_matches_runtime(
runtime_artifact_viewer_component_identity(values)
for values in layer.component_values
) == Counter(
- runtime_artifact_viewer_component_identity(payload["components"])
+ runtime_artifact_viewer_component_identity(payload.components)
for payload in layer.payload_summaries
)
assert all(component in layer.axis_labels for component in layer.stack_axes)
@@ -411,59 +414,44 @@ def _assert_napari_state_matches_runtime(
assert layer.data_shape[axis_index] == len(component_values)
for expected in runtime_observation.expectation.artifact_viewer:
- # One producer identity can legitimately back both its native image
- # layer and its derived shapes overlay; the payload expectation below
- # compares ndarray summaries, so match the image layer.
- matching_layers = tuple(
+ # One producer identity can back both its native image layer and its
+ # derived shapes overlay. The producer's expected payloads are every
+ # payload it streamed, so compare against all of its layers' payloads.
+ producer_layers = tuple(
layer
for layer in state.layers
if expected.producer_identity in layer.producer_identities
- and StreamingDataType.IMAGE.value in layer.data_types
)
- assert len(matching_layers) == 1
- layer = matching_layers[0]
+ image_layers = tuple(
+ layer
+ for layer in producer_layers
+ if StreamingDataType.IMAGE.value in layer.data_types
+ )
+ assert len(image_layers) == 1
+ layer = image_layers[0]
assert layer.mounted
- # The presentation may aggregate a varying component axis (e.g. site)
- # into one layer item. That item represents both the aggregate payload
- # itself and each per-coordinate payload it covers, so the actual
- # identity set is the union of the summary's own identity and every
- # expanded per-coordinate identity.
- actual_payloads: list[RuntimeArtifactViewerPayloadExpectation] = []
- for payload in layer.payload_summaries:
- aggregates = payload.get("aggregate_component_values") or {}
- varying_axes = tuple(aggregates.items())
-
- def summary_identity(components) -> RuntimeArtifactViewerPayloadExpectation:
- return RuntimeArtifactViewerPayloadExpectation(
- components=runtime_artifact_viewer_component_identity(components),
- source_spatial_domain=SourceSpatialDomain.from_viewer_wire_mapping(
- payload,
- source_label="official30 Napari payload summary",
- ),
- )
-
- actual_payloads.append(summary_identity(payload["components"]))
- axes_values = [values for _, values in varying_axes]
- for combination in product(*axes_values):
- expanded = dict(payload["components"])
- expanded.update(
- {
- component: value
- for (component, _), value in zip(
- varying_axes, combination, strict=True
- )
- }
- )
- actual_payloads.append(summary_identity(expanded))
+ actual_payloads = tuple(
+ RuntimeArtifactViewerPayloadExpectation(
+ components=runtime_artifact_viewer_component_identity(
+ payload.components
+ ),
+ source_spatial_domain=SourceSpatialDomain.from_viewer_wire_mapping(
+ payload.to_wire_mapping(),
+ source_label="official30 Napari payload summary",
+ ),
+ )
+ for producer_layer in producer_layers
+ for payload in producer_layer.payload_summaries
+ )
assert Counter(payload.identity_key for payload in actual_payloads) == Counter(
payload.identity_key for payload in expected.payloads
)
- payload_paths = tuple(payload["path"] for payload in layer.payload_summaries)
+ payload_paths = tuple(payload.path for payload in layer.payload_summaries)
assert all(isinstance(path, str) and path for path in payload_paths)
payload_data_types = tuple(
- dict.fromkeys(payload["data_type"] for payload in layer.payload_summaries)
+ dict.fromkeys(payload.data_type for payload in layer.payload_summaries)
)
assert len(payload_data_types) == 1
assert layer.data_types == payload_data_types
@@ -563,13 +551,12 @@ def test_official30_fiji_variants_project_registered_viewer_configs() -> None:
partition_filter_config=PartitionFilterConfig(partition_filter=1)
)
- for viewer_types in _OFFICIAL30_FIJI_VIEWER_VARIANTS:
+ for viewers in _OFFICIAL30_FIJI_VIEWER_VARIANTS:
ports_by_viewer = {
- viewer_type: 20_000 + index * 100
- for index, viewer_type in enumerate(viewer_types)
+ viewer: 20_000 + index * 100 for index, viewer in enumerate(viewers)
}
configs = _registered_streaming_config_kwargs(
- viewer_types,
+ viewers,
ports_by_viewer,
)
variant_config = GlobalPipelineConfig(
@@ -585,11 +572,11 @@ def test_official30_fiji_variants_project_registered_viewer_configs() -> None:
if field.name not in registered_keys
)
assert {
- config.viewer_family.viewer_type() for config in configs.values() if config.enabled
- } == set(viewer_types)
+ config.viewer_family for config in configs.values() if config.enabled
+ } == set(viewers)
assert all(config.persistent is config.enabled for config in configs.values())
assert {
- config.viewer_family.viewer_type(): config.port
+ config.viewer_family: config.port
for config in configs.values()
if config.enabled
} == ports_by_viewer
@@ -742,12 +729,12 @@ def test_official30_nonpersistent_napari_isolated_per_case(
@pytest.mark.integration
@pytest.mark.parametrize(
- ("viewer_types", "case_index", "case"),
+ ("viewers", "case_index", "case"),
_OFFICIAL30_FIJI_VIEWER_PARAMS,
)
def test_official30_persistent_fiji_variants_isolated_per_case(
tmp_path: Path,
- viewer_types: tuple[ViewerType, ...],
+ viewers: tuple[type[ViewerFamily], ...],
case_index: int,
case: CellProfilerComparisonCase,
) -> None:
@@ -760,14 +747,14 @@ def test_official30_persistent_fiji_variants_isolated_per_case(
transport_mode=OPENHCS_ZMQ_CONFIG.transport_mode,
)
ports_by_viewer = {
- viewer_type: _free_zmq_port_pair(
+ viewer: _free_zmq_port_pair(
excluded_ports,
transport_mode=TransportMode.TCP,
)
- for viewer_type in viewer_types
+ for viewer in viewers
}
streaming_configs = _registered_streaming_config_kwargs(
- viewer_types,
+ viewers,
ports_by_viewer,
)
global_config = GlobalPipelineConfig(
@@ -775,15 +762,15 @@ def test_official30_persistent_fiji_variants_isolated_per_case(
**streaming_configs,
)
endpoints = {
- viewer_type.value: ViewerRuntimeEndpoint(
- streaming_configs[viewer_type.family.config_key]
+ viewer.wire_value: ViewerRuntimeEndpoint(
+ streaming_configs[viewer.config_key]
.viewer_runtime_config()
.transport_endpoint,
OPENHCS_ZMQ_CONFIG,
)
- for viewer_type in viewer_types
+ for viewer in viewers
}
- variant_id = "+".join(viewer_type.value for viewer_type in viewer_types)
+ variant_id = "+".join(viewer.wire_value for viewer in viewers)
case_output_root = tmp_path / variant_id / f"case_{case_index:02d}"
macro_path = tmp_path / f"{variant_id}_fresh_process_probe.ijm"
macro_path.write_text(_FIJI_FRESH_PROCESS_IMAGE_PROBE, encoding="utf-8")
@@ -821,18 +808,18 @@ def test_official30_persistent_fiji_variants_isolated_per_case(
assert (case_output_root / "summary.csv").is_file()
runtime_observation = _runtime_observation(observations[0])
- for viewer_type in viewer_types:
- config = streaming_configs[viewer_type.family.config_key]
- if viewer_type is ViewerType.NAPARI:
+ for viewer in viewers:
+ config = streaming_configs[viewer.config_key]
+ if viewer is NapariViewer:
_assert_live_napari_state(config, runtime_observation)
- elif viewer_type is ViewerType.FIJI:
+ elif viewer is FijiViewer:
_assert_fiji_fresh_process_image(
config=config,
macro_path=macro_path,
)
else:
raise AssertionError(
- f"Unhandled registered Official30 viewer {viewer_type.name!r}."
+ f"Unhandled registered Official30 viewer {viewer.display_name!r}."
)
except Exception as error:
failures.append(
diff --git a/tests/integration/test_count_cells_simple_dual_channel.py b/tests/integration/test_count_cells_simple_dual_channel.py
index 79f9c6ba38..06baf077fe 100644
--- a/tests/integration/test_count_cells_simple_dual_channel.py
+++ b/tests/integration/test_count_cells_simple_dual_channel.py
@@ -55,6 +55,7 @@
SyntheticMicroscopyGenerator,
)
from openhcs.domains.microscopy.axes import Microscopy
+from openhcs.microscopes.imagexpress import ImageXpressHandler
def _write_known_dual_channel_images(plate_dir):
@@ -74,19 +75,19 @@ def _write_known_dual_channel_images(plate_dir):
image_dir = plate_dir / "TimePoint_1"
for well in ("A01", "B01"):
tifffile.imwrite(
- image_dir / f"{well}_s001_w1_z001_t001.tif",
+ image_dir / f"{well}_s001_w1.tif",
channel_1,
)
tifffile.imwrite(
- image_dir / f"{well}_s001_w2_z001_t001.tif",
+ image_dir / f"{well}_s001_w2.tif",
channel_2,
)
tifffile.imwrite(
- image_dir / f"{well}_s002_w1_z001_t001.tif",
+ image_dir / f"{well}_s002_w1.tif",
channel_1,
)
tifffile.imwrite(
- image_dir / f"{well}_s002_w2_z001_t001.tif",
+ image_dir / f"{well}_s002_w2.tif",
channel_2,
)
@@ -186,7 +187,6 @@ def test_dual_channel_count_runs_on_synthetic_plate_with_channel_stack(
from openhcs.processing.materialization import CsvOptions, MaterializationSpec, ROIOptions
import numpy as np
-from openhcs.microscopes.imagexpress import ImageXpressHandler
@numpy
diff --git a/tests/integration/test_custom_source_transport.py b/tests/integration/test_custom_source_transport.py
index 9863a0e07c..1d795f1e53 100644
--- a/tests/integration/test_custom_source_transport.py
+++ b/tests/integration/test_custom_source_transport.py
@@ -35,6 +35,7 @@
if dependency_root:
sys.path.insert(0, str(WORKTREE))
from openhcs._source_dependencies import ensure_source_checkout_external_paths
+
ensure_source_checkout_external_paths(Path(dependency_root))
@@ -89,9 +90,9 @@ def _run_child(env, *arguments):
while True:
remaining = deadline - time.monotonic()
assert remaining > 0, f"Child exceeded 30s: {arguments!r}"
- assert os.fstat(output_file.fileno()).st_size < OUTPUT_LIMIT, (
- f"Child output exceeded {OUTPUT_LIMIT} bytes: {arguments!r}"
- )
+ assert (
+ os.fstat(output_file.fileno()).st_size < OUTPUT_LIMIT
+ ), f"Child output exceeded {OUTPUT_LIMIT} bytes: {arguments!r}"
try:
returncode = process.wait(timeout=min(remaining, 0.1))
except subprocess.TimeoutExpired:
@@ -99,9 +100,9 @@ def _run_child(env, *arguments):
break
output_file.seek(0)
captured.extend(output_file.read(OUTPUT_LIMIT))
- assert len(captured) < OUTPUT_LIMIT, (
- f"Child output exceeded {OUTPUT_LIMIT} bytes: {arguments!r}"
- )
+ assert (
+ len(captured) < OUTPUT_LIMIT
+ ), f"Child output exceeded {OUTPUT_LIMIT} bytes: {arguments!r}"
finally:
if process.poll() is None:
process.kill()
@@ -424,8 +425,13 @@ def _spawned_custom_task(payload, execution_id, subject_id):
owner = reference.declaration.artifact_outputs[0].measurement_feature_owner
results.append(owner.make_row(2.5))
ProgressExecutionContext(execution_id, subject_id).emit(
- axis_id="synthetic", step_name="custom", phase=StepCompletedProgressPhase,
- status=SuccessProgressStatus, percent=100.0, completed=1, total=1,
+ axis_id="synthetic",
+ step_name="custom",
+ phase=StepCompletedProgressPhase,
+ status=SuccessProgressStatus,
+ percent=100.0,
+ completed=1,
+ total=1,
)
return os.getpid(), tuple(results)
@@ -454,7 +460,8 @@ def _compiled_custom_contexts(payload, runtime):
from openhcs.core.compiled_step_plan import CompiledStepPlan
from openhcs.core.context.processing_context import ProcessingContext
from openhcs.core.function_patterns import (
- compile_function_pattern, normalize_function_pattern,
+ compile_function_pattern,
+ normalize_function_pattern,
)
from openhcs.core.function_step_transport import FunctionStepTransportAuthority
@@ -474,11 +481,14 @@ def _compiled_custom_contexts(payload, runtime):
assert callable(invocation.contract.raw_processing_function)
prepared = invocation.contract.resolve_runtime_callable()
plan = CompiledStepPlan(
- step_index=0, step_name="Synthetic custom",
- axis_id=axis_id, compiled_function_pattern=pattern,
+ step_index=0,
+ step_name="Synthetic custom",
+ axis_id=axis_id,
+ compiled_function_pattern=pattern,
)
context = ProcessingContext(
- axis_id=axis_id, step_plans={0: plan},
+ axis_id=axis_id,
+ step_plans={0: plan},
filemanager=FileManager({Backend.MEMORY.value: MemoryStorageBackend()}),
)
context.freeze()
@@ -490,11 +500,15 @@ def _compiled_custom_contexts(payload, runtime):
assert transported.contract is not invocation.contract
assert invocation.contract.resolve_runtime_callable() is prepared
assert callable(invocation.contract.raw_processing_function)
- assert FunctionStepTransportAuthority.normalize_callable_contract(
- transported.contract
- ) is transported.contract
+ assert (
+ FunctionStepTransportAuthority.normalize_callable_contract(
+ transported.contract
+ )
+ is transported.contract
+ )
return CompiledExecutionBundle.from_runtime_contexts(
- pipeline_definition=(), runtime_contexts=contexts,
+ pipeline_definition=(),
+ runtime_contexts=contexts,
worker_assignments={f"worker_{i}": [axis] for i, axis in enumerate(contexts)},
runtime_environment=runtime,
).transport_contexts
@@ -503,12 +517,13 @@ def _compiled_custom_contexts(payload, runtime):
def _spawn(path):
import multiprocessing
from openhcs.core.compiled_execution import (
- CompiledRuntimeEnvironmentPlan, CompiledWorkerStartPlan,
+ CompiledRuntimeEnvironmentPlan,
+ CompiledWorkerStartPlan,
)
from openhcs.core.config import MultiprocessingStartMethod
from openhcs.core.orchestrator.cancellation import ExecutionCancellationSignal
from openhcs.core.orchestrator.worker_execution import WorkerExecutorFactory
- from openhcs.core.progress import ProgressExecutionContext
+ from openhcs.core.progress import ProgressExecutionContext, ProgressStatus
# A future derived context can carry nontransportable local runtime state;
# no generic worker-bootstrap consumer should need to learn its fields.
@@ -521,19 +536,25 @@ class RichProgressContext(ProgressExecutionContext):
payload = _load(path)
method = MultiprocessingStartMethod.SPAWN
runtime = CompiledRuntimeEnvironmentPlan(
- worker_start=CompiledWorkerStartPlan(method, method, "source fixture", False, False),
- use_threading=False, configured_num_workers=2,
+ worker_start=CompiledWorkerStartPlan(
+ method, method, "source fixture", False, False
+ ),
+ use_threading=False,
+ configured_num_workers=2,
)
queue = multiprocessing.get_context(method.value).Queue()
resources = WorkerExecutorFactory(
- log_file_base=None, progress_queue=queue,
+ log_file_base=None,
+ progress_queue=queue,
cancellation=ExecutionCancellationSignal(),
).create(runtime_environment=runtime, actual_max_workers=2)
contexts = _compiled_custom_contexts(payload, runtime)
try:
with resources.execution_context():
tasks = tuple(
- resources.executor.submit(_spawned_compiled_custom_task, lane_context, helpers)
+ resources.executor.submit(
+ _spawned_compiled_custom_task, lane_context, helpers
+ )
for lane_context, (_reference, helpers) in zip(
contexts.values(), payload, strict=True
)
@@ -547,7 +568,10 @@ class RichProgressContext(ProgressExecutionContext):
assert values == [[0, 1, 2], [3, 4, 5]]
assert type(row) is helpers[4] and row == helpers[5]
pid, rows = resources.executor.submit(
- _spawned_custom_task, payload, context.execution_id, context.subject_id,
+ _spawned_custom_task,
+ payload,
+ context.execution_id,
+ context.subject_id,
).result(timeout=15)
assert pid != os.getpid()
for row, (_reference, helpers) in zip(rows, payload, strict=True):
@@ -633,10 +657,20 @@ def test_persisted_payload_preserves_nominal_helpers_in_independent_consumer(
def test_persisted_custom_revision_executes_in_original_spawn_factory(
- tmp_path, transport_environment,
+ tmp_path,
+ transport_environment,
):
- print(_run_child(transport_environment, "spawn", str(tmp_path / "spawn.pkl"),
- "reference", "cold", "unchanged"), end="")
+ print(
+ _run_child(
+ transport_environment,
+ "spawn",
+ str(tmp_path / "spawn.pkl"),
+ "reference",
+ "cold",
+ "unchanged",
+ ),
+ end="",
+ )
@pytest.mark.parametrize("mutation", ["changed", "deleted", "renamed"])
diff --git a/tests/integration/test_measured_cli_session.py b/tests/integration/test_measured_cli_session.py
index 7eb363e046..27ae7a7509 100644
--- a/tests/integration/test_measured_cli_session.py
+++ b/tests/integration/test_measured_cli_session.py
@@ -78,7 +78,37 @@ def _source_state(root: Path) -> dict[str, tuple]:
return state
+# Everyone may not create, change or delete entries; inherited by the tree.
+# Write rights are named one by one: the generic W also denies SYNCHRONIZE,
+# which every open (reads included) requests.
+# Deny adding files and folders to the root itself. An inherited deny over the
+# tree (OI)(CI) also refused reading the source files on the CI runner.
+WINDOWS_READ_ONLY_ACE = "*S-1-1-0:(WD,AD)"
+
+if sys.platform == "win32":
+ # A console's Ctrl+C cannot be sent to one process; Ctrl+Break can, to a
+ # child started in its own process group.
+ INTERRUPT_LAUNCH = {"creationflags": subprocess.CREATE_NEW_PROCESS_GROUP}
+ INTERRUPT_SIGNAL = signal.CTRL_BREAK_EVENT
+else:
+ INTERRUPT_LAUNCH = {}
+ INTERRUPT_SIGNAL = signal.SIGINT
+
+
def _set_writable(root: Path, writable: bool) -> None:
+ """Make ``root`` and its tree writable again, or genuinely not writable.
+
+ Windows ignores the read-only attribute on directories (and os.access
+ ignores ACLs), so there a deny ACE on the root refuses creating files in it;
+ the before/after source state still catches any write below it.
+ """
+
+ if sys.platform == "win32":
+ change = (
+ ("/remove:d", "*S-1-1-0") if writable else ("/deny", WINDOWS_READ_ONLY_ACE)
+ )
+ subprocess.run(("icacls", str(root), *change), check=True, capture_output=True)
+ return
for path in (root, *root.rglob("*")):
mode = path.stat().st_mode
path.chmod(mode | 0o200 if writable else mode & ~0o222)
@@ -114,7 +144,9 @@ def _command(
def _environment(tmp_path: Path) -> dict[str, str]:
- environment = {**os.environ, "PYTHONPATH": str(REPOSITORY_ROOT)}
+ # The CLI child imports benchmark and openhcs from the same installation as
+ # this test process; a checkout path would shadow an installed candidate.
+ environment = dict(os.environ)
environment.setdefault("XDG_DATA_HOME", str(tmp_path / "xdg"))
environment.pop("DISPLAY", None)
return environment
@@ -176,6 +208,7 @@ def test_ctrl_c_stops_the_run_and_its_server(tmp_path: Path) -> None:
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True,
+ **INTERRUPT_LAUNCH,
)
stderr_lines: list[str] = []
try:
@@ -186,7 +219,7 @@ def test_ctrl_c_stops_the_run_and_its_server(tmp_path: Path) -> None:
else:
pytest.fail("".join(stderr_lines)[-4000:])
time.sleep(1.0)
- process.send_signal(signal.SIGINT)
+ process.send_signal(INTERRUPT_SIGNAL)
_stdout, rest = process.communicate(timeout=RUN_DEADLINE_SECONDS)
finally:
if process.poll() is None:
@@ -211,7 +244,13 @@ def test_read_only_source_plate_completes_and_stays_byte_identical(
port = _port(2)
try:
completed = subprocess.run(
- _command(plate, source, output_dir, port, wait_ms=int(RUN_DEADLINE_SECONDS * 1000)),
+ _command(
+ plate,
+ source,
+ output_dir,
+ port,
+ wait_ms=int(RUN_DEADLINE_SECONDS * 1000),
+ ),
cwd=REPOSITORY_ROOT,
env=_environment(tmp_path),
capture_output=True,
diff --git a/tests/integration/test_neurite_outgrowth_synthetic_plate.py b/tests/integration/test_neurite_outgrowth_synthetic_plate.py
index 4192e66170..18598a890d 100644
--- a/tests/integration/test_neurite_outgrowth_synthetic_plate.py
+++ b/tests/integration/test_neurite_outgrowth_synthetic_plate.py
@@ -67,6 +67,7 @@
ViewerStreamBackendCallKwargs,
)
from openhcs.processing.backends.analysis.neurite_outgrowth import (
+ CellProfilerNeuriteEngineProfile,
MetaXpressCellBodySettings,
MetaXpressNuclearSettings,
MetaXpressOutgrowthSettings,
@@ -94,13 +95,13 @@ def _write_known_neurite_images(plate_dir):
image_dir = plate_dir / "TimePoint_1"
for site in (1, 2):
- tifffile.imwrite(image_dir / f"A01_s{site:03d}_w1_z001_t001.tif", neurites)
- tifffile.imwrite(image_dir / f"A01_s{site:03d}_w2_z001_t001.tif", nuclei)
+ tifffile.imwrite(image_dir / f"A01_s{site:03d}_w1.tif", neurites)
+ tifffile.imwrite(image_dir / f"A01_s{site:03d}_w2.tif", nuclei)
return np.stack((neurites, nuclei))
def test_neurite_outgrowth_runs_on_synthetic_plate_as_2d_channel_stack(
- tmp_path, caplog, monkeypatch
+ tmp_path, caplog, monkeypatch, viewer_ack_return_route
):
caplog.set_level(logging.CRITICAL)
plate_dir = tmp_path / "synthetic_neurite_plate"
@@ -220,9 +221,7 @@ def observe_dense_output(output, components):
progress_events = []
while True:
try:
- progress_events.append(
- progress_queue.get_nowait()
- )
+ progress_events.append(progress_queue.get_nowait())
except queue.Empty:
break
live_payloads = tuple(
@@ -311,19 +310,14 @@ def observe_dense_output(output, components):
np.testing.assert_array_equal(retained, expected_labels)
checkpoint_paths = []
+ # Checkpoints are the declared outputs between the label artifacts and
+ # the trailing morphology table; the callable's output family owns them.
+ checkpoint_names = tuple(
+ spec.name
+ for spec in CellProfilerNeuriteEngineProfile.artifact_outputs()[6:-1]
+ )
for artifact_name, expected_checkpoint in zip(
- (
- "neurite_candidate_mask",
- "neurite_unrooted_residual",
- "neurite_secondary_ownership",
- "neurite_topology_dropped_trace",
- "neurite_topology_added_trace",
- "neurite_enhanced_response",
- "neurite_threshold_support",
- "neurite_retained_support",
- "neurite_local_response",
- "neurite_local_support",
- ),
+ checkpoint_names,
expected[7:-1],
strict=True,
):
@@ -336,7 +330,9 @@ def observe_dense_output(output, components):
retained,
np.asarray(expected_checkpoint)[0],
)
- assert len({path.name for path in checkpoint_paths}) == 20
+ assert len({path.name for path in checkpoint_paths}) == 2 * len(
+ checkpoint_names
+ )
assert compiled_plan.output_dataset_root is not None
output_dataset_root = Path(compiled_plan.output_dataset_root)
@@ -368,7 +364,8 @@ def observe_dense_output(output, components):
# Exercise the real stream projection of the compiled writer outputs.
# Artifact storage axes are legitimately empty; source image planes are not.
- assert len(dense_outputs) == 18
+ # Both sites publish four label images and every declared checkpoint.
+ assert len(dense_outputs) == 2 * (4 + len(checkpoint_names))
stream_kwargs = ViewerStreamBackendCallKwargs(
ViewerStreamBackendKwargs(
ViewerStreamRequest(
@@ -424,13 +421,16 @@ def observe_dense_output(output, components):
request
),
display_payload_extra=backend.display_payload_extra(request),
+ return_route=viewer_ack_return_route,
),
)
for output, item in zip(outputs, batch.batch_images, strict=True):
assert output.variable_components == ()
- # This fixture injects physical pixel_size into the callable,
- # but does not declare voxel spacing on its runtime images.
- assert output.metadata.source_voxel_spacing == SourceVoxelSpacing()
+ # The acquisition's calibration is the same physical pixel
+ # size the compiler injected into the callable.
+ assert output.metadata.source_voxel_spacing == SourceVoxelSpacing(
+ (compiled_invocation.kwargs_dict["pixel_size"],) * 2
+ )
if checkpoint_batch:
assert "_w1_" in Path(output.path).name
else:
diff --git a/tests/integration/test_session_journey.py b/tests/integration/test_session_journey.py
index fe7966d343..f480cb2fcd 100644
--- a/tests/integration/test_session_journey.py
+++ b/tests/integration/test_session_journey.py
@@ -1,8 +1,9 @@
-"""One dataset through the session, headless over MCP and in the Qt GUI.
+"""One dataset through the session, headless over MCP.
-Both journeys add a synthetic dataset, give it a pipeline, initialize, compile
-and run it on a real execution server, following the session's events. They
-use the same SessionOperation classes; only the client differs.
+The journey adds a synthetic dataset, gives it a pipeline, initializes,
+compiles and runs it on a real execution server, following the session's
+events. tests/pyqt_gui/test_session_gui_journey.py runs the same
+SessionOperation classes through the Qt widgets.
"""
from __future__ import annotations
@@ -18,12 +19,9 @@
from openhcs.agent.path_policy import AgentPathPolicy
from openhcs.authoring.session.operations.datasets import (
AddDatasets,
- CompileDatasets,
InitializeDatasets,
- RunDatasets,
)
from openhcs.authoring.session.session import CallerThread
-from openhcs.authoring.session.views import DatasetListView
from openhcs.core.config import PipelineConfig
from openhcs.core.pipeline_document import PipelineDocumentCodec
from openhcs.core.steps import FunctionStep
@@ -147,96 +145,18 @@ def test_headless_mcp_journey_adds_compiles_and_runs_a_dataset(tmp_path: Path) -
{"after_sequence": 0, "timeout_seconds": 0.0},
)["events"]
}
- assert {"DatasetsChanged", "CompiledStateChanged", "DatasetExecutionFinished"} <= kinds
+ assert {
+ "DatasetsChanged",
+ "CompiledStateChanged",
+ "DatasetExecutionFinished",
+ } <= kinds
finally:
context.session.close()
-def test_gui_journey_runs_the_same_operations_through_the_widgets(
- tmp_path: Path,
-) -> None:
- pytest.importorskip("PyQt6")
- from PyQt6.QtWidgets import QApplication
- from pyqt_reactive.services.ui_thread_dispatch import UiThreadDispatcher
-
- from openhcs.authoring.session.session import DispatcherThread, Session
- from openhcs.core.config import GlobalPipelineConfig
- from openhcs.pyqt_gui.config import get_default_ui_config
- from openhcs.pyqt_gui.widgets.plate_manager import PlateManagerWidget
- from tests.unit.pyqt_gui.session_harness import GuiServiceStub, release_widgets
-
- application = QApplication.instance() or QApplication([])
- root, source = _synthetic_dataset(tmp_path)
- dispatcher = UiThreadDispatcher()
- session = Session(
- transport_config=OpenHCSZMQConfig(default_port=_port(1), persistent=False),
- global_config=GlobalPipelineConfig(),
- main_thread=DispatcherThread(dispatcher),
- )
- services = GuiServiceStub()
- manager = PlateManagerWidget(
- services, session, gui_config=get_default_ui_config()
- )
- try:
- assert session.invoke(AddDatasets, AddDatasets.request(roots=(str(root),))).accepted
- (scope_id,) = session.dataset_scope_ids()
- session.set_pipeline_source(scope_id, source)
- application.processEvents()
- assert [row.scope_id for row in manager.plates] == [scope_id]
- manager.item_list.setCurrentRow(0)
- application.processEvents()
- assert manager.selection_scope_ids() == (scope_id,)
-
- def wait_until(condition, label) -> None:
- deadline = time.monotonic() + STAGE_TIMEOUT_SECONDS
- while not condition():
- application.processEvents()
- assert time.monotonic() < deadline, label
- time.sleep(0.05)
-
- for operation, finished in (
- (InitializeDatasets, lambda row: row.initialized and not row.init_pending),
- (CompileDatasets, lambda row: row.compiled and not row.compile_pending),
- (
- RunDatasets,
- lambda row: row.terminal_status is not None and not row.execution_active,
- ),
- ):
- if operation is CompileDatasets:
- # Disconnected, the compile button is Connect; connect first.
- assert manager.buttons[operation.operation_id].text() == "Connect"
- manager.handle_button_action(operation.operation_id)
- wait_until(
- lambda: CompileDatasets.resolved(session) is CompileDatasets,
- "connect",
- )
- manager.update_button_states()
- assert manager.buttons[operation.operation_id].isEnabled(), operation
- manager.handle_button_action(operation.operation_id)
- deadline = time.monotonic() + STAGE_TIMEOUT_SECONDS
- while True:
- application.processEvents()
- (row,) = DatasetListView.state_of(session).rows
- if finished(row):
- break
- assert time.monotonic() < deadline, (operation, row)
- time.sleep(0.05)
- assert row.terminal_status == "complete", row
- wait_until(lambda: not session.execution_state.busy, "batch finished")
- application.processEvents()
- assert manager.buttons[RunDatasets.operation_id].text() == "Run"
- finally:
- manager.cleanup()
- manager.close()
- release_widgets(application, manager)
- session.close()
- dispatcher.close()
-
-
def test_read_only_dataset_initializes_in_a_mirrored_workspace(tmp_path: Path) -> None:
"""Initialization never writes into a source it may not write."""
- from openhcs.authoring.session.operations.datasets import AddDatasets
from openhcs.authoring.session.session import Session
from openhcs.core.config import GlobalPipelineConfig
@@ -256,7 +176,9 @@ def test_read_only_dataset_initializes_in_a_mirrored_workspace(tmp_path: Path) -
workspace_root=workspaces,
)
try:
- assert session.invoke(AddDatasets, AddDatasets.request(roots=(str(root),))).accepted
+ assert session.invoke(
+ AddDatasets, AddDatasets.request(roots=(str(root),))
+ ).accepted
(scope_id,) = session.dataset_scope_ids()
started = session.invoke(
InitializeDatasets, InitializeDatasets.request(scope_ids=(scope_id,))
@@ -272,7 +194,7 @@ def test_read_only_dataset_initializes_in_a_mirrored_workspace(tmp_path: Path) -
assert not failures, failures
assert time.monotonic() < deadline
time.sleep(0.05)
- assert session.orchestrator(scope_id).plate_path.is_relative_to(workspaces)
+ assert session.orchestrator(scope_id).dataset_root.is_relative_to(workspaces)
assert _source_state(root) == before
finally:
session.close()
diff --git a/tests/pyqt_gui/integration/test_end_to_end_workflow_foundation.py b/tests/pyqt_gui/integration/test_end_to_end_workflow_foundation.py
index 454a2e3aef..daf7a9e30d 100644
--- a/tests/pyqt_gui/integration/test_end_to_end_workflow_foundation.py
+++ b/tests/pyqt_gui/integration/test_end_to_end_workflow_foundation.py
@@ -37,6 +37,11 @@
)
from pyqt_reactive.forms.parameter_form_manager import ParameterFormManager
+from openhcs.authoring.session.operations.datasets import (
+ AddDatasets,
+ EditDatasetConfig,
+ InitializeDatasets,
+)
from openhcs.core.config import (
GlobalPipelineConfig,
LazyPathPlanningConfig,
@@ -227,7 +232,9 @@ def materialization_config(
),
),
setup_modifications=(
- FieldModificationSpec(("step_partition_filter_config", "partition_filter"), 5),
+ FieldModificationSpec(
+ ("step_partition_filter_config", "partition_filter"), 5
+ ),
),
field_to_test=FieldModificationSpec(
("path_planning_config", "partition_filter"), 99
@@ -240,7 +247,9 @@ def materialization_config(
("napari_streaming_config", "partition_filter"),
("fiji_streaming_config", "partition_filter"),
),
- excluded_source_paths=(("path_planning_config", "partition_filter"),),
+ excluded_source_paths=(
+ ("path_planning_config", "partition_filter"),
+ ),
),
),
),
@@ -630,8 +639,12 @@ def _access_plate_manager(context: WorkflowContext) -> WorkflowContext:
def _add_and_select_plate(context: WorkflowContext) -> WorkflowContext:
- """Add synthetic plate and select it in plate manager."""
- context.plate_manager_widget.add_plate_callback([context.synthetic_plate_dir])
+ """Add synthetic plate through the session and select it in plate manager."""
+ result = context.plate_manager_widget.session.invoke(
+ AddDatasets,
+ AddDatasets.request(roots=(str(context.synthetic_plate_dir),)),
+ )
+ assert result.accepted, result.errors
_wait_for_gui(TIMING.ACTION_DELAY)
item_list = context.plate_manager_widget.item_list
@@ -647,14 +660,21 @@ def _add_and_select_plate(context: WorkflowContext) -> WorkflowContext:
def _initialize_plate(context: WorkflowContext) -> WorkflowContext:
"""Initialize plate using Init button."""
- init_button = context.plate_manager_widget.buttons["init_plate"]
+ plate_manager = context.plate_manager_widget
+ init_button = plate_manager.buttons[InitializeDatasets.operation_id]
if not init_button.isEnabled():
raise AssertionError(
"Init button is disabled - plate may not be properly added"
)
init_button.click()
- _wait_for_gui(TIMING.SAVE_DELAY)
+ scope_id = plate_manager.selected_plate_path
+ session = plate_manager.session
+ deadline = time.monotonic() + 60.0
+ while not session.is_initialized(scope_id) or scope_id in session.init_pending:
+ if time.monotonic() >= deadline:
+ raise AssertionError(f"Dataset {scope_id!r} did not finish initializing")
+ _wait_for_gui(0.05)
return context
@@ -665,9 +685,8 @@ def _apply_orchestrator_config(context: WorkflowContext) -> WorkflowContext:
"Test scenario must be provided for parameterized orchestrator configuration"
)
- orchestrator = context.plate_manager_widget.get_selected_orchestrator()
- if orchestrator is None:
- raise AssertionError("The selected plate has no initialized orchestrator")
+ plate_manager = context.plate_manager_widget
+ orchestrator = plate_manager.session.orchestrator(plate_manager.selected_plate_path)
orchestrator.apply_pipeline_config(context.test_scenario.pipeline_config)
_wait_for_gui(TIMING.ACTION_DELAY)
@@ -709,7 +728,7 @@ def _wait_for_form_build(
)
def _open_config_window(context: WorkflowContext) -> WorkflowContext:
"""Open plate-specific configuration window."""
- edit_button = context.plate_manager_widget.buttons["edit_config"]
+ edit_button = context.plate_manager_widget.buttons[EditDatasetConfig.operation_id]
if not edit_button.isEnabled():
raise AssertionError(
"Edit button is disabled - plate may not be properly initialized"
@@ -891,7 +910,7 @@ def _reopen_config_window(context: WorkflowContext) -> WorkflowContext:
context = _close_config_window(context)
# Validate edit button state
- edit_button = context.plate_manager_widget.buttons["edit_config"]
+ edit_button = context.plate_manager_widget.buttons[EditDatasetConfig.operation_id]
if not edit_button.isEnabled():
raise AssertionError(
"LAZY CONFIG BUG: Edit button disabled after closing config window. "
diff --git a/tests/pyqt_gui/test_image_browser_filter_controller.py b/tests/pyqt_gui/test_image_browser_filter_controller.py
index fb5d82e26b..0ba8845dab 100644
--- a/tests/pyqt_gui/test_image_browser_filter_controller.py
+++ b/tests/pyqt_gui/test_image_browser_filter_controller.py
@@ -20,6 +20,7 @@
from openhcs.core.dataset_sources.interfaces import (
AnalysisResultDirectory,
FilenameParserCapability,
+ MetadataHandler,
)
from openhcs.pyqt_gui.widgets.image_browser import (
ImageBrowserFilterController,
@@ -126,7 +127,9 @@ def parse_filename(self, filename: str):
)
-class _InventoryMetadataHandler:
+class _InventoryMetadataHandler(MetadataHandler):
+ """Inherits the handler defaults (workspace root, no virtual workspace)."""
+
def __init__(self, handler_result_dir: Path) -> None:
self._handler_result_dir = handler_result_dir
@@ -134,11 +137,17 @@ def get_image_files(self, _plate_path: Path, *, all_subdirs: bool):
assert all_subdirs is True
return ("images/A01_w1.tif",)
- def source_workspace_metadata_document(self, _plate_path: Path):
- return None
+ def find_metadata_file(self, _plate_path: Path) -> Path:
+ raise AssertionError("Inventory does not read the metadata file.")
- def source_dataset(self, _plate_path: Path):
- return None
+ def get_grid_dimensions(self, _plate_path: Path):
+ raise AssertionError("Inventory does not read grid dimensions.")
+
+ def get_pixel_size(self, _plate_path: Path):
+ raise AssertionError("Inventory does not read pixel size.")
+
+ def component_value_set(self, _plate_path: Path):
+ raise AssertionError("Inventory does not read the component value set.")
def analysis_result_directories(self, _plate_path: Path):
return (
diff --git a/tests/pyqt_gui/test_managed_windows.py b/tests/pyqt_gui/test_managed_windows.py
index b82f64d0a9..e167e980d3 100644
--- a/tests/pyqt_gui/test_managed_windows.py
+++ b/tests/pyqt_gui/test_managed_windows.py
@@ -33,7 +33,7 @@ def __init__(self, orchestrator: _OrchestratorStub) -> None:
self._orchestrator = orchestrator
self.plate_selected = _Signal()
- def get_selected_orchestrator(self) -> _OrchestratorStub:
+ def _get_current_orchestrator(self) -> _OrchestratorStub:
return self._orchestrator
def select(self, orchestrator: _OrchestratorStub) -> None:
@@ -141,12 +141,14 @@ class PlateManagerStub(QWidget):
def __init__(
self,
service_adapter,
+ session,
color_scheme,
*,
gui_config,
) -> None:
super().__init__()
del service_adapter, color_scheme
+ captured["session"] = session
captured["gui_config"] = gui_config
def cleanup(self) -> None:
@@ -159,11 +161,13 @@ def cleanup(self) -> None:
"PlateManagerWidget",
PlateManagerStub,
)
- monkeypatch.setattr(PlateManagerWindow, "_setup_connections", lambda self: None)
+ session = object()
main_window = QWidget()
+ main_window.session = session
window = PlateManagerWindow(main_window, ServiceAdapterStub())
qtbot.addWidget(main_window)
qtbot.addWidget(window)
assert captured["gui_config"] is resolved_ui_config
+ assert captured["session"] is session
diff --git a/tests/pyqt_gui/test_session_gui_journey.py b/tests/pyqt_gui/test_session_gui_journey.py
new file mode 100644
index 0000000000..aad333283c
--- /dev/null
+++ b/tests/pyqt_gui/test_session_gui_journey.py
@@ -0,0 +1,119 @@
+"""One dataset through the session, driven through the Qt widgets.
+
+The same SessionOperation classes as the headless MCP journey in
+tests/integration/test_session_journey.py run here through the plate manager
+on a real execution server; only the client differs.
+"""
+
+from __future__ import annotations
+
+import time
+from pathlib import Path
+
+import pytest
+from objectstate.object_state import ObjectStateRegistry
+from PyQt6.QtWidgets import QApplication
+from pyqt_reactive.services.ui_thread_dispatch import UiThreadDispatcher
+
+from openhcs.authoring.session.operations.datasets import (
+ AddDatasets,
+ CompileDatasets,
+ InitializeDatasets,
+ RunDatasets,
+)
+from openhcs.authoring.session.session import DispatcherThread, Session
+from openhcs.authoring.session.views import DatasetListView
+from openhcs.core.config import GlobalPipelineConfig
+from openhcs.pyqt_gui.config import get_default_ui_config
+from openhcs.pyqt_gui.widgets.plate_manager import PlateManagerWidget
+from openhcs.runtime.zmq_config import OpenHCSZMQConfig
+from tests.integration.test_session_journey import (
+ STAGE_TIMEOUT_SECONDS,
+ _port,
+ _synthetic_dataset,
+)
+from tests.unit.pyqt_gui.session_harness import GuiServiceStub, release_widgets
+
+
+@pytest.fixture(autouse=True)
+def fresh_object_state_registry():
+ """The session starts from an empty dataset list."""
+
+ ObjectStateRegistry.clear()
+ yield
+ ObjectStateRegistry.clear()
+
+
+def test_gui_journey_runs_the_same_operations_through_the_widgets(
+ tmp_path: Path,
+) -> None:
+ application = QApplication.instance() or QApplication([])
+ root, source = _synthetic_dataset(tmp_path)
+ dispatcher = UiThreadDispatcher()
+ session = Session(
+ transport_config=OpenHCSZMQConfig(default_port=_port(1), persistent=False),
+ global_config=GlobalPipelineConfig(),
+ main_thread=DispatcherThread(dispatcher),
+ )
+ services = GuiServiceStub()
+ manager = PlateManagerWidget(services, session, gui_config=get_default_ui_config())
+ try:
+ assert session.invoke(
+ AddDatasets, AddDatasets.request(roots=(str(root),))
+ ).accepted
+ (scope_id,) = session.dataset_scope_ids()
+ session.set_pipeline_source(scope_id, source)
+ application.processEvents()
+ assert [row.scope_id for row in manager.plates] == [scope_id]
+ manager.item_list.setCurrentRow(0)
+ application.processEvents()
+ assert manager.selection_scope_ids() == (scope_id,)
+
+ def wait_until(condition, label) -> None:
+ deadline = time.monotonic() + STAGE_TIMEOUT_SECONDS
+ while not condition():
+ application.processEvents()
+ assert time.monotonic() < deadline, label
+ time.sleep(0.05)
+
+ for operation, finished in (
+ (InitializeDatasets, lambda row: row.initialized and not row.init_pending),
+ (CompileDatasets, lambda row: row.compiled and not row.compile_pending),
+ (
+ RunDatasets,
+ lambda row: row.terminal_status is not None
+ and not row.execution_active,
+ ),
+ ):
+ if operation is CompileDatasets:
+ # Disconnected, the compile button is Connect; connect first.
+ assert manager.buttons[operation.operation_id].text() == "Connect"
+ manager.handle_button_action(operation.operation_id)
+ wait_until(
+ lambda: CompileDatasets.resolved(session) is CompileDatasets,
+ "connect",
+ )
+ manager.update_button_states()
+ # The previous stage's batch ends after its row reports done.
+ wait_until(lambda: not session.execution_state.busy, "batch finished")
+ manager.update_button_states()
+ assert manager.buttons[operation.operation_id].isEnabled(), operation
+ manager.handle_button_action(operation.operation_id)
+ deadline = time.monotonic() + STAGE_TIMEOUT_SECONDS
+ while True:
+ application.processEvents()
+ (row,) = DatasetListView.state_of(session).rows
+ if finished(row):
+ break
+ assert time.monotonic() < deadline, (operation, row)
+ time.sleep(0.05)
+ assert row.terminal_status == "complete", row
+ wait_until(lambda: not session.execution_state.busy, "batch finished")
+ application.processEvents()
+ assert manager.buttons[RunDatasets.operation_id].text() == "Run"
+ finally:
+ manager.cleanup()
+ manager.close()
+ release_widgets(application, manager)
+ session.close()
+ dispatcher.close()
diff --git a/tests/unit/agent/test_capabilities.py b/tests/unit/agent/test_capabilities.py
index eacfe31e20..6cdc70a27a 100644
--- a/tests/unit/agent/test_capabilities.py
+++ b/tests/unit/agent/test_capabilities.py
@@ -121,8 +121,7 @@ def test_capability_transport_defaults_to_local_stdio():
def test_capability_exposition_owns_its_json_projection():
capability = next(
- capability
- for capability in get_capability_registry().capabilities
+ capability for capability in get_capability_registry().capabilities
)
exposition = capability.exposition
@@ -294,9 +293,15 @@ def test_custom_function_registration_is_a_desktop_authoring_mutation():
}
registration = full_capabilities["openhcs_register_custom_function"]
- assert registration.exposition.workflow_group is CapabilityWorkflowGroup.FUNCTION_AUTHORING
+ assert (
+ registration.exposition.workflow_group
+ is CapabilityWorkflowGroup.FUNCTION_AUTHORING
+ )
assert registration.exposition.workflow_stage is CapabilityWorkflowStage.AUTHORING
- assert registration.exposition.target_context is CapabilityTargetContext.FUNCTION_REGISTRY
+ assert (
+ registration.exposition.target_context
+ is CapabilityTargetContext.FUNCTION_REGISTRY
+ )
assert registration.exposition.visibility is CapabilityVisibility.STANDARD
assert registration.exposition.role is CapabilityRole.PRIMARY
assert registration.mutating is True
@@ -344,7 +349,12 @@ def _declaration_error(**attributes) -> Exception:
type(
"RejectedDeclaration",
(UiBridgeCapability,),
- {"name": name, "title": "Rejected", "description": "", "service": "test"}
+ {
+ "name": name,
+ "title": "Rejected",
+ "description": "",
+ "service": "test",
+ }
| attributes,
)
finally:
@@ -407,9 +417,9 @@ def test_similar_mcp_tool_names_are_disambiguated_by_target_context_and_role():
assert capabilities["openhcs_sample_plate_image"].exposition.target_context is (
CapabilityTargetContext.PLATE_PATH
)
- assert capabilities["openhcs_ui_sample_selected_plate_image"].exposition.target_context is (
- CapabilityTargetContext.UI_SELECTED_PLATE
- )
+ assert capabilities[
+ "openhcs_ui_sample_selected_plate_image"
+ ].exposition.target_context is (CapabilityTargetContext.UI_SELECTED_PLATE)
assert capabilities["openhcs_ui_sample_selected_plate_image"].exposition.role is (
CapabilityRole.MODE_VARIANT
)
@@ -420,20 +430,25 @@ def test_similar_mcp_tool_names_are_disambiguated_by_target_context_and_role():
CapabilityTargetContext.HEADLESS_SESSION
)
assert (
- capabilities["openhcs_get_runtime_server_execution_status"].exposition.target_context
+ capabilities[
+ "openhcs_get_runtime_server_execution_status"
+ ].exposition.target_context
is CapabilityTargetContext.RUNTIME_SERVER
)
- assert capabilities["openhcs_ui_get_operation_status"].exposition.target_context is (
- CapabilityTargetContext.UI_BRIDGE
- )
- assert capabilities["openhcs_ui_wait_for_operation_receipt"].exposition.target_context is (
- CapabilityTargetContext.UI_BRIDGE
- )
+ assert capabilities[
+ "openhcs_ui_get_operation_status"
+ ].exposition.target_context is (CapabilityTargetContext.UI_BRIDGE)
+ assert capabilities[
+ "openhcs_ui_wait_for_operation_receipt"
+ ].exposition.target_context is (CapabilityTargetContext.UI_BRIDGE)
assert (
capabilities["openhcs_ui_wait_for_operation_receipt"].exposition.role
is CapabilityRole.PRIMARY
)
- assert capabilities["openhcs_ui_invoke_action"].exposition.role is CapabilityRole.PRIMARY
+ assert (
+ capabilities["openhcs_ui_invoke_action"].exposition.role
+ is CapabilityRole.PRIMARY
+ )
assert capabilities["openhcs_ui_invoke_widget_action"].exposition.role is (
CapabilityRole.FALLBACK
)
@@ -446,9 +461,9 @@ def test_similar_mcp_tool_names_are_disambiguated_by_target_context_and_role():
assert capabilities["openhcs_validate_pipeline"].exposition.workflow_stage is (
CapabilityWorkflowStage.VALIDATION
)
- assert capabilities["openhcs_ui_mutate_object_state_field"].exposition.visibility is (
- CapabilityVisibility.EXPERT
- )
+ assert capabilities[
+ "openhcs_ui_mutate_object_state_field"
+ ].exposition.visibility is (CapabilityVisibility.EXPERT)
def test_ui_bridge_capabilities_declare_runtime_security_and_data_exposure():
@@ -528,3 +543,27 @@ def test_connection_shape_is_declared_by_the_invocation_family():
):
assert issubclass(capability, RuntimeServerCapability)
assert isinstance(capability.invocation, AgentFromFieldsServiceInvocation)
+
+
+def test_requestless_ui_bridge_capability_passes_connection_as_connection():
+ """A requestless operation's only argument is the connection, never a request."""
+
+ from types import SimpleNamespace
+
+ from openhcs.agent.services.ui_bridge_service import UiBridgeService
+
+ seen = []
+
+ class RecordingUiBridgeService(UiBridgeService):
+ def invoke(self, operation, request=None, connection=None):
+ seen.append((operation, request, connection))
+ return "served"
+
+ connection = object()
+ invocation = agent_capabilities.ui_bridge_status.invocation
+ context = SimpleNamespace(
+ ui_bridge_service=RecordingUiBridgeService(gateway=object())
+ )
+
+ assert invocation.execute(context, connection) == "served"
+ assert seen == [(invocation.operation, None, connection)]
diff --git a/tests/unit/agent/test_knowledge_base_service.py b/tests/unit/agent/test_knowledge_base_service.py
index f636461b22..4438f7a24e 100644
--- a/tests/unit/agent/test_knowledge_base_service.py
+++ b/tests/unit/agent/test_knowledge_base_service.py
@@ -128,8 +128,7 @@ def test_search_prefers_declared_document_matches_over_equal_body_matches(tmp_pa
(docs / "notes.md").write_text(
"# Setup notes\n\n"
+ "\n".join(
- f"## Note {index}\n\nThis body mentions a recipe.\n"
- for index in range(8)
+ f"## Note {index}\n\nThis body mentions a recipe.\n" for index in range(8)
),
encoding="utf-8",
)
@@ -314,14 +313,19 @@ def test_knowledge_base_retrieves_canonical_image_source_guide():
assert "CZI" in document.content
assert "OME-TIFF" in document.content
assert "OME-Zarr" in document.content
- assert "WELL" in document.content
- assert "SITE" in document.content
- assert "CHANNEL" in document.content
- assert "Z_INDEX" in document.content
- assert "TIMEPOINT" in document.content
+ from openhcs.domains.microscopy.axes import Microscopy
+
+ for axis in (
+ Microscopy.Well,
+ Microscopy.Site,
+ Microscopy.Channel,
+ Microscopy.ZIndex,
+ Microscopy.Timepoint,
+ ):
+ assert f"Microscopy.{axis.__name__}" in document.content
assert "Binding positions are not microscope channel values" in document.content
assert "full named physical" in document.content
- assert "inputs consumed by the nominal handler projection" in document.content
+ assert "inputs consumed by the dataset source projection" in document.content
assert "Seeing all three in the source workspace inventory is valid" in (
document.content
)
@@ -348,8 +352,9 @@ def test_knowledge_base_retrieves_canonical_image_source_guide():
assert "``neurite_channel_index=0``" in document.content
assert "``nuclear_stain.channel_index=1``" in document.content
assert "not equivalent to MAP2-seeded analysis" in document.content
- assert "``variable_components=[CHANNEL]`` assembles the channel stack" in (
- document.content
+ assert (
+ "``variable_components=[Microscopy.Channel]`` assembles the channel stack"
+ in (document.content)
)
assert "compiler normalizes it to" in document.content
assert "``Ungrouped``" in document.content
diff --git a/tests/unit/agent/test_mcp_dev_client_sampling_results.py b/tests/unit/agent/test_mcp_dev_client_sampling_results.py
index ffb7621857..49b08ecc47 100644
--- a/tests/unit/agent/test_mcp_dev_client_sampling_results.py
+++ b/tests/unit/agent/test_mcp_dev_client_sampling_results.py
@@ -14,16 +14,28 @@
from openhcs.agent.capabilities import SamplePlateImageCapability, agent_capabilities
from openhcs.agent.dto.common import AgentError, SCHEMA_VERSION
-from openhcs.agent.dto.plate import PlateImageSampleResult, SelectedPlateImageSampleResult
-from openhcs.agent.dto.viewer import ViewerWindowImageSampleRecord, ViewerWindowImageSampleResult
+from openhcs.agent.dto.plate import (
+ PlateImageSampleResult,
+ SelectedPlateImageSampleResult,
+)
+from openhcs.agent.dto.viewer import (
+ ViewerWindowImageSampleRecord,
+ ViewerWindowImageSampleResult,
+)
from openhcs.mcp import dev_client
from openhcs.mcp.dev_client_commanding import CapabilityBackedCommandSpec
from openhcs.mcp.dev_client_core import (
- McpDevClientPhase, McpDevPayloadFailure, McpDevServerSpec,
- McpDevToolBatchResponse, McpDevToolResult,
+ McpDevClientPhase,
+ McpDevPayloadFailure,
+ McpDevServerSpec,
+ McpDevToolBatchResponse,
+ McpDevToolResult,
)
from openhcs.mcp.dev_client_rendering import McpDevOutputRenderer
-from openhcs.mcp.dev_client_renderers.plate import PlateImageSampleRenderer, SelectedPlateSampleRenderer
+from openhcs.mcp.dev_client_renderers.plate import (
+ PlateImageSampleRenderer,
+ SelectedPlateSampleRenderer,
+)
from openhcs.mcp.dev_client_renderers.viewer import ViewerImageSampleRenderer
from openhcs.runtime.viewer_protocol import ViewerArrayValueSummary
from python_introspect import to_jsonable
@@ -33,29 +45,57 @@
def forbid_native_or_provider(monkeypatch):
def forbidden(*args, **kwargs):
raise AssertionError("Source sampling controls must not spawn a process")
+
monkeypatch.setattr(subprocess, "Popen", forbidden)
monkeypatch.setattr(asyncio, "create_subprocess_exec", forbidden)
monkeypatch.setattr(asyncio, "create_subprocess_shell", forbidden)
def sample(**kwargs):
- return replace(PlateImageSampleResult(
- schema_version=SCHEMA_VERSION, plate_path="source-plate", requested_image_path="image.tif",
- virtual_path="images/image.tif", source_path="physical/image.tif",
- shape=(1, 2, 2), resolution_shape=(1, 2, 2), dtype="uint16",
- minimum=0, maximum=4, mean=2.5, selected_resolution_index=0,
- resolution_count=1, downsample_yx=(1.0, 1.0), statistics_scope="source_resolution",
- sample_shape=(1, 2, 2), sample_included=True, sample_values=[[[0, 2], [3, 4]]],
- ), **kwargs)
+ return replace(
+ PlateImageSampleResult(
+ schema_version=SCHEMA_VERSION,
+ plate_path="source-plate",
+ requested_image_path="image.tif",
+ virtual_path="images/image.tif",
+ source_path="physical/image.tif",
+ shape=(1, 2, 2),
+ resolution_shape=(1, 2, 2),
+ dtype="uint16",
+ minimum=0,
+ maximum=4,
+ mean=2.5,
+ selected_resolution_index=0,
+ resolution_count=1,
+ downsample_yx=(1.0, 1.0),
+ statistics_scope="source_resolution",
+ sample_shape=(1, 2, 2),
+ sample_included=True,
+ sample_values=[[[0, 2], [3, 4]]],
+ ),
+ **kwargs,
+ )
def selected_row():
return DatasetRowState(
- scope_id="selected-root", name="selection", root="selected-root",
- pipeline_path=None, selected=True, initialized=True, compiled=False,
- init_pending=False, compile_pending=False, execution_active=False,
- status_prefix="", orchestrator_state=None, execution_id=None,
- terminal_status=None, runtime_state=None, runtime_percent=None, queue_position=None,
+ scope_id="selected-root",
+ name="selection",
+ root="selected-root",
+ pipeline_path=None,
+ selected=True,
+ initialized=True,
+ compiled=False,
+ init_pending=False,
+ compile_pending=False,
+ execution_active=False,
+ status_prefix="",
+ orchestrator_state=None,
+ execution_id=None,
+ terminal_status=None,
+ runtime_state=None,
+ runtime_percent=None,
+ queue_position=None,
)
@@ -63,38 +103,53 @@ def selected(value):
return SelectedPlateImageSampleResult(
schema_version=SCHEMA_VERSION,
selected_plate=to_jsonable(selected_row()),
- image_path="image.tif", auto_selected_image_path=False, sample=value,
+ image_path="image.tif",
+ auto_selected_image_path=False,
+ sample=value,
)
def batch(value, capability):
receipt = {"isError": False, "structuredContent": to_jsonable(value), "content": []}
return McpDevToolBatchResponse.from_results(
- McpDevServerSpec(sys.executable), (McpDevToolResult.from_payload(capability.name, receipt),),
+ McpDevServerSpec(sys.executable),
+ (McpDevToolResult.from_payload(capability.name, receipt),),
)
def command_render(response, argv):
args = dev_client._build_parser().parse_args(argv)
- return dev_client.McpDevCommandSpec.for_name(args.command).render_result(response, args)
+ return dev_client.McpDevCommandSpec.for_name(args.command).render_result(
+ response, args
+ )
@pytest.mark.parametrize("structured", [False, True])
@pytest.mark.parametrize("selected_case", [False, True])
-def test_original_session_framing_to_generated_sampling_command(structured, selected_case, monkeypatch):
- from test_mcp_dev_client_pipeline_results import ControlledWireSession
+def test_original_session_framing_to_generated_sampling_command(
+ structured, selected_case, monkeypatch
+):
+ from tests.unit.agent.test_mcp_dev_client_pipeline_results import (
+ ControlledWireSession,
+ )
import openhcs.mcp.dev_client_core as ingress
value = selected(sample()) if selected_case else sample()
- argv = ("selected-plate-sample",) if selected_case else ("sample-plate-image", "source-plate", "image.tif")
+ argv = (
+ ("selected-plate-sample",)
+ if selected_case
+ else ("sample-plate-image", "source-plate", "image.tif")
+ )
args = dev_client._build_parser().parse_args(argv)
command = dev_client.McpDevCommandSpec.for_name(args.command)
peer = ControlledWireSession((value,), structured=structured)
decoded = []
original_decode = ingress.dataclass_from_mapping
+
def observe(contract, receipt):
decoded.append(contract)
return original_decode(contract, receipt)
+
monkeypatch.setattr(ingress, "dataclass_from_mapping", observe)
response = asyncio.run(command.run_session(peer, args))
assert decoded == [type(value)]
@@ -107,8 +162,14 @@ def observe(contract, receipt):
@pytest.mark.parametrize("selected_case", [False, True])
@pytest.mark.parametrize("generic_call", [False, True])
-def test_generated_commands_retain_decoded_sampling_facts(monkeypatch, selected_case, generic_call):
- capability = agent_capabilities.ui_sample_selected_plate_image if selected_case else agent_capabilities.sample_plate_image
+def test_generated_commands_retain_decoded_sampling_facts(
+ monkeypatch, selected_case, generic_call
+):
+ capability = (
+ agent_capabilities.ui_sample_selected_plate_image
+ if selected_case
+ else agent_capabilities.sample_plate_image
+ )
value = selected(sample()) if selected_case else sample()
response = batch(value, capability)
decoded = response.results[0].first_decoded_payload()
@@ -119,10 +180,17 @@ def test_generated_commands_retain_decoded_sampling_facts(monkeypatch, selected_
def forbidden(value):
raise AssertionError("Typed sampling must not flatten an owned result to JSON")
+
monkeypatch.setattr(dev_client, "to_jsonable", forbidden)
monkeypatch.setattr("python_introspect.jsonable.to_jsonable", forbidden)
- argv = ("call", capability.name, "--arguments", "{}") if generic_call else (
- ("selected-plate-sample",) if selected_case else ("sample-plate-image", "source-plate", "image.tif")
+ argv = (
+ ("call", capability.name, "--arguments", "{}")
+ if generic_call
+ else (
+ ("selected-plate-sample",)
+ if selected_case
+ else ("sample-plate-image", "source-plate", "image.tif")
+ )
)
rendered = command_render(response, argv)
assert "Image: images/image.tif" in rendered
@@ -131,23 +199,51 @@ def forbidden(value):
assert "mean=2.500" in rendered
assert "Sample values:" in rendered
if selected_case:
- assert "Selected plate: selection root=selected-root target=selected" in rendered
+ assert (
+ "Selected plate: selection root=selected-root target=selected" in rendered
+ )
assert "Selected image: image.tif auto=False" in rendered
@pytest.mark.parametrize("selected_case", [False, True])
-@pytest.mark.parametrize("reason,shape,hint", [
- ("max_array_elements_exceeded", (1, 8, 8), "--max-array-elements 64 or smaller --width/--height"),
- ("sample has 256 elements, above max_array_elements=40", (1, 16, 16), "--max-array-elements 256"),
- ("array_values_not_requested", (1, 2, 2), "--include-array-values --max-array-elements 4"),
- ("empty", (), "Sample values omitted: empty"),
- ("max_array_elements_exceeded", (1, 0, 2), "--max-array-elements 0"),
-])
+@pytest.mark.parametrize(
+ "reason,shape,hint",
+ [
+ (
+ "max_array_elements_exceeded",
+ (1, 8, 8),
+ "--max-array-elements 64 or smaller --width/--height",
+ ),
+ (
+ "sample has 256 elements, above max_array_elements=40",
+ (1, 16, 16),
+ "--max-array-elements 256",
+ ),
+ (
+ "array_values_not_requested",
+ (1, 2, 2),
+ "--include-array-values --max-array-elements 4",
+ ),
+ ("empty", (), "Sample values omitted: empty"),
+ ("max_array_elements_exceeded", (1, 0, 2), "--max-array-elements 0"),
+ ],
+)
def test_omission_and_budget_advice_preserved(selected_case, reason, shape, hint):
- value = sample(sample_included=False, sample_values=(), sample_shape=shape, sample_omitted_reason=reason)
- capability = agent_capabilities.ui_sample_selected_plate_image if selected_case else agent_capabilities.sample_plate_image
+ value = sample(
+ sample_included=False,
+ sample_values=(),
+ sample_shape=shape,
+ sample_omitted_reason=reason,
+ )
+ capability = (
+ agent_capabilities.ui_sample_selected_plate_image
+ if selected_case
+ else agent_capabilities.sample_plate_image
+ )
response = batch(selected(value) if selected_case else value, capability)
- renderer = SelectedPlateSampleRenderer if selected_case else PlateImageSampleRenderer
+ renderer = (
+ SelectedPlateSampleRenderer if selected_case else PlateImageSampleRenderer
+ )
assert hint in renderer.render(response)
@@ -156,28 +252,54 @@ def test_legitimate_absent_sample_and_false_zero_empty_are_distinct():
rendered = SelectedPlateSampleRenderer.render(response)
assert "Sample: " in rendered
assert "auto=False" in rendered
- zero = sample(virtual_path="", dtype="", mean=0.0, minimum=False, maximum=0,
- sample_shape=(), sample_values=[], sample_included=True)
- rendered = PlateImageSampleRenderer.render(batch(zero, agent_capabilities.sample_plate_image))
+ zero = sample(
+ virtual_path="",
+ dtype="",
+ mean=0.0,
+ minimum=False,
+ maximum=0,
+ sample_shape=(),
+ sample_values=[],
+ sample_included=True,
+ )
+ rendered = PlateImageSampleRenderer.render(
+ batch(zero, agent_capabilities.sample_plate_image)
+ )
assert "Image: \n" in rendered
assert "dtype= min=False max=0 mean=0.000" in rendered
assert "shape= included=True" in rendered
assert "Sample values:\n[]" in rendered
-@pytest.mark.parametrize("values,count", [([False, 0, "", None], 3), (list(range(65)), 65), ({"a": [0, False], "b": ""}, 3)])
+@pytest.mark.parametrize(
+ "values,count",
+ [([False, 0, "", None], 3), (list(range(65)), 65), ({"a": [0, False], "b": ""}, 3)],
+)
def test_shared_preview_policy_and_richer_viewer_owner(values, count):
assert McpDevOutputRenderer.json_value_count(values) == count
- response = batch(sample(sample_values=values), agent_capabilities.sample_plate_image)
+ response = batch(
+ sample(sample_values=values), agent_capabilities.sample_plate_image
+ )
rendered = PlateImageSampleRenderer.render(response)
assert ("65 elements; pass --json" in rendered) == (count > 64)
record = ViewerWindowImageSampleRecord(
- layer_route_key="layer", layer_title="image", payload_route_key="payload",
- data_type="image", path="source", components={}, array_values=(values,),
- array_value_summary=ViewerArrayValueSummary(requested=True, included=True, shape=(1, count)),
+ layer_route_key="layer",
+ layer_title="image",
+ payload_route_key="payload",
+ data_type="image",
+ path="source",
+ components={},
+ array_values=(values,),
+ array_value_summary=ViewerArrayValueSummary(
+ requested=True, included=True, shape=(1, count)
+ ),
+ )
+ viewer = ViewerWindowImageSampleResult(
+ schema_version=SCHEMA_VERSION, observed=True, records=(record,)
+ )
+ text = ViewerImageSampleRenderer.render(
+ batch(viewer, agent_capabilities.sample_viewer_window_image)
)
- viewer = ViewerWindowImageSampleResult(schema_version=SCHEMA_VERSION, observed=True, records=(record,))
- text = ViewerImageSampleRenderer.render(batch(viewer, agent_capabilities.sample_viewer_window_image))
assert ("sample values:" in text) == (count <= 64)
assert record.array_value_summary.shape_element_count == count
@@ -186,19 +308,44 @@ def test_shared_preview_policy_and_richer_viewer_owner(values, count):
def test_original_typed_diagnostics_retained_once(selected_case):
error = AgentError("sample_failure", "retained cause", "retained hint")
value = sample(errors=(error,))
- capability = agent_capabilities.ui_sample_selected_plate_image if selected_case else agent_capabilities.sample_plate_image
- response = batch(replace(selected(value), errors=(error,)) if selected_case else value, capability)
- text = (SelectedPlateSampleRenderer if selected_case else PlateImageSampleRenderer).render(response)
+ capability = (
+ agent_capabilities.ui_sample_selected_plate_image
+ if selected_case
+ else agent_capabilities.sample_plate_image
+ )
+ response = batch(
+ replace(selected(value), errors=(error,)) if selected_case else value,
+ capability,
+ )
+ text = (
+ SelectedPlateSampleRenderer if selected_case else PlateImageSampleRenderer
+ ).render(response)
assert "failed" in text
assert text.count("sample_failure") == 1
assert "retained cause" in text and "retained hint" in text
assert response.has_errors()
-@pytest.mark.parametrize("receipt", [{"alien": 1}, {"schema_version": SCHEMA_VERSION}, {"schema_version": SCHEMA_VERSION, "plate_path": "p", "requested_image_path": "i", "sample_included": "false"}])
+@pytest.mark.parametrize(
+ "receipt",
+ [
+ {"alien": 1},
+ {"schema_version": SCHEMA_VERSION},
+ {
+ "schema_version": SCHEMA_VERSION,
+ "plate_path": "p",
+ "requested_image_path": "i",
+ "sample_included": "false",
+ },
+ ],
+)
def test_malformed_receipt_never_becomes_successful_empty_sample(receipt):
- result = McpDevToolResult.from_payload(agent_capabilities.sample_plate_image.name, {"structuredContent": receipt})
- response = McpDevToolBatchResponse.from_results(McpDevServerSpec(sys.executable), (result,))
+ result = McpDevToolResult.from_payload(
+ agent_capabilities.sample_plate_image.name, {"structuredContent": receipt}
+ )
+ response = McpDevToolBatchResponse.from_results(
+ McpDevServerSpec(sys.executable), (result,)
+ )
assert isinstance(result.payloads[0], McpDevPayloadFailure)
assert result.payloads[0].payload == receipt
rejection = to_jsonable(response)["results"][0]["payloads"][0]
@@ -211,12 +358,19 @@ def test_malformed_receipt_never_becomes_successful_empty_sample(receipt):
def test_transport_missing_and_unknown_receipts_keep_original_diagnostics():
server = McpDevServerSpec(sys.executable)
- missing = McpDevToolBatchResponse.from_results(server, (McpDevToolResult(agent_capabilities.sample_plate_image.name, False, ()),))
+ missing = McpDevToolBatchResponse.from_results(
+ server,
+ (McpDevToolResult(agent_capabilities.sample_plate_image.name, False, ()),),
+ )
assert "mcp_payload_missing" in PlateImageSampleRenderer.render(missing)
- response = McpDevToolBatchResponse.from_transport_failure(server, McpDevClientPhase.CALL_TOOL, RuntimeError("wire lost"))
+ response = McpDevToolBatchResponse.from_transport_failure(
+ server, McpDevClientPhase.CALL_TOOL, RuntimeError("wire lost")
+ )
assert "wire lost" in PlateImageSampleRenderer.render(response)
receipt = {"opaque": {"false": False, "zero": 0, "empty": ""}}
- unknown = McpDevToolResult.from_payload("unregistered-external-tool", {"structuredContent": receipt})
+ unknown = McpDevToolResult.from_payload(
+ "unregistered-external-tool", {"structuredContent": receipt}
+ )
assert unknown.payloads == (receipt,)
assert to_jsonable(unknown)["payloads"] == [receipt]
@@ -228,11 +382,16 @@ class NewSample(PlateImageSampleResult):
independent_fact: str = "owned"
events = []
+
class SourceFact(PlateImageSampleRenderer):
@classmethod
def render_payload(cls, payload, options):
events.append("source")
- return super().render_payload(payload, options) + "\n" + payload.independent_fact
+ return (
+ super().render_payload(payload, options)
+ + "\n"
+ + payload.independent_fact
+ )
class AuditFact(PlateImageSampleRenderer):
@classmethod
@@ -242,12 +401,17 @@ def render_payload(cls, payload, options):
bases = (AuditFact, SourceFact) if reverse_order else (SourceFact, AuditFact)
NewRenderer = type("NewSampleRenderer", bases, {"output_contract": NewSample})
+
class NewCapability(SamplePlateImageCapability):
name = f"openhcs_s1_new_sample_{int(reverse_order)}"
cli_command = f"s1-new-sample-{int(reverse_order)}"
output_contract = NewSample
- value = NewSample(schema_version=SCHEMA_VERSION, plate_path="new-plate", requested_image_path="new-image")
+ value = NewSample(
+ schema_version=SCHEMA_VERSION,
+ plate_path="new-plate",
+ requested_image_path="new-image",
+ )
response = batch(value, NewCapability)
assert type(response.results[0].first_decoded_payload()) is NewSample
command = CapabilityBackedCommandSpec.for_capability_name(NewCapability.name)
@@ -261,4 +425,7 @@ class NewCapability(SamplePlateImageCapability):
def test_sampling_renderers_share_the_json_preview_count():
assert "_json_value_count" not in vars(PlateImageSampleRenderer)
assert "_json_value_count" not in vars(ViewerImageSampleRenderer)
- assert PlateImageSampleRenderer.json_value_count.__func__ is ViewerImageSampleRenderer.json_value_count.__func__
+ assert (
+ PlateImageSampleRenderer.json_value_count.__func__
+ is ViewerImageSampleRenderer.json_value_count.__func__
+ )
diff --git a/tests/unit/agent/test_mcp_dev_client_ui_renderers.py b/tests/unit/agent/test_mcp_dev_client_ui_renderers.py
index 5f8a8a6057..24353fde25 100644
--- a/tests/unit/agent/test_mcp_dev_client_ui_renderers.py
+++ b/tests/unit/agent/test_mcp_dev_client_ui_renderers.py
@@ -60,7 +60,7 @@
UiActionCatalogRenderOptions,
WidgetTreeRenderOptions,
)
-from test_mcp_dev_client_renderers import SERVER, framed, sample_dto
+from tests.unit.agent.test_mcp_dev_client_renderers import SERVER, framed, sample_dto
def render(payload, options=None) -> str:
@@ -71,7 +71,10 @@ def render(payload, options=None) -> str:
def snapshot(index: int, label: str = "edit") -> UiSnapshotRef:
return replace(
- sample_dto(UiSnapshotRef), index=index, label=label, branch="main",
+ sample_dto(UiSnapshotRef),
+ index=index,
+ label=label,
+ branch="main",
snapshot_id=f"snapshot-{index}",
)
@@ -112,9 +115,11 @@ def test_bridge_status_and_unavailable_bridge():
server=SERVER,
results=(McpDevToolResult(tool="t", mcp_error=True, payloads=()),),
)
- assert McpDevOutputRenderer.for_output_contract(UiBridgeStatus).render(
- failed
- ).startswith("UI bridge: unavailable\nErrors:")
+ assert (
+ McpDevOutputRenderer.for_output_contract(UiBridgeStatus)
+ .render(failed)
+ .startswith("UI bridge: unavailable\nErrors:")
+ )
def window(window_id: str, title: str, kind: str, visible: bool) -> UiWindowSummary:
@@ -147,21 +152,28 @@ def test_window_catalog_flags_only_visible_stray_top_level_windows():
'Attention: 1 visible top-level window(s): qt_top_level:1 title="Error"'
in rendered
)
- assert '- plate_manager [embedded] visible=True' in rendered
+ assert "- plate_manager [embedded] visible=True" in rendered
def test_ui_smoke_composes_each_tool_payload():
from openhcs.agent.capabilities import agent_capabilities
def result(capability, payload):
- return McpDevToolResult(tool=capability.name, mcp_error=False, payloads=(payload,))
+ return McpDevToolResult(
+ tool=capability.name, mcp_error=False, payloads=(payload,)
+ )
health = replace(
sample_dto(McpServerHealthResult),
- status="ok", restart_required=False, stale_source_paths=(), warnings=(),
+ status="ok",
+ restart_required=False,
+ stale_source_paths=(),
+ warnings=(),
)
bridges = replace(sample_dto(UiBridgeCatalog), warnings=())
- status = replace(sample_dto(UiBridgeStatus), reachable=True, descriptor_status="ok", warnings=())
+ status = replace(
+ sample_dto(UiBridgeStatus), reachable=True, descriptor_status="ok", warnings=()
+ )
response = McpDevToolBatchResponse(
server=SERVER,
results=(
@@ -211,9 +223,15 @@ def document(document_id):
def plate_row(name: str, selected: bool, **values) -> DatasetRowState:
return replace(
sample_dto(DatasetRowState),
- name=name, selected=selected, orchestrator_state="compiled",
- status_prefix="", terminal_status=None, root=f"/{name}",
- output_root=None, source_root=None, **values,
+ name=name,
+ selected=selected,
+ orchestrator_state="compiled",
+ status_prefix="",
+ terminal_status=None,
+ root=f"/{name}",
+ output_root=None,
+ source_root=None,
+ **values,
)
@@ -245,15 +263,21 @@ def test_state_surface_documents_render_through_their_surface_state():
sample_dto(UiPipelineEditorState),
summary=surface_summary(
PipelineEditorStateSurfaceIdentityDeclaration.require_value(),
- current_selection_count=1, total_scope_count=2,
+ current_selection_count=1,
+ total_scope_count=2,
),
current_snapshot=snapshot(4, "edit pipeline"),
selected_scope_ids=("/tmp/plate-a::functionstep_1",),
steps=(
replace(
sample_dto(PipelineStepState),
- index=0, name="normalize", dirty=True, default_diff=False,
- function_names=("normalize",), function_ids=(), debug_pause=False,
+ index=0,
+ name="normalize",
+ dirty=True,
+ default_diff=False,
+ function_names=("normalize",),
+ function_ids=(),
+ debug_pause=False,
step_scope_id=None,
),
),
@@ -262,7 +286,9 @@ def test_state_surface_documents_render_through_their_surface_state():
rendered = render(document_for(editor))
assert 'Snapshot: 4 "edit pipeline"' in rendered
assert "Selected scopes: /tmp/plate-a::functionstep_1" in rendered
- assert "- 0. [*] normalize: enabled=True, selected=True, funcs=normalize" in rendered
+ assert (
+ "- 0. [*] normalize: enabled=True, selected=True, funcs=normalize" in rendered
+ )
assert "ids=" not in rendered
@@ -288,7 +314,9 @@ def test_unregistered_or_unreadable_surface_points_to_the_catalog():
summary=surface_summary("plate_manager", readable=False),
payload={},
warnings=(),
- errors=(AgentError(code="ui_state_surface_unreadable", message="Not readable."),),
+ errors=(
+ AgentError(code="ui_state_surface_unreadable", message="Not readable."),
+ ),
)
rendered = render(document)
assert "Surface: plate_manager" in rendered
@@ -299,38 +327,68 @@ def test_unregistered_or_unreadable_surface_points_to_the_catalog():
def tree_node(class_name: str, *children, **values) -> UiWidgetTreeNode:
defaults = dict(
- object_name="", text=None, title=None, current_text=None, visible=True,
- enabled=True, clickable=True, actionable=False, action_kinds=(),
+ object_name="",
+ text=None,
+ title=None,
+ current_text=None,
+ visible=True,
+ enabled=True,
+ clickable=True,
+ actionable=False,
+ action_kinds=(),
)
return replace(
sample_dto(UiWidgetTreeNode),
- class_name=class_name, children=children, **(defaults | values),
+ class_name=class_name,
+ children=children,
+ **(defaults | values),
)
def test_widget_tree_outline_shows_addressable_paths_and_hides_technical_nodes():
root = tree_node(
"PipelineEditorWidget",
- tree_node("QPushButton", text="Edit", path_id="1.0.2", action_kinds=("button",),
- enabled=False, clickable=False),
- tree_node("QPushButton", text="Generate", path_id="1.0.3",
- action_kinds=("button",), visible=False),
+ tree_node(
+ "QPushButton",
+ text="Edit",
+ path_id="1.0.2",
+ action_kinds=("button",),
+ enabled=False,
+ clickable=False,
+ ),
+ tree_node(
+ "QPushButton",
+ text="Generate",
+ path_id="1.0.3",
+ action_kinds=("button",),
+ visible=False,
+ ),
tree_node("QScrollBar"),
tree_node("QLineEdit", path_id="1.0.4", actionable=True, text="Gamma"),
tree_node("QLabel", current_text="long " * 40),
)
action = replace(
sample_dto(UiWidgetActionSummary),
- path_id="1.0.4", object_state_scope_id="/plate::step", field_path="gamma",
+ path_id="1.0.4",
+ object_state_scope_id="/plate::step",
+ field_path="gamma",
semantic_markers=("*",),
)
result = replace(
sample_dto(UiWidgetTreeResult),
- root=root, actionable_widgets=(action,), tree_truncated=False,
- actionable_widgets_truncated=False, warnings=(),
- summary=replace(sample_dto(UiWindowSummary), title="Pipeline Editor",
- dirty=False, dirty_field_count=0, signature_diff=True,
- semantic_markers=()),
+ root=root,
+ actionable_widgets=(action,),
+ tree_truncated=False,
+ actionable_widgets_truncated=False,
+ warnings=(),
+ summary=replace(
+ sample_dto(UiWindowSummary),
+ title="Pipeline Editor",
+ dirty=False,
+ dirty_field_count=0,
+ signature_diff=True,
+ semantic_markers=(),
+ ),
)
rendered = render(result)
assert "Window: Pipeline Editor" in rendered
@@ -357,22 +415,29 @@ def test_widget_tree_outline_shows_addressable_paths_and_hides_technical_nodes()
def test_code_document_bounds_its_source():
document = replace(
- sample_dto(UiCodeDocument), source="pattern = (some_func," + "x" * 50,
- current_snapshot=snapshot(10), warnings=(),
+ sample_dto(UiCodeDocument),
+ source="pattern = (some_func," + "x" * 50,
+ current_snapshot=snapshot(10),
+ warnings=(),
)
rendered = render(document, CodeDocumentRenderOptions(max_source_chars=20))
assert "Revision: token=" in rendered and "snapshot=main@10 head=" in rendered
assert "Source:\npattern = (some_func" in rendered
assert "..." in rendered
- assert "Source:" not in render(document, CodeDocumentRenderOptions(include_source=False))
+ assert "Source:" not in render(
+ document, CodeDocumentRenderOptions(include_source=False)
+ )
def test_code_document_apply_shows_revisions_and_snapshots():
applied = replace(
sample_dto(UiCodeDocumentApplyResult),
- base_revision_token="rev-123", current_revision_token="rev-456",
- new_revision_token="rev-456", current_snapshot=snapshot(10),
- undo_snapshot=None, warnings=(),
+ base_revision_token="rev-123",
+ current_revision_token="rev-456",
+ new_revision_token="rev-456",
+ current_snapshot=snapshot(10),
+ undo_snapshot=None,
+ warnings=(),
)
rendered = render(applied)
assert "Revision: base=rev-123 current=rev-456 new=rev-456" in rendered
@@ -393,13 +458,20 @@ def test_action_catalog_filters_actions_and_warnings_by_widget():
sample_dto(UiActionCatalog),
actions=(
ui_action(
- "plate_manager", "compile_datasets", enabled=False,
- disabled_error=AgentError(code="no_plate", message="No plate.", hint="Add a plate."),
+ "plate_manager",
+ "compile_datasets",
+ enabled=False,
+ disabled_error=AgentError(
+ code="no_plate", message="No plate.", hint="Add a plate."
+ ),
),
ui_action("image_browser", "open_file"),
),
warnings=(
- AgentWarning(code="plate_path_setup_uses_code_document", message="Use plate_manager code."),
+ AgentWarning(
+ code="plate_path_setup_uses_code_document",
+ message="Use plate_manager code.",
+ ),
AgentWarning(code="unrelated", message="Something else."),
),
)
@@ -427,7 +499,9 @@ def test_action_catalog_filters_actions_and_warnings_by_widget():
def test_disabled_actions_do_not_make_the_call_fail():
catalog = replace(
sample_dto(UiActionCatalog),
- actions=(ui_action("w", "a", disabled_error=AgentError(code="off", message="Off.")),),
+ actions=(
+ ui_action("w", "a", disabled_error=AgentError(code="off", message="Off.")),
+ ),
)
result = McpDevToolResult(tool="t", mcp_error=False, payloads=(catalog,))
assert not result.has_errors()
@@ -438,8 +512,12 @@ def test_tool_list_filters_groups_and_flattens():
server=SERVER,
tool_count=3,
tools=(
- McpDevToolMetadata("openhcs_get_viewer_window_state", "Read bounded viewer state.", {}),
- McpDevToolMetadata("openhcs_validate_viewer_window_state", "Validate viewer.", {}),
+ McpDevToolMetadata(
+ "openhcs_get_viewer_window_state", "Read bounded viewer state.", {}
+ ),
+ McpDevToolMetadata(
+ "openhcs_validate_viewer_window_state", "Validate viewer.", {}
+ ),
McpDevToolMetadata("openhcs_health_check", "Health.", {}),
),
)
@@ -453,4 +531,6 @@ def test_tool_list_filters_groups_and_flattens():
assert "[Viewer Review]" not in flat
projected = ToolListRenderer.project_response(tools, contains="viewer", limit=1)
assert projected["matched_tool_count"] == 2
- assert [tool["name"] for tool in projected["tools"]] == ["openhcs_get_viewer_window_state"]
+ assert [tool["name"] for tool in projected["tools"]] == [
+ "openhcs_get_viewer_window_state"
+ ]
diff --git a/tests/unit/agent/test_mcp_plate_stream_progress.py b/tests/unit/agent/test_mcp_plate_stream_progress.py
index 87289aa9c1..fb954b6a45 100644
--- a/tests/unit/agent/test_mcp_plate_stream_progress.py
+++ b/tests/unit/agent/test_mcp_plate_stream_progress.py
@@ -18,8 +18,10 @@
from polystore.streaming.viewer_transport import ViewerStreamKwarg
from openhcs.agent.capabilities import (
- AgentCapabilityDeclaration, MainThreadProgressCapability,
- StreamPlateFilesToViewerCapability, UiStreamSelectedPlateFilesToViewerCapability,
+ AgentCapabilityDeclaration,
+ MainThreadProgressCapability,
+ StreamPlateFilesToViewerCapability,
+ UiStreamSelectedPlateFilesToViewerCapability,
)
from openhcs.agent.services.plate_streaming_service import PlateStreamingService
from openhcs.core.roi_source_metadata import ROIArchiveSourceMetadata
@@ -30,7 +32,10 @@
from openhcs.mcp.server import build_server
from openhcs.mcp.socket import McpSocketTransport, wait_for_socket
-from test_persisted_output_reopen import NoRuntimeBridge, declared_output
+from tests.unit.agent.test_persisted_output_reopen import (
+ NoRuntimeBridge,
+ declared_output,
+)
from tests.unit.test_streaming_service import FakeViewer
@@ -44,19 +49,26 @@ def saved_roi_stream(tmp_path, monkeypatch):
archive = result_root / "not-an-acquisition.roi.zip"
geometry = [ROI([PointShape(12.5, 8.25)], {"label": 7})]
DiskStorageBackend().save(
- ROIArchiveSourceMetadata.bind(geometry, projection.image_metadata), archive,
+ ROIArchiveSourceMetadata.bind(geometry, projection.image_metadata),
+ archive,
)
persisted = ROIArchiveSourceMetadata.decode(DiskStorageBackend().load(archive))
assert persisted.source_provenance == projection.image_metadata.source_provenance
- assert persisted.source_voxel_spacing == projection.image_metadata.source_voxel_spacing
+ assert (
+ persisted.source_voxel_spacing == projection.image_metadata.source_voxel_spacing
+ )
unsigned = result_root / "missing-source.roi.zip"
DiskStorageBackend().save(geometry, unsigned)
# Reuse the original path authority, explicitly adding this synthetic root.
from openhcs.agent.path_policy import AgentPathPolicy
from openhcs.agent.services.plate_inspection_service import PlateInspectionService
- inspection = PlateInspectionService(AgentPathPolicy.with_roots(
- readable_roots=(tmp_path,), writable_roots=(),
- ))
+
+ inspection = PlateInspectionService(
+ AgentPathPolicy.with_roots(
+ readable_roots=(tmp_path,),
+ writable_roots=(),
+ )
+ )
viewer = FakeViewer()
received = []
timings = []
@@ -75,12 +87,21 @@ def receive(_manager, data, paths, backend, **fields):
restored = ImagePayloadMetadata.from_viewer_image_metadata(
request.source.item_fields["image_metadata"]
)
- assert restored.to_viewer_image_metadata() == projection.image_metadata.to_viewer_image_metadata()
+ assert (
+ restored.to_viewer_image_metadata()
+ == projection.image_metadata.to_viewer_image_metadata()
+ )
assert request.source.metadata.metadata_by_path[str(archive)] == dict(
persisted.source_provenance.scalar_source_identity.component_metadata,
)
- assert restored.source_voxel_spacing == projection.image_metadata.source_voxel_spacing
- assert restored.source_spatial_domain == projection.image_metadata.source_spatial_domain
+ assert (
+ restored.source_voxel_spacing
+ == projection.image_metadata.source_voxel_spacing
+ )
+ assert (
+ restored.source_spatial_domain
+ == projection.image_metadata.source_spatial_domain
+ )
# The archive travels as saved: its geometry plus the bound source
# declaration, which the viewer reads and keeps out of the feature table.
assert [ROIArchiveSourceMetadata.geometry(rois) for rois in data] == [geometry]
@@ -98,22 +119,30 @@ def settle():
return True
monkeypatch.setattr(
- "openhcs.agent.services.plate_streaming_service.StreamingViewerLifecycle.get_or_create_visualizer", acquire,
+ "openhcs.agent.services.plate_streaming_service.StreamingViewerLifecycle.get_or_create_visualizer",
+ acquire,
)
monkeypatch.setattr(FileManager, "save_batch", receive)
monkeypatch.setattr(viewer, "settle_viewer_state", settle)
service = PlateStreamingService(inspection, NoRuntimeBridge())
arguments = {
"plate_path": str(source_path.parent.parent),
- "result_directory": str(result_root), "kind": "result", "limit": 1,
- "file_paths": [archive.name], "fresh_viewer": False,
+ "result_directory": str(result_root),
+ "kind": "result",
+ "limit": 1,
+ "file_paths": [archive.name],
+ "fresh_viewer": False,
}
return service, arguments, identity, received, timings, unsigned
-@pytest.mark.parametrize("declaration", (
- StreamPlateFilesToViewerCapability, UiStreamSelectedPlateFilesToViewerCapability,
-))
+@pytest.mark.parametrize(
+ "declaration",
+ (
+ StreamPlateFilesToViewerCapability,
+ UiStreamSelectedPlateFilesToViewerCapability,
+ ),
+)
def test_stream_declaration_composes_original_progress_and_main_affinity(declaration):
assert issubclass(declaration, MainThreadProgressCapability)
spec = declaration
@@ -124,12 +153,15 @@ def test_stream_declaration_composes_original_progress_and_main_affinity(declara
def test_saved_roi_continuous_sdk_receipt_error_reuse_beyond_ordinary_idle(
- unix_socket_dir, saved_roi_stream,
+ unix_socket_dir,
+ saved_roi_stream,
):
service, arguments, identity, received, timings, unsigned = saved_roi_stream
transport = McpSocketTransport(unix_socket_dir / "stream.sock")
- built = build_server(OpenHCSAgentContext(plate_streaming_service=service),
- main_thread_dispatcher=transport.execution.dispatcher)
+ built = build_server(
+ OpenHCSAgentContext(plate_streaming_service=service),
+ main_thread_dispatcher=transport.execution.dispatcher,
+ )
clients = AsyncOperationExecutor(max_workers=1)
diagnostics = io.StringIO()
events = []
@@ -138,37 +170,72 @@ class ObservedSession(McpDevSocketSession):
def record_progress_notification(self, notification):
super().record_progress_notification(notification)
assert threading.current_thread() is not threading.main_thread()
- events.append((time.monotonic(), notification.params.progressToken, notification.params.message))
+ events.append(
+ (
+ time.monotonic(),
+ notification.params.progressToken,
+ notification.params.message,
+ )
+ )
async def exercise():
- assert await asyncio.to_thread(wait_for_socket, transport.socket_path, timeout_seconds=10)
+ assert await asyncio.to_thread(
+ wait_for_socket, transport.socket_path, timeout_seconds=10
+ )
try:
- async with ObservedSession(McpDevServerSpec(sys.executable), diagnostics, transport.socket_path) as session:
+ async with ObservedSession(
+ McpDevServerSpec(sys.executable), diagnostics, transport.socket_path
+ ) as session:
await session.initialize(timeout_seconds=10)
- for index, paths in enumerate((arguments["file_paths"], [unsigned.name], arguments["file_paths"])):
+ for index, paths in enumerate(
+ (arguments["file_paths"], [unsigned.name], arguments["file_paths"])
+ ):
before = len(events)
request_id = session.request_id + 1
started = time.monotonic()
- result = await session.call_tool(StreamPlateFilesToViewerCapability.name,
- {**arguments, "file_paths": paths}, timeout_seconds=10)
+ result = await session.call_tool(
+ StreamPlateFilesToViewerCapability.name,
+ {**arguments, "file_paths": paths},
+ timeout_seconds=10,
+ )
elapsed = time.monotonic() - started
progress = events[before:]
assert progress and progress[0][0] - started < 1
assert all(event[1] == request_id for event in progress)
payload = result["structuredContent"]
if index == 1:
- assert payload["errors"][0]["code"] == "plate_file_stream_failed"
- assert "Native ROI source metadata is required" in payload["errors"][0]["message"]
+ assert (
+ payload["errors"][0]["code"] == "plate_file_stream_failed"
+ )
+ assert (
+ "Native ROI source metadata is required"
+ in payload["errors"][0]["message"]
+ )
assert len(received) == 1
else:
assert payload["errors"] == []
- assert payload["streamed_roi_paths"] == [str(Path(arguments["result_directory"]) / paths[0])]
+ assert payload["streamed_roi_paths"] == [
+ str(Path(arguments["result_directory"]) / paths[0])
+ ]
assert payload["streamed_image_paths"] == []
if index == 0:
assert elapsed >= 12
- assert any(event[0] - started > 10 and "still running" in event[2] for event in progress)
- assert any("Streaming 1 ROI file(s)" in event[2] for event in progress)
- print("ROI_STREAM_TERMINAL", index, elapsed, "idle10", "token", request_id, flush=True)
+ assert any(
+ event[0] - started > 10 and "still running" in event[2]
+ for event in progress
+ )
+ assert any(
+ "Streaming 1 ROI file(s)" in event[2] for event in progress
+ )
+ print(
+ "ROI_STREAM_TERMINAL",
+ index,
+ elapsed,
+ "idle10",
+ "token",
+ request_id,
+ flush=True,
+ )
finally:
transport._stop.set()
if transport._listener is not None:
@@ -185,7 +252,9 @@ async def exercise():
clients.close()
-def test_independent_stream_leaf_cooperative_hook_uses_generated_consumer(saved_roi_stream):
+def test_independent_stream_leaf_cooperative_hook_uses_generated_consumer(
+ saved_roi_stream,
+):
service, arguments, identity, received, timings, _unsigned = saved_roi_stream
timings.append(0.0) # This declaration test does not repeat the slow experiment.
hooks = []
@@ -219,15 +288,18 @@ async def report_progress(self, progress, total=None, message=None):
hooks.append(message)
executor = McpTransportExecutor()
- built = build_server(OpenHCSAgentContext(plate_streaming_service=service),
- main_thread_dispatcher=executor.dispatcher)
+ built = build_server(
+ OpenHCSAgentContext(plate_streaming_service=service),
+ main_thread_dispatcher=executor.dispatcher,
+ )
async def exercise():
token = identity.set("stream-request")
try:
for declaration in (IndependentStream, IndependentStreamAfter):
result = await built._tool_manager.get_tool(declaration.name).fn(
- mcp_context=ProgressContext(), **arguments)
+ mcp_context=ProgressContext(), **arguments
+ )
assert result["errors"] == []
assert received[-1][3] == "stream-request"
finally:
@@ -238,7 +310,9 @@ async def exercise():
assert len(received) == 2
assert hooks.count("enter") == hooks.count("exit") == 2
assert hooks.index("enter") < hooks.index("exit")
- assert any("published and viewer settlement completed" in message for message in hooks)
+ assert any(
+ "published and viewer settlement completed" in message for message in hooks
+ )
finally:
executor.close()
for declaration in (IndependentStream, IndependentStreamAfter):
diff --git a/tests/unit/agent/test_mcp_process_resource_lifetime.py b/tests/unit/agent/test_mcp_process_resource_lifetime.py
index 1c0079f189..59b43a08c1 100644
--- a/tests/unit/agent/test_mcp_process_resource_lifetime.py
+++ b/tests/unit/agent/test_mcp_process_resource_lifetime.py
@@ -35,7 +35,9 @@ def shutdown_jvm(self):
context.ij = Gateway()
resource_patch.setattr(BioFormatsJavaContext, "_instance", context)
resource_patch.setattr(backend_registry, "_cleanup_callbacks", [])
- backend_registry.register_cleanup_callback(BioFormatsJavaContext.shutdown_instance)
+ backend_registry.register_cleanup_callback(
+ BioFormatsJavaContext.shutdown_instance
+ )
class AuditCloseCapability:
def close(self):
@@ -68,7 +70,10 @@ def independent_resource():
assert BioFormatsJavaContext._instance is None
assert context.ij is None
assert calls[:4] == [
- "cooperative enter", "gateway disposed", "JVM stopped", "independent resource"
+ "cooperative enter",
+ "gateway disposed",
+ "JVM stopped",
+ "independent resource",
]
assert calls.count("cooperative exit") == int(failure is None)
executor.close()
@@ -79,7 +84,9 @@ def independent_resource():
def test_off_main_close_rejected_before_resource_release(monkeypatch):
calls = []
- monkeypatch.setattr(backend_registry, "_cleanup_callbacks", [lambda: calls.append("closed")])
+ monkeypatch.setattr(
+ backend_registry, "_cleanup_callbacks", [lambda: calls.append("closed")]
+ )
executor = McpTransportExecutor()
failures = []
@@ -121,7 +128,8 @@ def test_stdio_eof_retains_exact_normal_child_exit(tmp_path, exit_code):
async def journey():
session = McpDevStdioSession(
- ScriptServerSpec(python_executable=sys.executable, script=script), io.StringIO()
+ ScriptServerSpec(python_executable=sys.executable, script=script),
+ io.StringIO(),
)
with (tmp_path / "diagnostic.log").open("w+") as diagnostic:
session.server_stderr = diagnostic
@@ -145,7 +153,8 @@ def test_stdio_stuck_child_uses_unchanged_forced_reap_budget(tmp_path):
async def journey():
with (tmp_path / "diagnostic.log").open("w+") as diagnostic:
session = McpDevStdioSession(
- ScriptServerSpec(python_executable=sys.executable, script=script), diagnostic
+ ScriptServerSpec(python_executable=sys.executable, script=script),
+ diagnostic,
)
assert session.teardown_timeout_seconds == 2.0
async with session:
@@ -157,7 +166,9 @@ async def journey():
@pytest.mark.parametrize("fail_cleanup", (False, True))
-def test_real_sdk_stdio_declared_resource_closes_before_child_exit(tmp_path, fail_cleanup):
+def test_real_sdk_stdio_declared_resource_closes_before_child_exit(
+ tmp_path, fail_cleanup
+):
from openhcs.runtime.import_authority import OpenHCSRuntimeImportAuthority
source_root = OpenHCSRuntimeImportAuthority.current().import_root
@@ -175,7 +186,11 @@ def test_real_sdk_stdio_declared_resource_closes_before_child_exit(tmp_path, fai
"def close_resource():\n"
" assert threading.current_thread() is threading.main_thread()\n"
f" Path({str(closed)!r}).write_text('main-thread resource closed')\n"
- + (" raise ValueError('original declared cleanup error')\n" if fail_cleanup else "")
+ + (
+ " raise ValueError('original declared cleanup error')\n"
+ if fail_cleanup
+ else ""
+ )
+ "register_cleanup_callback(close_resource)\n"
"with McpStdioTransport.reserve_process_stdio() as transport:\n"
" transport.run(server)\n"
@@ -184,21 +199,32 @@ def test_real_sdk_stdio_declared_resource_closes_before_child_exit(tmp_path, fai
async def journey():
with (tmp_path / "sdk-diagnostic.log").open("w+") as diagnostic:
session = McpDevStdioSession(
- ScriptServerSpec(python_executable=sys.executable, script=script), diagnostic
+ ScriptServerSpec(python_executable=sys.executable, script=script),
+ diagnostic,
)
async with session:
process = session.require_process()
await session.initialize(timeout_seconds=10)
first = await session.call_tool("health", {}, timeout_seconds=10)
second = await session.call_tool("health", {}, timeout_seconds=10)
- assert first["structuredContent"] == second["structuredContent"] == {"ok": True}
+ assert (
+ first["structuredContent"]
+ == second["structuredContent"]
+ == {"ok": True}
+ )
assert process.returncode == int(fail_cleanup)
assert closed.read_text() == "main-thread resource closed"
diagnostic.seek(0)
text = diagnostic.read()
assert "Fatal error" not in text
assert ("original declared cleanup error" in text) is fail_cleanup
- print("SDK_STDIO_DECLARED_RESOURCE", process.pid, process.returncode, fail_cleanup, flush=True)
+ print(
+ "SDK_STDIO_DECLARED_RESOURCE",
+ process.pid,
+ process.returncode,
+ fail_cleanup,
+ flush=True,
+ )
asyncio.run(journey())
@@ -228,11 +254,15 @@ def release_resource():
clients = AsyncOperationExecutor(max_workers=1)
async def journey():
- assert await asyncio.to_thread(wait_for_socket, transport.socket_path, timeout_seconds=10)
+ assert await asyncio.to_thread(
+ wait_for_socket, transport.socket_path, timeout_seconds=10
+ )
try:
for connection in range(2):
async with McpDevSocketSession(
- McpDevServerSpec(sys.executable), io.StringIO(), transport.socket_path
+ McpDevServerSpec(sys.executable),
+ io.StringIO(),
+ transport.socket_path,
) as session:
await session.initialize(timeout_seconds=10)
result = await session.call_tool("health", {}, timeout_seconds=10)
diff --git a/tests/unit/agent/test_selected_plane_reopen_stream.py b/tests/unit/agent/test_selected_plane_reopen_stream.py
index 333e000a04..9f95643b93 100644
--- a/tests/unit/agent/test_selected_plane_reopen_stream.py
+++ b/tests/unit/agent/test_selected_plane_reopen_stream.py
@@ -39,7 +39,10 @@
)
from openhcs.runtime.napari_viewer_server import NapariBatchPayload, NapariImagePayload
-from test_persisted_output_reopen import NoRuntimeBridge, declared_output
+from tests.unit.agent.test_persisted_output_reopen import (
+ NoRuntimeBridge,
+ declared_output,
+)
@pytest.fixture
diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py
new file mode 100644
index 0000000000..53cfa68a4f
--- /dev/null
+++ b/tests/unit/conftest.py
@@ -0,0 +1,16 @@
+"""Unit-suite process setup."""
+
+import pytest
+
+
+@pytest.fixture(scope="session", autouse=True)
+def session_qapplication(request):
+ """Create the unit session's one QApplication before any test runs.
+
+ A process holds one Qt application. In-process MCP transports adopt an
+ existing one and otherwise create a QCoreApplication, after which no later
+ unit test could create a widget. Sessions without pytest-qt (CPU-only, or a
+ narrowed ``--confcutdir``) run no widget tests.
+ """
+ if request.config.pluginmanager.hasplugin("pytestqt"):
+ request.getfixturevalue("qapp")
diff --git a/tests/unit/test_aggregate_source_scope.py b/tests/unit/test_aggregate_source_scope.py
index e17ba4f9d2..8d935961f3 100644
--- a/tests/unit/test_aggregate_source_scope.py
+++ b/tests/unit/test_aggregate_source_scope.py
@@ -12,22 +12,37 @@
from openhcs.core.component_group_scope import RuntimeExecutionAxisScope
from openhcs.core.measurement_row_materialization import MeasurementSparseColumnarRows
from openhcs.core.orchestrator.execution_result import (
- RuntimeContextObservation, RuntimeExecutionObservation,
+ RuntimeContextObservation,
+ RuntimeExecutionObservation,
)
from openhcs.core.projected_image_output import SourceProjectedImageOutput
from openhcs.core.runtime_artifact_values import ArtifactKey, RuntimeValue
from openhcs.core.runtime_exports import (
- RuntimeExportExpectation, RuntimeExportObservation, runtime_export_failures,
+ RuntimeExportExpectation,
+ RuntimeExportObservation,
+ runtime_export_failures,
)
from openhcs.core.runtime_image_values import ImagePayloadMetadata
-from openhcs.core.runtime_measurements import MeasurementScope, MeasurementSubject, MeasurementTable
-from openhcs.core.runtime_plane_projection import RuntimePlaneAxis, RuntimePlaneAxisValueProjection
+from openhcs.core.runtime_measurements import (
+ MeasurementScope,
+ MeasurementSubject,
+ MeasurementTable,
+)
+from openhcs.core.runtime_plane_projection import (
+ RuntimePlaneAxis,
+ RuntimePlaneAxisValueProjection,
+)
from openhcs.core.runtime_stores import (
- RuntimeArtifactAddress, RuntimeArtifactLocation, RuntimeValueStore,
+ RuntimeArtifactAddress,
+ RuntimeArtifactLocation,
+ RuntimeValueStore,
)
from openhcs.core.runtime_tabular_values import FieldSpec
from openhcs.core.source_image_provenance import (
- RuntimeSourceImageProvenancePlane, SourceImageIdentity, SourceImageProvenancePlanes,
+ RuntimeSourceImageProvenancePlane,
+ SourceImageIdentity,
+ SourceImageProvenanceContributor,
+ SourceImageProvenancePlanes,
)
from openhcs.core.source_metadata import SourceVoxelSpacing
from openhcs.core.steps.abstract import StepExecutionObservation
@@ -36,7 +51,7 @@
from polystore.filemanager import FileManager
from polystore.memory import MemoryStorageBackend
-from test_materialization_core import _viewer_stream_backend_kwargs
+from tests.unit.test_materialization_core import _viewer_stream_backend_kwargs
from tests.materialization_plans import output_plan as materialization_output_plan
from openhcs.core.artifacts.plans import MAIN_FLOW
@@ -48,12 +63,19 @@ def _source(sites):
source_image_provenance_planes=SourceImageProvenancePlanes.from_components(
paths=tuple(f"/input/field_{i}.tif" for i in range(len(sites))),
component_metadata=tuple(
- {"well": "A01", "channel": 2, "z_index": 1, "timepoint": 1,
- **({"site": site} if site is not None else {})}
+ {
+ "well": "A01",
+ "channel": 2,
+ "z_index": 1,
+ "timepoint": 1,
+ **({"site": site} if site is not None else {}),
+ }
for site in sites
),
),
- ).payload_with(np.arange(len(sites) * 12, dtype=np.uint16).reshape(len(sites), 3, 4))
+ ).payload_with(
+ np.arange(len(sites) * 12, dtype=np.uint16).reshape(len(sites), 3, 4)
+ )
class ProjectionAudit:
@@ -83,8 +105,12 @@ def test_mixed_acquired_and_reduced_batch_uses_distinct_original_display_scopes(
source = _source((1, 3))
declaration = DeclaredReducedField(np.full((3, 4), 720, dtype=np.uint16), [])
reduced = ImageArtifactType.contextualize_output(
- source, declaration, MAIN_FLOW,
- RuntimePlaneAxisValueProjection.preserve(axis=RuntimePlaneAxis.SOURCE_BINDING, axis_size=2),
+ source,
+ declaration,
+ MAIN_FLOW,
+ RuntimePlaneAxisValueProjection.preserve(
+ axis=RuntimePlaneAxis.SOURCE_BINDING, axis_size=2
+ ),
)
assert declaration.calls == ["before", "after"]
acquired_metadata = source.metadata.for_leading_source_plane(0)
@@ -98,33 +124,52 @@ def test_mixed_acquired_and_reduced_batch_uses_distinct_original_display_scopes(
request = kwargs["stream_request"]
single = adapter.to_filemanager_kwargs(output)["stream_request"]
assert request.display_config == single.display_config
- assert request.source.metadata.component_metadata_for_item(output.path, 0) == single.source.metadata.component_metadata_for_item(output.path, 0)
+ assert request.source.metadata.component_metadata_for_item(
+ output.path, 0
+ ) == single.source.metadata.component_metadata_for_item(output.path, 0)
assert request.producer == adapter.values.stream_request.producer
- assert request.viewer_transport == adapter.values.stream_request.viewer_transport
- assert request.display_config.component_modes() == adapter.values.stream_request.display_config.component_modes()
+ assert (
+ request.viewer_transport == adapter.values.stream_request.viewer_transport
+ )
+ assert (
+ request.display_config.component_modes()
+ == adapter.values.stream_request.display_config.component_modes()
+ )
assert output.metadata.source_voxel_spacing == SourceVoxelSpacing((0.5, 0.5))
- acquired_request, reduced_request = (kwargs["stream_request"] for _, kwargs in batches)
+ acquired_request, reduced_request = (
+ kwargs["stream_request"] for _, kwargs in batches
+ )
assert "site" in acquired_request.display_config.COMPONENT_ORDER
assert "site" not in reduced_request.display_config.COMPONENT_ORDER
- assert aggregate.metadata.source_provenance.source_image_provenance_planes.contributor_count == 2
+ assert (
+ aggregate.metadata.source_provenance.source_image_provenance_planes.contributor_count
+ == 2
+ )
np.testing.assert_array_equal(aggregate.content, declaration.data)
@pytest.mark.parametrize("sites", ((), (None,), (1,), (1, 1), (1, None), (None, None)))
-def test_absence_one_equal_or_incomplete_contributors_cannot_exempt_acquired_plane(sites):
+def test_absence_one_equal_or_incomplete_contributors_cannot_exempt_acquired_plane(
+ sites,
+):
metadata = _source(sites).metadata.collapse_leading_plane_axis()
components = {
- "well": "A01", "channel": 2, "z_index": 1, "timepoint": 1,
+ "well": "A01",
+ "channel": 2,
+ "z_index": 1,
+ "timepoint": 1,
}
# Explicitly absent scalar identity, not a selected plane with a valid common site.
metadata = ImagePayloadMetadata(
source_component_metadata=components,
- source_image_provenance_planes=SourceImageProvenancePlanes((
- RuntimeSourceImageProvenancePlane(
- SourceImageIdentity(component_metadata=components),
- contributors=metadata.source_image_provenance_planes.contributors,
- ),
- )),
+ source_image_provenance_planes=SourceImageProvenancePlanes(
+ (
+ RuntimeSourceImageProvenancePlane(
+ SourceImageIdentity(component_metadata=components),
+ contributors=metadata.source_image_provenance_planes.contributors,
+ ),
+ )
+ ),
)
output = Output("unaddressed.tif", np.zeros((3, 4), dtype=np.uint16), metadata)
adapter = _viewer_stream_backend_kwargs()
@@ -135,66 +180,148 @@ def test_absence_one_equal_or_incomplete_contributors_cannot_exempt_acquired_pla
adapter.filemanager_batches((output,))
+def test_axis_varying_between_runtime_planes_is_kept_not_reduced():
+ common = {"well": "A01", "z_index": 1, "timepoint": 1}
+ planes = tuple(
+ RuntimeSourceImageProvenancePlane(
+ SourceImageIdentity(component_metadata={**common, "site": site}),
+ contributors=tuple(
+ SourceImageProvenanceContributor(
+ SourceImageIdentity(
+ component_metadata={**common, "site": site, "channel": channel}
+ )
+ )
+ for channel in (1, 2)
+ ),
+ )
+ for site in (1, 2)
+ )
+ metadata = ImagePayloadMetadata(
+ plane_axis=RuntimePlaneAxis.SOURCE_BINDING,
+ source_component_metadata=common,
+ source_image_provenance_planes=SourceImageProvenancePlanes(planes),
+ )
+ assert metadata.source_provenance.required_scalar_components(
+ ("site", "channel", "well")
+ ) == ("site", "well")
+
+
def test_present_scalar_override_remains_required_despite_varied_contributors():
metadata = (_source((1, 3))).metadata.collapse_leading_plane_axis()
- metadata = metadata.replace_fields(source_component_metadata={
- "well": "A01", "site": 7, "channel": 2, "z_index": 1, "timepoint": 1,
- })
+ metadata = metadata.replace_fields(
+ source_component_metadata={
+ "well": "A01",
+ "site": 7,
+ "channel": 2,
+ "z_index": 1,
+ "timepoint": 1,
+ }
+ )
assert metadata.source_provenance.required_scalar_components(("site",)) == ("site",)
output = Output("override.tif", np.zeros((3, 4), dtype=np.uint16), metadata)
- request = _viewer_stream_backend_kwargs().to_filemanager_kwargs(output)["stream_request"]
+ request = _viewer_stream_backend_kwargs().to_filemanager_kwargs(output)[
+ "stream_request"
+ ]
assert "site" in request.display_config.COMPONENT_ORDER
- assert request.source.metadata.component_metadata_for_item(output.path, 0)["site"] == 7
+ assert (
+ request.source.metadata.component_metadata_for_item(output.path, 0)["site"] == 7
+ )
def _saved_table(tmp_path, *, producer, fields):
table = MeasurementTable(
- name="Cells", subject=MeasurementSubject(MeasurementScope.ARTIFACT, "Cells"),
+ name="Cells",
+ subject=MeasurementSubject(MeasurementScope.ARTIFACT, "Cells"),
rows=MeasurementSparseColumnarRows.from_rows(
- ({field: 1 for field in fields},), fields=tuple(FieldSpec(field) for field in fields),
+ ({field: 1 for field in fields},),
+ fields=tuple(FieldSpec(field) for field in fields),
),
)
store = RuntimeValueStore()
- record = store.record(RuntimeValue(
- key=ArtifactKey(name="Cells", artifact_type=MeasurementsArtifactType,
- scope=RuntimeExecutionAxisScope(axis_id="A01")),
- data=table,
- ), path=f"/runtime/{producer}/Cells", backend="memory")
+ record = store.record(
+ RuntimeValue(
+ key=ArtifactKey(
+ name="Cells",
+ artifact_type=MeasurementsArtifactType,
+ scope=RuntimeExecutionAxisScope(axis_id="A01"),
+ ),
+ data=table,
+ ),
+ path=f"/runtime/{producer}/Cells",
+ backend="memory",
+ )
filemanager = FileManager({"memory": MemoryStorageBackend()})
outputs = materialization_outputs(
- MaterializationSpec(CsvOptions(filename_suffix=".csv")), table,
- str(tmp_path / f"Cells_{producer}"), filemanager, output_plan=materialization_output_plan()
+ MaterializationSpec(CsvOptions(filename_suffix=".csv")),
+ table,
+ str(tmp_path / f"Cells_{producer}"),
+ filemanager,
+ output_plan=materialization_output_plan(),
)
for output in outputs:
# Persist exactly the original writer's content and declared path.
Path(output.path).write_text(output.require_text_content(), encoding="utf-8")
observation = StepExecutionObservation(
- {RuntimeArtifactAddress.from_record(record): tuple(RuntimeArtifactLocation(output.path, "disk") for output in outputs)},
+ {
+ RuntimeArtifactAddress.from_record(record): tuple(
+ RuntimeArtifactLocation(output.path, "disk") for output in outputs
+ )
+ },
tuple(Path(output.path) for output in outputs),
)
return record, observation
def test_same_named_field_and_aggregate_tables_keep_exact_producer_ownership(tmp_path):
- acquired, acquired_outputs = _saved_table(tmp_path, producer="step1", fields=("site", "source_image_name"))
- aggregate, aggregate_outputs = _saved_table(tmp_path, producer="step3", fields=("area",))
- execution = RuntimeExecutionObservation((
- RuntimeContextObservation("A01", (acquired, aggregate), StepExecutionObservation.combine((acquired_outputs, aggregate_outputs))),
- ))
+ acquired, acquired_outputs = _saved_table(
+ tmp_path, producer="step1", fields=("site", "source_image_name")
+ )
+ aggregate, aggregate_outputs = _saved_table(
+ tmp_path, producer="step3", fields=("area",)
+ )
+ execution = RuntimeExecutionObservation(
+ (
+ RuntimeContextObservation(
+ "A01",
+ (acquired, aggregate),
+ StepExecutionObservation.combine((acquired_outputs, aggregate_outputs)),
+ ),
+ )
+ )
transported = pickle.loads(pickle.dumps(execution))
exports = RuntimeExportObservation.from_runtime_observations((transported,))
- spec = ArtifactSpec.output(name="Cells", artifact_type=MeasurementsArtifactType,
- materialization=MaterializationSpec(CsvOptions(filename_suffix=".csv")))
+ spec = ArtifactSpec.output(
+ name="Cells",
+ artifact_type=MeasurementsArtifactType,
+ materialization=MaterializationSpec(CsvOptions(filename_suffix=".csv")),
+ )
expectation = RuntimeExportExpectation.from_output_specs((spec,))
- assert exports.matching_table_outputs(acquired) == acquired_outputs.runtime_export_paths
- assert exports.matching_table_outputs(aggregate) == aggregate_outputs.runtime_export_paths
- assert runtime_export_failures(expectation, exports, {"A01": (acquired, aggregate)}) == ()
+ assert (
+ exports.matching_table_outputs(acquired)
+ == acquired_outputs.runtime_export_paths
+ )
+ assert (
+ exports.matching_table_outputs(aggregate)
+ == aggregate_outputs.runtime_export_paths
+ )
+ assert (
+ runtime_export_failures(expectation, exports, {"A01": (acquired, aggregate)})
+ == ()
+ )
# Files and a same-named neighbor cannot replace the missing producer relation.
- unowned = RuntimeExportObservation.from_output_paths(exports.output_files, outputs=aggregate_outputs)
- failures = runtime_export_failures(expectation, unowned, {"A01": (acquired, aggregate)})
+ unowned = RuntimeExportObservation.from_output_paths(
+ exports.output_files, outputs=aggregate_outputs
+ )
+ failures = runtime_export_failures(
+ expectation, unowned, {"A01": (acquired, aggregate)}
+ )
assert len(failures) == 1 and "no matching table output" in failures[0]
# A correct address does not waive its own required columns.
acquired_outputs.runtime_export_paths[0].write_text("area\n1\n", encoding="utf-8")
invalid = RuntimeExportObservation.from_runtime_observations((transported,))
- failures = runtime_export_failures(expectation, invalid, {"A01": (acquired, aggregate)})
- assert any("site" in failure and "source_image_name" in failure for failure in failures)
+ failures = runtime_export_failures(
+ expectation, invalid, {"A01": (acquired, aggregate)}
+ )
+ assert any(
+ "site" in failure and "source_image_name" in failure for failure in failures
+ )
diff --git a/tests/unit/test_automatic_image_role_materialization_435.py b/tests/unit/test_automatic_image_role_materialization_435.py
index 25720be57c..f8a86a31ba 100644
--- a/tests/unit/test_automatic_image_role_materialization_435.py
+++ b/tests/unit/test_automatic_image_role_materialization_435.py
@@ -1,6 +1,9 @@
"""Default retained image artifacts keep their declared output role identity."""
-from openhcs.processing.materialization.options import NameByArtifactName, NameBySourceIdentity
+from openhcs.processing.materialization.options import (
+ NameByArtifactName,
+ NameBySourceIdentity,
+)
from dataclasses import replace
from pathlib import Path
import json
@@ -12,9 +15,16 @@
from polystore.filemanager import FileManager
from polystore.memory import MemoryStorageBackend
-from test_function_outputs import context_stub, function_step_plan, record_output_path
+from tests.unit.test_function_outputs import (
+ context_stub,
+ function_step_plan,
+ record_output_path,
+)
from openhcs.constants.constants import Backend
-from openhcs.core.aligned_image_payload import AlignedImageSliceContext, ImagePayloadBundleContext
+from openhcs.core.aligned_image_payload import (
+ AlignedImageSliceContext,
+ ImagePayloadBundleContext,
+)
from openhcs.core.artifacts.plans import ArtifactOutputPlan
from tests.materialization_plans import output_plan as materialization_output_plan
from openhcs.core.artifacts.specs import ArtifactSpec
@@ -22,7 +32,11 @@
from openhcs.core.compiled_step_plan import RuntimeArtifactMaterializationPlan
from openhcs.core.pipeline.artifact_planning import ArtifactOutputMaterializationPlanner
from openhcs.core.runtime_artifact_values import RuntimeValue
-from openhcs.core.runtime_image_values import ImageMetadataPayload, ImagePayloadMetadata, ImagePayloadMetadataCompositionMode
+from openhcs.core.runtime_image_values import (
+ ImageMetadataPayload,
+ ImagePayloadMetadata,
+ ImagePayloadMetadataCompositionMode,
+)
from openhcs.core.runtime_stores import RuntimeValueStore
from openhcs.core.source_metadata import SourceVoxelSpacing
from openhcs.core.source_projection import SourceProjectionMetadataSerializer
@@ -30,7 +44,9 @@
from openhcs.core.steps.function_output_identity import (
FunctionOutputIdentity,
)
-from openhcs.core.steps.function_artifact_materialization import ArtifactMaterializationTargetPlan
+from openhcs.core.steps.function_artifact_materialization import (
+ ArtifactMaterializationTargetPlan,
+)
from openhcs.core.steps.function_outputs import (
PrimaryImageMetadataTarget,
)
@@ -39,11 +55,15 @@
@pytest.mark.parametrize("plane_count", (1, 2))
-def test_automatic_two_role_images_publish_their_actual_saved_occurrences(tmp_path, plane_count):
- filemanager = FileManager({
- Backend.DISK.value: DiskStorageBackend(),
- Backend.MEMORY.value: MemoryStorageBackend(),
- })
+def test_automatic_two_role_images_publish_their_actual_saved_occurrences(
+ tmp_path, plane_count
+):
+ filemanager = FileManager(
+ {
+ Backend.DISK.value: DiskStorageBackend(),
+ Backend.MEMORY.value: MemoryStorageBackend(),
+ }
+ )
context = context_stub(filemanager, parser=SourceSchemaFilenameParser())
context.microscope_handler.source_name = "openhcsdata"
context.runtime_value_store = RuntimeValueStore()
@@ -60,17 +80,25 @@ def test_automatic_two_role_images_publish_their_actual_saved_occurrences(tmp_pa
plan.sub_dir = "images"
plan.analysis_results_dir = str(plan.output_dir)
plan.runtime_artifact_materialization = RuntimeArtifactMaterializationPlan(
- persistent_enabled=True, persistent_backend=Backend.DISK.value,
+ persistent_enabled=True,
+ persistent_backend=Backend.DISK.value,
)
context.step_plans = {plan.step_index: plan}
plans = tuple(
ArtifactOutputPlan(
- name=name, path=f"/memory/{name}.pkl", artifact_type=ImageArtifactType,
+ name=name,
+ path=f"/memory/{name}.pkl",
+ artifact_type=ImageArtifactType,
producer_step_index=plan.step_index,
producer_step_scope_id=plan.step_scope_id,
producer_step_name=plan.step_name,
materialization=ArtifactOutputMaterializationPlanner.materialization_for(
- ArtifactSpec(name=name, artifact_type=ImageArtifactType, plan_type=ArtifactOutputPlan), (),
+ ArtifactSpec(
+ name=name,
+ artifact_type=ImageArtifactType,
+ plan_type=ArtifactOutputPlan,
+ ),
+ (),
),
)
for name in ("raw_body_processing_role", "capped_outgrowth_processing_role")
@@ -84,8 +112,12 @@ def test_automatic_two_role_images_publish_their_actual_saved_occurrences(tmp_pa
(np.arange(20, dtype=np.float32).reshape(4, 5) + index) * factor,
ImagePayloadMetadata(
source_component_metadata={
- "well": "A01", "site": "1", "channel": "2", "z_index": str(index),
- "timepoint": "1", "extension": ".tif",
+ "well": "A01",
+ "site": "1",
+ "channel": "2",
+ "z_index": str(index),
+ "timepoint": "1",
+ "extension": ".tif",
},
source_image_names=(output.name,),
source_voxel_spacing=SourceVoxelSpacing((1.3556, 1.3556)),
@@ -94,115 +126,192 @@ def test_automatic_two_role_images_publish_their_actual_saved_occurrences(tmp_pa
for index in range(1, plane_count + 1)
)
value = (
- payloads[0] if plane_count == 1 else ImagePayloadBundleContext.from_payloads(
- payloads, metadata_mode=ImagePayloadMetadataCompositionMode.STACK,
+ payloads[0]
+ if plane_count == 1
+ else ImagePayloadBundleContext.from_payloads(
+ payloads,
+ metadata_mode=ImagePayloadMetadataCompositionMode.STACK,
).compose()
)
context.runtime_value_store.record(
RuntimeValue.normalize(output, value, axis_id="A01"),
- path=output.path, backend=Backend.MEMORY.value,
+ path=output.path,
+ backend=Backend.MEMORY.value,
)
expected[output.name] = []
for payload in payloads:
identity = FunctionOutputIdentity.from_filename_metadata(
- context.microscope_handler.parser, payload.metadata,
+ context.microscope_handler.parser,
+ payload.metadata,
).with_filename_qualifier(output_context.output_key)
- destination = plan.output_dir / identity.filename(context.microscope_handler.parser)
+ destination = plan.output_dir / identity.filename(
+ context.microscope_handler.parser
+ )
filemanager.ensure_directory(plan.output_dir, Backend.MEMORY.value)
filemanager.ensure_directory(plan.output_dir, Backend.DISK.value)
filemanager.save(payload, str(destination), Backend.MEMORY.value)
filemanager.save(payload.data, str(destination), Backend.DISK.value)
record_output_path(
- context, plan, str(destination), output_context=output_context,
- image_metadata=payload.metadata, identity=identity,
+ context,
+ plan,
+ str(destination),
+ output_context=output_context,
+ image_metadata=payload.metadata,
+ identity=identity,
)
expected[output.name].append((destination, payload.data))
saved = ArtifactMaterializationTargetPlan.materialize(context, plan)
- target = replace(PrimaryImageMetadataTarget.from_plan(plan), artifact_materializations=saved)
+ target = replace(
+ PrimaryImageMetadataTarget.from_plan(plan), artifact_materializations=saved
+ )
# The original implementation raises its real duplicate projection guard
# here: both automatic materializations used the same unqualified path.
structured = target.produced_projection_entries(context, plan)
structured = SourceProjectionMetadataSerializer.projection_fields(
structured.projection_paths
)
- assert {item["source_alias"] for item in structured["source_projection"]} == set(expected)
+ assert {item["source_alias"] for item in structured["source_projection"]} == set(
+ expected
+ )
assert len(structured["source_projection"]) == 2 * plane_count
for materialized in saved:
outputs = materialized.outputs_for_backend(Backend.DISK.value)
assert len(outputs) == plane_count
- for actual, (path, pixels) in zip(outputs, expected[materialized.materialization.output_plan.name], strict=True):
+ for actual, (path, pixels) in zip(
+ outputs,
+ expected[materialized.materialization.output_plan.name],
+ strict=True,
+ ):
assert actual.path == str(path)
np.testing.assert_array_equal(tifffile.imread(path), pixels)
target.write(context, produced_plan=plan)
document = json.loads((tmp_path / "openhcs_metadata.json").read_text())
- reopened = VirtualWorkspaceSourceProjection.from_openhcs_metadata(tmp_path, document)
+ reopened = VirtualWorkspaceSourceProjection.from_openhcs_metadata(
+ tmp_path, document
+ )
for name, expected_planes in expected.items():
for path, pixels in expected_planes:
- projection = reopened.source_projections_by_virtual_path[str(path.relative_to(tmp_path))]
+ projection = reopened.source_projections_by_virtual_path[
+ str(path.relative_to(tmp_path))
+ ]
assert projection.source_alias == name
assert projection.address.as_component_metadata()["channel"] == "2"
- assert projection.image_metadata.source_voxel_spacing == SourceVoxelSpacing((1.3556, 1.3556))
- np.testing.assert_array_equal(tifffile.imread(tmp_path / projection.ref.backend_address), pixels)
+ assert projection.image_metadata.source_voxel_spacing == SourceVoxelSpacing(
+ (1.3556, 1.3556)
+ )
+ np.testing.assert_array_equal(
+ tifffile.imread(tmp_path / projection.ref.backend_address), pixels
+ )
-@pytest.mark.parametrize("purpose", (
- "explicit_source", "explicit_template", "terminal_template",
- "explicit_artifact", "terminal_artifact", "unnamed_terminal", "unnamed_explicit",
- "explicit_source_declared_terminal", "terminal_source_declared_explicit",
-))
-def test_retained_role_policy_preserves_authored_and_unnamed_image_paths(tmp_path, purpose):
+@pytest.mark.parametrize(
+ "purpose",
+ (
+ "explicit_source",
+ "explicit_template",
+ "terminal_template",
+ "explicit_artifact",
+ "terminal_artifact",
+ "unnamed_terminal",
+ "unnamed_explicit",
+ "explicit_source_declared_terminal",
+ "terminal_source_declared_explicit",
+ ),
+)
+def test_retained_role_policy_preserves_authored_and_unnamed_image_paths(
+ tmp_path, purpose
+):
from openhcs.processing.materialization import (
- ImageFileOptions, MaterializationSpec,
- TerminalMaterializationSpec, materialization_outputs,
+ ImageFileOptions,
+ MaterializationSpec,
+ TerminalMaterializationSpec,
+ materialization_outputs,
)
options = ImageFileOptions(
filename_suffix=".tif",
- relative_path_template=("authored/plane_{index}.tif" if purpose.endswith("template") else None),
+ relative_path_template=(
+ "authored/plane_{index}.tif" if purpose.endswith("template") else None
+ ),
filename_identity=(
- NameByArtifactName if purpose.endswith("artifact")
- else NameBySourceIdentity
+ NameByArtifactName if purpose.endswith("artifact") else NameBySourceIdentity
),
)
spec = (
- TerminalMaterializationSpec(options) if purpose.startswith("terminal") or purpose == "unnamed_terminal"
+ TerminalMaterializationSpec(options)
+ if purpose.startswith("terminal") or purpose == "unnamed_terminal"
else MaterializationSpec(options)
)
declared_spec = (
- TerminalMaterializationSpec(options) if purpose == "explicit_source_declared_terminal"
- else MaterializationSpec(options) if purpose == "terminal_source_declared_explicit"
- else spec
+ TerminalMaterializationSpec(options)
+ if purpose == "explicit_source_declared_terminal"
+ else (
+ MaterializationSpec(options)
+ if purpose == "terminal_source_declared_explicit"
+ else spec
+ )
)
- output_plan = materialization_output_plan() if purpose in ("unnamed_terminal", "unnamed_explicit") else ArtifactOutputPlan(
- name="DeclaredRole", path="/memory/DeclaredRole.pkl",
- artifact_type=ImageArtifactType, materialization=declared_spec,
+ output_plan = (
+ materialization_output_plan()
+ if purpose in ("unnamed_terminal", "unnamed_explicit")
+ else ArtifactOutputPlan(
+ name="DeclaredRole",
+ path="/memory/DeclaredRole.pkl",
+ artifact_type=ImageArtifactType,
+ materialization=declared_spec,
+ )
)
payloads = tuple(
- ImageMetadataPayload(np.ones((4, 5), dtype=np.float32) * index, ImagePayloadMetadata(
- source_component_metadata={
- "well": "A01", "site": "1", "channel": "2", "z_index": str(index),
- "timepoint": "1", "extension": ".tif",
- },
- ))
+ ImageMetadataPayload(
+ np.ones((4, 5), dtype=np.float32) * index,
+ ImagePayloadMetadata(
+ source_component_metadata={
+ "well": "A01",
+ "site": "1",
+ "channel": "2",
+ "z_index": str(index),
+ "timepoint": "1",
+ "extension": ".tif",
+ },
+ ),
+ )
for index in (1, 2)
)
value = ImagePayloadBundleContext.from_payloads(
- payloads, metadata_mode=ImagePayloadMetadataCompositionMode.STACK,
+ payloads,
+ metadata_mode=ImagePayloadMetadataCompositionMode.STACK,
).compose()
filemanager = FileManager({Backend.MEMORY.value: MemoryStorageBackend()})
context = context_stub(filemanager, parser=SourceSchemaFilenameParser())
outputs = materialization_outputs(
- spec, value, str(tmp_path / "AuthoredBase"), filemanager,
- context=context, output_plan=output_plan,
+ spec,
+ value,
+ str(tmp_path / "AuthoredBase"),
+ filemanager,
+ context=context,
+ output_plan=output_plan,
variable_components=(Microscopy.ZIndex,),
)
expected_paths = (
- ("authored/plane_1.tif", "authored/plane_2.tif") if purpose.endswith("template")
- else ("AuthoredBase.tif",) if purpose.endswith("artifact")
- else ("A01_s001_w2_z001_t001_DeclaredRole.tif", "A01_s001_w2_z002_t001_DeclaredRole.tif") if purpose == "terminal_source_declared_explicit"
- else ("A01_s001_w2_z001_t001.tif", "A01_s001_w2_z002_t001.tif")
+ ("authored/plane_1.tif", "authored/plane_2.tif")
+ if purpose.endswith("template")
+ else (
+ ("AuthoredBase.tif",)
+ if purpose.endswith("artifact")
+ else (
+ (
+ "A01_s001_w2_z001_t001_DeclaredRole.tif",
+ "A01_s001_w2_z002_t001_DeclaredRole.tif",
+ )
+ if purpose == "terminal_source_declared_explicit"
+ else ("A01_s001_w2_z001_t001.tif", "A01_s001_w2_z002_t001.tif")
+ )
+ )
+ )
+ assert (
+ tuple(Path(output.path).relative_to(tmp_path).as_posix() for output in outputs)
+ == expected_paths
)
- assert tuple(Path(output.path).relative_to(tmp_path).as_posix() for output in outputs) == expected_paths
if len(outputs) == 2:
for actual, expected in zip(outputs, payloads, strict=True):
np.testing.assert_array_equal(actual.content, expected.data)
@@ -211,60 +320,130 @@ def test_retained_role_policy_preserves_authored_and_unnamed_image_paths(tmp_pat
def test_authored_duplicate_template_still_fails_before_any_save(tmp_path):
- from openhcs.processing.materialization import ImageFileOptions, TerminalMaterializationSpec, materialization_outputs
+ from openhcs.processing.materialization import (
+ ImageFileOptions,
+ TerminalMaterializationSpec,
+ materialization_outputs,
+ )
- spec = TerminalMaterializationSpec(ImageFileOptions(
- filename_suffix=".tif", relative_path_template="same.tif",
- ))
- output_plan = ArtifactOutputPlan(name="NamedRole", path="/memory/role.pkl", artifact_type=ImageArtifactType, materialization=spec)
+ spec = TerminalMaterializationSpec(
+ ImageFileOptions(
+ filename_suffix=".tif",
+ relative_path_template="same.tif",
+ )
+ )
+ output_plan = ArtifactOutputPlan(
+ name="NamedRole",
+ path="/memory/role.pkl",
+ artifact_type=ImageArtifactType,
+ materialization=spec,
+ )
payloads = tuple(
- ImageMetadataPayload(np.ones((4, 5), dtype=np.float32), ImagePayloadMetadata(
- source_component_metadata={"well": "A01", "site": "1", "channel": "2", "z_index": str(index), "timepoint": "1", "extension": ".tif"},
- )) for index in (1, 2)
+ ImageMetadataPayload(
+ np.ones((4, 5), dtype=np.float32),
+ ImagePayloadMetadata(
+ source_component_metadata={
+ "well": "A01",
+ "site": "1",
+ "channel": "2",
+ "z_index": str(index),
+ "timepoint": "1",
+ "extension": ".tif",
+ },
+ ),
+ )
+ for index in (1, 2)
)
- value = ImagePayloadBundleContext.from_payloads(payloads, metadata_mode=ImagePayloadMetadataCompositionMode.STACK).compose()
+ value = ImagePayloadBundleContext.from_payloads(
+ payloads, metadata_mode=ImagePayloadMetadataCompositionMode.STACK
+ ).compose()
manager = FileManager({Backend.MEMORY.value: MemoryStorageBackend()})
with pytest.raises(ValueError, match="produced duplicate paths"):
- materialization_outputs(spec, value, str(tmp_path / "role"), manager,
- context=context_stub(manager, parser=SourceSchemaFilenameParser()),
- output_plan=output_plan)
+ materialization_outputs(
+ spec,
+ value,
+ str(tmp_path / "role"),
+ manager,
+ context=context_stub(manager, parser=SourceSchemaFilenameParser()),
+ output_plan=output_plan,
+ )
assert not (tmp_path / "same.tif").exists()
@pytest.mark.parametrize("suffix", (".tif", ".png", ".ome.tif", ".labels.tif"))
@pytest.mark.parametrize("plane_count", (1, 2))
-def test_retained_image_qualifier_respects_complete_writer_suffix(tmp_path, suffix, plane_count):
- from openhcs.core.steps.function_artifact_materialization import RuntimeArtifactMaterialization
- from openhcs.processing.materialization import ImageFileOptions, TerminalMaterializationSpec
+def test_retained_image_qualifier_respects_complete_writer_suffix(
+ tmp_path, suffix, plane_count
+):
+ from openhcs.core.steps.function_artifact_materialization import (
+ RuntimeArtifactMaterialization,
+ )
+ from openhcs.processing.materialization import (
+ ImageFileOptions,
+ TerminalMaterializationSpec,
+ )
manager = FileManager({Backend.MEMORY.value: MemoryStorageBackend()})
context = context_stub(manager, parser=SourceSchemaFilenameParser())
- plan = function_step_plan("Retained format role", variable_components=(Microscopy.ZIndex,) if plane_count > 1 else ())
+ plan = function_step_plan(
+ "Retained format role",
+ variable_components=(Microscopy.ZIndex,) if plane_count > 1 else (),
+ )
plan.output_dir = tmp_path / "images"
plan.analysis_results_dir = str(plan.output_dir)
output_plan = ArtifactOutputPlan(
- name="Role Name", path="/memory/role.pkl", artifact_type=ImageArtifactType,
- materialization=TerminalMaterializationSpec(ImageFileOptions(filename_suffix=suffix)),
+ name="Role Name",
+ path="/memory/role.pkl",
+ artifact_type=ImageArtifactType,
+ materialization=TerminalMaterializationSpec(
+ ImageFileOptions(filename_suffix=suffix)
+ ),
)
payloads = tuple(
- ImageMetadataPayload(np.ones((4, 5), dtype=np.uint8) * index, ImagePayloadMetadata(
- source_component_metadata={"well": "A01", "site": "1", "channel": "2", "z_index": str(index), "timepoint": "1", "extension": ".tif"},
- )) for index in range(1, plane_count + 1)
+ ImageMetadataPayload(
+ np.ones((4, 5), dtype=np.uint8) * index,
+ ImagePayloadMetadata(
+ source_component_metadata={
+ "well": "A01",
+ "site": "1",
+ "channel": "2",
+ "z_index": str(index),
+ "timepoint": "1",
+ "extension": ".tif",
+ },
+ ),
+ )
+ for index in range(1, plane_count + 1)
+ )
+ value = (
+ payloads[0]
+ if plane_count == 1
+ else ImagePayloadBundleContext.from_payloads(
+ payloads,
+ metadata_mode=ImagePayloadMetadataCompositionMode.STACK,
+ ).compose()
)
- value = payloads[0] if plane_count == 1 else ImagePayloadBundleContext.from_payloads(
- payloads, metadata_mode=ImagePayloadMetadataCompositionMode.STACK,
- ).compose()
store = RuntimeValueStore()
- record = store.record(RuntimeValue.normalize(output_plan, value, axis_id="A01"), path=output_plan.path, backend=Backend.MEMORY.value)
+ record = store.record(
+ RuntimeValue.normalize(output_plan, value, axis_id="A01"),
+ path=output_plan.path,
+ backend=Backend.MEMORY.value,
+ )
outputs = RuntimeArtifactMaterialization.from_record(
- output_plan=output_plan, record=record, plan=plan, context=context,
+ output_plan=output_plan,
+ record=record,
+ plan=plan,
+ context=context,
).outputs(plan, context)
assert len(outputs) == plane_count
for index, (output, payload) in enumerate(zip(outputs, payloads, strict=True), 1):
identity = FunctionOutputIdentity.from_filename_metadata(
- context.microscope_handler.parser, payload.metadata,
+ context.microscope_handler.parser,
+ payload.metadata,
).with_filename_qualifier(output_plan.name)
- expected = replace(identity, extension=suffix).filename(context.microscope_handler.parser)
+ expected = replace(identity, extension=suffix).filename(
+ context.microscope_handler.parser
+ )
assert Path(output.path).name == expected
assert Path(output.path).name.endswith("_Role_Name" + suffix)
assert output.metadata.source_component_metadata["channel"] == "2"
@@ -273,60 +452,121 @@ def test_retained_image_qualifier_respects_complete_writer_suffix(tmp_path, suff
def test_distinct_aliases_that_normalize_to_one_filename_still_conflict(tmp_path):
- from openhcs.core.steps.function_artifact_materialization import RuntimeArtifactMaterialization
- from openhcs.processing.materialization import ImageFileOptions, TerminalMaterializationSpec
+ from openhcs.core.steps.function_artifact_materialization import (
+ RuntimeArtifactMaterialization,
+ )
+ from openhcs.processing.materialization import (
+ ImageFileOptions,
+ TerminalMaterializationSpec,
+ )
manager = FileManager({Backend.MEMORY.value: MemoryStorageBackend()})
context = context_stub(manager, parser=SourceSchemaFilenameParser())
plan = function_step_plan("Ambiguous role spelling")
plan.output_dir = tmp_path / "images"
plan.analysis_results_dir = str(plan.output_dir)
- value = ImageMetadataPayload(np.ones((4, 5), dtype=np.float32), ImagePayloadMetadata(
- source_component_metadata={"well": "A01", "site": "1", "channel": "2", "z_index": "1", "timepoint": "1", "extension": ".tif"},
- ))
+ value = ImageMetadataPayload(
+ np.ones((4, 5), dtype=np.float32),
+ ImagePayloadMetadata(
+ source_component_metadata={
+ "well": "A01",
+ "site": "1",
+ "channel": "2",
+ "z_index": "1",
+ "timepoint": "1",
+ "extension": ".tif",
+ },
+ ),
+ )
paths = []
store = RuntimeValueStore()
for name in ("a/b", "a b"):
- output_plan = ArtifactOutputPlan(name=name, path=f"/memory/{name}.pkl", artifact_type=ImageArtifactType,
- materialization=TerminalMaterializationSpec(ImageFileOptions(filename_suffix=".tif")))
- record = store.record(RuntimeValue.normalize(output_plan, value, axis_id="A01"), path=output_plan.path, backend=Backend.MEMORY.value)
- outputs = RuntimeArtifactMaterialization.from_record(output_plan=output_plan, record=record, plan=plan, context=context).outputs(plan, context)
+ output_plan = ArtifactOutputPlan(
+ name=name,
+ path=f"/memory/{name}.pkl",
+ artifact_type=ImageArtifactType,
+ materialization=TerminalMaterializationSpec(
+ ImageFileOptions(filename_suffix=".tif")
+ ),
+ )
+ record = store.record(
+ RuntimeValue.normalize(output_plan, value, axis_id="A01"),
+ path=output_plan.path,
+ backend=Backend.MEMORY.value,
+ )
+ outputs = RuntimeArtifactMaterialization.from_record(
+ output_plan=output_plan, record=record, plan=plan, context=context
+ ).outputs(plan, context)
paths.append(outputs[0].path)
assert paths[0] == paths[1]
# Existing main-flow destination authority rejects this declared naming
# collision instead of choosing a new suffix or inventing a channel.
from openhcs.core.steps.function_output_identity import FunctionOutputIdentity
+
with pytest.raises(ValueError, match="duplicate"):
- FunctionOutputIdentity.validate_output_paths(paths, input_paths=[], step_name=plan.step_name, pattern_repr="declared roles")
+ FunctionOutputIdentity.validate_output_paths(
+ paths,
+ input_paths=[],
+ step_name=plan.step_name,
+ pattern_repr="declared roles",
+ )
-def test_batch_binds_actual_purpose_once_without_rewriting_plan_or_source(monkeypatch, tmp_path):
- from openhcs.processing.materialization import ImageFileOptions, MaterializationSpec, TerminalMaterializationSpec
- from openhcs.processing.materialization.core import MaterializationBatch, MaterializationContext, Output, WriterSpec, _WRITERS_BY_OPTIONS
+def test_batch_binds_actual_purpose_once_without_rewriting_plan_or_source(
+ monkeypatch, tmp_path
+):
+ from openhcs.processing.materialization import (
+ ImageFileOptions,
+ MaterializationSpec,
+ TerminalMaterializationSpec,
+ )
+ from openhcs.processing.materialization.core import (
+ MaterializationBatch,
+ MaterializationContext,
+ Output,
+ WriterSpec,
+ _WRITERS_BY_OPTIONS,
+ )
options = ImageFileOptions(filename_suffix=".tif")
declared = TerminalMaterializationSpec(options)
explicit = MaterializationSpec(options)
- plan = ArtifactOutputPlan(name="Role", path="/memory/role", artifact_type=ImageArtifactType, materialization=declared)
+ plan = ArtifactOutputPlan(
+ name="Role",
+ path="/memory/role",
+ artifact_type=ImageArtifactType,
+ materialization=declared,
+ )
manager = FileManager({Backend.MEMORY.value: MemoryStorageBackend()})
source = {"physical_source": "original"}
context = MaterializationContext(
- base_path=str(tmp_path / "role"), backends=[], backend_kwargs={},
- filemanager=manager, extra_inputs=source, output_plan=plan,
+ base_path=str(tmp_path / "role"),
+ backends=[],
+ backend_kwargs={},
+ filemanager=manager,
+ extra_inputs=source,
+ output_plan=plan,
materialization_spec=declared,
)
seen = []
+
def custom_writer(data, actual_options, actual_context):
assert actual_options is options
assert actual_context.output_plan is plan
assert actual_context.extra_inputs is source
seen.append(actual_context)
return [Output(path=str(tmp_path / "custom.tif"), content=data)]
- monkeypatch.setitem(_WRITERS_BY_OPTIONS, ImageFileOptions, WriterSpec(
- options_type=ImageFileOptions,
- write=custom_writer, primary_path=lambda outputs: outputs[0].path,
- candidate_paths=lambda _options, base: (base,),
- ))
+
+ monkeypatch.setitem(
+ _WRITERS_BY_OPTIONS,
+ ImageFileOptions,
+ WriterSpec(
+ options_type=ImageFileOptions,
+ write=custom_writer,
+ primary_path=lambda outputs: outputs[0].path,
+ candidate_paths=lambda _options, base: (base,),
+ ),
+ )
pixels = np.ones((4, 5), dtype=np.float32)
first = MaterializationBatch.render(explicit, pixels, context)
second = MaterializationBatch.render(declared, pixels, first.context)
@@ -339,20 +579,48 @@ def custom_writer(data, actual_options, actual_context):
assert source == {"physical_source": "original"}
-def test_direct_image_writer_without_purpose_binding_retains_legacy_source_path(tmp_path):
- from openhcs.processing.materialization import ImageFileOptions, TerminalMaterializationSpec
- from openhcs.processing.materialization.core import MaterializationContext, write_image_file
+def test_direct_image_writer_without_purpose_binding_retains_legacy_source_path(
+ tmp_path,
+):
+ from openhcs.processing.materialization import (
+ ImageFileOptions,
+ TerminalMaterializationSpec,
+ )
+ from openhcs.processing.materialization.core import (
+ MaterializationContext,
+ write_image_file,
+ )
options = ImageFileOptions(filename_suffix=".tif")
- plan = ArtifactOutputPlan(name="Role", path="/memory/role", artifact_type=ImageArtifactType,
- materialization=TerminalMaterializationSpec(options))
+ plan = ArtifactOutputPlan(
+ name="Role",
+ path="/memory/role",
+ artifact_type=ImageArtifactType,
+ materialization=TerminalMaterializationSpec(options),
+ )
manager = FileManager({Backend.MEMORY.value: MemoryStorageBackend()})
- value = ImageMetadataPayload(np.ones((4, 5), dtype=np.float32), ImagePayloadMetadata(
- source_component_metadata={"well": "A01", "site": "1", "channel": "2", "z_index": "1", "timepoint": "1", "extension": ".tif"},
- ))
- context = MaterializationContext(base_path=str(tmp_path / "role"), backends=[], backend_kwargs={},
- filemanager=manager, extra_inputs={}, output_plan=plan,
- context=context_stub(manager, parser=SourceSchemaFilenameParser()))
+ value = ImageMetadataPayload(
+ np.ones((4, 5), dtype=np.float32),
+ ImagePayloadMetadata(
+ source_component_metadata={
+ "well": "A01",
+ "site": "1",
+ "channel": "2",
+ "z_index": "1",
+ "timepoint": "1",
+ "extension": ".tif",
+ },
+ ),
+ )
+ context = MaterializationContext(
+ base_path=str(tmp_path / "role"),
+ backends=[],
+ backend_kwargs={},
+ filemanager=manager,
+ extra_inputs={},
+ output_plan=plan,
+ context=context_stub(manager, parser=SourceSchemaFilenameParser()),
+ )
outputs = write_image_file(value, options, context)
assert Path(outputs[0].path).name == "A01_s001_w2_z001_t001.tif"
assert context.materialization_spec is None
@@ -361,23 +629,50 @@ def test_direct_image_writer_without_purpose_binding_retains_legacy_source_path(
@pytest.mark.parametrize("purpose", ("named_terminal", "explicit", "unnamed_terminal"))
-def test_parser_free_render_requires_parser_only_for_automatic_named_role(tmp_path, purpose):
- from openhcs.processing.materialization import ImageFileOptions, MaterializationSpec, TerminalMaterializationSpec, materialization_outputs
+def test_parser_free_render_requires_parser_only_for_automatic_named_role(
+ tmp_path, purpose
+):
+ from openhcs.processing.materialization import (
+ ImageFileOptions,
+ MaterializationSpec,
+ TerminalMaterializationSpec,
+ materialization_outputs,
+ )
options = ImageFileOptions(filename_suffix=".tif")
- spec = MaterializationSpec(options) if purpose == "explicit" else TerminalMaterializationSpec(options)
- plan = materialization_output_plan() if purpose == "unnamed_terminal" else ArtifactOutputPlan(
- name="Role", path="/memory/role", artifact_type=ImageArtifactType, materialization=spec,
+ spec = (
+ MaterializationSpec(options)
+ if purpose == "explicit"
+ else TerminalMaterializationSpec(options)
+ )
+ plan = (
+ materialization_output_plan()
+ if purpose == "unnamed_terminal"
+ else ArtifactOutputPlan(
+ name="Role",
+ path="/memory/role",
+ artifact_type=ImageArtifactType,
+ materialization=spec,
+ )
)
manager = FileManager({Backend.MEMORY.value: MemoryStorageBackend()})
- value = ImageMetadataPayload(np.ones((4, 5), dtype=np.float32), ImagePayloadMetadata(
- source_path="/physical/OriginalSource.tif",
- ))
+ value = ImageMetadataPayload(
+ np.ones((4, 5), dtype=np.float32),
+ ImagePayloadMetadata(
+ source_path="/physical/OriginalSource.tif",
+ ),
+ )
if purpose == "named_terminal":
- with pytest.raises(ValueError, match="Parser-backed source-stem authority requires a parser"):
- materialization_outputs(spec, value, str(tmp_path / "Role"), manager, output_plan=plan)
+ with pytest.raises(
+ ValueError, match="Parser-backed source-stem authority requires a parser"
+ ):
+ materialization_outputs(
+ spec, value, str(tmp_path / "Role"), manager, output_plan=plan
+ )
else:
- outputs = materialization_outputs(spec, value, str(tmp_path / "Role"), manager, output_plan=plan)
+ outputs = materialization_outputs(
+ spec, value, str(tmp_path / "Role"), manager, output_plan=plan
+ )
assert Path(outputs[0].path).name == "OriginalSource.tif"
np.testing.assert_array_equal(outputs[0].content, value.data)
assert value.metadata.source_path == "/physical/OriginalSource.tif"
diff --git a/tests/unit/test_build_mcp_knowledge_assets.py b/tests/unit/test_build_mcp_knowledge_assets.py
index aa05965926..07adb739b0 100644
--- a/tests/unit/test_build_mcp_knowledge_assets.py
+++ b/tests/unit/test_build_mcp_knowledge_assets.py
@@ -3,13 +3,15 @@
import glob
import json
import tomllib
-from pathlib import Path
+from pathlib import Path, PureWindowsPath
import pytest
from scripts.build_mcp_knowledge_assets import (
KNOWLEDGE_MANIFEST_RELATIVE_PATH,
+ PACKAGED_KNOWLEDGE_PATH_BUDGET,
PACKAGED_KNOWLEDGE_ROOT_RELATIVE_PATH,
+ knowledge_asset_projections,
project_knowledge_assets,
)
from openhcs.agent.knowledge_manifest_schema import (
@@ -294,3 +296,47 @@ def test_wheel_globs_include_the_projected_hidden_plugin_manifest(tmp_path):
assert plugin_manifest in included
plugin_root = plugin_manifest.parent.parent
assert {path for path in projected if path.is_relative_to(plugin_root)} <= included
+
+
+PROJECT_ROOT = Path(__file__).resolve().parents[2]
+# The installer CI proof root under the runner profile, with long paths disabled.
+WINDOWS_INSTALLED_KNOWLEDGE_ROOT = PureWindowsPath(
+ r"C:\Users\runneradmin\AppData\Local\OpenHCS Installer Smoke",
+ "env-00000000",
+ "Lib",
+ "site-packages",
+ *PACKAGED_KNOWLEDGE_ROOT_RELATIVE_PATH.parts,
+)
+WINDOWS_MAX_PATH = 260
+
+
+def test_every_packaged_resource_fits_the_windows_installed_path_budget():
+ workflow = PROJECT_ROOT / ".github" / "workflows" / "integration-tests.yml"
+ assert '"OpenHCS Installer Smoke"' in workflow.read_text(encoding="utf-8")
+ root_length = len(str(WINDOWS_INSTALLED_KNOWLEDGE_ROOT)) + 1
+ assert root_length + PACKAGED_KNOWLEDGE_PATH_BUDGET < WINDOWS_MAX_PATH
+ projections = knowledge_asset_projections(PROJECT_ROOT)
+ assert any(path.suffix == ".cppipe" for path in projections)
+ too_long = [
+ str(WINDOWS_INSTALLED_KNOWLEDGE_ROOT / relative)
+ for relative in projections
+ if len(str(WINDOWS_INSTALLED_KNOWLEDGE_ROOT / relative)) >= WINDOWS_MAX_PATH
+ ]
+ assert too_long == []
+
+
+@pytest.mark.parametrize(
+ "case_name", ["x" * PACKAGED_KNOWLEDGE_PATH_BUDGET, "nested/name", "name:stream"]
+)
+def test_native_placement_rejects_unbudgeted_case_names(tmp_path, case_name):
+ project_root, manifest_path, _ = _project_with_document(tmp_path)
+ recipe_path = project_root / "recipe.json"
+ recipe_path.write_text(
+ json.dumps({"cases": [{"name": case_name, "cppipe_path": "a.cppipe"}]}),
+ encoding="utf-8",
+ )
+ catalog = json.loads(manifest_path.read_text(encoding="utf-8"))
+ catalog["documents"].append({"source_path": "recipe.json"})
+ manifest_path.write_text(json.dumps(catalog), encoding="utf-8")
+ with pytest.raises(ValueError):
+ knowledge_asset_projections(project_root)
diff --git a/tests/unit/test_build_website.py b/tests/unit/test_build_website.py
index 526f7f91f5..3a7563a35c 100644
--- a/tests/unit/test_build_website.py
+++ b/tests/unit/test_build_website.py
@@ -949,8 +949,9 @@ def test_website_source_and_workflow_follow_package_metadata_authorities():
assert RELEASE_VERSION_TOKEN in source_html
assert "0.5.21" not in source_html
assert "0.5.22" not in source_html
- assert workflow.count(' - "openhcs/__init__.py"') == 2
- assert workflow.count(' - "openhcs/resources/assets/openhcs-*.svg"') == 2
+ # The site imports openhcs (version, assets, session operations), so any
+ # change under the package re-validates it.
+ assert workflow.count(' - "openhcs/**"') == 2
for page_name in ("privacy.html", "support.html", "terms.html"):
page_source = (REPO_ROOT / "website" / page_name).read_text(encoding="utf-8")
assert CONTACT_EMAIL_TOKEN in page_source
diff --git a/tests/unit/test_ci_import_guards.py b/tests/unit/test_ci_import_guards.py
new file mode 100644
index 0000000000..ccc9036f75
--- /dev/null
+++ b/tests/unit/test_ci_import_guards.py
@@ -0,0 +1,38 @@
+"""CI runs pytest with ``--import-mode=importlib`` against the installed wheel
+(``scripts/run_installed_tests.py``). In that mode a test directory is not on
+``sys.path``, so a test module can reach shared helpers only through the
+``tests`` package, never by a sibling module's bare name."""
+
+from __future__ import annotations
+
+import ast
+from pathlib import Path
+
+TESTS_ROOT = Path(__file__).resolve().parents[1]
+
+
+def _test_tree_module_names() -> frozenset[str]:
+ return frozenset(path.stem for path in TESTS_ROOT.rglob("*.py")) - {"__init__"}
+
+
+def test_test_modules_import_helpers_through_the_tests_package() -> None:
+ local_names = _test_tree_module_names()
+ offenders: list[str] = []
+ for path in sorted(TESTS_ROOT.rglob("*.py")):
+ tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
+ for node in ast.walk(tree):
+ if isinstance(node, ast.ImportFrom) and node.level == 0 and node.module:
+ names = (node.module,)
+ elif isinstance(node, ast.Import):
+ names = tuple(alias.name for alias in node.names)
+ else:
+ continue
+ for name in names:
+ if name.split(".")[0] in local_names:
+ offenders.append(
+ f"{path.relative_to(TESTS_ROOT.parent)}:{node.lineno}: {name}"
+ )
+ assert not offenders, (
+ "Import test helpers as tests..; bare sibling imports "
+ "fail under --import-mode=importlib:\n" + "\n".join(offenders)
+ )
diff --git a/tests/unit/test_ci_package_boundaries.py b/tests/unit/test_ci_package_boundaries.py
index e25b647bf8..ee9db39b8b 100644
--- a/tests/unit/test_ci_package_boundaries.py
+++ b/tests/unit/test_ci_package_boundaries.py
@@ -476,14 +476,16 @@ def test_website_source_validation_uses_local_candidate_owners() -> None:
install_step = next(
step
for step in validate_job["steps"]
- if step.get("name") == "Install website test dependencies"
+ if step.get("name")
+ == "Install the OpenHCS candidate and the repository's test extra"
)
install_script = install_step["run"]
assert checkout_step["with"]["submodules"] == "recursive"
- assert "discover_local_projects" in install_script
- assert 'python -m pip install "${local_project_paths[@]}"' in install_script
- assert 'python -m pip install "${declared_requirements[@]}"' not in install_script
+ assert "python -m scripts.install_ci_candidate" in install_script
+ assert "--dependency-source submodules" in install_script
+ # pytest.ini's addopts need the dev extra's plugins (pytest-xdist).
+ assert "--extras dev" in install_script
def test_publish_workflow_consumes_release_requirements_authority() -> None:
diff --git a/tests/unit/test_colocalization_metric_setting_authority.py b/tests/unit/test_colocalization_metric_setting_authority.py
index 59d99fede6..e5ae55a195 100644
--- a/tests/unit/test_colocalization_metric_setting_authority.py
+++ b/tests/unit/test_colocalization_metric_setting_authority.py
@@ -153,7 +153,7 @@ def test_recorded_source_pair_features_exclude_disabled_metrics(disabled, famili
for name in enabled.columns
if not any(
name.startswith(
- f"Correlation_{(family if family == "RWC" else family.title())}_"
+ f"Correlation_{(family if family == 'RWC' else family.title())}_"
)
for family in families
)
diff --git a/tests/unit/test_dataset_access.py b/tests/unit/test_dataset_access.py
new file mode 100644
index 0000000000..1ed8f06e1b
--- /dev/null
+++ b/tests/unit/test_dataset_access.py
@@ -0,0 +1,22 @@
+"""Whether a dataset initializes in place is the filesystem's answer.
+
+The non-writable case (POSIX mode bits, Windows ACLs) is exercised by the
+read-only integration journeys, which make a genuinely non-writable tree.
+"""
+
+from pathlib import Path
+
+from openhcs.authoring.session.session import UnrestrictedDatasetAccess
+
+
+def test_writable_root_initializes_in_place_and_the_probe_leaves_nothing(
+ tmp_path: Path,
+) -> None:
+ (tmp_path / "image.tif").write_bytes(b"pixels")
+
+ assert UnrestrictedDatasetAccess().initializes_in_place(tmp_path)
+ assert [path.name for path in tmp_path.iterdir()] == ["image.tif"]
+
+
+def test_missing_root_does_not_initialize_in_place(tmp_path: Path) -> None:
+ assert not UnrestrictedDatasetAccess().initializes_in_place(tmp_path / "absent")
diff --git a/tests/unit/test_debug_runtime.py b/tests/unit/test_debug_runtime.py
index 0c51d22d04..1ab0ed86fd 100644
--- a/tests/unit/test_debug_runtime.py
+++ b/tests/unit/test_debug_runtime.py
@@ -187,6 +187,7 @@ def lane(cls) -> WorkerLaneExecutionContext:
worker_slot="worker_0",
worker_assignments={"worker_0": ("A01",)},
)
+
SEGMENT_NAME = "segment"
@staticmethod
@@ -564,10 +565,15 @@ def test_debug_artifact_boundary_has_no_name_key_reconstruction():
"DebugArtifactRefProjection",
{"from_artifact_plans"},
),
+ (
+ repository_root / "openhcs/core/steps/function_runtime.py",
+ "FunctionCoreDebugMixin",
+ {"debug_artifacts", "debug_event"},
+ ),
(
repository_root / "openhcs/core/steps/function_runtime.py",
"FunctionCoreExecutor",
- {"debug_artifacts", "debug_event", "invoke"},
+ {"invoke"},
),
)
violations: list[tuple[str, int, str]] = []
@@ -646,9 +652,7 @@ def test_debug_execution_config_normalizes_compile_cache_payload():
assert compile_payload["start_step_index"] == 0
assert compile_payload["start_after_invocation_key"] is None
assert compile_payload["selected_source_group"] == DebugRuntimeFixture.AXIS_ID
- assert (
- compile_payload["replay_mode"] == PersistentPausedWorkerReplay.name
- )
+ assert compile_payload["replay_mode"] == PersistentPausedWorkerReplay.name
@pytest.mark.parametrize(
@@ -987,8 +991,7 @@ def test_debug_step_executes_one_function_pattern_invocation():
np.testing.assert_array_equal(result, np.array([2, 4, 6]))
assert [
- DebugProgressContext.from_context(event.context).event_type
- for event in emitted
+ DebugProgressContext.from_context(event.context).event_type for event in emitted
] == [BeforeInvocationDebugBoundary, AfterInvocationDebugBoundary]
@@ -1024,7 +1027,9 @@ def test_debug_step_can_advance_past_current_function_pattern_invocation():
np.testing.assert_array_equal(result, np.array([2, 3, 4]))
assert len(emitted) == 2
- assert DebugProgressContext.from_context(emitted[0].context).cursor.invocation_key.endswith(":1:plus_one")
+ assert DebugProgressContext.from_context(
+ emitted[0].context
+ ).cursor.invocation_key.endswith(":1:plus_one")
def test_execute_chain_emits_debug_exception_event():
@@ -1059,7 +1064,11 @@ def test_projection_exception_contains_complete_invocation_context_without_debug
func=DebugRuntimeFixture.projection_mismatch,
function_name="correct_illumination_calculate",
module_name="CorrectIlluminationCalculate",
- input_memory_type=MEMORY_TYPE_NUMPY, output_memory_type=MEMORY_TYPE_NUMPY, runtime_adapter=runtime_adapter, processing_contract=Pure2DContract, runtime_image_execution_mode=FullStackExecution,
+ input_memory_type=MEMORY_TYPE_NUMPY,
+ output_memory_type=MEMORY_TYPE_NUMPY,
+ runtime_adapter=runtime_adapter,
+ processing_contract=Pure2DContract,
+ runtime_image_execution_mode=FullStackExecution,
)
image = ImagePayloadMetadata(
source_image_names=("OrigStain1", "OrigStain2"),
@@ -1097,10 +1106,7 @@ def test_projection_exception_contains_complete_invocation_context_without_debug
assert "declared_cardinality=2" in message
assert "selected_runtime_plane=preserved_stack" in message
assert "execution_axis_cardinality=3" in message
- assert (
- "image_payload_execution_mode=FullStackExecution"
- in message
- )
+ assert "image_payload_execution_mode=FullStackExecution" in message
assert "processing_contract=Pure2DContract" in message
assert isinstance(exc.__cause__, RuntimeSliceProjectionDeclarationError)
assert str(exc.__cause__) == "projection preflight mismatch"
@@ -1286,7 +1292,8 @@ def test_progress_debug_event_sink_uses_injected_progress_emitter():
assert emitted[0].status is StartedProgressStatus
assert emitted[0].context is not None
assert (
- emitted[0].context[DebugProgressContext.context_key]["debug_session_id"] == DebugRuntimeFixture.DEBUG_SESSION_ID
+ emitted[0].context[DebugProgressContext.context_key]["debug_session_id"]
+ == DebugRuntimeFixture.DEBUG_SESSION_ID
)
assert (
emitted[0].context[DebugProgressContext.context_key]["snapshot_store_ref"]
@@ -1464,9 +1471,9 @@ def test_debug_execution_policy_skips_before_start_step_index():
def test_debug_session_marks_current_cursor_dirty_nominally():
cursor = DebugRuntimeFixture.cursor()
- session = DebugSession.create(subject_id=DebugRuntimeFixture.SUBJECT_ID).with_cursor(
- cursor
- )
+ session = DebugSession.create(
+ subject_id=DebugRuntimeFixture.SUBJECT_ID
+ ).with_cursor(cursor)
dirty_session = session.mark_dirty_from_cursor()
diff --git a/tests/unit/test_derived_role_publication_435.py b/tests/unit/test_derived_role_publication_435.py
index e7d7a202be..e11ba17894 100644
--- a/tests/unit/test_derived_role_publication_435.py
+++ b/tests/unit/test_derived_role_publication_435.py
@@ -1,6 +1,9 @@
"""Issue435: real saved image occurrences projected through two existing owners."""
-from openhcs.processing.materialization.options import NameByArtifactName, NameBySourceIdentity
+from openhcs.processing.materialization.options import (
+ NameByArtifactName,
+ NameBySourceIdentity,
+)
from dataclasses import asdict, replace
import json
@@ -11,59 +14,99 @@
from polystore.filemanager import FileManager
from polystore.memory import MemoryStorageBackend
-from test_function_outputs import (
- context_stub, function_step_plan, record_output_path,
+from tests.unit.test_function_outputs import (
+ context_stub,
+ function_step_plan,
+ record_output_path,
)
from openhcs.core.dataset_sources.source_schema import SourceSchemaFilenameParser
from openhcs.constants.constants import Backend
-from openhcs.core.aligned_image_payload import AlignedImageSliceContext, stack_image_payloads
+from openhcs.core.aligned_image_payload import (
+ AlignedImageSliceContext,
+ stack_image_payloads,
+)
from openhcs.core.artifacts.plans import ArtifactOutputPlan
from openhcs.core.artifacts.kinds import ImageArtifactType, ObjectLabelsArtifactType
-from openhcs.core.component_group_scope import ComponentGroupScope, RuntimeExecutionAxisScope
+from openhcs.core.component_group_scope import (
+ ComponentGroupScope,
+ RuntimeExecutionAxisScope,
+)
from openhcs.core.compiled_step_plan import RuntimeArtifactMaterializationPlan
from openhcs.core.runtime_artifact_values import RuntimeValue
from openhcs.core.runtime_image_values import (
- ImageMetadataPayload, ImagePayloadMetadata, ImagePayloadMetadataCompositionMode,
+ ImageMetadataPayload,
+ ImagePayloadMetadata,
+ ImagePayloadMetadataCompositionMode,
)
from openhcs.core.runtime_stores import RuntimeValueStore
-from openhcs.core.runtime_object_label_building import SourceImageObjectLabelBuildRequest
+from openhcs.core.runtime_object_label_building import (
+ SourceImageObjectLabelBuildRequest,
+)
from openhcs.core.source_metadata import SourceVoxelSpacing
from openhcs.core.source_image_provenance import (
- SourceImageProvenance, SourceImageProvenancePlanes,
+ SourceImageProvenance,
+ SourceImageProvenancePlanes,
)
from openhcs.core.source_projection import SourceProjectionMetadataSerializer
from openhcs.core.source_workspace_projection import VirtualWorkspaceSourceProjection
-from openhcs.core.steps.function_artifact_materialization import ArtifactMaterializationTargetPlan
+from openhcs.core.steps.function_artifact_materialization import (
+ ArtifactMaterializationTargetPlan,
+)
from openhcs.core.steps.function_outputs import (
PrimaryImageMetadataTarget,
)
from openhcs.core.steps.function_output_identity import FunctionOutputIdentity
-from openhcs.core.runtime_plane_projection import RuntimePlaneAxis, RuntimePlaneAxisValueProjection
+from openhcs.core.runtime_plane_projection import (
+ RuntimePlaneAxis,
+ RuntimePlaneAxisValueProjection,
+)
from python_introspect import to_jsonable
from openhcs.processing.materialization import (
- ImageFileOptions, MaterializationSpec,
+ ImageFileOptions,
+ MaterializationSpec,
)
from openhcs.domains.microscopy.axes import Microscopy
-@pytest.mark.parametrize("scenario,aggregate", (
- *((scenario, False) for scenario in (
- "same_occurrence", "different_path", "stale_scope", "conflicting_metadata", "different_kind",
- )),
- *((scenario, True) for scenario in (
- "same_occurrence", "different_path", "stale_scope", "conflicting_metadata", "different_kind",
- )),
- ("conflicting_scope", True),
- ("mosaic_context", False),
-))
-def test_saved_roles_publish_once_per_persisted_occurrence(tmp_path, scenario, aggregate):
+@pytest.mark.parametrize(
+ "scenario,aggregate",
+ (
+ *(
+ (scenario, False)
+ for scenario in (
+ "same_occurrence",
+ "different_path",
+ "stale_scope",
+ "conflicting_metadata",
+ "different_kind",
+ )
+ ),
+ *(
+ (scenario, True)
+ for scenario in (
+ "same_occurrence",
+ "different_path",
+ "stale_scope",
+ "conflicting_metadata",
+ "different_kind",
+ )
+ ),
+ ("conflicting_scope", True),
+ ("mosaic_context", False),
+ ),
+)
+def test_saved_roles_publish_once_per_persisted_occurrence(
+ tmp_path, scenario, aggregate
+):
mosaic = scenario == "mosaic_context"
same_persisted_path = scenario != "different_path"
- filemanager = FileManager({
- Backend.DISK.value: DiskStorageBackend(),
- Backend.MEMORY.value: MemoryStorageBackend(),
- })
+ filemanager = FileManager(
+ {
+ Backend.DISK.value: DiskStorageBackend(),
+ Backend.MEMORY.value: MemoryStorageBackend(),
+ }
+ )
context = context_stub(filemanager, parser=SourceSchemaFilenameParser())
context.microscope_handler.source_name = "openhcsdata"
context.runtime_value_store = RuntimeValueStore()
@@ -77,24 +120,29 @@ def test_saved_roles_publish_once_per_persisted_occurrence(tmp_path, scenario, a
plan.sub_dir = "images"
plan.analysis_results_dir = str(plan.output_dir)
plan.runtime_artifact_materialization = RuntimeArtifactMaterializationPlan(
- persistent_enabled=True, persistent_backend=Backend.DISK.value,
+ persistent_enabled=True,
+ persistent_backend=Backend.DISK.value,
)
context.step_plans = {plan.step_index: plan}
filemanager.ensure_directory(plan.output_dir, Backend.MEMORY.value)
components = {
- "well": "A01", "site": "1", "channel": "2",
- "z_index": "1", "timepoint": "1", "extension": ".tif",
+ "well": "A01",
+ "site": "1",
+ "channel": "2",
+ "z_index": "1",
+ "timepoint": "1",
+ "extension": ".tif",
}
roles = (
- (("MosaicRole", 1.0),) if mosaic
- else (("RawRole", 1.0), ("CappedRole", 0.5))
+ (("MosaicRole", 1.0),) if mosaic else (("RawRole", 1.0), ("CappedRole", 0.5))
)
if aggregate:
components.update(channel="1", z_index="0")
plan.variable_components = (Microscopy.ZIndex,)
plan.group_by = Microscopy.Channel
plan.execution_group_scope = ComponentGroupScope.from_raw(
- ("1",), component=Microscopy.Channel,
+ ("1",),
+ component=Microscopy.Channel,
)
source_planes = []
source_dir = tmp_path / "physical_source"
@@ -103,13 +151,16 @@ def test_saved_roles_publish_once_per_persisted_occurrence(tmp_path, scenario, a
for z_index, plane in enumerate(pixels):
path = source_dir / f"A01_s001_w1_z{z_index:03d}_t001.tif"
filemanager.save(plane, str(path), Backend.DISK.value)
- source_planes.append(ImagePayloadMetadata(
- source_path=str(path),
- source_component_metadata={**components, "z_index": str(z_index)},
- source_voxel_spacing=SourceVoxelSpacing((2.0, 0.65, 0.65)),
- ).payload_with(filemanager.load(str(path), Backend.DISK.value)))
+ source_planes.append(
+ ImagePayloadMetadata(
+ source_path=str(path),
+ source_component_metadata={**components, "z_index": str(z_index)},
+ source_voxel_spacing=SourceVoxelSpacing((2.0, 0.65, 0.65)),
+ ).payload_with(filemanager.load(str(path), Backend.DISK.value))
+ )
source_stack = stack_image_payloads(
- source_planes, metadata_mode=ImagePayloadMetadataCompositionMode.STACK,
+ source_planes,
+ metadata_mode=ImagePayloadMetadataCompositionMode.STACK,
)
roles = (("fixture_image_494", 1.0),)
for role, scale in roles:
@@ -119,29 +170,39 @@ def test_saved_roles_publish_once_per_persisted_occurrence(tmp_path, scenario, a
f"_z{int(components['z_index']):03d}_t001_{role}.tif"
)
output_plan = ArtifactOutputPlan(
- name=role, path=f"/memory/{filename.removesuffix('.tif')}.pkl",
+ name=role,
+ path=f"/memory/{filename.removesuffix('.tif')}.pkl",
artifact_type=(
ObjectLabelsArtifactType
- if scenario == "different_kind" and tested_role else ImageArtifactType
+ if scenario == "different_kind" and tested_role
+ else ImageArtifactType
),
producer_step_index=plan.step_index,
producer_step_scope_id=(
"different-step-scope"
- if scenario == "stale_scope" and tested_role else plan.step_scope_id
+ if scenario == "stale_scope" and tested_role
+ else plan.step_scope_id
),
producer_step_name=plan.step_name,
- materialization=MaterializationSpec(ImageFileOptions(
- filename_suffix=".tif",
- filename_identity=(
- NameByArtifactName if aggregate
- else NameBySourceIdentity
- ),
- relative_path_template=(filename if same_persisted_path else filename.replace(".tif", "_independent.tif")),
- )),
+ materialization=MaterializationSpec(
+ ImageFileOptions(
+ filename_suffix=".tif",
+ filename_identity=(
+ NameByArtifactName if aggregate else NameBySourceIdentity
+ ),
+ relative_path_template=(
+ filename
+ if same_persisted_path
+ else filename.replace(".tif", "_independent.tif")
+ ),
+ )
+ ),
)
if aggregate:
output_plan = replace(
- output_plan, group_keys=("1",), group_component=Microscopy.Channel,
+ output_plan,
+ group_keys=("1",),
+ group_component=Microscopy.Channel,
variable_components=(Microscopy.ZIndex,),
)
plan.artifact_outputs[output_plan.ref()] = output_plan
@@ -174,128 +235,205 @@ def test_saved_roles_publish_once_per_persisted_occurrence(tmp_path, scenario, a
component_metadata=({"site": "1"}, {"site": "2"}),
),
),
- ).payload_with(np.stack([
- filemanager.load(str(path), Backend.DISK.value)
- for path in contributor_paths
- ]))
+ ).payload_with(
+ np.stack(
+ [
+ filemanager.load(str(path), Backend.DISK.value)
+ for path in contributor_paths
+ ]
+ )
+ )
if aggregate:
payload = ImageArtifactType.contextualize_output(
- source_stack, pixels.copy(), output_plan,
- RuntimePlaneAxisValueProjection(RuntimePlaneAxis.RUNTIME_SLICE, (), None, 4),
+ source_stack,
+ pixels.copy(),
+ output_plan,
+ RuntimePlaneAxisValueProjection(
+ RuntimePlaneAxis.RUNTIME_SLICE, (), None, 4
+ ),
)
if aggregate:
execution_scope = RuntimeExecutionAxisScope.from_raw(
- "A01", component=Microscopy.Channel, value="1",
+ "A01",
+ component=Microscopy.Channel,
+ value="1",
fixed_component_values=(
- (Microscopy.Site, "1"), (Microscopy.Timepoint, "1"),
+ (Microscopy.Site, "1"),
+ (Microscopy.Timepoint, "1"),
),
)
payload = RuntimeValue.normalize_for_execution_scope(
- replace(output_plan, artifact_type=ImageArtifactType), payload,
+ replace(output_plan, artifact_type=ImageArtifactType),
+ payload,
execution_scope=execution_scope,
).data
artifact_payload = payload
if tested_role and scenario == "conflicting_metadata":
conflicting_fields = (
{"source_voxel_spacing": SourceVoxelSpacing((3.0, 0.65, 0.65))}
- if aggregate else {"source_image_names": ("IndependentRole",)}
+ if aggregate
+ else {"source_image_names": ("IndependentRole",)}
)
artifact_payload = payload.metadata.replace_fields(
**conflicting_fields
).payload_with(payload.data)
if tested_role and scenario == "different_kind":
artifact_payload = SourceImageObjectLabelBuildRequest(
- image=payload, labels=np.ones(payload.data.shape, dtype=np.int32),
- declared_object_count=1, declared_object_ids=(1,),
+ image=payload,
+ labels=np.ones(payload.data.shape, dtype=np.int32),
+ declared_object_count=1,
+ declared_object_ids=(1,),
).payload()
output_identity = (
FunctionOutputIdentity.from_metadata(
- context.microscope_handler.parser, payload.metadata,
+ context.microscope_handler.parser,
+ payload.metadata,
variable_components=plan.variable_components,
- ) if aggregate else None
+ )
+ if aggregate
+ else None
)
value = (
RuntimeValue.normalize_for_execution_scope(
- output_plan, artifact_payload,
+ output_plan,
+ artifact_payload,
execution_scope=(
RuntimeExecutionAxisScope.from_raw(
- "A01", component=Microscopy.Channel, value="1",
+ "A01",
+ component=Microscopy.Channel,
+ value="1",
fixed_component_values=(
- (Microscopy.Site, "2"), (Microscopy.Timepoint, "1"),
+ (Microscopy.Site, "2"),
+ (Microscopy.Timepoint, "1"),
),
- ) if scenario == "conflicting_scope" else execution_scope
+ )
+ if scenario == "conflicting_scope"
+ else execution_scope
),
- ) if aggregate else (
+ )
+ if aggregate
+ else (
RuntimeValue.normalize_for_execution_scope(
- output_plan, artifact_payload,
+ output_plan,
+ artifact_payload,
execution_scope=RuntimeExecutionAxisScope.from_raw(
- "A01", component=None, value=None, fixed_component_values=(
- (Microscopy.Channel, "2"), (Microscopy.ZIndex, "1"),
+ "A01",
+ component=None,
+ value=None,
+ fixed_component_values=(
+ (Microscopy.Channel, "2"),
+ (Microscopy.ZIndex, "1"),
(Microscopy.Timepoint, "1"),
),
),
- ) if mosaic else RuntimeValue.normalize(output_plan, artifact_payload, axis_id="A01")
+ )
+ if mosaic
+ else RuntimeValue.normalize(
+ output_plan, artifact_payload, axis_id="A01"
+ )
)
)
if mosaic:
payload = value.data
output_identity = FunctionOutputIdentity(
component_values={
- key: value for key, value in components.items()
+ key: value
+ for key, value in components.items()
if key not in ("site", "extension")
},
filename_component_values={
- key: value for key, value in components.items() if key != "extension"
+ key: value
+ for key, value in components.items()
+ if key != "extension"
},
- extension=".tif", source="collapsed Mosaic contributors",
+ extension=".tif",
+ source="collapsed Mosaic contributors",
)
context.runtime_value_store.record(
value,
- path=output_plan.path, backend=Backend.MEMORY.value,
+ path=output_plan.path,
+ backend=Backend.MEMORY.value,
)
destination = plan.output_dir / filename
filemanager.save(payload, str(destination), Backend.MEMORY.value)
filemanager.ensure_directory(plan.output_dir, Backend.DISK.value)
filemanager.save(payload.data, str(destination), Backend.DISK.value)
record_output_path(
- context, plan, str(destination), image_metadata=payload.metadata,
+ context,
+ plan,
+ str(destination),
+ image_metadata=payload.metadata,
output_context=AlignedImageSliceContext.main_flow(
- output_key=role, artifact_kind=ImageArtifactType.value,
+ output_key=role,
+ artifact_kind=ImageArtifactType.value,
),
identity=output_identity,
)
saved = ArtifactMaterializationTargetPlan.materialize(context, plan)
assert len(saved) == len(roles)
- target = replace(PrimaryImageMetadataTarget.from_plan(plan), artifact_materializations=saved)
+ target = replace(
+ PrimaryImageMetadataTarget.from_plan(plan), artifact_materializations=saved
+ )
records = target.produced_records(context, plan)
artifacts = target.runtime_artifact_projection_paths(context, plan)
receipt = {
"source_scope": plan.step_scope_id,
- "produced": [{"path": str(record.path_under(plan.output_dir)),
- "identity": asdict(record.producer_identity),
- "address": record.filename_address.as_component_metadata()} for record in records],
- "materialized": [{"path": output.path,
- "role": item.materialization.output_plan.name,
- "semantic_source": None if item.materialization.source_identity is None else {
- "path": item.materialization.source_identity.path,
- "component_metadata": dict(item.materialization.source_identity.component_metadata or {}),
- },
- "producer_scope": item.materialization.output_plan.producer_step_scope_id,
- "stored_key": repr(item.materialization.record.key)}
- for item in saved for output in item.outputs_for_backend(Backend.DISK.value)],
- "artifact_projections": [{"path": path,
- "role": projection.projection_role.name,
- "address": None if projection.address is None else projection.address.as_component_metadata(),
- "alias": projection.source_alias,
- "kind": projection.artifact_kind.value}
- for projection, path in artifacts],
+ "produced": [
+ {
+ "path": str(record.path_under(plan.output_dir)),
+ "identity": asdict(record.producer_identity),
+ "address": record.filename_address.as_component_metadata(),
+ }
+ for record in records
+ ],
+ "materialized": [
+ {
+ "path": output.path,
+ "role": item.materialization.output_plan.name,
+ "semantic_source": (
+ None
+ if item.materialization.source_identity is None
+ else {
+ "path": item.materialization.source_identity.path,
+ "component_metadata": dict(
+ item.materialization.source_identity.component_metadata
+ or {}
+ ),
+ }
+ ),
+ "producer_scope": item.materialization.output_plan.producer_step_scope_id,
+ "stored_key": repr(item.materialization.record.key),
+ }
+ for item in saved
+ for output in item.outputs_for_backend(Backend.DISK.value)
+ ],
+ "artifact_projections": [
+ {
+ "path": path,
+ "role": projection.projection_role.name,
+ "address": (
+ None
+ if projection.address is None
+ else projection.address.as_component_metadata()
+ ),
+ "alias": projection.source_alias,
+ "kind": projection.artifact_kind.value,
+ }
+ for projection, path in artifacts
+ ],
"pre_join_main_flow": SourceProjectionMetadataSerializer.projection_records(
- replace(target, artifact_materializations=()).produced_projection_entries(context, plan).projection_paths,
+ replace(target, artifact_materializations=())
+ .produced_projection_entries(context, plan)
+ .projection_paths,
+ ),
+ "pre_join_artifacts": SourceProjectionMetadataSerializer.projection_records(
+ artifacts
),
- "pre_join_artifacts": SourceProjectionMetadataSerializer.projection_records(artifacts),
"produced_records": [to_jsonable(record) for record in records],
}
- (tmp_path / "persisted-occurrence-witness.json").write_text(json.dumps(receipt, indent=2))
+ (tmp_path / "persisted-occurrence-witness.json").write_text(
+ json.dumps(receipt, indent=2)
+ )
produced_paths = {item["path"] for item in receipt["produced"]}
materialized_paths = {item["path"] for item in receipt["materialized"]}
if same_persisted_path:
@@ -305,29 +443,44 @@ def test_saved_roles_publish_once_per_persisted_occurrence(tmp_path, scenario, a
if mosaic:
expected_sums = {780.0}
elif aggregate:
- expected_sums = {140.0} if scenario == "different_kind" else {float(pixels.sum())}
+ expected_sums = (
+ {140.0} if scenario == "different_kind" else {float(pixels.sum())}
+ )
else:
expected_sums = {20.0} if scenario == "different_kind" else {20.0, 10.0}
- assert {float(tifffile.imread(item["path"]).sum()) for item in receipt["materialized"]} == expected_sums
- if scenario not in ("same_occurrence", "mosaic_context") and not (aggregate and scenario == "different_path"):
+ assert {
+ float(tifffile.imread(item["path"]).sum()) for item in receipt["materialized"]
+ } == expected_sums
+ if scenario not in ("same_occurrence", "mosaic_context") and not (
+ aggregate and scenario == "different_path"
+ ):
message = (
"Conflicting metadata for persisted image"
- if scenario in ("conflicting_metadata", "conflicting_scope", "mosaic_context")
+ if scenario
+ in ("conflicting_metadata", "conflicting_scope", "mosaic_context")
else "Duplicate source projection address"
)
with pytest.raises(ValueError, match=message) as rejection:
target.produced_projection_entries(context, plan)
receipt["rejection"] = str(rejection.value)
- (tmp_path / "persisted-occurrence-witness.json").write_text(json.dumps(receipt, indent=2))
+ (tmp_path / "persisted-occurrence-witness.json").write_text(
+ json.dumps(receipt, indent=2)
+ )
return
if aggregate:
(main_flow,) = receipt["pre_join_main_flow"]
(artifact,) = receipt["pre_join_artifacts"]
assert main_flow["address"] is artifact["address"] is None
- assert main_flow["execution_scope"] == artifact["execution_scope"] == {
- "axis_id": "A01", "component": "channel", "value": "1",
- "fixed_component_values": [["site", "1"], ["timepoint", "1"]],
- }
+ assert (
+ main_flow["execution_scope"]
+ == artifact["execution_scope"]
+ == {
+ "axis_id": "A01",
+ "component": "channel",
+ "value": "1",
+ "fixed_component_values": [["site", "1"], ["timepoint", "1"]],
+ }
+ )
assert main_flow["image_metadata"] == artifact["image_metadata"]
assert main_flow["source_metadata"] == artifact["source_metadata"]
assert receipt["produced"][0]["address"]["z_index"] == "0"
@@ -335,14 +488,20 @@ def test_saved_roles_publish_once_per_persisted_occurrence(tmp_path, scenario, a
metadata = main_flow["image_metadata"]
assert metadata["source_dtype"] == "uint16"
assert metadata["source_voxel_spacing"] == {
- "values_zyx": [2.0, 0.65, 0.65], "unit": "micrometers",
+ "values_zyx": [2.0, 0.65, 0.65],
+ "unit": "micrometers",
}
provenance = metadata["source_provenance"]
assert "z_index" not in provenance["source_component_metadata"]
- assert [plane["component_metadata"]["z_index"] for plane in
- provenance["source_image_provenance_planes"]] == ["0", "1", "2", "3"]
- assert [plane["path"] for plane in provenance["source_image_provenance_planes"]] == [
- str(source_dir / f"A01_s001_w1_z{z_index:03d}_t001.tif") for z_index in range(4)
+ assert [
+ plane["component_metadata"]["z_index"]
+ for plane in provenance["source_image_provenance_planes"]
+ ] == ["0", "1", "2", "3"]
+ assert [
+ plane["path"] for plane in provenance["source_image_provenance_planes"]
+ ] == [
+ str(source_dir / f"A01_s001_w1_z{z_index:03d}_t001.tif")
+ for z_index in range(4)
]
assert metadata["source_spatial_domain"]["spatial_dimensions"] == 2
for path in produced_paths | materialized_paths:
@@ -358,20 +517,32 @@ def test_saved_roles_publish_once_per_persisted_occurrence(tmp_path, scenario, a
assert main_flow["image_metadata"] == artifact["image_metadata"]
assert main_flow["source_metadata"] == artifact["source_metadata"]
assert receipt["produced"][0]["address"]["site"] == "1"
- assert "site" not in receipt["materialized"][0]["semantic_source"]["component_metadata"]
+ assert (
+ "site"
+ not in receipt["materialized"][0]["semantic_source"]["component_metadata"]
+ )
structured = SourceProjectionMetadataSerializer.projection_fields(
structured.projection_paths
)
occurrence_count = 2 if aggregate and scenario == "different_path" else 1
assert len(structured["source_projection"]) == len(roles) * occurrence_count
- assert {item["source_alias"] for item in structured["source_projection"]} == {role for role, _ in roles}
+ assert {item["source_alias"] for item in structured["source_projection"]} == {
+ role for role, _ in roles
+ }
target.write(context, produced_plan=plan)
document = json.loads((tmp_path / "openhcs_metadata.json").read_text())
- reopened = VirtualWorkspaceSourceProjection.from_openhcs_metadata(tmp_path, document)
+ reopened = VirtualWorkspaceSourceProjection.from_openhcs_metadata(
+ tmp_path, document
+ )
projections = reopened.source_projections_by_virtual_path
- relative_paths = {path.removeprefix(str(tmp_path) + "/") for path in produced_paths | materialized_paths}
+ relative_paths = {
+ path.removeprefix(str(tmp_path) + "/")
+ for path in produced_paths | materialized_paths
+ }
assert set(projections) == relative_paths | produced_paths | materialized_paths
- assert {projection.source_alias for projection in projections.values()} == {role for role, _ in roles}
+ assert {projection.source_alias for projection in projections.values()} == {
+ role for role, _ in roles
+ }
for relative_path in relative_paths:
projection = projections[relative_path]
if mosaic:
@@ -382,15 +553,26 @@ def test_saved_roles_publish_once_per_persisted_occurrence(tmp_path, scenario, a
assert metadata.source_image_provenance_planes.contributor_count == 2
assert metadata.source_voxel_spacing.values_zyx == (0.5, 0.5)
assert projection.execution_scope.fixed_component_values == (
- (Microscopy.Channel, "2"), (Microscopy.ZIndex, "1"),
+ (Microscopy.Channel, "2"),
+ (Microscopy.ZIndex, "1"),
(Microscopy.Timepoint, "1"),
)
- np.testing.assert_array_equal(tifffile.imread(tmp_path / relative_path), mosaic_pixels)
+ np.testing.assert_array_equal(
+ tifffile.imread(tmp_path / relative_path), mosaic_pixels
+ )
continue
if aggregate:
assert projection.address is None
- np.testing.assert_array_equal(tifffile.imread(tmp_path / relative_path), pixels)
+ np.testing.assert_array_equal(
+ tifffile.imread(tmp_path / relative_path), pixels
+ )
continue
- assert projection.address.as_component_metadata() == {key: components[key] for key in ("well", "site", "channel", "z_index", "timepoint")}
+ assert projection.address.as_component_metadata() == {
+ key: components[key]
+ for key in ("well", "site", "channel", "z_index", "timepoint")
+ }
expected_scale = 1.0 if projection.source_alias == "RawRole" else 0.5
- np.testing.assert_array_equal(tifffile.imread(tmp_path / relative_path), np.ones((4, 5), dtype=np.float32) * expected_scale)
+ np.testing.assert_array_equal(
+ tifffile.imread(tmp_path / relative_path),
+ np.ones((4, 5), dtype=np.float32) * expected_scale,
+ )
diff --git a/tests/unit/test_function_outputs.py b/tests/unit/test_function_outputs.py
index d1cadc1851..6f22c9febd 100644
--- a/tests/unit/test_function_outputs.py
+++ b/tests/unit/test_function_outputs.py
@@ -73,12 +73,15 @@
MaterializedRuntimeArtifact,
RuntimeArtifactMaterialization,
)
-from openhcs.core.steps.function_artifact_materialization import ArtifactMaterializationTargetPlan
+from openhcs.core.steps.function_artifact_materialization import (
+ ArtifactMaterializationTargetPlan,
+)
from openhcs.core.steps.function_outputs import (
MaterializedImageOutputWriter,
MemoryOutputWriter,
OpenHCSMetadataTarget,
MaterializedImageMetadataTarget,
+ PrimaryImageMetadataTarget,
RuntimeArtifactMetadataTarget,
StreamOutputsAuthority,
StreamOutputBatch,
@@ -122,7 +125,8 @@ def _publish_saved_step(context, plan, *, artifact_materializations=()):
METADATA_CONFIG.metadata_path(target.dataset_root) for target in facts
):
entries = {
- target: value for target, value in facts.items()
+ target: value
+ for target, value in facts.items()
if METADATA_CONFIG.metadata_path(target.dataset_root) == metadata_path
}
AtomicMetadataWriter().reconcile_completed_dataset(
@@ -173,7 +177,8 @@ def test_function_step_metadata_follows_runtime_artifact_persistence(
authority,
method_name,
lambda _context, _plan, event=event, **_kwargs: (
- events.append(event), {} if event == "metadata" else ()
+ events.append(event),
+ {} if event == "metadata" else (),
)[1],
)
@@ -188,6 +193,62 @@ def test_function_step_metadata_follows_runtime_artifact_persistence(
]
+@pytest.mark.parametrize(
+ ("backend", "expected_targets"),
+ [
+ (
+ Backend.DISK.value,
+ {
+ PrimaryImageMetadataTarget,
+ MaterializedImageMetadataTarget,
+ RuntimeArtifactMetadataTarget,
+ },
+ ),
+ (Backend.OMERO_LOCAL.value, set()),
+ ],
+)
+def test_metadata_document_targets_follow_backend_declaration(
+ monkeypatch, backend, expected_targets
+) -> None:
+ """A virtual backend's output root holds no local metadata document."""
+ root = "/omero/plate_1_outputs"
+ plan = CompiledStepPlan(
+ step_index=0,
+ step_name="count",
+ axis_id="A01",
+ output_dir=Path(root) / "images",
+ output_dataset_root=root,
+ sub_dir="images",
+ analysis_results_dir=f"{root}/results",
+ write_backend=backend,
+ materialized_output=MaterializedOutputPlan(
+ output_dir=Path(root) / "checkpoint",
+ backend=backend,
+ dataset_root=root,
+ sub_dir="checkpoint",
+ analysis_results_dir=f"{root}/results",
+ ),
+ runtime_artifact_materialization=RuntimeArtifactMaterializationPlan(
+ persistent_enabled=True, persistent_backend=backend
+ ),
+ create_openhcs_metadata=True,
+ )
+ assert {type(t) for t in OpenHCSMetadataTarget.for_plan(plan)} == expected_targets
+
+ documents = []
+ monkeypatch.setattr(
+ AtomicMetadataWriter,
+ "reconcile_completed_dataset",
+ lambda _self, metadata_path, *_args, **_kwargs: documents.append(metadata_path),
+ )
+ OpenHCSMetadataTarget.finalize_completed_dataset(
+ {"A01": SimpleNamespace(step_plans={0: plan})}
+ )
+ assert documents == (
+ [METADATA_CONFIG.metadata_path(root)] if expected_targets else []
+ )
+
+
def test_memory_output_writer_rejects_payload_path_cardinality_mismatch():
with pytest.raises(ValueError, match="1 payloads for 0 paths"):
MemoryOutputWriter.payloads(
@@ -304,7 +365,10 @@ def parse_filename(self, name):
}
)
return FilenameParseResult(
- ((component, metadata.get(component.name)) for component in AxisFamily.active().axes),
+ (
+ (component, metadata.get(component.name))
+ for component in AxisFamily.active().axes
+ ),
extension=str(metadata["extension"]),
)
@@ -513,7 +577,9 @@ def test_memory_output_writer_materializes_preserved_source_at_current_destinati
@pytest.mark.parametrize("source_extension", (None, ".czi"))
-def test_function_output_identity_preserves_non_axis_source_metadata(source_extension) -> None:
+def test_function_output_identity_preserves_non_axis_source_metadata(
+ source_extension,
+) -> None:
identity = FunctionOutputIdentity(
component_values={"channel": "2", "site": "1"},
extension=".tif",
@@ -521,10 +587,10 @@ def test_function_output_identity_preserves_non_axis_source_metadata(source_exte
)
source_metadata = {
- "Run": "Sequence1",
- "Specimen": "DrosophilaEmbryo",
- "ChannelNumber": "1",
- "OpenHCSOriginalSourceMetadata": {"FrameNumber": "0007"},
+ "Run": "Sequence1",
+ "Specimen": "DrosophilaEmbryo",
+ "ChannelNumber": "1",
+ "OpenHCSOriginalSourceMetadata": {"FrameNumber": "0007"},
}
if source_extension is not None:
source_metadata["extension"] = source_extension
@@ -539,7 +605,9 @@ def test_function_output_identity_preserves_non_axis_source_metadata(source_exte
**({"extension": source_extension} if source_extension is not None else {}),
}
assert identity.filename_component_metadata() == {
- "channel": "2", "site": "1", "extension": ".tif",
+ "channel": "2",
+ "site": "1",
+ "extension": ".tif",
}
assert source_metadata["ChannelNumber"] == "1"
assert source_metadata.get("extension") == source_extension
@@ -1668,7 +1736,9 @@ def test_produced_projection_metadata_persists_typed_collapsed_semantics(
assert len(restored.source_provenance.represented_source_identities) == 2
assert record["address"] is None
assert record["execution_scope"]["fixed_component_values"] == [
- ["channel", "1"], ["z_index", "1"], ["timepoint", "1"],
+ ["channel", "1"],
+ ["z_index", "1"],
+ ["timepoint", "1"],
]
assert "site" not in record["source_metadata"]
assert "site" not in subdirectory[FIELDS.SOURCE_METADATA]["images/A01_s1_w1.tif"]
@@ -1947,7 +2017,10 @@ def parse_filename(self, name):
}
)
return FilenameParseResult(
- ((component, metadata.get(component.name)) for component in AxisFamily.active().axes),
+ (
+ (component, metadata.get(component.name))
+ for component in AxisFamily.active().axes
+ ),
extension=str(metadata["extension"]),
)
@@ -2087,9 +2160,7 @@ def test_produced_address_publication_never_parses_generated_filenames(
},
source_voxel_spacing=SourceVoxelSpacing((0.5, 0.5)),
)
- identity = FunctionOutputIdentity.from_metadata(
- parser, metadata
- )
+ identity = FunctionOutputIdentity.from_metadata(parser, metadata)
assert identity is not None
assert identity.extension == extension
identity = identity.with_filename_qualifier("centre_dots")
@@ -2261,11 +2332,15 @@ def test_completed_plate_metadata_includes_outputs_written_after_owner_axis(
follower_plan.create_openhcs_metadata = True
filemanager.save(first_pixels, str(later_image), Backend.MEMORY.value)
record_output_path(follower_context, follower_plan, str(later_image))
- later_facts = OpenHCSMetadataTarget.observe_for_step(follower_context, follower_plan)
+ later_facts = OpenHCSMetadataTarget.observe_for_step(
+ follower_context, follower_plan
+ )
combined_facts = {
target: VirtualWorkspaceSourceProjectionEntries.combine(
- (first_facts.get(target, VirtualWorkspaceSourceProjectionEntries({})),
- later_facts.get(target, VirtualWorkspaceSourceProjectionEntries({})))
+ (
+ first_facts.get(target, VirtualWorkspaceSourceProjectionEntries({})),
+ later_facts.get(target, VirtualWorkspaceSourceProjectionEntries({})),
+ )
)
for target in dict.fromkeys((*first_facts, *later_facts))
}
@@ -2392,7 +2467,9 @@ def test_runtime_image_metadata_target_requires_persisted_images(tmp_path, conte
context, plan, artifact_materializations=saved_artifacts
)
if contents == "images":
- assert selected == (replace(target, create_openhcs_metadata=plan.create_openhcs_metadata),)
+ assert selected == (
+ replace(target, create_openhcs_metadata=plan.create_openhcs_metadata),
+ )
else:
assert selected == ()
plan.create_openhcs_metadata = True
@@ -2462,12 +2539,8 @@ def test_declared_image_destinations_publish_and_reconcile_after_value_cleanup(
path=output_plan.path,
backend=Backend.MEMORY.value,
)
- materializations = ArtifactMaterializationTargetPlan.materialize(
- context, plan
- )
- _publish_saved_step(
- context, plan, artifact_materializations=materializations
- )
+ materializations = ArtifactMaterializationTargetPlan.materialize(context, plan)
+ _publish_saved_step(context, plan, artifact_materializations=materializations)
context.runtime_value_store.clear()
if stray_image:
tifffile.imwrite(tmp_path / "images/review/unowned.tif", pixels)
@@ -2559,9 +2632,7 @@ def test_actual_array_exports_only_publish_declared_raster_inventory(
backend=Backend.MEMORY.value,
)
- materializations = ArtifactMaterializationTargetPlan.materialize(
- context, plan
- )
+ materializations = ArtifactMaterializationTargetPlan.materialize(context, plan)
saved_outputs = tuple(
output
for artifact in materializations
@@ -2579,15 +2650,15 @@ def test_actual_array_exports_only_publish_declared_raster_inventory(
location
for artifact in materializations
for locations in (
- artifact.observation(plan, context).materialized_locations_by_address.values()
+ artifact.observation(
+ plan, context
+ ).materialized_locations_by_address.values()
)
for location in locations
)
assert {Path(location.path) for location in observed_locations} == expected_paths
- _publish_saved_step(
- context, plan, artifact_materializations=materializations
- )
+ _publish_saved_step(context, plan, artifact_materializations=materializations)
context.runtime_value_store.clear()
OpenHCSMetadataTarget.finalize_completed_dataset({"A01": context})
metadata_path = tmp_path / "openhcs_metadata.json"
@@ -2626,8 +2697,13 @@ def test_actual_array_exports_only_publish_declared_raster_inventory(
def _stream_batch_sources(projections):
plan = function_step_plan("StreamOwned")
- arrays = [np.full((2, 3), index, dtype=np.float32) for index in range(len(projections))]
- paths = [str(plan.output_dir / f"A01_s1_w{index}.tif") for index in range(len(projections))]
+ arrays = [
+ np.full((2, 3), index, dtype=np.float32) for index in range(len(projections))
+ ]
+ paths = [
+ str(plan.output_dir / f"A01_s1_w{index}.tif")
+ for index in range(len(projections))
+ ]
records = tuple(
ProducedOutputSemantics.from_output(
plan,
@@ -2638,7 +2714,8 @@ def _stream_batch_sources(projections):
source="stream source",
),
output_context=AlignedImageSliceContext.main_flow(
- output_key=f"image-{index}", projection_key=projection,
+ output_key=f"image-{index}",
+ projection_key=projection,
artifact_kind=ImageArtifactType.value,
),
)
@@ -2647,8 +2724,12 @@ def _stream_batch_sources(projections):
return arrays, paths, records
-def test_stream_batch_owns_correlated_projection_order_and_frozen_input_lists(monkeypatch):
- arrays, paths, records = _stream_batch_sources(("second", "first", "second", "first"))
+def test_stream_batch_owns_correlated_projection_order_and_frozen_input_lists(
+ monkeypatch,
+):
+ arrays, paths, records = _stream_batch_sources(
+ ("second", "first", "second", "first")
+ )
expected_arrays, expected_paths = tuple(arrays), tuple(paths)
observed = []
original_project = StreamOutputBatch.project_item
@@ -2661,7 +2742,9 @@ def project(request):
monkeypatch.setattr(StreamOutputBatch, "project_item", staticmethod(project))
batches = StreamOutputBatch.from_projection_groups(
- parser=ImageXpressFilenameParser(), payloads=arrays, paths=paths,
+ parser=ImageXpressFilenameParser(),
+ payloads=arrays,
+ paths=paths,
produced_outputs=records,
)
assert observed == [expected_paths[index] for index in (0, 2, 1, 3)]
@@ -2671,14 +2754,20 @@ def project(request):
records[index].producer_identity for index in (0, 2, 1, 3)
]
for item, index in zip(
- (item for batch in batches for item in batch.items), (0, 2, 1, 3), strict=True,
+ (item for batch in batches for item in batch.items),
+ (0, 2, 1, 3),
+ strict=True,
):
assert item.data is expected_arrays[index]
assert item.source_component_metadata["channel"] == str(index)
-def test_stream_batch_keeps_first_projection_error_and_prior_callback_effects(monkeypatch):
- arrays, paths, records = _stream_batch_sources(("first", "second", "first", "second"))
+def test_stream_batch_keeps_first_projection_error_and_prior_callback_effects(
+ monkeypatch,
+):
+ arrays, paths, records = _stream_batch_sources(
+ ("first", "second", "first", "second")
+ )
observed = []
failure = ValueError("actual projector failure")
original_project = StreamOutputBatch.project_item
@@ -2692,7 +2781,9 @@ def project(request):
monkeypatch.setattr(StreamOutputBatch, "project_item", staticmethod(project))
with pytest.raises(ValueError) as raised:
StreamOutputBatch.from_projection_groups(
- parser=ImageXpressFilenameParser(), payloads=arrays, paths=paths,
+ parser=ImageXpressFilenameParser(),
+ payloads=arrays,
+ paths=paths,
produced_outputs=records,
)
assert raised.value is failure
@@ -2705,23 +2796,37 @@ def test_stream_batch_validates_cardinality_and_routes_before_projecting(monkeyp
def forbidden_project(request):
raise AssertionError("invalid inputs reached projection")
- monkeypatch.setattr(StreamOutputBatch, "project_item", staticmethod(forbidden_project))
+ monkeypatch.setattr(
+ StreamOutputBatch, "project_item", staticmethod(forbidden_project)
+ )
parser = ImageXpressFilenameParser()
with pytest.raises(ValueError, match="payload/path cardinality mismatch"):
StreamOutputBatch.from_projection_groups(
- parser=parser, payloads=arrays, paths=paths[:1], produced_outputs=(),
+ parser=parser,
+ payloads=arrays,
+ paths=paths[:1],
+ produced_outputs=(),
)
with pytest.raises(ValueError, match="payload/output-record cardinality mismatch"):
StreamOutputBatch.from_projection_groups(
- parser=parser, payloads=arrays, paths=paths, produced_outputs=records[:1],
+ parser=parser,
+ payloads=arrays,
+ paths=paths,
+ produced_outputs=records[:1],
)
with pytest.raises(ValueError, match="at least one produced output record"):
StreamOutputBatch.from_projection_groups(
- parser=parser, payloads=(), paths=(), produced_outputs=(),
+ parser=parser,
+ payloads=(),
+ paths=(),
+ produced_outputs=(),
)
with pytest.raises(ValueError, match="cannot mix producer projections"):
StreamOutputBatch.from_projection(
- parser=parser, payloads=arrays, paths=paths, produced_outputs=records,
+ parser=parser,
+ payloads=arrays,
+ paths=paths,
+ produced_outputs=records,
)
@@ -2783,7 +2888,10 @@ def test_whole_volume_checkpoint_publication_retains_exact_producer_scope(
)
assert str(contextualized.source_component_metadata["timepoint"]) == "1"
assert "z_index" not in contextualized.source_component_metadata
- assert contextualized.source_image_provenance_planes == metadata.source_image_provenance_planes
+ assert (
+ contextualized.source_image_provenance_planes
+ == metadata.source_image_provenance_planes
+ )
manager = FileManager(
{"disk": DiskStorageBackend(), "memory": MemoryStorageBackend()}
)
@@ -2853,9 +2961,7 @@ def test_whole_volume_checkpoint_publication_retains_exact_producer_scope(
path=output_plan.path,
backend=Backend.MEMORY.value,
)
- materializations = ArtifactMaterializationTargetPlan.materialize(
- context, plan
- )
+ materializations = ArtifactMaterializationTargetPlan.materialize(context, plan)
assert {
Path(output.path)
for artifact in materializations
@@ -2869,19 +2975,14 @@ def test_whole_volume_checkpoint_publication_retains_exact_producer_scope(
assert projection.address is None
assert projection.source_alias == "DNA"
assert (
- projection.execution_scope.value_text_for_component(Microscopy.Channel)
- == "2"
- )
- assert (
- projection.execution_scope.value_text_for_component(Microscopy.Site) == "1"
+ projection.execution_scope.value_text_for_component(Microscopy.Channel) == "2"
)
+ assert projection.execution_scope.value_text_for_component(Microscopy.Site) == "1"
assert (
- projection.execution_scope.value_text_for_component(Microscopy.Timepoint)
- == "1"
+ projection.execution_scope.value_text_for_component(Microscopy.Timepoint) == "1"
)
assert (
- projection.execution_scope.value_text_for_component(Microscopy.ZIndex)
- is None
+ projection.execution_scope.value_text_for_component(Microscopy.ZIndex) is None
)
assert (
projection.image_metadata.source_image_provenance_planes.component_metadata
diff --git a/tests/unit/test_gray_to_color_binding_axis_432.py b/tests/unit/test_gray_to_color_binding_axis_432.py
index 5bb11ef1d0..5ebccb1b7f 100644
--- a/tests/unit/test_gray_to_color_binding_axis_432.py
+++ b/tests/unit/test_gray_to_color_binding_axis_432.py
@@ -6,7 +6,10 @@
import pytest
from openhcs.core.component_group_scope import RuntimeExecutionAxisScope
-from openhcs.core.aligned_image_payload import (AlignedImageStack, ImagePayloadBundleContext)
+from openhcs.core.aligned_image_payload import (
+ AlignedImageStack,
+ ImagePayloadBundleContext,
+)
from openhcs.core.callable_contract import CallableContract
from openhcs.core.artifacts.specs import ArtifactSpec
from openhcs.core.artifacts.kinds import ImageArtifactType
@@ -26,13 +29,22 @@
from openhcs.interop.cellprofiler.runtime.function_contract_execution import (
CellProfilerFunctionContractExecutor,
)
-from openhcs.interop.cellprofiler.runtime.module_execution import CellProfilerModuleExecutor
+from openhcs.interop.cellprofiler.runtime.module_execution import (
+ CellProfilerModuleExecutor,
+)
from openhcs.interop.cellprofiler.runtime.output_recording import (
CellProfilerOutputRecorder,
)
-from openhcs.processing.backends.cellprofiler.color import GrayToColorModule, gray_to_color
-from test_cellprofiler_generic_special_input_binding import _compile_public_step
-from tests.unit.cellprofiler_runtime_test_support import cellprofiler_runtime_adapter_for_test
+from openhcs.processing.backends.cellprofiler.color import (
+ GrayToColorModule,
+ gray_to_color,
+)
+from tests.unit.test_cellprofiler_generic_special_input_binding import (
+ _compile_public_step,
+)
+from tests.unit.cellprofiler_runtime_test_support import (
+ cellprofiler_runtime_adapter_for_test,
+)
from openhcs.domains.microscopy.axes import Microscopy
from openhcs.core.axes import ColourAxis
from openhcs.core.image_payload_execution_mode import (
@@ -42,7 +54,9 @@
@pytest.mark.parametrize("explicit_selector", [False, True])
-def test_declared_fitc_runtime_plane_retains_values_and_physical_identity(explicit_selector):
+def test_declared_fitc_runtime_plane_retains_values_and_physical_identity(
+ explicit_selector,
+):
kwargs = {
"color_scheme": GrayToColorModule.Scheme.STACK,
"rescale_intensity": False,
@@ -51,37 +65,61 @@ def test_declared_fitc_runtime_plane_retains_values_and_physical_identity(explic
if explicit_selector:
kwargs["image_name"] = ("FITC",)
step = FunctionStep(
- func=(gray_to_color, kwargs), name="RetainRawReference",
+ func=(gray_to_color, kwargs),
+ name="RetainRawReference",
source_bindings=StepSourceBindingsConfig(
- enabled=True, bindings=(NamedSourceBinding("FITC"),),
+ enabled=True,
+ bindings=(NamedSourceBinding("FITC"),),
),
)
invocation = _compile_public_step(step)
- contract = replace(invocation.contract, runtime_adapter=CellProfilerRuntimeAdapter.runtime_adapter_spec())
+ contract = replace(
+ invocation.contract,
+ runtime_adapter=CellProfilerRuntimeAdapter.runtime_adapter_spec(),
+ )
pixels = np.arange(20, dtype=np.float32).reshape(4, 5) * 1000
- physical = {"well": "A01", "site": "1", "channel": "2", "z_index": "1", "timepoint": "1"}
+ physical = {
+ "well": "A01",
+ "site": "1",
+ "channel": "2",
+ "z_index": "1",
+ "timepoint": "1",
+ }
metadata = ImagePayloadMetadata(
plane_axis=RuntimePlaneAxis.RUNTIME_SLICE,
source_image_provenance_planes=SourceImageProvenancePlanes.from_components(
- paths=("/synthetic/A01_s1_w2_z1_t1.tif",), component_metadata=(physical,),
+ paths=("/synthetic/A01_s1_w2_z1_t1.tif",),
+ component_metadata=(physical,),
),
source_image_names=("FITC",),
source_voxel_spacing=SourceVoxelSpacing((1.0, 1.0)),
- source_spatial_domain=SourceSpatialDomain(origin_yx=(0, 0), source_shape_yx=(4, 5)),
+ source_spatial_domain=SourceSpatialDomain(
+ origin_yx=(0, 0), source_shape_yx=(4, 5)
+ ),
)
source = metadata.payload_with(pixels[None], None)
runtime = cellprofiler_runtime_adapter_for_test(
- runtime_value_store=RuntimeValueStore(), callable_contract=contract,
+ runtime_value_store=RuntimeValueStore(),
+ callable_contract=contract,
variable_components=(Microscopy.Channel,),
- axis_scope=RuntimeExecutionAxisScope.from_raw("synthetic", component=None, value=None),
- artifact_inputs={edge.key: InvocationArtifactInputEdgePlan.from_source_declarations(
- key=edge.key, spec=edge.spec, main_flow_artifacts=contract.artifact_inputs,
- invocation_sources=contract.artifact_inputs,
- ) for edge in invocation.artifact_input_edges},
+ axis_scope=RuntimeExecutionAxisScope.from_raw(
+ "synthetic", component=None, value=None
+ ),
+ artifact_inputs={
+ edge.key: InvocationArtifactInputEdgePlan.from_source_declarations(
+ key=edge.key,
+ spec=edge.spec,
+ main_flow_artifacts=contract.artifact_inputs,
+ invocation_sources=contract.artifact_inputs,
+ )
+ for edge in invocation.artifact_input_edges
+ },
)
executor = CellProfilerModuleExecutor(gray_to_color, contract)
request = executor._image_request(
- source, runtime, module_type=GrayToColorModule,
+ source,
+ runtime,
+ module_type=GrayToColorModule,
active_input_specs=contract.artifact_inputs.specs,
)
assert contract.artifact_inputs.names() == ("FITC",)
@@ -93,22 +131,38 @@ def test_declared_fitc_runtime_plane_retains_values_and_physical_identity(explic
assert request.plane_projection.axis is RuntimePlaneAxis.RUNTIME_SLICE
assert request.plane_projection.axis_size == 1
assert request.plane_projection.plane_index is None
- assert request.payload.slices[0].metadata.plane_axis is RuntimePlaneAxis.SOURCE_BINDING
+ assert (
+ request.payload.slices[0].metadata.plane_axis is RuntimePlaneAxis.SOURCE_BINDING
+ )
assert request.payload.slices[0].metadata.source_image_names == ("FITC",)
execution = executor._invocation_request(
- image_request=request, adapter=runtime, current_image=source,
+ image_request=request,
+ adapter=runtime,
+ current_image=source,
module_type=GrayToColorModule,
- kwargs={"color_scheme": GrayToColorModule.Scheme.STACK, "rescale_intensity": False},
+ kwargs={
+ "color_scheme": GrayToColorModule.Scheme.STACK,
+ "rescale_intensity": False,
+ },
)
result = CellProfilerFunctionContractExecutor().execute(
- contract, contract.resolve_canonical_raw_callable(), execution.payload, execution.kwargs,
- execution_mode=execution.execution_mode, plane_projection=execution.plane_projection,
+ contract,
+ contract.resolve_canonical_raw_callable(),
+ execution.payload,
+ execution.kwargs,
+ execution_mode=execution.execution_mode,
+ plane_projection=execution.plane_projection,
)
# Float32 is the original Stack runner's output contract, not unit normalization.
assert result.metadata.plane_axis is RuntimePlaneAxis.RUNTIME_SLICE
- plane = RuntimeSliceProjection.value_for_slice(result, RuntimePlaneAxisValueProjection.from_selected_plane(
- axis=RuntimePlaneAxis.RUNTIME_SLICE, plane_index=0, axis_size=1,
- ))
+ plane = RuntimeSliceProjection.value_for_slice(
+ result,
+ RuntimePlaneAxisValueProjection.from_selected_plane(
+ axis=RuntimePlaneAxis.RUNTIME_SLICE,
+ plane_index=0,
+ axis_size=1,
+ ),
+ )
assert plane.data.shape == (4, 5, 1)
np.testing.assert_array_equal(plane.data[..., 0], pixels)
np.testing.assert_array_equal(source.data[0], pixels)
@@ -122,17 +176,27 @@ def test_declared_fitc_runtime_plane_retains_values_and_physical_identity(explic
def _source_plane(pixels, alias):
return ImagePayloadMetadata(
source_path="/synthetic/A01_s1_w2_z1_t1.tif",
- source_component_metadata={"well": "A01", "site": "1", "channel": "2", "z_index": "1", "timepoint": "1"},
+ source_component_metadata={
+ "well": "A01",
+ "site": "1",
+ "channel": "2",
+ "z_index": "1",
+ "timepoint": "1",
+ },
source_image_names=(alias,),
source_voxel_spacing=SourceVoxelSpacing((1.0, 1.0)),
- source_spatial_domain=SourceSpatialDomain(origin_yx=(0, 0), source_shape_yx=pixels.shape[:2]),
+ source_spatial_domain=SourceSpatialDomain(
+ origin_yx=(0, 0), source_shape_yx=pixels.shape[:2]
+ ),
).payload_with(pixels, None)
def _execute_bound_stack(bundle):
contract = CallableContract.from_callable(gray_to_color)
return CellProfilerFunctionContractExecutor().execute(
- contract, contract.resolve_canonical_raw_callable(), bundle,
+ contract,
+ contract.resolve_canonical_raw_callable(),
+ bundle,
{"color_scheme": GrayToColorModule.Scheme.STACK, "rescale_intensity": False},
execution_mode=FullStackExecution,
plane_projection=RuntimePlaneAxisValueProjection.preserve(
@@ -143,11 +207,17 @@ def _execute_bound_stack(bundle):
)
-@pytest.mark.parametrize("aliases", [("RawBody", "CappedOutgrowth"), ("IndependentRoleA", "IndependentRoleB")])
+@pytest.mark.parametrize(
+ "aliases",
+ [("RawBody", "CappedOutgrowth"), ("IndependentRoleA", "IndependentRoleB")],
+)
def test_two_processing_roles_share_physical_channel_without_fabrication(aliases):
raw = np.arange(20, dtype=np.float32).reshape(4, 5) * 1000
capped = np.clip(raw / 6000, 0, 1)
- inputs = tuple(_source_plane(pixels, alias) for pixels, alias in zip((raw, capped), aliases, strict=True))
+ inputs = tuple(
+ _source_plane(pixels, alias)
+ for pixels, alias in zip((raw, capped), aliases, strict=True)
+ )
bundle = ImagePayloadBundleContext.from_payloads(inputs).compose()
assert bundle.metadata.plane_axis is RuntimePlaneAxis.SOURCE_BINDING
result = _execute_bound_stack(bundle)
@@ -167,10 +237,17 @@ def test_projected_runtime_plane_needs_a_separate_named_binding_axis():
pixels = np.arange(20, dtype=np.float32).reshape(4, 5) * 1000
scalar = _source_plane(pixels, "FITC")
metadata = scalar.metadata
- retained = replace(metadata, plane_axis=RuntimePlaneAxis.RUNTIME_SLICE).payload_with(pixels[None], None)
- projected = RuntimeSliceProjection.value_for_slice(retained, RuntimePlaneAxisValueProjection.from_selected_plane(
- axis=RuntimePlaneAxis.RUNTIME_SLICE, plane_index=0, axis_size=1,
- ))
+ retained = replace(
+ metadata, plane_axis=RuntimePlaneAxis.RUNTIME_SLICE
+ ).payload_with(pixels[None], None)
+ projected = RuntimeSliceProjection.value_for_slice(
+ retained,
+ RuntimePlaneAxisValueProjection.from_selected_plane(
+ axis=RuntimePlaneAxis.RUNTIME_SLICE,
+ plane_index=0,
+ axis_size=1,
+ ),
+ )
assert projected.metadata.plane_axis is None
bundle = ImagePayloadBundleContext.from_payloads((projected,)).compose()
assert bundle.metadata.plane_axis is RuntimePlaneAxis.SOURCE_BINDING
@@ -182,7 +259,9 @@ def test_projected_runtime_plane_needs_a_separate_named_binding_axis():
def test_stack_color_output_is_not_a_second_scalar_gray_binding():
pixels = np.arange(20, dtype=np.float32).reshape(4, 5)
- named = ImagePayloadBundleContext.from_payloads((_source_plane(pixels, "FITC"),)).compose()
+ named = ImagePayloadBundleContext.from_payloads(
+ (_source_plane(pixels, "FITC"),)
+ ).compose()
color = _execute_bound_stack(named)
# The existing STACK contract emits YXC even for one role. A second grayscale
# binding introduces C,Y,X,source-color rather than the required C,Y,X.
diff --git a/tests/unit/test_gray_to_color_payload_axis_compile_432.py b/tests/unit/test_gray_to_color_payload_axis_compile_432.py
index d66fd1323b..cdb2bd8dba 100644
--- a/tests/unit/test_gray_to_color_payload_axis_compile_432.py
+++ b/tests/unit/test_gray_to_color_payload_axis_compile_432.py
@@ -7,26 +7,41 @@
import tifffile
from openhcs.core.callable_contract import (
- CallableContract, CreatedPayloadAxis, PayloadAxisRequirement, PreservedPayloadAxes,
- creates_payload_axis, preserves_payload_axes,
+ CallableContract,
+ CreatedPayloadAxis,
+ PayloadAxisRequirement,
+ PreservedPayloadAxes,
+ creates_payload_axis,
+ preserves_payload_axes,
)
from openhcs.core.compiled_step_plan import CompiledStepPlan
from openhcs.core.function_patterns import (
- CompiledFunctionGroup, CompiledFunctionPattern, PayloadAxisProof,
- InheritedPayloadAxisProof, CreatedPayloadAxisProof,
+ CompiledFunctionGroup,
+ CompiledFunctionPattern,
+ PayloadAxisProof,
+ InheritedPayloadAxisProof,
+ CreatedPayloadAxisProof,
UnprovedPayloadAxisProof,
)
from openhcs.core.pipeline.compiler import PipelineCompiler
from openhcs.core.aligned_image_payload import ImagePayloadBundleContext
from openhcs.core.image_payload_execution_mode import NaturalExecution
from openhcs.core.step_dependencies import StepOutputInput
-from openhcs.interop.cellprofiler.runtime.function_contract_execution import CellProfilerFunctionContractExecutor
+from openhcs.interop.cellprofiler.runtime.function_contract_execution import (
+ CellProfilerFunctionContractExecutor,
+)
from openhcs.processing.backends.cellprofiler.color import (
- ColorToGrayMode, ImageChannelType, color_to_gray, gray_to_color,
+ ColorToGrayMode,
+ ImageChannelType,
+ color_to_gray,
+ gray_to_color,
split_color_to_gray,
)
-from test_gray_to_color_binding_axis_432 import _execute_bound_stack, _source_plane
-from test_payload_axis_compile_gate import _compiled_pattern, _session
+from tests.unit.test_gray_to_color_binding_axis_432 import (
+ _execute_bound_stack,
+ _source_plane,
+)
+from tests.unit.test_payload_axis_compile_gate import _compiled_pattern, _session
from openhcs.core.payload_axes import ColourSampleAxisSpec
from openhcs.core.axes import ColourAxis
@@ -36,13 +51,18 @@ def _gray_creator_session(tmp_path):
tifffile.imwrite(path, np.zeros((4, 5), dtype=np.uint16))
session = _session(tmp_path, (path,))
producer = replace(
- session.plans[0], step_name="ComposeScalarRoles", step_scope_id="step-0",
+ session.plans[0],
+ step_name="ComposeScalarRoles",
+ step_scope_id="step-0",
compiled_function_pattern=_compiled_pattern(gray_to_color),
)
consumer = CompiledStepPlan(
- step_index=1, step_name="SplitComposedRoles", axis_id="A01",
+ step_index=1,
+ step_name="SplitComposedRoles",
+ axis_id="A01",
main_input_dependency=StepOutputInput(
- source_step_index=0, source_step_scope_id="step-0",
+ source_step_index=0,
+ source_step_scope_id="step-0",
),
compiled_function_pattern=_compiled_pattern(color_to_gray),
)
@@ -59,14 +79,23 @@ def test_declared_gray_creator_proves_color_consumer_before_execution(tmp_path):
def test_declared_gray_creator_proves_same_group_color_consumer(tmp_path):
session = _gray_creator_session(tmp_path)
- group = CompiledFunctionGroup("default", tuple(
- invocation
- for plan in session.plans.values()
- for invocation in plan.compiled_function_pattern.iter_invocations()
- ))
- session.plans = {0: replace(session.plans[0], compiled_function_pattern=CompiledFunctionPattern(
- groups=(group,), is_grouped=False,
- ))}
+ group = CompiledFunctionGroup(
+ "default",
+ tuple(
+ invocation
+ for plan in session.plans.values()
+ for invocation in plan.compiled_function_pattern.iter_invocations()
+ ),
+ )
+ session.plans = {
+ 0: replace(
+ session.plans[0],
+ compiled_function_pattern=CompiledFunctionPattern(
+ groups=(group,),
+ is_grouped=False,
+ ),
+ )
+ }
session.context.step_plans = session.plans
PipelineCompiler.validate_payload_axis_requirements(session)
@@ -76,10 +105,19 @@ def test_preservation_annotation_cannot_create_missing_source_payload_axis(tmp_p
producer = session.plans[0]
(invocation,) = tuple(producer.compiled_function_pattern.iter_invocations())
# A local counterexample only: do not mutate production callable/registry.
- falsely_preserving = replace(invocation, contract=replace(invocation.contract, payload_axis_transition=PreservedPayloadAxes()))
- session.plans[0] = replace(producer, compiled_function_pattern=CompiledFunctionPattern(
- groups=(CompiledFunctionGroup("default", (falsely_preserving,)),), is_grouped=False,
- ))
+ falsely_preserving = replace(
+ invocation,
+ contract=replace(
+ invocation.contract, payload_axis_transition=PreservedPayloadAxes()
+ ),
+ )
+ session.plans[0] = replace(
+ producer,
+ compiled_function_pattern=CompiledFunctionPattern(
+ groups=(CompiledFunctionGroup("default", (falsely_preserving,)),),
+ is_grouped=False,
+ ),
+ )
session.context.step_plans = session.plans
with pytest.raises(ValueError, match="requires a declared ColourAxis payload axis"):
PipelineCompiler.validate_payload_axis_requirements(session)
@@ -88,18 +126,26 @@ def test_preservation_annotation_cannot_create_missing_source_payload_axis(tmp_p
def _runtime_scalar_role_split():
raw = np.arange(20, dtype=np.float32).reshape(4, 5) * 1000 / 65535
capped = np.clip((raw - 39 / 65535) / (5961 / 65535), 0, 1)
- bundle = ImagePayloadBundleContext.from_payloads(tuple(
- _source_plane(pixels, name)
- for pixels, name in ((raw, "RawBody"), (capped, "CappedOutgrowth"))
- )).compose()
+ bundle = ImagePayloadBundleContext.from_payloads(
+ tuple(
+ _source_plane(pixels, name)
+ for pixels, name in ((raw, "RawBody"), (capped, "CappedOutgrowth"))
+ )
+ ).compose()
composite = _execute_bound_stack(bundle)
assert composite.data.shape == (4, 5, 2)
assert composite.metadata.axis_position(ColourAxis) == -1
contract = CallableContract.from_callable(color_to_gray)
result = CellProfilerFunctionContractExecutor().execute(
- contract, contract.resolve_canonical_raw_callable(), composite,
- {"mode": ColorToGrayMode.SPLIT, "image_type": ImageChannelType.CHANNELS,
- "channel_indices": (0, 1), "contributions": (1.0, 1.0)},
+ contract,
+ contract.resolve_canonical_raw_callable(),
+ composite,
+ {
+ "mode": ColorToGrayMode.SPLIT,
+ "image_type": ImageChannelType.CHANNELS,
+ "channel_indices": (0, 1),
+ "contributions": (1.0, 1.0),
+ },
execution_mode=NaturalExecution,
)
return result, (raw, capped)
@@ -112,7 +158,9 @@ def test_original_runtime_split_preserves_both_role_values_and_physical_channel(
assert output.data.shape == (4, 5)
np.testing.assert_array_equal(output.data, expected)
assert output.metadata.source_component_metadata["channel"] == "2"
- assert set(output.metadata.source_image_paths) == {"/synthetic/A01_s1_w2_z1_t1.tif"}
+ assert set(output.metadata.source_image_paths) == {
+ "/synthetic/A01_s1_w2_z1_t1.tif"
+ }
def test_scalar_role_outputs_consume_the_color_payload_axis():
@@ -123,7 +171,9 @@ def test_scalar_role_outputs_consume_the_color_payload_axis():
@pytest.mark.parametrize("same_group", [False, True])
-def test_independent_creator_declaration_needs_no_consumer_edits(tmp_path, monkeypatch, same_group):
+def test_independent_creator_declaration_needs_no_consumer_edits(
+ tmp_path, monkeypatch, same_group
+):
@creates_payload_axis(ColourAxis)
def independent_lane_creator(image):
return np.stack((image, image * 2), axis=-1)
@@ -144,15 +194,30 @@ def independent_lane_preserver(image):
for invocation in _compiled_pattern(callable_).iter_invocations()
)
if same_group:
- consumer_invocations = tuple(session.plans[1].compiled_function_pattern.iter_invocations())
- session.plans = {0: replace(session.plans[0], compiled_function_pattern=CompiledFunctionPattern(
- groups=(CompiledFunctionGroup("default", producer_invocations + consumer_invocations),),
- is_grouped=False,
- ))}
+ consumer_invocations = tuple(
+ session.plans[1].compiled_function_pattern.iter_invocations()
+ )
+ session.plans = {
+ 0: replace(
+ session.plans[0],
+ compiled_function_pattern=CompiledFunctionPattern(
+ groups=(
+ CompiledFunctionGroup(
+ "default", producer_invocations + consumer_invocations
+ ),
+ ),
+ is_grouped=False,
+ ),
+ )
+ }
else:
- session.plans[0] = replace(session.plans[0], compiled_function_pattern=CompiledFunctionPattern(
- groups=(CompiledFunctionGroup("default", producer_invocations),), is_grouped=False,
- ))
+ session.plans[0] = replace(
+ session.plans[0],
+ compiled_function_pattern=CompiledFunctionPattern(
+ groups=(CompiledFunctionGroup("default", producer_invocations),),
+ is_grouped=False,
+ ),
+ )
session.context.step_plans = session.plans
def reject_source_backtracking(*args, **kwargs):
@@ -170,9 +235,12 @@ def test_group_proof_stops_at_creator_but_rejects_later_unknown(unknown_after_cr
def unknown(image):
return image
- functions = (gray_to_color, unknown) if unknown_after_creator else (unknown, gray_to_color)
+ functions = (
+ (gray_to_color, unknown) if unknown_after_creator else (unknown, gray_to_color)
+ )
invocations = tuple(
- invocation for callable_ in functions
+ invocation
+ for callable_ in functions
for invocation in _compiled_pattern(callable_).iter_invocations()
)
proof = CompiledFunctionGroup("default", invocations).payload_axis_proof(
@@ -181,7 +249,9 @@ def unknown(image):
failures = []
assert proof.validate_obligation(
failure_message=lambda invocation: invocation.contract.function_name,
- source_validation=lambda: pytest.fail("Creation/rejection must stop source validation"),
+ source_validation=lambda: pytest.fail(
+ "Creation/rejection must stop source validation"
+ ),
failures=failures,
)
assert failures == (["unknown"] if unknown_after_creator else [])
@@ -197,7 +267,9 @@ def validate_source():
complete = InheritedPayloadAxisProof().validate_obligation(
source_validation=validate_source,
- failure_message=lambda invocation: pytest.fail("Inherited proof has no failed invocation"),
+ failure_message=lambda invocation: pytest.fail(
+ "Inherited proof has no failed invocation"
+ ),
failures=failures,
)
assert complete is source_complete
@@ -206,14 +278,25 @@ def validate_source():
@pytest.mark.parametrize("capability_first", [False, True])
-@pytest.mark.parametrize("proof_type,args,expected_visits,expected_failures", [
- (InheritedPayloadAxisProof, (), ["before", "source", "after"], []),
- (CreatedPayloadAxisProof, (), ["before", "after"], []),
- (UnprovedPayloadAxisProof,
- tuple(_compiled_pattern(color_to_gray).iter_invocations()), ["before", "after"], ["rejected color_to_gray"]),
-])
+@pytest.mark.parametrize(
+ "proof_type,args,expected_visits,expected_failures",
+ [
+ (InheritedPayloadAxisProof, (), ["before", "source", "after"], []),
+ (CreatedPayloadAxisProof, (), ["before", "after"], []),
+ (
+ UnprovedPayloadAxisProof,
+ tuple(_compiled_pattern(color_to_gray).iter_invocations()),
+ ["before", "after"],
+ ["rejected color_to_gray"],
+ ),
+ ],
+)
def test_independent_same_node_validation_capability_composes_both_mro_orders(
- capability_first, proof_type, args, expected_visits, expected_failures,
+ capability_first,
+ proof_type,
+ args,
+ expected_visits,
+ expected_failures,
):
visits, failures = [], []
@@ -226,7 +309,11 @@ def validate_obligation(self, **kwargs):
# Same nominal owner in both bases puts the independent capability before
# the shared operation in C3, regardless of its position beside the leaf.
- bases = (ValidationVisitCapability, proof_type) if capability_first else (proof_type, ValidationVisitCapability)
+ bases = (
+ (ValidationVisitCapability, proof_type)
+ if capability_first
+ else (proof_type, ValidationVisitCapability)
+ )
declared_proof = type("IndependentlyObservedProof", bases, {})(*args)
def validate_source():
@@ -235,7 +322,8 @@ def validate_source():
assert declared_proof.validate_obligation(
source_validation=validate_source,
- failure_message=lambda invocation: "rejected " + invocation.contract.function_name,
+ failure_message=lambda invocation: "rejected "
+ + invocation.contract.function_name,
failures=failures,
)
assert visits == expected_visits
@@ -256,18 +344,25 @@ def reject_source():
assert failures == ["exact source missing its declared payload axis"]
-def test_color_to_gray_does_not_preserve_consumed_payload_axis_for_later_consumer(tmp_path):
+def test_color_to_gray_does_not_preserve_consumed_payload_axis_for_later_consumer(
+ tmp_path,
+):
session = _gray_creator_session(tmp_path)
consumer = session.plans[1]
session.plans[2] = replace(
- consumer, step_index=2, step_name="RejectSecondScalarSplit",
+ consumer,
+ step_index=2,
+ step_name="RejectSecondScalarSplit",
main_input_dependency=StepOutputInput(
- source_step_index=1, source_step_scope_id="step-1",
+ source_step_index=1,
+ source_step_scope_id="step-1",
),
)
session.plans[1] = replace(consumer, step_scope_id="step-1")
session.context.step_plans = session.plans
- with pytest.raises(ValueError, match="not preserved by producer step 1 callable 'color_to_gray'"):
+ with pytest.raises(
+ ValueError, match="not preserved by producer step 1 callable 'color_to_gray'"
+ ):
PipelineCompiler.validate_payload_axis_requirements(session)
@@ -281,9 +376,15 @@ def test_scalar_projection_modes_keep_pixels_mask_and_original_source(image_type
expected = split_color_to_gray(source, image_type, (0, 1))
contract = CallableContract.from_callable(color_to_gray)
result = CellProfilerFunctionContractExecutor().execute(
- contract, contract.resolve_canonical_raw_callable(), source,
- {"mode": ColorToGrayMode.SPLIT, "image_type": image_type,
- "channel_indices": (0, 1), "contributions": (1.0, 1.0)},
+ contract,
+ contract.resolve_canonical_raw_callable(),
+ source,
+ {
+ "mode": ColorToGrayMode.SPLIT,
+ "image_type": image_type,
+ "channel_indices": (0, 1),
+ "contributions": (1.0, 1.0),
+ },
execution_mode=NaturalExecution,
)
for output, expected_pixels in zip(result.slices, expected, strict=True):
@@ -292,7 +393,10 @@ def test_scalar_projection_modes_keep_pixels_mask_and_original_source(image_type
output_metadata = output.metadata
assert output_metadata.axis_position(ColourAxis) is None
assert output_metadata.source_path == metadata.source_path
- assert output_metadata.source_component_metadata == metadata.source_component_metadata
+ assert (
+ output_metadata.source_component_metadata
+ == metadata.source_component_metadata
+ )
assert output_metadata.source_image_names == metadata.source_image_names
assert output_metadata.source_voxel_spacing == metadata.source_voxel_spacing
assert output_metadata.source_spatial_domain == metadata.source_spatial_domain
diff --git a/tests/unit/test_library_registry_discovery.py b/tests/unit/test_library_registry_discovery.py
index f364215d7f..73fc7c4082 100644
--- a/tests/unit/test_library_registry_discovery.py
+++ b/tests/unit/test_library_registry_discovery.py
@@ -50,6 +50,110 @@ def test_native_catalog_import_failures_follow_declared_memory_admission(
assert registry.get_modules_to_scan() == []
+@pytest.mark.parametrize(
+ "requirement, outcome",
+ [
+ # The package's own marker leaves the distribution out of this environment.
+ ("absent-catalog-probe>=1; platform_system == 'NoSuchSystem'", "excluded"),
+ # Declared only for an extra this installation did not select.
+ ("absent-catalog-probe>=1; extra == 'probe'", "excluded"),
+ # Required here, yet absent: a broken installation fails loudly.
+ ("absent-catalog-probe>=1", "broken"),
+ # A module cannot claim a distribution the package never declares.
+ ("unrelated-catalog-probe>=1", "undeclared"),
+ ],
+)
+def test_native_catalog_excludes_modules_whose_distribution_markers_exclude_here(
+ tmp_path, monkeypatch, requirement, outcome
+) -> None:
+ from packaging.requirements import Requirement
+
+ from openhcs.processing.backends.lib_registry import openhcs_registry
+ from openhcs.processing.backends.lib_registry.openhcs_registry import (
+ OpenHCSRegistry,
+ )
+
+ package_name = "native_catalog_distribution_probe"
+ package = tmp_path / package_name
+ package.mkdir()
+ (package / "__init__.py").write_text("")
+ (package / "processing.py").write_text(
+ "from openhcs.core.memory.decorators import numpy\n"
+ "import absent_catalog_probe\n"
+ "REQUIRED_DISTRIBUTIONS = ('absent-catalog-probe',)\n"
+ "@numpy\n"
+ "def process(image):\n return image\n"
+ )
+ monkeypatch.syspath_prepend(str(tmp_path))
+ monkeypatch.setenv("OPENHCS_CPU_ONLY", "1")
+ monkeypatch.setattr(
+ openhcs_registry,
+ "_openhcs_requirements",
+ lambda: (Requirement(requirement),),
+ )
+ registry = OpenHCSRegistry()
+ registry.MODULES_TO_SCAN = [f"{package_name}.processing"]
+
+ if outcome == "excluded":
+ assert registry.get_modules_to_scan() == []
+ assert registry.cache_discovery_context()[
+ "distributions_excluded_by_package_metadata"
+ ] == ["absent-catalog-probe"]
+ elif outcome == "broken":
+ with pytest.raises(ModuleNotFoundError, match="absent_catalog_probe"):
+ registry.get_modules_to_scan()
+ else:
+ with pytest.raises(ValueError, match="absent-catalog-probe"):
+ registry.get_modules_to_scan()
+
+
+@pytest.mark.parametrize(
+ "platform_system, platform_machine, excluded",
+ [
+ ("Darwin", "x86_64", True),
+ ("Darwin", "arm64", False),
+ ("Linux", "x86_64", False),
+ ],
+)
+def test_basicpy_catalog_module_follows_the_package_basicpy_marker(
+ monkeypatch, platform_system, platform_machine, excluded
+) -> None:
+ """Intel macOS installs omit openhcs-basicpy; its catalog module follows."""
+
+ import importlib.metadata
+
+ import packaging.markers
+
+ from openhcs.processing.backends.lib_registry.openhcs_registry import (
+ _module_distributions_excluded_by_package_metadata,
+ )
+
+ environment = packaging.markers.default_environment()
+ monkeypatch.setattr(
+ packaging.markers,
+ "default_environment",
+ lambda: {
+ **environment,
+ "platform_system": platform_system,
+ "platform_machine": platform_machine,
+ },
+ )
+ installed_distribution = importlib.metadata.distribution
+
+ def distribution_without_basicpy(name: str):
+ if name == "openhcs-basicpy":
+ raise importlib.metadata.PackageNotFoundError(name)
+ return installed_distribution(name)
+
+ monkeypatch.setattr(
+ importlib.metadata, "distribution", distribution_without_basicpy
+ )
+
+ assert _module_distributions_excluded_by_package_metadata(
+ "openhcs.processing.backends.enhance.basic_processor_jax"
+ ) == (("openhcs-basicpy",) if excluded else ())
+
+
def test_concurrent_catalog_requests_share_one_metadata_load(monkeypatch) -> None:
from openhcs.processing.backends.lib_registry.registry_service import (
RegistryService,
diff --git a/tests/unit/test_named_checkpoint_publication.py b/tests/unit/test_named_checkpoint_publication.py
index 11a07a52f1..5b2643e910 100644
--- a/tests/unit/test_named_checkpoint_publication.py
+++ b/tests/unit/test_named_checkpoint_publication.py
@@ -12,7 +12,7 @@
import pytest
import tifffile
-from test_artifact_publication_journey import _plate, progress_events
+from tests.unit.test_artifact_publication_journey import _plate, progress_events
from openhcs.core.artifacts.sidecars import QaCheckpoint
from openhcs.core.artifacts.kinds import ImageArtifactType
from openhcs.core.artifacts.specs import (
@@ -20,14 +20,20 @@
MainFlowStackOutputSpec,
)
from openhcs.core.config import (
- LazyPathPlanningConfig, LazyProcessingConfig, LazyStepMaterializationConfig,
+ LazyPathPlanningConfig,
+ LazyProcessingConfig,
+ LazyStepMaterializationConfig,
PipelineConfig,
)
from openhcs.core.memory import numpy as numpy_decorator
from openhcs.core.orchestrator.orchestrator import PipelineOrchestrator
-from openhcs.core.orchestrator.execution_result import RuntimeContextObservation, RuntimeExecutionObservation
+from openhcs.core.orchestrator.execution_result import (
+ RuntimeContextObservation,
+ RuntimeExecutionObservation,
+)
from openhcs.core.pipeline.function_contracts import (
- artifact_outputs, required_axis_roles,
+ artifact_outputs,
+ required_axis_roles,
)
from openhcs.core.projected_image_output import SourceProjectedImageOutput
from openhcs.core.runtime_image_values import ImagePayloadMetadata
@@ -41,13 +47,13 @@
Pure3DContract,
)
from openhcs.processing.materialization import (
- ImageFileOptions, MaterializationSpec,
+ ImageFileOptions,
+ MaterializationSpec,
)
from openhcs.core.axes import TileAxis
from openhcs.domains.microscopy.axes import Microscopy
-
@dataclass(frozen=True)
class SyntheticAggregateField(SourceProjectedImageOutput):
data: np.ndarray
@@ -58,15 +64,22 @@ def with_data(self, data):
def resolve_source_context(self, source, projection):
assert projection is not None and projection.plane_index is None
assert projection.axis_size == 24
- return source.metadata.collapse_leading_plane_axis().payload_with(self.data, None)
+ return source.metadata.collapse_leading_plane_axis().payload_with(
+ self.data, None
+ )
def _field(name):
return MainFlowPlaneProjectionOutputSpec.output(
- name, ImageArtifactType, sidecar_role=QaCheckpoint,
- materialization=MaterializationSpec(ImageFileOptions(
- filename_suffix=".tif", filename_identity=NameByArtifactName,
- )),
+ name,
+ ImageArtifactType,
+ sidecar_role=QaCheckpoint,
+ materialization=MaterializationSpec(
+ ImageFileOptions(
+ filename_suffix=".tif",
+ filename_identity=NameByArtifactName,
+ )
+ ),
)
@@ -74,7 +87,8 @@ def _field(name):
@required_axis_roles(TileAxis)
@artifact_outputs(
MainFlowStackOutputSpec.output("corrected", ImageArtifactType),
- _field("flat"), _field("dark"),
+ _field("flat"),
+ _field("dark"),
)
def synthetic_three_outputs(image):
assert image.shape == (24, 8, 8)
@@ -86,25 +100,35 @@ def synthetic_three_outputs(image):
@pytest.mark.parametrize("main_filter", (0, 1))
-def test_named_and_ordinary_checkpoint_inventory_owns_every_address(tmp_path, main_filter):
+def test_named_and_ordinary_checkpoint_inventory_owns_every_address(
+ tmp_path, main_filter
+):
plate = _plate(tmp_path / "source", site_count=24)
config = PipelineConfig(
- path_planning_config=LazyPathPlanningConfig(output_dir_suffix="_out", partition_filter=main_filter),
- num_workers=1, use_threading=True,
+ path_planning_config=LazyPathPlanningConfig(
+ output_dir_suffix="_out", partition_filter=main_filter
+ ),
+ num_workers=1,
+ use_threading=True,
)
orchestrator = PipelineOrchestrator(plate, pipeline_config=config).initialize()
step = FunctionStep(
func=synthetic_three_outputs,
processing_config=LazyProcessingConfig(
- group_by=Microscopy.Channel, variable_components=[Microscopy.Site],
+ group_by=Microscopy.Channel,
+ variable_components=[Microscopy.Site],
),
step_materialization_config=LazyStepMaterializationConfig(enabled=True),
)
bundle = orchestrator.compile_pipelines([step])
- observations = RuntimeExecutionObservation(contexts=tuple(
- RuntimeContextObservation(context_key=key, records=(), outputs=step.process(context, 0))
- for key, context in bundle.runtime_contexts.items()
- ))
+ observations = RuntimeExecutionObservation(
+ contexts=tuple(
+ RuntimeContextObservation(
+ context_key=key, records=(), outputs=step.process(context, 0)
+ )
+ for key, context in bundle.runtime_contexts.items()
+ )
+ )
OpenHCSMetadataTarget.finalize_completed_dataset(
bundle.runtime_contexts, runtime_observations=(observations,)
)
@@ -113,10 +137,14 @@ def test_named_and_ordinary_checkpoint_inventory_owns_every_address(tmp_path, ma
reopened = VirtualWorkspaceSourceProjection.from_openhcs_metadata(root, metadata)
for target, count in (("checkpoints", 48), ("checkpoints_results", 2)):
entry = metadata["subdirectories"][target]
- inventory = {str(path.relative_to(root)) for path in (root / target).glob("*.tif")}
+ inventory = {
+ str(path.relative_to(root)) for path in (root / target).glob("*.tif")
+ }
assert len(inventory) == count
assert set(entry["image_files"]) == inventory
- assert {item["virtual_path"] for item in entry["source_projection"]} == inventory
+ assert {
+ item["virtual_path"] for item in entry["source_projection"]
+ } == inventory
for item in entry["source_projection"]:
retained = ImagePayloadMetadata.from_mapping(item["image_metadata"])
assert retained.source_voxel_spacing == SourceVoxelSpacing((0.65, 0.65))
@@ -126,13 +154,23 @@ def test_named_and_ordinary_checkpoint_inventory_owns_every_address(tmp_path, ma
projection
for item in checkpoints
if isinstance(
- projection := reopened.source_projections_by_virtual_path[item["virtual_path"]],
+ projection := reopened.source_projections_by_virtual_path[
+ item["virtual_path"]
+ ],
SourceArtifactProjection,
)
]
assert len(ordinary) == len(checkpoints) - len(ordinary) == 24
assert {projection.address for projection in ordinary} == {
- OpenHCSPlaneAddress(((Microscopy.Well, "A01"), (Microscopy.Site, site), (Microscopy.Channel, 1), (Microscopy.ZIndex, 1), (Microscopy.Timepoint, 1)))
+ OpenHCSPlaneAddress(
+ (
+ (Microscopy.Well, "A01"),
+ (Microscopy.Site, site),
+ (Microscopy.Channel, 1),
+ (Microscopy.ZIndex, 1),
+ (Microscopy.Timepoint, 1),
+ )
+ )
for site in range(1, 25)
}
for item in checkpoints:
@@ -154,9 +192,19 @@ def test_named_and_ordinary_checkpoint_inventory_owns_every_address(tmp_path, ma
assert all_inventory.issubset(reopened.source_projections_by_virtual_path)
if main_filter:
assert len(metadata["subdirectories"]["images"]["image_files"]) == 24
- assert PipelineOrchestrator(root).initialize().microscope_handler.get_pixel_size(root) == 0.65
+ assert (
+ PipelineOrchestrator(root)
+ .initialize()
+ .microscope_handler.get_pixel_size(root)
+ == 0.65
+ )
# Keep fail-closed reconciliation: an actually unowned image is still an
# error. This is not a missing-address permissive fallback.
- tifffile.imwrite(root / "checkpoints" / "unaddressed.tif", np.zeros((8, 8), dtype=np.uint16))
- with pytest.raises(MetadataWriteError, match="Saved images lack typed produced addresses.*unaddressed"):
+ tifffile.imwrite(
+ root / "checkpoints" / "unaddressed.tif", np.zeros((8, 8), dtype=np.uint16)
+ )
+ with pytest.raises(
+ MetadataWriteError,
+ match="Saved images lack typed produced addresses.*unaddressed",
+ ):
OpenHCSMetadataTarget.finalize_completed_dataset(bundle.runtime_contexts)
diff --git a/tests/unit/test_openhcs_adapter.py b/tests/unit/test_openhcs_adapter.py
index 766cc3ac75..bf4b86f135 100644
--- a/tests/unit/test_openhcs_adapter.py
+++ b/tests/unit/test_openhcs_adapter.py
@@ -153,7 +153,6 @@ def test_openhcs_adapter_preserves_declared_main_flow_output_policy(
configured = adapter._execution_global_config(
request,
output_suffix="_candidate",
- output_dataset_root=tmp_path / "candidate-plate",
compilation_debug_config=CompilationDebugConfig(),
)
@@ -168,6 +167,8 @@ def test_openhcs_adapter_preserves_declared_main_flow_output_policy(
assert (
configured.analysis_consolidation_config.output_filename == "custom-summary.csv"
)
+ # Analysis results resolve inside the compiled output root by default.
+ assert configured.materialization_results_path == Path("results")
assert base_config.path_planning_config.global_output_folder is None
assert base_config.analysis_consolidation_config.enabled is True
@@ -388,13 +389,15 @@ def poll_status(self, execution_id):
== FakeZMQExecutionClient.submitted[1].pipeline_code
)
assert all(
- submission.global_config_code == ConfigDocumentAuthority.render(
+ submission.global_config_code
+ == ConfigDocumentAuthority.render(
global_config, expected_config_type=GlobalPipelineConfig
)
for submission in FakeZMQExecutionClient.submitted
)
assert [
- submission.compile_control.compile_only for submission in FakeZMQExecutionClient.submitted
+ submission.compile_control.compile_only
+ for submission in FakeZMQExecutionClient.submitted
] == [
True,
False,
@@ -486,6 +489,19 @@ def test_openhcs_progress_observer_tracks_every_server_event(
assert observer.progress_description() == "artifact_transfer/running"
+def test_openhcs_progress_silence_is_measured_from_run_submission(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ """Server startup belongs to the client's readiness timeout, not the watchdog."""
+ observer = _ZMQProgressTimingObserver()
+ assert observer.inactivity_seconds(observed_at=500.0) == 0.0
+
+ monkeypatch.setattr("benchmark.openhcs_measured_run.time.monotonic", lambda: 200.0)
+ observer.begin_run()
+
+ assert observer.inactivity_seconds(observed_at=321.0) == 121.0
+
+
def test_openhcs_watchdog_allows_execution_without_interval_timer(
monkeypatch: pytest.MonkeyPatch,
) -> None:
diff --git a/tests/unit/test_preparation_admission.py b/tests/unit/test_preparation_admission.py
index 42c4d60a8f..6f1049f93b 100644
--- a/tests/unit/test_preparation_admission.py
+++ b/tests/unit/test_preparation_admission.py
@@ -242,14 +242,16 @@ def prepare_selected():
assert events == ["selected", "unselected", "dynamic"]
-def test_unavailable_affinity_does_not_admit_speculative_parallelism(monkeypatch):
+def test_platform_without_affinity_admits_its_online_cpus(monkeypatch):
events = []
- started, _, _ = simulated_workers(monkeypatch, 8)
+ started, peak, _ = simulated_workers(monkeypatch, 8)
monkeypatch.delattr(os, "sched_getaffinity")
+ monkeypatch.setattr(os, "cpu_count", lambda: 2)
PreparationCacheBatch(
tuple(DeclaredCache(str(i), events) for i in range(3))
).populate_child_caches(max_workers=8)
- assert not started and not events
+ assert started == [("0",), ("1",), ("2",)]
+ assert max(peak) == 2
def test_affinity_query_failure_is_not_misreported_as_success(monkeypatch):
diff --git a/tests/unit/test_rescale_stream_plane_declaration.py b/tests/unit/test_rescale_stream_plane_declaration.py
index 4c9a9cf4c0..a1e75c33cd 100644
--- a/tests/unit/test_rescale_stream_plane_declaration.py
+++ b/tests/unit/test_rescale_stream_plane_declaration.py
@@ -13,8 +13,13 @@
from openhcs.core.runtime_stores import RuntimeValueStore
from openhcs.core.component_group_scope import RuntimeExecutionAxisScope
from openhcs.core.function_patterns import InvocationArtifactInputEdgePlan
-from openhcs.core.runtime_object_label_building import SourceImageObjectLabelBuildRequest
-from openhcs.core.runtime_plane_projection import RuntimePlaneAxis, RuntimePlaneAxisValueProjection
+from openhcs.core.runtime_object_label_building import (
+ SourceImageObjectLabelBuildRequest,
+)
+from openhcs.core.runtime_plane_projection import (
+ RuntimePlaneAxis,
+ RuntimePlaneAxisValueProjection,
+)
from openhcs.core.runtime_object_label_domains import ObjectLabelDomainScope
from openhcs.core.source_image_provenance import SourceImageProvenancePlanes
from openhcs.core.config import StepSourceBindingsConfig
@@ -23,44 +28,69 @@
from openhcs.core.source_metadata import SourceVoxelSpacing, SourceVoxelSpacingUnit
from openhcs.core.steps.function_outputs import StreamOutputBatch
from openhcs.core.steps.function_step import FunctionStep
-from openhcs.core.steps.stream_component_semantics import StreamImagePayloadMetadataProjector
+from openhcs.core.steps.stream_component_semantics import (
+ StreamImagePayloadMetadataProjector,
+)
from openhcs.processing.backends.cellprofiler.intensity import (
- AutomaticHigh, AutomaticLow, RescaleIntensityModule, RescaleMethod, rescale_intensity,
+ AutomaticHigh,
+ AutomaticLow,
+ RescaleIntensityModule,
+ RescaleMethod,
+ rescale_intensity,
)
from openhcs.processing.backends.cellprofiler.shape import measure_object_size_shape
from openhcs.interop.cellprofiler.runtime.adapter import CellProfilerRuntimeAdapter
-from openhcs.interop.cellprofiler.runtime.module_execution import CellProfilerModuleExecutor
-from openhcs.interop.cellprofiler.runtime.function_contract_execution import CellProfilerFunctionContractExecutor
+from openhcs.interop.cellprofiler.runtime.module_execution import (
+ CellProfilerModuleExecutor,
+)
+from openhcs.interop.cellprofiler.runtime.function_contract_execution import (
+ CellProfilerFunctionContractExecutor,
+)
from openhcs.runtime.napari_streaming_handlers import (
- NapariAggregateAxisBindingBuilder, NapariStreamLayerAddress, NapariStreamLayerItem,
+ NapariAggregateAxisBindingBuilder,
+ NapariStreamLayerAddress,
+ NapariStreamLayerItem,
)
from openhcs.runtime.viewer_component_system import (
- ViewerComponentAxisSemanticsFactory, ViewerComponentValueDomainPayload,
+ ViewerComponentAxisSemanticsFactory,
+ ViewerComponentValueDomainPayload,
)
# Reuse the original provider-free compiler and runtime fixtures, not a second ABI.
-from test_cellprofiler_generic_special_input_binding import _compile_public_step
+from tests.unit.test_cellprofiler_generic_special_input_binding import (
+ _compile_public_step,
+)
from tests.unit.cellprofiler_runtime_test_support import (
cellprofiler_runtime_adapter_for_test,
)
from tests.unit.viewer_axes_fixture import STREAM_AXES
-@pytest.mark.parametrize("method", [RescaleMethod.DIVIDE_BY_VALUE, RescaleMethod.MANUAL_INPUT_RANGE])
+@pytest.mark.parametrize(
+ "method", [RescaleMethod.DIVIDE_BY_VALUE, RescaleMethod.MANUAL_INPUT_RANGE]
+)
@pytest.mark.parametrize("matching", [None, "Bright", "Other"])
-def test_public_rescale_preserves_one_source_plane_through_stream_binding(method, matching):
+def test_public_rescale_preserves_one_source_plane_through_stream_binding(
+ method, matching
+):
kwargs = {
- "select_the_input_image": "Bright", "name_the_output_image": "Detection",
- "rescale_method": method, "automatic_low": AutomaticLow.CUSTOM,
- "automatic_high": AutomaticHigh.CUSTOM, "source_low": 0.0,
- "source_high": 20.0, "divisor_value": 2.0,
+ "select_the_input_image": "Bright",
+ "name_the_output_image": "Detection",
+ "rescale_method": method,
+ "automatic_low": AutomaticLow.CUSTOM,
+ "automatic_high": AutomaticHigh.CUSTOM,
+ "source_low": 0.0,
+ "source_high": 20.0,
+ "divisor_value": 2.0,
}
if matching is not None:
kwargs["select_image_to_match_in_maximum_intensity"] = matching
step = FunctionStep(
- func=(rescale_intensity, kwargs), name="SyntheticRescale",
+ func=(rescale_intensity, kwargs),
+ name="SyntheticRescale",
source_bindings=StepSourceBindingsConfig(
- enabled=True, bindings=(NamedSourceBinding("Bright"),),
+ enabled=True,
+ bindings=(NamedSourceBinding("Bright"),),
),
)
invocation = _compile_public_step(step)
@@ -71,18 +101,32 @@ def test_public_rescale_preserves_one_source_plane_through_stream_binding(method
pixels = np.arange(20, dtype=np.float32).reshape(4, 5)
metadata = ImagePayloadMetadata(
source_path="/synthetic/A01_s1_w1_z1_t1.tif",
- source_component_metadata={"well": "A01", "site": "1", "channel": "1", "z_index": "1", "timepoint": "1"},
+ source_component_metadata={
+ "well": "A01",
+ "site": "1",
+ "channel": "1",
+ "z_index": "1",
+ "timepoint": "1",
+ },
source_image_names=("Bright",),
- source_voxel_spacing=SourceVoxelSpacing((1.0, 1.0, 1.0), SourceVoxelSpacingUnit.RELATIVE),
- source_spatial_domain=SourceSpatialDomain(origin_yx=(0, 0), source_shape_yx=(4, 5)),
+ source_voxel_spacing=SourceVoxelSpacing(
+ (1.0, 1.0, 1.0), SourceVoxelSpacingUnit.RELATIVE
+ ),
+ source_spatial_domain=SourceSpatialDomain(
+ origin_yx=(0, 0), source_shape_yx=(4, 5)
+ ),
)
source = metadata.payload_with(pixels, None)
runtime = cellprofiler_runtime_adapter_for_test(
- runtime_value_store=RuntimeValueStore(), callable_contract=contract,
- axis_scope=RuntimeExecutionAxisScope.from_raw("synthetic", component=None, value=None),
+ runtime_value_store=RuntimeValueStore(),
+ callable_contract=contract,
+ axis_scope=RuntimeExecutionAxisScope.from_raw(
+ "synthetic", component=None, value=None
+ ),
artifact_inputs={
edge.key: InvocationArtifactInputEdgePlan.from_source_declarations(
- key=edge.key, spec=edge.spec,
+ key=edge.key,
+ spec=edge.spec,
main_flow_artifacts=contract.artifact_inputs,
invocation_sources=contract.artifact_inputs,
)
@@ -91,50 +135,85 @@ def test_public_rescale_preserves_one_source_plane_through_stream_binding(method
)
executor = CellProfilerModuleExecutor(rescale_intensity, contract)
request = executor._image_request(
- source, runtime, module_type=RescaleIntensityModule,
+ source,
+ runtime,
+ module_type=RescaleIntensityModule,
active_input_specs=contract.artifact_inputs.specs,
)
execution = executor._invocation_request(
- image_request=request, adapter=runtime, current_image=source,
+ image_request=request,
+ adapter=runtime,
+ current_image=source,
module_type=RescaleIntensityModule,
- kwargs={"rescale_method": method, "automatic_low": AutomaticLow.CUSTOM,
- "automatic_high": AutomaticHigh.CUSTOM, "source_low": 0.0,
- "source_high": 20.0, "divisor_value": 2.0},
+ kwargs={
+ "rescale_method": method,
+ "automatic_low": AutomaticLow.CUSTOM,
+ "automatic_high": AutomaticHigh.CUSTOM,
+ "source_low": 0.0,
+ "source_high": 20.0,
+ "divisor_value": 2.0,
+ },
)
result = CellProfilerFunctionContractExecutor().execute(
- contract, rescale_intensity, execution.payload, execution.kwargs,
- execution_mode=execution.execution_mode, plane_projection=execution.plane_projection,
+ contract,
+ rescale_intensity,
+ execution.payload,
+ execution.kwargs,
+ execution_mode=execution.execution_mode,
+ plane_projection=execution.plane_projection,
+ )
+ (item,) = tuple(
+ StreamOutputBatch.project_item(
+ RuntimeProjectionSourceIdentityRequest(
+ value=result,
+ source_description="/synthetic/Detection.tif",
+ )
+ )
+ )
+ fields = StreamImagePayloadMetadataProjector.item_fields(
+ item.metadata, ("well", "site", "channel", "z_index", "timepoint")
)
- (item,) = tuple(StreamOutputBatch.project_item(RuntimeProjectionSourceIdentityRequest(
- value=result, source_description="/synthetic/Detection.tif",
- )))
- fields = StreamImagePayloadMetadataProjector.item_fields(item.metadata, ("well", "site", "channel", "z_index", "timepoint"))
native_item = NapariStreamLayerItem(
data=item.data,
producer=StreamProducerIdentity.pipeline_output(
- output_kind="main", output_key="main", projection_key="main",
- step_name="SyntheticRescale", pipeline_position=0, step_scope_id="synthetic",
+ output_kind="main",
+ output_key="main",
+ projection_key="main",
+ step_name="SyntheticRescale",
+ pipeline_position=0,
+ step_scope_id="synthetic",
),
address=NapariStreamLayerAddress(
components=dict(item.require_source_component_metadata()),
- path="/synthetic/Detection.tif", stream_layer_data_type=StreamingDataType.IMAGE,
+ path="/synthetic/Detection.tif",
+ stream_layer_data_type=StreamingDataType.IMAGE,
),
image_metadata=item.metadata,
plane_component_domain=ViewerComponentValueDomainPayload.from_wire_mapping(
- fields.get(ViewerWireField.PLANE_COMPONENT_VALUES.value, {}), context="synthetic producer fields",
+ fields.get(ViewerWireField.PLANE_COMPONENT_VALUES.value, {}),
+ context="synthetic producer fields",
declared_axes=STREAM_AXES,
),
)
# The exact original strict receiver is reached, not replaced by a mock.
- NapariAggregateAxisBindingBuilder.bindings((native_item,), ViewerComponentAxisSemanticsFactory.empty())
+ NapariAggregateAxisBindingBuilder.bindings(
+ (native_item,), ViewerComponentAxisSemanticsFactory.empty()
+ )
assert contract.artifact_inputs.names() == ("Bright",)
assert request.image_count == 1
assert result.data.shape == (4, 5)
assert result.metadata.source_spatial_domain == metadata.source_spatial_domain
assert result.metadata.source_image_paths == metadata.source_image_paths
assert result.metadata.source_voxel_spacing == metadata.source_voxel_spacing
- assert SourceVoxelSpacing.common_physical_pixel_size((result.metadata.source_voxel_spacing,)) is None
- assert item.require_source_component_metadata() == metadata.source_component_metadata
+ assert (
+ SourceVoxelSpacing.common_physical_pixel_size(
+ (result.metadata.source_voxel_spacing,)
+ )
+ is None
+ )
+ assert (
+ item.require_source_component_metadata() == metadata.source_component_metadata
+ )
expected = pixels / (2.0 if method is RescaleMethod.DIVIDE_BY_VALUE else 20.0)
np.testing.assert_array_equal(item.data, expected)
@@ -147,14 +226,17 @@ def test_declared_source_plane_has_area_not_volume(retained_site):
metadata = ImagePayloadMetadata(
source_path="/synthetic/site.tif",
source_component_metadata={"site": "7"},
- source_spatial_domain=SourceSpatialDomain(origin_yx=(0, 0), source_shape_yx=(4, 5)),
+ source_spatial_domain=SourceSpatialDomain(
+ origin_yx=(0, 0), source_shape_yx=(4, 5)
+ ),
)
if retained_site:
pixels = pixels[None]
labels = labels[None]
metadata = ImagePayloadMetadata(
source_image_provenance_planes=SourceImageProvenancePlanes.from_components(
- paths=("/synthetic/site.tif",), component_metadata=({"site": "7"},),
+ paths=("/synthetic/site.tif",),
+ component_metadata=({"site": "7"},),
),
source_spatial_domain=metadata.source_spatial_domain,
plane_axis=RuntimePlaneAxis.RUNTIME_SLICE,
@@ -166,19 +248,26 @@ def test_declared_source_plane_has_area_not_volume(retained_site):
domain_scope=ObjectLabelDomainScope.PLANE if retained_site else None,
plane_projection=(
RuntimePlaneAxisValueProjection.from_source_declaration(
- metadata.plane_axis, metadata.source_provenance,
+ metadata.plane_axis,
+ metadata.source_provenance,
)
- if retained_site else None
+ if retained_site
+ else None
),
).payload()
assert objects.plane_axis is (
RuntimePlaneAxis.RUNTIME_SLICE if retained_site else None
)
assert objects.object_label_domain().scope is (
- ObjectLabelDomainScope.PLANE if retained_site else ObjectLabelDomainScope.PAYLOAD
+ ObjectLabelDomainScope.PLANE
+ if retained_site
+ else ObjectLabelDomainScope.PAYLOAD
)
_, rows = measure_object_size_shape.__wrapped__(
- payload, objects, calculate_advanced=False, calculate_zernikes=False,
+ payload,
+ objects,
+ calculate_advanced=False,
+ calculate_zernikes=False,
)
fields = tuple(field.name for field in rows.fields)
assert "Area" in fields
@@ -191,10 +280,15 @@ def test_real_volume_is_not_squeezed_or_relabelled_as_area():
pixels = np.ones((3, 4, 5), dtype=np.float32)
labels = np.zeros_like(pixels, dtype=np.int32)
labels[1:, 1:3, 1:4] = 1
- payload = ImagePayloadMetadata(source_path="/synthetic/volume.tif").payload_with(pixels, None)
+ payload = ImagePayloadMetadata(source_path="/synthetic/volume.tif").payload_with(
+ pixels, None
+ )
objects = SourceImageObjectLabelBuildRequest(image=payload, labels=labels).payload()
_, rows = measure_object_size_shape.__wrapped__(
- payload, objects, calculate_advanced=False, calculate_zernikes=False,
+ payload,
+ objects,
+ calculate_advanced=False,
+ calculate_zernikes=False,
)
fields = tuple(field.name for field in rows.fields)
assert "Volume" in fields
diff --git a/tests/unit/test_selected_plane_stream_materialization.py b/tests/unit/test_selected_plane_stream_materialization.py
index e84052612c..05969a4902 100644
--- a/tests/unit/test_selected_plane_stream_materialization.py
+++ b/tests/unit/test_selected_plane_stream_materialization.py
@@ -56,12 +56,12 @@
)
from openhcs.processing.materialization import materialize
-from test_function_artifact_materialization import (
+from tests.unit.test_function_artifact_materialization import (
_context,
_plan,
streaming_config_stub,
)
-from test_materialization_core import _viewer_stream_backend_kwargs
+from tests.unit.test_materialization_core import _viewer_stream_backend_kwargs
from openhcs.domains.microscopy.axes import Microscopy
from tests.materialization_plans import output_plan as materialization_output_plan
from openhcs.core.artifacts.plans import MAIN_FLOW
@@ -102,7 +102,9 @@ def _source_stack(axis, component=Microscopy.Channel.name):
)
def test_public_selected_checkpoint_materializes_and_streams_without_storage_axes(
axis,
- output_type, artifact_name, dtype,
+ output_type,
+ artifact_name,
+ dtype,
tmp_path,
monkeypatch,
):
@@ -151,7 +153,8 @@ def capture_stream(data_list, paths, backend, **kwargs):
["disk", "napari_stream"],
{"napari_stream": _viewer_stream_backend_kwargs()},
context=_context(filemanager),
- variable_components=(), output_plan=materialization_output_plan()
+ variable_components=(),
+ output_plan=materialization_output_plan(),
)
assert Path(result).is_file()
np.testing.assert_array_equal(
@@ -168,11 +171,10 @@ def capture_stream(data_list, paths, backend, **kwargs):
request.source.metadata.component_metadata_for_item(path, 0)["channel"] == 2
)
assert payload.metadata.source_image_names == ("FITC",)
- assert (
- payload.metadata.source_path
- == "/input/A01_s001_w2_z001_t001.tif"
+ assert payload.metadata.source_path == "/input/A01_s001_w2_z001_t001.tif"
+ assert payload.metadata.source_voxel_spacing == SourceVoxelSpacing(
+ (1.3556, 1.3556)
)
- assert payload.metadata.source_voxel_spacing == SourceVoxelSpacing((1.3556, 1.3556))
finally:
viewer.cleanup()
@@ -241,9 +243,7 @@ def with_data(self, data):
)
assert calls == ["observe", "reverse", "declaration"]
assert payload.metadata.source_image_names == ("FITC", "DAPI")
- assert payload.metadata.retained_plane_component_values() == {
- "channel": ("2", "1")
- }
+ assert payload.metadata.retained_plane_component_values() == {"channel": ("2", "1")}
np.testing.assert_array_equal(np.asarray(payload), selected.data)
@@ -265,11 +265,13 @@ class DeclaredResponse(ObservedProjection, SelectedDiagnosticPlaneImageOutput):
pass
source = _source_stack(RuntimePlaneAxis.SOURCE_BINDING)
- pixels = np.full((1, 7, 8), .75, dtype=np.float32)
+ pixels = np.full((1, 7, 8), 0.75, dtype=np.float32)
response = DeclaredResponse(pixels, (1,))
calls.clear()
payload = ImageArtifactType.contextualize_output(
- source, response, MAIN_FLOW,
+ source,
+ response,
+ MAIN_FLOW,
RuntimePlaneAxisValueProjection.preserve(
axis=RuntimePlaneAxis.SOURCE_BINDING, axis_size=2
),
@@ -294,11 +296,27 @@ def test_all_public_declared_outputs_persist_with_selected_qa_streams(
image=source, labels=labels, plane_projection=projection
).payload()
summary = NeuriteOutgrowthSummary(
- **{field.name: 0 for field in fields(NeuriteOutgrowthSummary) if field.name != "coordinate_unit"},
+ **{
+ field.name: 0
+ for field in fields(NeuriteOutgrowthSummary)
+ if field.name != "coordinate_unit"
+ },
coordinate_unit=SourceVoxelSpacingUnit.MICROMETERS,
)
cell = NeuriteOutgrowthCellResult(
- 1, 1, SourceVoxelSpacingUnit.MICROMETERS, 0.0, 0, 0.0, 0.0, 0.0, 0, 0.0, 1.0, 0.0, False
+ 1,
+ 1,
+ SourceVoxelSpacingUnit.MICROMETERS,
+ 0.0,
+ 0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0,
+ 0.0,
+ 1.0,
+ 0.0,
+ False,
)
selected = ImageArtifactType.contextualize_output(
source,
@@ -327,12 +345,17 @@ def test_all_public_declared_outputs_persist_with_selected_qa_streams(
),
*NeuriteAdmissionPlanes(
np.asarray(source)[1].astype(np.float32),
- labels[1] > 0, labels[1] > 0,
- np.asarray(source)[1].astype(np.float32), labels[1] > 0,
+ labels[1] > 0,
+ labels[1] > 0,
+ np.asarray(source)[1].astype(np.float32),
+ labels[1] > 0,
np.asarray(source)[1].astype(np.float32),
).selected_outputs(1),
*NeuriteOwnershipPlanes(
- labels[1], labels[1], labels[1], (labels[1] > 0).astype(np.uint8),
+ labels[1],
+ labels[1],
+ labels[1],
+ (labels[1] > 0).astype(np.uint8),
).selected_outputs(1),
graph,
)
@@ -369,7 +392,9 @@ def test_all_public_declared_outputs_persist_with_selected_qa_streams(
output_dir=tmp_path / "images",
)
for output, value in zip(plans, values, strict=True):
- value = (ImageArtifactType if output is None else output.artifact_type).contextualize_output(
+ value = (
+ ImageArtifactType if output is None else output.artifact_type
+ ).contextualize_output(
source,
value,
output or MAIN_FLOW,
@@ -393,13 +418,16 @@ def capture_stream(data_list, paths, backend, **kwargs):
monkeypatch.setattr(filemanager, "save_batch", capture_stream)
try:
- PersistentArtifactMaterializationTargetPlan("disk").materialize_outputs(filemanager, plan, context)
+ PersistentArtifactMaterializationTargetPlan("disk").materialize_outputs(
+ filemanager, plan, context
+ )
materializations = runtime_artifact_materializations(plan, context)
assert {item.output_plan.name for item in materializations} == set(
specs.names()
)
assert len(materializations) == 12 + len(
- NeuriteAdmissionPlanes.artifact_specs() + NeuriteOwnershipPlanes.artifact_specs()
+ NeuriteAdmissionPlanes.artifact_specs()
+ + NeuriteOwnershipPlanes.artifact_specs()
)
for item in materializations:
outputs = item.outputs(plan, context)
@@ -411,7 +439,8 @@ def capture_stream(data_list, paths, backend, **kwargs):
if paths[0].endswith(".checkpoint.tif")
]
assert len(checkpoints) == 5 + len(
- NeuriteAdmissionPlanes.artifact_specs() + NeuriteOwnershipPlanes.artifact_specs()
+ NeuriteAdmissionPlanes.artifact_specs()
+ + NeuriteOwnershipPlanes.artifact_specs()
)
for (data,), (path,), request in checkpoints:
assert (
diff --git a/tests/unit/test_singleton_rgb_label_invocation_477.py b/tests/unit/test_singleton_rgb_label_invocation_477.py
index c771043b81..8ce2e0b81b 100644
--- a/tests/unit/test_singleton_rgb_label_invocation_477.py
+++ b/tests/unit/test_singleton_rgb_label_invocation_477.py
@@ -1,4 +1,5 @@
"""Scalar RGB invocations consume only an explicitly declared singleton root."""
+
import numpy as np
import pytest
@@ -6,11 +7,23 @@
from openhcs.core.artifacts.kinds import ImageArtifactType, ObjectLabelsArtifactType
from openhcs.core.function_contract_metadata import FunctionContractAttribute
from openhcs.core.runtime_image_values import ImageMetadataPayload, ImagePayloadMetadata
-from openhcs.core.runtime_object_label_domains import ObjectLabelDomain, ObjectLabelDomainScope
-from openhcs.core.runtime_object_labels import ObjectLabelSet, ObjectLabelVariantData, object_label_dense_array
-from openhcs.core.runtime_plane_projection import RuntimePlaneAxis, RuntimePlaneProjection
+from openhcs.core.runtime_object_label_domains import (
+ ObjectLabelDomain,
+ ObjectLabelDomainScope,
+)
+from openhcs.core.runtime_object_labels import (
+ ObjectLabelSet,
+ ObjectLabelVariantData,
+ object_label_dense_array,
+)
+from openhcs.core.runtime_plane_projection import (
+ RuntimePlaneAxis,
+ RuntimePlaneProjection,
+)
from openhcs.core.runtime_slice_projection import RuntimeSliceProjectionDeclarationError
-from openhcs.interop.cellprofiler.runtime.function_contract_execution import CellProfilerFunctionContractExecutor
+from openhcs.interop.cellprofiler.runtime.function_contract_execution import (
+ CellProfilerFunctionContractExecutor,
+)
from openhcs.processing.backends.cellprofiler.outlines import OverlayOutlinesModule
from tests.unit.test_cellprofiler_module_execution import (
_FakeCellProfilerRuntime,
@@ -26,64 +39,106 @@
)
from openhcs.core.pipeline.function_contracts import (
FullStackLabels,
+ MatchImageStackLabels,
+ SliceAlignedLabels,
)
def _invocation_case(*, root_count=1, label_count=1):
- image_spec = ArtifactSpec.input('GFPandDNA', ImageArtifactType)
- labels_spec = ArtifactSpec.input('Nuclei', ObjectLabelsArtifactType)
+ image_spec = ArtifactSpec.input("GFPandDNA", ImageArtifactType)
+ labels_spec = ArtifactSpec.input("Nuclei", ObjectLabelsArtifactType)
raw = OverlayOutlinesModule.require_callable()
- contract = _compiled_callable_contract(raw, artifact_inputs=(image_spec, labels_spec))
+ contract = _compiled_callable_contract(
+ raw, artifact_inputs=(image_spec, labels_spec)
+ )
data = np.linspace(0, 1, 8 * 9 * 3, dtype=np.float32).reshape(8, 9, 3)
- image = ImageMetadataPayload(data, ImagePayloadMetadata(axes=PayloadAxes.colour_samples(2), source_image_names=('GFPandDNA',)))
+ image = ImageMetadataPayload(
+ data,
+ ImagePayloadMetadata(
+ axes=PayloadAxes.colour_samples(2), source_image_names=("GFPandDNA",)
+ ),
+ )
label_data = np.zeros((label_count, 8, 9), dtype=np.int32)
label_data[:, 2:5, 3:6] = 1
labels = ObjectLabelSet(
- name='Nuclei', variant_data=ObjectLabelVariantData(labels=label_data),
+ name="Nuclei",
+ variant_data=ObjectLabelVariantData(labels=label_data),
plane_axis=RuntimePlaneAxis.RUNTIME_SLICE,
- domain=ObjectLabelDomain(declared_object_id_domains=((1,),) * label_count, scope=ObjectLabelDomainScope.PLANE),
+ domain=ObjectLabelDomain(
+ declared_object_id_domains=((1,),) * label_count,
+ scope=ObjectLabelDomainScope.PLANE,
+ ),
)
adapter = _FakeCellProfilerRuntime(
- {'GFPandDNA': image}, objects={'Nuclei': labels}, callable_contract=contract,
- artifact_input_edges=tuple(_artifact_input_edge_for_test(spec) for spec in (image_spec, labels_spec)),
+ {"GFPandDNA": image},
+ objects={"Nuclei": labels},
+ callable_contract=contract,
+ artifact_input_edges=tuple(
+ _artifact_input_edge_for_test(spec) for spec in (image_spec, labels_spec)
+ ),
plane_projection=RuntimePlaneProjection.stack(root_count),
)
executor = _module_executor(contract)
- request = executor._image_request(image, adapter, module_type=OverlayOutlinesModule, active_input_specs=(image_spec, labels_spec))
+ request = executor._image_request(
+ image,
+ adapter,
+ module_type=OverlayOutlinesModule,
+ active_input_specs=(image_spec, labels_spec),
+ )
return raw, contract, image, labels, adapter, executor, request
def _bind(case):
raw, contract, image, labels, adapter, executor, request = case
return executor._invocation_request(
- image_request=request, adapter=adapter, current_image=image,
- kwargs={'outline_colors': ('#21FFFF',)}, module_type=OverlayOutlinesModule,
+ image_request=request,
+ adapter=adapter,
+ current_image=image,
+ kwargs={"outline_colors": ("#21FFFF",)},
+ module_type=OverlayOutlinesModule,
)
def _execute(case, invocation):
raw, contract, *_ = case
return CellProfilerFunctionContractExecutor().execute(
- contract, raw, invocation.payload, invocation.kwargs,
- execution_mode=invocation.execution_mode, plane_projection=invocation.plane_projection,
+ contract,
+ raw,
+ invocation.payload,
+ invocation.kwargs,
+ execution_mode=invocation.execution_mode,
+ plane_projection=invocation.plane_projection,
)
-def test_match_image_labels_consume_declared_singleton_root_for_scalar_rgb():
+@pytest.mark.parametrize("label_mode", (MatchImageStackLabels, SliceAlignedLabels))
+def test_plane_label_modes_consume_declared_singleton_root_for_scalar_rgb(
+ monkeypatch, label_mode
+):
+ raw = OverlayOutlinesModule.require_callable()
+ monkeypatch.setattr(
+ raw, FunctionContractAttribute.object_label_input_execution_mode, label_mode
+ )
case = _invocation_case()
raw, contract, image, labels, *_ = case
image_before = image.data.copy()
labels_before = object_label_dense_array(labels).copy()
invocation = _bind(case)
- selected = invocation.kwargs['object_labels'][0]
+ selected = invocation.kwargs["object_labels"][0]
assert invocation.execution_mode is NaturalExecution
assert invocation.plane_projection is None
assert invocation.payload.metadata.axis_position(ColourAxis) == 2
assert selected.plane_axis is None
assert object_label_dense_array(selected).shape == (8, 9)
output = _execute(case, invocation)
- scalar_labels = ObjectLabelSet(name='Nuclei', variant_data=ObjectLabelVariantData(labels=labels_before[0]), domain=ObjectLabelDomain(declared_object_ids=(1,), scope=ObjectLabelDomainScope.PAYLOAD))
- expected = raw(image, object_labels=(scalar_labels,), outline_colors=('#21FFFF',))
+ scalar_labels = ObjectLabelSet(
+ name="Nuclei",
+ variant_data=ObjectLabelVariantData(labels=labels_before[0]),
+ domain=ObjectLabelDomain(
+ declared_object_ids=(1,), scope=ObjectLabelDomainScope.PAYLOAD
+ ),
+ )
+ expected = raw(image, object_labels=(scalar_labels,), outline_colors=("#21FFFF",))
np.testing.assert_array_equal(output.data, expected.data)
np.testing.assert_array_equal(image.data, image_before)
np.testing.assert_array_equal(object_label_dense_array(labels), labels_before)
@@ -91,49 +146,76 @@ def test_match_image_labels_consume_declared_singleton_root_for_scalar_rgb():
assert image.metadata.plane_axis is None
-@pytest.mark.parametrize('root_count', (None, 2))
+@pytest.mark.parametrize("root_count", (None, 2))
def test_scalar_rgb_cannot_authorize_unknown_or_multiple_root_planes(root_count):
case = _invocation_case(root_count=root_count)
invocation = _bind(case)
- assert invocation.kwargs['object_labels'][0].plane_axis is RuntimePlaneAxis.RUNTIME_SLICE
- with pytest.raises(RuntimeSliceProjectionDeclarationError, match='Kwargs cannot create image-axis execution semantics'):
+ assert (
+ invocation.kwargs["object_labels"][0].plane_axis
+ is RuntimePlaneAxis.RUNTIME_SLICE
+ )
+ with pytest.raises(
+ RuntimeSliceProjectionDeclarationError,
+ match="Kwargs cannot create image-axis execution semantics",
+ ):
_execute(case, invocation)
-def test_explicit_full_stack_label_contract_preserves_scalar_boundary_refusal(monkeypatch):
+def test_explicit_full_stack_label_contract_preserves_scalar_boundary_refusal(
+ monkeypatch,
+):
raw = OverlayOutlinesModule.require_callable()
- monkeypatch.setattr(raw, FunctionContractAttribute.object_label_input_execution_mode, FullStackLabels)
+ monkeypatch.setattr(
+ raw,
+ FunctionContractAttribute.object_label_input_execution_mode,
+ FullStackLabels,
+ )
case = _invocation_case()
invocation = _bind(case)
- assert invocation.kwargs['object_labels'][0].plane_axis is RuntimePlaneAxis.RUNTIME_SLICE
- with pytest.raises(RuntimeSliceProjectionDeclarationError, match='Kwargs cannot create image-axis execution semantics'):
+ assert (
+ invocation.kwargs["object_labels"][0].plane_axis
+ is RuntimePlaneAxis.RUNTIME_SLICE
+ )
+ with pytest.raises(
+ RuntimeSliceProjectionDeclarationError,
+ match="Kwargs cannot create image-axis execution semantics",
+ ):
_execute(case, invocation)
def test_declared_singleton_root_refuses_mismatched_label_cardinality():
case = _invocation_case(label_count=2)
- with pytest.raises(ValueError, match='Object-label runtime plane-axis cardinality mismatch'):
+ with pytest.raises(
+ ValueError, match="Object-label runtime plane-axis cardinality mismatch"
+ ):
_bind(case)
def test_final_module_mode_sees_full_labels_before_scalar_projection(monkeypatch):
case = _invocation_case()
+
def mode(cls, default, *, image, kwargs, variable_components):
- assert kwargs['object_labels'][0].plane_axis is RuntimePlaneAxis.RUNTIME_SLICE
- assert object_label_dense_array(kwargs['object_labels'][0]).shape == (1, 8, 9)
+ assert kwargs["object_labels"][0].plane_axis is RuntimePlaneAxis.RUNTIME_SLICE
+ assert object_label_dense_array(kwargs["object_labels"][0]).shape == (1, 8, 9)
return FullStackExecution
- monkeypatch.setattr(OverlayOutlinesModule, 'execution_mode', classmethod(mode))
+
+ monkeypatch.setattr(OverlayOutlinesModule, "execution_mode", classmethod(mode))
invocation = _bind(case)
assert invocation.execution_mode is FullStackExecution
- assert invocation.kwargs['object_labels'][0].plane_axis is RuntimePlaneAxis.RUNTIME_SLICE
+ assert (
+ invocation.kwargs["object_labels"][0].plane_axis
+ is RuntimePlaneAxis.RUNTIME_SLICE
+ )
def test_module_mode_failure_precedes_projection_guard(monkeypatch):
case = _invocation_case(label_count=2)
- error = RuntimeError('module image-domain selection failed')
+ error = RuntimeError("module image-domain selection failed")
+
def mode(cls, default, *, image, kwargs, variable_components):
raise error
- monkeypatch.setattr(OverlayOutlinesModule, 'execution_mode', classmethod(mode))
+
+ monkeypatch.setattr(OverlayOutlinesModule, "execution_mode", classmethod(mode))
with pytest.raises(RuntimeError) as caught:
_bind(case)
assert caught.value is error
diff --git a/tests/unit/test_smoke_installed_gui.py b/tests/unit/test_smoke_installed_gui.py
index 5a378c4850..7461331098 100644
--- a/tests/unit/test_smoke_installed_gui.py
+++ b/tests/unit/test_smoke_installed_gui.py
@@ -164,12 +164,41 @@ def test_gui_smoke_allows_wheel_venv_inside_checkout(tmp_path: Path) -> None:
)
+def _prepared_execution_services() -> Future[None]:
+ future: Future[None] = Future()
+ future.set_result(None)
+ return future
+
+
+def test_gui_smoke_waits_for_gui_endpoint_before_reading_catalog() -> None:
+ class UnreachableFunctionCatalogService:
+ @staticmethod
+ def prepare() -> Future[FunctionCatalogPage]:
+ raise AssertionError(
+ "The catalog attaches only after the GUI endpoint starts."
+ )
+
+ with pytest.raises(
+ OperationTimeoutError,
+ match="Timed out waiting for test installed function catalog",
+ ):
+ verify_installed_function_catalog(
+ UnreachableFunctionCatalogService(),
+ execution_services_prepared=Future(),
+ deadline=OperationDeadline.after_milliseconds(
+ 1,
+ operation="test installed function catalog",
+ ),
+ )
+
+
def test_gui_smoke_requires_prepared_catalog_and_resolvable_reference() -> None:
entry = _catalog_entry()
service = _FunctionCatalogService(_catalog_page(entry))
result = verify_installed_function_catalog(
service,
+ execution_services_prepared=_prepared_execution_services(),
deadline=OperationDeadline.after_milliseconds(
2_000,
operation="test installed function catalog",
@@ -195,6 +224,7 @@ def prepare() -> Future[FunctionCatalogPage]:
):
verify_installed_function_catalog(
PendingFunctionCatalogService(),
+ execution_services_prepared=_prepared_execution_services(),
deadline=OperationDeadline.after_milliseconds(
1,
operation="test installed function catalog",
@@ -215,6 +245,7 @@ def prepare() -> Future[FunctionCatalogPage]:
with pytest.raises(TimeoutError, match="catalog owner timed out") as caught:
verify_installed_function_catalog(
FailedFunctionCatalogService(),
+ execution_services_prepared=_prepared_execution_services(),
deadline=OperationDeadline.after_milliseconds(
2_000,
operation="test installed function catalog",
diff --git a/tests/unit/test_snapshot_deadline.py b/tests/unit/test_snapshot_deadline.py
index b0dbb1a6da..3fc216571f 100644
--- a/tests/unit/test_snapshot_deadline.py
+++ b/tests/unit/test_snapshot_deadline.py
@@ -8,7 +8,7 @@
import pytest
-from test_napari_render_snapshot import queued_viewer
+from tests.unit.test_napari_render_snapshot import queued_viewer
from openhcs.agent.dto.execution import ExecutionConnectionSpec
from openhcs.agent.dto.viewer import ViewerWindowSnapshotRequest
from openhcs.agent.services import viewer_window_service as service_module
@@ -56,7 +56,9 @@ def close(self, *args, **kwargs):
pass
def send(self, data, flags):
- accepted = NapariAcceptedControlRequest.from_wire_mapping(pickle.loads(data))
+ accepted = NapariAcceptedControlRequest.from_wire_mapping(
+ pickle.loads(data)
+ )
self.message = accepted.message
server.accepted_control_requests.put(replace(accepted, response=reply))
server.process_messages()
@@ -110,28 +112,38 @@ def poll(self, timeout):
def test_snapshot_wire_offer_is_admitted_once_and_capture_uses_local_identity(tmp_path):
from zmqruntime.messages import MessageFields
- from openhcs.runtime.viewer_protocol import ViewerControlMessageRequest, ViewerRuntimeEndpoint
+ from openhcs.runtime.viewer_protocol import (
+ ViewerControlMessageRequest,
+ ViewerRuntimeEndpoint,
+ )
from openhcs.runtime.zmq_config import OPENHCS_ZMQ_CONFIG
original = ViewerWindowSnapshotRequest.from_fields(
- connection=ExecutionConnectionSpec(port=5584), output_dir_path=str(tmp_path),
+ connection=ExecutionConnectionSpec(port=5584),
+ output_dir_path=str(tmp_path),
).start_operation()
envelope = ViewerControlMessageRequest(
endpoint=ViewerRuntimeEndpoint(
transport=original.connection.transport_endpoint(OPENHCS_ZMQ_CONFIG),
config=OPENHCS_ZMQ_CONFIG,
),
- message_type="screenshot", payload=original,
+ message_type="screenshot",
+ payload=original,
operation_deadline=original.operation_deadline,
)
wire = pickle.loads(pickle.dumps(envelope.to_wire_mapping()))
assert wire["payload"].operation_deadline is None
- assert 0 < wire[MessageFields.OBSERVATION_BUDGET_SECONDS] <= original.timeout_ms / 1000
+ assert (
+ 0 < wire[MessageFields.OBSERVATION_BUDGET_SECONDS] <= original.timeout_ms / 1000
+ )
admitted_at = time.monotonic()
accepted = NapariAcceptedControlRequest.from_wire_mapping(wire)
assert accepted.observation_deadline is not original.operation_deadline
assert accepted.observation_deadline.expires_at >= admitted_at
- assert accepted.message["payload"].snapshot_operation_deadline() is accepted.observation_deadline
+ assert (
+ accepted.message["payload"].snapshot_operation_deadline()
+ is accepted.observation_deadline
+ )
assert MessageFields.OBSERVATION_BUDGET_SECONDS not in accepted.message
# Qt/action consumers receive an admitted object, not a reusable duration.
with pytest.raises(KeyError):
diff --git a/tests/unit/test_validate_documentation_audit.py b/tests/unit/test_validate_documentation_audit.py
index 8b3dc9f474..66cf6054c0 100644
--- a/tests/unit/test_validate_documentation_audit.py
+++ b/tests/unit/test_validate_documentation_audit.py
@@ -26,6 +26,15 @@ def test_repository_paths_retain_the_full_jsonl_suffix():
)
+def test_repository_paths_match_whole_paths_only():
+ target = "packaging/codex/openhcs/skills/use-openhcs/references/viewer-qa.md"
+ matches = [
+ match.group("target")
+ for match in REPOSITORY_SOURCE_PATH.finditer(f'"source_path": "{target}"')
+ ]
+ assert matches == [target]
+
+
def _entry(
path: str,
source: bytes = b"Index\n=====\n",
diff --git a/tests/unit/test_viewer_spatial_sample.py b/tests/unit/test_viewer_spatial_sample.py
index 12bce6de08..e589928945 100644
--- a/tests/unit/test_viewer_spatial_sample.py
+++ b/tests/unit/test_viewer_spatial_sample.py
@@ -5,7 +5,7 @@
import numpy as np
import pytest
-from test_viewer_feature_measurement import native_route
+from tests.unit.test_viewer_feature_measurement import native_route
from openhcs.agent.dto.execution import ExecutionConnectionSpec
from openhcs.agent.dto.viewer import ViewerWindowImageSampleRequest
@@ -97,7 +97,9 @@ def test_raw_array_slices_keep_trailing_dimension_contract_in_same_native_projec
rgb = np.stack((gray, gray + 1000, gray + 2000), axis=-1)
items = server.component_groups.items_for("source")
items[0] = replace(
- items[0], data=rgb, image_metadata=ImagePayloadMetadata(axes=PayloadAxes.colour_samples(2))
+ items[0],
+ data=rgb,
+ image_metadata=ImagePayloadMetadata(axes=PayloadAxes.colour_samples(2)),
)
projection = NapariViewerPayloadProjection(
server=server,
|