bon-333: W2.3 events transfer — EventBus + 4 consumers#10
Merged
Conversation
…rs factory Lift bonfire.events from private V1 into public v0.1. Typed async publish-subscribe bus (C7 ordering: typed before global, sequential await, consumer isolation, monotonic sequence stamping via model_copy) plus 4 consumer implementations + wire_consumers factory. Infra-tier. Modules: - events/bus.py — EventBus (129 LOC, V1 verbatim) - events/__init__.py — re-exports EventBus + BonfireEvent - events/consumers/cost.py — CostTracker (latched warning/exceeded) - events/consumers/display.py — DisplayConsumer (sync+async callback) - events/consumers/logger.py — SessionLoggerConsumer (positional append) - events/consumers/vault_ingest.py — VaultIngestConsumer (in-file stub) - events/consumers/__init__.py — wire_consumers(*, bus, persistence, cost_tracker, display_callback, vault_backend) factory TDD provenance (dual-Knight + Sage synthesis): - Knight-A antawari/bon-333-knight-a @ e08506a: 94 tests, distributed- systems adversarial lens (concurrency, consumer isolation, ordering). - Knight-B antawari/bon-333-knight-b @ 0aa66d2: 83 tests, fidelity lens mirroring private V1 test_event_bus.py + consumer tests. - Sage antawari/bon-333-sage @ f15b132: 112 canonical tests (42 bus + 70 consumers), resolved 3 structural divergences: 1. Snapshot vs live iteration: DROPPED Knight-A's snapshot test — V1 uses live iteration (self._typed.get(type(event), [])), zero docstring evidence of snapshot intent. 2. VaultIngestConsumer: SURFACE ONLY (Knight-A direction) — V1's vault_ingest.py is a 5-LOC re-export; deep vault semantics out of W2.3 scope. Warrior chose Option A (in-file stub). 3. wire_consumers: IN SCOPE (V1 grep confirmed, Knight-B direction) — dropped persona + cost_ledger_path kwargs (absent in public). - Warrior antawari/bon-333-warrior @ 0c72276: 448 LOC across 7 files, V1 verbatim modulo three adaptations. Three adaptations from private V1: 1. Private refs scrubbed (no BON-NNN, no bonfire-beta#113). 2. BonfireEvent imports from bonfire.models.events (public post-BON-331). 3. wire_consumers dropped persona + cost_ledger_path (public v0.1 scope). Sage decisions doc: docs/audit/sage-decisions/bon-333-sage-20260418T004958Z.md Verification: 374 tests passing (262 existing + 112 new). Ruff clean. Closes BON-333. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Author
Pre-merge dual-lens gate — clearedBoth lenses ran in parallel on dedicated review worktrees. Full convergence: both recommend MERGE.
Invariants confirmed
Follow-ups (non-blocking)
Audit trail committed to this PR
Gate tier: Infra cleared. Ready to squash-merge into |
This was referenced Apr 22, 2026
Merged
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 2.3 of the public v0.1 transfer. Lifts
bonfire.events— the typed async publish-subscribe bus that wires every stage of the pipeline. Lands simultaneously with BON-332 (protocols) as the second half of Wave 2's transfer of foundation infrastructure.EventBus (129 LOC,
bus.py)subscribe(EventType, handler)) + global (subscribe_all)model_copy(update={"sequence": n})type(event) is event_type, NOT isinstance — subclasses don't fire parent handlers)4 Consumers
CostTracker(budget_usd, bus)— subscribes toDispatchCompleted, latched emits ofCostBudgetWarning(≥80%) +CostBudgetExceeded(≥100%)DisplayConsumer(callback, persona=None)— 4 typed subscriptions, sync/async detect, swallows callback exceptionsSessionLoggerConsumer(persistence)— globalsubscribe_all, positionalappend_event(session_id, event), swallows persistence exceptionsVaultIngestConsumer(backend, project_name)— 4 typed subscriptions (stage/dispatch/session events), in-file stub (deep semantics deferred to future vault wave)wire_consumersfactory (keyword-only)wire_consumers(*, bus, persistence, cost_tracker, display_callback, vault_backend) -> NoneTDD provenance
antawari/bon-333-knight-ae08506aantawari/bon-333-knight-b0aa66d2antawari/bon-333-sagef15b132antawari/bon-333-warrior0c72276Three structural divergences — Sage adjudications
1. Snapshot vs live iteration — DROPPED Knight-A's snapshot test
Knight-A asserted "handler registered mid-emit only seen by next emit" (snapshot semantics). V1's
bus.py:108iteratesself._typed.get(type(event), [])— a list reference (live). Zero docstring/test evidence of snapshot intent in 616 LOC of V1 tests. Decision: match V1's live iteration. The canonical does NOT assert snapshot behavior.2. VaultIngestConsumer — surface only (Knight-A direction)
Knight-A: re-export only (V1's
vault_ingest.pyis 5 LOC). Knight-B: full class with store semantics. V1 source confirms re-export; deep class lives inbonfire.vault.consumerwithVaultEntry+content_hashdependencies absent in public v0.1. Decision: surface-only contract (importable, 4 typed subscriptions, register method). Warrior chose Option A (minimal in-file stub).3.
wire_consumersfactory — in scope (Knight-B direction)V1 grep confirms
wire_consumersatevents/consumers/__init__.py:30-56with 4 V1 tests. Public v0.1 dropspersona+cost_ledger_pathkwargs (absent). Decision: lock the 5-kwarg public signature.Three adaptations from private V1
BonfireEventimports frombonfire.models.events(present post-BON-331).wire_consumerssignature trimmed to public v0.1 surface.Verification
Gate tier
Infra (per
docs/release-gates.md).Sage decisions doc:
docs/audit/sage-decisions/bon-333-sage-20260418T004958Z.md.Dual-lens pre-merge review follows.
Closes BON-333.
🤖 Generated with Claude Code