feat(observability): implement Event System Domain Event Taxonomy (full EventType enum + DomainEvent model)
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 3m26s
CI / build (pull_request) Successful in 14s
CI / typecheck (pull_request) Successful in 4m2s
CI / quality (pull_request) Successful in 4m2s
CI / security (pull_request) Successful in 4m23s
CI / integration_tests (pull_request) Successful in 9m35s
CI / unit_tests (pull_request) Successful in 9m39s
CI / docker (pull_request) Successful in 2m15s
CI / e2e_tests (pull_request) Successful in 12m32s
CI / coverage (pull_request) Successful in 11m41s
CI / status-check (pull_request) Successful in 1s
CI / build (push) Successful in 23s
CI / lint (push) Successful in 3m17s
CI / typecheck (push) Successful in 4m11s
CI / quality (push) Successful in 4m11s
CI / security (push) Successful in 4m28s
CI / benchmark-regression (push) Has been skipped
CI / integration_tests (push) Successful in 9m13s
CI / unit_tests (push) Successful in 9m26s
CI / e2e_tests (push) Successful in 10m20s
CI / docker (push) Successful in 1m16s
CI / coverage (push) Successful in 12m2s
CI / status-check (push) Successful in 1s
CI / benchmark-publish (push) Successful in 33m17s
CI / benchmark-regression (pull_request) Successful in 57m23s

Verified and completed the Event System Domain Event Taxonomy implementation.

Gap analysis:
- EventType enum (38 types across 12 domains): ALREADY EXISTED, verified complete
- DomainEvent Pydantic model (all 9 fields): ALREADY EXISTED, verified complete
- EventBus Protocol (emit + subscribe): ALREADY EXISTED, verified complete
- ReactiveEventBus (RxPY Subject, stream property): ALREADY EXISTED, verified
- LoggingEventBus (structlog-based): ALREADY EXISTED, verified complete

Added:
- In-memory audit_log on ReactiveEventBus (list[DomainEvent] with defensive copy)
- Emit ordering aligned with specification: RxPY stream push, then audit append,
  then handler dispatch (§Event System)
- Clarified audit_log docstring: volatile in-memory log, not durable SQLite
  persistence (durable persistence wired separately via audit service layer)
- Behave feature: features/observability/event_system_taxonomy.feature (36 scenarios)
- Step definitions: features/steps/event_system_taxonomy_steps.py
- Robot integration test: robot/event_system_taxonomy_integration.robot (13 test cases)
- Robot helper: robot/helper_event_system_taxonomy.py (13 subcommands)
- ASV benchmark: benchmarks/bench_event_bus.py (5 benchmark suites)
- vulture_whitelist.py: added audit_log property

Code review fixes applied:
- Reordered emit() to match spec: on_next → audit_log → handlers (was: audit → on_next → handlers)
- Clarified audit_log docstring to distinguish volatile in-memory log from spec SQLite table
- Fixed benchmark TaxonomyAuditLogSuite: parameterized setup instead of inline construction
- Added teardown to TaxonomyLoggingEmitSuite to restore logging.disable(NOTSET)
- Clear _received list between benchmark iterations to reduce noise
- Catch specific pydantic.ValidationError in frozen model mutation tests
- Consolidated duplicate singular/plural step definitions
- Tightened EventType count threshold from >=30 to >=38

Quality gates:
- lint: PASSED
- typecheck: PASSED (0 errors)
- unit_tests: 36/36 new scenarios PASSED (pre-existing 12 flaky failures unchanged)
- integration_tests: 1483/1483 PASSED
- security_scan: PASSED
- dead_code: PASSED

ISSUES CLOSED: #587
This commit was merged in pull request #712.
This commit is contained in:
CoreRasurae
2026-03-12 01:45:49 +00:00
committed by Luis Mendes
parent f6b8c0ffa9
commit 00881a3e5f
11 changed files with 1706 additions and 27 deletions
+4 -3
View File
@@ -93,7 +93,7 @@ satisfies it:
```python
from cleveragents.infrastructure.events import EventBus, ReactiveEventBus
bus = ReactiveEventBus()
bus = ReactiveEventBus(max_audit_log_size=5000) # optional retention cap
assert isinstance(bus, EventBus) # True
```
@@ -124,7 +124,7 @@ bus = ReactiveEventBus()
# Subscribe a plain callback
bus.subscribe(EventType.PLAN_CREATED, lambda e: print("Plan created:", e.plan_id))
# Use the raw RxPY stream for advanced operators
# Use the read-only RxPY stream for advanced operators
bus.stream.pipe(
rx.operators.filter(lambda e: e.plan_id is not None),
).subscribe(lambda e: print("Plan event:", e))
@@ -136,7 +136,8 @@ bus.emit(DomainEvent(event_type=EventType.PLAN_CREATED, plan_id="01ABC..."))
- `emit(event)` pushes to the RxPY Subject **and** calls all type-specific
handlers synchronously.
- `subscribe(event_type, handler)` registers a callback for one event type.
- `stream` exposes the raw `rx.Observable` for RxPY operators.
- `stream` exposes a read-only `rx.Observable` view for RxPY operators.
- `clear_audit_log()` clears retained in-memory events when needed.
---