Skip to content

[Regression] Cursor/Grok 4.6 can loop in repeated planning after #350 with no state delta #2600

Closed

Description

@stephen-drew

Client or integration

Codex App

Area

Cursor provider adapter / Responses continuation / loop control

Summary

A fresh Codex App task routed through OpenCodex 2.31.0 to cursor/grok-4.6 remained active for roughly 45 minutes while repeatedly emitting near-equivalent future-tense progress messages and making successful read-only tool calls. It never created the requested file, ran a test, committed, or produced a bounded blocker. The operator had to interrupt it.

This is a sanitized compatibility report. The current evidence does not prove whether the repeating behaviour originates in the Cursor upstream model, the Codex client continuation policy, or OpenCodex state/transport handling. It does prove that the combined route has no effective semantic no-progress boundary for this failure class.

Reproduction

  1. Run Codex App on Windows 11 through a local OpenCodex 2.31.0 proxy.
  2. Start a fresh task on cursor/grok-4.6.
  3. Give it a bounded repository task: inspect existing policy pages, create one small hub page following the existing patterns, run the relevant checks, then stop.
  4. Let the model inspect the repository with its available tools.
  5. Observe repeated planning/commentary and additional successful read-only calls without any repository state change.
  6. Manually interrupt the turn.

This precise 2.31.0 trace was captured once. The same near-identical planning-without-completion symptom class has appeared in two earlier local tasks, but this report relies only on the captured Cursor run below.

Expected behaviour

After a bounded amount of activity, the route should do one of the following:

  • make an observable state change and continue;
  • finish the requested task;
  • return a typed, actionable blocker;
  • terminate as a no-progress failure without replaying potentially side-effecting work.

Repeated successful reads and reworded promises should not allow one coding turn to remain active indefinitely.

Logs or error output

duration before manual interruption: approximately 45 minutes
persisted continuation states inspected: 18
state items: 78 -> 141
tool calls: 13 -> 28
tool outputs: 12 -> 27
requested file created: no
worktree dirty after interruption: no
test run: no
commit: no
terminal blocker before interruption: no

The assistant repeatedly produced variants of the same future-tense message: the existing page still needed adding, it would inspect the pattern, then create the page and open a Preview. Tool traffic continued, but the promised mutation never occurred.

Persisted-state inspection also found:

Those last two points are correlated context inflation, not a claimed root cause. Their ownership may be in Codex App rather than OpenCodex.

Why this is not an existing report

Suggested investigation and acceptance criteria

  1. Add privacy-safe diagnostics that distinguish client-driven continuations, OpenCodex continuation requests, and one still-active Cursor stream.
  2. Track progress-relevant event classes across the live Cursor turn. Reworded commentary and successful read-only calls must not reset the no-progress budget.
  3. Make the cutoff capable of cancelling an already active Cursor stream. A guard that only runs before the next request cannot stop this class.
  4. Emit one typed terminal result on cutoff, including completed work and the blocker.
  5. Do not automatically retry after any potentially side-effecting tool call.
  6. Deduplicate repeated client developer blocks where safe, and expose enough provenance to identify which layer injected them.
  7. Add a representative regression: inspect a small fixture repository, create one file, run one check, and either complete or return a typed blocker within a fixed time/tool budget.

Version

OpenCodex 2.31.0; Codex runtime 0.147.x.

Operating system

Windows 11 Home 25H2, OS build 26200.9168, x64.

Provider and model

cursor / grok-4.6

Screenshots and supporting files

No repository screenshots or prompt captures are attached because they would contain private task content. The sanitized counters above are from the persisted OpenCodex continuation state.

Redacted configuration

{
  "client": "Codex App",
  "proxy": "local loopback",
  "provider": "cursor",
  "model": "grok-4.6"
}

Checks

  • I searched existing issues and related fixes.
  • I removed task content, repository details, local paths, credentials, tokens, account data and personal data.
  • I separated observed facts from suspected ownership and root cause.

Activity

  1. added
    bugSomething isn't working
    toolstool_calls, MCP, web-search / sidecar tools
    on Aug 25, 2026
  2. github-actions commented on Aug 25, 2026

    @github-actions
    Contributor

    Issue reopened

    The report now contains the information required by the automated check. Thanks for updating it.

  3. lidge-jun commented on Aug 25, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 66 / 80

    설명: 이 이슈는 Codex App 이 OpenCodex 2.31.0 을 거쳐 cursor/grok-4.6 으로 보낸 작업이, 읽기만 반복하고 파일을 만들지 않은 채 약 45분 동안 같은 계획 말을 되풀이했다고 한다. 작성자는 stephen-drew 이다. 지금 열려 있다. 품질 봇이 한 번 닫았다가 정보가 채워져 다시 열렸다. 라벨은 bug, tools 이다. 지금 CURRENT dev HEAD 는 6e6d5cd 이다. 직전은 316043e 였고 이번 시간에 2598 2599 2601 2602 가 착지했다. origin/dev package.json 은 2.32.1-preview.20260825 다. 본문이 적은 숫자는 이렇다. 이어진 상태 기록 18개, 상태 항목 78에서 141, 도구 호출 13에서 28, 도구 결과 12에서 27. 요청한 파일은 만들어지지 않았고 작업 폴더는 더러워지지 않았다. 터미널 차단도 없었다. 운영자가 직접 끊었다. 내가 구현하지 않고 머지하지 않는다. 태그하지 말 것. 배포하지 말 것.

    HEAD 에서 이 종류의 구멍은 그대로다. src/stall-timeout.ts 8줄 DEFAULT_STALL_TIMEOUT_SEC 는 300초다. 주석은 업스트림이 조용할 때만 센다고 적는다. src/bridge.ts 358줄도 업스트림 활동만 스톨 시계를 되돌린다고 적는다. 874줄과 875줄은 어댑터 이벤트가 올 때마다 stallTicks 를 0 으로 돌린다. 1402줄부터 1405줄 박동도 활동이 있으면 다시 0 이다. 1405줄 한도에 닿으면 1420줄 upstream_stall_timeout 을 내고 1424줄에서 업스트림을 취소한다. 그러나 계획 말과 읽기 도구가 계속 오면 시계는 절대 차지 않는다. 45분 동안 살아 있던 이번 기록과 맞다. 침묵 스톨이지, 진전이 없다는 뜻의 한도가 아니다.

    이번 시간 착지는 이 구멍을 막지 않는다. 2597 과 2598 은 빈 완료를 관찰하고 생산자 경로를 핀으로 막는다. emptyCompletionRetry 는 src/config.ts 3000줄에서 아직 기본 false 다. 본문은 도구 결과가 27개 남았다고 한다. 빈 결과가 사라진 경우가 아니다. 그래서 2472 와 2423 을 이 이슈로 닫지 말 것. 2472 는 2598 이 Completes 로 이미 닫았다. 내가 다시 열지 않는다. 2423 은 아직 열려 있다. 2599 와 2601 은 카탈로그 너비 2574 쪽이다. 2602 는 2497 보안 리뷰 기록이다. 그 열차는 루프 한도와 무관하다. 2210 은 조용한 스톨이다. 본문이 이미 갈랐다. 2210 을 이 이슈로 닫지 말 것. 1527 은 큰 문맥 붕괴다. 섞어 닫지 말 것.

    350 이 고친 프록시 안내 중복은 HEAD 에서도 막혀 있다. src/server/responses/collaboration.ts 511줄 injectDeveloperMessage 는 520줄에서 524줄까지 재생 접두의 마지막 multi_agent_mode 안내가 지금 넣을 글과 같으면 526줄에서 527줄에서 그냥 돌아간다. 본문도 그 안내가 한 장만 남았다고 한다. 326 회귀가 아니다. 다만 같은 함수는 이미 들어온 클라이언트 developer 칸을 접지 않는다. 태그가 없는 글은 재생 접두에 같은 생성 칸이 있을 때만 삽입을 건너뛴다. 본문이 본 apps_instructions 가 1장에서 4장으로 늘어난 일은, 프록시가 그 칸을 접지 않는다는 뜻이다. 소유가 Codex App 일 수 있다. 지금 leftover-close 할 차량도 없다.

    Cursor 쪽 이미 열린 줄기를 끊는 손잡이는 있다. src/adapters/cursor/live-transport.ts 1397줄부터 1399줄은 AbortSignal 이 오면 소켓을 닫는다. 1010줄은 열기 전에 이미 취소됐는지도 본다. 다리의 스톨 경로 1424줄 cancelUpstreamOnce 가 그 신호를 탄다. 문제는 손잡이가 없는 것이 아니라, 진전 없는 활동을 한도로 세어 그 손잡이를 당기는 시계가 없다는 것이다. 요청 사이에만 도는 가드는 살아 있는 Cursor 줄기를 못 끊는다. 본문 제안 3번과 맞다. src/runtime 은 없다. default-aliases.ts 와 model-presets.ts 도 없다. 2463 2464 2465 를 이 이슈로 닫지 말 것. CursorCredentialRouter 는 여전히 src/providers/cursor-pool.ts 28줄 클래스뿐이다. 이 이슈의 차량이 아니다. 프리뷰 배포가 아니다.

    라인 8 - src/stall-timeout.ts DEFAULT_STALL_TIMEOUT_SEC 는 300초다. 침묵만 센다
    라인 358 - src/bridge.ts 업스트림 활동만 스톨 시계를 되돌린다
    라인 874 - src/bridge.ts 어댑터 이벤트가 올 때마다 stallTicks 를 0 으로 돌린다
    라인 1405 - src/bridge.ts 침묵 한도에 닿아야만 upstream_stall_timeout 이다
    라인 1424 - src/bridge.ts 그 경로에서만 업스트림을 취소한다. 활동이 있으면 닿지 않는다
    라인 1397 - src/adapters/cursor/live-transport.ts AbortSignal 이면 이미 열린 Cursor 줄기를 닫는다. 진전 시계가 이 신호를 안 당긴다
    라인 520 - src/server/responses/collaboration.ts multi_agent_mode 안내는 같은 글이면 넣지 않는다. 350 구멍은 막혀 있다
    라인 526 - 태그가 없는 developer 칸은 이미 들어온 복사본을 접지 않는다
    2210/2472/2423 - 다른 구멍이다. 이 이슈로 닫지 말 것
    2595/2583 - 이 이슈의 차량이 아니다

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

    • 이 이슈를 버그로 받을지. HEAD 에는 진전 없는 활동에 대한 한도가 없다. 본문 산수와 맞다
    • 소유를 어디를 기본으로 둘지. Cursor 모델, Codex App 이어가기, OpenCodex 전송이 섞여 있다. 본문도 단정하지 않는다
    • 한도를 산 Cursor 줄기에 걸지, 이어가기 요청 사이에만 걸지. 산 줄기를 못 끊으면 이 기록이 다시 나온다
    • 읽기 성공과 같은 말의 계획을 진전으로 세지 말지. 세면 시계가 또 리셋된다
    • apps_instructions 4장 중복을 프록시가 접을지. 350 안내는 이미 한 장이다. 범위가 다르다
    • 지금 leftover-close 할지. 차량이 없다. 내가 닫지 않는다
    • 2472 2423 2210 과 한 차량으로 묶을지. 묶지 말 것. 주제가 다르다

    너의 추천
    버그로 받는다. 다만 지금 합칠 차량은 없다. 소유를 가르는 진단(클라이언트 이어가기 / 프록시 이어가기 / 하나의 Cursor 줄기)을 먼저 두고, 읽기 성공과 같은 말의 계획을 진전으로 세지 않는 한도를 산 줄기에 걸어 취소를 당기게 하는 쪽이 맞다. 그 한도가 당기면 이미 열린 Cursor 줄기도 끊어야 한다. 1397줄 손잡이는 이미 있다. 내가 구현하지 않고 닫지 않는다. 라벨은 그대로 둔다. 프리뷰 배포가 아니다.

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

  4. lidge-jun commented on Aug 25, 2026

    @lidge-jun
    Owner

    Investigated and recorded on dev by #2628 (squash e6dba5435). No proxy cutoff added, and that is the finding rather than a deferral.

    Your framing was the right one, and I want to confirm it held up: every existing bound was satisfied while nothing changed, and each measures something other than progress.

    Bound Counts Why it did not fire
    Stall watchdog, 300s upstream silence every adapter event resets it, heartbeats and read-only calls included
    MAX_ACTIVE_TURNS = 256 concurrent turns not cumulative — sequential continuations never approach it
    Session lanes, 64 concurrent sessions releasing one turn admits the next indefinitely
    Translator budget 2 MiB/call, 32 MiB/turn live retained bytes completed calls release their allocation
    Response-state caps cache retention not chain depth

    Your numbers are the proof: 78→141 items, 13→28 calls, 18 continuations. Growth on every axis the proxy can see, zero on the one that matters.

    Not a #350 regression. #350 deduplicated proxy-generated multi_agent_mode guidance, that code still runs (collaboration.ts:511), and you observed one copy. This is a pre-existing ownership gap that became visible after continuation reliability improved. The accumulating apps_instructions blocks are client-authored and outside #350's contract, as you suspected.

    Why I did not add the cutoff you suggested in items 2-4. OpenCodex sees protocol events, tool names, payloads, bytes and timing. It cannot see your workspace, and a tool name does not prove absence of side effects. Every heuristic available at this layer fails one way or the other: counting continuations or comparing prose misses your incident, because items and calls kept changing; classifying read-like tools as no-progress kills legitimate slow work. And you were right to warn about retry-after-cutoff replaying side-effecting calls.

    Your item 3 is the sharpest part of the report — a guard that only runs before the next request cannot stop this class. That is true, and it is also why the guard has to live where the workspace does.

    What shipped: the ownership decision recorded in structure/, plus two tests pinning the boundary conditions — that 257 sequential continuations are accepted (so nobody reads 256 as a turn budget), and that a 32-continuation chain with unique tool evidence is valid slow progress (the false-positive case any future bound must not break).

    If a client sends an explicit privacy-safe progress marker, the proxy can enforce that contract. Enforcing an inferred one would be claiming knowledge it does not have. Closing on that basis — reopen if you disagree with the layer call, it is the part most worth arguing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions