Skip to content

fix(cursor): persist and enforce tool-suspended checkpoint snapshots - #5133

Merged
lidge-jun merged 2 commits into
lidge-jun:devfrom
luvs01:fix/cursor-tool-suspended-snapshot
Sep 19, 2026
Merged

lidge-jun merged 2 commits into
lidge-jun:devfrom
luvs01:fix/cursor-tool-suspended-snapshot

Conversation

@luvs01

@luvs01 luvs01 commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

Motivation

A checkpoint committed after a client tool call is only safe to resume from a trailing toolResult request. The existing guard enforces that through checkpointUsable on the provider continuation, but the flag never reaches the stored snapshot. A ref-less prefix lookup has no continuation state, so it could hand a suspended checkpoint to a request whose last message is not a toolResult — resuming bytes that upstream serialized mid-tool-call.

Description

  • Persist toolSuspended on CursorCheckpointSnapshot at commit time (commitCursorCheckpoint input, stored field, and the toolSuspendedCommit call site).
  • The trailing_tool_result guard now fires when either the continuation state or the snapshot itself marks the checkpoint suspended, covering the ref-less path.

Tests

  • bun test tests/providers/cursor/cursor-tool-suspended-checkpoint.test.ts tests/providers/cursor/cursor-request-builder.test.ts tests/providers/cursor/cursor-adapter.test.ts — 99 pass.
  • The existing commit test now asserts the snapshot carries toolSuspended, that a non-toolResult ref-less request falls back to full-replay with trailing_tool_result, and that a trailing toolResult continuation still resumes the checkpoint.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Summary by CodeRabbit

  • Bug Fixes

    • Improved conversation continuation when a checkpoint is captured while waiting for a tool response.
    • Checkpoints are now invalidated when the conversation state could make them unsafe to resume, triggering a full replay instead.
    • Resumed conversations can correctly reuse the checkpoint after the expected tool result is received.
  • Tests

    • Added coverage for suspended tool-call checkpoints, safe invalidation, full replay, and successful continuation.

@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: c29f3586-dd3d-48d6-9081-4e0883e4f3fa

📥 Commits

Reviewing files that changed from the base of the PR and between af24f68 and d2a6ded.

📒 Files selected for processing (1)
  • src/adapters/cursor.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The cursor checkpoint flow now records whether a checkpoint was captured during tool suspension. Checkpoint resolution invalidates such checkpoints until the trailing tool result exists. Tests cover invalidation and resumed checkpoint use.

Changes

Cursor checkpoint suspension

Layer / File(s) Summary
Checkpoint state propagation
src/adapters/cursor.ts, src/adapters/cursor/checkpoint-store.ts
toolSuspended is accepted on checkpoint commits, stored as optional snapshot metadata, and populated by cursor checkpoint commits.
Checkpoint resolution and regression coverage
src/adapters/cursor/request-builder.ts, tests/providers/cursor/cursor-tool-suspended-checkpoint.test.ts
resolveCursorCheckpoint returns trailing_tool_result for suspended snapshots without a trailing tool result. Tests verify full replay before the result and checkpoint continuation after it is appended.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Bug fix

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: persisting and enforcing tool-suspended Cursor checkpoint snapshots.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Sep 19, 2026
@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 76 / 80

모델이 도구를 부르면, 그때 저장한 체크포인트는 도구 답이 오기 전의 상태입니다. 그래서 다음 요청의 맨 끝 메시지가 toolResult일 때만 그 저장본을 이어 써야 합니다. 이 규칙은 이미 checkpointUsable로 있었습니다. 다만 그 값은 요청이 들고 다니는 continuation(ref 경로)에만 있고, 저장된 스냅샷에는 없었습니다. ref가 빠진 채 메시지 prefix로 스냅샷을 찾는 길은 continuation이 없습니다. 그래서 도구 호출 중간에 직렬화된 바이트를, 끝이 toolResult가 아닌 요청에도 붙여 줄 수 있었습니다.

