Implements AuditEventSubscriber that subscribes to all 9 security-relevant
EventType members and persists redacted audit entries via AuditService.record().
Key components:
- AuditEventSubscriber: bridges EventBus and AuditService (SEC7)
- SECURITY_EVENT_MAP: maps EventType enum to audit type strings
- Redaction via redact_dict() on event details before persistence
- Graceful error handling: failures logged, never propagated
Post-review fixes applied:
- BUG-1: Remove dead correlation_id null-check guard (DomainEvent.correlation_id
is always non-None via ULID default_factory)
- SEC-2: Redact exception messages in warning logs via redact_value() to prevent
potential leakage of sensitive internal state (e.g. DB connection strings)
- PERF-3: Pre-generate unique DomainEvent instances in ASV benchmark setup to
avoid skew from reusing a single frozen object
- Wire event_bus from the DI container into CorrectionService (plan.py),
ConfigService (config.py, skill.py x2, server.py), and
PersistentSessionService (session.py) at their CLI construction sites.
Closes#581
Implement the structured metrics collection framework covering 14
operational metric types with proper Histogram, Counter, and Gauge
semantics per the spec (Architecture > Observability > Metrics
Collection, lines ~43805-43825).
Domain layer:
- Add MetricType enum (HISTOGRAM, COUNTER, GAUGE) to metrics.py
- Add MetricDefinition model and METRIC_DEFINITIONS registry mapping
all 14 OperationalMetricKey values to their metric types
- Extend MetricCollector with typed factory methods (histogram, counter,
gauge) and 14 convenience methods (plan_duration, plan_cost,
plan_decision_count, subplan_count, actor_invocation_count,
actor_latency, tool_invocation_count, tool_error_rate,
context_build_time, context_token_count, llm_call_count,
llm_total_tokens, llm_total_cost, llm_avg_latency)
- Extend MetricEntry with optional metric_type field that auto-resolves
from METRIC_DEFINITIONS via MetricCollector.record()
Infrastructure layer:
- Add MetricsEmitter (infrastructure/observability/metrics_emitter.py)
with emit(), emit_batch(), from_settings(), and enabled/disabled
support for structured log emission in local mode
- Add metrics_log_processor (config/metrics_processor.py) for structlog
integration
Configuration:
- Add metrics_enabled and metrics_export_prometheus settings
- Register MetricsEmitter as DI Singleton in application container
Instrumentation:
- Add best-effort metric emission in PlanExecutor for plan_duration
(both runtime and stub execute paths) and plan_decision_count
(strategize path) via _try_emit_metric helper that tolerates invalid
plan IDs in test fixtures
Testing:
- 34 Behave BDD scenarios (features/observability/metrics_collection.feature)
- 8 Robot Framework integration tests (robot/metrics_collection.robot)
- ASV benchmark suite (benchmarks/bench_metrics_collection.py)
Closes#579