feat(transcription): Phase A — engine protocol + registry + orchestrator (clean-room)

Introduces the architectural spine for the Wyrdnote engine layer per
the spec at outputs/wyrdnote/2026-05-10-engine-architecture-spec.md
in the CORBEL-Main workspace, Codex-reviewed 2026/05/10.

Clean-room derivative of VoiceInk's TranscriptionService /
TranscriptionServiceRegistry / TranscriptionPipeline shape. Built from
the architectural pattern only; no GPL-3.0 code, comments, or
identifiers lifted. Wyrdnote names, control flow, and trait signatures
are original.

What this lands:

crates/cloud-providers/src/provider.rs (new, 184 lines)
  TranscriptionProvider async trait. Object-safe via async_trait so
  Arc<dyn TranscriptionProvider> is the canonical shape. Lives in
  cloud-providers (not transcription) per D7 so an OEM licensee
  implementing the trait depends only on this crate plus magnotia-core
  (no transcription internals leaked through the trait surface).
  Surrounding types: ProviderId (lower-kebab-case), ProviderKind,
  NetworkRequirement, CostClass, ProviderCapabilities, EngineProfile,
  ProviderTranscript. Compile-time object-safety witness in tests.

crates/cloud-providers/Cargo.toml (modified)
  Adds async-trait + serde dependencies.

crates/cloud-providers/src/lib.rs (modified)
  Re-exports the provider surface.

crates/transcription/src/registry.rs (new, 183 lines)
  EngineRegistry: catalogue of providers keyed by ProviderId. Push-style
  registration, default-provider id captured at construction. Read-only
  after boot. Full unit-test coverage: empty default, register/get
  round-trip, default-resolves-after-registration, re-register replaces,
  ids enumeration.

crates/transcription/src/orchestrator.rs (new, 286 lines)
  LocalProviderAdapter wraps Arc<LocalEngine>, presents async
  TranscriptionProvider upward via tokio::task::spawn_blocking. Per D7
  the adapter lives in the orchestrator, NOT as impl TranscriptionProvider
  for LocalEngine — this keeps the dependency edge one-directional and
  avoids leaking async-runtime requirements onto the synchronous
  Transcriber trait.

  Orchestrator: single transcribe() entry point; resolves a provider
  from the registry, derives TranscriptionOptions from the EngineProfile,
  dispatches. Returns a clear error when an unregistered provider is
  named. Unit tests use a mock CannedProvider to validate dispatch,
  error handling, and option routing without booting a real model.

crates/transcription/Cargo.toml (modified)
  Depends on magnotia-cloud-providers + async-trait. Dev-dep tokio
  gains macros + rt-multi-thread features for #[tokio::test].

crates/transcription/src/lib.rs (modified)
  Exports Orchestrator, EngineRegistry, LocalProviderAdapter, and
  re-exports the provider trait surface so downstream crates depend
  only on magnotia-transcription for both local engines and the trait.

KNOWN-ISSUES.md (modified)
  Adds KI-06: existing dictation, live, and meeting commands still call
  LocalEngine::transcribe_sync directly via pick_engine. The orchestrator
  path is dormant until a Phase A.1 follow-up commit migrates the call
  sites. Cloud providers (Phase G) cannot be exercised end-to-end until
  the rewire lands. Phasing rationale: this commit lands the abstractions
  cleanly with full test coverage; the rewire is a separate diff so the
  chunking + post-processing logic in transcribe_file moves without
  inflating this commit beyond review fidelity.

Test results: 9 new tests pass (orchestrator: dispatches, errors-on-
unregistered, routes-initial-prompt, object-safe; registry: empty-default,
round-trip, default-resolves, re-register, ids-list). Pre-existing
59 transcription tests + 5 cloud-providers tests still green.
cargo check --workspace clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 07:49:24 +01:00
parent d21109ec27
commit 6bc8acccce
9 changed files with 710 additions and 2 deletions

4
Cargo.lock generated
View File

@@ -2846,7 +2846,9 @@ dependencies = [
name = "magnotia-cloud-providers"
version = "0.1.0"
dependencies = [
"async-trait",
"magnotia-core",
"serde",
]
[[package]]
@@ -2925,7 +2927,9 @@ dependencies = [
name = "magnotia-transcription"
version = "0.1.0"
dependencies = [
"async-trait",
"futures-util",
"magnotia-cloud-providers",
"magnotia-core",
"num_cpus",
"reqwest 0.12.28",