Files
rond-with-you/.workbuddy/memory/2026-09-28.md
T
tamakyi 3f1b737a78 记忆:把 .workbuddy/memory 纳入版本控制
用户要求这份记忆跟着仓库走,而不是只留在本机。

.gitignore 里挡路的是 `.workbuddy/`。改成反忽略六行,并且**只放行 .md**:
别用 `!.workbuddy/memory/**`——它会把 memory 下任何文件都放行,本项目真发生过
`web.test.exe` 混进暂存。复核用 `git check-ignore -q --no-index` 逐条打靶:
memory/*.png、memory/sub/x.md、memory/MEMORY.md.bak、plans/a.md 全 IGNORED,
17 个 .md 全 included;`git add -An .workbuddy/` 恰好 16 个文件。
其余 .workbuddy 内容(计划、本地设置、临时产物)继续忽略。记忆里没有真凭据
(唯一命中是 2026-09-14.md 里的 `password=rond-admin`,那是项目默认口令)。

顺带做两件整理:

1. 取证了注入上限。以前按「字节数」判断 MEMORY.md 是否超限是错的:
   WorkBuddy 的 `getWorkingMemoryContent()` 用 `MAX_MEMORY_CHARS = 1e4` 比 JS
   `content.length`,即**字符数**(中文 1 字算 1);超了 substring 截断、尾部静默丢失。
   本 details 与每日日志都不参与注入,只有 MEMORY.md 受约束。
   现已从 6604 字符收到上限的 66%,并把这条上限写进索引抬头。
2. 把「备份合并」「导出/恢复三条铁律」两段长文迁进 MEMORY-details.md 的卅一/卅二节,
   索引只留关键词指针。行尾统一为 LF(仓库 .gitattributes 是 `* text=auto eol=lf`)。
2026-09-30 18:31:08 +08:00

79 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-09-28
## rondbackup「差异分析 → 合并执行」实测(真机导出包 20260928_183908)
环境 `conf/app.ini`(库 `rond_with_you`、`data_dir=storage/data`、:8080),数据集 id=9;
把真机新导出的 `data/20260928_183908.rondbackup`(955 到访 / 432 地点)合并进已有 867 到访的数据集。
**主链路生效**(真跑路由 + 站长登录):`POST /admin/preview` → `GET 差异页` → `POST /admin/preview/{token}/merge`,
`add_new=1`、冲突留默认 ⇒ 新增 70 地点 / 1 活动 / 134 到访,随到访带入 24 位移 / 32 天气;
到访 867→1001、地点 368→438、位移 791→815、天气 557→589;**孤儿位移 0、无地点到访 0、重复 UUID 0 组**。
逐条核对:备份里 953 条带 UUID 的到访全部命中且**到达时间微秒级零误差**;站内 2 条 app 已删的 UUID 到访被正确保留。
合并后 12 个关键页全 200。报告:`storage/.mergetest/报告-rondbackup合并实测-20260928.md`
**三个问题**
1. 🟠 同名同坐标的重复地点**不幂等**:`DiffPlaces` 的 `byCoord`/`byName` 是单槽位 map + `used[]`,
同一坐标下 N 条备份地点最多只能配 2 条 ⇒ 第 3 条永远判「备份新增」,每次合并多插一条。
实测备份里同名同坐标共 10 组、只有「柳州站」count=3 ⇒ **每次合并固定多 1 个重复地点**,差异永不归零
(438→439→…)。修法:多值 map 逐条消费 + `mergePlaces` INSERT 前按 `PlaceCoordKey` 去重兜底。
2. 🟠 到访冲突被「活动改名」放大:`visitFieldChanges` 比的是**活动名称**不是身份;16 个活动改名
(餐厅→吃饭…)⇒ 355 条到访冲突全部同因(库内挂在改名活动下的 378 条 − 站内独有 23 条 = 355,精确吻合),
活动类型本身只报 16 条。且对到访选「用实机」**不会**改活动名(`activity_id` 按 UUID 映射回同一个)
⇒ 冲突永不收敛。修法:到访层面按活动身份(`ZUID_`/id)比对,改名只在活动层报一次。
3. 🟡 21 个**长沙**地点两边坐标差 661~748m,实测 20 条满足「站内坐标 == 反纠偏(备份坐标)」
⇒ 坐标系不一致(站内那侧被多做了一次 GCJ→WGS 反向换算);**差异页冲突的默认选项是「用实机」**,
直接点合并会把这批坐标覆盖、地图上跳 ~660m。另有 分类 12 处(站内中文标签 vs 备份 `MKPOICategory*` 原始枚举)。
**备份 / 回滚**:`storage/.mergetest/rond_with_you-before-20260928.sql`(整库 pg_dump);
差异页快照 `diff-before-merge.html` / `diff-after-merge.html`。库内最终状态 = 合并一次(我重复跑测试
多插的 1 个重复地点已回退)。
**方法(可复用)**:临时 Go 测试(`package web`,才能拿到非导出的 `srv.captcha`)——
`httptest.NewServer(srv.Handler())` + cookie jar + **`CheckRedirect: http.ErrUseLastResponse`**
(站点用 303,必须关掉自动跟随才能读到 Location);验证码答案直接读 `captcha.Store.Answer(id)`,
绕开图像识别;管理员口令是默认 `rond-admin`(pbkdf2 可离线核对)。跑完删掉测试文件,`git status` 保持干净。
## 同日晚:修复 + 复测(回档数据库后重跑)
按上面前两条问题改了代码,**5 个文件 +172/−22**:
1. `internal/rond/diff.go` `DiffPlaces`:`byCoord`/`byName` 由单槽位 map 改为**多值索引**(`map[string][]StoredPlace`),
抽出 `take()` 逐条认领未使用的候选(认领即置 `used`)。`classify` 不再自己置 used。
2. `internal/rond/diff.go` `visitFieldChanges`:到访的「活动」改为**按身份**比(新增参数 `actUUID`),
两边都缺 UUID 才退回名称;`DiffVisits` 传入 `b.ActivityUUID`。`StoredVisit` 新增 `ActivityUUID`。
3. `internal/web/backup_diff.go` `loadStoredVisits`:多 JOIN 一次 `entity_raw`(`entity='ZACTIVITY'`,按 `a.src_pk`)取 `ZUID_`。
**复测结果**(回档 → 重跑同一份备份,同环境):到访冲突 **253 → 6**(247 条活动改名假冲突消失);
地点新增 **70 → 67**,合并后再比对 **地点新增 1 → 0**;再点合并由「再插 1 个重复地点」变成
「**按当前的选择没有要写入的改动,数据未变**」,地点 435→435、「柳州站」稳定 3 条。
到访 1001 / 位移 815 / 天气 589 与修复前一致;953 条 UUID 到访全命中、到达时间 0 偏差、孤儿位移 0。
**新增守护**:`TestDiffPlacesAlignsDuplicateNameCoordGroups`(含「站内真少两条仍报新增 2」反向断言)、
`TestDiffVisitsIgnoresActivityRename`、`TestDiffVisitsReportsActivityChange`、
`TestDiffVisitsActivityWithoutUUIDFallsBackToName`;`TestBackupDiffIdentical` 补地点新增断言、
两个 merge 幂等用例补「地点差异归零」。**变异验证**:把两处改回旧逻辑,对应用例确实全挂
(⚠️ 变异要忠于旧行为:旧 `byCoord` 是 map「后者覆盖前者」=取最后一条;我第一次写成「取第一条」,
误报 8 条,与真实旧行为不符,改用 `byCoord[k] = []StoredPlace{sp}` + `byName` 只留第一条才复现出正确的 1 条)。
**仍未动**:21~24 个长沙地点坐标系差 ~660m(默认「用实机」会覆盖站内;现连带 25 条到访「地点」冲突)、
分类 12 处中文标签 vs `MKPOICategory*` 枚举、16 个活动改名需选「用实机」才同步。
报告已更新为「实测 → 修复 → 复测」完整版。
## 收尾:回滚 + 推送 + 重建(19:35)
- **回滚**:`DROP/CREATE rond_with_you` + 恢复 `storage/.mergetest/rond_with_you-before-20260928.sql`,
核对回到合并前基线 867 到访 / 368 地点 / 31 活动 / 791 位移 / 557 天气 / 2538 留档 / 柳州站 3。
- **推送**:提交 `10d23e0`(5 文件 +172/−22),`git push origin master`(远端 `git.shiroko.one`)。
本地原本就**领先远端 8 个提交**,一并推上去了(`78865fc..10d23e0`)。
⚠️ **推送凭据陷阱**:`~/.git-credentials`(store)与 GCM 里缓存的 token **都已失效**
(`remote: Failed to authenticate user`),SSH 22 端口 `Network is unreachable`(只有 HTTP 代理
`http.proxy=http://100.104.48.63:11882` 能通)。**别加 `GIT_TERMINAL_PROMPT=0`**——它会让 GCM
直接放弃认证;去掉这个变量、让 GCM 自己走一遍交互认证即可成功(13 秒完成)。
- **重建**:`go build -o bin/rond.exe .`(`bin/rond.exe` 原本还是 9-20 的旧构建,不含修复;
旧文件留了副本 `bin/rond.exe.bak-20260920`)。只读冒烟:`/healthz` `/` `/admin/login` 均 200、
`/admin` 303,随后 `taskkill //F //IM rond.exe` 停掉、8080 释放,库内计数不变。
- 注意:`go build` 那次 `bin/rond.exe~`(9-20 12:14 的更旧副本)不见了;原因未查明,
两个都是修复前的旧产物,无实际损失。