3.1 KiB
audio-core spec delta — refresh-audio-queue-2026-05-23
ADDED Requirements
Requirement: AnalysisOp operator contract
The audio-core package SHALL expose an AnalysisOp<TConfig, TResult> operator contract that every @suaveplan/audio-analysis-* package implements, and a createAnalysisOp builder that constructs conforming operators with Zod-validated config, OTel instrumentation, and AbortSignal-aware cancellation.
AnalysisOp is a discriminated operator with: name: string (unique within an analysis pipeline), version: string (semver), capabilities: AnalysisCapabilities (streaming/offline/realtime/blocking flags), configSchema: z.ZodTypeAny, run(doc: AudioDocument, config: TConfig, opts?: { signal?: AbortSignal }): Promise<TResult>, and runStream?(source: AsyncIterable<AnalysisFrame>, config: TConfig, opts?: { signal?: AbortSignal }): AsyncIterable<TResult>.
AnalysisResult is the base shape every analyser extends — { analyserName: string; analyserVersion: string; sampleRate: number; durationSamples: number; computedAt: number }. Concrete analyser results add their own fields on top.
Scenario: builder validates config via Zod
- WHEN
createAnalysisOp({ name, version, capabilities, configSchema, run })is called and a downstream caller invokesop.run(doc, badConfig) - THEN the builder MUST reject with
AudioError("AUDIO_CORE.ANALYSIS_INVALID_CONFIG")carrying the Zod issues oncontext.issues, beforerunis invoked
Scenario: builder instruments runs via OpenTelemetry
- WHEN
op.run(doc, validConfig)is called and succeeds - THEN the builder MUST emit one OTel span named
analysis.${name}with attributes{ "analysis.name": name, "analysis.version": version, "audio.sample_rate": doc.sampleRate, "audio.duration_samples": doc.sourceLength }, increment counteraudio_analysis_runs_total{analysis=${name}}, and record histogramaudio_analysis_duration_ms{analysis=${name}}
Scenario: builder forwards AbortSignal
- WHEN
op.run(doc, validConfig, { signal })is invoked andsignal.aborted === truebeforerunreturns - THEN the inner
runcallback MUST observe the signal and the builder MUST surface the abort asAudioError("AUDIO_CORE.ANALYSIS_ABORTED")
Scenario: capability flags advertise streaming support
- WHEN a caller inspects
op.capabilities.supportsStream - THEN the value MUST equal
typeof op.runStream === "function"
Requirement: AnalysisFrame streaming window descriptor
The audio-core package SHALL export an AnalysisFrame shape — { samples: Float32Array[]; sampleRate: number; channelCount: number; startSample: number; sampleCount: number } — usable as the streaming-input element type for any AnalysisOp.runStream implementation. The shape is types-only; no runtime construction helper is mandated.
Scenario: streaming analyser consumes AnalysisFrame iterable
- WHEN a spectrum analyser's
runStream(source, config)is invoked with anAsyncIterable<AnalysisFrame> - THEN the analyser MUST process each frame independently and MAY yield zero, one, or many
AnalysisResult-extending values per input frame