Skip to content

Type behavior action metadata + fieldsAttributes (shared FieldMapInput) #419

Description

@mocha06

Part of the "Typed payloads: parse, don't validate" milestone. Follows #416 / PR #417.

Problem

Behavior action metadata is a pass-through dict and the per-actionType validators in models/ai_agent.py (_validate_card_field_metadata, _validate_fields_attributes_entries, _validate_move_card_metadata, _validate_create_table_record_metadata, _validate_send_email_template_metadata) walk it with raw .get() string keys. Its fieldsAttributes entries are untyped dicts.

Wire shape (introspected, dev)

AiBehaviorMetadataInput inputFields: allowTemplateModifications, destinationPhaseId, emailTemplateId, fieldsAttributes (→ FieldMapInput), mcpServerId, pipeId, tableId, toolInputs (→ AiBehaviorMcpToolInput), toolName. All camelCase.

FieldMapInput inputFields: fieldId, inputMode, valuefieldId and inputMode are NON_NULL. This is the same type used by classic-automation field_map (see the sibling issue), so extract a shared model.

Note: mcpServerId / toolName / toolInputs did not exist when the current models were written — metadata grows, so keep extra="allow".

Live proof the wire name is exact (dev, createAutomation)

FieldMapInput rejects the undeclared spelling at GraphQL coercion, before execution:

  • field_map[0].field_id (snake) → Field is not defined on FieldMapInput
  • field_map[0].fieldId (camel) → passes coercion (fails later only on business validation)

Scope

  • Model AiBehaviorMetadataInput (typed known fields, extra="allow" for the growing tail) and a shared FieldMapInput-shaped model (fieldId/inputMode/value) reused by behaviors and classic automations.
  • Move the per-actionType metadata checks onto the typed fields, preserving the pinned error-message substrings (pipeId, fieldsAttributes, fieldId, inputMode, tableId, emailTemplateId, allowTemplateModifications, destinationPhaseId) that tests match= on.
  • Keep metadata accepting unknown keys so GET→update round-trips stay byte-identical.

Sequencing

Touches models/ai_agent.py — sequence after #410.

Acceptance

  • Metadata checks read typed fields; pinned error messages unchanged.
  • GET→update clone round-trip verified on a lab pipe.
  • uv run pytest -m "not integration" + ruff + import-linter green.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions