修改YAML和换船识别机制,以适配GUI2.0 - #521
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
yltx
left a comment
There was a problem hiding this comment.
本次审计结论:Request changes。CI 的 lint/pytest 均通过,但以下运行时与跨仓契约问题尚未被测试覆盖,合并前需要修复并增加回归测试。
阻塞项
-
API 活动入口地图未按 YAML 语义解析
autowsgr/server/serializers.py的build_combat_plan()将request.map直接赋给map_id,没有调用CombatPlan.parse_map_value()。- 对
chapter=H, map=1a,当前结果会是map_id=1a, entrance=None,后续生成H1a,而不是map_id=1, entrance=a。 - 请统一 API/YAML 解析路径,并补充入口后缀回归测试。
-
OCR target-context fallback 在实际换船流程中不可达
_detect.py只有收到expected_names才启用新 fallback。_change.py中FleetChangeMixin.change_fleet()的detect_fleet()调用没有传入expected_names。- 因此自定义舰名/模糊 OCR 的实际流程仍不会使用该修复。请接通调用链并测试真实调用路径,而不只是孤立 helper。
-
已在舰队中的主候选不会验证舰种与等级约束
_match_existing_members()/_slot_matches()只检查身份或search_name;ship_type、min_level、max_level只在重新选船时检查。- 同名但等级或舰种不合格的现有成员可能被直接保留,违反“主候选严格验证”的契约。
- 请让保留现有成员和新选择使用相同约束,并覆盖 existing-member early-return 测试。
-
API NodeDecision 字段传播不完整
- API schema 未覆盖 YAML 已支持的
enemy_formation_rules、SL_when_spot_enemy_fails、SL_when_enter_fight、formation_when_spot_enemy_fails等字段。 - 通过 GUI/API 和通过 YAML 执行同一计划可能产生不同结果。请补齐 schema/serializer 或明确缩小 PR 声明的契约。
- API schema 未覆盖 YAML 已支持的
-
与 GUI #17 的纯候选槽位语义不一致
- 后端允许没有顶层
name的 candidate-only slot,并将候选视为平等替代项。 - 当前 GUI #17 会将
candidates[0]提升为顶层name,改变为严格主候选 + fallback。 - 两仓合并前需要确定唯一规范并增加跨仓契约用例。
- 后端允许没有顶层
验收要求
- 为上述 1–4 增加后端回归测试;
- 与 GUI #17 对 candidate-only slot 达成一致;
- 补充真实模拟器上的换船/OCR/失败中止验证记录;
- 保持现有 legacy YAML 与 current-main 行为兼容。
此外 Codecov 报告 patch coverage 89.03%,其中 serializers.py 的改动覆盖率为 0%,恰好包含上述 API 地图问题,建议一并补齐。
yltx
left a comment
There was a problem hiding this comment.
本次追加审查针对的是架构回归,不是前一轮已指出的几个局部正确性 Bug。当前实现把舰队选择规则同时扩散到 combat、server、ops 和 ui,如果只逐项修复活动地图解析、OCR fallback 和约束绕过,仍会留下一个必须跨四层同步修改的永久结构。
阻塞结论
PR 当前没有唯一的舰队规则内部模型。同一套 name / candidates / search_name / ship_type / min_level / max_level 语义至少被四处独立解释:
-
autowsgr/combat/plan.pyCombatPlan.fleet_presets_normalize_preset_slot()_normalize_ship_rule()_normalize_ship_types()
-
autowsgr/server/schemas.pyFleetShipRuleRequestFleetRuleRequest._upgrade_legacy_candidates()
-
autowsgr/ui/battle/fleet_change/_change.py_extract_selector()_normalize_option()_rule_field()
-
autowsgr/ui/choose_ship_page.py_selection_options()- 舰种、等级、候选和 relaxed constraints 的再次解释
这些不是四个单纯的边界 adapter;它们分别决定主候选提升、旧约束继承、去重、候选顺序、类型归一化和 fallback 行为,而且实现已经出现差异。因此当前结构不能合并。
1. CombatPlan 不应保存 GUI/YAML 原始传输结构
CombatPlan.fleet_presets: list[dict[str, Any]] | None 的注释直接称其为“GUI 整理后的舰队预设列表”。但 CombatEngine 不消费该字段,只有 NormalFightRunner 在战前准备阶段读取第一份 preset。
这把 GUI 文档组织和兼容迁移塞进了战斗状态机计划模型。删除测试很明确:如果在 YAML/operation 入口把舰队预设转换为类型化的战前舰队策略,CombatEngine 无须改动。
整改要求:
CombatPlan不得持有 GUI-shaped raw dict;- 如果计划确实需要携带舰队准备策略,应携带后端自己的类型化领域对象;
- 旧 YAML 兼容只在 YAML ingress adapter 中执行一次;
- 删除
CombatPlan中重复的舰队规则 normalization,或让它只委托唯一 canonical constructor。
2. Server DTO 不得直接流入 ops/UI
server/routes/task.py 当前把 FleetRuleRequest Pydantic 对象直接传入 runner;YAML 路径传入 dict;NormalFightRunner 因而接受 list[Any];FleetChangeMixin._rule_field() 再通过 dict/getattr 同时兼容两种对象。
这意味着 UI 隐式了解 HTTP DTO、YAML 字典和内部 selector 三种结构,server transport seam 没有完成转换。
整改要求:
- 在 server/YAML 边界把外部 DTO 转换为同一个内部类型;
NormalFightRunner不再接受list[Any];FleetChangeMixin不再用_rule_field()duck-type dict 与 Pydantic 对象;- UI 包不应解释 server 或 YAML 的兼容格式。
3. ChooseShipPage 不应成为第二个舰队规则策略引擎
页面控制器应负责 OCR、滚动、点击和页面原子操作。当前 _selection_options() 又重新决定候选顺序、legacy candidates、约束继承、主候选和 relaxed constraints;这些策略已在 FleetChangeMixin 中计算过一次。
整改要求:
- 候选策略、优先级、兼容迁移和约束放宽由一个上层模块负责;
ChooseShipPage接收一个 canonical selector,或每次接收一个已解析的具体选择尝试;- 删除页面层对
options/candidates双格式的重复解释; - OCR 页面操作可以留在 UI,领域候选策略不能留在 UI。
4. 舰种词汇必须统一转换
当前至少存在:
server/schemas.py::_ALLOWED_SHIP_TYPE_CODESChooseShipPage._SHIP_TYPE_KEYWORDSautowsgr/types.py::ShipType
三套词汇并不等价,尤其是 ddg、ddgaa、cgaa、ss_or_ssg 与领域枚举中的 ASDG、AADG、CG、BG、SC、NAP。
**整改要求:**建立唯一转换模块,明确:
- API code → domain type/constraint;
- domain type → OCR label;
- synthetic group(如
ss_or_ssg)的正式表示; - 未知 code 的失败行为。
不得继续在 schema 和页面中各维护一份表。
5. Override precedence 必须一次性解析
当前有效舰队策略的优先级分散在:
- route:顶层 request、inline plan、YAML plan;
- runner constructor:显式
fleet_rules与plan.fleet_presets; - preparation:rules 与 plain fleet;
- normal/event 路径还有不同的
fleet_id处理。
审查者必须阅读三层代码才能知道最终使用哪个舰队。
**整改要求:**在 operation/server 边界一次性构建类似 ResolvedFleetSelection 的内部命令,明确:
- 最终 fleet ID;
- canonical slot rules;
- plain fleet fallback;
- 数据来源和 override precedence。
后续 runner/UI 不得再次决定来源优先级。
建议的目标结构
名称可以调整,但职责必须类似:
YAML fleet preset dict ─┐
├─ adapter / compatibility constructor
HTTP FleetRuleRequest ──┘
↓
canonical FleetSlotRule[]
↓
ResolvedFleetSelection
↓
NormalFightRunner / ops
↓
FleetChange orchestration
↓
ChooseShipPage(OCR / scroll / click only)
例如 canonical model 至少需要表达:
@dataclass(frozen=True)
class ShipSelector:
name: str | None
search_name: str | None
ship_types: tuple[ShipTypeConstraint, ...]
min_level: int | None
max_level: int | None
@dataclass(frozen=True)
class FleetSlotRule:
primary: ShipSelector | None
alternatives: tuple[ShipSelector, ...]这里不是要求照抄类名,而是要求:只有一个内部不变量所有者,所有 ingress 都转换到它,ops/UI 不再接收 transport shape。
新增验收条件
除前一轮 correctness blockers 外,新提交必须证明:
- YAML 旧格式与 HTTP 新格式经过不同 adapter 后得到相同 canonical model;
- candidate-only slot 不会被任一边界偷偷提升为 strict primary;
- 每个 candidate 的独立类型/等级/search-name 约束在转换后保持不变;
CombatPlan、server DTO 和 UI 不再各自执行同一套 legacy migration;- runner/UI 公共签名中不再出现
list[Any]、raw dict/Pydantic 双态处理; - normal/event 的 override precedence 有参数化测试;
- 舰种 code 转换有单一测试矩阵;
- 真实调用链测试覆盖
route/YAML → canonical model → fleet change → ChooseShipPage,不是只测 schema validator; - 前一轮指出的活动入口、OCR expected-names、existing-member constraints 和 NodeDecision 字段问题在新架构中一并修复。
关于继续修补当前实现
这部分已经达到 Patch Freeze 条件:同一规则有多个状态/语义所有者,correctness 修复需要继续跨层增加分支,单元测试全绿仍不能证明真实调用链。请不要再在四个 normalizer 上分别补条件。
建议保留需求和已有测试,重做舰队规则内部模型及 ingress adapter;如果无法在当前分支中完整删除重复 normalization 和 Any seam,请从本 PR base 建立干净分支重提较小 PR。
|
补充一项可直接执行的整改依据:后端当前已经正式依赖 �utowsgr_native>=0.2.0,该包公开了 �utowsgr_native.VesselType,并提供 rom_english()、rom_chinese()、�s_english()、�s_chinese()。它已覆盖 BBG / ASDG / AADG / CG / SSG / SC / AP 等 canonical 类型。\n\n因此上一条 review 中的‘唯一舰种转换模块’不应再定义第四套后端枚举,而应优先以 �utowsgr_native.VesselType 作为 canonical vocabulary:\n\n |
|
更正上一条关于 �utowsgr_native.VesselType 的接入建议:包提供者已说明该接口正在整改,目前缺少 IDE/type-stub 信息、没有稳定子包边界,并要求导入整个扩展包。因此本 PR 暂不得直接把 canonical fleet-rule 模型绑定到当前 VesselType 接口,也不要为了赶整改进度新增对其未稳定 API 的依赖。\n\n当前应先在 AutoWSGR 内建立稳定、类型化且有测试的舰种约束 seam,并把 API aliases、组合约束(如 ss_or_ssg)和 OCR 展示转换集中到一个 adapter。等 �utowsgr_native 发布带明确子包、类型信息和兼容承诺的新接口后,再由该 adapter 接入,避免领域层和 server/UI 直接依赖原生扩展细节。上一条中‘不要维护多份手写词汇表’的结论仍成立,但接入 native 的时机延后。 |
|
补充更新: 已验证变化:
但仍然必须通过显式 adapter 接入,不能按英文名称直接转换
尤其注意: 如果使用
上游证据:
结论更新:现在可以解除“完全暂缓接入”,改为“允许通过后端拥有的 typed adapter 接入”;仍不接受把 native |
0f2ce87 to
3fd15ae
Compare
改动说明
CombatPlan支持规范化fleet_presets,并兼容旧版字符串candidates。fleet_rules时,可直接执行 YAML 中第一套舰队预设。search_name、多舰种、最低等级和最高等级。兼容性说明
candidates: [舰名1, 舰名2],后端会迁移为主选和位置级备选。name、只包含对象candidates的纯备选槽位。fleet_rules优先于 YAML 预设;YAML 预设优先于普通fleet舰名列表。测试
177 passed in 6.77sAll checks passed!git diff --check:通过后续验证
仍需在模拟器中验证 YAML 预设自动换船、主选失败后切换备选、备选 OCR 降级和 API
fleet_rules覆盖行为。