Part of the "Typed payloads: parse, don't validate" milestone. Follows #416 / PR #417.
Problem
Classic (non-AI) automation preflight walks CreateAutomationInput-style dicts with dual-alias branches in automation_preflight.py:
extra.get("event_params") or extra.get("eventParams"), ev.get("to_phase_id") or ev.get("toPhaseId")
extra.get("action_params") or extra.get("actionParams"), act.get("to_phase_id") or act.get("toPhaseId")
act.get("field_map") or act.get("fieldMap"), entry.get("fieldId") or entry.get("field_id")
Wire shape (introspected, dev)
CreateAutomationInput: action_id, event_id, action_params (→ AutomationActionParamsInput), event_params (→ AutomationEventParamsInput), condition, name, ... — snake_case input field names.
AutomationActionParamsInput: field_map (snake) alongside aiBehaviorParams/httpMethod (camel), plus card_id, email_template_id, to_phase_id, fields_map_order (snake).
FieldMapInput: fieldId, inputMode, value (camel; fieldId/inputMode NON_NULL).
Live proof (dev, createAutomation) — the second spelling never worked
action_params.field_map[0].field_id (snake) → Field is not defined on FieldMapInput.
action_params.field_map[0].fieldId (camel) → passes coercion.
action_params.fieldMap (camel wrapper) → Field is not defined on AutomationActionParamsInput.
So fieldMap and field_id in the dual-alias branches accept inputs the API always rejected.
Scope
- Model the classic-automation params subtree (
AutomationActionParamsInput fields the preflight touches: field_map, to_phase_id, phase) with per-declared-field aliases, extra="allow", reusing the shared FieldMapInput model from the behavior-metadata issue.
- Read typed attributes in
automation_preflight.py; delete the dual-alias branches. Update any tests pinning the (non-functional) camel/snake passthrough to the declared names.
Acceptance
- No dual-alias branches remain in
automation_preflight.py.
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
Classic (non-AI) automation preflight walks
CreateAutomationInput-style dicts with dual-alias branches inautomation_preflight.py:extra.get("event_params") or extra.get("eventParams"),ev.get("to_phase_id") or ev.get("toPhaseId")extra.get("action_params") or extra.get("actionParams"),act.get("to_phase_id") or act.get("toPhaseId")act.get("field_map") or act.get("fieldMap"),entry.get("fieldId") or entry.get("field_id")Wire shape (introspected, dev)
CreateAutomationInput:action_id,event_id,action_params(→AutomationActionParamsInput),event_params(→AutomationEventParamsInput),condition,name, ... — snake_case input field names.AutomationActionParamsInput:field_map(snake) alongsideaiBehaviorParams/httpMethod(camel), pluscard_id,email_template_id,to_phase_id,fields_map_order(snake).FieldMapInput:fieldId,inputMode,value(camel;fieldId/inputModeNON_NULL).Live proof (dev, createAutomation) — the second spelling never worked
action_params.field_map[0].field_id(snake) →Field is not defined on FieldMapInput.action_params.field_map[0].fieldId(camel) → passes coercion.action_params.fieldMap(camel wrapper) →Field is not defined on AutomationActionParamsInput.So
fieldMapandfield_idin the dual-alias branches accept inputs the API always rejected.Scope
AutomationActionParamsInputfields the preflight touches:field_map,to_phase_id,phase) with per-declared-field aliases,extra="allow", reusing the sharedFieldMapInputmodel from the behavior-metadata issue.automation_preflight.py; delete the dual-alias branches. Update any tests pinning the (non-functional) camel/snake passthrough to the declared names.Acceptance
automation_preflight.py.uv run pytest -m "not integration"+ ruff + import-linter green.