Skip to content

feat: unified provider architecture (Phase 1) — trait, registry, unified errors, validation, live OpenRouter#17

Open
mrap wants to merge 1 commit into
mainfrom
boi/S4117-provider-architecture-phase1
Open

feat: unified provider architecture (Phase 1) — trait, registry, unified errors, validation, live OpenRouter#17
mrap wants to merge 1 commit into
mainfrom
boi/S4117-provider-architecture-phase1

Conversation

@mrap
Copy link
Copy Markdown
Owner

@mrap mrap commented Apr 30, 2026

Summary

  • Provider trait (Provider: Send + Sync) replaces PhaseRuntimename, capabilities, validate_config, invoke, cost_estimate, actual_cost
  • ProviderRegistry with built-ins registered at startup: claude (always), openrouter (if API key present, else disabled with reason), deterministic (always)
  • ProviderError unified enum: NotConfigured, AuthFailed, RateLimit, Timeout, BadResponse, NetworkError, CapabilityMissing, BudgetExceeded
  • Validation lifecycle: registration-time → TOML-load-time (loud startup WARN) → pre-invocation; OpenRouter-runtime-drop bug now impossible
  • runner.rs refactored to registry lookup; BOI_FORCE_CLAUDE escape hatch removed
  • Unified telemetry: boi.phase.invoked / boi.phase.completed / boi.provider.error replace bespoke spawn events
  • CodexProvider added as third impl — proves extensibility without touching runner.rs or worker.rs
  • Live verification documented in docs/diagnostics/2026-04-30-provider-arch-live-verified.md
  • docs/providers.md — full trait contract + how-to-add-a-provider guide

Hard outcome

The OpenRouter-runtime-drop bug (discovered 2026-04-29) is now structurally impossible. Daemon either dispatches to the named runtime= provider or fails loudly at startup:

WARN: phase 'spec-critique' wants runtime='openrouter' but OPENROUTER_API_KEY is not set.
      Phases using this runtime will fail until configured.
      Add OPENROUTER_API_KEY to ~/.boi/.env.

Version target

v1.3.0 — please ship once reviewed. v1.2.0 was S9CE3 telemetry.

Test plan

  • cargo build passes
  • cargo test --lib passes (provider_trait, provider_error, provider_registry, provider_validation_startup, runner, telemetry_unified, codex_provider)
  • boi providers list prints claude + openrouter (or disabled) + deterministic
  • Daemon startup emits WARN for any phase with runtime=openrouter when key is absent
  • Live verification doc confirms OpenRouter dispatch end-to-end

🤖 Generated with Claude Code

…ied errors, validation, live OpenRouter

Implements axes 1, 2, 7, 8, 9 from the BOI provider architecture design doc:

- Provider trait: name, capabilities, validate_config, invoke, cost_estimate, actual_cost
- ProviderRegistry: built-ins (claude, openrouter, deterministic) registered at startup
- ProviderError: unified enum replacing bespoke per-provider error handling
- Validation lifecycle: registration-time, TOML-load-time (loud startup warnings), pre-invocation
- runner.rs refactored: registry lookup replaces if/else chain + BOI_FORCE_CLAUDE removed
- Unified telemetry: boi.phase.invoked / boi.phase.completed / boi.provider.error
- CodexProvider added as third impl (proves extensibility without touching runner.rs)
- Live verification: OpenRouter confirmed firing via daemon log + cost signature
- docs/providers.md: full Provider trait contract + how-to guide

Hard outcome: the OpenRouter-runtime-drop bug is now impossible. The daemon
either honors `runtime = "openrouter"` or fails loudly at startup with an
actionable message pointing to the missing env var.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant