Skip to content

fix(core): add iteration limit wrap-up nudge and reasoning length err… - #1029

Open
Premvkmishra wants to merge 5 commits into
truefoundry:mainfrom
Premvkmishra:fix/950-builder-turn-artifact
Open

Premvkmishra wants to merge 5 commits into
truefoundry:mainfrom
Premvkmishra:fix/950-builder-turn-artifact

Conversation

@Premvkmishra

@Premvkmishra Premvkmishra commented Oct 10, 2026 •

Copy link
Copy Markdown

Summary

Turns that reach iteration_limit now get a chance to finish cleanly. A short wrap-up warning goes to the agent a few calls before the limit, so it can write its pending files and summarize what's left. Output-cap errors caused by reasoning now say so and point to the setting that fixes it.

Closes #950

Changes

  • Wrap-up warning: when min(3, iteration_limit - 1) LLM calls remain, each request carries a short note asking the agent to write pending outputs and summarize anything incomplete. It is sent with the request only, so it never enters thread history and works with compaction. It is skipped when iteration_limit is 1. The limit and its error message are unchanged.
  • Clearer length error: when a response stops at max_tokens and reasoning tokens make up at least half of the output, the error now says the budget was used by reasoning. It points to model.params.max_tokens and reasoning_effort. Without reasoning usage, the message stays max_tokens breached.
  • Docs:
    • The iteration-limit default now reads 512, matching the code.
    • Documented the wrap-up warning.
    • Described max_tokens and reasoning_effort as the per-agent output and reasoning controls.
    • Added a short-session compaction example: { "type": "input_tokens", "value": 40000 }.
  • Changeset: patch bump for @truefoundry/trueforge-core.

No schema, default or compaction behavior changes.

How was this tested?

New tests/orchestration/orchestrationIterationLimit.test.ts covers:

  • the warning on exactly the last three requests with a limit of 5, with accurate remaining counts
  • the warning staying out of stored thread context and out of follow-up turns
  • the warning surviving a compaction that rewrites context on the reserve step
  • no warning when the limit is 1 or when the turn finishes early
  • the length error with and without reasoning usage

The full trueforge-core suite passes on Node 22, alongside pnpm lint:ci, pnpm typecheck and pnpm format:check.

Checklist

  • I have read the contributing guidelines
  • pnpm build, pnpm test, pnpm typecheck, pnpm lint:ci, and pnpm format:check pass locally
  • Tests added/updated where it makes sense
  • No hand-edits to generated code (packages/trueforge-sdk, python/trueforge_sdk, .github/fern/openapi/openapi.json, docs/openapi.json)
  • Docs / .env.example updated if configuration or behavior changed

Note

Low Risk
Runtime behavior changes are scoped to end-of-turn warnings and clearer error text; iteration limit enforcement and defaults in code are unchanged aside from request-only messaging.

Overview
Adds an iteration-limit wrap-up nudge: on the last up-to-three LLM calls in a turn (min(3, iteration_limit - 1)), AgentThread injects an ephemeral internal system message asking the model to finish files and summarize before the harness halts. It is attached only to the outgoing request (not persisted in thread context), including when compaction rewrites context on those steps. The nudge is skipped when iteration_limit is 1 or the turn ends before the reserve window.

finish_reason: length handling now surfaces a richer error when reasoning tokens consume most of the output budget, pointing users at model.params.max_tokens and reasoning_effort; otherwise the message stays max_tokens breached.

Docs align the documented default iteration limit with 512, describe the wrap-up behavior, expand max_tokens / reasoning_effort guidance, and add a short-session compaction threshold example. New orchestration tests cover nudge timing, non-leakage, compaction interaction, and length errors with/without reasoning usage.

Reviewed by Cursor Bugbot for commit 8567f66. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot

changeset-bot Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8567f66

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@truefoundry/trueforge-core Patch
@truefoundry/trueforge Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cursor cursor 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.

Stale Bugbot comment from a previous run.

@mintlify

mintlify Bot commented Nov 7, 2026

Copy link
Copy Markdown

Preview approval required

This pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit 8567f66.

Approve preview

@qodex-ai qodex-ai 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.

Inline findings below. See the walkthrough comment for the summary.


if (finishError !== undefined) {
const errorContent = completion?.type === 'error' ? completion.error_message : finishError;
if (finishReason === 'length') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 Major Content-filter failures reported as success · bug

The length-only checks ignore content_filter errors returned by finishReasonErrorMessage. Root threads report filtered responses as successful turns. Child threads return filtered output as successful completions. Check finishError in both branches and preserve its content-filter message alongside the specialized length message.

@qodex-ai

qodex-ai Bot commented Nov 7, 2026

Copy link
Copy Markdown

Qodex review

Reviewed units u1. Adds a core patch changeset.

Effort: ●●●○○ (3/5) · 1 finding: 1 major

Scope: the whole pull request (no previous review: the whole change is reviewed).

Qodex review threads: 1 open, 0 resolved by Qodex.

1 inline comment attached to specific lines below.

Walkthrough

Notes

  • Code around the change is partial: 85 symbols cut at the packet byte cap.
What Qodex checked

Lenses (openqodex review with checked answers, all dimensions)

  • Bugs and correctness
  • Security (auth, injection, SSRF, secrets, headers)
  • Performance regressions
  • Maintainability
  • Style and consistency

Scanners

  • actionlint did not run
  • bandit did not run
  • brakeman did not run
  • cargo-deny did not run
  • checkov did not run
  • gitleaks 8.21.2
  • golangci did not run
  • hadolint did not run
  • kube-linter did not run
  • kubeconform did not run
  • osv-scanner 2.6.0
  • oxlint 1.86.0
  • rubocop did not run
  • ruff did not run
  • semgrep 1.94.0
  • shellcheck did not run
  • sqlfluff did not run
  • sqllint did not run
  • squawk did not run
  • tflint did not run
  • trivy did not run
  • zizmor did not run
  • 2 scanner candidates raised or dropped by the reviewer

Quality filters

  • PR description: complete (length, checklist, issue link)
  • Repo card last refreshed 8 days ago — may not reflect very recent code structure changes
  • Per-category confidence floor (bug/security/performance 0.7, maintainability 0.71, style 0.75)
  • Severity threshold from .qodex.yaml: minor
  • Path excludes (none configured in .qodex.yaml)
  • Disabled rules (none configured in .qodex.yaml)

Verification (M1b probes against PR preview)

  • Preview deployment discovered (no successful deploy for this commit)
  • Probes (skipped, no preview to probe)

Cross-review dedup

  • Full review: no previous review: the whole change is reviewed
  • Carry-overs (no findings from earlier reviews still present)

Output

  • Walkthrough comment posted
  • 1 inline comment on specific lines

Reviewer: shown 12 of 12 changed hunks, opened 6 files (2 changed files never opened), 18 tool calls served, 14 model calls, 0 correction rounds, 52.2s. The library does not report tool calls asked apart from those it served, or a step cap.
Reviewed by Qodex · 85.8s

Every dimension is reviewed. Bugs, security, and performance post inline; low-severity style and convention notes are collected in the group above rather than dropped. Mark a finding resolved or flag a false positive in the Qodex Pull Requests view.


if (finishError !== undefined) {
const errorContent = completion?.type === 'error' ? completion.error_message : finishError;
if (finishReason === 'length') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Medium: Preserve the content-filter stop before tool execution

Replacing finishError !== undefined with a length-only check lets content_filter fall through to stepToolResponse() when accumulated tool calls are present; VercelAILLM retains those calls on a filtered finish, although I couldn't confirm a live provider emits valid calls before that finish. Preserve both finishError guards, including the parent-completion branch at line 1295, and customize only the length-error wording so the provider's filtering signal remains a terminal stop.

@veria-ai

veria-ai Bot commented Nov 7, 2026

Copy link
Copy Markdown

PR overview

The PR adds an iteration-limit wrap-up nudge and adjusts reasoning-length error handling in the core agent runtime.

One open issue remains: the revised finish-error checks can allow accumulated tool calls to execute after a provider reports a content-filter stop. This weakens the filtering signal as a terminal boundary, though a live provider returning valid tool calls with that finish condition has not been confirmed. Preserving the general finish-error guards while customizing only length-error wording would address the concern.

Open issues (1)

Fixed/addressed: 0 · PR risk: 4/10

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8567f66. Configure here.

if (finishError !== undefined) {
const errorContent = completion?.type === 'error' ? completion.error_message : finishError;
if (finishReason === 'length') {
const errorContent = completion?.type === 'error' ? completion.error_message : baseLengthErrorMessage;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Content filter no longer fails turns

High Severity

The finish-reason guard now checks finishReason === 'length' instead of finishError !== undefined. A content_filter stop no longer errors the turn: finishReasonErrorMessage still returns the blocked-output copy, but that value is ignored. Root turns complete as done with partial content, and sub-agents send that content to the parent as success.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8567f66. Configure here.

This branch has not been deployed

No deployments
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.

Builder turns: reasoning model exhausts 32k output with no file; 40-iteration limit loses the artifact; per-turn context re-send cost

1 participant