fix(copilot): single-task copilot uses filename instead of copilot_list#492
Open
travellerse wants to merge 2 commits intoMaaAssistantArknights:mainfrom
Open
fix(copilot): single-task copilot uses filename instead of copilot_list#492travellerse wants to merge 2 commits intoMaaAssistantArknights:mainfrom
filename instead of copilot_list#492travellerse wants to merge 2 commits intoMaaAssistantArknights:mainfrom
Conversation
99f59c3 to
901a185
Compare
There was a problem hiding this comment.
Hey - 我发现了两个问题,并给出了一些整体性的反馈:
- 条件
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),这样可以提升健壮性。
给 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>帮我变得更有用吧!请对每条评论点 👍 或 👎,我会根据这些反馈改进后续评审。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- The
if copilot_files_count == 1 && self.raid != 2condition introduces a magic number for theraidmode; consider replacing2with a named constant or enum value to make the intent clearer and less error-prone. - Using
unwrap()onstage_list.into_iter().next()assumescopilot_files_count == 1always impliesstage_listis non-empty; it would be safer to handle theNonecase explicitly (e.g., viaok_or_elsewith 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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
b8f11aa to
ba3f573
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
当只有一个 copilot 作业(且 --raid != 2)时传递
filename,多个作业仍使用copilot_list。以此修复像 剿灭作战 这类需在右下角有 “开始作战” 界面启动任务却因为战斗列表尝试进行导航而无法启动的问题。Summary by Sourcery
调整 copilot 预设参数的生成逻辑:对于单个且非 raid 模式的任务,使用单文件模式,并相应更新测试。
Bug Fixes:
Tests:
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:
Tests: