Skip to content

feat(cursor): serve-time blob integrity diagnostic for replay corruption - #2656

Merged
lidge-jun merged 4 commits into
codex/cursor-gap-5from
codex/cursor-gap-6
Aug 26, 2026
Merged

feat(cursor): serve-time blob integrity diagnostic for replay corruption#2656
lidge-jun merged 4 commits into
codex/cursor-gap-5from
codex/cursor-gap-6

Conversation

@lidge-jun

Copy link
Copy Markdown
Owner

Summary

  • Live subagent probes (devlog/_plan/260826_cursor_responses_gap, S2a) caught token-splice corruption inside replayed [tool_result] envelopes (a stray token spliced into structural markers). Cursor root/turn blobs are content-addressed (SHA-256 id == bytes), so corruption is detectable at serve time: getBlobArgs now verifies the digest for raw 32-byte ids and emits a blob-integrity-mismatch provider diagnostic (key prefix + byte length only — no payload, privacy-scan safe). Serving is never blocked; this is instrumentation for the G4 investigation.
  • The G2 turn-stall diagnosis already had its state surfaced (run-request diagnostic logs continuationMode + checkpointInvalidationReason); 080 documents the SSE capture procedure for the next stall occurrence rather than shipping a speculative fix.

Stacked on #2654. Design: 080_stall_corruption_diag.md.

Verification

  • bun test tests/cursor-blob-integrity.test.ts — 3 pass (clean pass, splice fault-injection detection, non-content-addressed pass-through).
  • bun test tests/cursor-native-exec.test.ts tests/cursor-blob.test.ts — 102 pass 0 fail total.
  • bun run privacy:scan — green.
  • bun x tsc --noEmit — clean.

Checklist

  • Focused tests green
  • Typecheck + privacy scan clean
  • Diagnostic-only (no serving behavior change)
  • Devlog updated (080)

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner August 26, 2026 03:44
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • ^dev$
  • ^preview$

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c8fdd6d3-1842-42d9-9dbd-a64debee23a4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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 enhancement New feature or request label Aug 26, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 963fff2f19

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +413 to +416
export function cursorBlobServeIntegrityOk(blobId: Uint8Array, served: Uint8Array): boolean {
if (blobId.byteLength !== 32) return true;
const digest = createHash("sha256").update(served).digest();
return digest.equals(Buffer.from(blobId));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check replay bytes before deriving their content ID

For locally generated replay blobs, storeCursorBlob derives the ID from the same bytes and setBlob stores a clone, so corruption introduced during root assembly or serialization is incorporated into both the payload and its hash and this check returns true. The new test bypasses the store by manually pairing an old digest with mutated bytes, so it does not demonstrate that the actual getBlobArgs path can detect the reported splice. Add the integrity/round-trip check at the assembly boundary, or introduce a real retained-store fault-injection seam and test the emitted diagnostic through handleCursorNativeKv.

AGENTS.md reference: src/AGENTS.md:L24-L26

Useful? React with 👍 / 👎.

const blobKey = key(kvMsg.message.value.blobId);
const blobData = getBlob(blobKey);
// Splice-class corruption guard (devlog 260826 080): diagnostic only, never blocks serving.
if (blobData && !cursorBlobServeIntegrityOk(kvMsg.message.value.blobId, blobData)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid hashing blobs when debug diagnostics are off

On every successful getBlobArgs carrying a 32-byte ID, this condition synchronously hashes the entire served payload before debugProviderDiagnostic gets a chance to discard the event when provider debugging is disabled. With blobs permitted up to 16 MiB, ordinary non-debug requests now incur substantial event-loop work per hydration, and repeated gets can delay stream processing and heartbeats. Gate the integrity calculation on the debug setting, or cache validation performed at admission, so opt-in instrumentation does not add this cost to every Cursor request.

Useful? React with 👍 / 👎.

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 54 / 80

설명

이 풀 리퀘스트는 Cursor 루트/턴 블롭이 재생될 때 글자가 끼어 들어가는 문제를, 서빙 순간에 찾아 진단 로그로 남깁니다. 지금 dev의 src/adapters/cursor/native-exec.ts 618-636줄 handleCursorNativeKv는 getBlobArgs가 오면 키로 바이트를 찾아 그대로 돌려줍니다. 내용 주소 블롭은 32바이트 id가 SHA-256입니다. 바이트가 바뀌면 id와 맞지 않게 됩니다. 라이브 서브에이전트가 [tool_result] 봉투 안에 엉뚱한 토큰이 끼는 모습을 보았고, 그 신호를 잡으려는 계측입니다.

cursorBlobServeIntegrityOk는 id가 32바이트일 때만 해시를 비교하고, 더 짧거나 긴 id는 통과시킵니다. 불일치여도 서빙을 막지 않고 blob-integrity-mismatch 진단만 남깁니다. 키 앞 18자와 길이만 남겨 본문은 로그에 안 넣습니다. 그 선택은 맞습니다. 파서가 아니라 재생 경로의 계측이고, 잘못된 양성으로 대화를 끊지 않습니다.

하지만 본문이 말하는 것과 실제 변경이 다릅니다. 체크리스트는 진단만 하고 서빙 동작을 바꾸지 않는다고 합니다. 실제 diff는 세 가지를 더 바꿉니다. src/adapters/cursor.ts는 체크포인트 커밋을 거절할 때 이유를 로그합니다. 이것은 진단입니다. src/adapters/cursor/live-transport.ts의 clientToolFinalizeGraceMsForRequest는 병렬 도구가 켜져 있고 도구가 둘 이상이면 드레인 유예 시간을 늘립니다. 이것은 서빙 동작입니다. src/adapters/cursor/tool-definitions.ts 665-667줄 안내는 NEVER attempt로 바뀌어, 거절 뒤 이어가라는 문장을 없앱니다. 이것도 모델이 읽는 프롬프트이므로 동작입니다.

이 PR도 지금 dev 위가 아닙니다. 베이스는 #2654입니다. 스택 규칙상 #2650-#2654가 먼저입니다. 테스트 세 개는 깨끗한 해시, 끼워 넣은 오염, 32바이트가 아닌 id를 고정합니다. getBlobArgs가 진단을 실제로 부르는지는 그 테스트가 아닙니다. 병렬 유예 시간 변경을 되돌리면 도구 개수 데모가 다시 일찍 끝날 수 있어서, 진단 PR에 숨기기에는 큽니다.

src/adapters/cursor/native-exec.ts:622 - getBlobArgs가 id와 바이트를 비교하지 않아 오염을 조용히 재생합니다.
src/adapters/cursor/live-transport.ts - 병렬 도구 유예 시간 연장은 진단이 아니라 서빙 동작인데 같은 PR에 들어 있습니다.
src/adapters/cursor/tool-definitions.ts:665 - NEVER 안내는 모델 행동을 바꾸므로 진단 전용이 아닙니다.
경로/심볼 - 베이스가 #2654라 현재 dev(7181ebd)에는 혼자 합칠 수 없습니다.
경로/심볼 - 불일치여도 서빙을 막지 않아, 오염은 로그에만 남고 사용자는 깨진 재생을 그대로 봅니다.

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

  • 블롭 불일치일 때 로그만 남길지, 그 턴을 실패로 바꿀지 정해야 합니다.
  • 병렬 유예 시간과 NEVER 안내를 이 진단 PR에서 뺄지, 따로 쌓을지 정해야 합니다.
  • #2654가 먼저 들어간 뒤에만 볼지 정해야 합니다.

너의 추천

블롭 해시 검사는 유지하되, 병렬 유예와 프롬프트 문장 변경은 이 PR에서 빼는 것을 추천합니다. 진단만 남긴 뒤 #2650-#2654 다음에 합치세요. 지금 dev에 직접 합치지 마세요. 미리보기 배포는 계획에 없습니다.

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

@lidge-jun
lidge-jun merged commit ae6d2ef into codex/cursor-gap-5 Aug 26, 2026
25 of 28 checks passed
@lidge-jun
lidge-jun deleted the codex/cursor-gap-6 branch August 26, 2026 06:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant