Incremental checkpoint read engine + FF (phase 2, 1/3)
What does this MR do?
Part 1/3 of the phase-2 incremental checkpoint read path (split out of !242398 (closed)). Adds the read engine with no consumer yet:
Gitlab::DuoWorkflow::ChannelValuesReconstructor— folds incremental checkpoint blobs intochannel_values(compaction ⇒ replace, conversation ⇒ append; dict-of-list channels supported). Guards corrupt/undecodable blob data.Ai::DuoWorkflows::Workflowread methods —checkpoint_header_for,latest_checkpoint_header,accumulated_blobs_for,ancestor_thread_ts,reconstructed_channel_values, and thereconstruct_from_blobs?/read_incremental_checkpoints_enabled?gates.accumulated_blobs_forselects the blobs on a checkpoint's ancestor chain within onecurrent_threadgroup, bounded to a single daily partition.duo_workflow_read_incremental_checkpointsfeature flag (wip, default off).
No API surface here — the internal by_thread_ts endpoint and the trace read
path land as separate MRs stacked on this branch.
Database
All read queries are keyed by workflow_id + workflow_created_at, so each
prunes to a single daily partition via index scan (no seq scans, no
cross-partition scans). Plans on the GDK DB:
checkpoint_header_for / latest_checkpoint_header / ancestor_thread_ts
(p_duo_workflows_checkpoint_headers):
Sort (cost=3.20..3.21 rows=1)
-> Index Scan using p_duo_workflows_checkpoint_headers_20260720_workflow_id_idx
on p_duo_workflows_checkpoint_headers_20260720
Index Cond: (workflow_id = 123)
Filter: (workflow_created_at = '2026-07-20 12:00:00+00' AND thread_ts = 'ts-1')accumulated_blobs_for (p_duo_workflows_checkpoint_blobs). Filtering
project_id — the leading column of idx_duo_wf_checkpoint_blobs_dedup
(project_id, workflow_id, thread_ts, channel, version, step_action, workflow_created_at) — lets that existing unique index serve the lookup, so no
new index is needed:
Index Scan using idx_duo_wf_checkpoint_blobs_dedup (partition local copy)
on p_duo_workflows_checkpoint_blobs_20260720
Index Cond: (project_id = 55 AND workflow_id = 123
AND thread_ts = ANY('{ts-1,ts-2}')
AND workflow_created_at = '2026-07-20 12:00:00+00')
Filter: (current_thread = 0)workflow_created_at is applied by the checkpoint_blobs / checkpoint_headers
association scope (partition pruning), so each read stays on one daily partition.
No new migrations — the tables, indexes, and partition scheme all come from the base branch (!244403 (merged), !244914 (merged)).
Feature flag
duo_workflow_read_incremental_checkpoints (wip, default off). Read path is a
no-op until enabled and until a consumer MR merges.
Testing
Reconstructor and model specs updated and green.
Related: #604677 (closed)
Restructure — epic &22939
Part of the phase-2 read-path epic. Each consumer is gated by its own flag in addition to the master kill switch.
- Delivery issue: #604677 (closed)
- Feature flag:
duo_workflow_read_incremental_checkpoints (master kill switch) - FF rollout issue: #604687
Code still gates on the shared
duo_workflow_read_incremental_checkpointsflag today; repointing to the per-consumer flag is deferred until the restructure lands.