bon-335: W3.2 dispatch transfer — execute_with_retry + TierGate + backends#11
Merged
Conversation
…/PydanticAI backends Lift bonfire.dispatch from private V1 into public v0.1. Retry engine, tier gating, dispatch results, two AgentBackend implementations. Infra-tier gate. Modules: - dispatch/__init__.py — re-exports (5 names) - dispatch/result.py — DispatchResult (frozen Pydantic, 4 fields) - dispatch/tier.py — TierGate (stateless, returns True in v0.1) - dispatch/runner.py — execute_with_retry (deterministic backoff, never raises, event emission, terminal-error frozenset) - dispatch/sdk_backend.py — ClaudeSDKBackend (axiom-load paths removed) - dispatch/pydantic_ai_backend.py — PydanticAIBackend (lazy import) TDD provenance: - Knight-A antawari/bon-335-knight-a @ 0853e04: 137 tests, resilience lens - Knight-B antawari/bon-335-knight-b @ 139c7ee: 92 tests, fidelity lens - Sage antawari/bon-335-sage @ f310f0c: 151 canonical tests, 5 scope decisions (pydantic_ai kept, dispatch_wiring cut, no resolver, no jitter, no budget short-circuit in dispatch) - Warrior antawari/bon-335-warrior @ 846919c: 570 LOC minimal impl Three adaptations from private V1: 1. Private refs scrubbed (no BON-NNN, no bonfire-beta#113). 2. Public v0.1 imports (AgentBackend/DispatchOptions from bonfire.protocols; events from bonfire.models.events). 3. Axiom-load paths removed from sdk_backend (no compiler interface in v0.1); _TERMINAL_ERROR_TYPES has 5 entries (V1 has 6; axiom_load dropped). Sage decisions doc: docs/audit/sage-decisions/bon-335-sage-2026-04-18T03-42-14Z.md Verification: 639 tests passing (488 baseline + 151 new). Ruff clean. Closes BON-335. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wizard + code-reviewer parallel pass. Both APPROVE. Non-blocking findings only: - Wizard: 1 NIT (Sage __all__ doc drift) - code-reviewer: 0 critical / 0 high / 2 low / 6 info Invariants confirmed: 639 tests green, ruff clean, scrub clean, _TERMINAL_ERROR_TYPES=5 (axiom_load correctly dropped), DispatchResult frozen, public v0.1 imports correct, retry algorithm V1-identical.
Contributor
Author
Dual-Lens Pre-Merge Review — APPROVEDBoth lenses merge-approve. Squash-merging. Verdicts
Invariants Confirmed
Non-Blocking Follow-Ups (file post-merge)
Audit Trail
GateInfra-tier gate: CLEARED. Merging via squash. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Wave 3.2 of the public v0.1 transfer. Lifts
bonfire.dispatchfrom private V1 — the retry/tier/backend layer that wraps every agent execution.Modules
dispatch/runner.py—execute_with_retry(deterministic exponential backoff, terminal-error frozenset, event emission, never raises)dispatch/tier.py—TierGate(stateless; returnsTruein v0.1 — full tier routing defers to W7)dispatch/result.py—DispatchResult(frozen Pydantic, 4 fields:envelope,duration_seconds,retries,cost_usd)dispatch/sdk_backend.py—ClaudeSDKBackend(Claude Agent SDK backend)dispatch/pydantic_ai_backend.py—PydanticAIBackend(lazy import; loads withoutpydantic_aiinstalled)Locked retry algorithm
frozenset[str]):{AgentError, RateLimitError, config, CLINotFoundError, executor}— no retry.delay = retry_delay * (2 ** attempt_index), no jitter.retriescounts retries (not attempts);retries == 0for first-attempt success.execute_with_retryNEVER raises; infrastructure exhaustion →ErrorDetail(error_type="infrastructure", ...).DispatchStarted, NDispatchRetry(N ==result.retries), terminal =DispatchCompletedXORDispatchFailed.TDD provenance
antawari/bon-335-knight-a0853e04antawari/bon-335-knight-b139c7eetest_dispatch_runner/tier/sdk_backend/pydantic_ai_backendantawari/bon-335-sagef310f0cantawari/bon-335-warrior846919c5 scope adjudications (Sage decisions)
pydantic_ai_backend: KEEP — 77 LOC, module-levelAgent: Any = Nonesentinel, lazy import insideexecute(). SealsAgentBackendprotocol as provider-agnostic.dispatch_wiring: CUT — V1's wiring tests targetbonfire.cli.commands.dispatch+ XPTracker composition. None exist in public v0.1; CLI is flat.resolve_llm_backend: OUT — grep confirms zero matches in V1'ssrc/bonfire/dispatch/; resolver lives in cartographer, not dispatch.Three adaptations from private V1
sdk_backend;_TERMINAL_ERROR_TYPEShas 5 entries (V1 has 6 —axiom_loaddropped because there's no compiler interface in v0.1).Verification
Gate tier
Infra (per
docs/release-gates.md).Sage decisions doc:
docs/audit/sage-decisions/bon-335-sage-2026-04-18T03-42-14Z.md.Dual-lens pre-merge review follows.
Closes BON-335.
🤖 Generated with Claude Code