snowy-yang
|
6268283fc6
|
chore: 存一个「改完怎么验收怎么交付」的技能,省得下次重新踩
本次踩到两个纯靠猜出不来的坑,都写进技能里了:
* `RemoteFarm.snapshot()` 是本地缓存不发请求,非实时脚本轮询会一直读到同一份
旧数据——服务器在推进却看起来像卡死,排查花了十几分钟。
* `git push` 报远端 `Failed to authenticate user` 其实是本地 GCM 拒绝交互
(`Cannot prompt because user interactivity has been disabled`),
加 `GCM_INTERACTIVE=always` 就好;再不行 `GCM_TRACE=1` 看日志。
验证推送成功要看 `git ls-remote`,`git status -sb` 的 `[gone]` 是沙箱
丢 `.git/refs/remotes/**` 写入导致的假象。
技能放在 .workbuddy/skills/simplefarm-verify-and-ship/,覆盖四步交付流程:
门禁 → 真机端到端验收(预置存档 + 后台任务起服务器)→ 写记忆 → 提交推送。
|
2026-09-19 17:06:15 +08:00 |
|
snowy-yang
|
cf24cb43f1
|
feat: 加天气 / 谷仓 / 建筑三套系统,收获改为入库 + 择时卖出
动手前先修掉了两处「死机制」,新增的三个系统各自激活一个:
* 天气:小麦 2 天成熟、初始水分 2、每天只掉 1 点,等于永远不需要浇水。
现在水分消耗由天气决定(晴 1 / 多云 0 / 干旱 2,雨天反补 3),而
`state.forecast` 把明天的天气提前告诉你——「今晚浇不浇」成了能算的决策。
5 种天气、权重合计 100、同种不连出;暴风雨会毁未熟作物,温室免疫。
* 谷仓:市场波动原本纯装饰(收获当场按市价变现,没有择时空间)。现在收获与
收取只进 `state.inventory`,靠新增的 `sell` 换金币,价格波动才真正参与决策。
仓满时 `_store()` 抛 `granary_full` 且不改状态,作物原样留在地里。
* 建筑:新增 `build`,买的是「以后不用天天手动做」——水井自动浇干地、
喂食器自动喂食(只吃库存饲料,绝不自动花钱)、温室、谷仓扩容。
每天在 `advance_day` 里先于生长执行,同类越造越贵(×1.6)。
存档升到 `SAVE_VERSION=2`,配 `MIN_SAVE_VERSION=1` 向后兼容,旧档缺的新字段
一律走默认值,不作废玩家手里的档。`advance_day(weather=...)` 是留给测试的
确定性入口。
顺带修两个真 bug:
* `daemon.py` 三处 `makedirs(os.path.dirname(path))`:`--pid-file pid.txt`
这种只给文件名的用法会让 dirname 变成空串,启动阶段直接崩。抽出
`ensure_parent_dir()` 兜底。
* `RemoteFarm.snapshot()` 是本地缓存,非实时客户端轮询会一直读到旧数据,
看起来像服务器卡死。补 docstring 指明要自己调 `refresh()`。
质量门禁:ruff format/check 全绿、pyright 0 错误、503 项测试 OK(skip 35)、
bench 无回归(brief 19.5µs / snapshot 48.5µs / POST full=1 400µs)。
另起真实服务器(--tick 3 + 预置存档)跑了 38 项端到端验收,全过。
|
2026-09-19 17:02:33 +08:00 |
|
snowy-yang
|
a92c29ead2
|
docs: 记录发布到 gitea.4i.hk 的仓库地址与推送认证方式
远端仓库:https://gitea.4i.hk/hhhge/SimpleFarm(public,main)。
关键坑:非交互 shell 里必须 GCM_INTERACTIVE=always,否则 GCM 会因为「用户交互被禁用」
直接拒绝,git 报 could not read Username / Failed to authenticate user,看起来像认证配错了。
另记:tea 的 OAuth token 能建仓、能调 API、能加 SSH 公钥,但不能签发访问令牌
(POST /users/{u}/tokens 需要 basic auth)。
|
2026-09-19 13:33:48 +08:00 |
|
snowy-yang
|
450655bebf
|
chore: 补记本机两个仓库/环境坑(Git Bash 缺 cat、.gitignore 的 /_* 规则)
提交多行 message 时用 git commit -F <文件>,不要用 $(cat <<EOF) —— 本机没装 cat,
git 会因此认为消息为空并 abort。
根目录以 _ 开头的文件被 .gitignore 的 /_* 静默忽略:临时脚本因此不会污染 git status,
但放错名字的正经文件也会被吞掉。
|
2026-09-19 12:56:07 +08:00 |
|
snowy-yang
|
f8e093c98f
|
feat: 实时通道改 SSE、服务器换 uvicorn+zttp、节奏改 40 分钟一天,并重建测试与优化热路径
三件事凑成一次不兼容变更:WebSocket 通道、Hypercorn 服务器、「手动推进一天」都消失了。
实时通道:WebSocket -> SSE
- hub 改为 SSE 订阅池,GET /api/stream 单向推送 welcome/state/day/notice/bye,
250ms 节流 + 15s 心跳注释;写操作一律走 HTTP POST /api/actions,不再有 WS 回执。
- 停机三处一起修才干净:close_all() 发 bye + timeout_graceful_shutdown 兜底 +
客户端 stop() 直接 shutdown 底层 socket(光 close() 会等读线程的锁,最长卡到下次心跳)。
- 网页版改 EventSource + fetch;tools/ws_demo.py 换成 tools/sse_demo.py。
服务器:Hypercorn -> uvicorn + zttp 的 HTTP/2
- 同一端口同时说 HTTP/1.1 与 HTTP/2。uvicorn 0.53 不再内置 h2,必须装 zttp 并显式
http="zttp",否则 Config.load() 直接 raise。
- 信号接管改成子类覆盖 Server.handle_exit(0.53 没有 install_signal_handlers 可覆盖),
不调 super()、不写 _captured_signals,退出时不重放信号;第二次信号强制退出。
- 入口启动前用 daemon.port_busy() 自检端口:Windows 上 SO_REUSEADDR 会让第二个实例
「绑上」已监听端口并抢走新连接,所以判断「能不能连上」而不是「能不能 bind」。
- 修掉 clear_pid_file 的 Windows 真 bug:文件还开着就 os.remove 会失败且被
except OSError 吞掉,导致每次正常退出都留 stale pid 文件。
节奏:一天 15 秒 -> 40 分钟,并整体移除「休息」
- DEFAULT_TICK_SECONDS=2400,MAX_TICK_SECONDS 放宽到 86400(旧上限会把 40 分钟夹回去)。
- engine.sleep / SLEEP_COOLDOWN / /api/sleep / TUI 的 [N]休息 / 网页版按钮全部删掉,
时间只能自然流动;机器人改为 wait_for_next_day(timeout),CLI 加 bot --wait。
- 时长显示统一走 models.format_duration(),不再出现 2399.6s 这种写法。
键盘:修复方向键/空格/Tab/ESC 全部静默失效
- keys.py 的具名键统一小写并成为唯一来源,tui.py 一律引用 keys.XXX 而不手写字符串
(历史 bug 就是 Windows 侧产大写 UP、TUI 里写小写 up,字母键正常所以很难发现)。
- 顺带修 POSIX 路径一次 os.read 只取首字节、其余按键静默丢弃的老毛病。
测试:重建 500 项
- test_engine 110 / test_actions 35 / test_server 131(skip 35) / test_tui_keys 75 /
test_bot 36 / test_client 113。未安装的第三方依赖用 skipUnless 自动跳过,其余照跑,
所以是全绿而不是靠跳过伪装。
- 被测试逼出两处真隐患并修掉:RemoteFarm._normalize() 必须「先补 scheme 再削尾斜杠」
(顺序反了畸形端点会溜到系统代理、报一个和真实原因无关的 502);StateView 空视图要
预置 plots/animals,否则连不上服务器时读 state.plots 直接 AttributeError。
优化:先量后改,依据与前后数字都在 tools/bench.py
- 新增 engine.brief_source():brief/summary/动作回执只要十几个标量,不再为它们序列化
整局;to_dict 里 net_worth 只算一次。brief -76%、summary -77%、leaderboard(32) -78%、
GET /api/world -31%、POST /api/actions?full=1 -16%。
- 拆掉三个巨型函数:create_app 475->32 行(_Services + _register_*)、
build_parser 与 available_actions 分组、__main__.main 160->7 行。
- 消除重复:等级门槛 5 处 -> _is_locked/_require_level/_newly_unlocked;hub 的
broadcast/broadcast_all/close_all -> _push_many;daemon.probe/world -> _get_json;
main.py 的 _add_endpoint_args/_leaderboard_lines。
- tools/bench.py 作为长期性能守卫(含调用计数器),谁把轻量路径改回 snapshot() 会立刻报警。
门禁:ruff format/check 全绿、pyright 0 错误、500 项测试 OK(skip 35)。
|
2026-09-19 12:55:45 +08:00 |
|
snowy-yang
|
47dd818360
|
docs: 记录规则生效的探针反证过程
复核 ruff/pyright 规则是否真的被加载:排除所有可能覆盖 pyproject 的配置文件,并用故意违规的探针文件确认精确命中。
新知识:X | None 的规则码在新版 ruff 是 UP045 而非 UP007;SIM105 对 try 体含 return 的情况有意不报,所以'没报出来'不等于'规则没启用'。
|
2026-09-18 21:32:46 +08:00 |
|
snowy-yang
|
52004571c8
|
docs: 记录工程化改造(uv + ruff + pyright)的决策与踩过的坑
日志补记下半场:三件套的落地细节、行宽为何定 100、PTH 为何不开、pyright 两个关键开关;新增三条可复用环境坑(write_text 会把 LF 变 CRLF、同文件多次 Edit 互相覆盖、git status 与 git diff 不一致的判别)。
项目长期约定同步更新:运行方式改为 uv 单一可信来源,新增代码风格约定与质量门禁命令。
|
2026-09-18 21:28:20 +08:00 |
|
snowy-yang
|
a7725f8812
|
chore: 工程化改造前的基线快照
在引入 uv 依赖管理、ruff 静态检查与格式化、pyright 类型检查之前,
先把当前可正常运行的状态固定下来,作为后续大规模自动改动的回滚点。
此时状态:97 项测试全绿,HTTP + WebSocket 多牧场服务器可用。
|
2026-09-18 21:12:40 +08:00 |
|