Originally created by: QY-25123
doberman_implementation_plan.mdThis PR adds the CostObserver plugin seam to the existing CB.1 cost meter.
After a CostEvent is successfully written to the local cost_events ledger, Doberman discovers and notifies plugins registered under the doberman.cost_observers entry-point group.
The implementation follows the existing DriftObserver and AuditSink plugin patterns:
CostObserver protocol with on_cost(event)discover_cost_observers() to the plugin registryA misbehaving observer cannot:
PASS, AUTH, or BLOCK verdictFiles changed:
src/doberman/storage/cost.pyCostObserver_looks_like_cost_observernotify_cost_observerswires observer notification into record_cost_event
src/doberman/engine/registry.py
COST_OBSERVER_GROUP = "doberman.cost_observers"adds discover_cost_observers()
tests/unit/test_cost_observer.py
adds unit tests for observer discovery, fan-out, isolation, and cost-meter integration
README.md
tests/unit/test_cost_observer.pyCostObserver protocol shape_looks_like_cost_observer with valid, missing, and non-callable on_cost attributesdiscover_cost_observers() returns [] when no plugin is registeredrecord_cost_event() notifies observers after a successful ledger writerecord_cost_event() does not raise when an observer failsverifies observers are not called when the database write fails
Existing tests/unit/test_cost_meter.py tests were also run to confirm that CB.1 behavior remains unchanged.
Local checks run:
ruff check .
ruff format --check .
lint-imports
pytest tests/unit/test_cost_observer.py tests/unit/test_cost_meter.py -v
pytest --cov=doberman --cov-report=term-missing -q
on_cost method is logged and skipped.The main risk is third-party observer behavior. This is mitigated through defensive discovery, structural validation, exception isolation, and by keeping observers outside the authorization decision path.
AI assistance was used to:
CostEvent and plugin architectureDriftObserver and AuditSink patternsI reviewed the implementation, fixed test failures, ran the checks, and validated the submitted changes.
Ticket changed by: QY-25123
Ticket changed by: QY-25123
Ticket changed by: QY-25123
Ticket changed by: QY-25123
Originally posted by: fu351
Thank you — this is a clean, well-isolated plugin seam, consistent with the existing registry/discovery precedent, and I appreciate the thorough tests. One tiny thing before merge: the docstring in storage/cost.py says each observer 'receives its own copy of the event,' but observers actually get the same frozen CostEvent instance (your own test_notify_passes_copy_not_original documents this correctly) — could you tweak that one line of wording? Once that's in and CI has run green I'll merge. Thanks for contributing, and apologies for the slow turnaround!
Ticket changed by: fu351
Originally posted by: fu351
Thank you for the CostObserver seam — you mirrored the DriftObserver/AuditSink pattern almost exactly, and didn't just implement isolation but tested it (a raising/malformed observer is logged & skipped, never blocks the ledger or decision), ~195 test lines. Appreciated the transparency about AI assistance + personal validation. One thing needed fixing before merge: a docstring said each observer 'receives its own copy' when the code passes the same frozen CostEvent — you fixed it fast (cf6fe3a). Solid; hope to see more.
Originally posted by: fu351
The
CostObserverplugin seam is clean work — the protocol, the structural validation, discovery, and the isolation guarantee (a raising observer can't touch the ledger write or the verdict) are exactly the shape the other extension seams use. You've now built one seam end-to-end, so [#200] (plugins+docs: complete thedoberman.detectorsextension seam, level-9) is the same shape applied to a different seam if you want to do it again. Issues are labelled level-1 through level-10 — a comment on one claims it.Related
Tickets: #200