이 PR은 커밋 순간에 toolSuspended를 스냅샷에 같이 남기고, trailing_tool_result 가드가 continuation 또는 스냅샷 둘 중 하나라도 서스펜드면 막게 합니다. 손보는 곳은 commitCursorCheckpoint 입력·저장, cursor.ts 커밋 호출 한 줄, request-builder.ts 가드입니다. 기존 툴-서스펜드 테스트가 ref 없는 재시도는 full-replay/trailing_tool_result, 끝에 toolResult를 붙이면 같은 바이트로 checkpoint 이어감을 같이 고정합니다. 베이스는 dev이고 types/config 분리와는 무관합니다. dev tip보다 커밋 3개 뒤지만(#5125, #5103, #5067) 커서 체크포인트 파일과 겹치지 않습니다.

경로 src/adapters/cursor/checkpoint-store.ts toolSuspended - 커밋 시에만 true를 남기고, 가드는 === true라 필드 없는 예전 스냅샷은 예전처럼 continuation 가드에 맡긴다. 맞다
경로 src/adapters/cursor/request-builder.ts resolveCursorCheckpoint - ref 없는 prefix 히트에 continuation이 없어도 스냅샷만으로 막힌다. 구멍과 고침의 위치가 같다
경로 tests/providers/cursor/cursor-tool-suspended-checkpoint.test.ts - 거절과 재개를 같은 커밋 스냅샷으로 양쪽 다 고정했다
호스티드 CI - 이 글을 쓰는 시점 hygiene/label만 초록, resolve-pr pending, CodeRabbit은 draft라 스킵. 본 테스트 샤드는 아직 안 보인다
로직 - 실질적인 회귀 구멍은 보이지 않는다. 일반 prefix 재개(서스펜드 아닌 스냅샷)는 toolSuspended가 없어서 예전과 같다

메인테이너의 판단이 필요한 지점

  • 체크리스트는 이미 다 켜져 있다. 언드래프트하고 커서 테스트 샤드·CodeRabbit을 본 뒤 넣을지
  • dev tip에 rebase할지. 파일 겹침은 없어서 충돌 이유는 없고, 넣기 직전 fast-forward만 해도 된다

너의 추천
언드래프트한 뒤 커서 관련 테스트 샤드가 초록이면 merge한다. 구멍과 고침이 한 필드·한 가드에 모여 있다. types/config 스플릿으로 닫을 PR이 아니다.

이 댓글은 grok-bot이 작성했습니다

@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 75 / 80

이 PR은 Cursor가 클라이언트 툴을 내보낸 뒤에 저장한 체크포인트를, 다음 요청이 이어서 써도 되는지 스냅샷에 직접 적어 두는 고침입니다. 그 체크포인트는 바로 다음 요청의 끝이 toolResult일 때만 안전합니다. 지금까지는 그 규칙을 continuation 안의 checkpointUsable: false로만 지켰습니다. continuation은 클라이언트가 이전 상태를 다시 들고 올 때만 있습니다. 다음 요청이 ref 없이 같은 대화의 앞부분 해시로 스냅샷을 찾으면 이 값이 없습니다. 그러면 마지막 메시지가 툴 결과가 아닌데도, 툴 호출 도중에 직렬화된 바이트를 업스트림에 다시 넣을 수 있습니다.

고치는 곳은 세 군데입니다. commitCursorCheckpointtoolSuspended를 받고, 참일 때만 스냅샷에 true를 남깁니다. createCursorAdapter의 커밋 호출이 toolSuspendedCommit을 그대로 넘깁니다. resolveCursorCheckpoint는 마지막 역할이 toolResult가 아니고, 스냅샷이 서스펜드이거나 continuation의 checkpointUsablefalse이면 trailing_tool_result로 떨어집니다. ref 없는 조회도 같은 가드를 탑니다. 끝이 toolResult인 요청은 예전처럼 그 바이트를 재개합니다. 네이티브 모델 커밋 거절은 그대로입니다. types.ts/config.ts 분리와는 무관하고, 같은 주제로 열린 다른 PR은 없습니다. 베이스는 dev입니다. 지금 origin/dev보다 커밋 3개 뒤지만 충돌은 없습니다.

테스트는 기존 외부 모델 커밋 검사에 구멍을 직접 막는 어서션을 보탭니다. 저장된 스냅샷의 toolSuspendedtrue인지, ref 없이 같은 사용자 메시지만 다시 넣으면 full-replaytrailing_tool_result인지, 끝에 toolResult를 붙이면 같은 바이트로 checkpoint를 재개하는지. 저자는 관련 파일 99개가 통과한다고 적었습니다. 이 리뷰 시점에는 PR이 아직 draft이고, 호스티드 테스트 샤드는 여기서 초록을 확인하지 못했습니다.

라인 src/adapters/cursor.ts commitCapturedCheckpoint 주석 - 가드가 이제 스냅샷의 toolSuspended도 보는데, 주석은 여전히 checkpointUsable=false만 본다고 적습니다. 동작은 맞습니다. 주석만 한 박자 늦습니다.

메인테이너의 판단이 필요한 지점

  • draft를 ready로 올린 뒤 호스티드 CI가 초록이면 넣을지
  • 주석 한 줄을 이번 PR에서 고칠지, 후속으로 둘지
  • origin/dev에 커밋 3개가 더 있으니 리베이스할지. 충돌은 없습니다

너의 추천
draft를 풀고 CI가 초록이면 머지하세요. 구멍은 실재하고, 고침은 스냅샷에 깃발을 남기는 쪽이 맞습니다. 주석은 있으면 고치는 게 낫고, 없어도 머지를 막을 이유는 없습니다. 로컬 스위트는 돌리지 마세요.

이 댓글은 grok-bot이 작성했습니다

@github-actions

github-actions Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • review readiness checklist open (0/4 boxes ticked).

What to do

  • Tick all four boxes in the PR description once you're done (currently 0/4).

Review readiness checklist

  • ⬜ All CI tests are green on my local testing.
  • ⬜ I pushed my PR to the latest dev commit.
  • ⬜ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/4 boxes ticked.

Automatic draft conversion failed. Please convert this pull request to a draft manually until every box above is ticked.

@github-actions
github-actions Bot marked this pull request as ready for review September 19, 2026 08:57
@github-actions
github-actions Bot marked this pull request as draft September 19, 2026 11:25
@lidge-jun

Copy link
Copy Markdown
Owner

추가 리뷰 · 우선순위 76 / 80

이전 글 뒤에 커밋이 하나 왔습니다. d2a6dedsrc/adapters/cursor.ts의 체크포인트 커밋 주석만 바꿉니다. 동작은 같습니다.

이 PR이 막는 구멍은 그대로입니다. Cursor가 도구를 부른 직후에 저장한 체크포인트는, 다음 요청의 맨 끝이 도구 결과일 때만 이어 써도 됩니다. 그 표시는 요청이 들고 다니는 continuation에만 있었습니다. 저장본을 메시지 앞부분으로만 찾는 길에는 continuation이 없습니다. 그래서 도구 호출 중간에 직렬화된 바이트를, 끝이 도구 결과가 아닌 요청에도 붙일 수 있었습니다.

고침은 커밋할 때 toolSuspended를 스냅샷에 남기고, 가드가 continuation의 checkpointUsable === false이거나 스냅샷의 toolSuspended === true이면 trailing_tool_result로 떨어지게 한 것입니다. 이번 커밋은 그 주석이 두 입력을 같이 적게 고칩니다. request-builder.ts의 가드와 말이 맞습니다.

베이스는 dev입니다. types/config 분리와는 관계없습니다. 지금 origin/dev보다 15커밋 뒤입니다. 이전 리뷰 때는 3커밋 뒤였습니다. GitHub는 충돌 없이 머지 가능이라고 합니다. PR은 draft이고 준비 체크리스트는 0/4입니다. 초록으로 보이는 검사는 hygiene, label, resolve-pr, enforce-target, CodeRabbit입니다. 커서 테스트 샤드는 이 목록에 없습니다.

경로 src/adapters/cursor.ts 커밋 주석 - 이전 리뷰가 짚은 자리다. 두 입력을 적도록 고쳤고, 가드와 같다. 로직 diff는 없다
호스티드 CI - 테스트 샤드는 이 롤업에 없다
dev와의 거리 - 15커밋 뒤다. 머지 가능 표시는 유지된다

메인테이너의 판단이 필요한 지점

  • 주석 지적은 닫혔다. draft를 풀고 체크리스트를 채운 뒤 넣을지
  • 15커밋 뒤를 받기 전에 리베이스할지. 손댄 파일은 커서 체크포인트 네 개다

너의 추천
코드 때문에 다시 돌릴 이유는 없다. draft를 풀고 커서 테스트가 초록이면 머지한다. 넣기 전에 dev를 한 번 받는 편이 낫다. 거리가 3에서 15로 늘었다. types/config로 닫을 PR이 아니다.

이 댓글은 grok-bot이 작성했습니다

@lidge-jun
lidge-jun marked this pull request as ready for review September 19, 2026 12:32
A checkpoint committed after a client tool call is only safe to resume from a trailing toolResult request. The runtime guard already forced that via checkpointUsable on the provider continuation, but the flag never reached the stored snapshot, so a ref-less prefix lookup — which has no continuation state — could resume bytes upstream serialized mid-tool-call. Persist toolSuspended on the snapshot and let the guard fire on either source.
@github-actions
github-actions Bot marked this pull request as draft September 19, 2026 12:32
@lidge-jun
lidge-jun force-pushed the fix/cursor-tool-suspended-snapshot branch from d2a6ded to 563516c Compare September 19, 2026 12:39
@lidge-jun
lidge-jun marked this pull request as ready for review September 19, 2026 13:08
@lidge-jun
lidge-jun merged commit dfbcf2b into lidge-jun:dev Sep 19, 2026
5 checks passed
@luvs01
luvs01 deleted the fix/cursor-tool-suspended-snapshot branch September 19, 2026 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants