Skip to content

修改YAML和换船识别机制,以适配GUI2.0 - #521

Open
ShiinaKuroko wants to merge 6 commits into
OpenWSGR:mainfrom
ShiinaKuroko:ShiinaKuroko
Open

修改YAML和换船识别机制,以适配GUI2.0#521
ShiinaKuroko wants to merge 6 commits into
OpenWSGR:mainfrom
ShiinaKuroko:ShiinaKuroko

Conversation

@ShiinaKuroko

Copy link
Copy Markdown
Contributor

改动说明

  • 统一作战计划 YAML、HTTP API 与换船执行层的舰队规则结构。
  • CombatPlan 支持规范化 fleet_presets,并兼容旧版字符串 candidates
  • 常规战与活动战在未传入 fleet_rules 时,可直接执行 YAML 中第一套舰队预设。
  • 每个槽位支持独立主选与位置级备选规则,包括 search_name、多舰种、最低等级和最高等级。
  • 主选舰严格校验舰名、舰种和等级;备选舰先尝试校验,识别失败或不匹配时允许按已确认舰名降级选择。
  • 补齐 API 舰队规则 Schema、节点决策字段转换和活动名称传递,避免接口接收字段后执行层丢失。
  • 换船失败时中止出征,避免错误舰队继续进入战斗。

兼容性说明

  • 继续支持旧格式 candidates: [舰名1, 舰名2],后端会迁移为主选和位置级备选。
  • 支持没有顶层 name、只包含对象 candidates 的纯备选槽位。
  • API 显式 fleet_rules 优先于 YAML 预设;YAML 预设优先于普通 fleet 舰名列表。
  • 备选降级只放宽舰种和等级判断,舰名本身仍必须识别匹配。

测试

  • 定向测试:177 passed in 6.77s
  • Ruff:All checks passed!
  • git diff --check:通过
  • 已覆盖预设解析、旧候选迁移、纯备选槽位、多舰种、预设自动执行、独立候选规则与宽松备选行为。

后续验证

仍需在模拟器中验证 YAML 预设自动换船、主选失败后切换备选、备选 OCR 降级和 API fleet_rules 覆盖行为。

@codecov

codecov Bot commented Aug 2, 2026

Copy link
Copy Markdown

@yltx yltx left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本次审计结论:Request changes。CI 的 lint/pytest 均通过,但以下运行时与跨仓契约问题尚未被测试覆盖,合并前需要修复并增加回归测试。

阻塞项

  1. API 活动入口地图未按 YAML 语义解析

    • autowsgr/server/serializers.pybuild_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 解析路径,并补充入口后缀回归测试。
  2. OCR target-context fallback 在实际换船流程中不可达

    • _detect.py 只有收到 expected_names 才启用新 fallback。
    • _change.pyFleetChangeMixin.change_fleet()detect_fleet() 调用没有传入 expected_names
    • 因此自定义舰名/模糊 OCR 的实际流程仍不会使用该修复。请接通调用链并测试真实调用路径,而不只是孤立 helper。
  3. 已在舰队中的主候选不会验证舰种与等级约束

    • _match_existing_members() / _slot_matches() 只检查身份或 search_nameship_typemin_levelmax_level 只在重新选船时检查。
    • 同名但等级或舰种不合格的现有成员可能被直接保留,违反“主候选严格验证”的契约。
    • 请让保留现有成员和新选择使用相同约束,并覆盖 existing-member early-return 测试。
  4. API NodeDecision 字段传播不完整

    • API schema 未覆盖 YAML 已支持的 enemy_formation_rulesSL_when_spot_enemy_failsSL_when_enter_fightformation_when_spot_enemy_fails 等字段。
    • 通过 GUI/API 和通过 YAML 执行同一计划可能产生不同结果。请补齐 schema/serializer 或明确缩小 PR 声明的契约。
  5. 与 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 yltx left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本次追加审查针对的是架构回归,不是前一轮已指出的几个局部正确性 Bug。当前实现把舰队选择规则同时扩散到 combatserveropsui,如果只逐项修复活动地图解析、OCR fallback 和约束绕过,仍会留下一个必须跨四层同步修改的永久结构。

阻塞结论

PR 当前没有唯一的舰队规则内部模型。同一套 name / candidates / search_name / ship_type / min_level / max_level 语义至少被四处独立解释:

  1. autowsgr/combat/plan.py

    • CombatPlan.fleet_presets
    • _normalize_preset_slot()
    • _normalize_ship_rule()
    • _normalize_ship_types()
  2. autowsgr/server/schemas.py

    • FleetShipRuleRequest
    • FleetRuleRequest._upgrade_legacy_candidates()
  3. autowsgr/ui/battle/fleet_change/_change.py

    • _extract_selector()
    • _normalize_option()
    • _rule_field()
  4. 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_CODES
  • ChooseShipPage._SHIP_TYPE_KEYWORDS
  • autowsgr/types.py::ShipType

三套词汇并不等价,尤其是 ddgddgaacgaass_or_ssg 与领域枚举中的 ASDGAADGCGBGSCNAP

**整改要求:**建立唯一转换模块,明确:

  • 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_rulesplan.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 外,新提交必须证明:

  1. YAML 旧格式与 HTTP 新格式经过不同 adapter 后得到相同 canonical model;
  2. candidate-only slot 不会被任一边界偷偷提升为 strict primary;
  3. 每个 candidate 的独立类型/等级/search-name 约束在转换后保持不变;
  4. CombatPlan、server DTO 和 UI 不再各自执行同一套 legacy migration;
  5. runner/UI 公共签名中不再出现 list[Any]、raw dict/Pydantic 双态处理;
  6. normal/event 的 override precedence 有参数化测试;
  7. 舰种 code 转换有单一测试矩阵;
  8. 真实调用链测试覆盖 route/YAML → canonical model → fleet change → ChooseShipPage,不是只测 schema validator;
  9. 前一轮指出的活动入口、OCR expected-names、existing-member constraints 和 NodeDecision 字段问题在新架构中一并修复。

关于继续修补当前实现

这部分已经达到 Patch Freeze 条件:同一规则有多个状态/语义所有者,correctness 修复需要继续跨层增加分支,单元测试全绿仍不能证明真实调用链。请不要再在四个 normalizer 上分别补条件。

建议保留需求和已有测试,重做舰队规则内部模型及 ingress adapter;如果无法在当前分支中完整删除重复 normalization 和 Any seam,请从本 PR base 建立干净分支重提较小 PR。

@yltx

yltx commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

补充一项可直接执行的整改依据:后端当前已经正式依赖 �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 ext\nAPI aliases / synthetic groups\n -> one adapter\nVesselType\n -> OCR/display adapter\n\n\n现有 API 值 ddg / ddgaa / cgaa 应在一个地方显式映射到 native 的 ASDG / AADG / CG;ss_or_ssg 是组合约束,应建模为类型集合或 predicate,而不是伪装成单个 VesselType。server/schemas.py::_ALLOWED_SHIP_TYPE_CODES 与 ChooseShipPage._SHIP_TYPE_KEYWORDS 不应继续各自维护完整表。请为所有 API alias、native canonical 值、组合约束和未知值补参数化测试。

@yltx

yltx commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

更正上一条关于 �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 的时机延后。

@yltx

yltx commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

补充更新:autowsgr_native 0.3.0 已于 2026-08-03 发布。此前我提出的“暂缓接入,等待稳定子包和类型信息”这一阻塞条件现在已经解除,但这不意味着可以直接用 native 类型替换后端领域模型。

已验证变化:

  • 新的公开导入路径:
    • from autowsgr_native.vessel_type import VesselType
    • from autowsgr_native.recognition import locate, recognize_enemy, recognize_map
  • 根包不再导出旧 API;0.2.0 -> 0.3.0 是明确声明的 breaking minor upgrade。
  • 新增 py.typed.pyi,现在具备可供 IDE/type checker 使用的公开类型契约。
  • VesselType.Fortess 已更正为 VesselType.Fortress,无兼容别名。

但仍然必须通过显式 adapter 接入,不能按英文名称直接转换 ShipType

AutoWSGR native 含义
NAP AP 补给
BG BBG 导战
CBG BG 大巡
Other 无直接对应 其他

尤其注意:

AutoWSGR ShipType.BG == 导战
native VesselType.BG == 大巡

如果使用 VesselType.from_english(ship_type.name),会产生不报错的静默误分类。因此建议:

  1. 将依赖作为显式迁移先固定到 autowsgr_native==0.3.0,不要继续使用允许未来 breaking minor 自动进入的 >=
  2. 单独迁移 recognition import path,并增加 native API 契约测试;
  3. 保留 AutoWSGR canonical ShipType
  4. 在唯一 adapter 中集中维护 NAP/APBG/BBGCBG/BG 等语义映射;
  5. route、combat、ops、ui 不得各自再复制转换逻辑;
  6. 增加明确断言:ShipType.BG 绝不能映射到 VesselType.BG

上游证据:

结论更新:现在可以解除“完全暂缓接入”,改为“允许通过后端拥有的 typed adapter 接入”;仍不接受把 native VesselType 直接扩散为 server/combat/ops/ui 的共享领域模型。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants