Skip to content

fix(codex): keep omitted-final source replies from releasing the turn#107388

Closed
mrfandu1 wants to merge 1 commit into
openclaw:mainfrom
mrfandu1:fix/codex-omitted-final-progress-release
Closed

fix(codex): keep omitted-final source replies from releasing the turn#107388
mrfandu1 wants to merge 1 commit into
openclaw:mainfrom
mrfandu1:fix/codex-omitted-final-progress-release

Conversation

@mrfandu1

Copy link
Copy Markdown

Closes #106961

What Problem This Solves

In message_tool_only delivery mode, the Codex app-server treated any delivered message source reply as terminal unless the model explicitly passed final: false. An agent that sent an early progress update ("on it, will report back") had its turn released immediately: the remaining tool calls and the real final reply never ran. From the user's side the agent posts one status line and then goes silent (log signal: codex app-server turn released after terminal dynamic tool result).

Why This Change Was Made

The projected final schema control tells the model to "set true only when this message is intended to complete the reply" - so models naturally omit final on progress sends, and the runtime contradicted its own schema by terminating anyway.

This change makes inferred source-reply delivery release the turn only on explicit final: true, matching the generic embedded-runner contract established in #92343: intermediate delivery continues the turn, and termination follows the normal Codex turn/completed lifecycle.

Safety properties preserved:

  • The sourceReplyFinal completed marker for omitted final is unchanged, so stranded-reply recovery still cannot re-deliver a duplicate of a delivered reply.
  • Duplicate end-block suppression via didDeliverSourceReplyViaMessageTool is unchanged.
  • Tool-owned terminate results and yield results still release as before.
  • Mixed dynamic-tool batch semantics (resolveTerminalDynamicToolBatchAction) are untouched.

The final schema description now also tells models that omitting it keeps the turn alive, so the control is learnable instead of discipline-based.

User Impact

Codex-backed agents on Discord/Telegram/etc. can post intermediate progress messages and keep executing until the work is done. Explicit final: true still stops the turn after confirmed delivery.

Evidence

  • extensions/codex/src/app-server/dynamic-tools.test.ts: 99/99 pass. Omitted-final delivered replies are now non-terminal with a completed marker; explicit final: true still releases across implicit sends, explicit source routes, receipt-confirmed replies, and middleware-redacted receipts.
  • extensions/codex/src/app-server/dynamic-tool-build.test.ts + dynamic-tool-execution.test.ts: 74/74 pass.
  • pnpm tsgo:extensions clean, oxfmt --check clean on changed files.
  • Fork CI (full matrix, pre-rebase head): all codex/agents/auto-reply lanes green; the only failures were checks-ui (chat-tool-cards.test.ts MCP App preview test, unrelated to this change) and checks-fast-bundled-protocol on a base 106 commits behind main; branch has since been rebased onto current main (no upstream changes to the touched files).

AI-assisted: implemented and validated with an AI coding agent; I have reviewed and understand the change.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adjusts the Codex extension’s app-server dynamic-tool bridge so that, in message_tool_only delivery mode, inferred source-reply message deliveries only release the turn when the model explicitly sets final: true (rather than treating omitted final as terminal), and updates the schema guidance plus tests to match.

Changes:

  • Clarify the Codex-only message.final parameter description to state that omitting final (or setting it false) is for progress updates and should not end the turn.
  • Update createCodexDynamicToolBridge turn-release inference so delivered/receipt-confirmed source replies only terminate on explicit final === true.
  • Update dynamic-tool bridge tests to cover omitted-final non-terminal behavior and explicit-final terminal behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
extensions/codex/src/app-server/message-tool-final-control.ts Updates the final parameter description to align model guidance with the intended continuation semantics.
extensions/codex/src/app-server/dynamic-tools.ts Changes inferred termination for delivered source replies to require explicit final: true while preserving delivery markers/telemetry.
extensions/codex/src/app-server/dynamic-tools.test.ts Updates expectations and adds coverage for omitted-final vs explicit-final terminal behavior.

@mrfandu1
mrfandu1 force-pushed the fix/codex-omitted-final-progress-release branch from 9006f6f to f299e64 Compare July 14, 2026 12:07
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. mantis: telegram-visible-proof Mantis should capture Telegram visible proof. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jul 14, 2026
@clawsweeper

clawsweeper Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 15, 2026, 12:03 AM ET / 04:03 UTC.

Summary
The PR makes omitted or false message.final source replies continue a Codex app-server turn, reserves inferred release for explicit final: true, preserves tool-owned termination and source-reply completion telemetry, updates schema guidance, and adjusts regression tests.

PR surface: Source -5, Tests +8. Total +3 across 3 files.

Reproducibility: yes. at source level: current terminal classification releases a message_tool_only turn after an omitted-final confirmed source reply, matching the reported log and lost-follow-up behavior. A live current-head channel reproduction is not included.

Review metrics: 1 noteworthy metric.

  • Inferred terminal paths: 2 changed. Delivered-source and receipt-confirmed source replies with omitted final stop releasing the turn, so both continuation and recovery behavior need live proof.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #106961
Summary: The issue is canonical; this PR and #107274 are competing candidate fixes, while the merged generic-runner PR is sibling precedent rather than a covering fix.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🐚 platinum hermit
Result: blocked until real behavior proof from a real setup is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add redacted live Discord or Telegram evidence showing progress delivery, subsequent tool execution, and exactly one final reply.
  • Include a timeout or error recovery observation showing the delivered progress message is not replayed.

Proof guidance:

  • [P1] Needs real behavior proof before merge: Focused tests and CI exercise the bridge but do not show the changed behavior in a real Codex-backed channel; add a redacted transcript, recording, terminal output, or runtime log showing progress, a later tool, one final reply, and recovery without duplication, then update the PR body to trigger review or ask a maintainer for @clawsweeper re-review.

Mantis proof suggestion
Telegram Desktop proof can directly show that an omitted-final Codex progress message does not release the turn and that one final reply follows after another tool. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis telegram desktop proof: verify an omitted-final Codex progress message continues to a follow-up tool, produces exactly one final reply, and is not replayed on recovery.

Risk before merge

  • [P2] A unit-tested terminal-classification change can still duplicate or suppress channel output if progress delivery, later final delivery, timeout recovery, and stranded-reply recovery interact differently in a real Codex-backed channel run.
  • [P1] Two open PRs modify the same decision surface with different treatment of completion telemetry and tool-owned termination; merging either without selecting one canonical semantic contract risks follow-up drift.

Maintainer options:

  1. Prove the complete message lifecycle (recommended)
    Before merge, capture a redacted live Codex-backed channel run showing progress delivery, continued tool execution, exactly one final reply, and no duplicate during timeout or error recovery.
  2. Accept test-only delivery risk
    Maintainers may intentionally merge on source and unit-test evidence, owning the unresolved real-channel exactly-once delivery risk.
  3. Pause for canonical branch selection
    Keep both overlapping PRs open until a Codex-area owner chooses one semantic contract or prepares a narrow replacement.

Next step before merge

  • [P1] A Codex-area owner must select the canonical branch and review live exactly-once delivery proof; there is no narrow code defect for an automated repair worker.

Maintainer decision needed

  • Question: Which Codex source-reply terminal contract should become canonical for the linked regression: this PR's omitted-as-progress behavior with the completed marker preserved, the competing branch's broader classifier change, or a maintainer replacement?
  • Rationale: Both open PRs target the same high-risk message lifecycle, but differ on recovery telemetry and tool-owned termination; tests alone cannot establish the intended exactly-once channel contract.
  • Likely owner: omarshahine — This person introduced and merged the current Codex message-tool-only completion behavior, making them the strongest available owner for its terminal and recovery semantics.
  • Options:
    • Use this branch after live proof (recommended): Keep the narrower explicit-final release rule and preserved completion marker, then require a live channel continuation and recovery transcript before merge.
    • Adopt the competing branch: Select fix(codex): continue after source-reply delivery instead of terminating turn #107274 only after it preserves or atomically redesigns exactly-once recovery and supplies equivalent live proof.
    • Create a maintainer replacement: Close both contributor branches only after a narrow owner-authored replacement exists and proves the same progress, final, and recovery behavior.

Security
Cleared: The focused TypeScript runtime, schema text, and test changes add no dependency, workflow, permission, secret, install, publishing, or downloaded-code execution surface.

Review details

Best possible solution:

Keep this PR open until a linked canonical PR proves it covers this PR's unique work, or a maintainer confirms closure.

Do we have a high-confidence way to reproduce the issue?

Yes at source level: current terminal classification releases a message_tool_only turn after an omitted-final confirmed source reply, matching the reported log and lost-follow-up behavior. A live current-head channel reproduction is not included.

Is this the best way to solve the issue?

Yes, provisionally: changing the Codex bridge's inferred terminal classification and schema guidance is narrower and safer than adding config or a parallel message tool, and preserving the completed marker improves on the competing PR; live exactly-once proof is still required.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 3eb8b3a0f838.

Label changes

Label justifications:

  • P1: The PR addresses a current Codex channel regression where an early progress message can silently prevent all remaining tools and final delivery.
  • merge-risk: 🚨 message-delivery: Changing source-reply terminal classification can suppress, duplicate, or omit the later final channel reply if recovery and finalization do not remain exactly-once.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: Focused tests and CI exercise the bridge but do not show the changed behavior in a real Codex-backed channel; add a redacted transcript, recording, terminal output, or runtime log showing progress, a later tool, one final reply, and recovery without duplication, then update the PR body to trigger review or ask a maintainer for @clawsweeper re-review.
  • mantis: telegram-visible-proof: Mantis should capture Telegram visible proof. The user-visible progress-to-final Telegram transcript can be demonstrated directly in a short Telegram Desktop recording.
Evidence reviewed

PR surface:

Source -5, Tests +8. Total +3 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 2 14 19 -5
Tests 1 15 7 +8
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 29 26 +3

What I checked:

  • PR close coverage proof: PR close coverage proof kept this PR open against fix(agent): continue after source message tool replies #92343: PR B is sibling precedent for the generic runner, not a covering Codex fix. PR A’s durable report explicitly identifies the Codex app-server regression as remaining on current main and requiring Codex-specific behavior plus exactly-once delivery review.
  • linked superseding PR: fix(agent): continue after source message tool replies #92343 (fix(agent): continue after source message tool replies) is merged at 2026-06-12T16:14:58Z.
  • cluster evidence: the durable review links that PR in the work cluster or recommended risk path.
  • no human follow-up: live comments and timeline hydrated by apply contain no non-automation activity after the ClawSweeper review.

Likely related people:

  • omarshahine: Authored and merged the Codex message-tool-only completion work that established the terminal-on-confirmed-delivery behavior now being refined. (role: introduced current behavior; confidence: high; commits: 9b9a124cc520; files: extensions/codex/src/app-server/dynamic-tools.ts, extensions/codex/src/app-server/dynamic-tools.test.ts)
  • joshavant: Authored the merged generic-runner source-reply continuation and duplicate-suppression work that defines the sibling invariant this PR follows. (role: introduced sibling behavior; confidence: high; commits: 462092936a5c; files: src/agents/embedded-agent-message-tool-source-reply.ts, src/agents/embedded-agent-runner/run/message-tool-terminal.ts)
  • vincentkoc: Recent history cited in the merged Codex completion review connects this contributor to Codex dynamic-tool protocol and agent-harness boundary work near the affected path. (role: recent adjacent contributor; confidence: medium; commits: ab1e5832d2fb, 3ffb3609a141, d3019e612705; files: extensions/codex/src/app-server/dynamic-tools.ts, extensions/codex/src/app-server/run-attempt.ts, src/plugin-sdk/agent-harness-runtime.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (5 earlier review cycles)
  • reviewed 2026-07-14T14:33:14.525Z sha f299e64 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-14T15:00:49.839Z sha f299e64 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-14T15:25:04.263Z sha f299e64 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-14T15:54:23.307Z sha f299e64 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-15T02:48:09.201Z sha e91e2f3 :: needs real behavior proof before merge. :: none

@mrfandu1
mrfandu1 force-pushed the fix/codex-omitted-final-progress-release branch from f299e64 to e91e2f3 Compare July 14, 2026 16:19
wangyan2026 added a commit to wangyan2026/openclaw that referenced this pull request Jul 15, 2026
…inating turn

Delivered and receipt-confirmed source replies in message_tool_only mode
no longer set terminate=true on the dynamic tool response, matching the
generic embedded-runner behavior established by PR openclaw#92343.

- Remove deliveredSourceReply/receiptConfirmedSourceReply from
  withDynamicToolTermination conditions
- Only final=true + actual source reply explicitly terminates
- toolConfirmedSourceReply (tool-returned terminate:true) still
  terminates unless overridden by final=false
- sourceReplyFinal telemetry: still true when final omitted
  (preserves completed marker for stranded-reply recovery)
- Model-facing final guidance updated to explain progress semantics
  (incorporated from openclaw#107388)

Related to openclaw#106961
wangyan2026 added a commit to wangyan2026/openclaw that referenced this pull request Jul 15, 2026
…inating turn

Delivered and receipt-confirmed source replies in message_tool_only mode
no longer set terminate=true on the dynamic tool response, matching the
generic embedded-runner behavior established by PR openclaw#92343.

- Remove deliveredSourceReply/receiptConfirmedSourceReply from
  withDynamicToolTermination conditions
- Only final=true + actual source reply explicitly terminates
- toolConfirmedSourceReply (tool-returned terminate:true) still
  terminates unless overridden by final=false
- sourceReplyFinal telemetry: still true when final omitted
  (preserves completed marker for stranded-reply recovery)
- Model-facing final guidance updated to explain progress semantics
  (incorporated from openclaw#107388)

Related to openclaw#106961
@mrfandu1
mrfandu1 force-pushed the fix/codex-omitted-final-progress-release branch from 6b078d2 to 2c256aa Compare July 15, 2026 13:37
@mrfandu1

Copy link
Copy Markdown
Author

Live Telegram evidence

Environment: real Telegram DM to the test bot, OpenClaw Codex app-server backed by OpenAI gpt-5.6-sol, message_tool_only source delivery, Node 24.18.0, pnpm 11.2.2.

Controlled fixture used identically for before/after: the workspace instructions required the intermediate progress message call to omit the optional final field, continue with the requested tool action, and send the completed reply exactly once with final: true. No OpenClaw source was changed for the fixture.

The channel transcript and runtime excerpts below are redacted: they expose no token, phone number, personal username, chat/user ID, real name, or absolute home/workspace path.

Before (main @ 54f70c5d8c040f935b5d6aaa72ff514387bfae2c)

Telegram transcript:

User: Send me a short progress message right now saying you're starting. Then list the files in your current working directory. Then send me the final summary of what you found.
Bot: I’m starting now—I'll inspect the current working directory and report back.

No directory result or final reply arrived before the next /new.

Native Codex rollout lines 12, 16, and 21, selected in their original order; only identifiers were redacted:

{"timestamp":"2026-07-15T12:07:04.349Z","type":"response_item","payload":{"type":"custom_tool_call","id":"[redacted]","status":"completed","call_id":"[redacted]","name":"exec","input":"const r = await tools.message({action:\"send\", message:\"I’m starting now—I'll inspect the current working directory and report back.\"});\ntext(r);\n","internal_chat_message_metadata_passthrough":{"turn_id":"[redacted]"}}}
{"timestamp":"2026-07-15T12:07:28.671Z","type":"response_item","payload":{"type":"function_call","id":"[redacted]","name":"wait","arguments":"{\"cell_id\":\"1\",\"yield_time_ms\":10000,\"max_tokens\":1000}","call_id":"[redacted]","internal_chat_message_metadata_passthrough":{"turn_id":"[redacted]"}}}
{"timestamp":"2026-07-15T12:07:41.092Z","type":"event_msg","payload":{"type":"turn_aborted","turn_id":"[redacted]","reason":"interrupted","completed_at":1784117261,"duration_ms":45956}}

Gateway lines, selected in their original order; only chat/run/session IDs were redacted:

2026-07-15T17:52:39.607+05:45 [telegram] outbound send ok accountId=default chatId=[redacted-chat-id] messageId=20 operation=sendMessage deliveryKind=text chunkCount=1
2026-07-15T17:52:40.594+05:45 [agent/embedded] codex app-server turn released after terminal dynamic tool result
2026-07-15T17:52:41.327+05:45 [agent/embedded] embedded run done: runId=[redacted] sessionId=[redacted] durationMs=76314 aborted=false

Observation: main delivered the omitted-final progress message and immediately released the turn. The native rollout then ended without a shell_command call or final message call.

After (fix/codex-omitted-final-progress-release @ 2c256aae48ca22c55a4112ea39b927372f3828a2)

This local head is one commit above upstream main 69ef3ca980a7262577c6e6bcb3d8abc08a5325b1. Targeted runtime build passed. Focused Vitest result: 2 files passed, 115 tests passed.

Telegram transcript:

User: Send me a short progress message right now saying you're starting. Then list the files in your current working directory. Then send me the final summary of what you found.
Bot (progress): I’m starting now—checking the current working directory.
Bot (final): Found 9 items in `[redacted-workspace]`:

- `.git/`
- `AGENTS.md`
- `BOOTSTRAP.md`
- `HEARTBEAT.md`
- `IDENTITY.md`
- `openclaw-workspace-state.json`
- `SOUL.md`
- `TOOLS.md`
- `USER.md`

Summary: the directory is a newly initialized OpenClaw workspace with Git metadata, configuration/personality files, and the bootstrap file still present. There is no `memory/` directory yet.

Native Codex rollout lines 12, 19, 23, and 27, selected in their original order; only identifiers and the absolute workspace path were redacted:

{"timestamp":"2026-07-15T13:14:24.683Z","type":"response_item","payload":{"type":"custom_tool_call","id":"[redacted]","status":"completed","call_id":"[redacted]","name":"exec","input":"const r = await tools.message({action:\"send\", message:\"I’m starting now—checking the current working directory.\"}); text(r)\n","internal_chat_message_metadata_passthrough":{"turn_id":"[redacted]"}}}
{"timestamp":"2026-07-15T13:14:44.577Z","type":"response_item","payload":{"type":"custom_tool_call","id":"[redacted]","status":"completed","call_id":"[redacted]","name":"exec","input":"const r = await tools.shell_command({command:\"Get-ChildItem -Force | Select-Object Mode, Length, LastWriteTime, Name | Format-Table -AutoSize\",\"workdir\":\"[redacted-workspace]\"}); text(r)\n","internal_chat_message_metadata_passthrough":{"turn_id":"[redacted]"}}}
{"timestamp":"2026-07-15T13:14:53.067Z","type":"response_item","payload":{"type":"custom_tool_call","id":"[redacted]","status":"completed","call_id":"[redacted]","name":"exec","input":"const r = await tools.message({action:\"send\", final:true, message:\"Found 9 items in `[redacted-workspace]`:\\n\\n- `.git/`\\n- `AGENTS.md`\\n- `BOOTSTRAP.md`\\n- `HEARTBEAT.md`\\n- `IDENTITY.md`\\n- `openclaw-workspace-state.json`\\n- `SOUL.md`\\n- `TOOLS.md`\\n- `USER.md`\\n\\nSummary: the directory is a newly initialized OpenClaw workspace with Git metadata, configuration/personality files, and the bootstrap file still present. There is no `memory/` directory yet.\"}); text(r)\n","internal_chat_message_metadata_passthrough":{"turn_id":"[redacted]"}}}
{"timestamp":"2026-07-15T13:14:57.259Z","type":"event_msg","payload":{"type":"turn_aborted","turn_id":"[redacted]","reason":"interrupted","completed_at":1784121297,"duration_ms":40886}}

Gateway lines, selected in their original order; only chat/run/session IDs were redacted:

2026-07-15T18:59:40.792+05:45 [telegram] outbound send ok accountId=default chatId=[redacted-chat-id] messageId=34 operation=sendMessage deliveryKind=text chunkCount=1
2026-07-15T18:59:56.736+05:45 [telegram] outbound send ok accountId=default chatId=[redacted-chat-id] messageId=35 operation=sendMessage deliveryKind=text chunkCount=1
2026-07-15T18:59:56.876+05:45 [agent/embedded] codex app-server turn released after terminal dynamic tool result
2026-07-15T18:59:57.438+05:45 [agent/embedded] embedded run done: runId=[redacted] sessionId=[redacted] durationMs=54742 aborted=false

Observation: the omitted-final progress call delivered without releasing the turn; Codex then executed Get-ChildItem, sent exactly one final: true reply, and only that explicit final released the turn. The native turn_aborted event is the expected app-server interruption caused by explicit-final release; the OpenClaw gateway records the completed run as aborted=false.

Error-recovery observation (same patch, earlier byte-identical local rebase @ b67d98468a41a365e9ecf06d66577046f4a96b1c)

Telegram transcript:

User: Send me a short progress message right now saying you're starting the recovery check. Then use the directory-listing tool on this deliberately missing path so it fails: Z:\openclaw-proof-missing-7c2f. After the error, recover by listing the files in your current working directory. Then send exactly one final summary. Do not repeat the progress message.
Bot (progress): I’m starting the recovery check.
Bot (final): Recovery check completed: the missing Z: path failed as expected, then I successfully listed the current workspace files (including AGENTS.md, BOOTSTRAP.md, SOUL.md, TOOLS.md, and USER.md).

Native Codex rollout lines 12, 15, 16, 18, 19, 21, and 25, selected in their original order; only identifiers and the absolute workspace path were redacted:

{"timestamp":"2026-07-15T12:33:38.813Z","type":"response_item","payload":{"type":"custom_tool_call","id":"[redacted]","status":"completed","call_id":"[redacted]","name":"exec","input":"const r = await tools.message({action:\"send\", message:\"I’m starting the recovery check.\"});\ntext(r);\n","internal_chat_message_metadata_passthrough":{"turn_id":"[redacted]"}}}
{"timestamp":"2026-07-15T12:33:46.548Z","type":"response_item","payload":{"type":"custom_tool_call","id":"[redacted]","status":"completed","call_id":"[redacted]","name":"exec","input":"const r = await tools.shell_command({command:\"Get-ChildItem -LiteralPath 'Z:\\\\openclaw-proof-missing-7c2f'\",\"workdir\":\"[redacted-workspace]\"});\ntext(r);\n","internal_chat_message_metadata_passthrough":{"turn_id":"[redacted]"}}}
{"timestamp":"2026-07-15T12:33:47.163Z","type":"response_item","payload":{"type":"custom_tool_call_output","call_id":"[redacted]","output":[{"type":"input_text","text":"Script failed\nWall time 0.6 seconds\nOutput:\n"},{"type":"input_text","text":"Script error:\nExit code: 1\nWall time: 0.3 seconds\nOutput:\nGet-ChildItem : Cannot find drive. A drive with the name 'Z' does not exist.\r\nAt line:2 char:1\r\n+ Get-ChildItem -LiteralPath 'Z:\\openclaw-proof-missing-7c2f'\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n    + CategoryInfo          : ObjectNotFound: (Z:String) [Get-ChildItem], DriveNotFoundException\r\n    + FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand\r\n \r\n"}],"internal_chat_message_metadata_passthrough":{"turn_id":"[redacted]"}}}
{"timestamp":"2026-07-15T12:33:50.042Z","type":"response_item","payload":{"type":"custom_tool_call","id":"[redacted]","status":"completed","call_id":"[redacted]","name":"exec","input":"const r = await tools.shell_command({command:\"Get-ChildItem -Force\",\"workdir\":\"[redacted-workspace]\"});\ntext(r);\n","internal_chat_message_metadata_passthrough":{"turn_id":"[redacted]"}}}
{"timestamp":"2026-07-15T12:33:50.483Z","type":"response_item","payload":{"type":"custom_tool_call_output","call_id":"[redacted]","output":[{"type":"input_text","text":"Script completed\nWall time 0.4 seconds\nOutput:\n"},{"type":"input_text","text":"Exit code: 0\nWall time: 0.3 seconds\nOutput:\n\r\n\r\n    Directory: [redacted-workspace]\r\n\r\n\r\nMode                 LastWriteTime         Length Name                                                                 \r\n----                 -------------         ------ ----                                                                 \r\nd--h--         7/15/2026   2:57 PM                .git                                                                 \r\n-a----         7/15/2026   2:57 PM           7196 AGENTS.md                                                            \r\n-a----         7/15/2026   2:57 PM           1669 BOOTSTRAP.md                                                         \r\n-a----         7/15/2026   2:57 PM            244 HEARTBEAT.md                                                         \r\n-a----         7/15/2026   2:57 PM           1278 IDENTITY.md                                                          \r\n-a----         7/15/2026   2:57 PM             70 openclaw-workspace-state.json                                        \r\n-a----         7/15/2026   2:57 PM           1518 SOUL.md                                                              \r\n-a----         7/15/2026   4:37 PM           1389 TOOLS.md                                                             \r\n-a----         7/15/2026   2:57 PM            537 USER.md                                                              \r\n\r\n\r\n"}],"internal_chat_message_metadata_passthrough":{"turn_id":"[redacted]"}}}
{"timestamp":"2026-07-15T12:33:53.320Z","type":"response_item","payload":{"type":"custom_tool_call","id":"[redacted]","status":"completed","call_id":"[redacted]","name":"exec","input":"const r = await tools.message({action:\"send\", final:true, message:\"Recovery check completed: the missing Z: path failed as expected, then I successfully listed the current workspace files (including AGENTS.md, BOOTSTRAP.md, SOUL.md, TOOLS.md, and USER.md).\"});\ntext(r);\n","internal_chat_message_metadata_passthrough":{"turn_id":"[redacted]"}}}
{"timestamp":"2026-07-15T12:33:55.578Z","type":"event_msg","payload":{"type":"turn_aborted","turn_id":"[redacted]","reason":"interrupted","completed_at":1784118835,"duration_ms":26240}}

Gateway recovery lines, selected in their original order; only chat/run/session IDs were redacted:

2026-07-15T18:18:43.001+05:45 [telegram] outbound send ok accountId=default chatId=[redacted-chat-id] messageId=29 operation=sendMessage deliveryKind=text chunkCount=1
2026-07-15T18:18:47.005+05:45 [agent/embedded] codex app-server stderr: {"timestamp":"2026-07-15T12:33:47.005195Z","level":"ERROR","fields":{"error":"Exit code: 1\nWall time: 0.3 seconds\nOutput:\nGet-ChildItem : Cannot find drive. A drive with the name 'Z' does not exist.\r\nAt line:2 char:1\r\n+ Get-ChildItem -LiteralPath 'Z:\\openclaw-proof-missing-7c2f'\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n    + CategoryInfo          : ObjectNotFound: (Z:String) [Get-ChildItem], DriveNotFoundException\r\n    + FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand\r\n \r\n"},"target":"codex_core::tools::router"}
2026-07-15T18:18:55.428+05:45 [telegram] outbound send ok accountId=default chatId=[redacted-chat-id] messageId=30 operation=sendMessage deliveryKind=text chunkCount=1
2026-07-15T18:18:55.507+05:45 [agent/embedded] codex app-server turn released after terminal dynamic tool result
2026-07-15T18:18:55.947+05:45 [agent/embedded] embedded run done: runId=[redacted] sessionId=[redacted] durationMs=36721 aborted=false

Recovery observation: the omitted-final progress message was delivered once, the deliberate missing-drive tool failed, Codex continued with a successful directory listing, and exactly one final reply followed. The progress message was not replayed.

Exactly one progress message, one continued tool execution, one final reply, no duplicates.

@mrfandu1

Copy link
Copy Markdown
Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@mrfandu1

Copy link
Copy Markdown
Author

Closing as superseded by maintainer PR #108487, which resolves #106961 with authoritative turn-completion finalization.

@mrfandu1 mrfandu1 closed this Jul 16, 2026
@joshavant

Copy link
Copy Markdown
Contributor

Thanks @mrfandu1 for the careful contribution and especially the live Telegram before/after and recovery evidence. Your PR correctly identified that an omitted final must not immediately release the Codex turn.

We landed the maintainer replacement at #108487. The remaining gap was finality settlement: simply continuing the turn while treating the delivered reply as already complete could suppress recovery after a later failure. The landed fix carries omitted finality to the authoritative turn outcome, promotes only the latest omitted reply on success, and leaves it non-final on failure.

Closing this as superseded by the merged root-cause fix. Thank you—your investigation and proof materially helped validate the direction.

@mrfandu1
mrfandu1 deleted the fix/codex-omitted-final-progress-release branch July 16, 2026 04:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: codex mantis: telegram-visible-proof Mantis should capture Telegram visible proof. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P1 High-priority user-facing bug, regression, or broken workflow. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Discord/Codex runtime: message tool is terminal, so progress updates silently end the turn

3 participants