diff --git a/docs/validation/paired_field_csv_314_20260930/PREFLIGHT.json b/docs/validation/paired_field_csv_314_20260930/PREFLIGHT.json new file mode 100644 index 0000000000..aff2179909 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/PREFLIGHT.json @@ -0,0 +1,11 @@ +{ + "command": "/home/ts/bin/agent-resource-check --assert-headroom", + "exit_code": 2, + "actual_result": {"free_gib": {"/": 8.2, "/dev/shm": 15.5, "/home": 12.5, "/tmp": 11.3}, "level": "warning", "ram_available_gib": 15.6, "reasons": ["/home free 12.5 GiB", "swap used 8.7 GiB"], "swap_used_gib": 8.7}, + "admission": "20GiB warning only; bounded source worker/scratch", + "worker_memory_limit_bytes": 536870912, + "cpu_quota_percent": 100, + "numeric_threads": 1, + "owned_scratch_limit_bytes": 268435456, + "owned_scratch": "/home/ts/.cache/agent-scratch/issue314-russell" +} diff --git a/docs/validation/paired_field_csv_314_20260930/SHA256SUMS b/docs/validation/paired_field_csv_314_20260930/SHA256SUMS new file mode 100644 index 0000000000..fb062f6484 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/SHA256SUMS @@ -0,0 +1,36 @@ +09bc36d6a804f1601315bb842a3c67852ed5d08f5a71b6fc632dd6f7c7a1e12a docs/validation/paired_field_csv_314_20260930/PREFLIGHT.json +e935c5bcb0d0bf4da40c68f855bff5afa484c6b1a6e667405004cb06164f0cd7 docs/validation/paired_field_csv_314_20260930/checkpoint.rst +f7a149a70dd9f82cadd4b1b43eae69a6c70354d04c870113bae2e80d6c697fcf docs/validation/paired_field_csv_314_20260930/core-census.json.gz +abcc421cce2ad109a4be6f9bae9c7ffdb80c48725a416bf4db5f60266fe610d5 docs/validation/paired_field_csv_314_20260930/core-census.log +22a3cbbcc9ac4d15b10c6ae38d5b930fb2a37a687f832f978d29543d803f399b docs/validation/paired_field_csv_314_20260930/core-overlay.json.gz +f842402ed5c9f2eb35f5a9abeb97ee7342e1e0269233d2149aab2a3b03ba4318 docs/validation/paired_field_csv_314_20260930/core-overlay.log +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 docs/validation/paired_field_csv_314_20260930/effective-imports-error.log +6260f67c73bdbb1e138b9733e3100f9c3b82daa032beaa1ab821891a830b8d68 docs/validation/paired_field_csv_314_20260930/effective-imports.json +2d22257b758889c28b6cc68e230075176e81e94b8b3eb5b93f0c8c0458040102 docs/validation/paired_field_csv_314_20260930/final-policy-exporter.log +4e6b60bb28c884fddf203e176e39b6b2a8fec79b0db065e2d3fb87bfc11967a1 docs/validation/paired_field_csv_314_20260930/merged-main-census.json +2a91055d7bc8f70cd70a234c0970babbf0bedf15cfce723dfecd4e028032cbb1 docs/validation/paired_field_csv_314_20260930/merged-main-census.log +bf1aee9710d476cd67ea9ef6e6a03805d50498d1dd38eb1f3f7e325e7fd2a1bd docs/validation/paired_field_csv_314_20260930/merged-main-owner.log +ef34e28b0d9ebe85c9a364f9d64bcba276f1af3de33c782a5452c74d8dd665b5 docs/validation/paired_field_csv_314_20260930/merged-main-related.log +45bb910ec84bccf47c98eab8d48668058c32f888bf3337eadf6af54ebdcc69a0 docs/validation/paired_field_csv_314_20260930/nra-original-resources.log +76efdf6231fe3d520aa5aad4ed07aa619696ed0a1bbc0d9ac58207439fbc1d1c docs/validation/paired_field_csv_314_20260930/nra-original.json +ca8e2518377ceb506f4b6616de041d8e5901d3abb7d53499aacacd284927f365 docs/validation/paired_field_csv_314_20260930/nra-owner-loop-resources.log +85fa34cc7c9365e1433cf13cfde3728b93e826212a862d5a896ee50f24215587 docs/validation/paired_field_csv_314_20260930/nra-owner-loop.json +0ef110ed10150278f09ba7ea4abf837c87f163d834794a99f26731ec3b722a69 docs/validation/paired_field_csv_314_20260930/owned-disposable.tar.gz +4068fc6aebd829bde62c38c9fd8a9c980ff2480214f9e68d638684cb643d6bef docs/validation/paired_field_csv_314_20260930/owner-census-delta.json +d221f41dafd990a5434428d86703b59b1eec66fb99c6537fb40a0c52bdd1205f docs/validation/paired_field_csv_314_20260930/owner-census-delta.log +8db82b08046f81c30100fe92751276352cef7f8a444a3297ec4df82ce2d95c0b docs/validation/paired_field_csv_314_20260930/owner-fixed-corrected.log +d50a0ddd323c80c4992f46e4a96c8a3a1515c6787ee2ad5f86191357ac964989 docs/validation/paired_field_csv_314_20260930/owner-fixed.log +d8375d089d79fb532af311d7d3b8f1509d0f21e9e045b7cd14a9ed04a132f509 docs/validation/paired_field_csv_314_20260930/ownership-receipt.rst +17988389f4355b8274b59fd1141bb8aa5757b6cb6adf94fe879c0b199ca023e3 docs/validation/paired_field_csv_314_20260930/policy-import.log +6b76484efe7c0bcd2634c61337c0f73018d457338bfb077c05c836ec42e3b481 docs/validation/paired_field_csv_314_20260930/pr-body.md +77b93b241966cb96c7e7e8e2adc7964d488abbaa14fb2ab94e74ddb1598c3673 docs/validation/paired_field_csv_314_20260930/related-owner-contracts.log +63ad8bd8fac5a6a06141b607f4d6bd51145a8952ee63bbc9c27c844c07724dcb docs/validation/paired_field_csv_314_20260930/reproducer-original-built.log +9aa5f6c2c1dd4b980dc653be3b1fca951a5eb42ba05e2a99709140190f08ffd0 docs/validation/paired_field_csv_314_20260930/reproducer-original.log +93abbe5307fdb809eae183b7e3fd79f276fb1044b1e889cf95ae3ec754dd36aa docs/validation/paired_field_csv_314_20260930/ruff-focused.log +9a5daa28530cfdb8a9a5dfaf7e4469862c7755067be421ee89f83f824ac2a6d7 docs/validation/paired_field_csv_314_20260930/source-anchors-base-empty-snapshot.log +05d26c97cc6db8b17026c5dfb984b13f5cc667479d0962613118c6d764edc22e docs/validation/paired_field_csv_314_20260930/source-anchors-base-verified.log +89e8f8287e324c431d734ec1f3dbb247459ed329ec939cc9478143cf239ccba3 docs/validation/paired_field_csv_314_20260930/source-anchors-base.log +5e739265347dec1ea5319579c3575f0ca9d4ba12a0a6c4209cbab692196c0b50 docs/validation/paired_field_csv_314_20260930/source-build.log +1b5332ef5d7e47ae6518191ef1e2564dd03ba02c716ded316439f5103b2b4e25 openhcs/core/source_matching.py +8eb0468a59f7545bbdbc86e12e0314899942dae444d57cb10bbf1ece2cd10d5b tests/unit/test_cellprofiler_spreadsheet_export.py +f106cb03132c41485c6c3dc065481abe80eac0682d25fb3a27cf0e1be093832c tests/unit/test_source_image_set_identity_policy.py diff --git a/docs/validation/paired_field_csv_314_20260930/check-installed-parent.py b/docs/validation/paired_field_csv_314_20260930/check-installed-parent.py new file mode 100644 index 0000000000..1fcc3c9c73 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/check-installed-parent.py @@ -0,0 +1,93 @@ +"""Read-only verification of the installed two-well synthetic export control.""" + +import csv +import json +from pathlib import Path + +import numpy as np +import tifffile + +from openhcs.core.source_workspace_projection import ( + VirtualWorkspacePathLookup, + VirtualWorkspaceSourceProjection, +) +from openhcs.runtime.zmq_execution_observation import ZMQRuntimeExecutionObservationExport + + +def main(root: Path) -> None: + receipts = root / "mcp-image-copy-reconnected" + status = json.loads((receipts / "receipt-017.json").read_text()) + execution = status["payload"]["results"][0]["payloads"][0] + assert status["returncode"] == 0 + assert execution["status"] == "complete" + assert execution["response"]["execution"]["error"] is None + inventory_receipt = json.loads((receipts / "receipt-020.json").read_text()) + inventory = inventory_receipt["payload"]["results"][0]["payloads"][0] + assert inventory_receipt["returncode"] == 0 + assert inventory["total_count"] == inventory["returned_count"] == 16 + assert inventory["truncated_count"] == 0 + cells_record = next(record for record in inventory["records"] if record["key"] == "Cells.csv") + assert cells_record["preview"]["truncated"] is False + cells = cells_record["preview"]["csv_rows"] + assert len(cells) == 4 + assert {(row["image_number"], row["object_label"]) for row in cells} == { + ("1", "1"), ("1", "2"), ("2", "1"), ("2", "2"), + } + for row in cells: + assert row["Parent_Nuclei"] == row["object_label"] + assert float(row["AreaShape_Area"]) == 52.0 + assert float(row["Location_Center_X"]) == float(row["AreaShape_Center_X"]) + assert float(row["Location_Center_Y"]) == float(row["AreaShape_Center_Y"]) + assert all(value != "" for value in row.values()) + + output = root / "outputs-input-source-fixed" / "synthetic_openhcs" + with (output / "results" / "Cells.csv").open(newline="") as handle: + assert list(csv.DictReader(handle)) == cells + projection = VirtualWorkspaceSourceProjection.from_openhcs_metadata( + output, json.loads((output / "openhcs_metadata.json").read_text()), + ) + assert len({(ref.backend, ref.backend_address) + for ref in projection.source_refs_by_virtual_path.values()}) == 4 + for well in ("A01", "B01"): + for channel, name, step in (("1", "Nuclei", 0), ("2", "Cells", 1)): + relative = f"results/{well}_w{channel}_{name}_step{step}.labels.tif" + lookup = VirtualWorkspacePathLookup(relative, str(output / relative)) + source_projection = projection.require_source_projection_for(lookup) + assert source_projection.ref.backend == "disk" + assert source_projection.ref.backend_address == relative + assert source_projection.image_metadata.source_spatial_domain.source_shape_yx == (32, 32) + nuclei = tifffile.imread(output / "results" / f"{well}_w1_Nuclei_step0.labels.tif") + expected_nuclei = np.zeros((32, 32), dtype=np.int32) + expected_nuclei[5:9, 5:9] = 1 + expected_nuclei[22:26, 22:26] = 2 + np.testing.assert_array_equal(nuclei.squeeze(), expected_nuclei) + cell_labels = tifffile.imread(output / "results" / f"{well}_w2_Cells_step1.labels.tif") + assert set(np.unique(cell_labels)) == {0, 1, 2} + assert np.count_nonzero(cell_labels == 1) == np.count_nonzero(cell_labels == 2) == 52 + dna = tifffile.imread(root / "synthetic" / "TimePoint_1" / f"{well}_s001_w1_z001_t001.tif") + expected_dna = np.zeros((32, 32), dtype=np.uint16) + expected_dna[5:9, 5:9] = 12000 + expected_dna[22:26, 22:26] = 18000 + np.testing.assert_array_equal(dna, expected_dna) + actin = tifffile.imread(root / "synthetic" / "TimePoint_1" / f"{well}_s001_w2_z001_t001.tif") + np.testing.assert_array_equal(actin, np.full((32, 32), 24000, dtype=np.uint16)) + + observation = ZMQRuntimeExecutionObservationExport.read( + root / "runtime-observation-input-source-fixed.json", + ) + assert observation.execution_id == "bb932c48-de7a-4867-bbb2-5a090a5aa928" + assert dict(observation.execution_success_by_axis) == {"A01": True, "B01": True} + close_receipt = json.loads((receipts / "receipt-021.json").read_text()) + outcome = close_receipt["payload"]["results"][0]["payloads"][0]["outcome"] + assert outcome["request_attempted"] and outcome["acknowledged"] and outcome["process_exited"] + print("PASS: installed COMPLETE; four complete paired cell rows; four label projections; " + "known source pixels unchanged; native export reopens; exact worker closed. " + "Physical calibration and biological accuracy are not assessed by this control.") + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("root", type=Path) + main(parser.parse_args().root) diff --git a/docs/validation/paired_field_csv_314_20260930/checkpoint.rst b/docs/validation/paired_field_csv_314_20260930/checkpoint.rst new file mode 100644 index 0000000000..fd32fe1384 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/checkpoint.rst @@ -0,0 +1,124 @@ +Tested source checkpoint +======================== + +Base702b2c2c9563da2636db251f8b734cff215596b8. Source repair only; +parent owns final integration and actual installed/MCP acceptance. + +Production change is confined to SourceImageSetIdentityPolicy.from_source_bindings. +Shared singleton coordinates across paired primary declarations stay field +identity. Explicit stacks/grouping keep precedence. Existing component_values() +owns biological assignment versus physical selector precedence. No exporter, +shape/secondary module, provenance schema, scope, registry or global package edit. + +Original built-source synthetic exporter failed:8 partial rows instead of4 +complete rows for2 fields x2 cells. Corrected source passes100 tests in15.52s +(wall17.07s, maximumRSS455620KiB) under512MiB cgroup/oneCPU/threads1. +Includes the real CSV exporter/counts, source binding/provenance contracts, +measurement source-group alignment, two-versus-three channels, site/well/z/time +negative controls, explicit stacked/grouped constant axes, physical-selector +versus biological-coordinate precedence and path fallback without metadata. +No scientific image read, segmentation, JVM, MCP/server/viewer startup, +installed-entrypoint or external CellProfiler oracle was performed. + +Original failures retained: fresh checkout missing compiled tabular extension +(reproducer-original.log); real baseline split (reproducer-original-built.log); +initial production patch had a misplaced generator comma and failed collection +(owner-fixed.log). Corrected that syntax, not assertions or behavior, before the +passing owner-fixed-corrected.log. Existing extension declarations were built +locally without installation or package/source dependency changes (source-build.log). +The failed complete-context NRA attempt (exit124, maximumRSS524536KiB) returned +deadline_incomplete JSON: complete=false, deadline_exceeded=true, stage +parse_python_module, internal budget20s and elapsed22.054s. This corrects the +earlier empty-JSON description; the raw original was preserved. It remains in +nra-original.json/nra-original-resources.log; it is not a complete +R1/semantic proof. Do not repeat with greater global limits. Core syntax census +and source-witness review are evidence at their actual scope, not global health. + +Ruff's initial all-rule check exposes13 inherited findings (import order and +quoted annotations). Keep unrelated source debt out of this repair; compare +changed-owner metrics against exact base rather than claiming a clean repository. +The new test module itself has no Ruff findings. Source/test git diff --check +passes. Published raw diagnostic logs retain original trailing whitespace; +an unrestricted evidence-inclusive diff --check flags that whitespace. + +Coordination: all7 then-open PR file lists checked. Shared source_bindings.py +(#206), runtime_image_values.py/runtime_plane_projection.py (#217) remain +read-only. Direct message addressed to Avicenna on their PR: +https://github.com/OpenHCSDev/openhcs/pull/217#issuecomment-5920836946. +No response or coordinated shared edit is claimed; no waiting or new agent call. + +H003e immutable REJECT remains untouched. Issue314 is not closed by source tests. +Source/wheel/MCP continuity and external semantics acceptance remain separate +parent gates. + +Additional source checkpoint before main integration +--------------------------------------------------- + +131 distinct source tests pass across the focused owner/exporter and related +CellProfiler shape, secondary-distance and module-scope shards. The final +policy/exporter shard is41 passed,4.91s, maximumRSS378400KiB and includes the +normal from_pipeline_config path. The related shard is30 passed/8 failed, +maximumRSS518188KiB (below512MiB). Its eight matched-anchor fixture failures +all reach MeasurementsArtifactType.require_output_subject before this policy: +legacy synthetic fixtures lack MeasurementSubjectRelation. All eight also fail +on unmodified main702b2c2c9 (source-anchors-base-verified.log:8 failed/3 passed). +Parent integration owns disposition of that inherited fixture mismatch; acceptance +requires properly typed subjects, not weakening the guard. No fixture/assertion +was relaxed. Earlier baseline snapshot assembly attempts (empty snapshot and +missing pytest_integration_options) are retained separately as harness failures. + +The lightweight one-file NRA loop completed in2.97s/77176KiB, zero findings; +its actual scan_status is exact_compact_global,79 analyzed detectors,0 omitted, +complete=true. This is complete ONLY for the selected source_matching.py input, +not a complete OpenHCS context scan or formal equivalence proof. Full-context +failure remains retained. Changed-core census at source commit6f636c2afe6d98cc +adds16 code lines and zero in every other measured syntax category. + +Effective OpenHCS imports resolve this worktree (source version0.8.7), not the +installed distribution metadata0.8.6. Existing editable PolyStore resolves +/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore, +clean at1209068f5fc9e4f7025a24ada8a73ef30dd1ddd7. This dependency is unmodified; +these checks do not prove a frozen gitlink dependency closure or installed parity. +Exact other effective imports/versions are retained in effective-imports.json. + +Merged current main and final handoff +------------------------------------ + +Main2b7969f700c33eb7f73f891d975afd1efc0628bb was integrated normally with merge +commit e3d991e9cf02c496daa7c9df32c73bfde4a7ad00. It changes viewer owners, not +the repaired policy. Core tree025c72d3ce07962cfbe57c181819bdc6d081e5f3 is byte +identical between original702b2c2c9 and new main; the changed branch core tree +is a10ecc8bcf0b4e68576eaa52b6479bd4f661999a. The original fixture and strict +artifact owner are also unchanged between those main revisions. + +Reran both shards on that exact merged head under the same512MiB/oneCPU bounds: +merged-main-owner.log:101 passed,14.13s, maximumRSS406828KiB; +merged-main-related.log:30 passed/8 inherited failures,6.01s,387968KiB. +No budget escalation or stopped-shard replay. These139 distinct selected tests +therefore have131 passes and8 failures, not a green broad suite. Shape/module/ +secondary contracts pass; the remaining failures occur at the existing strict +subject guard before the changed policy and have an original-source control. +Parent integration is the named disposition owner for those fixture failures; +no active repair by another agent or final acceptance is invented. + +merged-main-census.json/log measures exact merged head against new main: +16 added core code lines and zero other measured syntax-count changes, no parse +failures, maximumRSS20304KiB. Earlier compact NRA input remains byte-identical. +No parent viewer/MCP acceptance is attributed to this source-only repair. + +Disposal: owned scratch860KiB and build objects400KiB were archived together as +owned-disposable.tar.gz (309648 bytes,124 archive entries, gzip integrity checked), +then only /home/ts/.cache/agent-scratch/issue314-russell and this worktree's build/ +were removed. SHA2560ef110ed10150278f09ba7ea4abf837c87f163d834794a99f26731ec3b722a69. +Removal is recoverable from that archive. Local compiled extensions remain in +the owned checkout for reproduction; no task-owned persistent worker is active. +Durable .issue314/ retains instruction extraction and original-source controls. +SHA256SUMS seals published source/tests/evidence (excluding the manifest itself). + +Reproduce from this checkout using the existing Python entrypoint and the exact +bounded commands retained in merged-main-owner.log/merged-main-related.log. +The baseline control command and original failing assertions remain in their +logs; no held-out TIFF, frozen CSV or scientific trial artifact is involved. +Draft PR317 contains the coherent source repair and original failures. Parent +owns merge, source-closure/installed/MCP/external-parity acceptance; no installation, +runtime launch, issue closure or frozen-file mutation is requested or claimed. diff --git a/docs/validation/paired_field_csv_314_20260930/core-census.json.gz b/docs/validation/paired_field_csv_314_20260930/core-census.json.gz new file mode 100644 index 0000000000..7f12ab38fd Binary files /dev/null and b/docs/validation/paired_field_csv_314_20260930/core-census.json.gz differ diff --git a/docs/validation/paired_field_csv_314_20260930/core-census.log b/docs/validation/paired_field_csv_314_20260930/core-census.log new file mode 100644 index 0000000000..54631c41b6 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/core-census.log @@ -0,0 +1,41 @@ +snapshot at 702b2c2c9563da2636db251f8b734cff215596b8 + +per 1,000 code lines snapshot +type_identity_check 0.25 +long_boolean_chain 0.45 +boolean_chain_terms 2.09 +codec_subclass 0.00 +foreign_absence_probe 5.49 +string_dispatch 0.08 +type_switch 0.32 +string_dispatch_arms 0.26 +type_switch_arms 1.38 +string_key_subscript 3.00 +literal_key_get 1.22 +string_equality 0.67 +none_identity 27.62 +isinstance_call 9.00 +attribute_by_name 0.14 +getattr_default 0.08 +broad_except 0.56 +json_loads 0.02 +long_function 0.50 +class_definition 15.03 +abstract_base 1.45 +enum_class 0.94 +HEADLINE 3.7 (type_identity_check + long_boolean_chain + string_key_subscript) +code lines 91295 + +Top 12 files by weighted debt (type_identity_check x3, long_boolean_chain x5, boolean_chain_terms x1, codec_subclass x3, foreign_absence_probe x1, string_dispatch x5, type_switch x5, string_dispatch_arms x1, type_switch_arms x1, string_key_subscript x1, literal_key_get x1, string_equality x1, none_identity x1, isinstance_call x1, attribute_by_name x3, getattr_default x3, broad_except x2): + 267 openhcs/core/debug.py (code lines +2663) + 202 openhcs/core/artifacts.py (code lines +2665) + 181 openhcs/core/steps/function_runtime.py (code lines +3182) + 161 openhcs/core/runtime_image_values.py (code lines +1816) + 153 openhcs/core/pipeline/path_planner.py (code lines +2711) + 148 openhcs/core/measurement_row_materialization.py (code lines +2156) + 144 openhcs/core/source_bindings.py (code lines +2346) + 129 openhcs/core/source_image_provenance.py (code lines +1732) + 128 openhcs/core/callable_contract.py (code lines +2019) + 122 openhcs/core/equivalence/measurement_rows.py (code lines +2341) + 105 openhcs/core/debug_view_models.py (code lines +394) + 85 openhcs/core/runtime_equivalence.py (code lines +2846) diff --git a/docs/validation/paired_field_csv_314_20260930/core-overlay.json.gz b/docs/validation/paired_field_csv_314_20260930/core-overlay.json.gz new file mode 100644 index 0000000000..c01643a817 Binary files /dev/null and b/docs/validation/paired_field_csv_314_20260930/core-overlay.json.gz differ diff --git a/docs/validation/paired_field_csv_314_20260930/core-overlay.log b/docs/validation/paired_field_csv_314_20260930/core-overlay.log new file mode 100644 index 0000000000..75a58cb2db --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/core-overlay.log @@ -0,0 +1,116 @@ +SUMMARY + string_dispatch 6 + isinstance_switch 33 + raw_shape 33 (bypassing an existing class: 28; hand-mapped into one: 1) + exact_key_set 0 + named_attribute 13 (most probed: __key_extractor__ x2, _identity x2, variable_component_stack_requirement x1, all x1, __name__ x1, __doc__ x1) + long_condition 4 (leads by dominant term: IDEN-1 1, IDEN-3 1, IMPL-1 1, IMPL-10 1) + god_class 14 + god_function 46 + legacy_marker 367 + positional_insert 0 + column_insert 0 + embedded_script 0 + child_process_site 0 + literal_roster 1 + dead_module 160 + +STRING DISPATCH: one subject compared against several string literals + 6 openhcs/core/runtime_array_values.py::runtime_array_ufunc_result on method ['__call__', 'accumulate', 'at', 'outer', 'reduce', 'reduceat'] + 3 openhcs/core/equivalence/keys.py::semantic_core_parts on self.parts[1] ['x', 'y', 'z'] + 3 openhcs/core/pipeline/funcstep_contract_validator.py::_add_module_if_external on module_name ['__builtins__', 'builtins', 'openhcs'] + 3 openhcs/core/runtime_profile.py::enabled on os.environ.get(PROFILE_RUNTIME_ENV, ''). ['1', 'true', 'yes'] + 3 openhcs/core/steps/function_execution.py::from_environment on raw_enabled.lower() ['1', 'true', 'yes'] + 3 openhcs/core/steps/function_runtime.py::enabled on raw_value.lower() ['1', 'true', 'yes'] + +ISINSTANCE SWITCHES: several type checks on one subject + 11 openhcs/core/debug.py::from_value on value + 8 openhcs/core/equivalence/cells.py::update_measurement_table_cell_hash on value + 7 openhcs/core/equivalence/cells.py::measurement_table_cell_payload on value + 6 openhcs/core/debug_view_models.py::debug_view_jsonable on value + 6 openhcs/core/debug_views.py::_debug_table_jsonable on value + 5 openhcs/core/debug_view_models.py::debug_view_cell_text on value + 5 openhcs/core/debug_views.py::_debug_table_cell_text on value + 5 openhcs/core/function_patterns.py::get_core_callable on func_pattern + 5 openhcs/core/function_step_transport.py::normalize_function_spec on func_spec + 4 openhcs/core/aligned_image_payload.py::payload_slices_for_alignment on payload + +RAW SHAPES: one subject read by several string keys + 18 openhcs/core/progress/types.py::from_dict data ['completed', 'component', 'context', 'error', 'message', 'owned_wells'] -> MATCHES openhcs/core/progress/types.py::ProgressEvent: a bypass, unless the data is copied into it elsewhere + 14 openhcs/core/debug.py::from_json_dict data ['axis_id', 'callable_name', 'cursor', 'exception', 'input_artifact_refs', 'invocation_parameters'] -> MATCHES openhcs/core/debug.py::DebugBoundaryState: a bypass, unless the data is copied into it elsewhere + 12 openhcs/core/runtime_spatial_grid.py::from_mapping data ['columns', 'ordering', 'origin', 'rows', 'slice_index', 'spot_table'] -> MATCHES openhcs/core/runtime_spatial_grid.py::SpatialGrid: a bypass, unless the data is copied into it elsewhere + 11 openhcs/core/progress/types.py::to_dict result ['component', 'context', 'error', 'message', 'owned_wells', 'pattern'] -> MATCHES openhcs/core/progress/types.py::ProgressEvent: a bypass, unless the data is copied into it elsewhere + 10 openhcs/core/debug.py::from_json_dict data ['content_digest', 'cursor', 'dtype', 'identity', 'kind', 'name'] -> MATCHES openhcs/core/debug.py::DebugArtifactRef: a bypass, unless the data is copied into it elsewhere + 9 openhcs/core/debug.py::from_payload payload ['command_type', 'debug_session_id', 'pause_step_indices', 'replay_mode', 'selected_source_group', 'snapshot_store_backend'] -> MATCHES openhcs/core/debug.py::DebugExecutionConfig: a bypass, unless the data is copied into it elsewhere + 9 openhcs/core/progress/live_measurements.py::from_dict data ['address', 'columns', 'materialized_locations', 'object_name', 'row_count', 'rows'] -> MATCHES openhcs/core/progress/live_measurements.py::LiveMeasurementTablePreview: a bypass, unless the data is copied into it elsewhere + 7 openhcs/core/debug.py::from_json_dict data ['group_keys', 'kind', 'name', 'producer_step_index', 'producer_step_name', 'producer_step_scope_id'] -> MATCHES openhcs/core/debug.py::DebugArtifactIdentity: a bypass, unless the data is copied into it elsewhere + 7 openhcs/core/utils.py::analyze_thread_activity activity ['action', 'active_threads', 'duration', 'function', 'thread_name', 'time'] -> unmodeled + 7 openhcs/core/utils.py::print_thread_activity_report overlap ['duration', 'function1', 'function2', 'thread1', 'thread2', 'well1'] -> unmodeled + +LONG CONDITIONS: syntactic ownership leads, verify before choosing a target + 14 terms openhcs/core/debug_view_models.py:463 [literal 13, predicate 1] -> lead: IMPL-1 + 9 terms openhcs/core/source_bindings.py:1973 [flag 8, absence 1] -> lead: IMPL-10 + 7 terms openhcs/core/debug.py:590 [same_value 7] -> lead: IDEN-1 + 6 terms openhcs/core/debug_session_projection.py:275 [absence 6] -> lead: IDEN-3 + +GOD CLASSES (over 500 lines) + 1806 openhcs/core/pipeline/compiler.py::PipelineCompiler + 1430 openhcs/core/pipeline/path_planner.py::PathPlannerArtifactStage + 1053 openhcs/core/runtime_image_values.py::ImagePayloadMetadata + 990 openhcs/core/steps/function_runtime.py::PatternGroupRuntime + 965 openhcs/core/orchestrator/orchestrator.py::PipelineOrchestrator + 914 openhcs/core/pipeline/funcstep_contract_validator.py::FuncStepContractValidator + 760 openhcs/core/equivalence/policy.py::RuntimeMeasurementDialect + 717 openhcs/core/equivalence/measurement_rows.py::RuntimeRowProjectionContext + 694 openhcs/core/callable_contract.py::CallableContract + 691 openhcs/core/steps/function_output_identity.py::FunctionOutputIdentityAuthority + +GOD FUNCTIONS (over 100 lines) + 324 openhcs/core/measurement_row_materialization.py::add + 246 openhcs/core/equivalence/policy.py::__post_init__ + 224 openhcs/core/pipeline/path_planner.py::process_artifact_outputs + 196 openhcs/core/aligned_image_payload.py::compose_aligned_image_payload + 190 openhcs/core/viewer_streaming_service.py::stream_rois + 176 openhcs/core/pipeline/compiler.py::compile_pipelines + 164 openhcs/core/steps/function_runtime.py::_save_outputs + 152 openhcs/core/steps/function_runtime.py::_load_input_stack + 150 openhcs/core/equivalence/relationships.py::object_measurement_values_by_label + 149 openhcs/core/source_binding_workspace.py::_matched_source_sets + +LITERAL ROSTERS: one set of string literals written in several places + 3x ['1', 'true', 'yes'] + +DEAD MODULES: imported by nothing in the package + 44 openhcs/core/alias_property.py + 1566 openhcs/core/aligned_image_payload.py + 218 openhcs/core/artifact_inspection.py + 120 openhcs/core/artifact_key_selection.py + 3172 openhcs/core/artifacts.py + 121 openhcs/core/autoregister_preparation.py + 88 openhcs/core/axis_filter.py + 2313 openhcs/core/callable_contract.py + 226 openhcs/core/compiled_execution.py + 480 openhcs/core/compiled_step_plan.py + Command being timed: "env PYTHONDONTWRITEBYTECODE=1 /home/ts/code/projects/openhcs/.venv/bin/python -B .issue314/refactor-audit/scripts/overlay.py --repo . --root openhcs/core --rev 702b2c2c9563da2636db251f8b734cff215596b8 --json docs/validation/paired_field_csv_314_20260930/core-overlay.json" + User time (seconds): 19.87 + System time (seconds): 0.52 + Percent of CPU this job got: 99% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:20.50 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 164228 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 110887 + Voluntary context switches: 758 + Involuntary context switches: 182 + Swaps: 0 + File system inputs: 0 + File system outputs: 200 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 diff --git a/docs/validation/paired_field_csv_314_20260930/create-live-fixture.py b/docs/validation/paired_field_csv_314_20260930/create-live-fixture.py new file mode 100644 index 0000000000..bb5e57f0e4 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/create-live-fixture.py @@ -0,0 +1,23 @@ +"""Author tiny known-value inputs; never reads or alters biological images.""" + +import sys +from pathlib import Path + +import numpy as np +import tifffile + + +root = Path(sys.argv[1]) +root.mkdir(parents=True, exist_ok=False) +(root / "plate.HTD").write_text( + '"XSites", 1\n"YSites", 1\n"PixelSizeUM", 0.65\n' +) +images = root / "TimePoint_1" +images.mkdir() +for well in ("A01", "B01"): + dna = np.zeros((32, 32), dtype=np.uint16) + dna[5:9, 5:9] = 12000 + dna[22:26, 22:26] = 18000 + for channel, image in ((1, dna), (2, np.full((32, 32), 24000, dtype=np.uint16))): + tifffile.imwrite(images / f"{well}_s001_w{channel}_z001_t001.tif", image) +print(root) diff --git a/docs/validation/paired_field_csv_314_20260930/effective-imports-error.log b/docs/validation/paired_field_csv_314_20260930/effective-imports-error.log new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/validation/paired_field_csv_314_20260930/effective-imports.json b/docs/validation/paired_field_csv_314_20260930/effective-imports.json new file mode 100644 index 0000000000..00903619fd --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/effective-imports.json @@ -0,0 +1,24 @@ +{ + "python": "3.12.3 (main, May 22 2025, 14:39:13) [GCC 14.2.1 20250207]", + "imports": { + "openhcs": "/home/ts/wt/openhcs-paired-field-csv-314-20260930/openhcs/__init__.py", + "polystore": "/home/ts/wt/openhcs-custom-function-admission-20260929/external/PolyStore/src/polystore/__init__.py", + "metaclass_registry": "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/metaclass_registry/__init__.py", + "python_introspect": "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/python_introspect/__init__.py", + "objectstate": "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/objectstate/__init__.py", + "arraybridge": "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/arraybridge/__init__.py", + "pycodify": "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pycodify/__init__.py" + }, + "versions": { + "openhcs": "0.8.6", + "polystore": "0.2.19", + "metaclass-registry": "0.2.1", + "python-introspect": "0.1.14", + "objectstate": "1.1.8", + "arraybridge": "0.3.4", + "pycodify": "0.1.3", + "numpy": "2.1.3", + "scipy": "1.18.1", + "scikit-image": "0.25.2" + } +} diff --git a/docs/validation/paired_field_csv_314_20260930/final-policy-exporter.log b/docs/validation/paired_field_csv_314_20260930/final-policy-exporter.log new file mode 100644 index 0000000000..3f3e8b5fa5 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/final-policy-exporter.log @@ -0,0 +1,37 @@ +......................................... [100%] +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +41 passed, 2 warnings in 4.91s + Command being timed: "timeout 60s env OPENHCS_CPU_ONLY=true OPENHCS_BACKEND_EXCLUDE=torch,tensorflow,jax,cupy OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest tests/unit/test_source_image_set_identity_policy.py tests/unit/test_cellprofiler_spreadsheet_export.py -q --basetemp=/home/ts/.cache/agent-scratch/issue314-russell/pytest-final" + User time (seconds): 5.73 + System time (seconds): 0.35 + Percent of CPU this job got: 99% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:06.14 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 378400 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 69876 + Voluntary context switches: 30 + Involuntary context switches: 39 + Swaps: 0 + File system inputs: 32 + File system outputs: 56 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 diff --git a/docs/validation/paired_field_csv_314_20260930/installed-parent-checkpoint.rst b/docs/validation/paired_field_csv_314_20260930/installed-parent-checkpoint.rst new file mode 100644 index 0000000000..44d35e3192 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/installed-parent-checkpoint.rst @@ -0,0 +1,101 @@ +Installed paired-field export acceptance +======================================= + +Parent integration owner. Production candidate d8fa0d5afd690cb1d37b7dd49012d1e0e8035716 +normally integrates main8e478cb21f89fe74c3d6ead0cb54c67a5e1047dc with PR317. +This checkpoint supersedes source-only limits without erasing their original +failures or claiming biological acceptance or full ZIP-refactor completion. + +Issue322 reproduces on both metadata-bearing connected-component and preserved +label inputs: two rich-image cases fail at ``image.copy()``, while both plain +array controls pass. The existing runtime payload already owns NumPy conversion. +Replace that consumer-only array-method assumption with ``np.asarray(image).copy()``. +Keep the original image in main flow and SourceImageObjectLabelBuildRequest, +preserving pixels, source identity, domain and declared spacing. No carrier +forwarding method, type/name switch, metadata mirror or fallback is added. +BOUND-2/BOUND-7 prevention uses the declared conversion owner instead of bypassing +the carrier or probing/adding private array attributes. This is an authored +boundary repair, not an NRA-produced equivalence proof. + +Current source checks: 51 tests pass across object-output contracts, source-image +identity policy and the real spreadsheet exporter. Four additional rendering +regression controls pass. The new rich/plain cases retain exact labels, object +counts, areas, untouched input pixels, original main-flow identity, provenance, +spatial domain and configured 0.65 XY spacing. The unchanged packaged structural +ratchet compares 5,133 metrics against current main with zero positive deltas. +Earlier full-context NRA/R1 deadline failures remain incomplete, not passes; +the owner explicitly directed that these not block verified useful checkpoints. +An initial source invocation used a nonexistent test filename and ran no tests; +its exit4/XML are retained and excluded from the 55-pass count. + +Persistent evidence:: + + /home/ts/wt/openhcs-issue-batch-20260929/paired-field-parent-20260930 + +Build/install uses the actual OpenHCS wheel and its recorded ArrayBridge409b1e0 +companion wheel in the isolated candidate environment. Heavy third-party packages +and Fiji are reused; download is disabled and shared main/installations unchanged. +Fresh MCP health reports site-packages, all 56 packaged resources and no stale +source. The original failed installed run029ec591 remains retained independently. + +The first receipt helper exits at its PTY's 4,096-byte canonical input limit +before parsing or dispatching the artifact-plan request. Its native startup +handle149206/create1790814373.81 remains live. A new MCP client150535 reconnects +with noncanonical, echo-disabled terminal input and observes that same child +through readiness. Startup, preparation and execution are not replayed. Ordinary +10-second observation deadlines are unchanged. Native cold preparation takes +about119 seconds; this is explicit preparation, not a raised per-command timeout. + +After the copy repair, executionfa80b527-a1c4-4f00-846c-28164f7a1b27 passes +KnownNuclei and fails at the next step: the parent's synthetic control left +KnownCells on PREVIOUS_STEP although it requires original Actin-channel main +flow, absent from the DNA-only predecessor. The reflected processing-config +schema and existing CellProfiler importer confirm that named artifact binding +does not select main-flow input. Correct only the fixture's existing lazy +``processing_config.input_source`` to PIPELINE_START. Preserve this failed +run and use ``outputs-input-source-fixed`` rather than overwriting its files. +No scientific parameters, algorithm or production source-selection guard change. + +The fresh complete PipelineDocument retains all source aliases/coordinate rules, +two wells, one worker and all four analysis/export steps. Artifact inspection +finds four source files, two axes and eight per-axis step plans without errors. +Compile14d9ac41-4b55-4c88-93a4-02ae04de50e9 completes in0.79 seconds. +Executionbb932c48-de7a-4867-bbb2-5a090a5aa928 completes in1.11 seconds, including +strict final metadata reconciliation. Reconnected receipt017 is terminal success. + +Read-only installed ``check-installed-parent.py RECEIPT_ROOT`` verifies: + +* MCP's explicit result-directory route returns all16 persisted result files + without truncation, including four complete Cells.csv rows rather than eight + split partial rows. Native bounded CSV previews agree exactly with disk. +* Image numbers1/2 each contain objects1/2. Parent_Nuclei, location and shape + coexist in every cell row; all fields are populated. Areas are52 pixels each; + location and shape centres agree at6.5/23.5 pixels. Fields are not collapsed. +* Four physical label references reopen through the existing workspace projection + with disk addresses and32x32 spatial domains. Nuclei labels match the known + masks exactly; both cell IDs contain52 pixels each. All four synthetic raw + images retain their fixture pixels unchanged. +* The existing ZMQRuntimeExecutionObservationExport reader reopens the actual + gzip-pickle observation (the extension is not its format authority) and + verifies both wells succeeded and the exact execution ID. No new reader. + +The checker initially counted aliased lookup keys as physical files; the native +projection intentionally admits relative/full aliases. The corrected assertion +counts distinct backend addresses and requires each declared source projection. +No CSV, label, source-pixel or completion assertion was removed. + +This manually declared generic source-binding fixture omits source_voxel_spacing; +its HTD text alone does not calibrate the generic declaration route. Do not +claim installed physical-unit acceptance from it. The rich-input unit control +proves declared spacing is preserved; this installed control proves field/row +identity and original pixel-coordinate semantics only. Biological raw/result/ +combined QA, calibration of future trials and fresh blind analysis remain separate. + +Owned runtime close in receipt021 reports request_attempted, acknowledged and +process_exited true for149206/create1790814373.81. MCP150535 exits normally; +independent process/socket checks confirm both gone and ports5997/6997 free. +No viewer was launched, desktop:0 and H002 untouched. Runtime logs/settings are +archived in ``runtime-image-copy-fixed-logs.tgz`` before removing only owned +18MiB scratch and20MiB generated build output. All scientific output, failed +attempts, requests, receipts, wheel candidates and persistent source are retained. +H003e remains immutable REJECT with no data access, rerun, tuning or answers read. diff --git a/docs/validation/paired_field_csv_314_20260930/merged-main-census.json b/docs/validation/paired_field_csv_314_20260930/merged-main-census.json new file mode 100644 index 0000000000..a32071ea49 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/merged-main-census.json @@ -0,0 +1,59 @@ +{ + "base": "2b7969f700c33eb7f73f891d975afd1efc0628bb", + "rev": "e3d991e9cf02c496daa7c9df32c73bfde4a7ad00", + "change": { + "tally": { + "type_identity_check": 0, + "long_boolean_chain": 0, + "boolean_chain_terms": 0, + "codec_subclass": 0, + "foreign_absence_probe": 0, + "string_dispatch": 0, + "type_switch": 0, + "string_dispatch_arms": 0, + "type_switch_arms": 0, + "string_key_subscript": 0, + "literal_key_get": 0, + "string_equality": 0, + "none_identity": 0, + "isinstance_call": 0, + "attribute_by_name": 0, + "getattr_default": 0, + "broad_except": 0, + "json_loads": 0, + "long_function": 0, + "class_definition": 0, + "abstract_base": 0, + "enum_class": 0, + "code_lines": 16 + }, + "files": { + "openhcs/core/source_matching.py": { + "type_identity_check": 0, + "long_boolean_chain": 0, + "boolean_chain_terms": 0, + "codec_subclass": 0, + "foreign_absence_probe": 0, + "string_dispatch": 0, + "type_switch": 0, + "string_dispatch_arms": 0, + "type_switch_arms": 0, + "string_key_subscript": 0, + "literal_key_get": 0, + "string_equality": 0, + "none_identity": 0, + "isinstance_call": 0, + "attribute_by_name": 0, + "getattr_default": 0, + "broad_except": 0, + "json_loads": 0, + "long_function": 0, + "class_definition": 0, + "abstract_base": 0, + "enum_class": 0, + "code_lines": 16 + } + }, + "unparsed": [] + } +} \ No newline at end of file diff --git a/docs/validation/paired_field_csv_314_20260930/merged-main-census.log b/docs/validation/paired_field_csv_314_20260930/merged-main-census.log new file mode 100644 index 0000000000..9692aea2a9 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/merged-main-census.log @@ -0,0 +1,54 @@ +changed between 2b7969f700c33eb7f73f891d975afd1efc0628bb..e3d991e9cf02c496daa7c9df32c73bfde4a7ad00 (touched files, after minus before) + +change in counts (2b7969f700c33eb7f73f891d975afd1efc0628bb..e3d991e9cf02c496daa7c9df32c73bfde4a7ad00): read these first when code was deleted as well as added + type_identity_check +0 + long_boolean_chain +0 + boolean_chain_terms +0 + codec_subclass +0 + foreign_absence_probe +0 + string_dispatch +0 + type_switch +0 + string_dispatch_arms +0 + type_switch_arms +0 + string_key_subscript +0 + literal_key_get +0 + string_equality +0 + none_identity +0 + isinstance_call +0 + attribute_by_name +0 + getattr_default +0 + broad_except +0 + json_loads +0 + long_function +0 + class_definition +0 + abstract_base +0 + enum_class +0 + code lines +16 + +(densities omitted: under 500 net code lines, a ratio of changes to net lines is meaningless) + +Top 12 files by weighted debt (type_identity_check x3, long_boolean_chain x5, boolean_chain_terms x1, codec_subclass x3, foreign_absence_probe x1, string_dispatch x5, type_switch x5, string_dispatch_arms x1, type_switch_arms x1, string_key_subscript x1, literal_key_get x1, string_equality x1, none_identity x1, isinstance_call x1, attribute_by_name x3, getattr_default x3, broad_except x2): + 0 openhcs/core/source_matching.py (code lines +16) + Command being timed: "timeout 60s env OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 /home/ts/code/projects/openhcs/.venv/bin/python -B .issue314/refactor-audit/scripts/debt_census.py --repo . --root openhcs/core --rev e3d991e9cf02c496daa7c9df32c73bfde4a7ad00 --base 2b7969f700c33eb7f73f891d975afd1efc0628bb --json docs/validation/paired_field_csv_314_20260930/merged-main-census.json" + User time (seconds): 0.17 + System time (seconds): 0.02 + Percent of CPU this job got: 98% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.20 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 20304 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 4669 + Voluntary context switches: 20 + Involuntary context switches: 2 + Swaps: 0 + File system inputs: 72 + File system outputs: 16 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 diff --git a/docs/validation/paired_field_csv_314_20260930/merged-main-owner.log b/docs/validation/paired_field_csv_314_20260930/merged-main-owner.log new file mode 100644 index 0000000000..fc5fcfd85f --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/merged-main-owner.log @@ -0,0 +1,38 @@ +........................................................................ [ 71%] +............................. [100%] +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +101 passed, 2 warnings in 14.13s + Command being timed: "timeout 60s env OPENHCS_CPU_ONLY=true OPENHCS_BACKEND_EXCLUDE=torch,tensorflow,jax,cupy OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest tests/unit/test_cellprofiler_spreadsheet_export.py tests/unit/test_source_bindings.py tests/unit/test_source_image_provenance.py tests/unit/test_cellprofiler_measurement_source_group_scope.py tests/unit/test_source_image_set_identity_policy.py -q --basetemp=/home/ts/.cache/agent-scratch/issue314-russell/pytest-merged-owner" + User time (seconds): 14.82 + System time (seconds): 0.46 + Percent of CPU this job got: 99% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:15.42 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 406828 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 87851 + Voluntary context switches: 16 + Involuntary context switches: 111 + Swaps: 0 + File system inputs: 88 + File system outputs: 128 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 diff --git a/docs/validation/paired_field_csv_314_20260930/merged-main-related.log b/docs/validation/paired_field_csv_314_20260930/merged-main-related.log new file mode 100644 index 0000000000..a28aa8e395 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/merged-main-related.log @@ -0,0 +1,659 @@ +.................FFFFFF..FF........... [100%] +=================================== FAILURES =================================== +_____ test_all_loaded_contract_collapses_sibling_alias_groups_by_metadata ______ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fdddcad47d0> + + def test_all_loaded_contract_collapses_sibling_alias_groups_by_metadata( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=tuple(alias for alias, _channel in SOURCE_ALIASES), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:270: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n...e'>, name='OrigSyto'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +___________ test_natural_callable_keeps_each_source_component_anchor ___________ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fdddcdf49b0> + + def test_natural_callable_keeps_each_source_component_anchor( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=tuple(alias for alias, _channel in SOURCE_ALIASES), + composed_image_set=False, + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:287: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n...e'>, name='OrigSyto'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +_______ test_selected_source_contract_keeps_only_its_exact_alias_anchor ________ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fdddca60710> + + def test_selected_source_contract_keeps_only_its_exact_alias_anchor( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=("OrigHoechst",), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:302: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n... name='OrigHoechst')),), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +_______ test_distinct_metadata_sets_each_keep_one_source_representative ________ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fdddca63950> + + def test_distinct_metadata_sets_each_keep_one_source_representative( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=tuple(alias for alias, _channel in SOURCE_ALIASES), + sites=(1, 2), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:315: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n...e'>, name='OrigSyto'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +______ test_independent_source_aligned_outputs_keep_each_execution_group _______ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fdddcdf6c00> + + def test_independent_source_aligned_outputs_keep_each_execution_group( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=("OrigER", "OrigHoechst"), + shared_output=False, + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:336: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='OrigERMeasurements', artifact_type=, param...pe'>, name='OrigER')),), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='OrigERMeasurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +______ test_missing_alias_in_match_dimension_keeps_existing_strict_error _______ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fdddca61d30> + + def test_missing_alias_in_match_dimension_keeps_existing_strict_error( + monkeypatch: pytest.MonkeyPatch, + ) -> None: + with pytest.raises( + ValueError, + match="METADATA source binding dimension is missing alias 'OrigER'", + ): +> _filter_source_anchors( + monkeypatch, + artifact_aliases=tuple(alias for alias, _channel in SOURCE_ALIASES), + dimension_aliases=tuple( + alias for alias, _channel in SOURCE_ALIASES if alias != "OrigER" + ), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:355: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n...e'>, name='OrigSyto'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError + +During handling of the above exception, another exception occurred: + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fdddca61d30> + + def test_missing_alias_in_match_dimension_keeps_existing_strict_error( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> with pytest.raises( + ValueError, + match="METADATA source binding dimension is missing alias 'OrigER'", + ): +E AssertionError: Regex pattern did not match. +E Regex: "METADATA source binding dimension is missing alias 'OrigER'" +E Input: "Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + +tests/unit/test_source_bound_matched_image_set_anchors.py:351: AssertionError +___ test_complete_source_set_templates_preserve_each_execution_group_anchor ____ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fdddcab2ae0> + + def test_complete_source_set_templates_preserve_each_execution_group_anchor( + monkeypatch: pytest.MonkeyPatch, + ) -> None: + bindings = tuple( + NamedSourceBinding( + alias=alias, + selector=SourceSelector( + components=(ComponentSelector(AllComponents.CHANNEL, channel),), + ), + origin=SourceBindingOrigin.PIPELINE_START, + component_identity=(ComponentSelector(AllComponents.CHANNEL, channel),), + ) + for alias, channel in (("OrigStain1", "1"), ("OrigStain2", "2")) + ) + input_specs = tuple(binding.input_spec() for binding in bindings) + measurements = ArtifactSpec.output( + "SourceSetMeasurements", + MeasurementsArtifactType, + relations=tuple( + GroupLineageSourceRelation(source=spec.ref()) for spec in input_specs + ), + ) + + @artifact_inputs(*input_specs) + @artifact_outputs(measurements) + @composed_image_payload + def align_source_set(image): + return image + + output_plan = ArtifactOutputPlan( + name=measurements.name, + path="/memory/SourceSetMeasurements.pkl", + artifact_type=measurements.artifact_type, + group_keys=("1", "2"), + group_component=AllComponents.SITE, + relations=measurements.relations, + ) + plan = SimpleNamespace( + axis_id="A01", + step_index=0, + step_name="Align", + main_input_dependency=StepInputDependency.pipeline_start(), + source_binding_plan=CompiledSourceBindingPlan( + bindings=bindings, + match_plan=SourceBindingMatchPlan( + method=SourceBindingMatchMethod.ORDER, + ), + ), + execution_group_scope=ComponentGroupScope.from_raw( + ("1", "2"), + component=AllComponents.SITE, + ), +> compiled_function_pattern=compile_function_pattern( + align_source_set, + {}, + {output_plan.ref(): output_plan}, + ), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:582: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='SourceSetMeasurements', artifact_type=, pa...>, name='OrigStain2'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='SourceSetMeasurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +__ test_static_site_groups_do_not_cross_project_natural_source_set_templates ___ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fdddcab2fc0> + + def test_static_site_groups_do_not_cross_project_natural_source_set_templates( + monkeypatch: pytest.MonkeyPatch, + ) -> None: + bindings = tuple( + NamedSourceBinding( + alias=alias, + selector=SourceSelector( + filters=(), + ), + origin=SourceBindingOrigin.PIPELINE_START, + component_identity=(ComponentSelector(AllComponents.CHANNEL, channel),), + ) + for alias, channel in (("OrigStain1", "1"), ("OrigStain2", "2")) + ) + input_specs = tuple(binding.input_spec() for binding in bindings) + output_specs = ( + ArtifactSpec.output( + "Stain1", + ImageArtifactType, + relations=(GroupLineageSourceRelation(source=input_specs[0].ref()),), + ), + ArtifactSpec.output( + "Stain2", + ImageArtifactType, + relations=(GroupLineageSourceRelation(source=input_specs[1].ref()),), + ), + ArtifactSpec.output( + "AlignMeasurements", + MeasurementsArtifactType, + relations=tuple( + GroupLineageSourceRelation(source=spec.ref()) for spec in input_specs + ), + ), + ) + + @artifact_inputs(*input_specs) + @artifact_outputs(*output_specs) + def align_source_set(image): + return image + + output_plans = { + spec.ref(): ArtifactOutputPlan( + name=spec.name, + path=f"/memory/{spec.name}.pkl", + artifact_type=spec.artifact_type, + group_keys=("1", "2"), + group_component=AllComponents.SITE, + relations=spec.relations, + ) + for spec in output_specs + } + plan = SimpleNamespace( + axis_id="A01", + step_index=0, + step_name="Align", + main_input_dependency=StepInputDependency.pipeline_start(), + source_binding_plan=CompiledSourceBindingPlan( + bindings=bindings, + match_plan=SourceBindingMatchPlan( + method=SourceBindingMatchMethod.ORDER, + ), + ), + execution_group_scope=ComponentGroupScope.from_raw( + ("1", "2"), + component=AllComponents.SITE, + ), +> compiled_function_pattern=compile_function_pattern( + align_source_set, + {}, + output_plans, + ), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:711: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='AlignMeasurements', artifact_type=, parame...>, name='OrigStain2'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='AlignMeasurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +=========================== short test summary info ============================ +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_all_loaded_contract_collapses_sibling_alias_groups_by_metadata +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_natural_callable_keeps_each_source_component_anchor +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_selected_source_contract_keeps_only_its_exact_alias_anchor +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_distinct_metadata_sets_each_keep_one_source_representative +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_independent_source_aligned_outputs_keep_each_execution_group +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_missing_alias_in_match_dimension_keeps_existing_strict_error +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_complete_source_set_templates_preserve_each_execution_group_anchor +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_static_site_groups_do_not_cross_project_natural_source_set_templates +8 failed, 30 passed, 2 warnings in 6.01s +Command exited with non-zero status 1 + Command being timed: "timeout 60s env OPENHCS_CPU_ONLY=true OPENHCS_BACKEND_EXCLUDE=torch,tensorflow,jax,cupy OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest tests/unit/test_cellprofiler_shape_hotpath.py tests/unit/test_cellprofiler_module_measurement_scopes.py tests/unit/test_cellprofiler_identify_secondary_replacement_primary.py tests/unit/test_cellprofiler_secondary_distance_backend.py tests/unit/test_source_bound_matched_image_set_anchors.py tests/unit/test_source_image_provenance_hotpath.py tests/unit/test_function_output_manifest_input_scope.py -q --basetemp=/home/ts/.cache/agent-scratch/issue314-russell/pytest-merged-related" + User time (seconds): 6.31 + System time (seconds): 0.47 + Percent of CPU this job got: 99% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:06.84 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 387968 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 89386 + Voluntary context switches: 15 + Involuntary context switches: 36 + Swaps: 0 + File system inputs: 200 + File system outputs: 128 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 1 diff --git a/docs/validation/paired_field_csv_314_20260930/nra-original-resources.log b/docs/validation/paired_field_csv_314_20260930/nra-original-resources.log new file mode 100644 index 0000000000..f0cc5becc6 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/nra-original-resources.log @@ -0,0 +1,24 @@ +Command exited with non-zero status 124 + Command being timed: "timeout 60s env OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=/home/ts/code/projects/nominal-refactor-advisor XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m nominal_refactor_advisor openhcs/core/source_matching.py openhcs/interop/cellprofiler/image_set_numbering.py --context-root openhcs --json --raw-findings --json-payload full --parse-workers 1 --analysis-workers 1 --no-cache" + User time (seconds): 22.78 + System time (seconds): 0.52 + Percent of CPU this job got: 99% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:23.46 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 524536 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 184195 + Voluntary context switches: 92 + Involuntary context switches: 222 + Swaps: 0 + File system inputs: 2752 + File system outputs: 8 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 124 diff --git a/docs/validation/paired_field_csv_314_20260930/nra-original.json b/docs/validation/paired_field_csv_314_20260930/nra-original.json new file mode 100644 index 0000000000..7a16c89d4e --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/nra-original.json @@ -0,0 +1,14 @@ +{ + "findings": [], + "plans": [], + "finding_payload_mode": "deadline_incomplete", + "finding_count": null, + "finding_counts": null, + "scan_status": { + "complete": false, + "deadline_exceeded": true, + "stage": "parse_python_module", + "budget_seconds": 20.0, + "elapsed_seconds": 22.054 + } +} diff --git a/docs/validation/paired_field_csv_314_20260930/nra-owner-loop-resources.log b/docs/validation/paired_field_csv_314_20260930/nra-owner-loop-resources.log new file mode 100644 index 0000000000..6f6f8d83e8 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/nra-owner-loop-resources.log @@ -0,0 +1,23 @@ + Command being timed: "timeout 60s env OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=/home/ts/code/projects/nominal-refactor-advisor XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m nominal_refactor_advisor openhcs/core/source_matching.py --json --raw-findings --json-payload loop --no-auto-context-root --parse-workers 1 --analysis-workers 1 --no-cache" + User time (seconds): 2.81 + System time (seconds): 0.14 + Percent of CPU this job got: 99% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:02.97 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 77176 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 12 + Minor (reclaiming a frame) page faults: 29232 + Voluntary context switches: 42 + Involuntary context switches: 21 + Swaps: 0 + File system inputs: 1496 + File system outputs: 8 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 diff --git a/docs/validation/paired_field_csv_314_20260930/nra-owner-loop.json b/docs/validation/paired_field_csv_314_20260930/nra-owner-loop.json new file mode 100644 index 0000000000..f3042364c8 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/nra-owner-loop.json @@ -0,0 +1,35 @@ +{ + "findings": [], + "plans": [], + "finding_payload_mode": "counts_only", + "finding_count": 0, + "finding_counts": { + "by_pattern": [], + "by_detector": [] + }, + "scan_status": { + "mode": "exact_compact_global", + "analyzed_detector_count": 79, + "omitted_detector_count": 0, + "complete": true, + "reason": "all_context_detectors_use_compact_global_projections" + }, + "timing": { + "parse_seconds": 1.231, + "analysis_seconds": 0.229, + "planning_seconds": 0.0, + "source_index_seconds": 0.0, + "analysis_cache_status": "disabled", + "total_seconds": 1.46 + }, + "active_finding_surface": "raw_findings", + "payload_timing": { + "observation_graph_seconds": 0.0, + "semantic_descent_graph_seconds": 0.0, + "source_snapshot_seconds": 0.0, + "source_index_payload_seconds": 0.0, + "semantic_refactor_gate_seconds": 0.0, + "finding_recipe_plan_seconds": 0.0, + "total_seconds": 0.003 + } +} diff --git a/docs/validation/paired_field_csv_314_20260930/owned-disposable.tar.gz b/docs/validation/paired_field_csv_314_20260930/owned-disposable.tar.gz new file mode 100644 index 0000000000..3e6e11fdc9 Binary files /dev/null and b/docs/validation/paired_field_csv_314_20260930/owned-disposable.tar.gz differ diff --git a/docs/validation/paired_field_csv_314_20260930/owner-census-delta.json b/docs/validation/paired_field_csv_314_20260930/owner-census-delta.json new file mode 100644 index 0000000000..5a074405bb --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/owner-census-delta.json @@ -0,0 +1,59 @@ +{ + "base": "702b2c2c9563da2636db251f8b734cff215596b8", + "rev": "HEAD", + "change": { + "tally": { + "type_identity_check": 0, + "long_boolean_chain": 0, + "boolean_chain_terms": 0, + "codec_subclass": 0, + "foreign_absence_probe": 0, + "string_dispatch": 0, + "type_switch": 0, + "string_dispatch_arms": 0, + "type_switch_arms": 0, + "string_key_subscript": 0, + "literal_key_get": 0, + "string_equality": 0, + "none_identity": 0, + "isinstance_call": 0, + "attribute_by_name": 0, + "getattr_default": 0, + "broad_except": 0, + "json_loads": 0, + "long_function": 0, + "class_definition": 0, + "abstract_base": 0, + "enum_class": 0, + "code_lines": 16 + }, + "files": { + "openhcs/core/source_matching.py": { + "type_identity_check": 0, + "long_boolean_chain": 0, + "boolean_chain_terms": 0, + "codec_subclass": 0, + "foreign_absence_probe": 0, + "string_dispatch": 0, + "type_switch": 0, + "string_dispatch_arms": 0, + "type_switch_arms": 0, + "string_key_subscript": 0, + "literal_key_get": 0, + "string_equality": 0, + "none_identity": 0, + "isinstance_call": 0, + "attribute_by_name": 0, + "getattr_default": 0, + "broad_except": 0, + "json_loads": 0, + "long_function": 0, + "class_definition": 0, + "abstract_base": 0, + "enum_class": 0, + "code_lines": 16 + } + }, + "unparsed": [] + } +} \ No newline at end of file diff --git a/docs/validation/paired_field_csv_314_20260930/owner-census-delta.log b/docs/validation/paired_field_csv_314_20260930/owner-census-delta.log new file mode 100644 index 0000000000..eaac50f198 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/owner-census-delta.log @@ -0,0 +1,31 @@ +changed between 702b2c2c9563da2636db251f8b734cff215596b8..HEAD (touched files, after minus before) + +change in counts (702b2c2c9563da2636db251f8b734cff215596b8..HEAD): read these first when code was deleted as well as added + type_identity_check +0 + long_boolean_chain +0 + boolean_chain_terms +0 + codec_subclass +0 + foreign_absence_probe +0 + string_dispatch +0 + type_switch +0 + string_dispatch_arms +0 + type_switch_arms +0 + string_key_subscript +0 + literal_key_get +0 + string_equality +0 + none_identity +0 + isinstance_call +0 + attribute_by_name +0 + getattr_default +0 + broad_except +0 + json_loads +0 + long_function +0 + class_definition +0 + abstract_base +0 + enum_class +0 + code lines +16 + +(densities omitted: under 500 net code lines, a ratio of changes to net lines is meaningless) + +Top 12 files by weighted debt (type_identity_check x3, long_boolean_chain x5, boolean_chain_terms x1, codec_subclass x3, foreign_absence_probe x1, string_dispatch x5, type_switch x5, string_dispatch_arms x1, type_switch_arms x1, string_key_subscript x1, literal_key_get x1, string_equality x1, none_identity x1, isinstance_call x1, attribute_by_name x3, getattr_default x3, broad_except x2): + 0 openhcs/core/source_matching.py (code lines +16) diff --git a/docs/validation/paired_field_csv_314_20260930/owner-fixed-corrected.log b/docs/validation/paired_field_csv_314_20260930/owner-fixed-corrected.log new file mode 100644 index 0000000000..3a2515575f --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/owner-fixed-corrected.log @@ -0,0 +1,38 @@ +........................................................................ [ 72%] +............................ [100%] +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +100 passed, 2 warnings in 15.52s + Command being timed: "timeout 60s env OPENHCS_CPU_ONLY=true OPENHCS_BACKEND_EXCLUDE=torch,tensorflow,jax,cupy OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest tests/unit/test_cellprofiler_spreadsheet_export.py tests/unit/test_source_bindings.py tests/unit/test_source_image_provenance.py tests/unit/test_cellprofiler_measurement_source_group_scope.py tests/unit/test_source_image_set_identity_policy.py -q --basetemp=/home/ts/.cache/agent-scratch/issue314-russell/pytest-fixed-corrected" + User time (seconds): 16.18 + System time (seconds): 0.64 + Percent of CPU this job got: 98% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:17.07 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 455620 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 110741 + Voluntary context switches: 264 + Involuntary context switches: 70 + Swaps: 0 + File system inputs: 11840 + File system outputs: 136 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 diff --git a/docs/validation/paired_field_csv_314_20260930/owner-fixed.log b/docs/validation/paired_field_csv_314_20260930/owner-fixed.log new file mode 100644 index 0000000000..db16ae6112 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/owner-fixed.log @@ -0,0 +1,175 @@ + +==================================== ERRORS ==================================== +_____ ERROR collecting tests/unit/test_cellprofiler_spreadsheet_export.py ______ +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/python.py:493: in importtestmodule + mod = import_path( +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/pathlib.py:587: in import_path + importlib.import_module(module_name) +../../.pyenv/versions/3.12.3/lib/python3.12/importlib/__init__.py:90: in import_module + return _bootstrap._gcd_import(name[level:], package, level) +:1387: in _gcd_import + ??? +:1360: in _find_and_load + ??? +:1331: in _find_and_load_unlocked + ??? +:935: in _load_unlocked + ??? +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py:185: in exec_module + exec(co, module.__dict__) +tests/unit/test_cellprofiler_spreadsheet_export.py:29: in + from openhcs.core.pipeline.artifact_planning import artifact_producers_for_outputs +openhcs/core/pipeline/__init__.py:20: in + from openhcs.core.pipeline.funcstep_contract_validator import ( +openhcs/core/pipeline/funcstep_contract_validator.py:44: in + from openhcs.core.steps.function_step import FunctionStep +openhcs/core/steps/__init__.py:7: in + from openhcs.core.steps.abstract import AbstractStep +openhcs/core/steps/abstract.py:33: in + from openhcs.core.runtime_stores import ( +openhcs/core/runtime_stores.py:34: in + from openhcs.core.runtime_image_values import ( +openhcs/core/runtime_image_values.py:38: in + from openhcs.core.source_image_provenance import ( +openhcs/core/source_image_provenance.py:27: in + from openhcs.core.source_matching import ( +E File "/home/ts/wt/openhcs-paired-field-csv-314-20260930/openhcs/core/source_matching.py", line 494 +E if selector.component not in shared_components +E ^^ +E SyntaxError: invalid syntax +_____________ ERROR collecting tests/unit/test_source_bindings.py ______________ +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/python.py:493: in importtestmodule + mod = import_path( +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/pathlib.py:587: in import_path + importlib.import_module(module_name) +../../.pyenv/versions/3.12.3/lib/python3.12/importlib/__init__.py:90: in import_module + return _bootstrap._gcd_import(name[level:], package, level) +:1387: in _gcd_import + ??? +:1360: in _find_and_load + ??? +:1331: in _find_and_load_unlocked + ??? +:935: in _load_unlocked + ??? +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py:185: in exec_module + exec(co, module.__dict__) +tests/unit/test_source_bindings.py:35: in + from openhcs.core.pipeline.step_snapshot import StepSnapshot +openhcs/core/pipeline/__init__.py:20: in + from openhcs.core.pipeline.funcstep_contract_validator import ( +openhcs/core/pipeline/funcstep_contract_validator.py:44: in + from openhcs.core.steps.function_step import FunctionStep +openhcs/core/steps/__init__.py:7: in + from openhcs.core.steps.abstract import AbstractStep +openhcs/core/steps/abstract.py:33: in + from openhcs.core.runtime_stores import ( +openhcs/core/runtime_stores.py:34: in + from openhcs.core.runtime_image_values import ( +openhcs/core/runtime_image_values.py:38: in + from openhcs.core.source_image_provenance import ( +openhcs/core/source_image_provenance.py:27: in + from openhcs.core.source_matching import ( +E File "/home/ts/wt/openhcs-paired-field-csv-314-20260930/openhcs/core/source_matching.py", line 494 +E if selector.component not in shared_components +E ^^ +E SyntaxError: invalid syntax +_________ ERROR collecting tests/unit/test_source_image_provenance.py __________ +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/python.py:493: in importtestmodule + mod = import_path( +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/pathlib.py:587: in import_path + importlib.import_module(module_name) +../../.pyenv/versions/3.12.3/lib/python3.12/importlib/__init__.py:90: in import_module + return _bootstrap._gcd_import(name[level:], package, level) +:1387: in _gcd_import + ??? +:1360: in _find_and_load + ??? +:1331: in _find_and_load_unlocked + ??? +:935: in _load_unlocked + ??? +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py:185: in exec_module + exec(co, module.__dict__) +tests/unit/test_source_image_provenance.py:7: in + from openhcs.core.runtime_image_values import ( +openhcs/core/runtime_image_values.py:38: in + from openhcs.core.source_image_provenance import ( +openhcs/core/source_image_provenance.py:27: in + from openhcs.core.source_matching import ( +E File "/home/ts/wt/openhcs-paired-field-csv-314-20260930/openhcs/core/source_matching.py", line 494 +E if selector.component not in shared_components +E ^^ +E SyntaxError: invalid syntax +_ ERROR collecting tests/unit/test_cellprofiler_measurement_source_group_scope.py _ +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/python.py:493: in importtestmodule + mod = import_path( +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/pathlib.py:587: in import_path + importlib.import_module(module_name) +../../.pyenv/versions/3.12.3/lib/python3.12/importlib/__init__.py:90: in import_module + return _bootstrap._gcd_import(name[level:], package, level) +:1387: in _gcd_import + ??? +:1360: in _find_and_load + ??? +:1331: in _find_and_load_unlocked + ??? +:935: in _load_unlocked + ??? +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py:185: in exec_module + exec(co, module.__dict__) +tests/unit/test_cellprofiler_measurement_source_group_scope.py:26: in + from openhcs.core.measurement_row_materialization import ( +openhcs/core/measurement_row_materialization.py:31: in + from openhcs.core.runtime_measurements import ( +openhcs/core/runtime_measurements.py:17: in + from openhcs.core.source_image_provenance import ( +openhcs/core/source_image_provenance.py:27: in + from openhcs.core.source_matching import ( +E File "/home/ts/wt/openhcs-paired-field-csv-314-20260930/openhcs/core/source_matching.py", line 494 +E if selector.component not in shared_components +E ^^ +E SyntaxError: invalid syntax +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +=========================== short test summary info ============================ +ERROR tests/unit/test_cellprofiler_spreadsheet_export.py +ERROR tests/unit/test_source_bindings.py +ERROR tests/unit/test_source_image_provenance.py +ERROR tests/unit/test_cellprofiler_measurement_source_group_scope.py +!!!!!!!!!!!!!!!!!!! Interrupted: 4 errors during collection !!!!!!!!!!!!!!!!!!!! +2 warnings, 4 errors in 2.07s +Command exited with non-zero status 2 + Command being timed: "timeout 60s env OPENHCS_CPU_ONLY=true OPENHCS_BACKEND_EXCLUDE=torch,tensorflow,jax,cupy OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest tests/unit/test_cellprofiler_spreadsheet_export.py tests/unit/test_source_bindings.py tests/unit/test_source_image_provenance.py tests/unit/test_cellprofiler_measurement_source_group_scope.py -q --basetemp=/home/ts/.cache/agent-scratch/issue314-russell/pytest-fixed" + User time (seconds): 2.30 + System time (seconds): 0.11 + Percent of CPU this job got: 99% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:02.42 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 90572 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 23855 + Voluntary context switches: 40 + Involuntary context switches: 17 + Swaps: 0 + File system inputs: 448 + File system outputs: 48 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 2 diff --git a/docs/validation/paired_field_csv_314_20260930/ownership-receipt.rst b/docs/validation/paired_field_csv_314_20260930/ownership-receipt.rst new file mode 100644 index 0000000000..23ab08d78a --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/ownership-receipt.rst @@ -0,0 +1,158 @@ +Issue314: paired-field measurement identity +========================================= + +Head audited: OpenHCSDev/openhcs main702b2c2c9563da2636db251f8b734cff215596b8. +Fresh isolated branch fix/paired-field-csv-314-20260930 in +/home/ts/wt/openhcs-paired-field-csv-314-20260930. Parent integration owns final +source/MCP acceptance; Russell owns this source reproduction and repair. +H003e remains an immutable REJECT: no reads of its scientific inputs/outputs, +no rerun, tuning, CSV repair or freeze mutation for this task. + +Instruction review before production implementation +-------------------------------------------------- + +Read complete nra-refactoring SKILL.md (SHA256 +9f2f8b28bc82256eefa3e9d63248c50722dc3ffe7d77adba5793296df196b47e), complete latest +refactor-audit.skill ZIP entrypoint, pattern README, full identity/boundaries/ +membership/implementation references and surface-receipt template. Archive SHA +100fbe8ef89664b866777e87b2c8640a3432e8a10e9188dff81c97942d551bf6. +The following IDs are actual catalog IDs, not guessed NRA detector findings: + +* IDEN-1: source_matching.py:475..488 uses presence of a declared component + both as a biological plane address and as image-set plane membership. +* IDEN-6: image_set_numbering.py:45..55 keys the source identity produced by + that policy. Fully addressed DNA/Actin bindings make every source component + a plane member; SourceImageSetIdentity.from_metadata (:550..566) then falls + back to two different filenames. This is the earliest broken identity rung. +* BOUND-2 prevention: NamedSourceBinding.component_values() at + source_bindings.py:1015..1040 already owns assignment-versus-selector value + precedence. Use it instead of creating another coordinate/value reader. +* IMPL-1/2, MEMB-1/2/5 prevention: no axis-name/module-name strings, consumer + switch, parallel registry, policy mirror or duplicated row schema. Existing + AllComponents, ComponentSet, binding/policy/provenance/row owners suffice. + +Current-head census and proof scope +---------------------------------- + +Archive debt_census.py scanned all179 tracked core Python files at this head: +91295 code lines; no parse-failure warning. Core totals: string_dispatch7, +type_switch29, string_key_subscript274. These are syntax screens, not diagnoses +or a whole-repository architecture verdict. source_matching.py:824 code lines, +zero string dispatch/type switches/string-key subscripts, one four-term chain, +four foreign absence probes. Source witnesses, not these counts, establish314. +core-census.json/log and core-overlay.json/log retain unfiltered observations; +unrelated overlay leads are not assigned to this issue. + +Full NRA scan attempted with complete openhcs context, requested policy/numbering +files, --json --raw-findings --json-payload full, parse/analysis workers1, +--no-cache and an enforced512MiB/one-CPU/60s scope. It terminated exit124 with +maximum RSS524536KiB and an explicit deadline_incomplete JSON result: +complete=false, deadline_exceeded=true, stage=parse_python_module, +budget_seconds20.0, elapsed_seconds22.054. Original stderr/resource receipt retained. +Do not call this complete, zero omissions, R1 evidence, DSL preflight, native +proof, or formal equivalence. The confirmed stopping condition is the internal +parse deadline, not a demonstrated OOM. Observed process peak slightly exceeds +512MiB despite the scoped cap; a repeat is not admitted under this RAM budget. +Required resources for a complete context scan remain unknown; no global limit +was raised. There are no R1 +mapping_read/unmodeled_record_shape/redundant_type_check findings to certify. +This repair is an explicitly authored semantic patch, not an NRA-generated plan. + +Follow-up one-module compact loop reports79 detectors,0 omissions, complete=true +and zero findings for source_matching.py alone (nra-owner-loop.json). Do not +extend that completeness to the failed full-package context. Source commit +6f636c2afe6d98cc has +16 core code lines and zero debt-screen delta in every +other measured category; owner-census-delta.json retains exact observations. + +Original failing reproducer +--------------------------- + +New tiny synthetic fixture in test_cellprofiler_spreadsheet_export.py uses +two fields, two cells per field, full well/site/channel/z/timepoint addresses, +typed SourceImageProvenancePlanes, ordinary RuntimeExecutionAxisScope and +RuntimeArtifactBatch. It calls the real export_to_spreadsheet function. Literal +small feature rows stand for known synthetic measurements; this is not a +segmentation or installed/native-process test and reads no image files. + +Policy from fully addressed paired bindings loses four shared fixed axes as +well as the varying channel. Both primary/secondary features have the same +axis_id, local slice/object IDs; separate DNA/Actin paths cause duplicate rows. +Original built-source test fails with8 partial rows instead of4 complete rows +(reproducer-original-built.log), maxRSS311488KiB,3.96s. Initial collection failed +because the fresh checkout lacked _tabular_native; retained reproducer-original.log. +Built existing unchanged setup.py extension declarations locally (no installation, +JVM/server/viewer or dependency changes), maxRSS144720KiB,4.02s; source-build.log. +Owner-policy import was40MiB and resolved this worktree, policy-import.log. + +Required relation and owners +---------------------------- + +CellProfiler ImageNumber identifies a source IMAGE SET, not the currently +selected channel, measurement module, local slice position or provenance path +when semantic coordinates identify the set. A full plane address does not +declare every address axis to be a stack member. Across a paired primary-plane +declaration set, equal singleton component_values are fixed field coordinates; +different/unresolved coordinates remain plane selection dimensions under the +existing binding rule. Explicit source_stack_components and group_component +remain authoritative, even when their current coordinate happens to be equal. +One-plane binding contexts retain their existing selection semantics (there is +no cross-binding equality witness); no compatibility facade/reader is added. + +SourceImageSetIdentityPolicy.from_source_bindings owns the repair. It compiles +plate-wide via from_pipeline_config at compiler.py:207..209 and travels as the +same typed value through ProcessingContext and RuntimeArtifactBatch. Provenance +continues to retain original plane paths and coordinates. RuntimeExecutionAxisScope +keeps independent execution axes. CellProfilerImageSetNumbering consumes the +policy unchanged. MeasureObjectSizeShapeModule retains declared object identity +and feature semantics; spreadsheet_export.py:296..300/:398..413 continues its +ordinary typed row projection/union. No stored-format change or frozen migration. + +Ownership crossings and destination +----------------------------------- + +Open PRs309,217,207,206,160,125,110 checked with exact changed-file lists. PR206 +touches source_bindings.py; PR217 (Avicenna) touches runtime_image_values.py and +runtime_plane_projection.py. Those files stay read-only in this repair. +Existing binding/provenance owners are consumed, not edited or duplicated. +Notify Avicenna directly if any of those shared owner implementations become +necessary; do not compete with their worktree. No new coordinator or worker. + +Replace only from_source_bindings' unqualified component-collection expression +with a derivation that excludes shared fixed cross-binding coordinates from +binding-selected membership. Keep explicit stack/group membership. No new +carrier/store/registry. Destination new-case experiment: add another plane +binding or different semantic field coordinates through existing declarations; +the consumer/exporter changes0 times. Tests cover independent site/z/time/well +and execution identities, explicit stacked equal-coordinate axes, column/label +semantics and existing applicable source-level CellProfiler contracts. + +Done when the original behavioral failure passes, controls preserve separate +fields and declared stacks, applicable provider-free source tests pass, changed +owner census adds no dispatch/raw-record/schema mirrors, and one coherent draft +PR is visible with original failures and exact proof limits. Live/native/JVM, +installed-entrypoint, external-oracle and biological acceptance remain parent +owned and unclaimed. Local focused tests are not full acceptance. + +Resources and disposal +---------------------- + +Preflight: warning-only20GiB policy; home12.5GiB free, availableRAM15.6GiB, +historical swap8.7GiB. Worker512MiB and CPUQuota100percent/threads1 enforced; +owned scratch ceiling256MiB at /home/ts/.cache/agent-scratch/issue314-russell. +Source, failures and handoff stay in this persistent worktree. Archive/preserve +failure logs and remove only owned disposable caches/builds after workers exit. + +Durable .issue314/ (41MiB) holds the extracted current instruction archive and +original702b2c2c9 source snapshot for the inherited-failure control. It is local, +not a published source change. Disposable scratch measured652KiB; local build +objects400KiB. No task process remains running at this checkpoint. Existing +locally built extensions are not installed globally. Full test/dependency proof +limits and inherited fixture ownership are recorded in checkpoint.rst. + +Current merged-head receipt: e3d991e9cf02c496daa7c9df32c73bfde4a7ad00 integrates +main2b7969f700c33eb7f73f891d975afd1efc0628bb without changing shared owners. +merged-main-census.json compares those exact revisions: +16 code lines, zero +other syntax-screen deltas. Production/test checks at this head pass131 of139 +selected tests; eight inherited strict-subject fixture failures are retained and +assigned to parent integration for disposition, not masked. Owned scratch/build +disposal is complete and recoverably archived; checkpoint.rst records paths/SHA. diff --git a/docs/validation/paired_field_csv_314_20260930/pipeline-live-parent.py b/docs/validation/paired_field_csv_314_20260930/pipeline-live-parent.py new file mode 100644 index 0000000000..b30ec89770 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/pipeline-live-parent.py @@ -0,0 +1,107 @@ +"""Synthetic paired-channel exporter journey, not a blind biological trial.""" + +from pathlib import Path + +from openhcs.constants import AllComponents, GroupBy, InputSource, Microscope, VariableComponents +from openhcs.core.config import ( + LazyPathPlanningConfig, + LazyProcessingConfig, + LazySourceBindingsConfig, + LazyVFSConfig, + MaterializationBackend, + PipelineConfig, +) +from openhcs.core.source_bindings import ( + ComponentSelector, MetadataExtractionRule, MetadataSource, + NamedSourceBinding, SourceFilterClause, SourceFilterMatchType, + SourceFilterSubject, SourceSelector, LazyStepSourceBindingsConfig, +) +from openhcs.core.steps.function_step import FunctionStep +from openhcs.processing.backends.cellprofiler.object_images import convert_image_to_objects +from openhcs.processing.backends.cellprofiler.secondary import SecondaryMethod, identify_secondary_objects +from openhcs.processing.backends.cellprofiler.shape import measure_object_size_shape +from openhcs.processing.backends.cellprofiler.spreadsheet_export import export_to_spreadsheet + +pipeline_config = PipelineConfig( + num_workers=1, + use_threading=True, + microscope=Microscope.IMAGEXPRESS, + processing_config=LazyProcessingConfig( + variable_components=[VariableComponents.SITE], group_by=GroupBy.CHANNEL, + ), + source_bindings_config=LazySourceBindingsConfig( + metadata_rules=(MetadataExtractionRule( + source=MetadataSource.FILE_NAME, + pattern=r"(?P[A-Z]\d{2})_s(?P\d+)_w(?P\d+)_z(?P\d+)_t(?P\d+)", + ),), + bindings=tuple( + NamedSourceBinding( + alias=alias, + selector=SourceSelector( + components=(ComponentSelector(AllComponents.CHANNEL, channel),), + filters=(SourceFilterClause( + SourceFilterSubject.FILE, SourceFilterMatchType.CONTAINS, + f"_w{channel}_", + ),), + ), + component_identity=tuple( + ComponentSelector(component, value) + for component, value in ( + (AllComponents.CHANNEL, channel), + (AllComponents.SITE, "1"), + (AllComponents.Z_INDEX, "1"), + (AllComponents.TIMEPOINT, "1"), + ) + ), + ) + for alias, channel in (("DNA", "1"), ("Actin", "2")) + ), + ), + path_planning_config=LazyPathPlanningConfig( + global_output_folder=Path("/home/ts/wt/openhcs-issue-batch-20260929/paired-field-parent-20260930/outputs-input-source-fixed"), + # This journey's durable result is the typed CSV bundle, not raw copies. + well_filter=0, + ), + vfs_config=LazyVFSConfig(materialization_backend=MaterializationBackend.DISK), +) +pipeline_steps = [ + FunctionStep( + name="KnownNuclei", + source_bindings=LazyStepSourceBindingsConfig( + enabled=True, + bindings=(NamedSourceBinding(alias="DNA"),), + ), + func={"1": (convert_image_to_objects, { + "select_the_input_image": "DNA", "name_the_output_objects": "Nuclei", + "cast_to_bool": True, + })}, + ), + FunctionStep( + name="KnownCells", + processing_config=LazyProcessingConfig(input_source=InputSource.PIPELINE_START), + source_bindings=LazyStepSourceBindingsConfig( + enabled=True, + bindings=(NamedSourceBinding(alias="Actin"),), + ), + func={"2": (identify_secondary_objects, { + "select_the_input_image": "Actin", "select_the_input_objects": "Nuclei", + "name_the_objects_to_be_identified": "Cells", "method": SecondaryMethod.DISTANCE_N, + "distance_to_dilate": 2, + })}, + ), + FunctionStep( + name="CellShapes", + source_bindings=LazyStepSourceBindingsConfig( + enabled=True, + bindings=(NamedSourceBinding(alias="Actin"),), + ), + func={"2": (measure_object_size_shape, { + "select_object_sets_to_measure": "Cells", + "calculate_advanced": False, "calculate_zernikes": False, + })}, + ), + FunctionStep( + name="CompleteCellRows", + func=(export_to_spreadsheet, {"add_filename_prefix": False}), + ), +] diff --git a/docs/validation/paired_field_csv_314_20260930/policy-import.log b/docs/validation/paired_field_csv_314_20260930/policy-import.log new file mode 100644 index 0000000000..90b2be6324 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/policy-import.log @@ -0,0 +1,24 @@ +/home/ts/wt/openhcs-paired-field-csv-314-20260930/openhcs/__init__.py openhcs.core.source_matching + Command being timed: "env OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 OPENHCS_CPU_ONLY=true /home/ts/code/projects/openhcs/.venv/bin/python -B -c from openhcs.core.source_matching import SourceImageSetIdentityPolicy; import openhcs; print(openhcs.__file__, SourceImageSetIdentityPolicy.__module__)" + User time (seconds): 0.35 + System time (seconds): 0.05 + Percent of CPU this job got: 104% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.39 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 40452 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 9470 + Voluntary context switches: 24 + Involuntary context switches: 2 + Swaps: 0 + File system inputs: 16 + File system outputs: 8 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 diff --git a/docs/validation/paired_field_csv_314_20260930/pr-body.md b/docs/validation/paired_field_csv_314_20260930/pr-body.md new file mode 100644 index 0000000000..4455fdfddc --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/pr-body.md @@ -0,0 +1,18 @@ +## Summary + +Refs #314. Source-only repair, originally reproduced on `702b2c2c9`, now normally merged with current main `2b7969f700c33eb7f73f891d975afd1efc0628bb`. Parent owns integration and final installed/MCP acceptance. + +- Fix the earliest original owner, `SourceImageSetIdentityPolicy.from_source_bindings`: shared singleton binding coordinates constrain the paired field rather than becoming plane-member axes. Explicit source-stack and group declarations retain precedence. +- Consume existing `NamedSourceBinding.component_values()` authority; no CSV joining, axis/module switches, parallel registries, mirrors, compatibility reader, measurement-module workaround, or shared-file edit. +- Reproduce through the real spreadsheet exporter using tiny synthetic typed paired fields. Original source produces eight partial rows instead of four complete rows; repaired source combines lineage/location and shape while keeping fields distinct. +- Preserve H003e as immutable REJECT; no frozen data/output access, rerun, retune or mutation. + +## Evidence and limits + +On merged source head `e3d991e9cf02c496daa7c9df32c73bfde4a7ad00`, **131 distinct source tests pass; eight inherited fixture failures remain**. Shards: 101 passed (14.13s, RSS406828KiB), then30 passed/8 failed (6.01s, RSS387968KiB), under an enforced512MiB / one CPU / one numeric-thread scope. Covers source identity, real exporter/counts, pipeline-config propagation, provenance/group scope, independent well/site/z/time, explicit stack/group overrides, third-channel extension, assignment precedence, missing-metadata paths and applicable shape/secondary/module contracts. Source/test `git diff --check` passes; raw logs deliberately retain diagnostic whitespace. + +The eight failures are synthetic matched-anchor fixtures lacking the strict `MeasurementSubjectRelation` required by main's existing artifact owner, before the repaired policy runs. All eight reproduce on untouched original main (8 failed/3 passed); affected fixtures/guard are unchanged in new main. Parent integration owns their disposition. No guard/assertion was relaxed; this is not a green broad suite or issue closure. + +Original collection/build, behavioral split, patch-syntax and baseline-harness assembly failures are retained. Full-context NRA returned an explicit deadline-incomplete receipt (exit124, parse stage, internal20s deadline, elapsed22.054s; peak RSS524536KiB); it was not an empty result or proven OOM. No complete package NRA/R1/DSL/native-equivalence proof claimed and no global limit raised. A one-module compact loop completed with79 detectors/0 omitted/0 findings, scoped only to the unchanged policy module. Exact merged-head core census shows +16 code lines and zero other measured debt-screen deltas. Actual pattern IDs/source witnesses, commands, dependency import qualifications and original failures are published under `docs/validation/paired_field_csv_314_20260930/`. Disposable owned scratch/builds are archived and removed; `SHA256SUMS` seals evidence/source bytes. + +Shared owners remain read-only; [direct Avicenna coordination](https://github.com/OpenHCSDev/openhcs/pull/217#issuecomment-5920836946) identifies the boundary. No new agent/model/provider or installation. Local unchanged extension build only; no MCP/GUI/native-process/JVM/external-oracle or biological acceptance. Parent owns those final gates. This draft does not close #314 or claim live readiness. diff --git a/docs/validation/paired_field_csv_314_20260930/related-owner-contracts.log b/docs/validation/paired_field_csv_314_20260930/related-owner-contracts.log new file mode 100644 index 0000000000..f1bdfead08 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/related-owner-contracts.log @@ -0,0 +1,659 @@ +.................FFFFFF..FF........... [100%] +=================================== FAILURES =================================== +_____ test_all_loaded_contract_collapses_sibling_alias_groups_by_metadata ______ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7ff9a670d040> + + def test_all_loaded_contract_collapses_sibling_alias_groups_by_metadata( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=tuple(alias for alias, _channel in SOURCE_ALIASES), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:270: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n...e'>, name='OrigSyto'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +___________ test_natural_callable_keeps_each_source_component_anchor ___________ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7ff9a70d13a0> + + def test_natural_callable_keeps_each_source_component_anchor( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=tuple(alias for alias, _channel in SOURCE_ALIASES), + composed_image_set=False, + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:287: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n...e'>, name='OrigSyto'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +_______ test_selected_source_contract_keeps_only_its_exact_alias_anchor ________ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7ff9a670fce0> + + def test_selected_source_contract_keeps_only_its_exact_alias_anchor( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=("OrigHoechst",), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:302: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n... name='OrigHoechst')),), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +_______ test_distinct_metadata_sets_each_keep_one_source_representative ________ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7ff9a70d1a90> + + def test_distinct_metadata_sets_each_keep_one_source_representative( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=tuple(alias for alias, _channel in SOURCE_ALIASES), + sites=(1, 2), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:315: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n...e'>, name='OrigSyto'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +______ test_independent_source_aligned_outputs_keep_each_execution_group _______ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7ff9a670c650> + + def test_independent_source_aligned_outputs_keep_each_execution_group( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=("OrigER", "OrigHoechst"), + shared_output=False, + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:336: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='OrigERMeasurements', artifact_type=, param...pe'>, name='OrigER')),), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='OrigERMeasurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +______ test_missing_alias_in_match_dimension_keeps_existing_strict_error _______ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7ff9a62abbc0> + + def test_missing_alias_in_match_dimension_keeps_existing_strict_error( + monkeypatch: pytest.MonkeyPatch, + ) -> None: + with pytest.raises( + ValueError, + match="METADATA source binding dimension is missing alias 'OrigER'", + ): +> _filter_source_anchors( + monkeypatch, + artifact_aliases=tuple(alias for alias, _channel in SOURCE_ALIASES), + dimension_aliases=tuple( + alias for alias, _channel in SOURCE_ALIASES if alias != "OrigER" + ), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:355: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n...e'>, name='OrigSyto'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError + +During handling of the above exception, another exception occurred: + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7ff9a62abbc0> + + def test_missing_alias_in_match_dimension_keeps_existing_strict_error( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> with pytest.raises( + ValueError, + match="METADATA source binding dimension is missing alias 'OrigER'", + ): +E AssertionError: Regex pattern did not match. +E Regex: "METADATA source binding dimension is missing alias 'OrigER'" +E Input: "Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + +tests/unit/test_source_bound_matched_image_set_anchors.py:351: AssertionError +___ test_complete_source_set_templates_preserve_each_execution_group_anchor ____ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7ff9a62ab9e0> + + def test_complete_source_set_templates_preserve_each_execution_group_anchor( + monkeypatch: pytest.MonkeyPatch, + ) -> None: + bindings = tuple( + NamedSourceBinding( + alias=alias, + selector=SourceSelector( + components=(ComponentSelector(AllComponents.CHANNEL, channel),), + ), + origin=SourceBindingOrigin.PIPELINE_START, + component_identity=(ComponentSelector(AllComponents.CHANNEL, channel),), + ) + for alias, channel in (("OrigStain1", "1"), ("OrigStain2", "2")) + ) + input_specs = tuple(binding.input_spec() for binding in bindings) + measurements = ArtifactSpec.output( + "SourceSetMeasurements", + MeasurementsArtifactType, + relations=tuple( + GroupLineageSourceRelation(source=spec.ref()) for spec in input_specs + ), + ) + + @artifact_inputs(*input_specs) + @artifact_outputs(measurements) + @composed_image_payload + def align_source_set(image): + return image + + output_plan = ArtifactOutputPlan( + name=measurements.name, + path="/memory/SourceSetMeasurements.pkl", + artifact_type=measurements.artifact_type, + group_keys=("1", "2"), + group_component=AllComponents.SITE, + relations=measurements.relations, + ) + plan = SimpleNamespace( + axis_id="A01", + step_index=0, + step_name="Align", + main_input_dependency=StepInputDependency.pipeline_start(), + source_binding_plan=CompiledSourceBindingPlan( + bindings=bindings, + match_plan=SourceBindingMatchPlan( + method=SourceBindingMatchMethod.ORDER, + ), + ), + execution_group_scope=ComponentGroupScope.from_raw( + ("1", "2"), + component=AllComponents.SITE, + ), +> compiled_function_pattern=compile_function_pattern( + align_source_set, + {}, + {output_plan.ref(): output_plan}, + ), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:582: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='SourceSetMeasurements', artifact_type=, pa...>, name='OrigStain2'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='SourceSetMeasurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +__ test_static_site_groups_do_not_cross_project_natural_source_set_templates ___ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7ff9a670e990> + + def test_static_site_groups_do_not_cross_project_natural_source_set_templates( + monkeypatch: pytest.MonkeyPatch, + ) -> None: + bindings = tuple( + NamedSourceBinding( + alias=alias, + selector=SourceSelector( + filters=(), + ), + origin=SourceBindingOrigin.PIPELINE_START, + component_identity=(ComponentSelector(AllComponents.CHANNEL, channel),), + ) + for alias, channel in (("OrigStain1", "1"), ("OrigStain2", "2")) + ) + input_specs = tuple(binding.input_spec() for binding in bindings) + output_specs = ( + ArtifactSpec.output( + "Stain1", + ImageArtifactType, + relations=(GroupLineageSourceRelation(source=input_specs[0].ref()),), + ), + ArtifactSpec.output( + "Stain2", + ImageArtifactType, + relations=(GroupLineageSourceRelation(source=input_specs[1].ref()),), + ), + ArtifactSpec.output( + "AlignMeasurements", + MeasurementsArtifactType, + relations=tuple( + GroupLineageSourceRelation(source=spec.ref()) for spec in input_specs + ), + ), + ) + + @artifact_inputs(*input_specs) + @artifact_outputs(*output_specs) + def align_source_set(image): + return image + + output_plans = { + spec.ref(): ArtifactOutputPlan( + name=spec.name, + path=f"/memory/{spec.name}.pkl", + artifact_type=spec.artifact_type, + group_keys=("1", "2"), + group_component=AllComponents.SITE, + relations=spec.relations, + ) + for spec in output_specs + } + plan = SimpleNamespace( + axis_id="A01", + step_index=0, + step_name="Align", + main_input_dependency=StepInputDependency.pipeline_start(), + source_binding_plan=CompiledSourceBindingPlan( + bindings=bindings, + match_plan=SourceBindingMatchPlan( + method=SourceBindingMatchMethod.ORDER, + ), + ), + execution_group_scope=ComponentGroupScope.from_raw( + ("1", "2"), + component=AllComponents.SITE, + ), +> compiled_function_pattern=compile_function_pattern( + align_source_set, + {}, + output_plans, + ), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:711: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='AlignMeasurements', artifact_type=, parame...>, name='OrigStain2'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='AlignMeasurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +=========================== short test summary info ============================ +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_all_loaded_contract_collapses_sibling_alias_groups_by_metadata +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_natural_callable_keeps_each_source_component_anchor +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_selected_source_contract_keeps_only_its_exact_alias_anchor +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_distinct_metadata_sets_each_keep_one_source_representative +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_independent_source_aligned_outputs_keep_each_execution_group +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_missing_alias_in_match_dimension_keeps_existing_strict_error +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_complete_source_set_templates_preserve_each_execution_group_anchor +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_static_site_groups_do_not_cross_project_natural_source_set_templates +8 failed, 30 passed, 2 warnings in 25.59s +Command exited with non-zero status 1 + Command being timed: "timeout 60s env OPENHCS_CPU_ONLY=true OPENHCS_BACKEND_EXCLUDE=torch,tensorflow,jax,cupy OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest tests/unit/test_cellprofiler_shape_hotpath.py tests/unit/test_cellprofiler_module_measurement_scopes.py tests/unit/test_cellprofiler_identify_secondary_replacement_primary.py tests/unit/test_cellprofiler_secondary_distance_backend.py tests/unit/test_source_bound_matched_image_set_anchors.py tests/unit/test_source_image_provenance_hotpath.py tests/unit/test_function_output_manifest_input_scope.py -q --basetemp=/home/ts/.cache/agent-scratch/issue314-russell/pytest-parity" + User time (seconds): 24.36 + System time (seconds): 2.18 + Percent of CPU this job got: 99% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:26.70 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 518188 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 390812 + Voluntary context switches: 41 + Involuntary context switches: 146 + Swaps: 0 + File system inputs: 184 + File system outputs: 5600 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 1 diff --git a/docs/validation/paired_field_csv_314_20260930/reproducer-original-built.log b/docs/validation/paired_field_csv_314_20260930/reproducer-original-built.log new file mode 100644 index 0000000000..a1ca749ad0 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/reproducer-original-built.log @@ -0,0 +1,125 @@ +F [100%] +=================================== FAILURES =================================== +_____ test_export_to_spreadsheet_pairs_fully_addressed_field_measurements ______ + + def test_export_to_spreadsheet_pairs_fully_addressed_field_measurements() -> None: + """A biological address is not a declaration to stack every addressed axis.""" + bindings = SourceBindingsConfig( + bindings=tuple( + NamedSourceBinding( + alias=alias, + component_identity=tuple( + ComponentSelector(component, value) + for component, value in ( + (AllComponents.WELL, "A01"), + (AllComponents.SITE, "1"), + (AllComponents.CHANNEL, channel), + (AllComponents.Z_INDEX, "1"), + (AllComponents.TIMEPOINT, "1"), + ) + ), + ) + for alias, channel in (("DNA", "1"), ("Actin", "2")) + ) + ) + policy = SourceImageSetIdentityPolicy.from_source_bindings( + bindings, + group_component=AllComponents.CHANNEL, + ) + records = [] + # Two independent fields, each with two cells. Local slice/object IDs repeat + # intentionally: provenance, not runtime row position, owns field identity. + for site in ("1", "2"): + for channel, features in ( + ("1", {"Parent_Nuclei": 1, "Location_Center_X": 1.5}), + ("2", {"AreaShape_Area": 4.0}), + ): + provenance = SourceImageProvenancePlanes.from_components( + paths=(f"/synthetic/A01-field{site}-plane{channel}.tif",), + component_metadata=({ + "well": "A01", "site": site, "channel": channel, + "z_index": "1", "timepoint": "1", + },), + ) + records.append(_measurement_record( + f"field{site}_plane{channel}", + axis_id="A01", + subject=MeasurementSubject(MeasurementScope.OBJECT, "Cells", "object_number"), + rows=tuple({"slice_index": 0, "object_number": number, **features} + for number in (1, 2)), + source_image_provenance_planes=provenance, + group_component=AllComponents.CHANNEL, + group_key=channel, + variable_components=(AllComponents.SITE,), + )) + records.append(_measurement_record( + f"field{site}_counts", + axis_id="A01", + subject=MeasurementSubject(MeasurementScope.IMAGE, "Image"), + rows=({"slice_index": 0, "Count_Cells": 2},), + source_image_provenance_planes=provenance, + group_component=AllComponents.CHANNEL, + group_key="2", + variable_components=(AllComponents.SITE,), + )) + batch = RuntimeArtifactBatch( + input_specs=tuple(ArtifactSpec.input(record.key.name, MeasurementsArtifactType) + for record in records), + records_by_axis={"A01": tuple(records)}, + source_image_set_identity_policy=policy, + ) + + bundle = export_to_spreadsheet(add_filename_prefix=False, artifact_batch=batch) + + cells = tuple(csv.DictReader(io.StringIO(bundle["Cells.csv"]))) +> assert cells == tuple( + {"image_number": image_number, "object_label": object_number, + "Parent_Nuclei": "1", "Location_Center_X": "1.5", "AreaShape_Area": "4.0"} + for image_number in ("1", "2") for object_number in ("1", "2") + ) +E AssertionError: assert ({'AreaShape_...3', ...}, ...) == ({'AreaShape_...r': '2', ...}) +E +E At index 0 diff: {'image_number': '1', 'object_label': '1', 'Parent_Nuclei': '1', 'Location_Center_X': '1.5', 'AreaShape_Area': ''} != {'image_number': '1', 'object_label': '1', 'Parent_Nuclei': '1', 'Location_Center_X': '1.5', 'AreaShape_Area': '4.0'} +E Left contains 4 more items, first extra item: {'AreaShape_Area': '', 'Location_Center_X': '1.5', 'Parent_Nuclei': '1', 'image_number': '3', ...} +E Use -v to get more diff + +tests/unit/test_cellprofiler_spreadsheet_export.py:641: AssertionError +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +=========================== short test summary info ============================ +FAILED tests/unit/test_cellprofiler_spreadsheet_export.py::test_export_to_spreadsheet_pairs_fully_addressed_field_measurements +1 failed, 2 warnings in 3.28s +Command exited with non-zero status 1 + Command being timed: "timeout 60s env OPENHCS_CPU_ONLY=true OPENHCS_BACKEND_EXCLUDE=torch,tensorflow,jax,cupy OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest tests/unit/test_cellprofiler_spreadsheet_export.py::test_export_to_spreadsheet_pairs_fully_addressed_field_measurements -q --basetemp=/home/ts/.cache/agent-scratch/issue314-russell/pytest-original" + User time (seconds): 3.57 + System time (seconds): 0.27 + Percent of CPU this job got: 96% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:03.96 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 311488 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 12 + Minor (reclaiming a frame) page faults: 49055 + Voluntary context switches: 251 + Involuntary context switches: 21 + Swaps: 0 + File system inputs: 11520 + File system outputs: 40 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 1 diff --git a/docs/validation/paired_field_csv_314_20260930/reproducer-original.log b/docs/validation/paired_field_csv_314_20260930/reproducer-original.log new file mode 100644 index 0000000000..3ae63c2bd5 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/reproducer-original.log @@ -0,0 +1,66 @@ +ERROR: found no collectors for /home/ts/wt/openhcs-paired-field-csv-314-20260930/tests/unit/test_cellprofiler_spreadsheet_export.py::test_export_to_spreadsheet_pairs_fully_addressed_field_measurements + + +==================================== ERRORS ==================================== +_____ ERROR collecting tests/unit/test_cellprofiler_spreadsheet_export.py ______ +ImportError while importing test module '/home/ts/wt/openhcs-paired-field-csv-314-20260930/tests/unit/test_cellprofiler_spreadsheet_export.py'. +Hint: make sure your test modules/packages have valid Python names. +Traceback: +../../.pyenv/versions/3.12.3/lib/python3.12/importlib/__init__.py:90: in import_module + return _bootstrap._gcd_import(name[level:], package, level) +tests/unit/test_cellprofiler_spreadsheet_export.py:29: in + from openhcs.core.pipeline.artifact_planning import artifact_producers_for_outputs +openhcs/core/pipeline/__init__.py:20: in + from openhcs.core.pipeline.funcstep_contract_validator import ( +openhcs/core/pipeline/funcstep_contract_validator.py:44: in + from openhcs.core.steps.function_step import FunctionStep +openhcs/core/steps/__init__.py:7: in + from openhcs.core.steps.abstract import AbstractStep +openhcs/core/steps/abstract.py:33: in + from openhcs.core.runtime_stores import ( +openhcs/core/runtime_stores.py:41: in + from openhcs.core.runtime_slice_projection import RuntimeSliceProjection +openhcs/core/runtime_slice_projection.py:18: in + from openhcs.core.measurement_row_materialization import MeasurementRowsAxisProjection +openhcs/core/measurement_row_materialization.py:23: in + from openhcs.core._tabular_native import ( +E ModuleNotFoundError: No module named 'openhcs.core._tabular_native' +=============================== warnings summary =============================== +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +=========================== short test summary info ============================ +ERROR tests/unit/test_cellprofiler_spreadsheet_export.py +2 warnings, 1 error in 1.05s +Command exited with non-zero status 4 + Command being timed: "timeout 60s env OPENHCS_CPU_ONLY=true OPENHCS_BACKEND_EXCLUDE=torch,tensorflow,jax,cupy OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest tests/unit/test_cellprofiler_spreadsheet_export.py::test_export_to_spreadsheet_pairs_fully_addressed_field_measurements -q --basetemp=/home/ts/.cache/agent-scratch/issue314-russell/pytest-first" + User time (seconds): 1.32 + System time (seconds): 0.10 + Percent of CPU this job got: 100% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:01.41 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 87464 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 21322 + Voluntary context switches: 26 + Involuntary context switches: 6 + Swaps: 0 + File system inputs: 0 + File system outputs: 56 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 4 diff --git a/docs/validation/paired_field_csv_314_20260930/ruff-focused.log b/docs/validation/paired_field_csv_314_20260930/ruff-focused.log new file mode 100644 index 0000000000..79cdd68585 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/ruff-focused.log @@ -0,0 +1,21 @@ +E501 Line too long (89 > 88) + --> tests/unit/test_cellprofiler_spreadsheet_export.py:119:89 + | +119 | def test_export_to_spreadsheet_contract_selects_ordered_tables_and_declares_bundle() -> ( + | ^ +120 | None +121 | ): + | + +E501 Line too long (93 > 88) + --> tests/unit/test_cellprofiler_spreadsheet_export.py:188:89 + | +186 | ), +187 | ( +188 | "Calculate the per-image standard deviation values for object measurements?", + | ^^^^^ +189 | "No", +190 | ), + | + +Found 2 errors. diff --git a/docs/validation/paired_field_csv_314_20260930/run-installed-parent.sh b/docs/validation/paired_field_csv_314_20260930/run-installed-parent.sh new file mode 100644 index 0000000000..65efa139bd --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/run-installed-parent.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# Parent owns the installed candidate and this isolated MCP acceptance slot. +set -eu +unset PYTHONPATH +export PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 +export OPENHCS_CPU_ONLY=true OPENHCS_SUBPROCESS_NO_GPU=1 POLYSTORE_SUBPROCESS_NO_GPU=1 +export OPENHCS_USE_THREADING=true OPENHCS_HEADLESS=true CUDA_VISIBLE_DEVICES= +export JAX_PLATFORMS=cpu XLA_FLAGS=--xla_cpu_multi_thread_eigen=false +export OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMBA_NUM_THREADS=1 +export QT_QPA_PLATFORM=xcb DISPLAY=:91 LIBGL_ALWAYS_SOFTWARE=1 +export POLYSTORE_IMAGEJ_CACHE_ROOT=/home/ts/.cache/polystore/imagej +export POLYSTORE_IMAGEJ_ALLOW_DOWNLOAD=false +export XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/paired-field-parent-20260930/cache +export XDG_CONFIG_HOME=/home/ts/.cache/agent-scratch/paired-field-parent-20260930/config +export XDG_DATA_HOME=/home/ts/.cache/agent-scratch/paired-field-parent-20260930/data +export XDG_STATE_HOME=/home/ts/.cache/agent-scratch/paired-field-parent-20260930/state +export XDG_RUNTIME_DIR=/home/ts/.cache/agent-scratch/paired-field-parent-20260930/runtime +export TMPDIR=/home/ts/.cache/agent-scratch/paired-field-parent-20260930 +export NUMBA_CACHE_DIR=/home/ts/.cache/agent-scratch/paired-field-parent-20260930/numba +export MPLCONFIGDIR=/home/ts/.cache/agent-scratch/paired-field-parent-20260930/matplotlib +export OPENHCS_UI_CONFIG_CACHE_FILE=/home/ts/.cache/agent-scratch/paired-field-parent-20260930/ui_config.config +export OPENHCS_AGENT_READ_ROOTS=/home/ts/wt/openhcs-viewer-reset-parent-integration-20260930:/home/ts/wt/openhcs-issue-batch-20260929/paired-field-parent-20260930:/home/ts/.cache/agent-scratch/paired-field-parent-20260930:/home/ts/.openhcs/tcp/5997.startup.lock:/home/ts/.openhcs/tcp/6997.startup.lock +export OPENHCS_AGENT_WRITE_ROOTS=/home/ts/wt/openhcs-issue-batch-20260929/paired-field-parent-20260930:/home/ts/.cache/agent-scratch/paired-field-parent-20260930:/home/ts/.openhcs/tcp/5997.startup.lock:/home/ts/.openhcs/tcp/6997.startup.lock +cd /home/ts/wt/basicpy-live-candidate-20260930 +exec /home/ts/wt/basicpy-live-candidate-20260930/.venv/bin/python -B "$@" diff --git a/docs/validation/paired_field_csv_314_20260930/source-anchors-base-empty-snapshot.log b/docs/validation/paired_field_csv_314_20260930/source-anchors-base-empty-snapshot.log new file mode 100644 index 0000000000..4a8efd8056 --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/source-anchors-base-empty-snapshot.log @@ -0,0 +1,77 @@ +Traceback (most recent call last): + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 858, in import_plugin + __import__(importspec) +ModuleNotFoundError: No module named 'tests' + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "", line 198, in _run_module_as_main + File "", line 88, in _run_code + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pytest/__main__.py", line 9, in + raise SystemExit(pytest.console_main()) + ^^^^^^^^^^^^^^^^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 201, in console_main + code = main() + ^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 156, in main + config = _prepareconfig(args, plugins) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 341, in _prepareconfig + config = pluginmanager.hook.pytest_cmdline_parse( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pluggy/_hooks.py", line 512, in __call__ + return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pluggy/_manager.py", line 120, in _hookexec + return self._inner_hookexec(hook_name, methods, kwargs, firstresult) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 167, in _multicall + raise exception + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall + teardown.throw(exception) + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/helpconfig.py", line 105, in pytest_cmdline_parse + config = yield + ^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 121, in _multicall + res = hook_impl.function(*args) + ^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 1140, in pytest_cmdline_parse + self.parse(args) + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 1494, in parse + self._preparse(args, addopts=addopts) + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 1377, in _preparse + self.pluginmanager.consider_preparse(args, exclude_only=False) + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 787, in consider_preparse + self.consider_pluginarg(parg) + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 810, in consider_pluginarg + self.import_plugin(arg, consider_entry_points=True) + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 860, in import_plugin + raise ImportError( + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 858, in import_plugin + __import__(importspec) +ImportError: Error importing plugin "tests.pytest_integration_options": No module named 'tests' +Command exited with non-zero status 1 + Command being timed: "timeout 60s env OPENHCS_CPU_ONLY=true OPENHCS_BACKEND_EXCLUDE=torch,tensorflow,jax,cupy OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest tests/unit/test_source_bound_matched_image_set_anchors.py -q --basetemp=/home/ts/.cache/agent-scratch/issue314-russell/pytest-base-controls" + User time (seconds): 0.18 + System time (seconds): 0.03 + Percent of CPU this job got: 110% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.19 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 26860 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 7128 + Voluntary context switches: 28 + Involuntary context switches: 1 + Swaps: 0 + File system inputs: 48 + File system outputs: 8 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 1 diff --git a/docs/validation/paired_field_csv_314_20260930/source-anchors-base-verified.log b/docs/validation/paired_field_csv_314_20260930/source-anchors-base-verified.log new file mode 100644 index 0000000000..379ff18faf --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/source-anchors-base-verified.log @@ -0,0 +1,659 @@ +.FFFFFF..FF [100%] +=================================== FAILURES =================================== +_____ test_all_loaded_contract_collapses_sibling_alias_groups_by_metadata ______ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fddda9758b0> + + def test_all_loaded_contract_collapses_sibling_alias_groups_by_metadata( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=tuple(alias for alias, _channel in SOURCE_ALIASES), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:270: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n...e'>, name='OrigSyto'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +___________ test_natural_callable_keeps_each_source_component_anchor ___________ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fddda8e6630> + + def test_natural_callable_keeps_each_source_component_anchor( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=tuple(alias for alias, _channel in SOURCE_ALIASES), + composed_image_set=False, + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:287: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n...e'>, name='OrigSyto'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +_______ test_selected_source_contract_keeps_only_its_exact_alias_anchor ________ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fddda8e4fb0> + + def test_selected_source_contract_keeps_only_its_exact_alias_anchor( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=("OrigHoechst",), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:302: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n... name='OrigHoechst')),), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +_______ test_distinct_metadata_sets_each_keep_one_source_representative ________ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fddda8dd460> + + def test_distinct_metadata_sets_each_keep_one_source_representative( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=tuple(alias for alias, _channel in SOURCE_ALIASES), + sites=(1, 2), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:315: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n...e'>, name='OrigSyto'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +______ test_independent_source_aligned_outputs_keep_each_execution_group _______ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fddda8d9880> + + def test_independent_source_aligned_outputs_keep_each_execution_group( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> filtered = _filter_source_anchors( + monkeypatch, + artifact_aliases=("OrigER", "OrigHoechst"), + shared_output=False, + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:336: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='OrigERMeasurements', artifact_type=, param...pe'>, name='OrigER')),), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='OrigERMeasurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +______ test_missing_alias_in_match_dimension_keeps_existing_strict_error _______ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fddda8e5100> + + def test_missing_alias_in_match_dimension_keeps_existing_strict_error( + monkeypatch: pytest.MonkeyPatch, + ) -> None: + with pytest.raises( + ValueError, + match="METADATA source binding dimension is missing alias 'OrigER'", + ): +> _filter_source_anchors( + monkeypatch, + artifact_aliases=tuple(alias for alias, _channel in SOURCE_ALIASES), + dimension_aliases=tuple( + alias for alias, _channel in SOURCE_ALIASES if alias != "OrigER" + ), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:355: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +tests/unit/test_source_bound_matched_image_set_anchors.py:208: in _filter_source_anchors + compiled_pattern = compile_function_pattern( +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='Measurements', artifact_type=, parameter_n...e'>, name='OrigSyto'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError + +During handling of the above exception, another exception occurred: + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fddda8e5100> + + def test_missing_alias_in_match_dimension_keeps_existing_strict_error( + monkeypatch: pytest.MonkeyPatch, + ) -> None: +> with pytest.raises( + ValueError, + match="METADATA source binding dimension is missing alias 'OrigER'", + ): +E AssertionError: Regex pattern did not match. +E Regex: "METADATA source binding dimension is missing alias 'OrigER'" +E Input: "Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='Measurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + +tests/unit/test_source_bound_matched_image_set_anchors.py:351: AssertionError +___ test_complete_source_set_templates_preserve_each_execution_group_anchor ____ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fddda8dddc0> + + def test_complete_source_set_templates_preserve_each_execution_group_anchor( + monkeypatch: pytest.MonkeyPatch, + ) -> None: + bindings = tuple( + NamedSourceBinding( + alias=alias, + selector=SourceSelector( + components=(ComponentSelector(AllComponents.CHANNEL, channel),), + ), + origin=SourceBindingOrigin.PIPELINE_START, + component_identity=(ComponentSelector(AllComponents.CHANNEL, channel),), + ) + for alias, channel in (("OrigStain1", "1"), ("OrigStain2", "2")) + ) + input_specs = tuple(binding.input_spec() for binding in bindings) + measurements = ArtifactSpec.output( + "SourceSetMeasurements", + MeasurementsArtifactType, + relations=tuple( + GroupLineageSourceRelation(source=spec.ref()) for spec in input_specs + ), + ) + + @artifact_inputs(*input_specs) + @artifact_outputs(measurements) + @composed_image_payload + def align_source_set(image): + return image + + output_plan = ArtifactOutputPlan( + name=measurements.name, + path="/memory/SourceSetMeasurements.pkl", + artifact_type=measurements.artifact_type, + group_keys=("1", "2"), + group_component=AllComponents.SITE, + relations=measurements.relations, + ) + plan = SimpleNamespace( + axis_id="A01", + step_index=0, + step_name="Align", + main_input_dependency=StepInputDependency.pipeline_start(), + source_binding_plan=CompiledSourceBindingPlan( + bindings=bindings, + match_plan=SourceBindingMatchPlan( + method=SourceBindingMatchMethod.ORDER, + ), + ), + execution_group_scope=ComponentGroupScope.from_raw( + ("1", "2"), + component=AllComponents.SITE, + ), +> compiled_function_pattern=compile_function_pattern( + align_source_set, + {}, + {output_plan.ref(): output_plan}, + ), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:582: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='SourceSetMeasurements', artifact_type=, pa...>, name='OrigStain2'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='SourceSetMeasurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +__ test_static_site_groups_do_not_cross_project_natural_source_set_templates ___ + +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fddda80c770> + + def test_static_site_groups_do_not_cross_project_natural_source_set_templates( + monkeypatch: pytest.MonkeyPatch, + ) -> None: + bindings = tuple( + NamedSourceBinding( + alias=alias, + selector=SourceSelector( + filters=(), + ), + origin=SourceBindingOrigin.PIPELINE_START, + component_identity=(ComponentSelector(AllComponents.CHANNEL, channel),), + ) + for alias, channel in (("OrigStain1", "1"), ("OrigStain2", "2")) + ) + input_specs = tuple(binding.input_spec() for binding in bindings) + output_specs = ( + ArtifactSpec.output( + "Stain1", + ImageArtifactType, + relations=(GroupLineageSourceRelation(source=input_specs[0].ref()),), + ), + ArtifactSpec.output( + "Stain2", + ImageArtifactType, + relations=(GroupLineageSourceRelation(source=input_specs[1].ref()),), + ), + ArtifactSpec.output( + "AlignMeasurements", + MeasurementsArtifactType, + relations=tuple( + GroupLineageSourceRelation(source=spec.ref()) for spec in input_specs + ), + ), + ) + + @artifact_inputs(*input_specs) + @artifact_outputs(*output_specs) + def align_source_set(image): + return image + + output_plans = { + spec.ref(): ArtifactOutputPlan( + name=spec.name, + path=f"/memory/{spec.name}.pkl", + artifact_type=spec.artifact_type, + group_keys=("1", "2"), + group_component=AllComponents.SITE, + relations=spec.relations, + ) + for spec in output_specs + } + plan = SimpleNamespace( + axis_id="A01", + step_index=0, + step_name="Align", + main_input_dependency=StepInputDependency.pipeline_start(), + source_binding_plan=CompiledSourceBindingPlan( + bindings=bindings, + match_plan=SourceBindingMatchPlan( + method=SourceBindingMatchMethod.ORDER, + ), + ), + execution_group_scope=ComponentGroupScope.from_raw( + ("1", "2"), + component=AllComponents.SITE, + ), +> compiled_function_pattern=compile_function_pattern( + align_source_set, + {}, + output_plans, + ), + ) + +tests/unit/test_source_bound_matched_image_set_anchors.py:711: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +openhcs/core/function_patterns.py:1057: in compile_function_pattern + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1057: in + groups=tuple(compiler.compile(group) for group in normalized.groups), +openhcs/core/function_patterns.py:1227: in compile + invocations = tuple( +openhcs/core/function_patterns.py:1228: in + _compile_invocation( +openhcs/core/function_patterns.py:1267: in _compile_invocation + artifact_selector.validate_artifact_output_declarations() +openhcs/core/artifact_key_selection.py:114: in validate_artifact_output_declarations + self.artifact_output_policy.validate_output_declarations(self.artifact_specs) +openhcs/core/artifact_key_selection.py:32: in validate_output_declarations + cls.validate_payload_declaration(spec) +openhcs/core/artifact_key_selection.py:47: in validate_payload_declaration + spec.artifact_type.validate_native_output_declaration(spec) +openhcs/core/artifacts.py:717: in validate_native_output_declaration + cls.require_output_subject(spec) +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +cls = +output = ArtifactSpec(name='AlignMeasurements', artifact_type=, parame...>, name='OrigStain2'))), plan_type=, measurement_feature_owner=None) + + @classmethod + def require_output_subject( + cls, + output: "ArtifactSpec | ArtifactOutputPlan", + ) -> "MeasurementSubject": + """Require the same declared subject at compile and runtime boundaries.""" + + subject = ArtifactSpecRelation.measurement_subject_for_output(output) + if subject is None: +> raise ValueError( + f"Measurement output {output.ref()!r} has no declared " + "measurement subject relation. Add ImageMeasurementSubjectRelation " + "with the exact declared image ref for image-level rows, " + "ObjectMeasurementSubjectRelation with the exact object-label ref " + "for object-level rows, or ArtifactMeasurementSubjectRelation() " + "for artifact-level rows to ArtifactSpec.output(..., relations=(...,))." + ) +E ValueError: Measurement output ArtifactSpecRef(plan_type=, artifact_type=, name='AlignMeasurements') has no declared measurement subject relation. Add ImageMeasurementSubjectRelation with the exact declared image ref for image-level rows, ObjectMeasurementSubjectRelation with the exact object-label ref for object-level rows, or ArtifactMeasurementSubjectRelation() for artifact-level rows to ArtifactSpec.output(..., relations=(...,)). + +openhcs/core/artifacts.py:699: ValueError +=============================== warnings summary =============================== +../../../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_default_fixture_loop_scope + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +../../../../code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441 + /home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1441: PytestConfigWarning: Unknown config option: asyncio_mode + + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +=========================== short test summary info ============================ +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_all_loaded_contract_collapses_sibling_alias_groups_by_metadata +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_natural_callable_keeps_each_source_component_anchor +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_selected_source_contract_keeps_only_its_exact_alias_anchor +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_distinct_metadata_sets_each_keep_one_source_representative +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_independent_source_aligned_outputs_keep_each_execution_group +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_missing_alias_in_match_dimension_keeps_existing_strict_error +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_complete_source_set_templates_preserve_each_execution_group_anchor +FAILED tests/unit/test_source_bound_matched_image_set_anchors.py::test_static_site_groups_do_not_cross_project_natural_source_set_templates +8 failed, 3 passed, 2 warnings in 2.91s +Command exited with non-zero status 1 + Command being timed: "timeout 60s env OPENHCS_CPU_ONLY=true OPENHCS_BACKEND_EXCLUDE=torch,tensorflow,jax,cupy OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest tests/unit/test_source_bound_matched_image_set_anchors.py -q --basetemp=/home/ts/.cache/agent-scratch/issue314-russell/pytest-base-complete" + User time (seconds): 3.18 + System time (seconds): 0.21 + Percent of CPU this job got: 100% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:03.39 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 177448 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 37150 + Voluntary context switches: 30 + Involuntary context switches: 22 + Swaps: 0 + File system inputs: 0 + File system outputs: 136 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 1 diff --git a/docs/validation/paired_field_csv_314_20260930/source-anchors-base.log b/docs/validation/paired_field_csv_314_20260930/source-anchors-base.log new file mode 100644 index 0000000000..42da0a71fd --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/source-anchors-base.log @@ -0,0 +1,77 @@ +Traceback (most recent call last): + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 858, in import_plugin + __import__(importspec) +ModuleNotFoundError: No module named 'tests.pytest_integration_options' + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "", line 198, in _run_module_as_main + File "", line 88, in _run_code + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pytest/__main__.py", line 9, in + raise SystemExit(pytest.console_main()) + ^^^^^^^^^^^^^^^^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 201, in console_main + code = main() + ^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 156, in main + config = _prepareconfig(args, plugins) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 341, in _prepareconfig + config = pluginmanager.hook.pytest_cmdline_parse( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pluggy/_hooks.py", line 512, in __call__ + return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pluggy/_manager.py", line 120, in _hookexec + return self._inner_hookexec(hook_name, methods, kwargs, firstresult) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 167, in _multicall + raise exception + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 139, in _multicall + teardown.throw(exception) + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/helpconfig.py", line 105, in pytest_cmdline_parse + config = yield + ^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/pluggy/_callers.py", line 121, in _multicall + res = hook_impl.function(*args) + ^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 1140, in pytest_cmdline_parse + self.parse(args) + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 1494, in parse + self._preparse(args, addopts=addopts) + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 1377, in _preparse + self.pluginmanager.consider_preparse(args, exclude_only=False) + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 787, in consider_preparse + self.consider_pluginarg(parg) + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 810, in consider_pluginarg + self.import_plugin(arg, consider_entry_points=True) + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 860, in import_plugin + raise ImportError( + File "/home/ts/code/projects/openhcs/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py", line 858, in import_plugin + __import__(importspec) +ImportError: Error importing plugin "tests.pytest_integration_options": No module named 'tests.pytest_integration_options' +Command exited with non-zero status 1 + Command being timed: "timeout 60s env OPENHCS_CPU_ONLY=true OPENHCS_BACKEND_EXCLUDE=torch,tensorflow,jax,cupy OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B -m pytest tests/unit/test_source_bound_matched_image_set_anchors.py -q --basetemp=/home/ts/.cache/agent-scratch/issue314-russell/pytest-base-controls" + User time (seconds): 0.19 + System time (seconds): 0.04 + Percent of CPU this job got: 111% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.21 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 26776 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 7160 + Voluntary context switches: 25 + Involuntary context switches: 0 + Swaps: 0 + File system inputs: 0 + File system outputs: 8 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 1 diff --git a/docs/validation/paired_field_csv_314_20260930/source-build.log b/docs/validation/paired_field_csv_314_20260930/source-build.log new file mode 100644 index 0000000000..1818a8deea --- /dev/null +++ b/docs/validation/paired_field_csv_314_20260930/source-build.log @@ -0,0 +1,32 @@ +running build_ext +building 'openhcs.processing.backends.cellprofiler._granularity_native' extension +g++ -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall -fPIC -DPy_LIMITED_API=0x030B0000 -I/home/ts/code/projects/openhcs/.venv/include -I/home/ts/.pyenv/versions/3.12.3/include/python3.12 -c openhcs/processing/backends/cellprofiler/_granularity_native.cpp -o /home/ts/.cache/agent-scratch/issue314-russell/build/openhcs/processing/backends/cellprofiler/_granularity_native.o -O3 +g++ -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall -shared /home/ts/.cache/agent-scratch/issue314-russell/build/openhcs/processing/backends/cellprofiler/_granularity_native.o -L/home/ts/.pyenv/versions/3.12.3/lib -o build/lib.linux-x86_64-cpython-312/openhcs/processing/backends/cellprofiler/_granularity_native.abi3.so +building 'openhcs.core._tabular_native' extension +g++ -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall -fPIC -DPy_LIMITED_API=0x030B0000 -I/home/ts/code/projects/openhcs/.venv/include -I/home/ts/.pyenv/versions/3.12.3/include/python3.12 -c openhcs/core/_tabular_native.cpp -o /home/ts/.cache/agent-scratch/issue314-russell/build/openhcs/core/_tabular_native.o -O3 +g++ -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall -shared /home/ts/.cache/agent-scratch/issue314-russell/build/openhcs/core/_tabular_native.o -L/home/ts/.pyenv/versions/3.12.3/lib -o build/lib.linux-x86_64-cpython-312/openhcs/core/_tabular_native.abi3.so +copying build/lib.linux-x86_64-cpython-312/openhcs/processing/backends/cellprofiler/_granularity_native.abi3.so -> openhcs/processing/backends/cellprofiler +copying build/lib.linux-x86_64-cpython-312/openhcs/core/_tabular_native.abi3.so -> openhcs/core + Command being timed: "timeout 60s env OPENHCS_CPU_ONLY=true OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 PYTHONDONTWRITEBYTECODE=1 XDG_CACHE_HOME=/home/ts/.cache/agent-scratch/issue314-russell /home/ts/code/projects/openhcs/.venv/bin/python -B setup.py build_ext --inplace --parallel 1 --build-temp /home/ts/.cache/agent-scratch/issue314-russell/build" + User time (seconds): 3.61 + System time (seconds): 0.33 + Percent of CPU this job got: 98% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:04.02 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 144720 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 91503 + Voluntary context switches: 217 + Involuntary context switches: 28 + Swaps: 0 + File system inputs: 4376 + File system outputs: 2304 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 diff --git a/openhcs/core/source_matching.py b/openhcs/core/source_matching.py index b6f3f5b1ce..8b973cff4c 100644 --- a/openhcs/core/source_matching.py +++ b/openhcs/core/source_matching.py @@ -464,24 +464,41 @@ def from_source_bindings( *, group_component: AllComponents | None = None, ) -> "SourceImageSetIdentityPolicy": - """Return image-plane membership declared by source and group semantics.""" + """Return image-plane membership declared by source and group semantics. + + Equal singleton coordinates across primary bindings constrain the field; + they do not declare another plane-member axis. Explicit stack/group axes + still declare membership, even when the current coordinates are equal. + A single binding has no cross-binding fixed-coordinate witness. + """ if not isinstance(source_bindings, SourceBindingDeclarationsMixin): raise TypeError( "SourceImageSetIdentityPolicy.from_source_bindings requires " "SourceBindingDeclarationsMixin." ) + bindings = source_bindings.primary_plane_bindings + shared_components = frozenset( + component + for component in AllComponents + if len(bindings) > 1 + for values in ( + tuple(binding.component_values(component) for binding in bindings), + ) + if len(values[0]) == 1 and all(value == values[0] for value in values[1:]) + ) return cls( frozenset( ComponentSet.collect( source_bindings.source_stack_components, ( selector.component - for binding in source_bindings.primary_plane_bindings + for binding in bindings for selector in ( *binding.selector.components, *binding.component_identity, ) + if selector.component not in shared_components ), (group_component,), ) diff --git a/openhcs/processing/backends/cellprofiler/object_images.py b/openhcs/processing/backends/cellprofiler/object_images.py index 11f49ba70e..2ec98235ff 100644 --- a/openhcs/processing/backends/cellprofiler/object_images.py +++ b/openhcs/processing/backends/cellprofiler/object_images.py @@ -200,7 +200,7 @@ def convert_image_to_objects( """ from skimage.measure import label - working_image = image.copy() + working_image = np.asarray(image).copy() if cast_to_bool: working_image = (working_image != background).astype(np.uint8) if preserve_label: diff --git a/tests/unit/test_cellprofiler_spreadsheet_export.py b/tests/unit/test_cellprofiler_spreadsheet_export.py index 61f6b691f5..4778256337 100644 --- a/tests/unit/test_cellprofiler_spreadsheet_export.py +++ b/tests/unit/test_cellprofiler_spreadsheet_export.py @@ -56,6 +56,11 @@ ) from openhcs.core.source_image_provenance import SourceImageProvenancePlanes from openhcs.core.source_matching import SourceImageSetIdentityPolicy +from openhcs.core.source_bindings import ( + ComponentSelector, + NamedSourceBinding, + SourceBindingsConfig, +) from openhcs.core.source_metadata import ORIGINAL_SOURCE_METADATA_FIELD from openhcs.interop.cellprofiler.module_declarations import ( CellProfilerModule, @@ -563,6 +568,108 @@ def test_export_to_spreadsheet_uses_declared_image_set_identity_across_channels( ) +def test_export_to_spreadsheet_pairs_fully_addressed_field_measurements() -> None: + """A biological address is not a declaration to stack every addressed axis.""" + bindings = SourceBindingsConfig( + bindings=tuple( + NamedSourceBinding( + alias=alias, + component_identity=tuple( + ComponentSelector(component, value) + for component, value in ( + (AllComponents.WELL, "A01"), + (AllComponents.SITE, "1"), + (AllComponents.CHANNEL, channel), + (AllComponents.Z_INDEX, "1"), + (AllComponents.TIMEPOINT, "1"), + ) + ), + ) + for alias, channel in (("DNA", "1"), ("Actin", "2")) + ) + ) + policy = SourceImageSetIdentityPolicy.from_source_bindings( + bindings, + group_component=AllComponents.CHANNEL, + ) + records = [] + # Two independent fields, each with two cells. Local slice/object IDs repeat + # intentionally: provenance, not runtime row position, owns field identity. + for site in ("1", "2"): + for channel, features in ( + ("1", {"Parent_Nuclei": 1, "Location_Center_X": 1.5}), + ("2", {"AreaShape_Area": 4.0}), + ): + provenance = SourceImageProvenancePlanes.from_components( + paths=(f"/synthetic/A01-field{site}-plane{channel}.tif",), + component_metadata=( + { + "well": "A01", + "site": site, + "channel": channel, + "z_index": "1", + "timepoint": "1", + }, + ), + ) + records.append( + _measurement_record( + f"field{site}_plane{channel}", + axis_id="A01", + subject=MeasurementSubject( + MeasurementScope.OBJECT, "Cells", "object_number" + ), + rows=tuple( + {"slice_index": 0, "object_number": number, **features} + for number in (1, 2) + ), + source_image_provenance_planes=provenance, + group_component=AllComponents.CHANNEL, + group_key=channel, + variable_components=(AllComponents.SITE,), + ) + ) + records.append( + _measurement_record( + f"field{site}_counts", + axis_id="A01", + subject=MeasurementSubject(MeasurementScope.IMAGE, "Image"), + rows=({"slice_index": 0, "Count_Cells": 2},), + source_image_provenance_planes=provenance, + group_component=AllComponents.CHANNEL, + group_key="2", + variable_components=(AllComponents.SITE,), + ) + ) + batch = RuntimeArtifactBatch( + input_specs=tuple( + ArtifactSpec.input(record.key.name, MeasurementsArtifactType) + for record in records + ), + records_by_axis={"A01": tuple(records)}, + source_image_set_identity_policy=policy, + ) + + bundle = export_to_spreadsheet(add_filename_prefix=False, artifact_batch=batch) + + cells = tuple(csv.DictReader(io.StringIO(bundle["Cells.csv"]))) + assert cells == tuple( + { + "image_number": image_number, + "object_label": object_number, + "Parent_Nuclei": "1", + "Location_Center_X": "1.5", + "AreaShape_Area": "4.0", + } + for image_number in ("1", "2") + for object_number in ("1", "2") + ) + assert tuple(csv.DictReader(io.StringIO(bundle["Image.csv"]))) == ( + {"image_number": "1", "Count_Cells": "2"}, + {"image_number": "2", "Count_Cells": "2"}, + ) + + def test_export_to_spreadsheet_nulls_metadata_that_differs_between_image_planes() -> ( None ): diff --git a/tests/unit/test_cellprofiler_uncovered_object_output_contracts.py b/tests/unit/test_cellprofiler_uncovered_object_output_contracts.py index 5042e20b44..cde5e96836 100644 --- a/tests/unit/test_cellprofiler_uncovered_object_output_contracts.py +++ b/tests/unit/test_cellprofiler_uncovered_object_output_contracts.py @@ -33,7 +33,11 @@ MeasurementRowAxisField, MeasurementRowValueField, ) -from openhcs.core.runtime_image_values import image_payload_metadata +from openhcs.core.runtime_image_values import ( + ImagePayloadMetadata, + image_payload_metadata, +) +from openhcs.core.source_metadata import SourceVoxelSpacing from openhcs.core.runtime_object_labels import ( ObjectLabelValue, object_label_dense_array, @@ -229,6 +233,40 @@ def test_object_leaf_callables_emit_schema_rows_then_nominal_labels() -> None: assert dead_worms[2].object_label_domain().declared_object_count == 0 +@pytest.mark.parametrize("metadata_bearing", (False, True)) +@pytest.mark.parametrize("preserve_label", (False, True)) +def test_image_conversion_preserves_source_pixels_and_metadata( + metadata_bearing: bool, preserve_label: bool, +) -> None: + pixels = np.zeros((12, 12), dtype=np.uint8) + pixels[2:5, 2:5] = 2 + pixels[7:10, 7:10] = 7 + original = pixels.copy() + metadata = ImagePayloadMetadata( + source_path="/input/A01_s001_w1_z001_t001.TIF", + source_component_metadata={"well": "A01", "channel": "1"}, + source_voxel_spacing=SourceVoxelSpacing((0.65, 0.65)), + ) + image = metadata.payload_with(pixels, None) if metadata_bearing else pixels + + main_image, rows, objects = inspect.unwrap(convert_image_to_objects)( + image, preserve_label=preserve_label, + ) + + assert main_image is image + np.testing.assert_array_equal(pixels, original) + expected_labels = original if preserve_label else np.where(original == 7, 2, original // 2) + np.testing.assert_array_equal(object_label_dense_array(objects), expected_labels) + assert objects.object_label_domain().declared_object_count == 2 + assert tuple(rows.iter_row_mappings()) == ( + {"slice_index": 0, "object_count": 2, "mean_area": 9.0, "total_area": 18}, + ) + source_metadata = image_payload_metadata(image) + assert objects.source_provenance == source_metadata.source_provenance + assert objects.parent_image_source_voxel_spacing == source_metadata.source_voxel_spacing + assert objects.source_spatial_domain.source_shape_yx == pixels.shape + + def test_identify_dead_worms_projects_exact_native_measurement_features() -> None: module = _module( IdentifyDeadWormsModule, diff --git a/tests/unit/test_source_bound_matched_image_set_anchors.py b/tests/unit/test_source_bound_matched_image_set_anchors.py index 28df89ff50..f79d80899b 100644 --- a/tests/unit/test_source_bound_matched_image_set_anchors.py +++ b/tests/unit/test_source_bound_matched_image_set_anchors.py @@ -5,6 +5,7 @@ from openhcs.constants.constants import AllComponents from openhcs.core.artifacts import ( + ArtifactMeasurementSubjectRelation, ArtifactOutputPlan, ArtifactSpec, GroupLineageSourceRelation, @@ -146,11 +147,16 @@ def _filter_source_anchors( ArtifactSpec.output( "Measurements" if shared_output else f"{binding.alias}Measurements", MeasurementsArtifactType, - relations=tuple( - GroupLineageSourceRelation(source=source_spec.ref()) - for source_spec in ( - declared_specs if shared_output else (binding.input_spec(),) - ) + # These synthetic declarations test dependency anchors, not image + # or object measurement rows. Their subject is the artifact itself. + relations=( + ArtifactMeasurementSubjectRelation(), + *( + GroupLineageSourceRelation(source=source_spec.ref()) + for source_spec in ( + declared_specs if shared_output else (binding.input_spec(),) + ) + ), ), ) for binding in (compiled_bindings[:1] if shared_output else compiled_bindings) @@ -163,9 +169,12 @@ def _filter_source_anchors( ArtifactSpec.output( "AggregateMeasurements", MeasurementsArtifactType, - relations=tuple( - GroupLineageSourceRelation(source=source_spec.ref()) - for source_spec in declared_specs + relations=( + ArtifactMeasurementSubjectRelation(), + *( + GroupLineageSourceRelation(source=source_spec.ref()) + for source_spec in declared_specs + ), ), ), ) @@ -545,8 +554,9 @@ def test_complete_source_set_templates_preserve_each_execution_group_anchor( measurements = ArtifactSpec.output( "SourceSetMeasurements", MeasurementsArtifactType, - relations=tuple( - GroupLineageSourceRelation(source=spec.ref()) for spec in input_specs + relations=( + ArtifactMeasurementSubjectRelation(), + *(GroupLineageSourceRelation(source=spec.ref()) for spec in input_specs), ), ) @@ -671,8 +681,9 @@ def test_static_site_groups_do_not_cross_project_natural_source_set_templates( ArtifactSpec.output( "AlignMeasurements", MeasurementsArtifactType, - relations=tuple( - GroupLineageSourceRelation(source=spec.ref()) for spec in input_specs + relations=( + ArtifactMeasurementSubjectRelation(), + *(GroupLineageSourceRelation(source=spec.ref()) for spec in input_specs), ), ), ) diff --git a/tests/unit/test_source_image_set_identity_policy.py b/tests/unit/test_source_image_set_identity_policy.py new file mode 100644 index 0000000000..b163730e82 --- /dev/null +++ b/tests/unit/test_source_image_set_identity_policy.py @@ -0,0 +1,164 @@ +"""Field identity derives from binding coordinates, not selected plane paths.""" + +import pytest + +from openhcs.constants.constants import AllComponents, GroupBy +from openhcs.core.component_group_scope import RuntimeExecutionAxisScope +from openhcs.core.source_bindings import ( + ComponentSelector, + NamedSourceBinding, + SourceBindingsConfig, + SourceSelector, +) +from openhcs.core.source_image_provenance import SourceImageProvenance +from openhcs.core.source_matching import ( + SourceImageSetIdentity, + SourceImageSetIdentityPolicy, +) +from openhcs.interop.cellprofiler.image_set_numbering import ( + CellProfilerImageSetNumbering, +) + + +def _bindings(*, stack=(), selectors=False): + return SourceBindingsConfig( + source_stack_components=stack, + bindings=tuple( + NamedSourceBinding( + alias=f"Plane{channel}", + selector=SourceSelector(components=coordinates) + if selectors + else SourceSelector(), + component_identity=() if selectors else coordinates, + ) + for channel in ("1", "2", "3") + for coordinates in ( + tuple( + ComponentSelector(component, value) + for component, value in ( + (AllComponents.WELL, "A01"), + (AllComponents.SITE, "1"), + (AllComponents.CHANNEL, channel), + (AllComponents.Z_INDEX, "1"), + (AllComponents.TIMEPOINT, "1"), + ) + ), + ) + ), + ) + + +@pytest.mark.parametrize("selectors", (False, True)) +def test_shared_binding_coordinates_remain_field_axes(selectors): + policy = SourceImageSetIdentityPolicy.from_source_bindings( + _bindings(selectors=selectors) + ) + + assert policy.plane_member_components == frozenset((AllComponents.CHANNEL,)) + + +@pytest.mark.parametrize( + "component", (AllComponents.SITE, AllComponents.Z_INDEX, AllComponents.TIMEPOINT) +) +def test_explicit_stack_and_group_axes_override_shared_coordinates(component): + for policy in ( + SourceImageSetIdentityPolicy.from_source_bindings( + _bindings(stack=(component,)) + ), + SourceImageSetIdentityPolicy.from_source_bindings( + _bindings(), group_component=component + ), + ): + assert policy.plane_member_components == frozenset( + (AllComponents.CHANNEL, component) + ) + + +@pytest.mark.parametrize( + "component", + ( + AllComponents.WELL, + AllComponents.SITE, + AllComponents.Z_INDEX, + AllComponents.TIMEPOINT, + ), +) +def test_numbering_combines_channels_but_preserves_independent_fields(component): + policy = SourceImageSetIdentityPolicy.from_source_bindings(_bindings()) + numbering = CellProfilerImageSetNumbering(policy) + scope = RuntimeExecutionAxisScope.from_raw( + "A01", component=AllComponents.CHANNEL, value="1" + ) + metadata = { + "well": "A01", + "site": "1", + "channel": "1", + "z_index": "1", + "timepoint": "1", + } + numbers = [] + for field_value in ("1", "2"): + for channel in ("1", "2", "3"): + provenance = SourceImageProvenance( + source_path=f"/synthetic/field-{field_value}-plane-{channel}.tif", + source_component_metadata={ + **metadata, + component.value: field_value, + "channel": channel, + }, + ) + numbers.append( + numbering.for_source_slice( + scope=scope, provenance=provenance, slice_index=0, owner="Cells" + ) + ) + + assert numbers == [1, 1, 1, 2, 2, 2] + + +def test_component_assignment_owns_identity_over_physical_selector(): + declarations = SourceBindingsConfig( + bindings=tuple( + NamedSourceBinding( + alias=f"Plane{channel}", + selector=SourceSelector( + components=(ComponentSelector(AllComponents.SITE, physical_site),) + ), + component_identity=( + ComponentSelector(AllComponents.SITE, "1"), + ComponentSelector(AllComponents.CHANNEL, channel), + ), + ) + for channel, physical_site in (("1", "7"), ("2", "8")) + ) + ) + policy = SourceImageSetIdentityPolicy.from_source_bindings(declarations) + + assert policy.plane_member_components == frozenset((AllComponents.CHANNEL,)) + + +def test_path_identity_stays_distinct_without_semantic_field_coordinates(): + policy = SourceImageSetIdentityPolicy.from_source_bindings(_bindings()) + + assert SourceImageSetIdentity.from_metadata( + {}, fallback_source_path="/synthetic/one.tif", policy=policy + ) != SourceImageSetIdentity.from_metadata( + {}, fallback_source_path="/synthetic/two.tif", policy=policy + ) + + +def test_pipeline_config_compiles_the_same_paired_field_identity_policy(): + from openhcs.core.config import ( + LazyProcessingConfig, + LazySourceBindingsConfig, + PipelineConfig, + ) + + config = PipelineConfig( + processing_config=LazyProcessingConfig(group_by=GroupBy.CHANNEL), + source_bindings_config=LazySourceBindingsConfig(bindings=_bindings().bindings), + ) + + policy = SourceImageSetIdentityPolicy.from_pipeline_config(config) + + assert policy.plane_member_components == frozenset((AllComponents.CHANNEL,))