Files
Jarvis/packages/Voice/Package.swift
james.maesandClaude Opus 4.7 e8bbbcfe10 fix(voice p1-4): RingBuffer index atomics via swift-atomics for explicit acquire/release semantics
Per audit 2026-05-04 concurrency HIGH-3: RingBuffer.writeIdx/readIdx
were plain UInt64 vars accessed cross-thread (Core Audio tap thread
producer; Swift Task consumer). The doc claimed "atomic load/store" but
no fences existed. ARM64 makes aligned 64-bit loads/stores atomic at the
hardware level, but Swift's optimizer is free to reorder, hoist, or
coalesce — meaning the consumer could observe writeIdx advanced past a
slot whose storage write hadn't yet committed.

Adopt swift-atomics 1.3.0 (already transitively resolved by other
packages). Switch writeIdx/readIdx to ManagedAtomic<UInt64> with
explicit ordering:
  - Producer: load writeIdx .relaxed, write slot, store writeIdx .releasing.
  - Consumer: load writeIdx .acquiring (synchronizes with producer's
    releasing store), read slot, store readIdx .releasing.
  - Lag computation: load both with .acquiring for a consistent snapshot.

New test R5 exercises the happens-before invariant: producer publishes
fingerprinted (chunk, slot)-encoded samples; consumer asserts it never
observes a 0.0 sample (uninitialized slot) under concurrent run. R1..R4
are unchanged behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 16:01:14 -05:00

81 lines
3.4 KiB
Swift

// swift-tools-version:6.0
// Phase 6 — Voice package.
//
// Package.swift is CLOSED for Phase 6: all three external dependencies are
// pinned here so Plans 06-02..06-05 never touch this file. Wave-2 plans
// (06-02 WakeWord + 06-03 VAD/STT) run in disjoint worktrees; their file
// ownership is limited to their own source subdirectories.
//
// Dep declarations carry NO source imports in this plan — those land in
// 06-02 (ORT for wake-word/VAD) and 06-04 (mlx-audio-swift for Orpheus).
// Resolution via `swift package resolve` is the v1 gate; import correctness
// is gated per plan.
import PackageDescription
let package = Package(
name: "Voice",
platforms: [.macOS(.v14)], // ORT 1.24.2 requires macOS 14+
products: [
.library(name: "Voice", targets: ["Voice"]),
],
dependencies: [
// Pinned for Plans 06-02/06-03 (wake-word + VAD) — NOT imported here.
.package(
url: "https://github.com/microsoft/onnxruntime-swift-package-manager",
from: "1.24.2"
),
// Pinned for Plan 06-03 (WhisperKit STT fallback) — NOT imported here.
.package(
url: "https://github.com/argmaxinc/argmax-oss-swift.git",
from: "0.18.0"
),
// Pinned for Plan 06-04 (Orpheus TTS tier 2) — NOT imported here.
.package(
url: "https://github.com/Blaizzy/mlx-audio-swift.git",
from: "0.1.2"
),
// P1-4 (audit 2026-05-04 concurrency HIGH-3): explicit
// acquire/release semantics for RingBuffer's writeIdx/readIdx.
// The producer/consumer cross thread without a fence; without
// explicit atomics the optimizer is free to reorder the index
// bump relative to the slot store.
.package(
url: "https://github.com/apple/swift-atomics.git",
from: "1.3.0"
),
],
targets: [
.target(
name: "Voice",
dependencies: [
// Added by Plan 06-02 (WakeWord) + 06-03 (SileroVAD): ORT inference
// for mel/embedding/classifier (06-02) and Silero VAD (06-03).
.product(name: "onnxruntime", package: "onnxruntime-swift-package-manager"),
// Added by Plan 06-03 (WhisperKit STT fallback).
.product(name: "WhisperKit", package: "argmax-oss-swift"),
// Added by Plan 06-04 (Orpheus TTS tier 2 via LlamaTTSModel).
.product(name: "MLXAudioTTS", package: "mlx-audio-swift"),
// Added by Plan 06-04 (TTSKit fallback, feature-flag gated).
.product(name: "TTSKit", package: "argmax-oss-swift"),
// P1-4 (audit 2026-05-04 concurrency HIGH-3): RingBuffer
// index atomics — explicit acquire/release semantics so
// Swift's optimizer can't reorder the index bump relative
// to the storage write.
.product(name: "Atomics", package: "swift-atomics"),
],
path: "Sources/Voice",
swiftSettings: [.swiftLanguageMode(.v6)],
linkerSettings: [
.linkedFramework("AVFoundation"),
.linkedFramework("Speech"),
]
),
.testTarget(
name: "VoiceTests",
dependencies: ["Voice"],
path: "Tests/VoiceTests",
swiftSettings: [.swiftLanguageMode(.v6)]
),
]
)