Closes the wake-hysteresis deferral noted in 08-VERIFICATION.md by
wiring the production OpenWakeWordSession through the full corpus
loop and seeding labels.json with TTS-synthesized clips.
Voice package
-------------
Adds public `feed(samples: [Float])` overload on OpenWakeWordSession
alongside the existing `feed(_:UnsafeBufferPointer<Float>)` API. The
new overload exists specifically because Swift 6 disallows escaping
an unsafe buffer pointer from the sync `withUnsafeBufferPointer`
closure into an async-suspended actor hop — the harness runner's
`for clip in corpus.clips` loop hits exactly that pattern. Production
audio-tap callers keep the buffer-pointer path to avoid the per-frame
Array copy. Hysteresis logic is shared via `runHysteresis(pcmSamples:)`.
Harness runner
--------------
Replaces the `pipelineNotWired` diagnostic with the actual
mel→embedding→classifier feed loop:
- decode WAV via WAVDecoder (16 kHz mono Float32),
- slice into 1280-sample frames (80 ms @ 16 kHz, openWakeWord chunk),
- await session.feed(samples: frame) per frame,
- count TP/FN/FP/TN per clip label.
Computes farPerHour from totalDurationSeconds and frrPercent from the
positive set; runs the D-18 thresholds (PASS at FAR ≤ 1.0/hr AND
FRR ≤ 10%; WARN between published targets and the fail line).
Seed corpus
-----------
labels.json populated with 10 TTS-synthesized clips (5 positive
"Hey Jarvis" across Daniel/Samantha/Karen/Tom/Victoria voices at
varying rates 150–200 wpm, 5 negative phrases) generated via
macOS `say` + `afconvert` to 16 kHz mono PCM. Operators replace
these per-host per the README protocol; the corpus contract is
that BOTH labels are present, not the exact clip count.
MANIFEST.json
-------------
Pinned SHA-256 hashes for melspectrogram / embedding_model /
hey_jarvis_v0.1 (was placeholder). Locked weight versions:
ba2b0e0f... melspectrogram.onnx
70d16429... embedding_model.onnx
94a13cfe... hey_jarvis_v0.1.onnx
Tests
-----
- 60/60 swift-testing in 9 suites pass; renamed
`defaultCorpusIsEmpty` → `seedCorpusHasBothLabels` to reflect the
new ship-with-baseline contract.
- 17/17 Voice tests still pass after the public surface widening.
Latent Voice-package gap surfaced
---------------------------------
Running `jarvis-eval wake-corpus` end-to-end against the real ONNX
models reveals an ORT chain bug in OpenWakeWordSession.runORT that
predates Phase 8: the embedding model's input is named `input_1`
(not `input`), the classifier's input is named `x.1` (not `input`),
and the mel→embedding hop needs a 76-frame mel window with reshape
to [_, 76, 32, 1] before feeding the embedding stage. None of the
existing Voice tests exercise this path (they use the internal
`scriptedClassifier:` seam via `feedTest()`). Routing the fix to the
Voice phase since this is a P6 implementation gap, not a P8 harness
issue — the harness is now correctly surfacing the gap as a runtime
error instead of silently passing.
Closes deferral (e) from 08-VERIFICATION.md (operator-action portion);
flags the Voice ORT chain gap as a P6 follow-up via runtime advisory.