# Speech socket/TLS read timing experiment

The separate-TX-lock physical trial still failed. For speech ID
`d768a400843341708b2285ad79649ccf`, the Gateway completed 157 chunks,
551540 PCM bytes, and `speech_end`. Its maximum send and lock wait were
0.278 ms and 0.023 ms. The device accepted a sequential prefix of 94 chunks
(331632 bytes / 3.76 seconds of audio), wrote all accepted bytes without
codec errors, and hit the unchanged 3-second receive idle timeout.
Those writes spanned 41.349 seconds. Four more chunks, sequences 94–97,
arrived after timeout; no end was seen and the connection subsequently
failed. The user reported slightly improved sound but no intelligible words.
This remains **class B / physical FAIL**, not a playback pass.

Evidence is saved on the Mac at `/tmp/speech-tx-lock-ab-0.5.1/`, including
both captures, correlation outputs and `attempt-report.json`. Captured chunk
lines are sampled: the 94-chunk count comes from the receiver/queue summary.
The first unobserved sequence is 98, which does not by itself prove packet
loss. The first chunk rejected after its receive deadline was sequence 94.

## Next hypothesis

Application dispatch is about 10 ms while frame/inter-part delays reach
seconds. The uncompressed Mac public-WSS control received a complete stream,
but it does not prove every network route or the ESP32 radio is healthy.
The next experiment adds timing evidence to distinguish waiting for TCP data,
TLS read work, and time between receive operations. It does not yet implement
a transport fix.

## Implementation and limits

`CONFIG_DEVICE_LAB_SPEECH_TRANSPORT_TRACE` depends on the existing speech
diagnostics and defaults off. This isolated experiment enables it. Linker
wrappers call the original `esp_transport_poll_read`, `esp_transport_read`,
`esp_tls_conn_read`, and `mbedtls_net_recv` with the same arguments, return the
same results, and preserve `errno` around metadata inspection.

Only calls from the WebSocket receive task are measured. A manual
`speech_request` arms a 120-second **diagnostic window**; the speech idle
timeout stays at 3000 ms. Existing TLS certificate verification and transport
construction are unchanged. The Gateway remains the separate 40-ms branch;
PS=0, separate TX lock, queue=8, credentials, text and AP profile are unchanged.

A fixed 16-record single-producer/single-consumer ring buffers metadata. The
existing input sender flushes at most four records per iteration. There are
no new tasks, payload logging or receive-buffer allocations. This still adds
timing and socket-query overhead; compare it as an observability experiment,
not as an audio improvement. Ring overflow is explicitly reported.

`[TRANSPORT_RX]` records report stage, operation/parent IDs, start/duration,
gap since the previous completion at that stage, requested/returned size,
socket queued bytes before/after, TLS buffered application bytes before read,
task priority/core, and aggregate counters. Slow calls/gaps (>=250 ms), errors,
the first call, and every 32nd call are sampled. Socket query failure is `-1`.

Nested operations overlap in time. Do not sum durations or byte counters
across stages. Transport counters include headers and repeated wrapper levels;
socket counters include TLS protocol bytes and are not PCM/chunk counts.
Gaps are per stage across handles, not one-way network latency. Log timestamps
are flush times; use `start_ms + elapsed_ms` for operation completion and
correlate with the same physical speech ID in the existing receive traces.

Save both captures and classify the trial before making another change. A
complete, intelligible human result is still required for physical PASS.
