-
Notifications
You must be signed in to change notification settings - Fork 3k
fix(cli): stop the TUI startup freeze when chat recording is disabled (#10293) #10303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
5122b88
f4abf53
c3c2146
56c30a4
9e58d09
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,6 +56,11 @@ export async function waitForGoalRuntime( | |
| ): Promise<boolean> { | ||
| const awaitReady = async (): Promise<void> => { | ||
| try { | ||
| // The call itself must stay inside the try: with chat recording | ||
| // disabled (--no-chat-recording, settings) getGoalRuntimeReady() | ||
| // THROWS synchronously instead of rejecting, and an escaped throw | ||
|
Comment on lines
+59
to
+61
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] The discipline this comment codifies is caller-side only, while the contract violation stays open at the producer: This is not hypothetical: #10128 forgot exactly this discipline in this very function, the escaped throw froze the TUI on the init banner and red-lined the post-merge E2E lanes (#10293). Every future caller of either method must independently remember to keep the call itself inside a try/catch; the first one that doesn't — a new hook, command, or ACP path running with Consider normalising at the producer (here or in a follow-up, given core-config sensitivity) so the Promise contract holds for every caller and this comment's warning becomes moot: getGoalRuntimeReady(): Promise<GoalRuntime> {
try {
const runtime = this.getGoalRuntime();
// ...existing readiness check...
return this.goalRuntimeReady.then(() => runtime);
} catch (error) {
return Promise.reject(error);
}
}Probe witness (isolated scratch tree, real If the normalisation lands, the pinning test is the core-side counterpart of 中文说明这条注释所固化的约束只存在于调用方一侧,而契约违背在产生方仍然敞开: 这并非理论风险:#10128 正是在本函数中遗忘了这一约束,逃逸的抛出使 TUI 冻结在启动横幅上,并令合并后的 E2E 全线变红(#10293)。这两个方法的每一个未来调用方都必须各自记得把调用本身放进 try/catch;第一个忘记这样做的调用方——某个运行在 建议在生产方归一化(在本 PR 或后续跟进中,考虑到 core 配置的敏感性),使 Promise 契约对每个调用方都成立,这条注释的警告也就随之失去必要: getGoalRuntimeReady(): Promise<GoalRuntime> {
try {
const runtime = this.getGoalRuntime();
// ...existing readiness check...
return this.goalRuntimeReady.then(() => runtime);
} catch (error) {
return Promise.reject(error);
}
}探针见证(隔离的 scratch tree,真实的 如果归一化落地,钉住它的测试是 — qwen3.8-max via Qwen Code /review (v0.22.2) |
||
| // rejects the AppContainer startup effect, freezing the TUI on the | ||
| // init banner forever. | ||
| await config.getGoalRuntimeReady(); | ||
| } catch (error) { | ||
| if (!(error instanceof GoalPersistenceUnavailableError)) throw error; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Suggestion] The new rethrow test pins error propagation only on the no-timeout path, but the production startup gate (
AppContainer.tsx) always callswaitForGoalRuntimewith atimeoutMs, i.e. through thePromise.racebranch — and no test covers error propagation on that branch (the swallow test already asserts both). I mutation-verified the hole in an isolated scratch tree: changing the race entry toawaitReady().catch(() => 'timeout' as const)— which would turn a genuine Goal-state error (e.g. a malformed lifecycle record) at TUI startup into a silentfalse/ "Goal features are degraded" outcome instead of surfacing it to the global error handler — leaves the whole file green (Tests 8 passed (8)), while adding the timeout-path assertion below kills the mutant (Tests 1 failed | 7 passed) and passes on the current code.The added assertion is its own fix witness: reintroduce the swallowing mutant on the race branch and it is the line that goes red — confirm the red after adding it.
中文说明
新增的重抛(rethrow)测试只在无超时路径上钉住了错误传播,但生产环境的启动门(
AppContainer.tsx)总是带timeoutMs调用waitForGoalRuntime,也就是走Promise.race分支 —— 而该分支的错误传播没有任何测试覆盖(吞异常(swallow)测试已经同时断言了两个分支)。我在隔离的 scratch tree 中做了变异验证:把 race 的一项改成awaitReady().catch(() => 'timeout' as const)—— 这会把 TUI 启动时真实的 Goal 状态错误(例如损坏的生命周期记录)变成静默的false/ "Goal features are degraded",而不是上抛给全局错误处理器 —— 整个测试文件仍然全绿(Tests 8 passed (8));而补上下方超时路径的断言即可杀死该变异体(Tests 1 failed | 7 passed),且在当前代码上通过。新增的断言本身就是修复见证:在 race 分支重新引入吞异常的变异体,正是该断言会变红 —— 添加后请确认它确实变红。
— qwen3.8-max via Qwen Code /review (v0.22.2)