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_keysarray param, capped at 100 items and 255 characters per item. - The endpoint returns 400, not 500, when
channel_keysexceeds the limit. -
CreateCheckpointServiceuses thechannel_keysparam when present, even ifchannel_valuesis also present. -
CreateCheckpointServicestill deriveschannel_keysfromchannel_valueswhen the param is absent. - The header column stays NULL only when both the param and
channel_valuesare absent. - A non-array
channel_keysvalue 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.