"""AI steps (the M0 AI-step contract): an AI step is a PROVIDER of a capability, with its fallback as the
manual path. Every step here is suggest-only — it never changes an item, never writes to a client.
erp-platform's capabilities.yaml names no AI capability yet, so no provider can be wired: each step answers
with its fallback, saying so. Proposed capability names, for when it does: `ai.suggest_plan`,
`ai.draft_message`, `ai.flag_risk`. Wiring one turns the same use case into a suggestion, with no new step.
"""
AI_STEPS = {
"ai_draft_plan": {
"capability": None,
"safe_for_auto": False, # a date promised to a client is a commitment
"fallback": "items are dated at the project's end and owned by the lead",
"benefit": "a plan of twenty items gets dates and owners in seconds, from how similar work actually went",
"eval": "finished projects replayed: suggested durations against actual, owner match rate",
},
"ai_chase_brief": {
"capability": None,
"safe_for_auto": False, # it speaks to the client
"fallback": "the board shows the item as blocked past the chase days",
"benefit": "a brief that is late gets a polite, specific reminder drafted the day it is due",
"eval": "drafts rated by leads for accuracy (names the right asset and date) and tone",
},
"ai_flag_shortfall": {
"capability": None,
"safe_for_auto": True, # it only warns
"fallback": "reconciliation reports the shortfall when the window closes",
"benefit": "a shortfall is seen while the window is open and the work can still be done",
"eval": "closed projects replayed two weeks before their end: precision and recall against the actual shortfall",
},
}
def suggest(step: str, mode: str, context: dict) -> dict:
"""The step's answer. Off: it does not run. Suggest with no provider: the fallback, in words."""
spec = AI_STEPS[step]
if mode == "off":
return {"step": step, "mode": "off", "suggestion": None, "applied": False,
"fallback": spec["fallback"], "reason": "this step is switched off in the settings"}
return {"step": step, "mode": mode, "suggestion": None, "applied": False, "fallback": spec["fallback"],
"reason": "no AI provider is connected for this step yet", "context": context}