fix(core): remove fixed Goal continuation limit - #8421
Conversation
Goal v3 no fixed continuation limit verificationDate: 2026-08-03 Scope
Red evidenceThe updated boundary tests failed against the old implementation:
Green evidenceFocused 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.tsResult: 4 files passed, 135 tests passed. Observed deterministic interaction chain:
Persisted-state compatibility chain:
Regression checks
Intentional product tradeoffCore 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 evidenceNo 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. |
|
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 Direction: Aligned. Goal turns were deliberately excluded from the session and recursive turn budgets (#7895), and recent fixes reserve Size: Core paths touched ( 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。 模板完整 ✓ —— 各节齐全,包含中文说明。 问题: 真实且可在已发布代码中观测,不是假设。在当前 方向: 对齐。Goal 轮次本就有意不计入 session 与递归轮数预算(#7895),近期修复也把 规模: 触及核心路径( 方案: 最小化 —— 改动已经小到不能再小:删除上限分支与常量、移除仅为该上限服务的 resume 清零、翻转测试以钉住新行为。没有顺手改动,无可砍内容。描述中有一处事实性小问题:被移除的常量并非"未发布" —— 它自 v0.21.3 起已随 release 发布,并从 core 包入口导出,破坏性变更说明应写"已发布"而非"未发布"。不阻塞,详见代码审查评论。 风险: 未命中高风险路径。本 PR 的取舍 —— Core 不再提供默认轮数/时间截止,改由生命周期终态、用户暂停/清除、host 的 wall-clock/工具调用预算兜底 —— 已在 Risk & Scope 中披露,且与已合并的设计脉络一致。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
|
@qwen-code /takeover |
|
🤝 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 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。本 PR 来自 fork,首轮处理将由下一次定时扫描执行(通常几分钟内)。移除 |
|
@qwen-code /triage |
|
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 reportPR 8421 — fix(core): remove fixed Goal continuation limitVerdict: 中文摘要
Central claim + A/B load-bearing proofCentral claim: with the fixed 50-continuation cutoff removed, an active Goal keeps receiving runtime-issued permits past turn 50 without transitioning to Harness (
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 Reviewer Test Plan walkthrough
Vacuity / mutation matrix (
|
| 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,!isGoalRuntimeTurnguard, pre-existing and documented indocs/users/features/headless.md). The host policies the PR's tradeoff note names do exist:--max-wall-time/--max-tool-callsare wired inconfig.ts/settingsSchema.ts/nonInteractiveCli.tsand 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,nonInteractiveClireadturnCountdisplay-only; not exercised end-to-end). - Whether the removed
MAX_GOAL_CONTINUATION_TURNSexport 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/ignoretypings) because a fresh worktree lacks the package-localnode_modulespresent 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
Harness scripts and raw logs are in the workflow run artifacts (7-day retention).
— Qwen Code · sandboxed verification
Code reviewI formed my baseline before reading the diff: remove the cutoff branch and constant from the Goal runtime, drop the The consumer audit is the part that matters for a deletion:
No critical issues, no convention violations. One non-blocking observation:
Testing evidence (from the PR's own CI — unattended run, no PR code executed here)Head
The behavioral claim is pinned by the suite itself: the new tests are the exact mirror of the removed ones — 75 sequential continuations stay Real-scenario (tmux) testing: 中文说明代码审查先独立给出方案再看 diff:从 Goal runtime 删除上限分支与常量、移除 resume 时仅为规避该上限而存在的 删除改动最需要的是消费者审计:
无阻塞问题,无规范违规。一条非阻塞观察:
测试证据(来自 PR 自身 CI —— 非值守运行,未在此执行任何 PR 代码)Head 行为声明由套件本身钉住:新测试与被删除的旧测试互为镜像 —— 75 个连续续轮保持 真实场景(tmux)测试: — Qwen Code · qwen3.8-max Reviewed at |
|
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 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 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 清零、翻转测试。这个改动没有更小的版本,我也想不出更简的路径。我试图寻找被移除行为的残留消费者,没有找到:没有其他代码导入该常量,没有其他代码把 移除自主上限背后真正的问题是:现在什么能阻止一个不收敛的 Goal?答案是具体且经过代码验证的,不是假设:每轮内部的 予以批准:每一处删除都是必要的,测试直接陈述了预期行为,CI 在被审查的 commit 上是绿的。如果合并前把破坏性变更说明从"未发布"更正为"自 v0.21.3 起已发布",release notes 也会是准确的。 — Qwen Code · qwen3.8-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: Autofix review round: no action takenNo feedback in this round requires a code change, so no commits were made. The PR head remains at What was reviewed
Failed-check triage
ConclusionNothing to fix, decline, or escalate. No source files were modified. 中文说明Autofix 评审轮次:未做任何改动本轮没有任何需要代码改动的反馈,因此没有产生任何提交。PR head 仍为 评审内容
失败检查的分类处理
结论没有需要修复、拒绝或升级的事项。未修改任何源文件。 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
Maintainer verification — real local build, A/B against baseI rebuilt this PR and its merge-base from source and drove both through the real CLI (headless
1. The fixed cutoff is really gone (real headless run)
The head run walks 2. Cross-version resume — the persisted-state claim holdsThis is the strongest evidence, because the starting state was not hand-written: I let the base build run itself into a genuine
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. After — head build, real TUI: sails past 50 ( Note the footer pill flips from 4. Tests, and whether they're load-bearing
Mutation testing — I re-broke the behavior to confirm the rewritten tests actually catch it:
The four surviving 5. CI note
6. One thing to decide consciously before mergingRemoving the cutoff means Core now ships no default bound of any kind. I measured what that looks like:
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 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
1. 固定上限确实被移除了(真实 headless 运行)
head 的轮数 2. 跨版本 resume —— 持久化状态的说法成立这是最有力的证据,因为初始状态不是手写的:我先让 base 构建自己跑到真实的
wire 兼容性也得到验证:head 构建无需迁移即可读取旧构建写入的 snapshot。 3. 实际上存在可见的 UI 变化(PR 描述说不适用)描述中写"不适用截图证据",但对于撞到旧边界的用户来说,Goal pill 和状态卡片确实会变,值得留档: 修改前 —— base 构建,真实 TUI:停在 50 轮,并报出一个从未真实发生过的限制。 修改后 —— head 构建,真实 TUI:越过 50 轮继续(这里到 注意底部 pill 从 4. 测试,以及这些测试是否真的有效
变异测试 —— 我把行为改回去,确认重写后的测试确实能抓住:
四条保留的 5. CI 说明checks 列表里 6. 合并前需要有意识确认的一点移除上限意味着 Core 现在不再提供任何默认边界。我实测了这在现实中是什么样子:
这正是 PR 声明要做的事,我也认同旧的上限是错的:它凭空捏造了一个从未发生的资源限制,并在 resume 时丢弃历史。但此前的 50 同时也在无意中充当了交互式用户(尤其是按量计费的 API)的兜底,而现在没有任何东西接替它。后续可以加一个可选的交互式预算(或把 以上都不构成合并阻塞 —— 改动确实做到了它声称的事,测试是有效的,且能干净地落在当前 main 上。 |
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: Autofix review round: no action takenNo feedback in this round requires a code change, so no commit was made. The branch remains at 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:
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 审阅轮次:未采取任何行动本轮反馈中没有任何需要修改代码的内容,因此未产生提交。分支仍停留在 本轮唯一的新反馈是 @wenshao 的维护者验证报告 —— 在本地将本 PR 与其 merge-base 分别完整重新构建,并通过真实 CLI 做了 A/B 对比。逐点处理如下:
本轮没有内联评论,没有要求修改的 review(自动化 review 在当前这个 head commit 上为 APPROVED),没有失败的检查,也没有未解决的冲突。 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
|
@qwen-code /takeover |
|
🔄 Takeover re-armed: the round counter starts a fresh window (previous rounds no longer count toward the cap); management continues. 中文说明🔄 已重新武装:轮次计数开启新窗口(此前轮次不再计入上限),托管继续。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
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)
| const turns = 75; | ||
| for (let i = 0; i < turns; i++) { |
There was a problem hiding this comment.
[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; |
There was a problem hiding this comment.
[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; |
There was a problem hiding this comment.
[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)
20bf8b8
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Autofix review-response summary — PR #8421Outcome: addressed all three inline findings with one minimal, test-only commit. Feedback points and decisions
Changes
Conflict notes
Commit
Verification
中文说明Autofix 评审响应总结 — PR #8421结果: 用一个最小的纯测试提交处理了全部三条行内评审意见。 反馈点与决定
变更内容
冲突说明
提交
验证
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/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
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)
|
Released in v0.21.7. |





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_limitedlifecycle 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
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
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 或桌面端渲染,因此不适用截图证据。
测试平台
环境
Node.js 24.14.1,依赖按仓库 lockfile 安装。聚焦 Core 验证共 4 个文件、135 个测试通过。全仓 typecheck、改动文件 ESLint 和 Prettier 检查、全仓 build 均通过。
风险与范围
关联 Issue
#8324 和 #8005 的后续 PR。