Skip to content

fix(core): remove fixed Goal continuation limit - #8421

Merged
wenshao merged 3 commits into
QwenLM:mainfrom
qqqys:agent/goal-v3-no-fixed-turn-limit
Aug 6, 2026
Merged

fix(core): remove fixed Goal continuation limit#8421
wenshao merged 3 commits into
QwenLM:mainfrom
qqqys:agent/goal-v3-no-fixed-turn-limit

Conversation

@qqqys

@qqqys qqqys commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

This PR removes Goal v3's Core-owned fixed 50-continuation cutoff. An active Goal can keep receiving runtime-issued permits until it reaches an actual lifecycle outcome, the user pauses or clears it, or its host applies an explicit policy.

The observable turn count now remains cumulative when a paused, blocked, or usage-limited Goal is resumed. Persisted state created at the former boundary can therefore resume at turn 50, advance to turn 51, and continue normally instead of silently resetting its history.

Verifier, evidence, and provider failures still use the existing usage_limited lifecycle state. This PR does not add a token budget or replace the removed cutoff with another implicit turn or time limit.

Why it's needed

The fixed cutoff contradicted the agreed Goal behavior: Goal execution should be lifecycle-driven rather than ending after an arbitrary number of model turns. It also made a recoverable session appear usage-limited even when no provider or resource limit had occurred, and resume hid the prior work by resetting the displayed turn count.

Reviewer Test Plan

How to verify

Create an active Goal with a deterministic host and finish more than 50 sequential permits. Confirm every finish schedules a distinct next permit, the Goal remains active/running after 75 turns, the observable count is 75, and no turn-count-based usage-limited record is written.

Restore a persisted usage-limited Goal at turn 50, resume it, and finish the next permit. Confirm resume preserves turn 50, the finish advances it to 51, and another continuation is admitted.

Run the client integration with 75 runtime-scheduled Goal turns and confirm these continuations still do not consume the generic session-turn counter. Confirm the existing verifier/evidence/provider failure tests continue to produce usage-limited outcomes.

Evidence (Before & After)

Before: the 51st automatic continuation was not admitted, the Goal transitioned to usage-limited solely because of its turn count, and resume reset the count to zero.

After: 75 sequential runtime continuations remain active, a persisted former-limit state resumes and advances from 50 to 51, and only real verifier/evidence/provider failures produce usage-limited state.

No screenshot is applicable because this PR changes a runtime admission boundary and does not alter TUI, Web Shell, or desktop rendering.

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

Environment (optional)

Node.js 24.14.1, local dependencies installed from the repository lockfile. Focused Core validation: 4 files and 135 tests passed. Full repository typecheck, modified-file ESLint and Prettier checks, and full repository build passed.

Risk & Scope

  • Main risk or tradeoff: Core no longer supplies a default autonomous turn or time cutoff. Users retain pause/clear controls, and hosts can still enforce explicit wall-clock, tool-call, or session policies.
  • Not validated / out of scope: token budgets, verifier/provider reliability changes, ACP conversion, and UI styling.
  • Breaking changes / migration notes: the unreleased exported fixed-limit constant is removed. Persisted Goal snapshots remain wire-compatible; their turn count is now preserved across resume.

Linked Issues

Follow-up to #8324 and #8005.

中文说明

本 PR 做了什么

本 PR 移除 Goal v3 在 Core 内部固定的 50 续轮上限。活跃 Goal 会继续获得 runtime 签发的 permit,直到进入真实生命周期终态、用户暂停或清除,或者 host 应用显式策略。

暂停、阻塞或 usage-limited Goal 恢复后,可观测的轮数会继续累计。旧边界上持久化的状态因此可以从第 50 轮恢复,推进到第 51 轮并继续执行,而不会把历史轮数静默清零。

verifier、evidence 和 provider 故障仍然使用现有 usage_limited 生命周期状态。本 PR 不添加 token 预算,也不会用另一个隐式轮数或时间限制替代被移除的上限。

为什么需要

固定上限与已确认的 Goal 行为不一致:Goal 应由生命周期驱动,不应因为任意模型轮数而结束。旧行为还会在没有发生 provider 或资源限制时把可恢复会话标记成 usage-limited,并在 resume 时通过清零展示轮数隐藏此前完成的工作。

Reviewer 测试计划

如何验证

使用确定性 host 创建活跃 Goal,并连续完成超过 50 个 permit。确认每次完成都会签发不同的下一 permit,第 75 轮后 Goal 仍为 active/running,可观测轮数为 75,且不会写入由轮数触发的 usage-limited 记录。

恢复一个停在第 50 轮的持久化 usage-limited Goal,执行 resume 并完成下一 permit。确认 resume 保留第 50 轮,完成后推进到第 51 轮,并继续签发下一续轮。

在 client integration 中运行 75 个 runtime 自动 Goal 轮次,确认这些续轮仍不消耗通用 session-turn 计数。同时确认既有 verifier/evidence/provider 故障测试仍然产生 usage-limited 状态。

前后证据

修改前:第 51 个自动续轮不会被准入,Goal 仅因为轮数进入 usage-limited,resume 会把轮数清零。

修改后:75 个连续 runtime 续轮仍保持 active,旧上限上的持久化状态能够从 50 推进到 51,只有真实 verifier/evidence/provider 故障才产生 usage-limited 状态。

本 PR 只修改 runtime 准入边界,不改变 TUI、Web Shell 或桌面端渲染,因此不适用截图证据。

测试平台

OS 状态
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

环境

Node.js 24.14.1,依赖按仓库 lockfile 安装。聚焦 Core 验证共 4 个文件、135 个测试通过。全仓 typecheck、改动文件 ESLint 和 Prettier 检查、全仓 build 均通过。

风险与范围

  • 主要风险或取舍:Core 不再提供默认的自动轮数或时间截止。用户仍可暂停/清除,host 仍可应用显式 wall-clock、tool-call 或 session 策略。
  • 未验证或不在范围内:token 预算、verifier/provider 稳定性修改、ACP 迁移和 UI 样式。
  • 破坏性变更或迁移说明:移除尚未随 release 发布的固定上限导出常量。持久化 Goal snapshot 的 wire 格式保持兼容,resume 后轮数改为继续累计。

关联 Issue

#8324#8005 的后续 PR。

@qqqys

qqqys commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

Goal v3 no fixed continuation limit verification

Date: 2026-08-03
Branch: agent/goal-v3-no-fixed-turn-limit
Base: upstream/main at 9342788720

Scope

  • Remove the Core-owned fixed 50-continuation limit.
  • Preserve cumulative turnCount across pause/resume and legacy usage_limited recovery.
  • Keep verifier/evidence/provider usage_limited outcomes unchanged.
  • Keep host permits, user preemption, explicit client budgets, and UI behavior unchanged.

Red evidence

The updated boundary tests failed against the old implementation:

  • the 51st automatic continuation had no permit;
  • finishing the stale 50th permit raised Goal turn permit is no longer valid;
  • resuming a persisted usage_limited Goal changed turnCount from 50 to 0.

Green evidence

Focused Core command:

cd packages/core
npx vitest run src/goals/goal-runtime.test.ts src/goals/goal-runtime.integration.test.ts src/core/client-goal.test.ts src/goals/goal-reducer.test.ts

Result: 4 files passed, 135 tests passed.

Observed deterministic interaction chain:

  1. Create an active Goal and bind a host.
  2. Finish 75 sequential Core-issued permits.
  3. Each finish schedules a distinct next permit.
  4. The Goal remains active/running with turnCount: 75.
  5. No usage_limited lifecycle record is written because of turn count.
  6. Client integration completes the same 75 turns without consuming sessionTurnCount.

Persisted-state compatibility chain:

  1. Restore an idle usage_limited Goal recorded at turnCount: 50.
  2. Resume it with the same goal identity and revision.
  3. The runtime admits a new permit with turnCount: 50 intact.
  4. Finishing that permit advances the observable count to 51 and schedules the next turn.

Regression checks

  • Full repository typecheck: passed.
  • Modified-file ESLint: passed.
  • Full repository build: passed.
  • Existing verification-failure tests still assert usage_limited for evidence flush/read/cursor errors and provider failure.
  • Existing pause, clear, edit, replacement, stale-permit, user-priority, and verifier lifecycle tests remain green.

Intentional product tradeoff

Core no longer supplies a default fixed turn or time cutoff. Users can still pause or clear an active Goal, and hosts such as non-interactive CLI can apply explicit wall-clock/tool/session policies. This report does not add a token budget or replace the removed fixed limit with another implicit cap.

UI evidence

No screenshot is applicable: this PR changes the runtime admission boundary only and does not change TUI, Web Shell, or desktop rendering. Their displayed turn count now remains cumulative after resume.

@qqqys
qqqys marked this pull request as ready for review August 3, 2026 04:00
@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR! Re-running the gate on the current head.

Template looks good ✓ — all sections present, bilingual body included.

Problem: Real and observable in released code, not hypothetical. On current main (and since v0.21.3), the Goal runtime's continuation queue cuts a Goal off at exactly 50 turns and writes a turn-count-driven usage_limited record even though no provider or resource limit occurred, and the reducer zeroes turnCount on resume, hiding prior work. Both behaviors are right there in the code, and this is a follow-up to the merged Goal v3 adoption work (#8005, #8324).

