fix(tauri): 修复 Windows 后端启动与前端就绪竞态#176
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR implements Tauri backend readiness detection and verification during startup, adds a startup-failure UI, improves Rust backend process logging and project-root detection, decouples Python shared-state typings from multiprocessing, and tightens .gitignore rules. ChangesTauri Backend Readiness Initialization
Python Type Decoupling and Build Configuration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
.gitignore (1)
168-169: ⚡ Quick win
data/.gitkeep例外规则可能不会生效(父目录被整体忽略)当前是先
data/再!data/.gitkeep,在 Git ignore 语义下通常需要先重新包含父目录,或改成data/*才能稳定保留占位文件。建议改为下面这种更稳妥的写法:Suggested diff
-# ── 运行时数据目录(整体忽略) ── -data/ -!data/.gitkeep +# ── 运行时数据目录(整体忽略,保留占位) ── +data/* +!data/.gitkeep🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.gitignore around lines 168 - 169, The current .gitignore uses "data/" then "!data/.gitkeep", which can fail because ignoring the directory prevents re-including the file; update the patterns so the directory itself isn't ignored: replace "data/" with "data/*" (to ignore contents) and keep the exception "!data/.gitkeep" (ensure the filename matches the actual placeholder), e.g. use "data/*" followed by "!data/.gitkeep" so the placeholder file is reliably preserved.application/engine/services/shared_state_repository.py (1)
20-20: ⚡ Quick winKeep the shared-state alias mapping-shaped instead of
Any.Line 20 turns the module’s main cross-process contract into
Any, so invalid injections will now type-check and only fail later on the first.get/[]/delpath. AMutableMapping[str, Any]alias keeps the import-time decoupling goal, but still preserves useful checking across__init__,set_shared_dict,init_shared_state_repository, andinject_shared_dict.♻️ Suggested typing-only tightening
-from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, MutableMapping, Optional, TypeAlias -SharedStateDict = Any +SharedStateDict: TypeAlias = MutableMapping[str, Any]Also applies to: 123-123, 142-142, 497-497, 513-513
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@application/engine/services/shared_state_repository.py` at line 20, Replace the overly-broad SharedStateDict = Any alias with a mapping-shaped typing to catch invalid injections earlier: change SharedStateDict to MutableMapping[str, Any] (import MutableMapping and Any from typing) and update signatures/usages in __init__, set_shared_dict, init_shared_state_repository, and inject_shared_dict to use this alias so the cross-process contract remains decoupled but enforces key/value mapping shape at type-check time.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src-tauri/src/backend.rs`:
- Around line 31-55: The drainer in spawn_stdio_drainers uses BufReader::lines()
and breaks on Err (e.g., invalid UTF-8), which can stop draining stdout/stderr
and deadlock the frozen backend; update the frozen backend Command to set
PYTHONIOENCODING=utf-8 and PYTHONUNBUFFERED=1 (in the same code that configures
HF_*_OFFLINE envs) and change the drainer loops that currently use
BufReader::lines() (and call log::info! or log_backend_stderr_line) to be
resilient: do not break on Err from lines(), instead continue draining and
decode using a loss-tolerant approach (e.g., read bytes and use
std::str::from_utf8_lossy or handle the Err by logging the raw bytes and
continuing) so stdout/stderr pipes are always fully drained.
---
Nitpick comments:
In @.gitignore:
- Around line 168-169: The current .gitignore uses "data/" then
"!data/.gitkeep", which can fail because ignoring the directory prevents
re-including the file; update the patterns so the directory itself isn't
ignored: replace "data/" with "data/*" (to ignore contents) and keep the
exception "!data/.gitkeep" (ensure the filename matches the actual placeholder),
e.g. use "data/*" followed by "!data/.gitkeep" so the placeholder file is
reliably preserved.
In `@application/engine/services/shared_state_repository.py`:
- Line 20: Replace the overly-broad SharedStateDict = Any alias with a
mapping-shaped typing to catch invalid injections earlier: change
SharedStateDict to MutableMapping[str, Any] (import MutableMapping and Any from
typing) and update signatures/usages in __init__, set_shared_dict,
init_shared_state_repository, and inject_shared_dict to use this alias so the
cross-process contract remains decoupled but enforces key/value mapping shape at
type-check time.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 2079c6ae-dd19-4da6-86b8-de02877184c8
📒 Files selected for processing (9)
.gitignoreapplication/engine/services/shared_state_repository.pyfrontend/package.jsonfrontend/scripts/test-tauri-readiness-gate.mjsfrontend/src-tauri/src/backend.rsfrontend/src/api/config.tsfrontend/src/main.tsinterfaces/main.pyout/tauri/plotpilot-backend/.gitkeep
|
感谢修 Windows / Tauri 启动竞态,这个方向很重要,安装包启动稳定性确实是近期重点。 这块在 v4.5.1 附近主线已经做过较大重写:后端启动、health check、端口探测、Tauri sidecar、前端 API 就绪逻辑都调整过。当前 PR 基于旧实现,继续合并会和现有启动链路冲突,所以我先关闭。 如果 readiness gate 或测试脚本里还有可以复用的部分,欢迎单独拆一个小 PR,或者联系我加入 dev 仓库先一起验证。确认没问题后再合 master,会更稳。感谢帮忙盯这个问题。 |
|
再补充说明一下:这次关闭 PR 只是基于当前 master 稳定性、冲突风险和合并策略做的整理,不代表否定大家的贡献。 大家已经投入的工作我都看到了,也会统一计入项目贡献者名单/贡献记录里。很多思路后续仍然会被吸收,只是现在为了尽量避免开源 master 出问题,我会更倾向于先在 dev 仓库里拆小块开发、验证、跑通,再合并到 master。 如果后续还愿意一起推进,欢迎直接联系我,我可以开放 dev 仓库权限。我们按模块拆 PR,一块一块稳着来。再次感谢大家愿意在这么早期、不成熟的阶段帮忙共建,真的感谢。 |
变更类型
Bug 修复
变更说明
修复 Windows/Tauri 启动后首屏请求
/novels、/llm-control/prompts/stats偶发ERR_NETWORK的问题。同时修复干净环境下 Tauri resource 目录缺失导致的 build-script 校验失败,并增强后端子进程启动日志,便于定位后续 Windows 启动问题。
根因
前端只等待 Tauri IPC 返回后端端口,没有等待 FastAPI
/health真正就绪,导致首屏并行请求可能早于 HTTP 服务可用。Windows dev 启动时,Tauri 后端 cwd 可能没有定位到仓库根目录,导致 Python 后端无法正确导入
interfaces。Tauri 配置引用了
out/tauri/plotpilot-backend资源目录,但干净仓库没有该空目录,会触发 build-script 的 resource path 校验失败。部分 Windows multiprocessing 相关类型标注存在导入期求值风险,可能影响后端/daemon 子进程启动。
修复
前端在 Tauri 模式下等待
/health返回 200 后再设置 API baseURL 并挂载应用。Tauri 后端启动逻辑向上查找包含
interfaces/main.py的仓库根目录。保留
out/tauri/plotpilot-backend/.gitkeep,只提交资源目录占位,不提交 PyInstaller 产物。将共享状态相关 multiprocessing 类型标注改为不会触发导入期求值的形式。
后端子进程 stdout/stderr 转发到 Tauri 日志,避免静默吞掉 Python 启动错误。
架构影响
不修改 REST API 契约。
不修改数据库 schema。
不改变后端 FastAPI / daemon / shared-state 架构。
本次变更仅聚焦 Windows/Tauri 启动与前端就绪竞态修复。
验证
npm run test:tauri-readinessnpm run buildnpm run tauri:dev/health返回 200/api/v1/novels返回 200/api/v1/llm-control/prompts/stats返回 200Summary by CodeRabbit
New Features
Bug Fixes
Tests
Chores
Refactor