Part of the "Typed payloads: parse, don't validate" milestone. Follows #416 / PR #417.
Problem
eventParams is walked with dual-alias branches in several places instead of a typed model:
ai_pipe_validation.py: ep.get("to_phase_id") or ep.get("toPhaseId")
ai_phase_transition_validation.py: src = ep.get("to_phase_id") or ep.get("toPhaseId")
automation_preflight.py: extra.get("event_params") or extra.get("eventParams"), then ev.get("to_phase_id") or ev.get("toPhaseId")
AutomationEventParamsInput already exists as an empty-shell pydantic model (models/ai_automation.py, extra="allow", no fields), so event_params on behaviors/automations is effectively untyped.
Wire shape (introspected, dev)
AutomationEventParamsInput inputFields: kindOfSla, fromPhaseId, inPhaseId, to_phase_id (snake!), triggerAutomationId, triggerFieldIds.
Note the mixed casing in one type: to_phase_id is snake while its siblings are camel — per-declared-field aliases only, never a blanket converter.
Scope
- Fill
AutomationEventParamsInput with the six declared fields (populate_by_name=True, per-field aliases, keep extra="allow").
- Type
event_params on the behavior read/normalize path (BehaviorPayload, BehaviorInput) and the automation input, and read typed attributes at the consumers above.
- Delete the
to_phase_id/toPhaseId and event_params/eventParams dual-alias branches.
Sequencing
Touches models/ai_agent.py and models/ai_automation.py. Sequence after #410 (capability/provider hardening) to avoid conflicts on models/ai_agent.py.
Acceptance
- No
to_phase_id/toPhaseId or event_params/eventParams dual-alias branches remain in the migrated paths.
uv run pytest -m "not integration" + ruff + import-linter green.
Part of the "Typed payloads: parse, don't validate" milestone. Follows #416 / PR #417.
Problem
eventParamsis walked with dual-alias branches in several places instead of a typed model:ai_pipe_validation.py:ep.get("to_phase_id") or ep.get("toPhaseId")ai_phase_transition_validation.py:src = ep.get("to_phase_id") or ep.get("toPhaseId")automation_preflight.py:extra.get("event_params") or extra.get("eventParams"), thenev.get("to_phase_id") or ev.get("toPhaseId")AutomationEventParamsInputalready exists as an empty-shell pydantic model (models/ai_automation.py,extra="allow", no fields), soevent_paramson behaviors/automations is effectively untyped.Wire shape (introspected, dev)
AutomationEventParamsInputinputFields:kindOfSla,fromPhaseId,inPhaseId,to_phase_id(snake!),triggerAutomationId,triggerFieldIds.Note the mixed casing in one type:
to_phase_idis snake while its siblings are camel — per-declared-field aliases only, never a blanket converter.Scope
AutomationEventParamsInputwith the six declared fields (populate_by_name=True, per-field aliases, keepextra="allow").event_paramson the behavior read/normalize path (BehaviorPayload,BehaviorInput) and the automation input, and read typed attributes at the consumers above.to_phase_id/toPhaseIdandevent_params/eventParamsdual-alias branches.Sequencing
Touches
models/ai_agent.pyandmodels/ai_automation.py. Sequence after #410 (capability/provider hardening) to avoid conflicts onmodels/ai_agent.py.Acceptance
to_phase_id/toPhaseIdorevent_params/eventParamsdual-alias branches remain in the migrated paths.uv run pytest -m "not integration"+ ruff + import-linter green.