Novolis Docs
novolis-governance / completed-plans/audio_cleanup_plan_97b57667.plan.md

Audio cleanup: XML docs + ATC to dogfooding

dotnetgovernancenovolis

name: Audio cleanup plan overview: Bring novolis-audio in line with governance XML-doc policy (strict CS1591 on all packable public APIs), relocate Novolis.Audio.Voice.Atc to dogfooding, and ensure generic GPR libraries (Avalonia.Voice, Voice.Design) never require Platform.Windows so Linux CI can build. todos:

  • id: decouple-platform-windows

content: Remove Platform.Windows from Novolis.Avalonia.Voice; add optional preview factory hook; wire Windows TTS in NovolisVoiceStudio only status: completed

  • id: xml-docs-strict

content: Document all public/protected APIs in packable novolis-audio projects; fix CS1591; add build/.novolis-documentation-complete + doc-audit status: completed

  • id: dogfooding-voice-lib

content: Create Novolis.Dogfooding.Voice (non-packable) with moved ATC types + code-export extensions status: completed

  • id: decouple-design

content: Remove ATC refs from Voice.Design (EffectChainBuilder, Draft, CodeEmitter enum); trim to generic templates status: completed

  • id: remove-atc-package

content: Delete Novolis.Audio.Voice.Atc from novolis-audio slnx/docs; bump version; GPR publish status: completed

  • id: migrate-consumers

content: Update dogfooding apps + tests + Directory.Packages.props; optional NovolisVoiceStudio export templates status: completed isProject: false


Goals

  1. Documentation — All packable projects under `novolis-audio/src` satisfy documentation policy: GenerateDocumentationFile, no CS1591 gaps on public/protected members, README Install + Quick start.
  2. ATC scope — Remove `Novolis.Audio.Voice.Atc` from GPR; host ATC/radio delivery in dogfooding as `Novolis.Dogfooding.Voice` (non-packable, ProjectReference only).
  3. Keep GPR Design generic — `Novolis.Audio.Voice.Design` stays on GPR for `Novolis.Avalonia.Voice`; ATC-specific code export moves to dogfooding extensions (your choice).
  4. Platform.Windows is opt-in — Generic GPR/UI packages target net10.0 only; `Novolis.Audio.Voice.Platform.Windows` is referenced only by Windows hosts (e.g. NovolisVoiceStudio), fixing avalonia merge CI.
flowchart TB
  subgraph gpr [GPR novolis-audio]
    Voice[Novolis.Audio.Voice]
    Profiles[Novolis.Audio.Voice.Profiles]
    Design[Novolis.Audio.Voice.Design]
    Effects[Novolis.Audio.Effects]
  end
  subgraph dogfood [novolis-dogfooding]
    DogVoice[Novolis.Dogfooding.Voice]
    Studio[NovolisVoiceStudio]
    Bridge[BridgeCommander]
  end
  Design --> Voice
  Design --> Effects
  DogVoice --> Voice
  DogVoice --> Profiles
  DogVoice --> Effects
  DogVoice --> Phraseology[Novolis.Audio.Voice.Phraseology]
  Studio --> Design
  Studio --> DogVoice
  Bridge --> DogVoice

Part A — XML documentation (strict public API)

Current state

Approach

StepAction
1Run Release build and collect CS1591 list; use `novolis-governance/scripts/add-missing-xml-docs.ps1` (iterative) as a bootstrap, then hand-fix summaries/<param>/<returns> on non-obvious APIs.
2Prioritize voice stack packables: Voice, Voice.Abstractions, Voice.Profiles, Voice.Phraseology, SherpaOnnx, Kokoro, Platform.*, Design, Effects, Filters, Playback, Core.
3Document public fields on design types (`VoiceDeliveryEffectStep`, `VoicePresetDraft`) or convert to properties with { get; set; } if that matches repo style elsewhere.
4Run `doc-audit.ps1` with -RequireDocumentationProps; fix README gaps (Install / Quick start) on any new packages.
5Add build/.novolis-documentation-complete only when dotnet build -c Release is 0 warnings on packable projects and doc-audit passes.

CI alignment

  • Ensure PR workflow (via novolis-workflows) runs doc-audit when marker is present, or add explicit doc-audit step to `pull-request.yml` after marker lands.

Part B — Move ATC to dogfooding

What moves (4 types today)

From `Novolis.Audio.Voice.Atc`:

FileRole
`AtcVoiceOptions.cs`Radio/phraseology delivery DTO
`AtcVoiceProfile.cs`ApplyDelivery on VoiceServiceBuilder
`AtcRadioEffects.cs`atc-radio filter chain
`AtcVoiceServiceCollectionExtensions.cs`AddNovolisAtcVoice DI sugar

New project: novolis-dogfooding/apps/shared/Novolis.Dogfooding.Voice/

  • IsPackable=false
  • Namespace: Novolis.Dogfooding.Voice (types renamed optional: keep AtcVoiceOptions / AtcVoiceProfile names to minimize churn)
  • PackageReferences (GPR): Novolis.Audio.Voice, Novolis.Audio.Voice.Profiles, Novolis.Audio.Voice.Phraseology, Novolis.Audio.Voice.SherpaOnnx, Novolis.Audio.Effects, Novolis.Audio.Filters — not Novolis.Audio.Voice.Atc
  • Add to `Novolis.Dogfooding.slnx`

Dogfood consumers (switch PackageReference → ProjectReference):

Decouple GPR Voice.Design from ATC

`Voice.Design` must not reference ATC after removal.

AreaChange
`VoiceEffectChainBuilder.cs`Remove AtcVoiceProfile.ApplyDelivery fallback; when EffectSteps is empty, apply phraseology/radio only via existing BuildFilters + NormalizeWith from draft flags (mirror current step logic).
`VoicePresetDraft.cs`Remove ToAtcOptions() and using Novolis.Audio.Voice.Atc; keep legacy scalar fields for UI or derive purely from EffectSteps.
`VoicePresetCodeEmitter.cs`Remove EmitAtcDelivery / AtcDeliveryStatic template and BridgeCharacter template or keep enum values but move emitters to dogfooding (recommended: trim enum to generic templates only: ArchetypeCatalogEntry, UsageSnippet).
Dogfooding extensionNew VoicePresetCodeEmitterAtcExtensions (or static DogfoodingVoiceCodeEmitter) in Novolis.Dogfooding.Voice implementing former AtcDeliveryStatic + BridgeCharacter output using AtcVoiceOptions.
`VoiceCodeExportPanel`Remains bound to GPR VoicePresetCodeTemplate; dogfood-only templates registered in NovolisVoiceStudio subclass or optional extra combo (studio-only), not in Avalonia package.

Remove ATC from novolis-audio

Tests

TestsDestination
AtcVoiceProfile_*, AtcRadioEffects_*, AudioEffectsTests ATC pathsNew novolis-dogfooding/tests/Novolis.Dogfooding.Voice.Unit or keep minimal coverage in dogfood app smoke
`VoiceArchetypeCatalogTests` ApplyDeliveryMove to dogfooding tests
`VoicePresetCodeEmitterTests` ATC deliveryMove to dogfooding
`VoiceStackTests`Remove AtcVoiceProfile from assembly scan; add dogfooding assembly check

Part D — Platform.Windows not required from generic libraries

