Architecture and scientific boundaries¶
VIPP 0.12 separates scientific services from Qt presentation so the same validated behavior can support interactive execution, generated Python, and batch runs.
flowchart TD
N["napari / npe2"] --> W["_widget.py\ncomposition and viewer coordination"]
W --> G["_graph.py\ngraph presentation and user intent"]
W --> U["ui/\nQt components, adapters, controllers, workers"]
U --> C["core/\nscientific, persistence, execution, and batch services"]
W --> C
core/ must not import napari, Qt, _widget.py, _graph.py, or ui/. ui/
may import Qt and core/, but must not import _widget.py. Architecture tests
enforce those directions. _widget.py can see every layer because it composes
the application; reusable policy should still move to the narrowest owner.
Ownership map¶
| Responsibility | Primary owner |
|---|---|
| Numerical operations and parameter validation | core/operations.py |
| Node declarations, typed ports, and graph execution | core/pipeline.py |
| Axes, calibration, kind, channels, and history | core/metadata.py |
| Multi-image and image/PSF grid validation | core/grid.py |
| Exact statistics, histograms, percentiles, contrast, and label sizes | core/diagnostics.py |
| File/store identities and stable snapshots | core/source_identity.py, core/file_sources.py |
| Live napari-layer snapshots and invalidation | ui/source_adapter.py |
| Detached graph/workflow state | core/snapshots.py, core/workflow.py |
| Typed headless execution and Qt adaptation | core/execution.py, ui/workers.py |
| Atomic JSON/text replacement | core/atomic_io.py |
| Batch configuration, planning, and execution | core/batch_setup.py, core/batch.py |
| Retained batch UI and representative navigation | ui/batch.py, ui/batch_controller.py, ui/batch_navigator.py |
| Reusable controls, dialogs, plots, and dimensions | focused modules under ui/ |
| Final application/viewer coordination | _widget.py |
Non-negotiable scientific contracts¶
- Inputs are stable revisions. Source bytes and metadata are detached and revision-checked; stale results are rejected.
- Axes and physical coordinates are data. Explicit versus inferred semantic confidence matters. Multi-input compatibility includes axis meaning, size, scale, units, and origin—not shape alone.
- Diagnostic populations are declared and exact. Chunking limits temporary memory; it is not sampling. Channel behavior is explicit.
- Invalid scientific parameters fail visibly. Do not silently clamp, swap, fill, normalize, or substitute unless an explicit persisted policy defines that behavior.
- Operations preserve upstream buffers. Scientific kernels accept read-only inputs and do not mutate source or cached arrays.
- Presentation is detached. Viewer layers, contrast, colormaps, thumbnails, and provisional display ranges cannot change calculations.
- Persistence is validated and atomic per artifact. Detached snapshots are graph-validated; non-finite JSON is rejected; replacement is fsynced and atomic. Several related files are not one transaction.
- Batch publication follows source verification. Stage outputs, reverify every source, then promote; record partial publication explicitly.
- Export uses the shared executor. Generated Python must preserve graph, source, metadata, version, and operation semantics.
Execution boundaries¶
GraphSnapshot owns detached ordered nodes, connections, and tunnels.
WorkflowSnapshot adds positions, notes, and UI metadata. A document is
materialized through a temporary pipeline so ports, types, cycles, duplicate
inputs, and tunnel references are validated before it can replace live state.
Background work receives a typed PipelineRunRequest and returns a typed
PipelineRunResult. The Qt runnable forwards progress/results; the headless
service owns workflow reconstruction and calculation. A result is accepted
only while its run, graph, and source revision tokens remain current.
The same executor is used by the GUI and Export Python.... Batch adds a deterministic planner, source identity checks, staged output promotion, and manifest/sidecar provenance around that executor.
Adding or changing behavior¶
For a scientific operation, define the population, axes/ranks, dtype and unit behavior, parameter domain, output contract, edge cases, and reference method before implementing it. Update operation registration, metadata propagation, grid validation, workflow/export support, tests, user documentation, and release notes together.
For a source, reuse normalized I/O and preserve its ImageState; add identity
capture and post-materialization verification. For a diagnostic, put the exact
calculation in core/diagnostics.py, Qt rendering in ui/plots.py, and typed
worker adaptation in ui/diagnostic_workers.py.
Verification ladder¶
Use focused tests while developing, then run the complete release checks:
python -m npe2 validate src/napari_vipp/napari.yaml
python -m ruff check .
python -m pytest
python -m build
Add grid, source, snapshot, execution, batch, diagnostic, workflow, export, and Qt integration tests in proportion to risk. Passing internal tests is not a claim of assay-specific validation; keep that boundary visible in user-facing documentation.