The node showed a colour image only, so debugging a depth pipeline meant
either trusting the numbers or writing a throwaway viewer. It now keeps one
panel per frame stream -- image and depth -- and tiles them side by side,
scaled to a common height. Wiring one is a plain viewer; wiring both shows
them together. bbox and text stay overlays rather than panels.
The contract follows what dora-zed already publishes: float32 [H, W] in
millimetres with 0 for missing. It is sent there as a raw byte slice, so the
payload arrives as UInt8 despite the Float32 contract and is reinterpreted
rather than converted.
Auto-ranging the colormap over the valid pixels' full extent, rather than over
a percentile of them, is deliberate and was not the first attempt: trimming
the tails collapsed on the scene the node exists for. A small object in front
of a large wall is a few percent of the pixels, so both tails landed on the
wall and the object clipped away. Rendering a frame and looking at it found
that; every test passed either way.
Verified with dora-cli 1.0.1: validate passes with both contracts injected,
the dataflow runs both nodes to a clean exit, and a composed frame was
rendered and inspected.
`dora validate` matches a node manifest to a `path:` node by entrypoint. With
no manifest beside examples/source.py the root dora-node.yml was tried against
it, did not match its entrypoint, and warned -- which `strict_types: true`
turns into a non-zero exit, failing CI.
Declaring the source's output types in the new manifest rather than as
`output_types:` in dataflow.yml is also what makes them checked: an inline
annotation shadows the manifest, so the manifest would be injected but never
compared against the viewer's.
Verified with dora-cli 1.0.1: validate passes with both contracts injected, and
`CI=true SOURCE_FRAMES=30 dora run dataflow.yml --uv` runs both nodes to a
clean exit.