core: Add inference pipeline traits, types, and mocks
Foundation for the three-stage face recognition pipeline (detect → align →
embed). The traits exist now so the daemon, integration tests, and (later)
NixOS VM tests can be wired against them while the real ONNX-backed
implementations land in subsequent sub-milestones (M3.2 detector, M3.4
embedder).
Types worth flagging:
- `Embedding` is a Vec<f32> newtype with L2-normalisation built into the
preferred constructor (`Embedding::normalized`) and cosine similarity
as a plain dot product. Pre-normalising lets the matching layer skip
per-comparison magnitude divisions in the hot path. Magnitude-zero
inputs round-trip without dividing by zero so a degenerate embedder can
never panic the daemon.
- `Landmarks` fixes the canonical InsightFace order (left eye, right eye,
nose, mouth-left, mouth-right). The affine alignment in M3.3 depends on
this order being stable; nailing it down here means future code that
iterates landmarks (via `Landmarks::iter`) doesn't have to negotiate.
- `PipelineError` deliberately doesn't derive `Clone` — the scripted mock
implements re-emission with an explicit per-variant clone helper, so
adding a new variant in the future requires a conscious decision about
how it should propagate through scripted timelines.
Mock pair: `FixedDetector` / `FixedEmbedder` for the common 'return the
same thing on every call' pattern; `ScriptedDetector` for tests that need
a specific timeline. Property tests on the embedding math (magnitude
preservation under normalisation, cosine commutativity, never-NaN on
well-formed inputs) guard the invariants the matching layer in M4 will
rely on.