Add checkpoint_ns support to scope Duo Workflow checkpoints

What does this MR do and why?

Duo Workflow's DelegationNode dispatches nested subgraph invocations (e.g. delegated subagents) that need their own checkpoint lineage, separate from the flow's top-level checkpoints. Without a way to distinguish them, callers could not fetch or scope checkpoints to one specific nested invocation.

This adds checkpoint_ns to Checkpoint.earliest/.latest, the presenter, the internal API (create and list endpoints), the checkpoint entity, and the GraphQL first_checkpoint/latest_checkpoint fields and DuoWorkflowEvent type. Blank/omitted checkpoint_ns continues to refer to the session's own top-level lineage, preserving prior behavior.

It also adds the migration for the checkpoint_ns column on p_duo_workflows_checkpoints (the column/constraint were already present in db/structure.sql, but the migration and its db/schema_migrations marker were missing), and regenerates the GraphQL introspection schemas and OpenAPI v3 docs that drifted out of date from the new fields/params.

No new index was added for checkpoint_ns: every query filtering on it (Checkpoint.earliest/.latest, the checkpoints list endpoint) always scopes to a single workflow first via the workflow.checkpoints association, which already uses the existing index_p_duo_workflows_checkpoints_thread index on (workflow_id, thread_ts). checkpoint_ns is then applied as a cheap filter on that already-narrow, per-workflow row set.

Also batches the new checkpointWrites GraphQL field via BatchLoader::GraphQL so resolving it across multiple workflows in one request issues a single query instead of one per workflow.

Query and query plan for Checkpoint.earliest/.latest

Raw SQL for the common (for_checkpoint_ns(nil)) path:

SELECT "p_duo_workflows_checkpoints".* FROM "p_duo_workflows_checkpoints"
WHERE "p_duo_workflows_checkpoints"."workflow_id" = 2010
  AND "p_duo_workflows_checkpoints"."created_at" <= '2026-07-14 18:35:36.846298'
  AND "p_duo_workflows_checkpoints"."checkpoint_ns" IS NULL
ORDER BY "p_duo_workflows_checkpoints"."thread_ts" ASC LIMIT 1

EXPLAIN (ANALYZE, BUFFERS) (top of plan; ran against my local dev database — see caveat below):

Limit  (cost=6.69..9.07 rows=1 width=398) (actual time=0.145..0.146 rows=0 loops=1)
  Buffers: shared hit=56
  ->  Merge Append  (cost=6.69..80.52 rows=31 width=398) (actual time=0.144..0.145 rows=0 loops=1)
        Sort Key: p_duo_workflows_checkpoints.thread_ts
        Buffers: shared hit=56
        ->  Index Scan using p_duo_workflows_checkpoints_20260614_workflow_id_thread_ts_idx on p_duo_workflows_checkpoints_20260614 ...
              Index Cond: (workflow_id = 2010)
              Filter: ((checkpoint_ns IS NULL) AND (created_at <= ...))
              Buffers: shared hit=5
        -> (repeats per daily partition within the retention window)
Planning Time: 7.920 ms
Execution Time: 2.259 ms

Every partition uses the existing (workflow_id, thread_ts) index (Index Cond: workflow_id = ?) and applies checkpoint_ns as a post-index Filter, not a sequential scan.

Caveat: the plan above is from local dev data (~5.3k rows / 56 MB across all partitions of p_duo_workflows_checkpoints), not GitLab.com production data, so costs/row estimates aren't representative at scale. The db:gitlabcom-database-testing pipeline still needs to be triggered against production-like data before requesting database review — I don't have access to trigger that job.

References

Screenshots or screen recordings

N/A — backend/API only change.

How to set up and validate locally

  1. Run bundle exec rails db:migrate to apply the checkpoint_ns migration.
  2. bundle exec rspec ee/spec/models/ai/duo_workflows/checkpoint_spec.rb ee/spec/presenters/ai/duo_workflows/workflow_presenter_spec.rb ee/spec/requests/api/ai/duo_workflows/workflows_internal_spec.rb ee/spec/requests/api/graphql/ai/duo_workflows/workflows_spec.rb

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Igor Drozdov

Merge request reports

Loading
Loading