Problem (avalonia merge CI #30)

`Novolis.Avalonia.Voice` targets `net10.0` but has a compile-time PackageReference to `Novolis.Audio.Voice.Platform.Windows` (net10.0-windows10.0.19041.0). Linux CI cannot build that dependency graph (same class of error as audio before EnableWindowsTargeting on the Windows package itself).

Today `PlatformVoicePreviewFactory` uses reflection, but the package reference still pulls the Windows TFM into the Avalonia build.

Design rule

LayerMay reference Platform.Windows?
Novolis.Audio.Voice, Voice.Design, Platform.Abstractions, Kokoro, SherpaOnnxNo
Novolis.Avalonia.Voice (GPR, net10.0)No
NovolisVoiceStudio, Novolis.Dogfooding.Voice (net10.0-windows host)Yes
Novolis.Audio.Voice.Platform.Windows (GPR, windows TFM + EnableWindowsTargeting for pack on Linux)Standalone optional package

Implementation

  1. Remove from `Novolis.Avalonia.Voice.csproj` and `Directory.Packages.props`: Novolis.Audio.Voice.Platform.Windows.
  2. Extend `VoicePreviewController` with an optional host hook, e.g. Func<VoicePresetDraft, IVoiceService>? PlatformPreviewFactory (or IVoicePreviewVoiceFactory interface in Avalonia.Voice).
  • When draft.Backend == Platform and factory is null → clear status: "Platform TTS preview requires a Windows host; set PlatformPreviewFactory."
  • Sherpa/Kokoro unchanged: still use `VoicePresetPreviewFactory`.
  1. Delete PlatformVoicePreviewFactory.cs from Avalonia.Voice (move wiring to host).
  2. NovolisVoiceStudio (`NovolisVoiceStudio.csproj`, already net10.0-windows):
  • PackageReference / ProjectReference to Novolis.Audio.Voice.Platform.Windows
  • On startup: previewController.PlatformPreviewFactory = (draft) => new WindowsPlatformVoiceService(draft.Platform ?? new(), phraseology?)
  1. Voice.Design — keep platform preview throwing PlatformNotSupportedException (host-provided); no Windows package reference (already true).
flowchart LR
  AvaloniaVoice[Novolis.Avalonia.Voice net10.0]
  Design[Novolis.Audio.Voice.Design]
  Studio[NovolisVoiceStudio windows]
  PlatWin[Novolis.Audio.Voice.Platform.Windows]
  AvaloniaVoice --> Design
  Studio --> AvaloniaVoice
  Studio --> PlatWin

Verify

  • dotnet build novolis-avalonia on Linux (CI) — must pass without Windows targeting on Avalonia projects.
  • dotnet build NovolisVoiceStudio on Windows — platform preview still works.

Can land early as a small avalonia PR before the full ATC/doc cleanup.


Part C — Breaking change and publish

GPR breaking change (document in release notes):

  • `Novolis.Audio.Voice.Atc` deprecated/removed — use Novolis.Dogfooding.Voice source or copy AtcVoiceProfile pattern into your app.
  • `Novolis.Audio.Voice.Design` — VoicePresetCodeTemplate.AtcDeliveryStatic / BridgeCharacter removed from GPR enum (dogfooding-only export).
  • Consumers on GPR: `Novolis.Audio.Voice` + `Profiles` + optional `SherpaOnnx` / `Kokoro` / `Platform.Abstractions`; add `Platform.Windows` only in Windows executables.
  • `Novolis.Avalonia.Voice` — no longer depends on Platform.Windows; hosts supply platform preview via factory.

Version: Bump `build/version.json` minor; publish novolis-audio to GPR; then update dogfood/avalonia Directory.Packages.props after packages land.

Verify: verify-nuget-only.ps1, dotnet build, dotnet test in both repos.


Recommended PR split

PRRepoContents
0novolis-avaloniaHotfix: remove Platform.Windows from Novolis.Avalonia.Voice; preview factory hook; wire in NovolisVoiceStudio — unblocks CI #30
1novolis-audioXML docs + .novolis-documentation-complete (no ATC move)
2novolis-audioRemove Voice.Atc; decouple Voice.Design; doc/README updates
3novolis-dogfoodingAdd Novolis.Dogfooding.Voice; migrate apps/tests; studio Platform.Windows + export extensions
4novolis-avaloniaDogfood-only code-export templates (if needed beyond studio app)

Non-goals (this cleanup)