Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 152 additions & 6 deletions docs/source/development/callable_artifact_authoring.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ runtime loader or default value for it.

Image outputs that retain the current stack's axes should use
``MainFlowStackOutputSpec.output(name, ImageArtifactType, ...)``. The compiler
binds its lineage to the current image input. Declare the image before any
trailing table outputs so the returned image participates in main flow.
binds their lineage to the current image input. Declare main-flow images before
trailing typed artifacts; multiple images share one aligned canonical return
slot, as described under **Return diagnostic images without flattening the ABI**.
For a cropped subset, return ``SelectedPlaneImageOutput(array, source_indices)``
in that image slot; the source indices must match the array's leading axis.
It is an array-compatible runtime payload: ``numpy.asarray(result)`` exposes
Expand Down Expand Up @@ -235,14 +236,13 @@ normal plate compilation/execution supplies image-source and runtime context.
:name: callable-artifact-reference-check

from openhcs.core.callable_contract import CallableContract
from openhcs.core.runtime_output_matching import RuntimeReturnedOutputMatcher
from openhcs.core.steps import FunctionStep

fixture = np.zeros((8, 8), dtype=np.uint16)
fixture[2:6, 3:7] = 1
returned = inspect_label_fixture(fixture)
contract = CallableContract.from_callable(inspect_label_fixture)
matched = RuntimeReturnedOutputMatcher(contract, returned).resolve()
matched = contract.resolve_returned_output(returned)
assert np.array_equal(matched[FIXTURE_IMAGE.ref()], fixture)
assert matched[FIXTURE_LABELS.ref()].dtype == np.int32
assert matched[FIXTURE_ROWS.ref()].row_mappings() == (
Expand All @@ -267,8 +267,154 @@ import, so its dataclass schema is valid in the custom execution namespace as
well as in an ordinary module. Persist the source when it must survive a new
process; session-only registration does not make a spawned worker import it.

Materialize typed 3D centres as feature-bearing Points
------------------------------------------------------------

Points are a materialization of ``MeasurementsArtifactType``, not a missing
``PointsArtifactType`` or a centre-voxel image. Attach ``PointROIOptions`` to the
same object-measurement output as ``CsvOptions``. The existing writer projects
the contextualized table into source-bearing point ROIs; reopening that archive
uses the native Points route and retains row features, including object identity.
A CSV alone is not that native geometry declaration.

This declaration block is for an operation that already produces labels and
3D centre rows. It specifies their ABI, not how to detect or choose a centre:

.. code-block:: python
:name: callable-artifact-points-reference

from dataclasses import dataclass

from openhcs.core.artifacts import (
ImageArtifactType, MainFlowStackOutputSpec, MeasurementsArtifactType,
ObjectLabelsArtifactType, ObjectMeasurementSubjectRelation,
)
from openhcs.core.measurement_row_materialization import (
DataclassMeasurementColumnarRows,
)
from openhcs.core.runtime_measurements import (
ObjectCoreMeasurementFeature, RuntimeMeasurementFeatureOwner,
)
from openhcs.processing.materialization import (
CsvOptions, MaterializationSpec, PointROIOptions,
)

class CentreFeatureOwner(RuntimeMeasurementFeatureOwner):
@classmethod
def owns_measurement_feature_name(cls, feature_name: str) -> bool:
return any(feature.feature_name == feature_name
for feature in ObjectCoreMeasurementFeature)

@classmethod
def owns_primary_measurement_feature_name(cls, feature_name: str) -> bool:
return cls.owns_measurement_feature_name(feature_name)

@dataclass(frozen=True)
class CentreRow:
object_label: int
center_z: float
center_y: float
center_x: float

CENTRE_IMAGE = MainFlowStackOutputSpec.output("centre_image", ImageArtifactType)
CENTRE_LABELS = MainFlowStackOutputSpec.output(
"centre_labels", ObjectLabelsArtifactType,
)
CENTRE_ROWS = MainFlowStackOutputSpec.output(
"centre_rows", MeasurementsArtifactType,
measurement_feature_owner=CentreFeatureOwner,
relations=(ObjectMeasurementSubjectRelation(
source=CENTRE_LABELS.ref(), id_field="object_label",
),),
materialization=MaterializationSpec(
CsvOptions(),
PointROIOptions(
z_feature=ObjectCoreMeasurementFeature.CENTER_Z,
y_feature=ObjectCoreMeasurementFeature.CENTER_Y,
x_feature=ObjectCoreMeasurementFeature.CENTER_X,
),
),
)

On the existing centre-producing callable, declare
``@artifact_outputs(CENTRE_IMAGE, CENTRE_LABELS, CENTRE_ROWS)`` and return
``image, labels, DataclassMeasurementColumnarRows(rows, row_type=CentreRow)``.
Use the existing memory/processing decorators, as in the executable reference;
``PURE_3D`` is appropriate only for an actual volumetric calculation. Retain
the complete source bindings and compiled ZYX grouping: the decorator alone
does not establish axis order. The row's representative rule (peak, body centre
or another task-defined location) remains the analysis contract, not the writer.

