Skip to content

fix(copilot): single-task copilot uses filename instead of copilot_list#492

Open
travellerse wants to merge 2 commits intoMaaAssistantArknights:mainfrom
travellerse:fix/single_file_copilot
Open

fix(copilot): single-task copilot uses filename instead of copilot_list#492
travellerse wants to merge 2 commits intoMaaAssistantArknights:mainfrom
travellerse:fix/single_file_copilot

Conversation

@travellerse
Copy link

@travellerse travellerse commented Feb 16, 2026

当只有一个 copilot 作业(且 --raid != 2)时传递 filename,多个作业仍使用 copilot_list。以此修复像 剿灭作战 这类需在右下角有 “开始作战” 界面启动任务却因为战斗列表尝试进行导航而无法启动的问题。

Summary by Sourcery

调整 copilot 预设参数的生成逻辑:对于单个且非 raid 模式的任务,使用单文件模式,并相应更新测试。

Bug Fixes:

  • 修复单任务 copilot 运行问题:当仅配置一个 job 且 raid 模式不为 2 时,通过传递顶层的 filename 参数而不是 copilot_list。

Tests:

  • 更新 copilot 预设测试,用于在单任务场景下断言基于 filename 的参数。
Original summary in English

Summary by Sourcery

Adjust copilot preset parameter generation to use single-file mode for a single non-raid task and update tests accordingly.

Bug Fixes:

  • Fix single-task copilot runs by passing a top-level filename parameter instead of a copilot_list when only one job is configured and raid mode is not 2.

Tests:

  • Update copilot preset tests to assert filename-based parameters for single-task scenarios.

@travellerse travellerse force-pushed the fix/single_file_copilot branch from 99f59c3 to 901a185 Compare February 16, 2026 14:41
Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - 我发现了两个问题,并给出了一些整体性的反馈:

  • 条件 if copilot_files_count == 1 && self.raid != 2raid 模式引入了一个“魔法数字”;建议用具名常量或枚举值来替代 2,以便更清晰地表达意图并减少出错风险。
  • stage_list.into_iter().next() 上直接使用 unwrap() 假定 copilot_files_count == 1 总能保证 stage_list 非空;为避免在列表不同步时可能出现的 panic,更安全的做法是显式处理 None 情况(例如通过带有清晰错误信息的 ok_or_else),这样可以提升健壮性。
给 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- 条件 `if copilot_files_count == 1 && self.raid != 2``raid` 模式引入了一个“魔法数字”;建议用具名常量或枚举值来替代 `2`,以便更清晰地表达意图并减少出错风险。
-`stage_list.into_iter().next()` 上直接使用 `unwrap()` 假定 `copilot_files_count == 1` 总能保证 `stage_list` 非空;为避免在列表不同步时可能出现的 panic,更安全的做法是显式处理 `None` 情况(例如通过带有清晰错误信息的 `ok_or_else`),这样可以提升健壮性。

## Individual Comments

### Comment 1
<location> `crates/maa-cli/src/run/preset/copilot.rs:168-169` </location>
<code_context>
         let ignore_requirements =
             self.ignore_requirements || default.get_or("ignore_requirements", false);

+        let copilot_files_count = copilot_files.len();
         let mut stage_list = Vec::new();
         for (_, file, value) in copilot_files {
             let copilot_task = value.map(Ok).unwrap_or_else(|| json_from_file(&file))?;
</code_context>

<issue_to_address>
**suggestion (bug_risk):**`copilot_files_count` 作为 `stage_list` 大小的代理,会导致下方的 `unwrap()` 在循环行为以后发生变化时变得脆弱。

目前 `copilot_files` 与推入 `stage_list` 的条目是 1:1 映射,因此 `copilot_files_count == 1` 恰好意味着 `stage_list` 中有一个元素。如果之后循环增加了条件行为(例如跳过无效条目),这个假设就会失效,即使 `copilot_files_count` 仍为 1,单文件分支中的 `unwrap()` 也可能触发 panic。为避免这种耦合,建议在循环结束后基于 `stage_list.len()` 分支,而不是基于原始的 `copilot_files` 长度。

建议实现:

```rust
        let ignore_requirements =
            self.ignore_requirements || default.get_or("ignore_requirements", false);

        let mut stage_list = Vec::new();
        for (_, file, value) in copilot_files {
            let copilot_task = value.map(Ok).unwrap_or_else(|| json_from_file(&file))?;
            }
        }

```

在该循环之后,任何当前基于 `copilot_files_count` 分支的地方,都应改为基于 `stage_list.len()`1.`if copilot_files_count == 1 { ... }` 之类的条件替换为 `if stage_list.len() == 1 { ... }`2. 如果单元素分支当前类似这样实现:
   ```rust
   let only = stage_list.into_iter().next().unwrap();
   ```
   这样仍然是安全的,因为 `stage_list.len() == 1` 能保证存在一个元素,但要确保在这个检查之前没有其他分支会消耗/清空 `stage_list`3. 删除现在已不再使用的 `copilot_files_count` 变量(上面的编辑已经去掉了声明,同时也要移除函数中其他地方对它的引用)。
</issue_to_address>

### Comment 2
<location> `crates/maa-cli/src/run/preset/copilot.rs:222-228` </location>
<code_context>
             "support_unit_name" =>? self.support_unit_name
         );

+        // Use single file mode when there's only one task and raid mode is not 2
+        if copilot_files_count == 1 && self.raid != 2 {
+            let stage_opt = stage_list.into_iter().next().unwrap();
+            insert!(params,
+                "filename" => stage_opt.filename.to_string_lossy().to_string()
</code_context>

<issue_to_address>
**suggestion (bug_risk):**`stage_list` 无条件调用 `unwrap()` 在边界情况下有触发 panic 的风险;建议采用更安全的模式或显式的假设。

这会让该分支对未来可能导致 `stage_list` 为空的更改变得很脆弱(例如后续增加的过滤/校验逻辑)。如果不变式是假定只有在恰好有一个元素时才会走到这个分支,那么可以使用 `expect("single-file mode requires exactly one copilot stage")`,或对 `stage_list.as_slice()` 做模式匹配,以更明确地强化和记录这一假设。

```suggestion
        // Use single file mode when there's only one task and raid mode is not 2
        if copilot_files_count == 1 && self.raid != 2 {
            let stage_opt = stage_list
                .into_iter()
                .next()
                .expect("single-file mode requires exactly one copilot stage");
            insert!(params,
                "filename" => stage_opt.filename.to_string_lossy().to_string()
            );
        } else {
```
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用吧!请对每条评论点 👍 或 👎,我会根据这些反馈改进后续评审。
Original comment in English

Hey - I've found 2 issues, and left some high level feedback:

  • The if copilot_files_count == 1 && self.raid != 2 condition introduces a magic number for the raid mode; consider replacing 2 with a named constant or enum value to make the intent clearer and less error-prone.
  • Using unwrap() on stage_list.into_iter().next() assumes copilot_files_count == 1 always implies stage_list is non-empty; it would be safer to handle the None case explicitly (e.g., via ok_or_else with a descriptive error) to avoid a potential panic if the lists ever get out of sync.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `if copilot_files_count == 1 && self.raid != 2` condition introduces a magic number for the `raid` mode; consider replacing `2` with a named constant or enum value to make the intent clearer and less error-prone.
- Using `unwrap()` on `stage_list.into_iter().next()` assumes `copilot_files_count == 1` always implies `stage_list` is non-empty; it would be safer to handle the `None` case explicitly (e.g., via `ok_or_else` with a descriptive error) to avoid a potential panic if the lists ever get out of sync.

## Individual Comments

### Comment 1
<location> `crates/maa-cli/src/run/preset/copilot.rs:168-169` </location>
<code_context>
         let ignore_requirements =
             self.ignore_requirements || default.get_or("ignore_requirements", false);

+        let copilot_files_count = copilot_files.len();
         let mut stage_list = Vec::new();
         for (_, file, value) in copilot_files {
             let copilot_task = value.map(Ok).unwrap_or_else(|| json_from_file(&file))?;
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Using `copilot_files_count` as a proxy for `stage_list` size can make the `unwrap()` below fragile if the loop behavior diverges later.

Currently there’s a 1:1 mapping between `copilot_files` and entries pushed to `stage_list`, so `copilot_files_count == 1` happens to imply `stage_list` has one element. If the loop later gains any conditional behavior (e.g., skipping invalid entries), that assumption breaks and the `unwrap()` in the single-file branch can panic even when `copilot_files_count` is 1. To avoid this coupling, branch on `stage_list.len()` after the loop instead of the original `copilot_files` length.

Suggested implementation:

```rust
        let ignore_requirements =
            self.ignore_requirements || default.get_or("ignore_requirements", false);

        let mut stage_list = Vec::new();
        for (_, file, value) in copilot_files {
            let copilot_task = value.map(Ok).unwrap_or_else(|| json_from_file(&file))?;
            }
        }

```

Anywhere below this loop that currently branches on `copilot_files_count` should instead branch on `stage_list.len()`:

1. Replace conditions like `if copilot_files_count == 1 { ... }` with `if stage_list.len() == 1 { ... }`.
2. If the single-element branch currently does something like:
   ```rust
   let only = stage_list.into_iter().next().unwrap();
   ```
   it is still safe because `stage_list.len() == 1` guarantees one element, but ensure there are no branches where `stage_list` could be consumed/cleared before this check.
3. Remove the now-unused `copilot_files_count` variable entirely (the edit above handles the declaration; also remove any remaining references elsewhere in the function).
</issue_to_address>

### Comment 2
<location> `crates/maa-cli/src/run/preset/copilot.rs:222-228` </location>
<code_context>
             "support_unit_name" =>? self.support_unit_name
         );

+        // Use single file mode when there's only one task and raid mode is not 2
+        if copilot_files_count == 1 && self.raid != 2 {
+            let stage_opt = stage_list.into_iter().next().unwrap();
+            insert!(params,
+                "filename" => stage_opt.filename.to_string_lossy().to_string()
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Unconditional `unwrap()` on `stage_list` risks a panic in edge cases; consider a safer pattern or explicit assumption.

This makes the branch fragile to future changes that might leave `stage_list` empty (e.g., later validation that filters entries). If the invariant is that this branch only runs with exactly one element, prefer `expect("single-file mode requires exactly one copilot stage")` or matching on `stage_list.as_slice()` to enforce and document that assumption more explicitly.

```suggestion
        // Use single file mode when there's only one task and raid mode is not 2
        if copilot_files_count == 1 && self.raid != 2 {
            let stage_opt = stage_list
                .into_iter()
                .next()
                .expect("single-file mode requires exactly one copilot stage");
            insert!(params,
                "filename" => stage_opt.filename.to_string_lossy().to_string()
            );
        } else {
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@travellerse travellerse force-pushed the fix/single_file_copilot branch from b8f11aa to ba3f573 Compare February 16, 2026 14:50
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.

1 participant

Comments