22,468 字
113 分钟

一个开源项目里的两套寻路:从“明知已有,另起一套”到“引流用户”

文章

MaaEnd 地图定位与寻路事件举证与论证

省流:MaaEnd 是一个游戏自动化工具。仓库里有两套地图导航——我的 MapNavigator(C++)和 isHarryh 的 MapTracker(Go)。两套不是同时立项的。我先完成了整套系统的研究且已准备好合并,对方以研究纯 CV 为由取得素材和技术细节,随后另起一套竞争实现并推动先行合并。从推动先行合并,到替换业务代码,再到形成单向的 AI 入口,是同一条链上的逐步升级。

核心问题:Go 侧的导航方案及后续操作,是正常的多方案共存,还是有目的性的持续性恶意竞争?

时间线

  • 02/04 — 我发布了 C++ 定位寻路方案(#352)的第一个演示视频。
  • 02/05 — 提交 PR #352,在群内公开技术细节和性能数据。
  • 02/14 — isHarryh 在群内索取我的地图素材和技术路线。当晚基于取得的同一素材启动纯 CV 实现与批测,次日建立 #560,连夜提交测试集,并以”为什么选择我们”的对比宣传文案将其包装为不需要训练数据的替代。
  • 02/15 — 两份 PR 被放在负责人面前。isHarryh 发布题为”为什么选择我们”的对比宣传卡片。负责人两次要求”等等 cpp agent”。一小时后,#560 被合并进主线。
  • 02/16 — #352 被关闭。

以上是起始节点。此后五个多月里,以下节点上出现了同方向的重复行为:

  • 测试邀约 — C++ 侧排障用户被分钟级定向拉去试用 Go 方案。
  • 特性跟进 — 点名的功能当晚紧急补齐,与关于 ADB 顺序的明确表态直接矛盾,削弱了”不内卷”的可信度。
  • 亦步亦趋 — NavMesh 和 Web 化两次事件中,Go 侧在十天尺度内跟随 C++ 侧的实施方案。
  • 故障拉踩 — 竞品卡死时发起对比。
  • AI 被提示词引流到其中一边 — 项目级 AI Agent SKILL 文件仅配置 Go 方案,持续 52 天,影响开发者使用 AI 工具的初始检索方向。

结论先行

这不是偶然的多方案共存,而是有目的性的持续性恶意竞争。

02/14 对方已获取 C++ 方案全部技术细节,02/15 #560 在负责人两次要求等待后被先行合并。公开场合给出的”没法用”三条理由,被压测数据、代码审查和 26 条讨论记录逐一推翻。#1283、03/16–17 的即时宣传 Go 方案发生在 C++ 排障期间,03/22 Go 侧在 C++ 公开特性后当晚补齐对应功能,当事人的公开自述与 Git 提交记录直接矛盾。#3125 建立兼容层后仅 27 小时,#3155 建立反向兼容层;NavMesh #3084 → #3359 和 Web 化 #4172 → #4461 间隔分别为 13 天和 10 天。#3830 引入 TurnNode 后 #3855 直接替换业务代码中的 C++ 动作。#3208 单独加入 Go Skill,52 天后才出现对等入口,同提示词复现的首轮选择随入口配置变化方向。

在已知 C++ 方案(MapNavigator)存在且可审查的情况下,Go 侧(MapTracker)持续建立功能重叠的另一套实现。#1283 与 03/16–17 在 C++ 测试排障时即时宣传 Go 方案;03/22 在 C++ 特性公开后当晚补齐;#1487 在公开承诺 C++ 先行之后仍抢先合并 Go ADB。#3125 建正向兼容层 27 小时后即出现反向入口 #3155;NavMesh 和 Web 化均以 10–13 天延迟跟随 C++ 方案。#3830 引入 TurnNode 后 #3855 直接替换业务代码中的 C++ 动作。#3208 单独加入 Go Skill,52 天后才出现对等入口,同提示词复现的首轮选择随入口配置变化方向。

不建立在代码抄袭或恶意推断之上。项目管理端在选型评估与合并监督上的缺位,同样是导致这场长期内耗的重要因素。

一、明知已有,另起一套 — #352 建立后,对方获取了核心素材、技术路线、性能数据和地图参数,随后才立项 #560。

二、抢先合入 — #560 建立当天即与 #352 并排置于负责人面前,通过对比性宣传文案推动优先合并,当晚合入主线;#352 次日关闭。

三、方案能用 — 事后说法是原方案”写了但没法用”。压测数据与后续 26 条公开讨论记录均不支持这一说法。

四、定向引流 — #1283 创建后 60 秒内 @正在排障 C++ 的用户,合入同一分钟再次 @通知;03/16–17 在 C++ 排障线程中直接宣传 Go 方案;04/08 用户求助 MapNavigator 的具体报错、作者已被 @ 到场,回应是“操作是换成 MapTracker”。宣传方与方案作者为同一人。

五、言行不一 — 2 月至 7 月,四个独立节点上出现同一方向的行为。关于 ADB 支持顺序的明确表态与后续 Git 合并次序直接矛盾,“保持良性竞争,不内卷”这一自我描述的可信度被削弱。

六、公开对质 — 04/19 对质中对方的三项辩解,逐一被平台时间戳推翻。在场成员确认客观上形成了竞争;对其主观动机,看法不一。

七、亦步亦趋 — 兼容层:C++ 建 #3125,仅 27 小时 Go 建反向兼容层 #3155。NavMesh:C++ 原生实现 #3084 公开 13 天后 Go 侧提交同名 PR #3359。Web 化:C++ 提方案 #4172 10 天后 Go 侧跟进 #4461。TurnNode:#3830 引入后 #3855 将业务路径切换至 MapTracker TurnNode。同方向的跟随模式在四次事件中仍然成立。

八、引流用户 — 项目级 Skill 文档仅配置 Go 侧方案,持续 52 天(05/28—07/20)。以 #3208 末位提交加入,对称性恢复后对方反向指责”抄袭”。


1. 明知已有,另起一套

要验证 #560 是否属于独立开发,先确认一件事:#560 建立前,其作者是否已经知道 #352 的存在、技术路线、核心素材和开发进度。

如果事先知情,“互不知情”这个理由就站不住。

记录

02/04 19:30 — 运行成果首次公开展示。

Lemon-miaow 在群里发送游戏内实机运行视频及带路径点标注的地图截图。群成员讨论“原来是玛丽在拉”、“这下只需要路径点了”。isHarryh 在同一对话序列中发言。

02/04 19:30 最初的演示与群内反应

02/05 15:45—16:21 — #352 建立并公开解答技术细节。

定位运行结果截图公开,包含坐标 (985.5, 1125.0)、Score=0.86 及 Time=612.9ms。15:46,Lemon-miaow 贴出 PR #352 链接。 PR #352 完整页面

在群内讨论中,Lemon-miaow 解答了关于特征过滤的提问,并公开了性能数据:“目前大概差不多平均一次 120ms”。

IMG_7162.JPG 02/05 群内公开 #352 并回答准确率与耗时

02/14 19:26 — 介入同一命题并提出否定评价。

isHarryh 引用 Lemon-miaow 前一日发送的网格图,发表意见:“这种小地图匹配不应该直接模板匹配”、“高度同质化的小地图送到图像分类模型效果应该是不行的”。

IMG_7153.JPG

02/14 19:39—19:45 — 直接索取素材并获知架构路线。

  • isHarryh 提出:“能给我发一下解包图吗?我研究一下纯 CV”。Lemon-miaow 在群内发送了 map.zip(134.07MB) 压缩包。
  • isHarryh 发言:“先立个 flag 在这,我感觉纯 CV 是可以定位的”。
  • Lemon-miaow 详细说明了自身使用的底层架构:“总之我确信上 yolo 匹配哪张模板图再去 SAD 又稳又快”、“我现在做的就是这套”,并贴出耗时日志(第一轮搜索 152ms 后进精搜,后续帧 33-46ms)。
  • isHarryh 当即回应:“哇 还有分级搜索”。

核心素材由对方打包提供,架构路线在同一对话中被完整披露。

02/14 索取解包图、map.zip 与两阶段方案同屏

02/14 20:33—20:38 — 地图缩放系数双边核对。

isHarryh 询问拼接规律;Lemon-miaow 说明系手写脚本拼接,并需缩放为 0.16x。isHarryh 回复:“对,我比较过了”、“我测的是 0.1625”。

02/14 地图拼接与缩放系数的交换

02/14 21:13 — 03:51 — 获取素材后连夜启动同类实现与批测。

  • 21:31,isHarryh 宣布“我寻思纯 CV 定位是可行的”。23:50,发布 159 张测试集的压测结果(平均 15ms,成功率 91.82%),并断言“理论上现在已经具备实现自动走路送货的条件了”。
  • 02/15 01:10,Go 方案提交测试集 PR。01:15,Lemon-miaow 再次贴出 #352 链接并声明:“我都写完了”。isHarryh 随后追问 Accuracy,并提出“举办一个 YOLO vs CV 定位识别大赛”。
  • 03:35—03:51,Go 侧上传演示视频,并在说明中进行对比宣传:“与 yolo 相比的优势,小地图定位无需搜集数据集、无需训练模型”。同时自述局限:“但是这些小地图方案很难处理高度”。
02/14 纯 CV 测试与已知区域名前提 IMG_7157.JPG 02/14 23:50 159 张测试集批测结果 02/15 01:10—01:22 测试集、#352 再次公开与YOLO vs CV 大赛 02/15 03:35—03:51 演示视频、优势比较与高度局限 02/15 凌晨 遮挡与用户建造干扰的讨论

推论

四条事实:

  1. 知道 PR 存在:#352 于 02/05 公开,02/15 被当事人直接引用并追问。
  2. 知道技术路线:YOLO 前置、SAD 精搜、分级搜索的架构在 02/14 被完整交代。
  3. 拿到核心素材:map.zip 由对方直接提供,缩放参数经过核对。
  4. 知道开发进度:“我都写完了”与对方追问同屏,#352 标记为 Ready。

反方的解释检验

反方的解释对应的记录
“不知道有这个 PR”02/05 在群内公开,02/15 01:15 被当事人直接引用。
“不知道对方进度”“我都写完了”与对方追问同屏,#352 处于 Ready 状态。
“碰巧同期做了相同的事”核心素材由对方提供,缩放系数经双边对齐,架构路线在同一对话中被完整披露。

小结

“互不知情独立开发”被客观记录否决。 后续所有争议均发生在 Go 侧已充分掌握 C++ 侧方案信息的前提下。

注:知情本身不构成违规。本章只确认信息对称前提,行为性质在下一章展开。


2. 抢先合入

确认事先知情后,核查合并流程:#560 是作为常规技术验证接受评估,还是在负责人明确要求等待的情况下,通过竞争性包装和群内推动促成 #560 先行合并,并由此导致 #352 被关闭。

记录

02/15 17:54—19:12 — 并排对比与宣传。

项目负责人在群里贴出两个 PR(#560 与 #352)询问区别。 PR #560 完整页面 isHarryh 在群里发布宣传卡片并 @项目负责人,标题为“为什么选择我们🔥——对比 Yolo 方案的七大优点💡”。

项目负责人当场提出质疑:“不对啊,另一个 PR 也不是 yolo 啊?”Lemon-miaow 澄清:“yolo 仅返回 zoneid,本质还是 cv”。宣传卡片将对方描述过度简化。

在此屏截图中,项目负责人已在 GitHub #560 页面留下明确评论:“等等 cpp agent 啊”。

02/15 #560 与 #352 同屏、为什么选择我们七大优点、群主追问、等等 cpp agent 啊

19:27—19:29 — 管理者重申等待评估。

项目负责人明确说明准备搞两个 agent:“就是我准备搞两个 agent,现在不是只有一个 go 吗,我准备再搞一个 cpp 的”、“等我晚点看看()”。isHarryh 发布修改后的卡片(六大优点),并向负责人承认:“不行,我这个 CV 有魔改”、“不是标准实现”。

IMG_7180.JPG

20:09—20:34 — 催促合并。

在负责人两次要求“等一等”后,isHarryh 在群内发送 #560 链接,连发“准备完毕”、“测试无误”,并在 20:30 发言:“合完立即开干送货”。群内随后出现“合!”、“加速!”等催合言论。面对有人询问“那檬喵的怎么办”,isHarryh 回复:“也可以合,爱用哪个用哪个”。

代码于 20:34:21 被合并(操作人:dongwlin)。此时距离负责人要求“等等 cpp agent”仅过去约一小时。

02/15 20:09—20:31 #560 Ready、测试无误与合完立即开干送货加速!

20:40—20:51 — 先行合并的既成事实。

20:40,项目负责人回到群里表示:“草,怎么都已经和过了”。因先行合并已经造成主线上的既成事实,20:51,负责人告知 C++ 作者:“你的 PR 只能关了啊,这是是我的锅,cpp agent 拖了好几天,没想到还能再出一个 PR”。

20:54,isHarryh 承认:“我没有做分层地图”,21:01 确认:“Yolo 仍是目前唯一能分层的方案,不能丢”。

02/15 20:40—20:51 怎么都已经和过了与你的PR只能关了啊 02/15 21:01 Yolo仍是目前唯一能分层的方案不能丢 02/15 21:04—21:14 11天试验、tier 问题、线性开销

02/16 02:03 — PR #352 关闭。

页面定格为 Closed with unmerged commits。

86B96B78CF695AB69562FC92CD21A1EC.png

推论

已获取细节 → 竞争性对比宣传 → 负责人要求等待 → 群内催合 → 第三方在等待评估尚未完成时执行合并 → 负责人事后发现 → #352 被关闭。

抢先合入的代码当晚即被确认缺少分层地图(Tier)功能。

反方的解释检验

当事人可能解释平台记录客观反证
“只是做验证,没想替代”发布题为“为什么选择我们🔥”的推广卡片并 @负责人。
“两份方案可以共存”结果是 #352 闭案退场,因为负责人明确告知“你的 PR 只能关了啊”。
“方案功能更全面”合并当晚本人承认缺少分层功能,确认“Yolo 仍是目前唯一能分层的方案”。
“合并按钮不是我按的”合并由第三方操作属实,但推广卡片与“测试无误、合完立即干”等催合论调均出自本人。

小结

抢先合入事实确立。 一份 Ready 状态的方案,在负责人两次要求等待后,被一份已知存在功能缺口的方案抢先合并,导致前者退出主线。


3. 方案能用

事后流传的说法是“原方案写了但没法用,也没有文档”。用压测数据和代码审查结果核验这个说法。

记录

02/28 20:22 — 重新加入群聊。

Lemon-miaow 重新加入开发群。

02/28 重新加入群聊

02/28 — C++ 路线性能日志。

按各自公开日志样本,C++ 单帧耗时约在 10-15ms 区间;同期 Go 侧公开报告耗时约为 27-30ms。注意两套截图字段口径不完全相同,比较仅限这些公开样本与各自 PR 报告,不外推为所有场景下的绝对性能结论。

C++ 公开压测图中跟踪平均耗时 11.5ms(P99 25.0ms),Go 侧公开压测图中推理平均耗时 30.2ms(P99 40.0ms)。

cpp-benchmark2 cpp-benchmark1

02/28 21:32 — PR #867 提交与原始数据公开。

Lemon-miaow 提交 PR #867,公开了 600MB 测试视频及原始 CSV 压测文件。负责人指派 isHarryh 审查,isHarryh 发言:“我无权 review 该 PR”。 PR #867 完整页面

02/28 21:32—21:44 #867 通知、assigned isHarryh 与拱火.jpg 02/28 21:47—21:49 测试视频与两份原始 CSV 02/28 22:04 我无权 review 该 PR 02/28 23:00—23:01 现在只做了定位

03/01 — Harry 回应 CV 组长称呼:表示已卸任。

isHarryh 在群内表示“现在不是 CV 组长了”“已卸任”。

03/01 现在不是 CV 组长了

03/02 — #912 分离 minicv。

#912 把 MapTracker 的 CV 逻辑抽为 minicv 独立包。 PR #912 完整页面

03/02 #912 以分离 minicv 为由引流

03/03凌晨 — Go 侧实机问题。

主线 Go 方案在测试中出现需要前台运行、原地跳跃、过桥失败等问题,isHarryh 要求用户手调 arrival_threshold 参数。

03/03 凌晨 Go 寻路测试:前台、转弯、过桥与 arrival_threshold 03/03 凌晨 过桥与 arrival_threshold 03/03 02:33—02:43 点插值、划线与navmesh 整出来就不用了

03/04 — 自动行走成果。 Lemon-miaow 展示自动行走录像,负责人调侃”给 cv 组长一点压力”,isHarryh 回应称 Go move 延迟主要在截图。

03/04 17:07—17:20 自动走演示、CV组长危险了与 go move 截图延迟

#867 审查与合并节点: 02/28 21:31:37 建立 → 03/02 17:36 MistEO 最终复核 → 03/02 19:47 合并(+2460 行)。

03/10 — C++ 寻路状态机合入主线。

PR #1170 将 C++ 寻路状态机正式合并(6 commits / 55 files / +7806 −43)。状态机包含 7 个闭环状态:Bootstrap(启动定位)→ AlignHeading(对齐朝向)→ AdvanceOnRoute(沿路点推进)→ WaitZoneTransition(等待区域切换)→ WaitRelocation(等待重定位)→ RecoverRejoin(恢复并重回路线)→ ExactTargetRefine(终点逼近)→ Finished / Failed。此时距 #352 关闭不到一个月,C++ 侧已具备从定位到寻路到状态控制的完整链路。 PR #1170 完整页面

推论

后续 Benchmark、审查和主线合并证明 C++ 核心技术路线具备实际可用性;“技术路线本身没法用”不成立。它不能反向证明 #352 在 02/15 已不存在任何工程接入问题。C++ 方案后续通过压测数据、公开原始 CSV、经完整公开审查并留下 26 条讨论记录后合入主线。

反方的解释检验

反方的解释对应的记录
“写了但没法用”#867 公开全量 Benchmark 与原始 CSV,通过审查并合入 main 主线。
“没有文档”#867 与 #1170 均包含完整性能报告与状态机指引,通过审查。
“Go 方案当时很稳定”03/03 测试暴露强依赖前台、过桥失败卡死等问题。

小结

“技术路线本身没法用”的说法不成立。 后续 Benchmark、审查和主线合并证明了 C++ 核心技术路线具备实际可用性。这不能反向证明 #352 在 02/15 已不存在任何工程接入问题。


4. 定向引流

核查 C++ 方案测试排障期间,是否存在针对特定测试人员的即时定向宣传。

记录

03/14—15 — 分钟级邀测。

  • 03/14 18:42,Lemon-miaow 发送 C++ 运行视频,并 @风归云冬 协助排障。
  • 03/15 01:56:15,isHarryh 创建针对进近体验的 PR #1283。
  • 03/15 01:57(创建不到 60 秒),isHarryh 在群里 @风归云冬 发送 PR 链接,提示其删除参数测试。
  • 03/15 11:07:19,PR #1283 完成合并。
  • 03/15 11:07(合并同一分钟内),isHarryh 再次 @风归云冬 宣布:“PR 严肃合并,欢迎试用优化版 go”。 PR #1283 完整页面

03/16 — 排障线程内即时宣传 Go 方案。 风归云冬反馈 C++ 方案洞穴识别卡顿,Lemon-miaow 正与其分析日志。isHarryh 在同一线程介入问:“@风归云冬 go 新版试了吗”,并在 00:00 直接贴出现成 Go 配置文件。

03/16 23:31—23:53 C++ MapNavigateAction 的测试与排障 03/16 23:55—03/17 00:00 go 新版试了吗、#1354 与 MapTrackerMove 配置 PR #1354 完整页面

03/22 — 特性点明后当晚紧急跟进。 10:45,Lemon-miaow 点明 C++ 方案具备无坐标 HEAD 终点朝向控制。当天 22:51,isHarryh 宣布也增加了该功能。Git 记录显示其 PR #1529 首次 commit 时间为当晚 23:18:55。 PR #1529 完整页面

03/22 10:45 C++ 已有的无坐标 HEAD 动作被点明 03/22 22:51 Go 侧也增加了该功能

03/26 — 框架接入后的质问。 用户将 C++ 方案接入 maafw 框架后,isHarryh 发言质问:“客户调研:为什么没有用 go”。

03/26 22:14 客户调研:为什么没有用 go 03/25 00:32 ADB 相关进展

04/08 — 用户求助 MapNavigator,被直接告知“操作是换成 MapTracker”。

04/08 开发群完整聊天记录:用户求助 MapNavigator 录制卡住,管理员 @ 作者出列,isHarryh 回复操作是换成 MapTracker

07/19 — 质疑 C++ 路径的线程内即时展示 Go 工具。

uy/sun 贴出 MapNavigator(C++)的一段绕行路径,问“为什么不直接直走跳下去”;Lemon-miaow 回复“safe”并附路径对比图,群内跟进“起伏更小吧”。uy/sun 随后追问“能够判断这种断崖吗?”。超越一梦想提出“添加单向路径()”,并预期“我猜很复杂”。星光(isHarryh)随即接话“easy”“人工画一条就行”,并连发两张 MapTracker 路网编辑器的案例截图——均为在地图上手动摆点连线、以红框标注单向箭头。

讨论对象是 C++ 方案的能力边界,回应内容是 Go 侧工具的现成界面,与 03/16 排障线程中的介入模式一致。

07/19 uy/sun 质疑 C++ 绕行路径与 Lemon_miaow 回应 07/19 单向路径讨论与 isHarryh 人工画路径应答 07/19 isHarryh 展示 MapTracker 路网编辑器人工连线案例

07/29 — 时隔四个多月,isHarryh 仍出现在同一目标用户的提问下。

风归云冬在开发群提问 maaend_entities.json 中是否包含采集物坐标;Lemon-miaow(MapNavigator)随即回应可以编写脚本提供该数据,风归云冬表示”来一个”。同一问题下,isHarryh 也引用该提问作出回复,先答“应该有的”,随后再次引用同一提问,指引运行 map_tracker_master.py 后访问 http://127.0.0.1:8060/web/navmesh-editor 查看,并附工具截图演示。03 月的记录已显示该用户是持续引流对象;四个多月后,同一用户提出技术问题时,isHarryh 依然在场并作出回应。

07/29 开发群完整聊天记录:风归云冬提问采集物坐标,Lemon_miaow 回应,isHarryh 指引访问 navmesh-editor

08/05 — 用户抱怨 MapNavigator,isHarryh 引用后回复“冷知识跑不通可以换一个工具”,并自注“(明示)”。

08/05 开发群完整聊天记录:ModiAWA 称已被 maplocator 气晕,isHarryh 两次引用该消息回复可以放转转回收、冷知识跑不通可以换一个工具,随后补充(明示)

推论

对象、时间、内容三点交叉,无法以巧合解释。

  • 对象 — 指定正在帮 C++ 侧排障的单一用户,不是群发。
  • 时间 — 创建后 60 秒内通知,合并同一分钟内再次通知。
  • 内容 — 推送内容精准针对对方测试时暴露的参数痛点。

04/08 的记录连交叉推断都省了。 提问明确指向 MapNavigator,作者已被 @ 到场,回应是“操作是换成 MapTracker”——字面即为换用竞品,且不含任何排障内容。

反方的解释检验

反方的解释对应的记录
“只是正常通知新版本”通知不是群发。指定了正在协助竞品排障的单一用户,且卡在创建和合并读秒阶段。
“改进就该给用户试用”推送本身没问题。但掐秒推送给“正在测试对手方案的唯一人员”,针对性明显。
“只是提供另一个选择”04/08 的提问是 MapNavigator 的具体报错,管理员已 @ 作者到场处理。回应不含任何针对该报错的排查,内容是换用竞品;真正的解法五分钟后由 MapNavigator 作者给出。同日 11:45 再次出现“建议更换接口”。
“只是开玩笑”是不是玩笑不改变记录本身:同一天、同一类线程内出现两次,方向一致。群内以“惨案了”“maa怎么这么坏”作回应,也说明它当时并未被当作普通的技术建议。

小结

定向导流事实成立。 精度达分钟级别,目标为正在协助竞品排障的单一用户。04/08 的记录最为直白:用户求助 MapNavigator 的具体报错、作者已被 @ 到场,得到的回应是“操作是换成 MapTracker”,同日再补一句“建议更换接口”。07/19 与 07/29 的记录显示,五个月后该模式仍在延续:C++ 方案被质疑、被求助或被提及的线程中,Go 侧工具随即被展示。


5. 言行不一

核查在不同场景下,同类行为是否重复出现,并比对公开言论与 Git 提交记录。

记录

03/18 — 公开自述与代码合并矛盾。

isHarryh 公开答复:“什么时候 cpp 支持了我再支持……保持良性竞争,不内卷”。 平台记录显示:Go 侧 ADB 支持 PR #1487 于 03/21 合并;C++ 侧 PR #1999 于 04/07 合并。Go 侧实际领先合并 17 天。 PR #1487 完整页面 PR #1999 完整页面

IMG_1115.JPG

推论

公开自述与代码行为矛盾。 关于 ADB 支持顺序的明确表态与后续 Git 合并次序直接矛盾,“保持良性竞争,不内卷”这一自我描述的可信度被削弱。上述宣传与补齐行为进一步佐证了同类模式的持续性。

反方的解释检验

反方的解释对应的记录
“ADB 的话只是随口说说”公开答复承诺清晰,3 天后其提交的 Go ADB 支持先行合并。
“朝向控制本来就在做”PR #1529 commit 显示核心代码首次写入于对方点明功能当晚 23:18:55。

小结

偶发解释被排除。 行为模式在四个独立节点重演,公开表态与代码提交记录直接冲突。


6. 公开对质

正面检验当事人在 04/19 对质中亲口抛出的辩解理由及现场群员评价。

记录

起因与辩解。

针对“为什么一个项目做两套”的质问,isHarryh 回复:“正在严肃垄断”,并给出三条理由:“进展太慢”、“功能没有涵盖所有需求”、“不认同设计理念”,补充称“写是写完了但没法用”。

04/19 17:34—17:40 从点位生成转向一个项目两套实现 04/19 17:40—17:44 两个施工队你俩搁这竞标呢 04/19 17:44—17:51 追溯 #352、谁先搓的第二套方案 04/19 17:51—18:01 三项理由与双方相反叙述 04/19 18:01—18:09 写是写完了,但是没法用与 18:06 道歉 04/19 18:14—18:21 纯 CV 验证动机、Tier 争点与 #560 文案复述

当事人承认。

  • 18:06 isHarryh:“那我素质可能有点差了,抱歉没有考虑你的感受。”
  • 18:26 isHarryh:“现在看来,我承认结果从主观上看确实对你不尊重。”
04/19 18:09—18:14 用我的定位做你的寻路与 #352 页面 04/19 18:21—18:26 争斗都是你的臆想与我承认结果从主观上看确实对你不尊重

第三方定性。

  • 暗檬:“甲乙两个施工队同时开工。”
  • issue小王:“你俩搁这竞标呢。”
  • uy/sun:“确实客观上竞争了。”
  • EeeMao:“没必要把人想得那么阴暗。”(本文接受此保留意见,不推定主观恶意)
04/19 18:26—18:31 确实客观上竞争了赛马与在场反应

反方的解释检验

反方的解释对应的记录
“不知道 C++ 方案在开发”02/14 本人向 C++ 作者索取素材、技术路线、性能数据和地图参数,当天同步的技术细节覆盖了整套方案的核心架构。群聊时间线(第一章)证明其完全知情。
“进展太慢”实际为代理部署排期问题,项目负责人已明确承认责任在自身。
“功能没有涵盖所有需求”合并当晚本人亲口承认自身缺少分层(Tier)功能,确认 C++ 是唯一可分层方案。
“不认同设计理念”可解释另起一套的偏好,但无法解释绕过等待评估抢先合并的流程问题。
“写是写完了但没法用”#352 在 #560 创建前已经转为 Ready,定位核心功能已完成,公开记录显示剩余条件主要是 cpp-algo 接入。现有公开记录也未显示项目存在必须在 02/15 当晚完成选型或立即合并寻路实现的明确期限。见第三章压测数据、原始 CSV、26 条讨论记录及合入主线记录。

小结

公开理由均无法在已披露的记录中无矛盾地成立。 当事人已承认结果上不尊重。在场成员确认客观上形成了竞争;对其主观动机,看法不一。


7. 亦步亦趋

核查 04/19 冲突后,Go 侧是否在 NavMesh、兼容层和工具 Web 化上重复了同方向的跟随模式。

记录

05/21 — C++ 原生 NavMesh 发布。

Lemon-miaow 提交 PR #3084,发布预烘焙多边形三角网格的全图及视频。 PR #3084 完整页面

05/21 #3084 C++ 原生 NavMesh 发布 1 05/21 #3084 C++ 原生 NavMesh 发布 2

05/23—05/25 — 兼容层争夺。

05/23,Lemon-miaow 提交 PR #3125,创建 MapTrackerMove→MapNavigatorCompatible 正向兼容层,使既有 MapTracker 路线无需调整坐标即可切换到 C++ 方案。 PR #3125 完整页面

约 27 小时后,isHarryh 提交 PR #3155(+1157 −27),反向建立 MapNavigateAction→MapTrackerMoveCompatible 入口,PR 正文明确引用 #3125。#3125 提供从 MapTracker 到 MapNavigator 的兼容路径,27 小时后 #3155 以反向兼容层回应,方向相反。 PR #3155 完整页面

06/03 — 名字叫 NavMesh,内容完全不是。

C++ 发布 NavMesh 前,公开仓库记录中未见 Go 侧提交路网或 NavMesh 相关实现。C++ 发布 NavMesh 仅 13 天后,isHarryh 提交 PR #3359,命名为 MapTrackerNavMesh——与 C++ 侧的 MapNavigatorNavMesh 仅差一个单词。PR 正文自述为“一种基于文本的路网数据文件”,代码结构由离散点与连线构成(固定路网 Waypoint Graph),无多边形面。NavMesh 的技术定义是以多边形(通常为三角形)网格表示可行走区域,而这份 PR 的数据结构是点线图,不具备网格面的特征。名称与其自身文档描述矛盾。 PR #3359 完整页面

能走到目的地,不代表它就是 NavMesh。 寻路的数据有两种常见做法,各有各的名字。一种是 NavMesh(导航网格):用一片片多边形把能走的地面铺满,哪里能走、哪里是边界,看这片面就知道,没铺到的地方就是走不了的地方。另一种是 Waypoint Graph(路点图):在地图上人工点几个点、连几条线,能走的只有这些线,线以外没有任何信息。

两种都能寻路,都能“说哪走哪”,走不走得到跟叫什么名字没关系。问题只有一个:MapTrackerNavMesh 存的是点和线,是第二种,名字却用了第一种的。

准确地说:NavMesh 与 Waypoint Graph 是路径规划中两类并列且各有定义的数据结构——前者的可行走性是多边形面本身的属性,面的边界即障碍边界;后者的可行走性只存在于人工摆放的点与边上。MapTrackerNavMesh 按定义属于后者,却使用了前者的名称。这不是功能强弱之争,是类别错配。

第四节 07/19 的对话正好印证了这点。被问到“能够判断这种断崖吗”,isHarryh 的回答是“人工画一条就行”,并贴出手动摆点连线、红框标注单向箭头的编辑器截图。断崖在哪、哪条路只能单向走,全都得靠人一条条画上去——真正的 NavMesh 里这些是面自己带的信息,不用人画。约束靠人工逐条标注而非从几何面推导,正是路点图的定义特征。此时距 06/03 命名矛盾被指出已过一个半月。

06/04 — 公开宣传。

isHarryh 在群内发布滑索寻路演示视频,配文“此视频您将看到我们最新成果的前瞻效果演示”,获群成员回应“强强!!!!”。宣传涵盖自动识别滑索、自动上滑索、容忍选错 / 阻挡、NavMesh 等要素。此时距离 PR #3359 提交仅过一天。 06/04 #3359 公开宣传 0 06/04 #3359 公开宣传 1 06/04 #3359 公开宣传 2 06/04 #3359 公开宣传 3

06/05—06/11 — 宣传与故障对比。

然而同期公开测试与开发者反馈中仍出现过桥、阻挡和运行稳定性等问题。在此前提下:

  • 06/05,针对 C++ A* 路线提问,isHarryh 回复“用 MapTrackerGoal 就行了,包对”。
  • 06/11,在用户反馈 C++ 编辑器卡死后,isHarryh 引用该报告发言:“看看我们的 nav mesh editor 会卡死吗”。
  • C++ 作者指出其“连个面都没有”,被 Go 侧定性为“开始攻击了”。
06/05 送货任务讨论及拉踩 1 06/05 送货任务讨论及拉踩 2 06/11 送货任务讨论及拉踩 3 06/11 送货任务讨论及拉踩 4 06/11 送货任务讨论及拉踩 5 06/11 送货任务讨论及拉踩 6 06/11 送货任务讨论及拉踩 7

07/13 — #4172 工具 Web 化。

Lemon-miaow 提交 PR #4172,将 MapNavigator 工具从 tk OpenCV GUI 迁移到 Web UI(Vue + Element Plus + FastAPI),形成统一的暗色地图编辑器。

PR #4172 完整页面

07/23 — #4461 跟进 Web 化。

#4172 合并十天后,isHarryh 提交 PR #4461,同样将 MapTracker 工具从 Python OpenCV GUI 迁移到 Web UI,描述为”MapTracker Master Tools 聚合式 Web 工具现已可用”。

PR #4461 完整页面

07/29 — 命名争议持续至今,未见更正。

命名矛盾指出后近两个月,文档未作调整。截至 2026-07-29 11:00(对应固定版本 MaaEnd/MaaEnd@0c7221a),.agents/skills/go-map-tracker-guide/SKILL.md 仍称 map_tracker_master.py 提供”NavMesh 编辑”功能;docs/zh_cn/developers/components/map-tracker.md 仍称 MapTrackerGoal 节点”基于 NavMesh 自动规划路径”,entity_id 参数说明仍写作”NavMesh 顶点关联的实体 ID”。底层数据结构未变,命名也未变。 07/29 11:00 SKILL.md 仍将 MapTracker 功能称为 NavMesh 编辑 07/29 11:00 map-tracker.md 仍称 MapTrackerGoal 基于 NavMesh 规划路径

07/30 — 隔了十一天,还是那句“自己画”。

19:11,用户明欣提问 MapTrackerGoal 可不可以指定目标的碰撞箱,并说明遇到的现象:“指定侧面的点位会在从另一侧寻路的时候卡住”。群成员 ocsin 的判断是“这应该是 navmesh 打点打少了”。isHarryh 随后回复“要什么路线自己画(”,并连发两张 MapTracker 编辑器截图;针对其中的路网放大图,他解释“你选择这个点(箭头所指)作为终点的话”“一般都会途径紫色线条,不会穿越中心的”。19:39,明欣自行确认了原因:“原来是填到演算台中间了”。

07/30 开发群完整聊天记录:明欣提问 MapTrackerGoal 碰撞箱与寻路卡住,ocsin 称 navmesh 打点打少了,isHarryh 回复要什么路线自己画并贴出编辑器路网截图

这段对话有三处值得看。

一是距 07/19 的“人工画一条就行”只过了十一天,同一个人对同一类问题的回答仍然是“自己画”。路线由人手工绘制,不是从地形几何算出来的——两次表述完全一致。

二是“打点打少了”这个说法本身。真正的 NavMesh 里不存在“点打少了”,面铺到哪里就能走到哪里,走不到只可能是面没铺;会因为“点少”而走不通的,只有路点图。群成员在排障时脱口而出的是路点图的那一套概念,用的却是 NavMesh 这个名字。名字张冠李戴的后果,不只停留在 PR 标题上,已经进入了使用者理解问题的方式。

三是 isHarryh 自己给出的解释:终点选在某个点上,路径“一般都会途径紫色线条,不会穿越中心的”。这句话就是路点图的定义——能走的只有人工连出来的那些线,线与线之间的区域没有任何可通行信息。NavMesh 的多边形面内部任意位置都可穿行,根本不会有“不会穿越中心”这一说。明欣把终点填在演算台中间就卡住,也正是因为那块地方没有人画过点。

距 06/03 命名矛盾被公开指出,已近两个月。

#3830 → #3855 — TurnNode 引入并替换主体业务。

isHarryh 在 PR #3830 中为 MapTracker 新增转向节点(TurnNode)。在此基础上,PR #3855 将该业务路径从 MapNavigateAction 切换至 MapTracker TurnNode,形成对既有 C++ 动作的实际替换。

PR #3830 完整页面 PR #3855 完整页面 PR #3855 diff 对比

反方的解释检验

反方的解释对应的记录
#3155 只是正常兼容层两个兼容层方向完全相反。#3125 把 Go 路线迁向 C++;仅 27 小时后 #3155 建立反向兼容层(+1157 行),且 PR 正文明确引用 #3125。这不像独立规划的功能需求,更像对竞争方动作的即时回应。
“NavMesh 是独立开发的”同名 PR MapTrackerNavMesh,提交于 C++ 原生 NavMesh 公开仅 13 天后。代码底层仅为离散点连线,无三角面,不构成 NavMesh 的技术定义。时间线、命名和自身文档描述的”文本路网数据文件”三处矛盾。
“能用就行,名字不重要”这里争的不是好不好用。NavMesh 和路点图是两种不同的东西,都能寻路;MapTrackerNavMesh 存的是点和线,是路点图,名字却用了 NavMesh。07/19 被问到能不能判断断崖,本人的回答是”人工画一条就行”;07/30 被问到寻路卡住,回答仍是”要什么路线自己画”,并解释路径”不会穿越中心”——能走的只有人工连出来的线,这正是路点图的定义。同一场对话里,群成员的排障用语是”navmesh 打点打少了”,混用已经从代码蔓延到了使用者。截至 07/30,文档、工具路由和日常使用里的这个名字都没改。
“Web 化是正常技术演进”单独看,Web 化可以属于正常技术演进;但在同一仓库、同位竞争模块和既有持续跟进模式中,十天内出现相同迁移方向,构成方向一致的补充证据。该节点不单独作为独立证据。
“只是行业都在做 Web 工具”同一仓库、同位竞争模块、同一迁移方向在十天内出现,难以仅用行业趋势解释。
“TurnNode 替换是正常重构”#3830 引入 MapTracker 专属转向节点,#3855 将环境监测业务的原有动作路径切换至 MapTracker TurnNode。该切换将业务依赖单向指向竞争模块,两个 PR 均未通知 C++ 侧或提出迁移方案。

小结

亦步亦趋的模式再次出现。 兼容层、NavMesh 与 Web 化三个节点,均表现为 C++ 侧先行、Go 侧随后作出同方向回应;TurnNode 则进一步表现为从自己模块蔓延到了公共业务,并对既有 C++ 动作形成实际替换。

其中 NavMesh 这一项和其他几项不太一样:它不只是时间上跟得紧,而是在 C++ 的 NavMesh 公开 13 天后,把一份点线路网也叫成了 NavMesh。这跟谁做得好不好没关系——两种东西都能寻路;问题是名字张冠李戴。被公开指出后,这个名字到 07/30 仍挂在文档、工具路由和日常使用里,没改;同一天群内答疑时,本人对“怎么让它走某条路”的回答依然是“自己画”,群成员排障时说的则是“navmesh 打点打少了”。


8. 引流用户

核查仅配置 Go 侧 Skill 对开发者及 AI 工具初始选择的影响。

记录

末位提交与审查缺口。

05/28 03:01,UI 重构 PR #3208 在通过机器人审查后,追加最后一个 commit docs: 加个妙妙SKILL,写入 Go 侧配置 go-map-tracker-guide。 PR #3208 完整页面

05/28 Skill 借 #3208 进入仓库

52 天仅 Go 侧有 Skill。

该文档包含强指令:“当你判断确实正在进行 MapTracker 的开发工作时,务必无条件地先读取……”。在此后 52 天内(05/28—07/20),项目中仅有 Go 侧 Skill 写入项目配置,AI 编码工具在此基础上优先检索 Go 方案。

实测对照显示:仅有 Go 侧 Skill 时,AI 首轮直接抓取 Go 文档并做优先推荐(在扫库 4 分钟后自行纠正)。

Skill 对比实测 0 Skill 对比实测 1 Skill 对比实测 2

平权后的指责。

07/20 C++ 侧补齐 map-navigator-guide 后,isHarryh 于次日截图指责:“连 skill 都抄是有点好笑的”。

07/21 MapNavigator 补齐 Skill 后被鉴抄 1 07/21 MapNavigator 补齐 Skill 后被鉴抄 2

反方的解释检验

反方的解释对应的记录
”只是随手加了个文档”Skill 以 UI 重构 PR 的末位提交加入;两次自动审查均发生在该提交之前,之后未见新的正式审查记录。
“没有规定两边都要有 Skill”没有。但结果就是单侧 Skill 持续 52 天,AI 实测首轮直接抓取 Go 文档优先推荐。Skill 以 UI 重构 PR 的末位提交加入,未见独立审查。
“Skill 只是辅助,不影响行为”实测 AI 在单侧配置下首轮抓取 Go 文档并做优先推荐。本次对照样本中的首轮影响真实存在。

小结

#3208 以末位提交单独加入 Go Skill,52 天后才出现对等入口,干预了初始检索方向。对称性恢复后,当事人随即反向指责对方”抄袭”。


结论

这不是偶然的多方案共存,而是有目的性的持续性恶意竞争。

02/14 对方已获取 C++ 方案全部技术细节,02/15 #560 在负责人两次要求等待后被先行合并。公开场合给出的”没法用”三条理由,被压测数据、代码审查和 26 条讨论记录逐一推翻。#1283、03/16–17 的即时宣传 Go 方案发生在 C++ 排障期间,03/22 Go 侧在 C++ 公开特性后当晚补齐对应功能,当事人的公开自述与 Git 提交记录直接矛盾。#3125 建立兼容层后仅 27 小时,#3155 建立反向兼容层;NavMesh #3084 → #3359 和 Web 化 #4172 → #4461 间隔分别为 13 天和 10 天。#3830 引入 TurnNode 后 #3855 直接替换业务代码中的 C++ 动作。#3208 单独加入 Go Skill,52 天后才出现对等入口,同提示词复现的首轮选择随入口配置变化方向。

在已知 C++ 方案(MapNavigator)存在且可审查的情况下,Go 侧(MapTracker)持续建立功能重叠的另一套实现。#1283、03/16–17、03/22 在 C++ 测试或公开特性后宣传 Go 或补齐对应功能。#3084 → #3359、#4172 → #4461 以 10–13 天延迟跟随。#3830 → #3855 替换业务代码中的 C++ 动作。#3208 单独加入 Go Skill,52 天后才出现对等入口,同提示词复现的首轮选择随入口配置变化方向。

不建立在代码抄袭或恶意推断之上。项目管理端在选型评估与合并监督上的缺位,同样是导致这场长期内耗的重要因素。


后日谈:把画面放回时间轴

2026 年 8 月 8 日 21:37,isHarryh 在 MaaEnd 开发群发布了一段约一千五百字的声明,随后退群。声明全文如下(群聊原图,未裁切):

2026-08-08 21:37,Harry(isHarryh)在 MaaEnd 开发群发布的声明全文

声明第二段开头写道:

“这也涉及近期某篇博客长文中被精心编排的‘证据链’,出于对事实的基本尊重,有几处被滤去的画面值得重新放回时间轴上。”

此前一天,08/07 12:44,isHarryh(群昵称“星光”)在群内发言:

“但我觉得或许能早日在某人的新闻学博客上看见我的这些发言了”

2026-08-07 12:44,星光(isHarryh):但我觉得或许能早日在某人的新闻学博客上看见我的这些发言了

那确实,这不就看见了嘛。

以下按声明的论述顺序,逐条核对其中的说法与平台记录。所引发言均附原始截图,截图未经裁切。

一、“某人尝试一类方案未达预期”

“2026 年年初,MaaEnd 启动寻路系统。在某人尝试一类方案未达预期,并且正在开发另一个新方案时,我出于重新检验前者的技术路线的目的,完成了从识别到寻路操作的完整可用方案。”

核验这一说法,先确认“未达预期”所指的方案,以及它的实际结局。

02/05 15:45 — #352 创建。

声明中“未达预期”的方案,是 #352 feat: 小地图定位。架构为:YOLO 前置分类锁定层级(Tier)并输出 ZoneId,再以 CV 模板在单张底图内局部检索;带速度预测的 ROI 防抖,正文记录了实测单帧耗时。同日群内贴出的定位结果见正文第 1 章。

02/16 02:03 — #352 由作者关闭。

平台记录显示,#352 未收到任何 CHANGES_REQUESTED,也没有评审否决记录,由作者本人关闭。关闭前后的经过见下文二。

03/02 — #867 合并。

声明中“正在开发的另一个新方案”,是 #867 feat: cppMapLocator,2026-03-02 合并,沿用至今。它沿用 #352 的架构,实现语言由 Python 改为 C++。

“未达预期”没有对应的评审记录。

未达预期的东西,现在还天天在跑呢。

二、被滤去的那个晚上

声明中关于 #352 关闭的一段:

“而紧接着发生了一件在整个叙事中被轻轻带过的、最值得重新思考其动机的事:某人未留一词,关闭了他的新方案的 PR,直接退出了开发者群聊。”

“悄无声息地,没有沟通、没有交接、没有协调,也没有一句‘我认为这样不对’。而我的方案已是唯一在跑的可用系统,面对某人沉默的离场,我选择继续维护自己的代码。”

核验这一段,需要确认 #352 关闭之前的几个小时里,平台和群内分别留下了什么记录。

平台时间线。 北京时间,取自 GitHub API:

时间事件
02-05 15:45:01Lemon-miaow 创建 #352
(挂起 10 天 10 小时 18 分)
02-15 17:54:41isHarryh 创建 #560“添加基于 CV 的通用小地图定位识别”
02-15 20:34:21#560 合并 —— 从创建到合并 2 小时 39 分 40 秒
02-16 00:51:56isHarryh 创建 #564“MapTracker 小地图定位支持 Tier 层级匹配”
02-16 02:03:44Lemon-miaow 关闭 #352
02-16 20:11:23#564 合并
02-17 00:56:50isHarryh 创建 #575“添加自动寻路功能”

两份 PR 从创建到结局:

plaintext
#352      开启 10 天 10 小时 18 分     未合并
#560      开启  2 小时 39 分 40 秒     已合并

#352 关闭于 #560 合并后 5 小时 29 分 23 秒,距 #564 创建 1 小时 11 分 48 秒。

群内记录。 #560 合并后,群内发言如下(截图见正文第 2 章,此处按时间排列):

时间发言人原话
02-15 20:40项目负责人“草,怎么都已经和过了”
02-15 20:51项目负责人“那 @檬喵Lemon_miaow 你的 PR 只能关了啊,这是是我的锅,cpp agent 拖了好几天,没想到还能再出一个 PR”
02-15 20:54isHarryh“不能否认Yolo方案的PR确实有可以互补的点”
02-15 20:56项目负责人“要不搞个兜底方案什么的 ()”
20:56 后Lemon-miaow“可是”“我有 tier 啊”
项目负责人“比如 cv 识别不出来的时候用 yolo 兜一下 ()”
isHarryh“我没有做分层地图”
isHarryh“分层地图会有的,加几张大图的事儿”
Lemon-miaow“我看未必”
Lemon-miaow“就加几张图能卡我 11 天换 yolo”
isHarryh“我现在立即加一下试一下”

以上发言均在 #352 关闭之前。

这一晚上群里可热闹了,悄无声息在哪呢。

另有两项平台记录与这一晚相关:

其一:#564 的标题。 #564 标题为“MapTracker 小地图定位支持 Tier 层级匹配”,创建于 02-16 00:51:56,早于 #352 关闭 1 小时 11 分 48 秒。

Tier 分层是 #352 正文列出的第一条特性,2026-02-05 起公开可读,原文:

“1. 支持多层级(Tier)精确分类

在包含大量层级和未来即将到来的更多区域的复杂场景中,传统的底图遍历匹配极易导致耗时剧增与跨层误判。本方案引入轻量级 YOLO 模型作为前置分类,直接锁定玩家当前所处的特定层级并输出对应 ZoneId,将 CV 匹配范围精准限定在单张底图内,有效解决了跨层级定位难题。”

前脚还是“我没有做分层地图”,不到四个小时,#564 标题就是 Tier 层级匹配了,这速度是真的快。

其二:#560 之前的 PR 记录。 #560 是 isHarryh 在本仓库的第一个 map 相关 PR。此前他的 12 个 PR 为:拼图任务 8 个、构建脚本 2 个、JSON Schema 1 个、format CI 1 个。

声明将 #560 的动因表述为“出于重新检验前者的技术路线的目的”。被检验的技术路线写在 #352 正文中,02-05 起公开可读;#560 创建于 10 天之后。

“紧接着”与时间线一致,“没有沟通”与群内记录不符。

三、“一段被自己亲手清空的参与记录”

“一段被自己亲手清空的参与记录,是否还能在此后重新拾起,作为指控他人的筹码?”

核验“清空”一说,只需查看 #352 当前的状态:

plaintext
状态      CLOSED(未合并)
正文      3946 字符      在
commit    4 个           在
评论      3 条           在
创建      2026-02-05 15:45:01
关闭      2026-02-16 02:03:44

在 GitHub 上关闭一个 PR,不会删除正文、commit、评论或时间戳。页面截图见正文第 2 章末尾。

关个 PR 就算清空了啊?东西都好好在这呢。

Lemon-miaow 在本仓库共有 108 个 PR,其中关闭且未合并的只有 #352 一个。

参与记录未被清空。

四、“数据会说话”

“现在,就 rc2 版的遥测情况而言,非常抱歉地看到在环境监测任务的数据上,某人自己原本长期负责的寻路节点失败率已是我所负责部分的 2 倍。此时此刻哪个系统更加稳定,数据会说话。”

核验这一说法,先确认所引数据的来源和统计范围。

08/07 10:23 — 面板发布。

声明所引数据,出自 isHarryh 于 08/07 10:23 在群内发布的 Sentry 面板,发布时的原话为“正式版的公开处刑面板已经搭好了”。声明中的“rc2”指 v2.23.0-rc.2,08-08 01:45 发布,至声明发出时上线不到 20 小时。

2026-08-07 10:23,Harry(null)(isHarryh):正式版的公开处刑面板已经搭好了

08/07 12:03—12:12 — 同日的三条发言。

同日中午,isHarryh 在群内另有三条发言:

上下文原话
有人问“寻路统一了嘛”“并非统一,单方面替换”
有人说“说上滑索有改”“等几个小时看看替换前的遥测结果,我看看哪来的自信”
有人说“然后今天 4 次上滑索都失败了 ()”“待会看看日志”“不用看了,已经停止维护了,等替换吧”
2026-08-07 12:03—12:12 开发群,Harry(null)(isHarryh):并非统一,单方面替换;等几个小时看看替换前的遥测结果,我看看哪来的自信;不用看了,已经停止维护了,等替换吧

面板的统计范围。 面板统计的是 EnvironmentMonitoring 下两组硬编码的 *Move 节点,范围小于环境监测模块,更小于整个寻路系统。两组标题分别为“环境监测:MapNavigator方式运行统计”和“环境监测:MapTracker方式运行统计”,公式为 failure_rate()。

08/22 03:01 — 面板的后续状态。

截图为原图,未裁切:

2026-08-22 03:01,isHarryh 的 Sentry 面板“组件 | MapTracker”

看笑了。

截图时,面板标题为“组件 | MapTracker”;时间窗 Aug 8–Aug 9;版本 MXU@2.4.3+MaaEnd@v2.23.0-rc.2;每 10 分钟一个点。左下 failure_rate() 为 55.98%,右下为 28.75%。右上角 Editors 一栏仅有 isHarryh 一人。

图中间的组件及其标题由 Lemon-miaow 添加,截图中原样保留。添加时 Lemon-miaow 拥有这张面板的编辑权限,截图时该权限已被收回;权限变更的具体时间,Lemon-miaow 一侧没有记录。

最上方两个文本组件由 isHarryh 撰写,原文:

神人

“⬇️8月13日来看的是吧,当时都没人用rc2版本了,我猜你是筛选日期都不会筛选,也是给我看笑了🤣,现在我给你筛选好了哈;还跑到Sentry里写这些东西🤣,你也是第一人👍”

说明

“此面板是8月8日2.23.0-rc2版本中,环境监测任务各寻路方式当时的失败率。考虑到现MapTracker已不再维护,此数据仅供历史参考。”

关于 08/13 的时间窗。 文本组件中对时间窗的批评成立。08/13 Lemon-miaow 查看这张面板时,时间窗为“最近 24 小时”;当时 rc.2 的使用者已经很少,窗口内样本量不足,这一窗口选择有误。

本节以下数据,一律采用 isHarryh 筛定的窗口:北京时间 2026-08-08 00:00 至 08-10 00:00,版本 v2.23.0-rc.2,环境监测的 *Move 节点。

行,那就按你筛好的来。

按 rc.2 代码中的实际调用分组。 每个 *Move 节点调用哪种寻路,写在 v2.23.0-rc.2 的 pipeline 中,字段为 custom_action。按该字段的实际取值分为两组:

rc.2 上调用的动作节点数调用失败失败率
MapNavigateAction711721.71%
MapTrackerMove / MapTrackerGoal / MapTrackerToward243489126.15%

调用与失败为 Sentry 存储的 span 条数;采样外推后两列同乘 8,比值不变(查询时间 2026-09-30)。原始数据:d1-rc2-window-em-move-by-node.csv。

MapNavigateAction 的 2 次失败,分别发生在 08-08 23:36(GoToIndoorCropsMove)和 08-09 11:17(GoToTreeOfTheOldCourtyardMove),见 d1b-rc2-window-mapnavigateaction-failures.csv。

按实际调用分组,声明中“某人自己原本长期负责的寻路节点失败率已是我所负责部分的 2 倍”一句对应的数值为:调用 MapTracker 的一组 26.15%,调用 MapNavigator 的一组 1.71%。

这 7 个节点的来历。 它们原先都调用 MapTrackerMove,由环境监测维护者 he0119(uy/sun)在四个 PR 中改为 MapNavigateAction:

节点PR合并时间
GoToAncientTreeMove、GoToIndoorCropsMove、GoToTreeOfTheOldCourtyardMove#3608“修正多个任务的点位”06-15 23:46
GoToCisternOriginiumSlugsMove、GoToVigorousCisternOriginiumSlugMove#3804“微调优化路径”06-22 20:22
GoToCollapsedTianshiPillarMove#3892“优化倒塌的天师桩路线”06-26 23:58
GoToAnnularWaterfallMove#4663“修复 ADB 快速传送与任务定位按钮复用”08-02 00:31

#3608 的说明为“为环境监控路线添加跨层级的 NAVMESH 目标支持”;同一 PR 也把 GoToFloraObservationPointMove 改成了 MapNavigateAction,该节点此后的变化见下文。#3892 对应的 issue 是 #3823“拍摄倒塌天师桩,天师桩被角色挡住,导致环境监测失败”。

rc.2 上,这 7 个节点都以 NAVMESH 目标寻路,路线在运行时由 navmesh 计算;其中 GoToTreeOfTheOldCourtyardMove 的目标带 target_tier: "Wuling_L4_330",是跨层目标。

面板左侧失败的来源。 截图左下的图表,在该窗口内有数十个非零的 10 分钟桶。实际调用 MapNavigateAction 的 7 个节点,整个窗口只失败上述 2 次,最多产生 2 个非零桶。由此可知,左侧组件中包含 rc.2 上调用 MapTracker 的节点。

08/13 按节点拆分时,左侧的失败集中在 GoToFloraObservationPointMove 一个节点,其调用全部失败。

该节点在 v2.23.0-rc.2 上的写法(FloraObservationPoint.json:376-408,节选):

json
"GoToFloraObservationPointMove": {
    "desc": "自动寻路前往植物观察点",
    "action": "Custom",
    "custom_action": "MapTrackerGoal",
    "custom_action_param": {
        "map_name": "map02_lv004",
        "target": [550.3, 252.6],
        "on_finish": {
            "action": "Custom",
            "custom_action": "MapTrackerToward",
            "custom_action_param": { "angle": 0 }
        }
    },
    "next": ["FloraObservationPointTakePhoto"]
}

它原先调用的是 MapNavigateAction(父提交中的写法)。改成 MapTrackerGoal 的是 #4779“feat(EnvironmentMonitoring): 优化拍照流程与武陵路线(子模块)”,he0119 提交,2026-08-05 20:06 由 zmdyy0318 合并,提交 f490cc9d。rc.1、rc.2 都包含这个提交。

在 isHarryh 筛定的窗口内,该节点调用 38 次,失败 38 次。同一窗口失败率为 100% 的节点共三个,在 rc.2 上调用的均为 MapTracker:

节点rc.2 上的动作调用失败
GoToFloraObservationPointMoveMapTrackerGoal3838
GoToHangingVinesMoveMapTrackerGoal2828
GoToWaterTemperatureControllerMoveMapTrackerMove1919

其中 GoToFloraObservationPointMove 属于标题为“环境监测:MapNavigator方式运行统计”的左侧组件。

GoToWaterTemperatureControllerMove 在 rc.2 上的写法。 08-04,isHarryh 在 #4743“净水温控装置改为MapTrackerGoal”中,把该节点由 MapTrackerMove 改为 MapTrackerGoal。08-06 合并的 #4797“寻路节点增加成功埋点”(zmdyy0318,提交 1d616200f)又把它改回 MapTrackerMove,这是该 PR 中唯一一处 custom_action 取值变化。此后 rc.1、rc.2、v2.23.0 以及 v2.24.0-beta.1、beta.2 都按 MapTrackerMove 运行,直到 beta.3 随 #4785 改用 MapNavigator。

rc.2 上运行的是被改回的 MapTrackerMove。不计这一节点,调用 MapTracker 的一组为 329 次调用、72 次失败,失败率 21.88%。

两侧“成功”的判定标准不同(见五):26.15% 是 MapTracker 一组失败率的下界,1.71% 是 MapNavigator 一组失败率的上界,两侧的实际差距至少等于这两个数之差。

rc.2 这个版本。 v2.23.0-rc.2 的 tag 指向 #4843“perf: 修改埋点字段”(zmdyy0318)的合并提交。该 PR 于 08-08 01:34 合并,11 分钟后 rc.2 发布。rc.2 是第一个上报 Node.Action.Succeeded / Node.Action.Failed 的版本;此前的 rc.1 上报的是 #4797 引入的 Node.PipelineNode.Succeeded。

同日 22:48,v2.23.0 正式版发布,rc.2 在发布约 21 小时后被取代。两者之间只有一个提交(#4846,账号切换的 OCR 兜底),环境监测的 pipeline 与埋点相同。面板的时间窗 Aug 8–Aug 9 覆盖了 v2.23.0 发布后的 25 小时;截至 08/22 的截图,面板的版本过滤仍只取 rc.2。

都没人用 rc2 了,那面板到 08/22 还只筛 rc2 是怎么回事啊。

面板说明称“此数据仅供历史参考”。上文表格同样取自 rc.2,用途限于核对面板在其自身窗口内的分组;迁移前后各模块、各版本的完整数据见第八节。

同一节点换引擎前后。 本段数据取自 isHarryh 筛定窗口之外的版本。按引擎分组的横向比较,两组跑的是不同路线:调用 MapNavigator 的 7 个节点,是维护者从 MapTrackerMove 换出来的路线,其余 24 个留在 MapTracker 上,组间差异混入了路线差异。排除路线差异的做法,是对同一节点比较换引擎前后,并以始终调用 MapNavigateAction 的 7 个节点作对照。

环境监测的 31 个 *Move 节点,迁移前取 v2.23.0,迁移后取 v2.25.0、v2.26.0、v2.27.0、v2.28.0、v2.29.0、v2.30.0、v2.30.1 七个正式版合计(含各 MXU 版本;v2.24.0 没有正式版;窗口截至 2026-09-30 13:30,查询时间 2026-09-30):

节点组v2.23.0(失败 / 调用)v2.25.0 至 v2.30.1 七个正式版合计(失败 / 调用)
对照:始终调用 MapNavigateAction 的 7 个445 / 9,446(4.71%)1,411 / 30,478(4.63%)
v2.23.0 上调用 MapTracker 的 24 个13,286 / 38,762(34.28%)3,081 / 93,568(3.29%)
 其中 GoToFloraObservationPointMove5,042 / 5,042(100.00%)64 / 4,109(1.56%)
 其中 GoToHangingVinesMove4,213 / 4,214(99.98%)266 / 4,691(5.67%)
 其中 GoToWaterTemperatureControllerMove3,551 / 3,559(99.78%)421 / 4,395(9.58%)
 其余 21 个480 / 25,947(1.85%)2,330 / 80,373(2.90%)

调用与失败为 Sentry 存储的 span 条数,口径同上文。原始数据:d2-em-move-by-node-and-release.csv。迁移由 #4785 完成,同时重写了各节点的目标点配置,例如 GoToFloraObservationPointMove 的目标改为带 target_tier 的跨层 NAVMESH 目标。

对照组合计前后持平,单节点有升有降。24 个节点的变化集中在上表三个节点:它们在 v2.23.0 上占这一组失败的 96.4%(12,806 / 13,286),迁移后失败率在 1.56% 至 9.58% 之间。

“我看看哪来的自信.jpg”

其余 21 个节点由 1.85% 升至 2.90%,个别节点上升明显,如 GoToCloudrestEcoSiteMove 由 0.27% 升至 12.11%。前者是失败率的下界,后者是上界(见五),这一行判断不了方向。

按失败率剔除节点,结论随剔除对象而变。 在 v2.23.0 上,只剔除 MapTracker 一侧上述三个节点,MapTracker 一组为 1.85%,低于 MapNavigator 一组的 4.71%;同样剔除 MapNavigator 一侧失败率最高的 GoToTreeOfTheOldCourtyardMove(17.77%),MapNavigator 一组降为 2.52%。三个节点换用 MapNavigator 后都能到达,把它们剔除,剔掉的正是调用 MapTracker 时失败、换用 MapNavigator 后到达的那部分样本。面板把其中的 GoToFloraObservationPointMove 计入了“MapNavigator方式”一侧。

关于这张面板的其他记录。

同一 Sentry 组织内,正式版全量口径(2026-08-13 查询,窗口为查询时的最近 24 小时):76,906 次调用,1,727 次失败,成功率 97.75%。这个口径是全部导航白名单节点,不限模块。面板取的是其中环境监测的一组 *Move 节点。

将版本过滤器由 rc.2 改为 beta.4,面板、公式与节点选择保持不变(同一次查询):

失败率
MapNavigator 侧6.30%
MapTracker 侧8.82%

面板发布当天(08/07,见本节开头 10:23 的截图),群内有人就两侧的上报基准提问:

isHarryh:“不过还是可以看失败成功分布的”

uy/sun:“诶,不是说都上报错了吗”

isHarryh:“按 trace.status 分组就行”

2026-08-07 10:23 前后,开发群:Harry“不过还是可以看失败成功分布的”;uy/sun 引用后问“诶,不是说都上报错了吗”;Harry 发出“正式版的公开处刑面板已经搭好了”及面板链接,随后引用 uy/sun 回复“按 trace.status 分组就行”

提问针对的是上报本身有没有问题,回答给出的是一个分组操作。

两侧上报基准的实际情况:08/07 10:25 截图对应的窗口内,环境监测模块中,MapNavigator 侧 14 个调用点产出 44 个事件(3.1 个/点),MapTracker 侧 72 个调用点产出 129 个事件(1.8 个/点),每调用点事件数相差 1.7 倍,两侧 span 发射基准不一致。

2026-08-07 10:25,面板“环境监测:MapNavigator方式运行统计”与“环境监测:MapTracker方式运行统计”手机截图

该截图的画面截止于 MapTracker 一侧的 internal_error 一行,cancelled 分桶不在画面内,因此 64/129 是 MapTracker 一侧 ok 率的上限。

面板与查询均在共享的 Sentry 组织内,组织成员可自行复现;每个节点的 custom_action 取值可在仓库对应的 tag 中查阅。

截至本文发布:v2.30.1。

v2.30.1 于 2026-09-26 14:47 发布,是本文发布时最新的正式版。这个版本上,环境监测的 31 个 *Move 节点全部调用 MapNavigateAction,两组用的是同一套判定标准。仍按 rc.2 上的实际调用分组(窗口为北京时间 09-26 14:47 至 09-30 13:30,含各 MXU 版本;查询时间 2026-09-30):

rc.2 上调用的动作节点数v2.30.1 上调用的动作调用失败失败率
MapNavigateAction7MapNavigateAction17,8877594.24%
MapTrackerMove / MapTrackerGoal / MapTrackerToward24MapNavigateAction54,0831,3342.47%
 其中 GoToFloraObservationPointMove2,206170.77%
 其中 GoToHangingVinesMove2,3201757.54%
 其中 GoToWaterTemperatureControllerMove2,3631837.74%
合计3171,9702,0932.91%

v2.30.1 的数据仍在 Sentry 的全量保留期内,上表为实际上报的 span 条数;约 30 天以前的数据只保留 1/8 抽样,本节其余各表为抽样后的条数,比较时看失败率。原始数据:d3-v2.30.1-em-move-by-node.csv。

rc.2 上失败率 26.15% 的 24 个节点,在 v2.30.1 上为 2.47%;当时 100% 失败的三个节点,现为 0.77%、7.54%、7.74%。

“数据会说话”所依据的分组,与 rc.2 的代码不一致。

数据确实会说话,就是说出来的跟声明里的是两回事。

五、“哪个系统更加稳定”

要比较“哪个系统更加稳定”,先确认两侧遥测中“成功”的判定条件是否相同。

MapNavigator(agent/cpp-algo/source/MapNavigator/)

定位失败时:

cpp
if (!position_provider.WaitForFix(&pos, initial_expected_zone, ...) || is_stopping()) {
    return false;
}

抵达终点时(navigation_session.cpp:102-113):

cpp
if (!final_goal.zone_id.empty() && position.zone_id != final_goal.zone_id) {
    return false;                                   // 区域必须匹配
}
const double distance = std::hypot(final_goal.x - position.x, final_goal.y - position.y);
return distance <= FinalGoalAcceptanceBand();       // 距离必须达标

MapTracker(agent/go-service/maptracker/default/move.go。以下行号取自 origin/v2,2026-08-24)

到达判定(:241-250):

go
// Check arrival
dist := curLoc.DistanceTo(targetLoc)
reachedByRotation := math.Abs(internal.DeltaRotation(targetRot, initRot)) > 90.0
if dist < param.ArrivalThreshold || reachedByRotation {
    if reachedByRotation {
        log.Info().Int("index", i).Float64("dist", dist).Int("targetRot", targetRot).Int("initRot", initRot).Msg("Target point reached (ordinary approach, guessed by rotation)")

dist 与 reachedByRotation 为或关系:方位角相对起始方位变化超过 90°,即判定到达,此时不检查距离。默认 ArrivalThreshold 为 2.5(:82),这条分支将其完全绕过。

绕行障碍、被外力推开、定位结果瞬时跳变,都可能使方位角变化超过 90°。

该分支触发时的实际距离写入了日志(Float64("dist", dist)),判定逻辑不使用这个值。日志对这一分支的描述是 guessed by rotation,原文出自 MapTracker 源码。

对,就是猜的。

其一:initRot 的零值。(:182-193)

go
// Show navigation UI
var initRot int
if initResult, err := doInfer(ctx, ctrl, param); err == nil && initResult != nil {
    initRot = int(initResult.Loc.AngleTo(targetLoc))
    ...
} else if err != nil {
    log.Debug().Err(err).Msg("Initial infer failed for moving UI")
}

该段注释为 “Show navigation UI”:initRot 最初为绘制导航界面取值,随后被复用于到达判定。

若此次定位失败,initRot 保持零值 0,即正北。此时 |DeltaRotation(targetRot, 0)| > 90 等价于目标方位位于南半边,约占一半方向;循环首轮即判定到达,角色在该路径点上未发生移动。

触发条件是 :184 的 doInfer 失败,且循环内 :228 的首次 doInfer 成功;定位持续失败时,:221 的 arrival timeout 会正确返回 false。因此这一路径命中的是瞬时的定位坏样本。

var initRot int 声明在 for i, targetLoc := range param.Path 的循环体内,每个路径点重新初始化一次;多点路径的节点,暴露次数等于路径点数。

其二:精调阶段的复测结果被丢弃。

reachedByRotation 触发之后(:251-255):

go
if enableFineApproach {
    runFineApproach(ctx, ctrl, ca, param, targetLoc, curRot)
}

break

runFineApproach 首先重新测距,若距离超过阈值(:645-655):

go
// Guard against residuals that are too large for fine approach.
if dist >= param.ArrivalThreshold {
    log.Warn().
        ...
        Msg("Fine approach residual exceeds arrival threshold, giving up the fine approach")
    return fineApproachDiverged, result
}

这条日志的含义是“残差超出到达阈值,放弃精调”,即复测得到的距离否定了上一步的“到达”判定。

调用方对 runFineApproach(...) 的返回值没有赋值,下一行即为 break。fineApproachReached / fineApproachExhausted(“accepting current position”)/ fineApproachDiverged 三种结果均被忽略。

并且 enableFineApproach 在默认配置下只在最后一个路径点为真(:80、:178-179):

go
enableFineApproach := (param.FineApproach == FINE_APPROACH_ALL_TARGETS) ||
    (param.FineApproach == FINE_APPROACH_FINAL_TARGET && i == len(param.Path)-1)

中间路径点不进入精调,也就不产生这次复测。

Run() 返回 true 的条件是:所有目标点均跳出内层循环,且 on_finish 成功。普通路径不做终局位置校验。

同一代码库中,goal.go 的 zipline 分支设有终局校验(validateZiplineArrival,距离不达标则 DisableVertex 并重新规划);普通路径的到达判定没有采用这一校验。

上述路径在遥测中均记为成功。

成功事件的判据是 Run() 返回 true,上述路径全部返回 true。

MapNavigator 的成功率,是真实到达率的下界。

MapTracker 的成功率,是真实到达率的上界。

一个下界和一个上界,不能直接比大小。

"Target point reached (ordinary approach, guessed by rotation)" 只有 Info 日志,未进遥测;mxu.node 的 span.duration 恒为 0,也无法用“秒过”作代理。假阳性率在现有仪器下不可量化。

简单来说就是作弊了。

关于两套埋点的一致性。 2026-08-06 与 08-07 先后合入的两套埋点(节点级成功事件、stage=action),在 rc.2 窗口内 234 个节点上失败数完全一致,逐节点差异为 0,可以排除漏报。两套埋点读取的是同一个 Run() 返回值,二者一致由构造决定,不能作为到达判定的独立验证。

“不利于” MapNavigator 的一组数字。

同一窗口(2026-08-07T17:30Z – 20:15Z,正向指定 release:"MXU@2.4.3+MaaEnd@v2.23.0-rc.2"):

引擎调用失败成功率
MapTracker 合计15972598.43%
MapNavigator 对照392897.96%

MapTracker 高 0.47 个百分点。这张表是 08-08 按当时的全量数据算的;Sentry 现在只保留这一窗口的 1/8 抽样,失败条数太少,按引擎拆开后复现不出原比例,抽样数据见 d5-rc2-0807-window-by-node.csv,说明见 附件说明。

该数字经过两次计算。第一次按 span.description 分组翻页,215 个分组只汇总到 2557 次调用中的 2233 次,得 98.33%;该次的版本过滤采用排除法,漏掉了窗口内最大的版本 MXU@2.4.1+MaaEnd@v2.22.0,据此曾算出 73.52%,已作废。上表为改用正向 release 指定、分块对账后的结果。

这组数字与本节结论一致:98.43% 的分子包含“方位角摆过 90° 即判到达”的样本,以及精调复测已判定距离超限、仍记为成功的样本;97.96% 的分子只包含通过 zone 与距离校验的样本。

以上代码均可在仓库历史中查阅;MapTracker 的目录此后随 #5239 移除。

声明以两侧成功率的高低论证“哪个系统更加稳定”,所比较的是两套不同的判定标准。

这边蒙的也算到了,那边验过才算到,拿这俩成功率比谁更稳,可不就是拿开卷考的分数去比闭卷考嘛 ()

六、“艺术孤品”

“开源本应是开放的公共资源,最怕的恰恰是把东西做成只有一个人能维护的‘艺术孤品’。事实上,最终所有的责任和后果理应由那个人自己来负责。”

这是声明的最后一段。核验这一段,需要确认 MapTracker 工具的前端源码何时进入仓库。

07/24 22:26 — #4461 工具改为 Web UI,前端只以编译产物入库。

#4461 feat(tools): MapTracker可视化工具现已升级为Web UI:isHarryh 提交,2026-07-24 22:26:36 合并,23 个文件,+1721 / −6995。此前的编辑器是仓库内的 Python 源码(map_tracker_editor.py 1794 行、nav_mesh_editor.py 1854 行,以及 _internal/gui_pages.py、_internal/gui_widgets.py),此 PR 将其删除,换成 Vue 写的 Web 前端。前端只以 tools/map_tracker/_frontend/dist/static_files.zip 入库,内含 Vite 打包后的 25 个文件(index.html、assets/index-CouAZ6vQ.js 等),.vue 源码不在仓库内,PR 描述也没有给出源码位置。

08/06 19:53 — #4803 上传工具前端源码。

#4803 feat(tools): 修订及上传 MapTracker 工具 Vue 侧源码:2026-08-06 19:53:38 创建,20:03:43 合并,由 isHarryh 本人合并,无人工 review,42 个文件,+8726 / −113。

新增内容为 tools/map_tracker/_frontend/ —— NodeEditorPage(1478 行)、NavMeshEditorPage(915 行)、CollectTestDataPage(462 行) 等。

上传之前,这套前端的源码没有开源。 从 07-24 22:26 到 08-06 20:03,近 13 天里仓库内只有编译产物 dist/static_files.zip;isHarryh 名下的公开仓库中,截至 09-30 也没有单独的前端仓库。#4803 只含一个提交,作者时间 08-06 19:50:23,PR 描述写的是“并同时上传了 Vue 侧源码,以便后续需要时参考”。

之前谁想改这个工具的前端,就只能对着这个 zip 发呆 ()

合并时间距其在 #4787 中“本人将不再参与 MaaEnd 项目的开发及维护”的声明,不足 20 小时。

08/07、08/08 — 两次关于维护的发言。

此后 isHarryh 在群内两次提到维护:08-07 的“不用看了,已经停止维护了,等替换吧”,上下文与截图见四;08-08 20:40—21:37,有人报“送货还有个老问题 滑索规划有问题”,回复是“很遗憾滑索寻路将不再维护了,需要另请高明迁移”,位于本篇开头声明截图的最上方。

MapTracker 工具的 Web 前端,自 07-24 入库起近 13 天没有开源,源码在作者宣布退出后不足 20 小时才上传。

七、“需要的不是证据”

“将一场技术路线分歧,完全转写为一个人的‘主观恶意’,需要的不是证据,而是对‘另一颗心’的读心术——而这恰恰是那篇文章做得最轻车熟路的事。”

同一份声明的前文另有一句:

“而紧接着发生了一件在整个叙事中被轻轻带过的、最值得重新思考其动机的事。”

声明全文约一千五百字,其中 commit hash、PR 编号、链接与时间戳的数量均为 0。声明开篇称“有几处被滤去的画面值得重新放回时间轴上”。

一千五百字,一个链接都没有,“需要的不是证据”倒是说到做到。

声明在批评“读心术”的同时,以“最值得重新思考其动机”作为论点,全文未附任何可核验的记录。 声明涉及的各处画面,本篇第一至第六节已与对应的 PR、commit、时间戳和原始截图逐条对照。

八、“迁移后的内容的稳定性超越迁移前的水平”

“至于某人,衷心祝愿他可以早日将我的全部工作从项目中抹除干净,好好露一手完成各种修复补丁、使迁移后的内容的稳定性超越迁移前的水平。”

这一标准可以用平台记录和遥测数据逐项核验。先列迁移过程。

08/05—08/29 — 迁移 PR 的合并与进入的版本。

七个迁移 PR 建立于 2026-08-05 19:40:56 至 19:41:20,前后 24 秒。下表按合并时间排列,另含三个相关 PR。“首个测试版”“首个正式版”指最早包含该 PR 合并提交的版本;版本时间取 GitHub Release 的发布时间(北京)。v2.24.0 只发布了测试版,没有正式版。

PR内容合并(北京)合并人首个测试版首个正式版
#4781生态农场08-06 00:51Lemon-miaowv2.23.0-beta.6(08-06 01:52)v2.23.0(08-08 22:48)
#4783售卖弹性物资08-09 21:28zmdyy0318v2.24.0-beta.2(08-10 01:34)v2.25.0(08-22 00:08)
#4786赠送干员礼物08-09 21:52zmdyy0318v2.24.0-beta.2(08-10 01:34)v2.25.0(08-22 00:08)
#4780自动采集08-09 23:14zmdyy0318v2.24.0-beta.2(08-10 01:34)v2.25.0(08-22 00:08)
#4785环境监测08-10 23:07he0119v2.24.0-beta.3(08-11 02:23)v2.25.0(08-22 00:08)
#4784SeizeDeliveryJobs08-22 00:24zmdyy0318v2.26.0-beta.1(08-22 00:31)v2.26.0(08-29 00:02)
#5075大地图选点改用 WorldMap08-25 20:55zmdyy0318v2.26.0-beta.6(08-25 23:37)v2.26.0(08-29 00:02)
#4782基质刷取08-25 23:05zmdyy0318v2.26.0-beta.6(08-25 23:37)v2.26.0(08-29 00:02)
#5223栖云生态点的间接引用08-26 09:12he0119v2.26.0-beta.7(08-27 00:52)v2.26.0(08-29 00:02)
#5239移除 MapTracker08-26 14:16Lemon-miaowv2.26.0-beta.7(08-27 00:52)v2.26.0(08-29 00:02)

进度追踪 issue #4793 于 08-25 23:35:53 关闭。

声明中用于论证“哪个系统更加稳定”的数据,取自环境监测模块。声明发布时,该模块的迁移 PR #4785 仍处于 OPEN 状态,尚未合并。

关于“抹除干净”。

删除 MapTracker 的操作此前已发生过一次,执行者为 isHarryh:

时间(北京)事件
08-05 19:41七个迁移 PR 建立完毕,全部 OPEN
08-05 22:31:53isHarryh 创建 #4787“移除MapTracker”,+74 / −13869
08-06 01:07:11#4787 由 isHarryh 本人合并。此时七个迁移 PR 中已合并 1 个
08-06 01:23:27zmdyy0318 创建 #4792“Revert “refactor(map): 移除MapTracker””
08-06 01:38:05#4792 合并,+13869 / −74
(其间 21 天,七个迁移 PR 逐个合入)
08-26 14:02:41Lemon-miaow 创建 #5239“Reapply “refactor(map): 移除MapTracker (#4787)” (#4792)”,+71 / −33617,229 个文件
08-26 14:16:32#5239 合并,d382169f。17 项检查通过,0 项失败

从 #4787 合并到回滚完成:30 分 54 秒。

从 #4792 合并到同一个删除动作重新落地:20 天 12 小时 38 分 27 秒。

#5239 的标题由 GitHub 自动生成:它 revert 的是 #4792,而 #4792 revert 的是 #4787。删除 MapTracker 的那个 commit,作者仍为 isHarryh。

兜了一大圈,删掉 MapTracker 的 commit,作者还是 isHarryh 本人,挺有意思的。

08/26 — 调用清零,最终清理由 #5239 完成。

删除之前,逐项核对业务模块对 MapTracker 的调用是否已经清零。

直接调用。 上面的迁移清单按 pipeline 中 custom_action 取值为 MapTracker* 的调用点计数,#4793 记为 241 处,七个迁移 PR 合入后为 0。

间接引用。 按 MapTracker* 检索覆盖不到间接引用,例如:

json
"custom_action": "SubTask",
"custom_action_param": {
    "sub": ["SceneEnterWorldWulingMarkerStone_Ship2DestinationThenWalkAndFight"]
}

该调用位于 EnvironmentMonitoring/MarkerStoneMonitoringTerminal/CloudrestEcoSite.json,被引用的节点定义在 MapTracker/Example/WLMarkerStoneShip2.json 中。其 custom_action 取值为 SubTask,按 MapTracker* 检索无法命中。

改用“pipeline/MapTracker/ 下定义的 32 个节点名在别处被引用的次数”重新检索,全树命中 1 处,即上面这一处。#5223(+82 / −282)处理它并删除该示例文件,08-26 09:12:45 由 he0119 合并。

另有两棵资源树此前未纳入统计:assets/resource_adb/ 内为两条注释;assets/resource_macos/pipeline/MacOSKeyMap.json 内为三个节点覆盖,其被覆盖对象定义在 MapTracker/OpenWorld.json,由 goal.go 引用。二者都不是业务调用,随 #5239 一并删除。

大地图选点:MapTrackerBigMapPick 由 #5075 替换。

custom_action 取值为 MapTrackerBigMapPick 的调用点,在 #5075 合并前共 54 处:SceneManager/SceneTeleportWuling.json 52 处,SceneManager/SceneTeleportValleyIV.json 2 处。替换它们的是 #5075“feat(WorldMap): 大地图点位识别”,Lemon-miaow 提交,08-19 00:08:12 创建,08-25 20:55:29 由 zmdyy0318 合并,+2980 / −1011,31 个文件。合并后,业务 pipeline 中对 MapTrackerBigMapPick 的引用为 0;它的 Go 实现 agent/go-service/maptracker/bigmap/pick.go 以及文档、schema 中的余下引用,随 #5239 删除。

WorldMap 基于 MapLocator 工作,为其他服务提供地图元素查找能力,是现在传送业务的核心。

点击之前的校验。 两种实现在点击之前做的事不同:

  • MapTrackerBigMapPick(bigmap/pick.go:121-144):推算当前视口,把目标坐标换算成屏幕坐标;坐标落进视口,就在该处点击并返回 true。点击前不检查那个位置上有没有传送点图标。
  • MapFind(WorldMapSolver::ConfirmSpot):求解视口后,在折算出的屏幕位置附近匹配图标模板,命中点与期望位置的偏差超过 10 个底图像素即判认错;确认不到图标就不交出坐标,节点判失败。WorldMap 文档原话:“给了 icon 时,确认不到图标就不给坐标”。

这一处替换提供了一组对照。埋点由 #5200“chore: 大地图传送增加埋点上报”加入,zmdyy0318 于 08-25 01:23:26 合并,+486 / −54,改动的两个文件正是 SceneManager/SceneTeleportValleyIV.json 与 SceneManager/SceneTeleportWuling.json——#5075 随后改写的那两个。埋点的合并 commit 11ac982a 最早出现在 v2.26.0-beta.5,WorldMap 的合并 commit 75a8e192 最早出现在 v2.26.0-beta.6。因此 beta.5 运行的是加了埋点的 MapTrackerBigMapPick,beta.6 运行的是 WorldMap。两版为同一批节点、同一套埋点、相邻的两个 release(同一看板,14 天窗口,查询时间 2026-09-02 03:48):

版本大地图选点实现调用失败成功率
v2.26.0-beta.5MapTrackerBigMapPick15K10299.3332%
v2.26.0-beta.6WorldMap(MapFind + Click)16K2799.8386%

失败率 0.6668% → 0.1614%。上表是看板读数;同一窗口内只取 __ScenePrivateMap* 传送节点做 span 级查询,beta.5 15,128 次调用、失败 40 次,beta.6 17,144 次调用、失败 0 次(采样外推值),方向相同,逐节点计数见 d6-bigmap-teleport-beta5-beta6.csv。beta.6 的一次成功,比 beta.5 多要求一道图标确认。

另有一份统计,见 #5075 的评论(zmdyy0318,2026-08-26 11:11:37):99.05% → 99.89%,方向一致。其口径与上表不同:过滤到 *EnterValleyIVTheHub* / *EnterWorldWuling* 两组传送节点的子集,样本 11,158 与 3,591,取数时点也早 7 天。两份并列于此,正文以上表为准。

#5075 评论中的统计图(zmdyy0318,2026-08-26):大地图选点成功率 beta.6 vs beta.5,99.05% → 99.89%,失败 106 → 4

多验了一道,失败还少了一大截呢。

#5239 合并前的核对结果:

业务模块直接调用0
业务模块间接引用0
大地图选点 MapTrackerBigMapPick 调用0
其余MapTracker 自身的 pipeline、Go 服务(含 bigmap/pick.go)、底图资产、macOS 键位覆盖,互相引用,由 #5239 成组删除

最终清理由 #5239 执行:229 个文件,186 个删除,+71 / −33617。

MapTracker 移除 commit:d382169f,2026-08-26 14:16:32 合并。

合并后 origin/v2 上:agent/go-service/maptracker/ 不存在,assets/resource/pipeline/MapTracker/ 不存在,全仓 custom_action 取值为 MapTracker* 的调用点为 0,MapTrackerBigMapPick 为 0。截至 2026-09-29 的 origin/v2(d95235ac),全仓不区分大小写检索 maptracker,命中 0 处。

迁移完成后成功率:d382169f 首次随 v2.26.0-beta.7 发布,v2.26.0 是首个不含 MapTracker 的正式版(tag 时间 2026-08-28 22:54:37)。该版本过去 24 小时,“MapNavigator 版本成功率”看板口径:330K 次调用,5,101 次失败,成功率 98.4559%(查询时间 2026-09-02 03:48)。同一窗口 v2.27.0-beta.3:61K 次调用,830 次失败,98.6557%。

这张看板带有过滤条件,完整如下:span.op = mxu.node、has task,按任务白名单取值,排除 *Main*、*Node*、AutoDelivery*、MapNavigator* 四类节点名,以及 span.description 含 MapTracker 的 span。排除项里有失败率高的节点——同一天的节点级面板上,AutoDeliveryNavigateDestination 2.9K 次调用失败率 16.81%,AutoDeliveryRouteDestinationDeliverTargetMap02Lv00502WithZipline 469 次调用失败率 50.32%。因此 98.4559% 的适用范围仅限这组白名单节点。第四节以节点选择范围为由指出那张面板的局限,同一标准也适用于这张看板。

该数字与第五节的 rc.2 数字属于不同总体:第五节为 2026-08-07T17:30Z – 20:15Z 窗口、MapNavigator 侧 392 次调用,此处为 24 小时、330K 次调用,二者不作比较。可比的对照是上面 beta.5 → beta.6 一组。

各模块迁移前后。

以上为白名单节点的合计。声明所定标准针对“迁移后的内容”,以下按模块拆分,逐版本列出(查询时间 2026-09-30,窗口 2026-08-06 00:00 至 2026-09-29 21:00):

导航节点成功率:顶部为五个模块合计,迁移前 97.74%、迁移后首个正式版 98.29%、最新 98.00%;下方为各模块按版本,标出迁移版本

口径:span.op = mxu.node、has task;节点名含 Goto / GoTo / Move / Navigate / Teleport / Route / Turn 等关键词;排除 task 为 *Main、*Node*、MapNavigator*、AutoDelivery*、SeizeDeliveryJobs、DeliveryJobs 的 span;排除 span.description 含 MapTracker 的 span 与大地图传送节点 __ScenePrivateMap*(这类节点单独见上文 beta.5 → beta.6 一组);单版本调用超过 500;按 MaaEnd 版本聚合,合并不同的 MXU 版本。计数为 Sentry 采样外推值。“首个版本”是 GitHub 上第一个包含迁移 PR 合并提交的 tag。原始数据:d4-modules-nav-nodes-by-release.csv,按 release 中 MaaEnd@ 之后的版本号合并即得表中各格。各模块的迁移 PR 与首个版本见本节开头的表。

模块迁移前迁移后首个正式版最新
环境监测71.53%(v2.23.0)95.18%(v2.25.0)97.37%(v2.30.1)
自动采集99.04%(v2.23.0)97.89%(v2.25.0)98.28%(v2.30.1)
赠送干员礼物99.95%(v2.23.0)99.97%(v2.25.0)99.94%(v2.30.1)
售卖弹性物资99.78%(v2.23.0)99.96%(v2.25.0)99.85%(v2.30.1)
基质刷取98.06%(v2.25.0)98.80%(v2.26.0)97.52%(v2.29.0)
生态农场—83.48%(v2.23.0)96.38%(v2.30.1)
有迁移前数据的五个模块合计97.74%98.29%98.00%
选剑演武(对照)96.31%(v2.23.0)—99.84%(v2.30.1)

合计行为环境监测、自动采集、赠送干员礼物、售卖弹性物资、基质刷取五个模块按调用数加权,各模块取本表同列的版本。

声明用于论证的环境监测,是七个迁移模块中迁移后提升最大的一个。

拿来论证的偏偏是涨得最多的那个模块,有点尴尬。

需要一并列出的反例与限制。

  • 自动采集迁移后略低。 v2.23.0 99.04%,v2.30.1 98.28%,低 0.76 个百分点。迁移初期的 v2.24.0-beta.2 至 beta.4 为 94–95%,此后逐版回升。节点粒度也有变化:按任务级 span 计,v2.23.0 每次运行约 57 个导航节点,迁移后 12–18 个,逐节点成功率与逐次运行成功率不能直接换算。
  • 合计的提升主要来自环境监测。 不计环境监测,其余四个模块合计迁移前 99.04%、最新 98.04%,差距主要在自动采集,它的节点粒度迁移前后不同(见上一条)。
  • 环境监测的跃升发生在迁移后一版。 #4785 的首个版本是 beta.3,beta.3 仍为 73.18%,升至 95.37% 的是 beta.4。beta.3 → beta.4 之间合入的有 Lemon-miaow 的两项引擎修复(#4939 转向、#4947 尾部裸坐标)和两次地图模型更新(#4938、#4948),也有 he0119 的三项修复(#4922 拍照判定、#4928 修复多个任务的路径、#4942 路径优化)。这次跃升与上述修复落在同一版本区间,不能单独归因于迁移。
  • 生态农场没有迁移前的数据。 #4781 08-06 00:51 合并,首个版本 v2.23.0-beta.6,看板上没有它迁移前的版本,图上全部是迁移后的点。它在 beta.4 从 79.80% 升至 96.06%,同一区间合入了 #4941(ModiAWA,工业设备选项点击)和 Lemon-miaow 的 #4943(线路)。
  • 基质刷取迁移前后持平。 迁移前 98.06–98.45%,迁移后 97.52–99.20%,v2.29.0 比迁移前低约 0.5 个百分点。v2.30.0 起该任务的统计有误,失败率虚高约 5 个百分点,图中截止到 v2.29.0。原始值如下:v2.30.0 94.35%,v2.30.1 94.86%;这段偏差最早见于 v2.30.0-beta.5(96.29%)。
  • 赠送干员礼物、售卖弹性物资,迁移前后都在 99.8–100%。 售卖弹性物资 v2.27.0 有一版 99.10%。
  • SeizeDeliveryJobs(#4784,首个版本 v2.26.0-beta.1)不在这张图上,上面的过滤条件排除了这个任务。单独统计,其余口径相同:“走到仓库”一类节点,迁移前 v2.23.0 98.61%、v2.25.0 98.68%,迁移后 v2.26.0 至 v2.30.1 在 98.56–99.53%。迁移后多出“送往目的地”一类节点(AutoDeliveryNavigateDestination),迁移前的遥测里没有对应节点;它 v2.26.0 83.24%,v2.30.1 93.12%,截至 v2.30.1,七个迁移模块里只有这一段还在 95% 以下。原始数据:d7-seizedeliveryjobs-by-release.csv。
  • 选剑演武全程未迁移。 v2.23.0 与 v2.26.0 的 pipeline 里都是 2 处 MapNavigateAction,不在迁移清单里。它从 96.31% 到 99.84%,是同期 MapNavigator 自身的变化;beta.3、beta.4 两版它也掉到过 89–90%。

判定标准对读图的影响。

图中橙色的点来自 MapTracker,是真实到达率的上界;蓝色的点来自 MapNavigator,是下界(判定代码见五)。

因此图中数据按两种情况解读:

  • 蓝点高于橙点:真实提升至少等于图中差值。 环境监测从 71.53% 到 97.37%,这 25.84 个百分点是提升的下限。
  • 蓝点持平或更低:这组数据无法判断方向。 自动采集低 0.76 个百分点,基质刷取 v2.29.0 低约 0.5 个百分点。一个偏高的数和一个偏低的数相差不到一个百分点,这两处记为“方向未定”。

按声明所定的标准,迁移后的总体水平已超过迁移前,环境监测提升最大;其余模块持平、方向未定或缺少迁移前数据。 有迁移前数据的五个模块合计,迁移前 97.74%,迁移后首个正式版 98.29%,最新 98.00%;迁移后一侧全部来自 MapNavigator,是下界,迁移前一侧以 MapTracker 为主,偏高。

祝愿收到了,谢谢你喵^^


数据附件。 后日谈所引的 Sentry 数字,原始查询结果导出为 CSV:打包下载(sentry-data.zip),逐个文件与说明见 README.md。导出只含节点名、版本号与状态计数,不含 trace、用户与设备信息;每个文件的时间窗与查询串记录在 queries.json。

本文所引 PR、commit、代码与截图均可公开查阅,遥测数据可在共享的 Sentry 组织内复现。如有与记录不符之处,欢迎指正。

一个开源项目里的两套寻路:从“明知已有,另起一套”到“引流用户”

https://tail.lemonmiaow.xyz/posts/maaend-pathfinding-dispute/
作者 Lemon_miaow
发布于 2026-07-27
许可协议 CC BY-NC-ND 4.0