Coordinates are floating source-grid Z/Y/X locations, not calibrated world
coordinates and not a manually rescaled display array. Keep the full declared
source Z domain, including planes without objects, and its source paths,
spatial frame and voxel spacing through the compiled source/subject relations.
The writer rejects missing source provenance, missing/foreign coordinate fields,
duplicate object IDs and non-finite coordinates. It currently rejects an empty
Points archive; empty measurement rows retain their table schema but do not
establish successful point materialization.

Fractional Z is geometry. An integer navigation slice, a rounded centre-voxel
marker image and a fractional point table describe different things. Do not
round the table to fit a viewer slice or infer its Z domain from occupied
labels/rounded markers. Compare the persisted table/archive and native Points
at matched raw coordinates, full Z extent and orthogonal views; inspect object
IDs/features and source calibration, not just point count. Verify that the
installed version exposes the required point producer/domain and viewer geometry
contracts before relying on automatic streaming or archive reopening. Declaration
and direct-call checks do not prove that live path.

Return diagnostic images without flattening the ABI
------------------------------------------------------------

``MainFlowStackOutputSpec`` declares source lineage; it does not give every
Image its own outer tuple slot. ``CallableContract`` groups the consecutive
leading main-flow Image declarations into one canonical return slot. Labels,
measurements and other remaining artifact declarations each have one trailing
slot in their exact order. Inspect ``canonical_return_output_specs`` and
``trailing_return_output_specs`` before extending a callable's returns.

For example, to extend the executable 2D reference with an aligned diagnostic,
keep its labels/rows declarations and replace its image declarations/decorator:

.. code-block:: python
:name: callable-artifact-diagnostic-reference

from openhcs.core.aligned_image_payload import (
AlignedImageSliceContext, pack_aligned_image_outputs,
)

FIXTURE_DIAGNOSTIC = MainFlowStackOutputSpec.output(
"fixture_diagnostic", ImageArtifactType,
)

@numpy(contract=ProcessingContract.PURE_2D)
@artifact_outputs(
FIXTURE_IMAGE, FIXTURE_DIAGNOSTIC, FIXTURE_LABELS, FIXTURE_ROWS,
)
def inspect_label_fixture_with_diagnostic(image: np.ndarray):
image, labels, rows = inspect_label_fixture(image)
image_specs = (FIXTURE_IMAGE, FIXTURE_DIAGNOSTIC)
main_images = pack_aligned_image_outputs(
(image, (labels > 0).astype(np.uint8)),
slice_contexts=AlignedImageSliceContext.main_flow_for_artifact_specs(
image_specs,
),
)
return main_images, labels, rows

The diagnostic here is only the synthetic fixture's foreground indicator.
The helper retains exact named slice contexts; it is not ``np.stack`` of
scientific Z planes and introduces no new return codec. With five leading
main-flow Images followed by three typed artifacts, the outer return has four
positions (one aligned image bundle plus three artifacts), not eight. A
``trailing return count`` error is repaired against this declared partition,
not by dropping useful diagnostics or weakening the matcher. Validate/compile
the corrected complete document and inspect its materialization/streaming plan;
this ABI repair does not itself settle a viewer or validate the analysis.

Consume a nominal artifact input
-------------------------------
--------------------------------

An artifact's input ABI is not necessarily its raw output representation.
``ObjectLabelsArtifactType`` accepts integer labels from a producer, but supplies
Expand Down Expand Up @@ -334,7 +480,7 @@ the corrected complete document through the ordinary route. This establishes
technical input compatibility, not object identity or biological accuracy.

Summarize declared measurements once per plate
---------------------------------------------
----------------------------------------------

A terminal ``PLATE`` callable receives ``RuntimeArtifactBatch``, not an image
or a directory of CSVs. The parent executes it once after the compiled axes
Expand Down
4 changes: 3 additions & 1 deletion docs/source/development/mcp_knowledge_base_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,7 @@
"document_id": "openhcs_callable_artifact_authoring",
"section_count": 0,
"source_path": "docs/source/development/callable_artifact_authoring.rst",
"summary": "Canonical callable and artifact authoring reference with executable nominal input annotations and artifact bindings, earliest compile-error repair, typed NumPy main flow, object labels, dataclass ColumnarRows, RuntimeMeasurementFeatureOwner, exact subject/source relations, CSV and ROI materialization, custom registration namespace boundaries, and a terminal PLATE summary using RuntimeArtifactBatch.",
"summary": "Canonical callable and artifact authoring reference with executable nominal input annotations and artifact bindings, earliest compile-error repair, aligned diagnostic-image returns, typed NumPy main flow, object labels, dataclass ColumnarRows, RuntimeMeasurementFeatureOwner, exact subject/source relations, CSV and fractional 3D Points materialization, custom registration namespace boundaries, and a terminal PLATE summary using RuntimeArtifactBatch.",
"tags": [
"custom function artifacts",
"callable artifact authoring",
Expand All @@ -726,6 +726,8 @@
"ObjectMeasurementSubjectRelation",
"materialization",
"ROIOptions",
"PointROIOptions fractional centres",
"aligned diagnostic image returns",
"CsvOptions",
"exact imports decorators",
"custom registration namespace"
Expand Down
117 changes: 117 additions & 0 deletions docs/validation/typed-centroid-authoring-20261005.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
Typed centroid and multi-image authoring checkpoint
==================================================

Owner and scope
---------------

Singer owns this documentation-only change. Planck explicitly released the
callable-artifact authoring and custom-function reference documentation; Planck
retains #748 geometry and its frozen engineering fixtures. No production,
installed package, live author, or scientific input is changed here.

The canonical owner is ``docs/source/development/callable_artifact_authoring.rst``.
The custom-function skill reference routes to that manifest-declared document;
``build_mcp_knowledge_assets.py`` copies both declared sources into package output.
It does not convert this RST into a second Markdown artifact manual.

Determining source contracts
----------------------------

``PointROIOptions`` in ``processing/materialization/options.py`` selects nominal
runtime measurement features. The registered ``_write_point_roi_zip`` writer in
``processing/materialization/core.py`` requires a contextualized measurement
table, object subject, owned coordinate fields, and exact source provenance.
It retains fractional Z through ``ROIFractionalZ`` and binds the archive through
``ROIArchiveSourceMetadata``. There is no new Points artifact declaration.

The read-only engineering494 ``point_volume_fixture_494.py`` is pinned at SHA256
``c883251abc009e9d2c7b82a3dde3b4fe4a66529b6c8117b6bf1bfd887fdc2935``.
Its typed feature owner, subject relation, and CSV plus PointROIOptions supply
the existing declaration example, not an assay algorithm or recommended settings.

``CallableContract.__post_init__`` separates consecutive main-flow Image specs
into canonical return specs; ``resolve_returned_output`` expects those images
in one named ``AlignedImageStack`` return slot. Each remaining typed artifact
has one trailing slot. A flat tuple of diagnostic Images is not this ABI.
``pack_aligned_image_outputs`` and ``AlignedImageSliceContext`` own its packing.

Review and readiness
--------------------

Read current skill-creator, NRA, authoritative refactor-audit archive, pattern
README and boundary patterns, Diataxis and use-openhcs. Relevant BOUND-2/4/8
risks are hand-decoding rows, flattening nominal output families, and inventing
an image/CSV substitute for the materialization contract. Existing owners remain
unchanged; this is not a structural refactor or global NRA proof.

Source reflection, executable declaration/ABI checks and real knowledge/package
projection follow the coherent documentation edit. Native #748 geometry/reopen
qualification remains with Planck/Dewey and is not claimed by these source checks.

Implemented owner/consumer change
--------------------------------

The RST adds one declaration-only centre row/feature owner and measurement output
with CSV plus PointROIOptions; detection is deliberately left to the existing
analysis callable. The original engineering fixture is not edited or copied as
a new detector. The named subject binds the exact labels declaration. The generic
writer accepts this independently named CentreRow/CentreFeatureOwner declaration
without consumer edits, retaining fractional geometry, row features and the full
four-plane source domain. Missing source/field, duplicate-ID and empty-archive
guards remain unchanged. No Points artifact, schema mirror or custom codec is added.

The existing executable image/labels/rows reference gains a diagnostic variant
using the original aligned image packer and named slice-context owner. Its two
Images share one canonical slot and labels/rows retain two trailing slots; the
original matcher rejects the flattened return. Five canonical Images plus three
trailing typed artifacts likewise means four outer return positions, not eight.
This identifies the reported return-count error as an ABI mismatch, not evidence
that diagnostics are unavailable or that a viewer geometry guard must change.

One obsolete direct-check consumer of the deleted RuntimeReturnedOutputMatcher
is migrated to CallableContract.resolve_returned_output. One stale fixture cache
clearing call is deleted: resolved_callable_type_hints now owns fresh resolution,
not that removed cache. No compatibility facade or alternative matcher is added.
Two pre-existing short RST heading underlines are corrected so the existing
knowledge section parser ends each section at its actual boundary. The packaged
custom-function reference links these two sections once, with no duplicated ABI
example or new SKILL entrypoint. Manifest discovery metadata names this same
canonical document; the original package builder copies its exact bytes.

Actual qualification and preserved failures
------------------------------------------

All controls use the existing run_installed_tests.py entrypoint, paired interpreter
and read-only qualified receiving14 runtime/dependencies. Six declaration/source
owners (artifacts, callable contract, runtime measurements, aligned image payload,
options and tabular CPP) byte-match current main. The backing writer includes
unmerged #748 PointROIOutput; native streaming/reopen is explicitly NOT inferred
from this disk/source contract proof. No install/build/catalogue/native/viewer or
provider request occurs. Current live authors and their instructions are untouched.

Original controls01:13PASS/3FAIL,7.02s,377724KiB max RSS, swaps0. Failures were
the new fixture assuming .csv instead of CsvOptions' declared _details.csv,
the pre-existing removed cache API call, and an undersized new RST heading.
Original controls02:15PASS/1FAIL,7.11s,372296KiB max RSS, swaps0. All executable
ABI/materialization tests passed; section retrieval still included later content
because the pre-existing input/plate section underlines were too short.
Final affected controls03:3PASS,6.42s,364996KiB max RSS, swaps0. No larger bound,
parser patch or assertion weakening: correct canonical section boundaries now
retrieve both sections untruncated at10000chars; whole-document retrieval remains
untruncated at30000chars. All16 distinct original controls are qualified by02/03;
this is not reported as a single clean16-test final run.

Real KnowledgeBaseService retrieval/search, exact RST/skill package projection,
isolated managed-skill sync and unchanged second sync pass. The original
skill-creator quick validator reports ``Skill is valid!``. No production changes,
scientific inputs/reference answers, settings recommendations, source calibration
invention or current-author coaching. git diff --check passes on this change;
eight unrelated foreign gitlinks and prior untracked evidence remain untouched.

Raw numbered stdout/stderr and original generated centroid CSV/ROI archive are
preserved in the byte-exact qualification archive adjacent to this receipt.
Its15members compare byte-exact to source; archive SHA256 is
``9cc10ec8c226b8c4d79c599aa6d2f670de7de8ca7432d828c501914cc451c6c9``.
CI is deferred. Documentation/contract exposure is ready for review and future
ordinary package delivery; #748's native geometry/selected-source acceptance
remains the explicit independent scope boundary, not a documentation delivery hold.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ Record the missing operation and expected input axes, dtype, units, memory
backend, outputs and empty-input behaviour before writing source.
For centre detection, specify what defines a centre, coordinate order/origin,
label identity and whether a count covers one plane or the whole volume.
For native feature-bearing 3-D Points, retrieve **Materialize typed 3D centres
as feature-bearing Points** in `openhcs_callable_artifact_authoring`: existing
`MeasurementsArtifactType` plus `PointROIOptions`, not a new Points artifact or
rounded centre-voxel image. When adding diagnostic Images, use that document's
**Return diagnostic images without flattening the ABI** section for the aligned
canonical image slot and trailing typed outputs.

Before writing source, verify the intended imports, decorators, helper types
and enum members against the actual public declarations for the installed
Expand Down
Loading
Loading