Accept the checkpoint channel membership from the gateway payload

Problem

Ai::DuoWorkflows::CreateCheckpointService#channel_keys derives the header's channel_keys column from channel_values.keys in the checkpoint payload.

The AI gateway drops channel_values from the payload when the instance advertises the incremental_checkpoints_only server capability. See gitlab-org/modelops/applied-ml/code-suggestions/ai-assist!6519 (merged).

In that mode, the backend has no data to derive channel_keys from. It writes NULL to p_duo_workflows_checkpoint_headers.channel_keys.

Channel membership matters. Blobs are an append-only log and cannot express a channel deletion. #613975 (closed) will filter the fold to the header's membership. !250826 (merged) makes a workflow with a NULL-membership header fall back to legacy rows. A workflow running in incremental-only mode has no legacy rows to fall back to.

The gateway will start sending channel_keys explicitly. See gitlab-org/modelops/applied-ml/code-suggestions/ai-assist#2745 (closed). The backend must accept and use this param before that change ships.

Proposal

Add an optional channel_keys param to POST /api/v4/ai/duo_workflows/workflows/:id/checkpoints in ee/lib/api/ai/duo_workflows/workflows_internal.rb. Make it an array of strings.

Limit the array to 100 items, matching the check_duo_wf_checkpoint_headers_channel_keys_cardinality constraint on the column. Limit each item to 255 characters, matching the channel limit on channel_blobs.

Reject an oversized array with a 400 response, not a database error.

In CreateCheckpointService, use the channel_keys param when it is present. Fall back to deriving from channel_values when the param is absent. Keep NULL only when neither source is present, since the read path in !250826 (merged) treats NULL as a signal to read legacy rows.

Acceptance criteria

  • The create checkpoint endpoint accepts an optional channel_keys array param, capped at 100 items and 255 characters per item.
  • The endpoint returns 400, not 500, when channel_keys exceeds the limit.
  • CreateCheckpointService uses the channel_keys param when present, even if channel_values is also present.
  • CreateCheckpointService still derives channel_keys from channel_values when the param is absent.
  • The header column stays NULL only when both the param and channel_values are absent.
  • A non-array channel_keys value does not raise a 500 error.
  • Specs cover all cases above.

Sequencing

The backend must accept channel_keys before the gateway starts sending it. Both changes must deploy before duo_workflow_write_incremental_only is enabled anywhere. The flag is off today.

References