From d1692d7b108cb0c4cf6880f0d1829348eb7a3995 Mon Sep 17 00:00:00 2001 From: snowy-yang <102860110+snowy-yang@users.noreply.github.com> Date: Wed, 23 Sep 2026 20:54:13 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=AE=B0=E5=BF=86=E4=B8=8E=E9=AA=8C?= =?UTF-8?q?=E6=94=B6=E6=8A=80=E8=83=BD=E8=B7=9F=E4=B8=8A=E2=80=94=E2=80=94?= =?UTF-8?q?=E5=AD=98=E6=A1=A3=E6=94=B9=E5=8A=A8=E5=BF=85=E9=A1=BB=E7=9C=9F?= =?UTF-8?q?=E6=9C=BA=E9=AA=8C=E8=87=AA=E5=8A=A8=E8=90=BD=E7=9B=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 单元测试抓不住 autosave 失效(dirty 被高频清零这件事测试里复现 不了真机时序),所以技能里加一条:动了存档/会话层,必须 0 客户端 在线做一个动作、等 60s、直接查库那一行有没有更新。 --- .workbuddy/memory/2026-09-23.md | 30 +++++++++++++++++++ .workbuddy/memory/MEMORY.md | 3 ++ .../simplefarm-verify-and-ship/SKILL.md | 5 ++++ 3 files changed, 38 insertions(+) create mode 100644 .workbuddy/memory/2026-09-23.md diff --git a/.workbuddy/memory/2026-09-23.md b/.workbuddy/memory/2026-09-23.md new file mode 100644 index 0000000..b45b835 --- /dev/null +++ b/.workbuddy/memory/2026-09-23.md @@ -0,0 +1,30 @@ + +## 2026-09-23 修网页版「点播种没反应」+ 顺手挖出自动存档失效的真 bug + +- 用户报网页版点播种没反应。服务端 plant 动作完全正常 → 定位到前端。 +- **根因 1(前端)**:播种按钮走 `window.prompt` 选作物,重置走 `window.confirm` + ——这俩在 sandbox iframe(内置预览面板)里会被**静默屏蔽**:不弹窗、直接返回 + null,于是"点了没反应"。普通浏览器里是好的,所以一直没暴露。 + 修复:改成页面内弹层(`#pickmask` + `openCropPick`,未锁定作物生成按钮 + + 取消按钮,点遮罩关闭);重置改两步确认(第一次点变"再点一次确认",3 秒还原)。 +- **根因 2(后端,真 bug)**:顺着验证存档时发现 `default` 牧场**根本没写进库** + ——内存里每次重启都是新档。排查出 `session.dirty` 一个标志位伺二主: + Publisher 推送循环每 ~250ms 就把 dirty 清掉(无人在线时更是直接清), + 而 `autosave()` 每 60s 才看一眼 dirty——窗口太小,**自动存档几乎永远不触发** + (约 0.4% 撞率)。进度只在手动存档/优雅停机时落盘,进程被强杀就全丢。 + 修复:新增 `session.unsaved` 专管「有变化没落盘」,`_on_change`/`load`/ + `touch`/`advance_day` 置位、只有成功的 `save()` 清位;`autosave()` 改看 + unsaved;dirty 继续只管推送节流。hub.py 的推送循环一字未动。 +- 两只旧测试(`test_autosave_skips_when_clean` / `..._writes_when_dirty` / + `autosave_all...`)直接操纵 dirty 驱动 autosave,固化了被修掉的耦合—— + 全部改成用 unsaved 表达意图。新增回归测试: + 「推送循环清了 dirty 也要照常落盘」「没有新变化不重复写库」。 +- **真机验证**:0 客户端在线、纯 HTTP 种一块地,70 秒内看到库行更新 + (gold 120→112,种子钱都对得上)。 +- 门禁:ruff/pyright 全绿,515 项测试 OK(skip 35)。 + +### 教训 +- 嵌入预览面板里 `window.prompt`/`confirm`/`alert` 都不可信,交互一律用页面内 + 弹层实现。 +- 一个布尔标志被两个子系统共用时,其中一个高频清零,另一个低频读取—— + 低频的那个等于瞎了。存盘依据必须由"成功的写入"来清。 diff --git a/.workbuddy/memory/MEMORY.md b/.workbuddy/memory/MEMORY.md index 8eb5538..b8e4feb 100644 --- a/.workbuddy/memory/MEMORY.md +++ b/.workbuddy/memory/MEMORY.md @@ -74,6 +74,9 @@ TUI 是纯客户端,同时开放 HTTP + SSE 接口给 AI/脚本调用(服务 方向键/空格/Tab/ESC 全部静默失效(字母键正常,所以很难注意到)。 动 `farm/keys.py` 时,`farm/tui.py` 里所有具名键引用都要跟着核一遍。 读不到键盘时(管道 / 非 Win32 控制台)TUI 会用 `reader.reason` 在底部明示原因。 +- **session 的 dirty / unsaved 必须分开**:`dirty` 只管推送节流(Publisher ~250ms 清一次), + `unsaved` 才是自动存档依据(只有成功的 `save()` 清它)。别再合并——合回来 autosave 就瞎了。 + 网页前端禁用 `window.prompt`/`confirm`(嵌入预览里被静默屏蔽),交互用页面内弹层。 - **多牧场**:一个 farm = 一个玩家 = 库里一行(`saves/farm.db`)+ 一个引擎 + 独立时钟; 时钟由**一个** `FarmClock` 线程统一驱动,不要给每个牧场开线程。 - **存档类动作**:`save` / `reset` 必须由会话(FarmSession)处理, diff --git a/.workbuddy/skills/simplefarm-verify-and-ship/SKILL.md b/.workbuddy/skills/simplefarm-verify-and-ship/SKILL.md index c9eb9d6..035ef0c 100644 --- a/.workbuddy/skills/simplefarm-verify-and-ship/SKILL.md +++ b/.workbuddy/skills/simplefarm-verify-and-ship/SKILL.md @@ -64,6 +64,11 @@ uv run main.py serve --port 8799 --tick 3 --save-dir .e2e-saves 动作回执带新字段、错误码符合预期(如 `nothing_to_sell` / `locked` / `unknown_building`)。 错误码去 `farm/engine.py` 里核对,别猜。 +**动了存档/会话层必须加验一条**:只走 HTTP(不连 SSE、0 客户端在线)做一个动作, +等 60 秒自动存档周期过去,直接查 `saves/farm.db` 那一行有没有更新。 +单元测试抓不住 autosave 失效——2026-09-23 就漏过一次:`dirty` 被推送循环高频清零, +autosave 60s 才看一眼,等于永不存盘,全量测试照样绿。 + ## 第 3 步:写记忆 - 当天日志追加到 `.workbuddy/memory/YYYY-MM-DD.md`(append-only)。