Skip to content

🤖 fix: page foreground task reports and stop repeating unbounded fields on later pages - #6427

Merged
ThomasK33 merged 6 commits into
mainfrom
tk/fu2-report-paging
Oct 14, 2026
Merged

ThomasK33 merged 6 commits into
mainfrom
tk/fu2-report-paging

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Summary

This fixes four paging gaps that #6312 left, tracked in #6353. A foreground task now returns a long report as pages, the same way task_await does, and the task card joins the pages back into the whole report. Pages after the first no longer repeat a skill's frontmatter or a workflow's structuredOutput. Xum now counts any page of a long report as delivered, so the owner is not woken again with the same report.

Background

#6312 split task_await reports and skills_catalog_read skill bodies into 50,000-character pages, because the next turn's rebuild no longer cuts long tool results (#6268). Its review rounds found four gaps, and I deferred them to #6353:

  • A foreground task (run_in_background: false) still returned the whole report in one result.
  • Every skills_catalog_read page repeated the skill's frontmatter. Its license and metadata strings have no size limit.
  • Every task_await report page repeated the workflow's structuredOutput, which also has no size limit.
  • Rereading an earlier page made the task card show a shorter report.

Implementation

  • Foreground task. The result holds the first 50,000 characters, plus nextCharOffset and a note that names task_await(task_ids: ["<id>"], timeout_secs: 0, offset_chars: N). This covers a single task, best-of-n (one page and note per report) and a foreground workspace turn. One shared helper, pageTaskReportForResult, writes the note for both tools.
  • Frontmatter and structuredOutput. Only a read from offset 0 carries them. The requested offset decides, not the page text, so an empty report also drops them on later reads.
  • Task card. The join starts from the foreground result's first page and adds the task_await pages. When the result holds only a first page, the card shows the joined report.
    • A page that matches the text already joined at its offset is a reread, so the joined report stays.
    • Only a page that reaches the end of the report clears the gap flag, so a first page read after a later page keeps the later await row visible.
    • A best-of group that is still running keeps the first page of each candidate that finished.
    • A delivered task_send_message to a task ID starts a new join, because sub-agent reports carry no ID for the run that wrote them.
  • Delivery receipts (taskService.ts). Each receipt records its page offset. A completion notification counts as delivered when a non-empty page equals the full report at that offset. This also fixes a duplicate wake that has been on main since 🤖 fix: cap background-bash reads, task reports and catalog skill bodies #6312, for a task_await read of a long workspace-turn report. A code_execution projection that drops the paging marker does not count as delivered, so the owner still wakes with the full report.
  • Self-healing. The join and the card skip null and non-object reports entries. The join handlers are a Map, so a persisted tool name such as __proto__ finds no handler instead of throwing.

Validation

Each change has a test that fails with its fix reverted. The tests that check refusals pass on the old code too, so I proved them against deliberately looser versions of the check instead.

  • A foreground report over 50,000 characters (n=1 and n=2) returns one page, and task_await with the returned offset joins back to the exact report. A long workspace-turn report keeps its supersede note.
  • Catalog and workflow pages: page 0 carries the frontmatter or structuredOutput, and later pages do not. That includes an empty report read at a positive offset.
  • The rendered card keeps the whole report in each of these cases:
    • a reread of page 0 after page 2,
    • only the continuation pages read,
    • a best-of candidate,
    • a best-of group that is still running.
  • A woken task's new report starts a new join.
  • Notifications are consumed by a first page that is not read further and by a later page. They are not consumed by a page of another report, by the right text at another offset, or by a code_execution projection without its marker.
  • A transcript with null reports entries, or with __proto__ and constructor tool rows, still renders and joins.

Risks

Medium. The change touches tool-result shapes, the renderer's report join, and how completion notifications are consumed.

  • If a receipt match were wrong, the result would be one duplicate wake (no data loss), or one missed wake for a report that the model already saw.
  • Known limits, tracked in 🤖 Deferred review findings from the tool-cap and compaction-trim PRs (#6312, #6324) #6353:
    • After a re-send to the same task ID, an older foreground card can show the new attempt's joined report. Background cards already behave this way on main.
    • A child that the user wakes by typing in its workspace leaves no send in the parent's transcript, so the card does not start a new join for it.
    • A report read out of order keeps await rows that repeat text the card already shows.

Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high • Cost: $302.52

…ds on later pages

- Foreground `task` (single, best-of-n and workspace turns) returns the first
  50,000-character page of a long report, with nextCharOffset and a note naming
  the task_await call that reads the next page.
- `task_await` report pages after the first drop structuredOutput.
- `skills_catalog_read` pages after the first drop the frontmatter.
- Task card: rereading an earlier report page keeps the later pages joined,
  and a first page read after a later one keeps that page's await row visible.

Refs #6353.

---
_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-14T00:51:27.012186Z 9cb6c11 New commits
🔒 Security Review ✅ Completed 2026-10-14T00:56:38.590960Z 9cb6c11 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: a0973f6e2c

ℹ️ 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 thread src/node/services/tools/task.ts
Comment thread src/node/services/tools/task_await.ts Outdated
Comment thread src/browser/utils/messages/taskReportLinking.ts
… reports by offset

Codex round 1 on #6427:
- Task card: a foreground result that holds only the first page of a report
  seeds the report join, and the card prefers the joined report, so the
  task_await pages the agent read stay on the card.
- task_await: the requested offset, not text equality, decides the first page,
  so an empty report read at offset_chars > 0 no longer repeats structuredOutput.
- Report join: a delivered task_send_message to a task starts a new join for
  its next report. Agent reports carry no attempt ID, so a new attempt whose
  first page matches the old text no longer keeps the old attempt's suffix.

---
_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->

@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: f7fdd7b974

ℹ️ 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 thread src/browser/utils/messages/taskReportLinking.ts Outdated
…f group

Codex round 2 on #6427: a foreground best-of group where one candidate
finished with a long report while another was backgrounded, interrupted or
timed out returns a pending aggregate status. The report join dropped that
completed candidate's first page, so a later task_await continuation counted
as a gap and the card kept the first page. Read paged `reports` entries
whatever the aggregate status; a single-task result still needs its own
completed status.

---
_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->

@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: 3d25b8dfc9

ℹ️ 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 thread src/browser/utils/messages/taskReportLinking.ts Outdated
…ering

A partly corrupted chat.jsonl can hold a null or non-object entry in a
foreground task result's `reports`. readForegroundFirstPages read
`nextCharOffset` before any check, and the task card read `report.taskId`,
so rendering the transcript threw. Both now skip such entries, so the
transcript still renders and the valid entries still join (self-healing).

---
_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->

@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: b9b7b5ac94

ℹ️ 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 thread src/node/services/tools/task.ts
…d receipt

Codex round 4 on #6427: a foreground workspace turn with a report over one page
returns only its first page, and a task_await read returns one page too. The
terminal-notification drain required the receipt text to equal the whole
recorded report, so the owner could wake again with a duplicate result.

A continuation receipt now keeps its page offset (startCharOffset, or 0 for a
first page with nextCharOffset). It matches when it is the whole report, or a
non-empty page that equals the recorded report at that offset. A page of a
different report, or at another offset, still keeps the notification.

---
_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->

@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: a7a05bcb6a

ℹ️ 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 thread src/browser/utils/messages/taskReportLinking.ts Outdated
Comment thread src/node/services/tools/task.ts
…rkers

Codex round 5 on #6427:
- taskReportLinking: the per-tool handlers are a Map, so a persisted toolName
  such as "__proto__" or "constructor" misses instead of resolving to an
  Object.prototype member and throwing while the transcript renders.
- taskService: initial and continuation receipts record their page offset, and
  a code_execution projection must match the genuine row's text and page
  offset. A projection that drops nextCharOffset hides the rest of a paged
  report, so it no longer consumes the terminal notification.

---
_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high -->
@ThomasK33
ThomasK33 added this pull request to the merge queue Oct 14, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 14, 2026
@ThomasK33
ThomasK33 added this pull request to the merge queue Oct 14, 2026
Merged via the queue into main with commit 9f975f3 Oct 14, 2026
32 of 33 checks passed
@ThomasK33
ThomasK33 deleted the tk/fu2-report-paging branch October 14, 2026 01:47
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