v0.44.0

thevoiceguy/siphon-aiv0.44.0Jul 26, 2026by github-actions[bot]

Upgrading?

Release notes summary

Addresses critical protocol compliance issues regarding playout clock management and outbound audio backpressure. The `clear` command now correctly handles pending marks, and the daemon implements the promised outbound buffer to prevent unbounded memory growth during server bursts.

Key Highlights

  • `clear` now drops pending marks and rebases the playout clock to prevent late firing (fixes #365).
  • Outbound audio backpressure is now implemented, limiting the buffer to 200ms and dropping oldest frames (fixes #366).
  • New metric `siphon_ai_outbound_audio_frames_dropped_total` tracks dropped frames.
  • Integration tests added to verify burst and mark behaviors.

New Features

  • Implementation of PROTOCOL.md §5.5 outbound-audio backpressure.

Full Release Notes

### Fixed

- **`clear` now drops pending `mark`s and rebases the playout clock — post-clear marks fire on time** (issue #365; PROTOCOL.md §4.1 violation). Marks were detached wall-clock timers (`tokio::spawn` + `sleep_until(first_push + frames × 20 ms)`) that nothing could cancel or re-time: a mark pending at clear-time **fired anyway** (telling the server the caller heard audio that was discarded), and because `clear` never reset the frame-count anchor, every mark sent **after** a clear fired **late by the entire flushed duration** — cumulative across repeated clears, breaking turn-taking in exactly the barge-in-driven calls that use marks most. Marks are now **queue-riders owned by the tap** (PROTOCOL.md §4.1's model): they hold their position in the outbound queue, arm against the playout clock when the audio ahead of them has been handed to the media engine, and die with the queue on `clear` / `auto_clear` / mute / park — every flush path now also rebases the playout clock (`clear` previously skipped it; the pause-arbitration paths already did this). Integration tests mirror the issue's timeline (burst → mark → clear → mark) and are CI-proven red on the pre-fix tap.

- **PROTOCOL.md §5.5 outbound-audio backpressure is now implemented — server bursts no longer queue unbounded** (issue #366). §5.5 promised a 200 ms outbound buffer with oldest-frame dropping and a `siphon_ai_outbound_audio_frames_dropped_total` metric; none of it existed — every frame a WS server sent was forwarded into forge's playout queue immediately, so a buggy or hostile server streaming faster than realtime grew per-call memory without bound while the caller heard ever-staler audio, with no metric, no error, and no log. The tap now owns a bounded outbound queue fed **just-in-time** into forge via the playout clock (≤ 100 ms in flight + the documented 10-frame/200 ms window, oldest-dropped beyond it, metric + one warn per call). Well-behaved paced senders (both server SDKs) never engage the holdback, and ≤ 200 ms TTS chunks are absorbed without loss; outbound DTMF rides the same queue so a burst-then-digit server keeps its ordering. Sustained over-rate streaming now degrades to "caller hears the newest audio" instead of "caller falls minutes behind". §5.5 rewritten to state the exact window; metric documented in DEPLOY.md. The queue is also what makes #365's marks droppable — the two fixes share the mechanism, and the burst-bounding test is CI-proven red on the pre-fix tap.

---

**Artifacts**

- Static musl binaries for `x86_64` and `aarch64` (`.tar.gz`).
- Debian packages (`.deb`) for `amd64` and `arm64` — `apt install ./siphon-ai_*_amd64.deb`.
- `SHA256SUMS` — verify with `sha256sum -c SHA256SUMS`.
- `*-sbom.cdx.json` — CycloneDX SBOM.
- `SHA256SUMS.cosign.bundle` — cosign keyless signature over the checksums.
- Container: `ghcr.io/thevoiceguy/siphon-ai:v0.44.0` (multi-arch, cosign-signed).

Verify the checksums signature:
```
cosign verify-blob --bundle SHA256SUMS.cosign.bundle \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp 'release.yml@' SHA256SUMS
```

Related Repositories