Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
b0e6ead
B6: receipt - main's CI green
trissim Oct 11, 2026
6bccc4d
B6: CI imports - test helpers through the tests package; website inst…
trissim Oct 11, 2026
62752cf
B6: process reaping works on Windows; GUI smoke imports session opera…
trissim Oct 11, 2026
c9de5fa
B6: docs path check matches whole repository paths
trissim Oct 11, 2026
5544c44
B6: website job installs the dev extra its pytest configuration needs
trissim Oct 11, 2026
6669b34
B6: pin PolyStore and pyqt-reactive docs without orphaned validation …
trissim Oct 11, 2026
f37e60a
B6: require pycodify 0.1.4, the release of the pinned source
trissim Oct 11, 2026
ca7f086
B6: test configuration reads CPU-only mode from its owner
trissim Oct 11, 2026
e59901e
B6: re-review 141 stale documentation pages against the refactored code
trissim Oct 11, 2026
f3eb57a
B6: pin zmqruntime docs without the removed viewer-mode members; mani…
trissim Oct 11, 2026
47724cc
B6: granularity native accepts uint32 queues exported as 'L' on Windows
trissim Oct 11, 2026
d5172f3
B6: persist custom-function sources as the exact bytes their revision…
trissim Oct 11, 2026
f59da9b
B6: GUI tests drive the session-backed plate manager
trissim Oct 11, 2026
03ca14c
B6: GUI smoke reads the catalog only after the GUI's endpoint is up
trissim Oct 11, 2026
c3ab994
B6: requestless UI-bridge capabilities pass the connection by keyword
trissim Oct 11, 2026
87e4df3
B6: MCP smoke names synthetic partitions with partition_values
trissim Oct 11, 2026
333d3e0
B6: pin zmqruntime 0.6.1 (startup owner read through the held lock)
trissim Oct 11, 2026
c1013dd
B6: catalog excludes modules whose distribution the package markers e…
trissim Oct 11, 2026
c00a652
B6: BaSiC observation check reads bare arrays through ImagePayload.of
trissim Oct 11, 2026
4b1dec1
B6: STEP_INPUT selectors match the stored source paths of current planes
trissim Oct 11, 2026
2eb48fb
B6: Official30 viewer variants select viewer families, not ViewerType…
trissim Oct 11, 2026
dbae3e0
B6: GUI session journey runs in the GUI job; orchestrator exposes dat…
trissim Oct 11, 2026
1bca1b1
B6: custom sources persist their exact bytes; transport test emits ty…
trissim Oct 11, 2026
42f51b7
B6: measured CLI child imports the installation under test
trissim Oct 11, 2026
5e51940
B6: mahotas is a core runtime requirement
trissim Oct 11, 2026
f8d6cdc
B6: U1 surface doc names where the GUI journey lives
trissim Oct 11, 2026
54e87d5
B6: pin PolyStore 0.6.1 (OMERO backend answers is_dir)
trissim Oct 11, 2026
df75920
B6: MCP transport releases the QCoreApplication it created
trissim Oct 11, 2026
89269b9
B6 cpproc: passthrough outputs keep their producer's filename qualifier
trissim Oct 11, 2026
34556a1
B6 cpproc: synthetic-plate integration tests follow current mechanisms
trissim Oct 11, 2026
0f0ad9f
B6 cpproc: benchmark adapter keeps imported config inheriting the glo…
trissim Oct 11, 2026
0d83e1e
B6 cpproc: benchmark watchdog measures silence from run submission
trissim Oct 11, 2026
35abb00
B6 cpproc: BaSiC accepts plain NumPy observations again
trissim Oct 11, 2026
7bb55dc
B6 cpproc: drop the dataclasses.field import the watchdog change left…
trissim Oct 11, 2026
fac0519
B6: pin ObjectState 1.3.2 (own annotations on Python 3.14)
trissim Oct 11, 2026
cf0b8eb
B6: black and the 3.11 syntax check over every Python file changed si…
trissim Oct 11, 2026
74e74e6
B6: re-pin documentation authorities after B6's own code changes
trissim Oct 11, 2026
6814764
Revert "B6: black and the 3.11 syntax check over every Python file ch…
trissim Oct 11, 2026
e8229ad
B6: colocalization test parses on Python 3.11 (no outer-quote reuse i…
trissim Oct 11, 2026
f2ae23c
B6: black over the Python files this PR changes
trissim Oct 11, 2026
239953c
B6: keep the structural ratchet: move cohesive method groups into mix…
trissim Oct 11, 2026
22a6856
B6: re-pin documentation authorities after the formatting and ratchet…
trissim Oct 11, 2026
35833f3
B6: place packaged native recipes by case name within the Windows pat…
trissim Oct 11, 2026
ded2427
B6: macOS kernel cache preparation uses its online CPUs
trissim Oct 11, 2026
e1bd343
B6: image-source guide test reads the axis-family spellings the guide…
trissim Oct 11, 2026
956326e
B6: metadata-document targets admit only backends with a local output…
trissim Oct 11, 2026
24bfafe
B6: debug-boundary guard reads the debug methods from FunctionCoreDeb…
trissim Oct 11, 2026
7c1c188
B6: re-pin documentation authorities after the OMERO metadata and tes…
trissim Oct 11, 2026
359e31d
B6: read the validated case name like the loader does (no new string …
trissim Oct 11, 2026
433b26f
B6: Windows read-only datasets and Ctrl+Break interrupt
trissim Oct 11, 2026
ab03ba7
B6: Session's debug-session methods move into SessionDebugMixin (ratc…
trissim Oct 11, 2026
7066af9
B6: keep runtime-plane axes in a stream's display scope; official30 v…
trissim Oct 11, 2026
7f5710a
B6: black test_debug_runtime.py
trissim Oct 11, 2026
a1e3bff
B6: re-pin documentation authorities after the Windows and viewer fixes
trissim Oct 11, 2026
e1a1719
B6: Windows read-only fixture denies adding entries to the root only
trissim Oct 11, 2026
e82ae33
B6: wheel-integration prepares the declared Fiji bundle its Bio-Forma…
trissim Oct 11, 2026
5de70ef
B6: slice-aligned labels match a scalar image to its singleton root; …
trissim Oct 11, 2026
fbe17e0
B6: check out tracked files as committed bytes on every platform
trissim Oct 11, 2026
f322e39
B6: the test session creates its QApplication first; MCP transport ke…
trissim Oct 11, 2026
9630f35
B6: re-pin documentation authorities after the demo, workflow and tes…
trissim Oct 11, 2026
dd8709f
B6: receipt - causes, fixes, guards and lockstep library PRs
trissim Oct 11, 2026
a5ce88b
B6: only the unit session pre-creates its QApplication; GUI journey w…
trissim Oct 11, 2026
88e2243
B6: installed demo reports the viewer's wire name through ViewerType.…
trissim Oct 11, 2026
c74b2bd
B6: dataset write probe makes one creation attempt
trissim Oct 11, 2026
2aaceea
B6: re-pin documentation authorities after the probe and test-session…
trissim Oct 11, 2026
b14856f
B6: observation-export test reads the partition count by its declared…
trissim Oct 11, 2026
39e963a
Merge remote-tracking branch 'origin/main' into refactor/B6-ci-green
trissim Oct 11, 2026
2ed28bb
B6: black after merging main
trissim Oct 11, 2026
b2fb1bc
B6: re-review 126 pages again after main's U3, K4, K5, K6, L4 and L6 …
trissim Oct 11, 2026
f419cb3
B6: receipt - library versions after merging main
trissim Oct 11, 2026
d43533f
B6: unit session asks for pytest-qt's QApplication only where the plu…
trissim Oct 11, 2026
7c20d51
Merge remote-tracking branch 'origin/main' into refactor/B6-ci-green
trissim Oct 11, 2026
0250ad8
B6: keep main's python-introspect 0.2.8 pin after the merge
trissim Oct 11, 2026
297a423
B6: re-review 127 pages after main's K2, K3, N1 and E1 merges
trissim Oct 11, 2026
ae618be
B6: black after merging main (K2, K3, N1, E1)
trissim Oct 11, 2026
3e089a3
B6: re-pin authorities reformatted after the merge (AST-equal)
trissim Oct 11, 2026
f8062d2
B6: unit-tests job runs file-isolated xdist workers to fit its 45-min…
trissim Oct 11, 2026
2e0deaf
B6: re-pin pages citing the integration workflow
trissim Oct 11, 2026
7379deb
B6 WIP: export scope crosses the request boundary as its enum view an…
trissim Oct 12, 2026
542f7ff
B6: unit-tests job runs as three serial groups within the 45-minute b…
trissim Oct 12, 2026
5721db4
B6: re-pin authorities after the E1 boundary fixes
trissim Oct 12, 2026
f0498a6
B6: GUI smoke compares the action status to ActionAccepted nominally
trissim Oct 12, 2026
8b31dc0
B6: re-pin pages citing the GUI smoke
trissim Oct 12, 2026
f84ded8
Merge remote-tracking branch 'origin/main' into refactor/B6-ci-green
trissim Oct 12, 2026
ce529e4
B6: website concurrency group is per ref
trissim Oct 12, 2026
abf42ae
B6: re-pin pages citing the website workflow
trissim Oct 12, 2026
8525047
B6: receipt - handoff
trissim Oct 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -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
36 changes: 34 additions & 2 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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: |
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
75 changes: 17 additions & 58 deletions .github/workflows/website-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,52 @@ 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:

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
Expand All @@ -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: |
Expand Down
35 changes: 20 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ graph LR
<td width="50%" valign="top">

### 🛡️ 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.

</td>
<td width="50%" valign="top">
Expand Down Expand Up @@ -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 |

---

Expand All @@ -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`.

</td>
<td width="60%" valign="top">
Expand Down Expand Up @@ -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)
```

Expand Down Expand Up @@ -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.

<details>
<summary><b>📦 All installation options</b></summary>
Expand Down Expand Up @@ -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
Expand Down
13 changes: 5 additions & 8 deletions benchmark/adapters/openhcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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."""
Expand All @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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)
Expand Down Expand Up @@ -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 = {
Expand Down
Loading