chore(core): stage 1 contracts — dialog, API schemas + fixtures, integrations, monitoring, modules
CI / ruff · pytest (pull_request) Successful in 22s
CI / ruff · pytest (pull_request) Successful in 22s
Freeze interfaces between lanes A (bot), B (backend), C (dashboard) so three developers work in parallel without touching each other's files: dialog Event/Reply + engine registry with echo stub, integration protocols with in-memory defaults, MonitoringService with noop stub, /api/v1 schemas with fixture-backed routes, frozen main.py wiring, config/modules.yaml + due_scales(), DAR-5, tzdata, Andrey trajectory, lanes in WORKFLOW, ADR-010. Refs #34 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
"""Monitoring service contract (frozen, #34).
|
||||
|
||||
Split of responsibilities (ADR-007):
|
||||
* Lane B (#38) implements `MonitoringService`: builds a patient snapshot from the DB, runs the deterministic
|
||||
rules (`app.core.rules.evaluate`), persists `alert`/`crisis` actions and notifies the doctor itself.
|
||||
* It returns only the *dialog* actions (schedule_scale, med_info, reach_out, show_safety_plan, suggest_visit,
|
||||
crisis) so the engine (lane A, #10) can react in the conversation. The engine never creates alerts.
|
||||
"""
|
||||
|
||||
from collections.abc import Callable
|
||||
from importlib import import_module, util
|
||||
from typing import Protocol
|
||||
|
||||
from app.core.rules import Action
|
||||
|
||||
|
||||
class MonitoringService(Protocol):
|
||||
async def after_data_change(self, token: str) -> list[Action]: ...
|
||||
|
||||
async def is_in_crisis(self, token: str) -> bool:
|
||||
"""True while an `acute` alert is open (not acked by the doctor) — FSM stays in `crisis`."""
|
||||
...
|
||||
|
||||
|
||||
class NoopMonitoring:
|
||||
"""Placeholder until #38 lands."""
|
||||
|
||||
async def after_data_change(self, token: str) -> list[Action]:
|
||||
return []
|
||||
|
||||
async def is_in_crisis(self, token: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
_factory: Callable[[], MonitoringService] = NoopMonitoring
|
||||
|
||||
|
||||
def register_monitoring(factory: Callable[[], MonitoringService]) -> None:
|
||||
global _factory
|
||||
_factory = factory
|
||||
|
||||
|
||||
def get_monitoring() -> MonitoringService:
|
||||
# find_spec instead of try/except ImportError: real import errors inside the module must surface.
|
||||
if util.find_spec("app.services.monitoring") is not None:
|
||||
import_module("app.services.monitoring")
|
||||
return _factory()
|
||||
Reference in New Issue
Block a user