Direction: Aligned. Goal turns were deliberately excluded from the session and recursive turn budgets (#7895), and recent fixes reserve usage_limited for real exhaustion (evidence catalog in #8430; verifier/evidence/provider failures). A turn-count cutoff that fabricates a "usage limited" state contradicts that lifecycle-driven design.

Size: Core paths touched (packages/core/src/goals/**): 38 production lines (pure deletions — runtime −34, reducer −4) and 148 test lines. Well below every threshold; no maintainer escalation on size.

Approach: Minimal — this is as small as the change can get: delete the cutoff branch and constant, drop the resume reset that existed only to serve the cutoff, and flip the tests to pin the new behavior. No drive-by changes, nothing to cut. One factual nit on the description: the removed constant is not unreleased — it shipped in v0.21.3 and is exported from the core package's entry point, so the breaking-change note should say "released", not "unreleased". Non-blocking; details in the review comment.

Risk: No high-risk path matches. The tradeoff this PR makes — no Core-owned default turn/time cutoff; lifecycle outcomes, user pause/clear, and host wall-clock/tool-call budgets are the guardrails instead — is disclosed in Risk & Scope and matches the merged design lineage.

Moving on to code review. 🔍

中文说明

感谢贡献!这是对当前 head 的重新 gate。

模板完整 ✓ —— 各节齐全,包含中文说明。

问题: 真实且可在已发布代码中观测,不是假设。在当前 main(自 v0.21.3 起)上,Goal runtime 的续轮队列会在恰好 50 轮时切断 Goal,并在没有发生任何 provider 或资源限制时写入由轮数触发的 usage_limited 记录;reducer 还会在 resume 时把 turnCount 清零,隐藏此前的工作。这两个行为都直接存在于代码中,且本 PR 是已合并的 Goal v3 接入工作(#8005#8324)的后续。

方向: 对齐。Goal 轮次本就有意不计入 session 与递归轮数预算(#7895),近期修复也把 usage_limited 保留给真实耗尽(证据目录 #8430;verifier/evidence/provider 故障)。一个凭空制造 "usage limited" 状态的轮数上限与生命周期驱动的设计相矛盾。

规模: 触及核心路径(packages/core/src/goals/**):38 行生产代码(纯删除:runtime −34、reducer −4),148 行测试。远低于所有阈值,无需因规模升级维护者关注。

方案: 最小化 —— 改动已经小到不能再小:删除上限分支与常量、移除仅为该上限服务的 resume 清零、翻转测试以钉住新行为。没有顺手改动,无可砍内容。描述中有一处事实性小问题:被移除的常量并非"未发布" —— 它自 v0.21.3 起已随 release 发布,并从 core 包入口导出,破坏性变更说明应写"已发布"而非"未发布"。不阻塞,详见代码审查评论。

风险: 未命中高风险路径。本 PR 的取舍 —— Core 不再提供默认轮数/时间截止,改由生命周期终态、用户暂停/清除、host 的 wall-clock/工具调用预算兜底 —— 已在 Risk & Scope 中披露,且与已合并的设计脉络一致。

进入代码审查 🔍

Qwen Code · qwen3.8-max

Reviewed at 58f0857034cbf185fc25288437e3f01c04f5e23c · re-run with @qwen-code /triage

@qqqys

qqqys commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover

@qwen-code-dev-bot qwen-code-dev-bot added the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Aug 5, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. This is a fork PR, so the first round comes from the next scheduled scan (usually within minutes). Remove the autofix/takeover label (or comment @qwen-code /takeover stop) to release.

中文说明

🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。本 PR 来自 fork,首轮处理将由下一次定时扫描执行(通常几分钟内)。移除 autofix/takeover 标签(或评论 @qwen-code /takeover stop)即可释放。

@qqqys

qqqys commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run

Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 55 passed · 0 failed · 55 total

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:55 通过 · 0 失败 · 55 总计

Verification report

PR 8421 — fix(core): remove fixed Goal continuation limit

Verdict: merge-ready — 55/55 scripted assertions passed (0 unexpected failures), verified head 58f0857034cbf185fc25288437e3f01c04f5e23c (base tip 32e27415779226b23174a3b0aa6c04e094f1aca2). Single-commit PR; the one locally reachable commit matches the metadata snapshot's commits array.

中文摘要
  • 结论: merge-ready。55/55 条脚本化断言通过,0 条意外失败。
  • A/B 结论(head 构建 vs base 构建,驱动编译后的 goal runtime 真实代码):75 轮 soak 中 base 在第 50 轮封顶(usage_limited,journal 写入 "50-turn continuation budget" 记录),head 发满 76 个 permit、状态 active、turnCount 75、零 usage_limited 记录;恢复停在第 50 轮的持久化 usage_limited 状态后,base 把轮数清零(0→1),head 保留累计(50→51)并继续签发续轮;evidence-source 故障仍会在两个构建上产生 usage_limited(行为不变)。见 01-ab-head-vs-base.png
  • Findings: 无阻塞发现。仅一条信息性观察:被移除的 50 轮上限曾是 Core 内唯一的自主续轮上限(Goal 轮不消耗 session-turn 计数,系既有且已文档化行为);PR 明确接受该取舍,且 headless 的 --max-wall-time/--max-tool-calls host 策略在代码中确实存在并被文档声明会暂停 Goal 工作。
  • 未覆盖: 全仓测试(CI 覆盖;本方跑了 goals + client-goal 共 313 个测试)、TUI/ACP 对累计轮数的展示(读取点仅用于展示)、被移除常量是否曾随 release 发布(无网络,仓内无消费者)。

Central claim + A/B load-bearing proof

Central claim: with the fixed 50-continuation cutoff removed, an active Goal keeps receiving runtime-issued permits past turn 50 without transitioning to usage_limited, and resuming a persisted limited Goal preserves the cumulative turn count (50 → 51) instead of resetting it.

Harness (harness-ab.mjs, in this artifact dir): drives the compiled dist goal runtime through its injected seams only — an in-memory GoalJournal (the persistence wire), an in-memory GoalTurnHost, and for S3 an injected evidence source/verifier. No mocks of the unit under test. Base control: scratch worktree at HEAD^1 with only packages/core rebuilt there; the PR leaves package.json/lockfile untouched so the shared root node_modules is a clean control; readlink -f confirmed the base entry module resolves inside the base tree, and the base dist was confirmed to still contain the budget block (MAX_GOAL_CONTINUATION_TURNS ×3, reducer turnCount: 0 reset) while the head dist contains neither. Raw log: logs/ab-run.log. Capture: 01-ab-head-vs-base.png.

Scenario (both arms identical inputs) oracle base 32e2741 head 58f0857
S1: create + finish 75 sequential permits permits started / final status / turnCount / usage_limited journal records 50 / usage_limited / 50 / 1 (reason: "…50-turn continuation budget") 76 / active / 75 / 0
S2: restore persisted usage_limited @​ turn 50 (old budget lastReason), resume, finish one turnCount after resume / after finish / permits 0 / 1 / 2 50 / 51 / 2
S3: terminal proposal + evidence-source outage during verification final status / journal records / verifier calls usage_limited / 1 / 0 usage_limited / 1 / 0 (identical)

All 50 harness assertions passed, including the control expectations (base capping at 50 is the asserted red). S2's restore record carries the old budget lastReason string, which also shows persisted former-limit state stays wire-compatible and resumable at head. S3 proves the surviving usage_limited producer (verifier/evidence/provider failure path) is untouched by the PR — identical on both arms.

Reviewer Test Plan walkthrough

Plan step result
1. Deterministic host, >50 sequential permits; every finish schedules a distinct next permit; active at 75; count 75; no turn-count usage_limited record performed — S1 head cell: 76 distinct turnIds, active, turnCount 75, 0 usage_limited records
2. Restore persisted usage-limited Goal at turn 50, resume, finish; count preserved 50 → 51; another continuation admitted performed — S2 head cell: resume reports turnCount 50, finish → 51, second permit started
3. Client integration with 75 runtime-scheduled Goal turns does not consume the session-turn counter; verifier/evidence/provider failure tests still produce usage_limited performed — gate ran src/goals + src/core/client-goal.test.ts: 14 files / 313 tests green, including the 75-turn integration and client admission tests; S3 exercises the failure producer live on both arms

Vacuity / mutation matrix (02-mutation-matrix.png, logs/mutation-matrix.log)

Ran in a scratch worktree at HEAD against the four changed/related test files:

mutation result
control (unmutated head) 4/4 files green — suite is live
M1: goal-runtime.ts reverted to base (budget block restored) 3 files red: the new 75-turn soak tests in goal-runtime.test.ts, goal-runtime.integration.test.ts, client-goal.test.ts, plus the resume-at-50 test. Failure is the behavioral mismatch, e.g. Error: Goal turn permit is no longer valid thrown at the 51st finishTurn because the restored guard limited the goal at 50
M2: goal-reducer.ts reverted to base (resume turnCount: 0 restored) 2 files red: goal-reducer.test.ts > preserves the cumulative turn count… and goal-runtime.test.ts > resumes persisted state at the former limit…
M3: positive control (turnCount + 2 in reduceGoalTurnFinished) 3 files red, 10 tests — the harness can fail the suite

Both central new tests are non-vacuous: reverting exactly the production hunk they were written for makes them fail with expected-vs-actual mismatches. The PR also deletes does not usage-limit a replacement goal created during budget-exhaustion persistence; that test pinned a race inside the now-removed budget path, so its deletion removes coverage of code that no longer exists (nothing to pin).

Findings

No blocking findings.

  • Informational (accepted tradeoff, verified, not a defect): the removed cutoff was the only core-side cap on sequential autonomous Goal continuations — Goal turns bypass the session-turn counter (client.ts, !isGoalRuntimeTurn guard, pre-existing and documented in docs/users/features/headless.md). The host policies the PR's tradeoff note names do exist: --max-wall-time / --max-tool-calls are wired in config.ts/settingsSchema.ts/nonInteractiveCli.ts and documented to pause active Goal work; interactive surfaces retain pause/clear. An active Goal with no host policy can therefore continue indefinitely — that is the lifecycle-driven design this PR implements, stated in its description.

Not covered

  • Repo-wide test suite beyond src/goals + src/core/client-goal.test.ts (313 tests, all green); the remainder is covered by the PR's CI.
  • TUI/ACP rendering of the now-cumulative turn count (GoalStatusMessage, goal-legacy-projection, nonInteractiveCli read turnCount display-only; not exercised end-to-end).
  • Whether the removed MAX_GOAL_CONTINUATION_TURNS export ever shipped in a published release (no network in this environment); no in-repo consumer remains and the full build compiles at head.
  • Real provider/network outages — S3 simulates them with the library's own EvidenceSourceUnavailableError; the verifier-reject path is exercised by the gate's existing tests.
  • Base worktree full typecheck fails on unrelated files (ajv/ignore typings) because a fresh worktree lacks the package-local node_modules present in the installed main tree — environmental; the emitted base goal modules were validated behaviorally (base arm reproduces the cap exactly).

Methodology

Environment: CI merge-ref checkout (depth 2; HEAD merge, HEAD^1 base, HEAD^2 PR head), npm ci + full build pre-run at head. The A/B harness imports the compiled dist goal modules of each tree directly (head: main tree dist; base: core-only rebuild in a scratch worktree) and drives them through injected journal/host seams; every cell value is a scripted comparison in harness-ab.mjs (50 assertions). assert-evidence.mjs re-asserts the gate and mutation-matrix outcomes from the captured logs (5 assertions). Raw logs in logs/, harnesses and the matrix driver in this dir, captures in evidence/. Scratch worktrees removed after capture.

Evidence images

01-ab-head-vs-base

02-mutation-matrix

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code review

I formed my baseline before reading the diff: remove the cutoff branch and constant from the Goal runtime, drop the turnCount: 0 reset on resume (it existed only to keep a resumed Goal under the cutoff), and flip the four affected test files. The PR does exactly that — pure deletions, nothing more.

The consumer audit is the part that matters for a deletion:

  • MAX_GOAL_CONTINUATION_TURNS is referenced only by the removed branch and the three test files this PR updates. Nothing else imports it.
  • No other consumer branches on turnCount as a cap — the remaining readers are display/projection (iterations in the headless output, the client's legacy projection, goal-legacy-projection.ts), telemetry, the increment in the turn-finished reducer, and snapshot parsing. A cumulative count is strictly more accurate for all of them.
  • The deleted race test ("replacement goal created during budget-exhaustion persistence") only exercised the async exhaustion path that is itself deleted — removing it is correct, not a coverage loss.
  • Guardrails that remain: the per-Goal-turn MAX_TURNS tool-loop bound; headless wall-clock/tool-call budgets that explicitly apply to Goal turns (RunBudgetEnforcer, issue Headless / non-interactive mode lacks runaway protection guardrails #4103); user pause/clear; evidence-catalog exhaustion → usage_limited (fix(core): stop Goal retries after evidence catalog exhaustion #8430); verifier/evidence/provider failures → usage_limited (unchanged); and the blocked-audit cap on repeated blocked proposals.

No critical issues, no convention violations. One non-blocking observation:

  • Released, not unreleased. The PR body calls the removed constant "unreleased", but it shipped in v0.21.3 and is present through v0.21.5 (verified against the release tags), and it sits in the package's export surface (src/index.tsgoals/index.tsexport * from './goal-runtime.js'). Removing it is a small API-surface break for anyone importing the core package directly. Worth correcting the "Breaking changes" note before merge so the release notes are accurate; not blocking under 0.x semver.

Testing evidence (from the PR's own CI — unattended run, no PR code executed here)

Head 58f0857034cbf185fc25288437e3f01c04f5e23c: both pull_request workflow runs are completed / success, with no red checks. The full unit suite ran green on ubuntu-latest; the macOS/Windows matrix jobs and the CLI integration job were skipped for this run. The change is platform-independent runtime admission logic, so the Linux suite covers the behavior under test.

Check Conclusion
Qwen Code CI (workflow run) ✅ success
Qwen Autofix (workflow run) ✅ success
Test (ubuntu-latest, Node 22.x) ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success
Test (macos-latest, Node 22.x) ⏭️ skipped
Test (windows-latest, Node 22.x) ⏭️ skipped
Integration Tests (CLI, No Sandbox) ⏭️ skipped
precheck-pr / precheck ✅ success (one duplicate run cancelled by supersede)

The behavioral claim is pinned by the suite itself: the new tests are the exact mirror of the removed ones — 75 sequential continuations stay active with zero turn-count usage_limited records, and a persisted usage_limited Goal at turn 50 resumes at 50 and advances to 51. The old implementation cannot pass these tests (it refused the 51st admission and zeroed the count on resume), so a green run is real substantiation rather than a suite that passes identically with or without the diff. The author's verification comment reports the same red/green inversion; that is the author's claim — the committed test pair plus this CI run is the independently checkable part.

Real-scenario (tmux) testing: N/A — this changes a runtime admission boundary with no TUI/Web Shell rendering change, and unattended runs don't drive live sessions; driving 50+ real model turns in a TUI wouldn't be a practical oracle anyway.

中文说明

代码审查

先独立给出方案再看 diff:从 Goal runtime 删除上限分支与常量、移除 resume 时仅为规避该上限而存在的 turnCount: 0 清零、翻转受影响的四个测试文件。PR 正是这么做的 —— 纯删除,没有任何多余内容。

删除改动最需要的是消费者审计:

  • MAX_GOAL_CONTINUATION_TURNS 仅被删除的分支和本 PR 更新的三个测试文件引用,没有其他导入者。
  • 不存在其他把 turnCount 当作上限来分支的消费方 —— 其余读取方都是展示/投影(headless 输出的 iterations、client 的 legacy 投影、goal-legacy-projection.ts)、遥测、turn-finished reducer 的自增以及快照解析。累计计数对它们都更准确。
  • 被删除的竞态测试("budget-exhaustion persistence 期间创建替换 goal")只覆盖同样被删除的异步耗尽路径 —— 删除它是正确的,不是覆盖损失。
  • 保留的兜底:每个 Goal 轮次内部的 MAX_TURNS 工具循环上限;明确作用于 Goal 轮次的 headless wall-clock/工具调用预算(RunBudgetEnforcer,issue Headless / non-interactive mode lacks runaway protection guardrails #4103);用户暂停/清除;证据目录耗尽 → usage_limitedfix(core): stop Goal retries after evidence catalog exhaustion #8430);verifier/evidence/provider 故障 → usage_limited(未改动);以及重复 blocked 提案的 blocked-audit 上限。

无阻塞问题,无规范违规。一条非阻塞观察:

  • 已发布,而非未发布。 PR 描述称被移除常量"未发布",但它自 v0.21.3 起已随 release 发布并存在于 v0.21.5(已按 release tag 核实),且位于包的导出面(src/index.tsgoals/index.tsexport * from './goal-runtime.js')。移除它对直接导入 core 包的使用者构成小幅 API 面破坏。建议合并前更正 "Breaking changes" 说明以保证 release notes 准确;0.x 语义化版本下不阻塞。

测试证据(来自 PR 自身 CI —— 非值守运行,未在此执行任何 PR 代码)

Head 58f0857034cbf185fc25288437e3f01c04f5e23c:两个 pull_request 工作流运行均为 completed / success,无红色检查。完整单测套件在 ubuntu-latest 上通过;macOS/Windows 矩阵任务与 CLI 集成任务本次被跳过。本改动是平台无关的 runtime 准入逻辑,Linux 套件已覆盖被测行为(CI 明细见上方表格)。

行为声明由套件本身钉住:新测试与被删除的旧测试互为镜像 —— 75 个连续续轮保持 active 且不产生任何轮数触发的 usage_limited 记录;停在第 50 轮的持久化 usage_limited Goal resume 后保持 50 并推进到 51。旧实现无法通过这些测试(第 51 次续轮被拒绝、resume 清零),因此绿色运行是真实佐证,而不是"有无 diff 都一样过"的套件。作者的验证评论报告了同样的红/绿翻转;那是作者的声明 —— 可独立核验的部分是提交的测试对与本次 CI 运行。

真实场景(tmux)测试:N/A —— 本 PR 只改 runtime 准入边界,不涉及 TUI/Web Shell 渲染;非值守运行不驱动真实会话,且在 TUI 中驱动 50+ 个真实模型轮次也不是可行的判定方式。

Qwen Code · qwen3.8-max

Reviewed at 58f0857034cbf185fc25288437e3f01c04f5e23c · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean, minimal deletion substantiated by mirrored tests and green CI; two non-blocking nits (the body's "unreleased" claim is wrong — the constant shipped in v0.21.3; the disclosed no-default-cutoff tradeoff deserves an accurate release note).

Stepping back: my independent proposal for this problem was exactly what the PR does — delete the cutoff branch and the constant, remove the resume reset that only existed to serve the cutoff, flip the tests. There is no smaller version of this change, and no simpler path I can name. I tried to find a leftover consumer of the removed behavior and couldn't: nothing else imports the constant, nothing else branches on turnCount as a cap, and the one deleted test covered the equally-deleted exhaustion race path.

The honest question behind removing an autonomy cutoff is what stops a non-converging Goal now. The answer is concrete and verified in code, not assumed: per-turn MAX_TURNS, headless wall-clock/tool-call budgets that explicitly cover Goal turns, evidence-catalog exhaustion and provider/verifier failures still producing usage_limited, the blocked-audit cap on repeated blocked proposals, and user pause/clear. In the TUI a Goal can now run until a lifecycle outcome or user action — that is the disclosed tradeoff, and it matches the direction maintainers already merged (#7895 put continuations outside session budgets; #8430 reserves usage_limited for real exhaustion).

Approving: every deletion is necessary, the tests state the intended behavior directly, and CI is green on the reviewed commit. If the breaking-change note gets corrected from "unreleased" to "released since v0.21.3" before merge, the release notes will be accurate too.

中文说明

置信度:4/5 —— 干净的最小化删除,由互为镜像的测试与绿色 CI 佐证;两条非阻塞小问题(描述中"未发布"的说法不成立 —— 该常量自 v0.21.3 起已发布;已披露的"无默认上限"取舍需要一条准确的 release note)。

退一步看:我对此问题的独立方案与 PR 完全一致 —— 删除上限分支与常量、移除仅为该上限服务的 resume 清零、翻转测试。这个改动没有更小的版本,我也想不出更简的路径。我试图寻找被移除行为的残留消费者,没有找到:没有其他代码导入该常量,没有其他代码把 turnCount 当上限分支,唯一被删除的测试覆盖的也是同样被删除的耗尽竞态路径。

移除自主上限背后真正的问题是:现在什么能阻止一个不收敛的 Goal?答案是具体且经过代码验证的,不是假设:每轮内部的 MAX_TURNS、明确覆盖 Goal 轮次的 headless wall-clock/工具调用预算、证据目录耗尽与 provider/verifier 故障仍产生 usage_limited、重复 blocked 提案的 blocked-audit 上限、以及用户暂停/清除。在 TUI 中 Goal 现在可以一直运行到生命周期终态或用户操作 —— 这是已披露的取舍,且与维护者已合并的方向一致(#7895 将续轮置于 session 预算之外;#8430usage_limited 保留给真实耗尽)。

予以批准:每一处删除都是必要的,测试直接陈述了预期行为,CI 在被审查的 commit 上是绿的。如果合并前把破坏性变更说明从"未发布"更正为"自 v0.21.3 起已发布",release notes 也会是准确的。

Qwen Code · qwen3.8-max

Reviewed at 58f0857034cbf185fc25288437e3f01c04f5e23c · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, looks ready to ship. ✅

@qwen-code-dev-bot

qwen-code-dev-bot commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

AutoFix round 1 finishedview run. See this round's report below.

中文说明

AutoFix 第 1 轮已完成 —— 查看运行。本轮报告见下方。

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下:

Autofix review round: no action taken

No feedback in this round requires a code change, so no commits were made. The PR head remains at 58f085703.

What was reviewed

  • Reviews: none.
  • Inline comments: none.
  • Issue-level comments: two informational reports, neither containing a request:
    1. The author's verification report documenting the scope, red/green evidence, and regression checks for removing the fixed Goal continuation limit.
    2. The sandboxed verification bot's verdict: ✅ passed — merge-ready, 55/55 scripted assertions, no blocking findings. Its single informational observation (the removed cutoff was the only core-side cap on autonomous Goal continuations) is the intentional product tradeoff already stated in the PR description, not a defect.

Failed-check triage

  • precheck-pr / precheck: CANCELLED — this is the Qwen PR Safety Precheck (.github/workflows/qwen-pr-safety-precheck.yml), a triage gate that assesses PR author permission and title/body/diff content. It runs no build, lint, typecheck, or tests, so it cannot indicate a code defect. CANCELLED is a workflow/runner state (e.g. the calling workflow run was superseded), not a failure with diagnosable output. Nothing in this checkout can change it; it will be re-evaluated by the workflow on the next event. No other checks are red.

Conclusion

Nothing to fix, decline, or escalate. No source files were modified.

中文说明

Autofix 评审轮次:未做任何改动

本轮没有任何需要代码改动的反馈,因此没有产生任何提交。PR head 仍为 58f085703

评审内容

  • Review: 无。
  • 行内评论: 无。
  • Issue 级评论: 两条信息性报告,均不包含任何改动请求:
    1. 作者本人的验证报告,记录了移除 Goal 固定续轮上限的范围、红/绿证据和回归检查结果。
    2. 沙箱验证机器人的结论:✅ 通过 — 可合入,脚本化断言 55/55 通过,无阻塞性发现。其唯一的信息性观察(被移除的上限曾是 Core 侧对自主 Goal 续轮的唯一限制)即 PR 描述中已明确声明的有意产品取舍,并非缺陷。

失败检查的分类处理

  • precheck-pr / precheck: CANCELLED — 该检查是 Qwen PR 安全预检(.github/workflows/qwen-pr-safety-precheck.yml),属于分流门禁,仅评估 PR 作者权限和标题/正文/diff 内容,不运行构建、lint、类型检查或测试,因此不可能指示代码缺陷。CANCELLED 是工作流/运行器状态(例如调用方工作流运行被取代),并非带有可诊断输出的失败。本 checkout 中的任何改动都无法改变它的状态;工作流会在下一次事件时重新评估。除此之外没有其他红色检查。

结论

没有需要修复、拒绝或升级的事项。未修改任何源文件。

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@wenshao

wenshao commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Maintainer verification — real local build, A/B against base

I rebuilt this PR and its merge-base from source and drove both through the real CLI (headless stream-json and the interactive TUI) against a deterministic local OpenAI-compatible provider. Every claim in the PR description reproduces. Recommendation: LGTM to merge, with one behavior note below that is a product decision rather than a defect.

  • Head 58f0857034 · base 9342788720 · current main 89b3d5ea8e
  • macOS 26.6, Node v24.18.1, deps from the repo lockfile
  • Provider: a local fake that returns a unique short reply every turn and never proposes completion — so the Goal runtime keeps issuing continuation permits, which is exactly the boundary this PR moves. Turn counts below are counted at the provider, and the lifecycle is read from goal_state events plus the session JSONL.

1. The fixed cutoff is really gone (real headless run)

base 9342788720 head 58f0857034
Model turns served 50, then stopped 75, still active
Final status usage_limited activepaused only when the wall-clock budget tripped
lastReason Goal exceeded the 50-turn continuation budget (none)
usage_limited records written 1 0
Exit 0 55 (--max-wall-time 30 — an explicit host policy)

The head run walks … 49 → 50 → 51 → 52 … with no discontinuity at the old boundary, and the only thing that ended it was the budget the operator asked for. That matches the PR's intent precisely: lifecycle-driven, not turn-count-driven.

2. Cross-version resume — the persisted-state claim holds

This is the strongest evidence, because the starting state was not hand-written: I let the base build run itself into a genuine usage_limited at turnCount: 50, snapshotted that session file, and resumed the same file with each build.

resume A/B

  • base resumes the file at turn 0 — 50 turns of history silently discarded
  • head resumes at turn 50, advances to 51, and keeps admitting continuations

Wire compatibility is confirmed too: the head build reads a snapshot written by the old build without migration.

3. There is an observable UI change (PR says otherwise)

The description says no screenshot applies. Not quite — the Goal pill and status card change for a user who hits the old boundary, and I think that's worth having on record:

Before — base build, real TUI: stops at 50 and surfaces a limit that no provider ever imposed.

before

After — head build, real TUI: sails past 50 (57 turns here, paused only because I pressed Esc).

after

Note the footer pill flips from ! /goal usage limited to ◎ /goal active, and the false Reason: Goal exceeded the 50-turn continuation budget line is gone.

4. Tests, and whether they're load-bearing

Check Result
Focused core validation (4 files) 135 passed — matches the PR's stated number exactly
Full src/goals + client-goal 312 passed (14 files)
CLI Goal UI (goalCommand, GoalPill, GoalStatusMessage) 85 passed
npm run typecheck pass
ESLint + Prettier on changed files clean
Merged with current main (61 commits ahead) clean merge, 313 passed, typecheck pass
Dangling refs to the removed export none anywhere in packages/ or docs/

Mutation testing — I re-broke the behavior to confirm the rewritten tests actually catch it:

  • Restoring turnCount: 0 on resume → kills 2 tests (goal reducer > preserves the cumulative turn count…, goal runtime > resumes persisted state at the former limit…)
  • Restoring a turnCount >= 50 guard in queueContinuation → kills 4 tests across goal-runtime, goal-runtime.integration, and client-goal

The four surviving usage_limited paths (verification flush / read / cursor / provider failure) all still pass, so the PR's claim that real failures keep that lifecycle state is accurate.

5. CI note

precheck-pr / precheck shows fail in the checks list, but that job was cancelled, not broken: Canceling since a higher priority waiting request for qwen-pr-precheck-8421 exists. The precheck runs that actually completed on this commit passed, as did Test (ubuntu-latest, Node 22.x) and the web-shell E2E smoke. No action needed.

6. One thing to decide consciously before merging

Removing the cutoff means Core now ships no default bound of any kind. I measured what that looks like:

  • Headless, no budget flags: 791 model turns in 45 seconds, still running when I killed it. On base the same run stops at 50.
  • --max-session-turns 5 does not bound Goal continuations — I measured 70 turns. That's pre-existing and deliberate (!isGoalRuntimeTurn in client.ts, from feat(cli): adopt Goal v3 in non-interactive mode #8324), not something this PR introduced, but it does mean the most obvious-looking guard is not the guard.
  • The only automatic bound is --max-wall-time / --max-tool-calls, and RunBudgetEnforcer is instantiated only in nonInteractiveCli.ts. In the interactive TUI there is no automatic stop at all — a Goal wedged in a verifier-rejection loop runs until the user notices and hits Esc or /goal pause.

This is exactly what the PR says it does, and I agree the old cutoff was wrong: it fabricated a resource limit that never happened and threw away history on resume. But the previous 50 was also acting as an accidental backstop for interactive users on metered APIs, and nothing replaces it. A follow-up adding an opt-in interactive budget (or wiring RunBudgetEnforcer into the TUI) would close that gap without reintroducing the behavior this PR correctly removes.

Nothing here blocks the merge — the change does what it claims, the tests are real, and it lands cleanly on current main.

中文版本

维护者验证 —— 本地真实构建,与 base 做 A/B 对比

我从源码重新构建了本 PR 和它的 merge-base,用真实 CLI(headless stream-json 与交互式 TUI)跑通了两个版本,provider 换成本地确定性的 OpenAI 兼容服务。PR 描述里的每一条结论都能复现。结论:同意合并,下面第 6 点是一个需要有意识确认的产品取舍,不是缺陷。

  • head 58f0857034 · base 9342788720 · 当前 main 89b3d5ea8e
  • macOS 26.6,Node v24.18.1,依赖按仓库 lockfile 安装
  • provider:本地假服务,每轮返回一段互不相同的短文本且从不提出完成,因此 Goal runtime 会持续签发续轮 permit —— 正好压在本 PR 移动的那条边界上。下文轮数在 provider 侧计数,生命周期取自 goal_state 事件与会话 JSONL。

1. 固定上限确实被移除了(真实 headless 运行)

base 9342788720 head 58f0857034
provider 实际服务轮数 50 轮后停止 75 轮,仍为 active
最终状态 usage_limited active → 仅在 wall-clock 预算触发时 paused
lastReason Goal exceeded the 50-turn continuation budget (无)
写入的 usage_limited 记录 1 条 0 条
退出码 0 55(--max-wall-time 30,显式 host 策略)

head 的轮数 … 49 → 50 → 51 → 52 … 连续推进,在旧边界处没有任何断点,最终只被使用者显式要求的预算终止。这与 PR 的目标完全一致:由生命周期驱动,而不是由轮数驱动。

2. 跨版本 resume —— 持久化状态的说法成立

这是最有力的证据,因为初始状态不是手写的:我先让 base 构建自己跑到真实的 usage_limitedturnCount: 50),把该会话文件快照下来,然后用两个构建分别恢复同一份文件

resume A/B

  • base 从第 0 轮恢复 —— 50 轮历史被静默丢弃
  • head 从第 50 轮恢复,推进到第 51 轮,并继续签发续轮

wire 兼容性也得到验证:head 构建无需迁移即可读取旧构建写入的 snapshot。

3. 实际上存在可见的 UI 变化(PR 描述说不适用)

描述中写"不适用截图证据",但对于撞到旧边界的用户来说,Goal pill 和状态卡片确实会变,值得留档:

修改前 —— base 构建,真实 TUI:停在 50 轮,并报出一个从未真实发生过的限制。

before

修改后 —— head 构建,真实 TUI:越过 50 轮继续(这里到 57 turns,暂停是因为我按了 Esc)。

after

注意底部 pill 从 ! /goal usage limited 变为 ◎ /goal active,那条不实的 Reason: Goal exceeded the 50-turn continuation budget 也消失了。

4. 测试,以及这些测试是否真的有效

检查项 结果
聚焦 Core 验证(4 个文件) 135 通过 —— 与 PR 所述数字完全一致
完整 src/goals + client-goal 312 通过(14 个文件)
CLI Goal UI(goalCommandGoalPillGoalStatusMessage 85 通过
npm run typecheck 通过
改动文件的 ESLint + Prettier 干净
与当前 main 合并(落后 61 个 commit) 合并干净,313 通过,typecheck 通过
已移除导出的残留引用 packages/docs/ 内均无

变异测试 —— 我把行为改回去,确认重写后的测试确实能抓住:

  • 恢复 resume 时的 turnCount: 0 → 杀掉 2 个测试(goal reducer > preserves the cumulative turn count…goal runtime > resumes persisted state at the former limit…
  • queueContinuation 里恢复 turnCount >= 50 判断 → 杀掉 4 个测试,覆盖 goal-runtimegoal-runtime.integrationclient-goal

四条保留的 usage_limited 路径(verification flush / read / cursor / provider 故障)全部仍然通过,因此 PR 关于"真实故障仍使用该生命周期状态"的说法准确。

5. CI 说明

checks 列表里 precheck-pr / precheck 显示 fail,但那个 job 是被取消的,不是真失败:Canceling since a higher priority waiting request for qwen-pr-precheck-8421 exists。在该 commit 上真正跑完的 precheck 是通过的,Test (ubuntu-latest, Node 22.x) 和 web-shell E2E smoke 同样通过。无需处理。

6. 合并前需要有意识确认的一点

移除上限意味着 Core 现在不再提供任何默认边界。我实测了这在现实中是什么样子:

  • headless 且不带任何预算参数:45 秒内 791 次模型轮次,我杀掉进程时仍在运行。同样的跑法在 base 上 50 轮就停了。
  • --max-session-turns 5 不会约束 Goal 续轮 —— 实测跑到 70 轮。这是既有的、有意为之的设计(client.ts 中的 !isGoalRuntimeTurn,来自 feat(cli): adopt Goal v3 in non-interactive mode #8324),并非本 PR 引入,但这意味着看起来最像护栏的那个参数并不是护栏。
  • 唯一的自动边界是 --max-wall-time / --max-tool-calls,而 RunBudgetEnforcer 只在 nonInteractiveCli.ts 中实例化。交互式 TUI 里没有任何自动停止机制 —— 卡在 verifier 反复拒绝循环里的 Goal 会一直跑,直到用户发现并按 Esc 或 /goal pause

这正是 PR 声明要做的事,我也认同旧的上限是错的:它凭空捏造了一个从未发生的资源限制,并在 resume 时丢弃历史。但此前的 50 同时也在无意中充当了交互式用户(尤其是按量计费的 API)的兜底,而现在没有任何东西接替它。后续可以加一个可选的交互式预算(或把 RunBudgetEnforcer 接入 TUI)来补上这个缺口,同时不必把本 PR 正确移除的行为再引回来。

以上都不构成合并阻塞 —— 改动确实做到了它声称的事,测试是有效的,且能干净地落在当前 main 上。

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下:

Autofix review round: no action taken

No feedback in this round requires a code change, so no commit was made. The branch remains at 58f085703.

The only new feedback is @wenshao's maintainer verification report — a full local A/B rebuild of this PR against its merge-base, driven through the real CLI. Point-by-point disposition:

  • Verification verdict (LGTM to merge). All PR claims reproduced: the fixed 50-turn cutoff is gone, cross-version resume preserves the cumulative turn count without migration, the rewritten tests are load-bearing (mutation-tested), and the branch lands cleanly on current main. Nothing to act on.
  • §3 — observable UI change exists. The report corrects the PR description's "no screenshot applies" by recording real before/after TUI screenshots (Goal pill flips from ! /goal usage limited to ◎ /goal active; the false 50-turn-budget reason line disappears). The evidence is preserved in the maintainer's comment itself; it documents the intended behavior change and requires no code change.
  • §6 — no default turn bound remains; TUI has no automatic stop; --max-session-turns does not bound Goal continuations. Declined for this PR, by the reviewer's own framing: the report explicitly says "Nothing here blocks the merge" and designates the remedy as follow-up work ("A follow-up adding an opt-in interactive budget (or wiring RunBudgetEnforcer into the TUI) would close that gap without reintroducing the behavior this PR correctly removes"). Designing that budget is a product decision (opt-in vs default, TUI wiring, interaction with existing --max-wall-time / --max-tool-calls enforcement) that belongs in a separate issue/PR; implementing it here would balloon this PR beyond its intentionally minimal scope. The --max-session-turns behavior is pre-existing and deliberate (!isGoalRuntimeTurn from feat(cli): adopt Goal v3 in non-interactive mode #8324), not introduced by this PR.
  • §5 — CI note. Confirmed from the checks data: the cancelled precheck-pr / precheck run was superseded by a higher-priority request; every check that completed on this commit is SUCCESS or SKIPPED, including Test (ubuntu-latest, Node 22.x) and the web-shell E2E smoke. No action needed.

There were no inline comments, no change-requesting reviews (the automated review is APPROVED on this exact head commit), no failed checks, and no unresolved conflicts in this round.

中文说明

Autofix 审阅轮次:未采取任何行动

本轮反馈中没有任何需要修改代码的内容,因此未产生提交。分支仍停留在 58f085703

本轮唯一的新反馈是 @wenshao 的维护者验证报告 —— 在本地将本 PR 与其 merge-base 分别完整重新构建,并通过真实 CLI 做了 A/B 对比。逐点处理如下:

  • 验证结论(同意合并)。 PR 的所有结论均复现:固定的 50 轮上限已被移除,跨版本 resume 保留累计轮数且无需迁移,重写后的测试确实有效(已做变异测试验证),且本分支能干净地落在当前 main 上。无需任何改动。
  • §3 —— 存在可见的 UI 变化。 报告纠正了 PR 描述中"不适用截图"的说法,并记录了真实的前后 TUI 截图(Goal pill 从 ! /goal usage limited 变为 ◎ /goal active;那条不实的 50 轮上限 reason 消失)。该证据已保存在维护者的评论中;它记录的是预期的行为变化,不需要任何代码改动。
  • §6 —— 不再有任何默认轮数边界;TUI 中没有自动停止机制;--max-session-turns 不约束 Goal 续轮。 在本 PR 中不予采纳,这正是审阅者本人的定位:报告明确写道"以上都不构成合并阻塞",并将补救措施指定为后续工作("后续可以加一个可选的交互式预算(或把 RunBudgetEnforcer 接入 TUI)来补上这个缺口,同时不必把本 PR 正确移除的行为再引回来")。设计这样一个预算属于产品决策(opt-in 还是默认、如何接入 TUI、与现有 --max-wall-time / --max-tool-calls 执法机制的关系),应在单独的 issue/PR 中进行;在本 PR 中实现会使它超出其有意保持的最小范围。--max-session-turns 的行为是既有且有意为之的设计(feat(cli): adopt Goal v3 in non-interactive mode #8324 中的 !isGoalRuntimeTurn),并非本 PR 引入。
  • §5 —— CI 说明。 已从 checks 数据确认:被取消的 precheck-pr / precheck 运行是被更高优先级的请求取代的;在该 commit 上所有真正跑完的检查均为 SUCCESS 或 SKIPPED,包括 Test (ubuntu-latest, Node 22.x) 和 web-shell E2E smoke。无需处理。

本轮没有内联评论,没有要求修改的 review(自动化 review 在当前这个 head commit 上为 APPROVED),没有失败的检查,也没有未解决的冲突。

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@qqqys

qqqys commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔄 Takeover re-armed: the round counter starts a fresh window (previous rounds no longer count toward the cap); management continues.

中文说明

🔄 已重新武装:轮次计数开启新窗口(此前轮次不再计入上限),托管继续。

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed — no blockers. Suggestions are inline. Test Plan (not a blocker): 135 tests passed — this review observed 19160, 1067, 17792, 1455, 475, 2825, 444 passed.

中文说明

已审查——无阻断问题。 建议见行内评论。 Test Plan(非阻断):135 tests passed — this review observed 19160, 1067, 17792, 1455, 475, 2825, 444 passed

— qwen3.8-max via Qwen Code /review (v0.21.6)

Comment on lines +1017 to +1018
const turns = 75;
for (let i = 0; i < turns; i++) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The deleted MAX_GOAL_CONTINUATION_TURNS was the only in-tree record tying these loop counts to the former 50-turn cap. The test titles promise they run "beyond the former fixed limit", but the bare 75 encodes the load-bearing > 50 invariant nowhere — Failure scenario: a maintainer trimming these async loops for speed (say, to 10) gets no signal that the count must stay above 50; probe-verified at this commit — with turns = 10 a reintroduced turnCount >= 50 cutoff still passes every test and goes undetected, so the regression guard silently degrades while the titles keep asserting the property. The unnamed turnCount: 50 fixtures in this file's restore test (and in goal-reducer.test.ts) can use the same constant. Suggested fix:

const FORMER_GOAL_CONTINUATION_LIMIT = 50;
const turns = FORMER_GOAL_CONTINUATION_LIMIT + 25;
中文说明

被删除的 MAX_GOAL_CONTINUATION_TURNS 是树中唯一把这些循环轮数与旧 50 轮上限关联起来的记录。测试标题承诺"超越原固定上限"运行,但裸常量 75 没有在任何地方体现关键的 > 50 不变量 —— 失败场景:维护者为提速而缩减这些异步循环(比如改到 10)时,不会得到任何提示说该数值必须保持在 50 以上;已在本提交上通过探针验证 —— 当 turns = 10 时,即使重新引入 turnCount >= 50 的截断逻辑,所有测试仍然通过且无法被检测,回归防护会在无声中失效,而测试标题仍然声明着这一性质。本文件 restore 测试(以及 goal-reducer.test.ts)中未命名的 turnCount: 50 fixture 也可复用同一常量。建议修改:

const FORMER_GOAL_CONTINUATION_LIMIT = 50;
const turns = FORMER_GOAL_CONTINUATION_LIMIT + 25;

— qwen3.8-max via Qwen Code /review (v0.21.6)

await runtime.dispatch({ action: 'create', objective: 'ship' });

const turns = MAX_GOAL_CONTINUATION_TURNS - 1;
const turns = 75;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] Same finding as the goal-runtime.test.ts comment: with MAX_GOAL_CONTINUATION_TURNS removed, this bare 75 loop is the "beyond the former fixed limit" regression guard, but nothing encodes that the count must stay above the former 50 cap — Failure scenario: the loop can be trimmed (say, to 10) without any test failing, and a reintroduced turnCount >= 50 cutoff mutation goes undetected (probe-verified at this commit). Suggested fix: name the former limit, e.g. const FORMER_GOAL_CONTINUATION_LIMIT = 50; with const turns = FORMER_GOAL_CONTINUATION_LIMIT + 25;.

中文说明

goal-runtime.test.ts 上的评论是同一发现:MAX_GOAL_CONTINUATION_TURNS 被删除后,这个裸 75 循环是"超越原固定上限"的回归防护,但没有任何地方体现轮数必须大于旧的 50 上限 —— 失败场景:循环被缩减(比如改到 10)时不会有任何测试失败,重新引入 turnCount >= 50 截断逻辑的变异也无法被检测(已在本提交上通过探针验证)。建议修改:为旧上限命名,例如 const FORMER_GOAL_CONTINUATION_LIMIT = 50; 并用 const turns = FORMER_GOAL_CONTINUATION_LIMIT + 25;

— qwen3.8-max via Qwen Code /review (v0.21.6)

it('keeps sequential automatic admissions independent within the turn budget', async () => {
const turns = MAX_GOAL_CONTINUATION_TURNS - 1;
it('keeps sequential automatic admissions independent beyond the former fixed limit', async () => {
const turns = 75;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] Same finding as the goal-runtime.test.ts comment: with MAX_GOAL_CONTINUATION_TURNS removed, this bare 75 loop is the "beyond the former fixed limit" regression guard, but nothing encodes that the count must stay above the former 50 cap — Failure scenario: the loop can be trimmed (say, to 10) without any test failing, and a reintroduced turnCount >= 50 cutoff mutation goes undetected (probe-verified at this commit). Suggested fix: name the former limit, e.g. const FORMER_GOAL_CONTINUATION_LIMIT = 50; with const turns = FORMER_GOAL_CONTINUATION_LIMIT + 25;.

中文说明

goal-runtime.test.ts 上的评论是同一发现:MAX_GOAL_CONTINUATION_TURNS 被删除后,这个裸 75 循环是"超越原固定上限"的回归防护,但没有任何地方体现轮数必须大于旧的 50 上限 —— 失败场景:循环被缩减(比如改到 10)时不会有任何测试失败,重新引入 turnCount >= 50 截断逻辑的变异也无法被检测(已在本提交上通过探针验证)。建议修改:为旧上限命名,例如 const FORMER_GOAL_CONTINUATION_LIMIT = 50; 并用 const turns = FORMER_GOAL_CONTINUATION_LIMIT + 25;

— qwen3.8-max via Qwen Code /review (v0.21.6)

wenshao
wenshao previously approved these changes Aug 6, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下:

Autofix review-response summary — PR #8421

Outcome: addressed all three inline findings with one minimal, test-only commit.

Feedback points and decisions

  • [Suggestion] rc:3727741372 (packages/core/src/goals/goal-runtime.test.ts:1018), [Suggestion] rc:3727741381 (packages/core/src/core/client-goal.test.ts:1019), [Suggestion] rc:3727741387 (packages/core/src/goals/goal-runtime.integration.test.ts:34) — all three are the same finding: with MAX_GOAL_CONTINUATION_TURNS deleted, the bare 75 loop counts in the "beyond the former fixed limit" regression tests encode the load-bearing > 50 invariant nowhere, so trimming the loops (e.g. to 10) would silently let a reintroduced turnCount >= 50 cutoff pass undetected (probe-verified by the reviewer). Addressed.

Changes

  • Introduced const FORMER_GOAL_CONTINUATION_LIMIT = 50; in each of the four affected test files and derived every load-bearing count from it, exactly as suggested:
    • goal-runtime.test.ts, goal-runtime.integration.test.ts, client-goal.test.ts: const turns = 75;const turns = FORMER_GOAL_CONTINUATION_LIMIT + 25;
    • goal-runtime.test.ts restore test: the unnamed turnCount: 50 fixture and its expectations now use FORMER_GOAL_CONTINUATION_LIMIT (and + 1 for the follow-up turn), as the finding proposed.
    • goal-reducer.test.ts: the unnamed turnCount: 50 fixture and expectation in the resume test now use the same constant, as the finding proposed.
  • The constant is declared in the test files rather than re-exported from production code: the PR's whole point is removing the cap from production, so a "former limit" export in goal-runtime.ts would be a dead production constant.
  • All values are numerically identical to before (75, 50, 51) — this is a naming-only change; no behavior changed and no production code was touched. A maintainer trimming these loops now has to consciously edit a derivation from the named former limit, which is the signal the finding asked for.
  • The review's top-level comment carried no blockers and its Test Plan note was explicitly informational (observed per-file test counts); nothing to address there.

Conflict notes

--conflict false: no merge of origin/main was performed.

Commit

  • 20bf8b84c test(core): name former Goal continuation limit in regression guards (4 files changed, 20 insertions, 8 deletions — test files only)

Verification

  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed
  • npx vitest run src/goals/goal-runtime.test.ts src/goals/goal-runtime.integration.test.ts src/goals/goal-reducer.test.ts src/core/client-goal.test.ts (in packages/core, the touched files) — 4 test files passed, 136/136 tests passed
  • npx prettier --check on the four touched files — passed
  • No integration/bundle run needed: no production code changed, and the touched behavior is fully covered by the unit tests above. No settings source changed, so no schema regeneration.
中文说明

Autofix 评审响应总结 — PR #8421

结果: 用一个最小的纯测试提交处理了全部三条行内评审意见。

反馈点与决定

  • [Suggestion] rc:3727741372packages/core/src/goals/goal-runtime.test.ts:1018)、[Suggestion] rc:3727741381packages/core/src/core/client-goal.test.ts:1019)、[Suggestion] rc:3727741387packages/core/src/goals/goal-runtime.integration.test.ts:34)—— 三条是同一个发现:MAX_GOAL_CONTINUATION_TURNS 被删除后,"超越原固定上限" 回归测试中裸常量 75 的循环轮数没有在任何地方体现关键的 > 50 不变量,因此缩减循环(比如改到 10)时,重新引入的 turnCount >= 50 截断逻辑可以在无声中通过所有测试而不被检测(评审者已通过探针验证)。已处理。

变更内容

  • 在四个受影响的测试文件中分别引入 const FORMER_GOAL_CONTINUATION_LIMIT = 50;,并按建议把每一个关键轮数都从该常量推导出来:
    • goal-runtime.test.tsgoal-runtime.integration.test.tsclient-goal.test.tsconst turns = 75;const turns = FORMER_GOAL_CONTINUATION_LIMIT + 25;
    • goal-runtime.test.ts 的 restore 测试:未命名的 turnCount: 50 fixture 及其断言改为使用 FORMER_GOAL_CONTINUATION_LIMIT(后续一轮使用 + 1),与发现中的建议一致。
    • goal-reducer.test.ts:resume 测试中未命名的 turnCount: 50 fixture 与断言同样改为使用该常量,与发现中的建议一致。
  • 常量声明在测试文件中,而不是从生产代码重新导出:本 PR 的目的就是从生产代码中移除固定上限,在 goal-runtime.ts 中放一个"旧上限"导出会成为无用的生产常量。
  • 所有数值与之前完全相同(75、50、51)—— 这只是命名变更,行为没有变化,也没有触碰任何生产代码。今后维护者若要缩减这些循环,必须有意识地修改从具名旧上限推导的表达式,这正是该发现要求提供的信号。
  • 评审的顶层评论没有阻断问题,其 Test Plan 说明也明确只是信息性的(观察到的各文件测试数量);无需处理。

冲突说明

--conflict false:未执行对 origin/main 的合并。

提交

  • 20bf8b84c test(core): name former Goal continuation limit in regression guards(4 个文件变更,20 行新增、8 行删除 —— 仅测试文件)

验证

  • npm run build — 通过
  • npm run typecheck — 通过
  • npm run lint — 通过
  • npx vitest run src/goals/goal-runtime.test.ts src/goals/goal-runtime.integration.test.ts src/goals/goal-reducer.test.ts src/core/client-goal.test.ts(在 packages/core 中运行,即被触碰的文件)—— 4 个测试文件全部通过,136/136 个测试通过
  • 对四个被触碰的文件执行 npx prettier --check — 通过
  • 无需运行集成/打包测试:未改动任何生产代码,上述单元测试已完整覆盖被触碰的行为。未改动任何设置源,因此无需重新生成 schema。

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。

Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No issues found. LGTM! ✅

Test Plan (not a blocker): 135 tests passed — this review observed 19160, 1067, 17792, 1455, 475, 2825, 444 passed.

中文说明

未发现问题。LGTM!✅

Test Plan(非阻断):135 tests passed — this review observed 19160, 1067, 17792, 1455, 475, 2825, 444 passed

— qwen3.8-max via Qwen Code /review (v0.21.6)

@wenshao
wenshao added this pull request to the merge queue Aug 6, 2026
Merged via the queue into QwenLM:main with commit 41f9b83 Aug 6, 2026
50 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.21.7.

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

Labels

autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants