Skip to content

docs(merge-queue): document single-to-parallel cutover and Queued-stuck causes#259

Draft
samgutentag wants to merge 1 commit into
mainfrom
sam-gutentag/parallel-cutover-docs
Draft

docs(merge-queue): document single-to-parallel cutover and Queued-stuck causes#259
samgutentag wants to merge 1 commit into
mainfrom
sam-gutentag/parallel-cutover-docs

Conversation

@samgutentag

@samgutentag samgutentag commented Jun 17, 2026

Copy link
Copy Markdown
Member

What

Documents the single → parallel mode cutover for Merge Queue, plus the most common reason a PR gets stuck in Queued after switching. Surfaced by a customer planning this exact migration.

In parallel mode a PR doesn't leave Queued until both branch protection passes and impacted targets are uploaded for its head SHA. There's no timeout fallback, so a stale PR whose impacted-targets job never ran hangs in Queued indefinitely — with no signal to the author that they need to rebase. None of the migration story was documented.

Changes

  • parallel-queues/index.mdx — new "Cutting over from single to parallel mode" section: land the impacted-targets job early (uploads are stored but unused in single mode, so it's safe), backfill open PRs so they don't hang, and raise the merge-base staleness requirement so new PRs always run the job. Plus a warning that a target-less PR stays Queued forever.
  • reference/troubleshooting.mdx — split the "stuck as Queued" accordion into Cause 1 (status checks) and Cause 2 (missing impacted targets), each with unstick steps.
  • using-the-queue/reference.mdx — lifecycle prose and the Queued table row now spell out the two parallel-mode gating conditions.

Source

Originated from a customer Slack thread on planning a single → parallel cutover: original conversation.

Behavior confirmed by Robert in that thread: no Queued timeout, posting targets before cutover is safe, re-uploading overrides (last-upload-wins), and auto-ALL is intentionally not done because it would collapse every lane.

Needs a check before merge

The claim that the PR detail page "shows which condition it's waiting on" rests on the timeline screenshot from the thread, not verified against the live app. Worth a glance before this ships.

🤖 Generated with Claude Code

…ck causes

Adds guidance for the single -> parallel mode cutover, surfaced by a
customer planning the switch. In parallel mode a PR stays Queued until
impacted targets are uploaded for its head SHA, with no timeout fallback,
so stale PRs that predate the impacted-targets job hang indefinitely.

- parallel-queues: new "Cutting over from single to parallel mode" section
  (land the job early, backfill open PRs, require a recent merge base)
- troubleshooting: split the stuck-Queued accordion into status-check vs
  missing-impacted-targets causes with unstick steps
- pr-states reference: spell out the two parallel-mode gating conditions

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mintlify

mintlify Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
trunk 🟢 Ready View Preview Jun 17, 2026, 7:36 AM

@samgutentag

Copy link
Copy Markdown
Member Author

Verification status (2026-06-17): unknown

Could not determine rollout state from available signals. Chaining to verify-docs-against-code for content-accuracy check.

  • Flag state: not applicable (no feature flag; documents long-existing parallel-queue / impacted-targets behavior)
  • Eng PR: none referenced in the PR body
  • Flag: none
  • Signals: no engineering PR refs and no Linear ticket in the body, so rollout state is not the relevant question here. This is a pure-prose docs PR about behavior that has shipped for years, not a flag-gated feature.

Next: see the verify-docs-against-code comment for the factual-accuracy check of the documented claims.

@samgutentag

Copy link
Copy Markdown
Member Author

Code verification (2026-06-17): 5 confirmed / 0 contradicted / 0 ambiguous / 2 unverifiable

Claim Verdict Source
Parallel (GRAPH) mode requires impacted targets; single mode does not confirmed readiness_checker.ts:145
A PR stays Queued until impacted targets are uploaded for the head SHA confirmed readiness_checker.ts:265-272
No timeout / auto-ALL fallback; the gate is presence-only confirmed readiness_checker.ts:84-86
Uploading targets in single mode is stored but not used for scheduling (safe pre-cutover) confirmed service.ts:80-119
/setImpactedTargets endpoint exists confirmed service.ts:80
Sending ALL as the impacted-targets value unverifiable (documented in parallel-queues/api.mdx, eng-confirmed in thread; sentinel not re-traced in code this pass)
PR detail page "shows which condition it's waiting on" unverifiable (frontend/UI claim, not backend code; already flagged in the PR body)

All five code-verifiable backend claims confirm cleanly against trunk-io/trunk. The two unverifiable items are not contradictions: ALL is pre-existing documented behavior on api.mdx, and the detail-page claim is a UI assertion this skill does not cover (it was already called out in the PR body as needing a live-app glance). Nothing to correct before publishing on the backend behavior.


Source #1 — Parallel (GRAPH) mode requires impacted targets; single mode does not (confirmed)

File: trunk-io/trunk/trunk/services/merge/src/controller/readiness_checker.ts#L145

return {
  requiresImpactedTargets: mode === "GRAPH",
  doesBaseBranchMatch: prBaseBranch === mergeBranch,

Reasoning: Impacted targets are required only when the merge instance mode is GRAPH (the internal name for parallel mode). In single mode requiresImpactedTargets is false, so the gate below is a no-op. This is exactly the doc's "single mode only needs branch protection; parallel mode needs one more thing."

Source #2 — A PR stays Queued until impacted targets are uploaded for the head SHA (confirmed)

File: trunk-io/trunk/trunk/services/merge/src/controller/readiness_checker.ts#L265-L272

async hasImpactedTargetsIfRequired(
  mergeInstance: prismaTypes.MergeInstance,
  sha: string,
): Promise<boolean> {
  if (mergeInstance.mode !== prismaTypes.MergeInstanceMode.GRAPH) {
    return true;
  }
  return this.impactedTargetsClient.has(mergeInstance.repoId, sha);
}

Reasoning: In GRAPH mode the gate resolves to impactedTargetsClient.has(repoId, sha), keyed on the specific head SHA. Until targets exist for that SHA the PR fails readiness and is not promoted out of Queued. The per-SHA keying is why a force-push or amend needs a fresh upload (matches the doc's "uploaded for the head SHA" wording).

Source #3 — No timeout / auto-ALL fallback; the gate is presence-only (confirmed)

File: trunk-io/trunk/trunk/services/merge/src/controller/readiness_checker.ts#L84-L86

const basePasses =
  doesBaseBranchMatch === true &&
  mergeItemInNotReady === true &&
  (requiresImpactedTargets === false || hasImpactedTargets === true);

Reasoning: Readiness is a pure boolean conjunction. The impacted-targets term is satisfied only by requiresImpactedTargets === false (single mode) or hasImpactedTargets === true (targets present). There is no time-based branch and no automatic ALL substitution anywhere in this path, so a GRAPH-mode PR with no targets stays Queued indefinitely. This is an absence-of-code argument, but the gate is self-contained, so the "no timeout, no auto-ALL" claim holds.

Source #4 — Uploading targets in single mode is stored but not used for scheduling (confirmed)

File: trunk-io/trunk/trunk/services/merge/src/service.ts#L80-L119

async setImpactedTargets(...) {
  const { repo, prSha, prNumber, targetBranch, impactedTargets } =
    mapper.fromSetImpactedTargetsRequest(call.request);
  ...
  await controller.putImpactedTargets(..., impactedTargets, ...);
  ...
  // Caught readiness check error when putting impacted targets; ignoring
}

Reasoning: setImpactedTargets persists targets via putImpactedTargets regardless of mode. Because the readiness gate (Source #1) only consults them in GRAPH mode, an upload while the queue is in single mode is stored but never affects scheduling. The handler even swallows readiness-check errors on put, so landing the upload job before cutover cannot disrupt the running single-mode queue. Confirms the doc's "stored but unused in single mode, so shipping the job early is safe."

Source #5 — /setImpactedTargets endpoint exists (confirmed)

File: trunk-io/trunk/trunk/services/merge/src/service.ts#L80

async setImpactedTargets(
  call: grpc.ServerUnaryCall<
    servicePb.SetImpactedTargetsRequest,
    servicePb.SetImpactedTargetsResponse
  >,

Reasoning: The SetImpactedTargets gRPC method backs the public POST /v1/setImpactedTargets REST path documented on api.mdx. The endpoint the cutover section links to is real and current.

@samgutentag samgutentag added the code-verified-partial verify-docs-against-code: confirmed claims, some unverifiable. label Jun 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

code-verified-partial verify-docs-against-code: confirmed claims, some unverifiable.

Development

Successfully merging this pull request may close these issues.

1 participant