Skip to content

fix(pixel): report a reply cut at the output limit instead of ending silently - #6742

Closed
Lightheartdevs wants to merge 2 commits into
mainfrom
claude/pixel-output-limit-recovery
Closed

Lightheartdevs wants to merge 2 commits into
mainfrom
claude/pixel-output-limit-recovery

Conversation

@Lightheartdevs

Copy link
Copy Markdown
Collaborator

Problem (owner report, strixy, 2026-09-25)

The owner asked Pixel: "make me a cool looking webpage with a forest theme and cool forest type effects. Best you can do." Nothing happened.

The session shows why. Qwen3.6-35B-A3B tried to write the whole page in one tool call, and the reply stopped at the output limit: stopReason: "length", usage.output: 8192, after 220 s. The unfinished write never ran, so no file was saved and nothing was published. The owner saw only "Let me build something impressive — a full forest-themed interactive experience."

OpenClaw 2026.6.33 treats a length-stopped reply that is the whole turn as incompleteTerminalAssistant. It then skips before_agent_finalize and shows only a generic "Agent couldn't generate a response. Please try again."

The fleet website journeys never hit this: their specified prompts lead to three small files. An open-ended "best you can do" prompt leads to one huge write.

Fix

  • Pixel records the stop reason of each saved assistant reply (before_message_write, which always runs for this reply). A later complete reply clears it.
  • Delivery. When the final reply was cut at the output limit, the delivered answer says so plainly: the reply reached the output limit before its last step finished, so that step did not run, and Pixel can continue in smaller steps. This replaces the generic retry text and works whether or not the finalize hook ran.
  • Finalize. When OpenClaw does run before_agent_finalize after such a reply, Pixel grants one bounded revision pass (maxAttempts: 1) asking for smaller steps. For a web page that means separate index.html, styles.css and script.js, each kept short. The instruction text is byte-stable.

Not in this PR (follow-up): preventing the oversized single write, by telling the model its output budget and to split large files, and continuing automatically. OpenClaw skips the finalize hook for this turn, so automatic continuation needs the ingress.

Tests

  • tests/output_limit_recovery.test.mjs (8 tests):
    • the strixy request with the recorded reply text;
    • a cut reply with a truncated tool call;
    • one pass only;
    • the delivery report when finalize is skipped;
    • a later complete reply clears the report;
    • heartbeat, cron, main-session and team turns are unchanged.
  • Without the fix, 4 behaviour tests fail.
  • owner_visible_reply.test.mjs, which shares the finalize path, still passes.
  • Full pixel-agent JS suite on Linux: 3129 tests, 3091 pass, 0 fail.

🤖 Generated with Claude Code

Lightheartdevs and others added 2 commits September 25, 2026 18:33
…silently

On strixy the owner asked for "a cool looking webpage with a forest
theme ... Best you can do". The model wrote the whole page in one call,
the reply stopped at the 8192-token output limit after 220 s, the
unfinished write never ran, and the owner saw one sentence and no page.

Pixel now records the stop reason of each saved assistant reply
(before_message_write). Delivery reports a final reply cut at the output
limit plainly, since OpenClaw 2026.6.33 skips before_agent_finalize for
such a turn and would otherwise show only a generic retry message. When
the hook does run, one bounded revision pass asks for smaller steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses the review of the first commit: strip suppressStaleExecWarning
from any failed receipt (ingress 502; same fix for the lane-stop branch),
limit the report to owner-interactive workspace tasks (text answers keep
their partial reply; heartbeat/cron/team receipts unchanged), keep the
text within the ingress bound, and drop the finalize revise that
OpenClaw 2026.6.33 skips for this turn.

Tests in tests/output_limit_recovery.test.mjs still target the first
commit and must be rewritten (ingress-level, per the review probes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Lightheartdevs
Lightheartdevs marked this pull request as draft September 25, 2026 23:16
@Lightheartdevs

Copy link
Copy Markdown
Collaborator Author

Held (draft): an adversarial review found the first commit can produce a failed receipt carrying suppressStaleExecWarning (ingress 502), replaces long partial text answers, and changes heartbeat/cron/team receipts. The WIP commit reworks the logic accordingly; tests still need rewriting at ingress level before this can merge. Not merged with #6739/#6740.

🤖 Generated with Claude Code

Lightheartdevs added a commit that referenced this pull request Sep 26, 2026
…the request

Brings the substance of #6742's adversarial-review fixes into the
continuation change so the combined output-limit handling is correct on
its own. The continuation comes first; the report is delivered only when
no continuation ran or the continuation was cut too.

- A receipt forced to failed (the output-limit report, a stopped
  mixed-task lane) or combined into a failed or pending mixed-task
  receipt drops suppressStaleExecWarning. The ingress accepts that key
  only on none/passed receipts and answered such a turn with HTTP 502.
- The report covers owner chat turns only. Heartbeat, cron and
  team-worker receipts are unchanged, and a receipt still waiting on the
  host or the owner (pending) is not turned into a failure.
- It covers written answers as well as workspace tasks: OpenClaw
  2026.6.33 replaces a cut text-only reply with its generic "couldn't
  generate a response" too. A workspace task hears that what the reply
  was still writing was not saved; a written answer hears that it was cut
  off, never that a file write was. A continuation cut again says so.
- The report and the run's own receipt text stay within the ingress's
  32 KiB receipt bound (the report stands alone otherwise); the ingress's
  continuation-failure line does too, and no longer tells a written
  answer to check the workspace.
- The before_agent_finalize revise is removed: OpenClaw skips the hook for
  a cut turn, and its instruction fit only file work. Only the
  continuation prompt's bytes remain (unchanged).

tests/output_limit_recovery.test.mjs is rewritten at ingress level: the
real ingress over loopback TCP in front of a gateway double whose Pixel
routes are served by a real tool-loop guard (the former 502 cases now
200, non-owner and pending receipts unchanged, text vs workspace wording,
the size bound, cut-then-continued turns). The real-harness fixture adds
a written answer cut twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Lightheartdevs

Copy link
Copy Markdown
Collaborator Author

Superseded by #6743. Please do not merge this PR.

#6743 is now the single output-limit change. It contains this PR's original commit e86cd4e4b. Its new commit a285ba2a6 fixes the adversarial-review findings on that commit, and adds ingress-level tests for each fix:

  1. HTTP 502 on failed receipts. Any receipt that ends failed or pending drops suppressStaleExecWarning. This covers the output-limit report and the lane-stop branch, and the combined mixed-task receipt too. The former 502 cases now return 200.
  2. Heartbeat, cron and team-worker runs. The report covers owner chat turns only. Those runs keep their receipts and their delivery payloads.
  3. 32 KiB bound. The report and the run's receipt text stay within the ingress bound; otherwise the report stands alone. The ingress's continuation-failure line keeps the bound too.
  4. Finalize revise. Removed: OpenClaw 2026.6.33 skips before_agent_finalize for a cut turn, and its instruction fit only file work.
  5. Text answers. A cut text-only reply comes back as OpenClaw's generic "couldn't generate a response". So text answers are continued once, and when not continued they get their own report wording, which never mentions a file write.

The WIP rework 56f724cc5 limited the report to workspace tasks. That is deliberately not carried over, because of finding 5.

Evidence for a285ba2a6:

  • full Linux suite fail 0, on the head and on the head merged with main;
  • real-stack test (real plugin, real ingress, pinned OpenClaw 2026.6.33) 6/6, on the head and on the head merged with main.

Details are in the #6743 description.

🤖 Generated with Claude Code

@Lightheartdevs

Copy link
Copy Markdown
Collaborator Author

Closing as superseded by #6743 (see the comment above). Everything passes on #6743's head a285ba2a6: the full Linux suite (fail 0, also merged with main), the real-stack test on tower2 (6/6, also merged with main), and all GitHub CI checks, including the real-runtime output-limit step on Linux and macOS. The branch is left in place.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant