feat(scene): add scene editor, executor and fakes (phase 1) #5

Merged
KremeCN merged 10 commits from feat/scene-editor into main 2026-08-25 20:20:19 +08:00
Member

Summary

Adds scene integration phase 1: domain model, fakes, UI, navigation, and tests.

Changes

  • domain: Add Scene, SceneAction (SocketPower/InfraredKey), SceneRepository, SceneExecutor, and execution result models.
  • testing/fakes: Add FakeSceneRepository (isolated by ProviderProfileId, seeded defaults for the local profile) and FakeSceneExecutor (sequential execution, continue-on-failure).
  • android/app: Add scene UI module - list screen, editor screen, execution progress screen - with Hilt-bound ViewModels.
  • navigation: Wire scene routes into ABoxManagerApp NavHost.
  • strings: Add scene_* entries for en and zh-rCN.
  • tests: Cover FakeSceneRepository CRUD/isolation and FakeSceneExecutor ordering/continue-on-failure.

Test plan

  • ./gradlew :testing:fakes:test passes.
  • FakeSceneRepository isolates scenes by provider profile.
  • FakeSceneExecutor runs actions in order and continues on failure.

Notes

Phase 1 ships fakes only; real repository/executor backed by the abox gateway will land in a follow-up.

## Summary Adds scene integration phase 1: domain model, fakes, UI, navigation, and tests. ## Changes - **domain**: Add `Scene`, `SceneAction` (SocketPower/InfraredKey), `SceneRepository`, `SceneExecutor`, and execution result models. - **testing/fakes**: Add `FakeSceneRepository` (isolated by `ProviderProfileId`, seeded defaults for the local profile) and `FakeSceneExecutor` (sequential execution, continue-on-failure). - **android/app**: Add scene UI module - list screen, editor screen, execution progress screen - with Hilt-bound ViewModels. - **navigation**: Wire scene routes into `ABoxManagerApp` NavHost. - **strings**: Add `scene_*` entries for en and zh-rCN. - **tests**: Cover `FakeSceneRepository` CRUD/isolation and `FakeSceneExecutor` ordering/continue-on-failure. ## Test plan - [x] `./gradlew :testing:fakes:test` passes. - [x] `FakeSceneRepository` isolates scenes by provider profile. - [x] `FakeSceneExecutor` runs actions in order and continues on failure. ## Notes Phase 1 ships fakes only; real repository/executor backed by the abox gateway will land in a follow-up.
pr-agent-bot bot reviewed 2026-08-24 11:31:04 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -189,5 +189,5 @@
}
}
internal fun com.flagship.abox.manager.domain.SceneColor.toColorOrNull(): Color? {
val hex = value.removePrefix("#")

编译错误

SceneColor 只定义了 hex 属性,此处访问不存在的 value 会导致主源码无法编译。应改为使用 hex.removePrefix("#")。

**编译错误** `SceneColor` 只定义了 `hex` 属性,此处访问不存在的 `value` 会导致主源码无法编译。应改为使用 `hex.removePrefix("#")`。 <!-- pr-agent-dedup: a50340fdd105 --> <!-- pr-agent-key-issue-location: 7c2f22d07761 -->
pr-agent-bot bot reviewed 2026-08-24 11:31:04 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -19,5 +19,5 @@
* [FakeSceneExecutor] that drives the existing socket/infrared fakes. The bindings live in the
* debug source set so the release build does not ship the Fake implementation; the real provider
* bindings will land in `feat/scene-persistence` and `feat/provider-login` respectively.
*/
@Module

发布构建

SceneRepository、SceneExecutor 和带 LocalProfileId 限定符的 ProviderProfileId 仅在 debug 源集中提供,但依赖它们的 ViewModel 位于 main 源集。当前没有 release 绑定,执行 release 变体的 Hilt 组件生成时会出现 MissingBinding;需要提供 release 实现或将相关消费者限制在 debug 变体。

**发布构建** `SceneRepository`、`SceneExecutor` 和带 `LocalProfileId` 限定符的 `ProviderProfileId` 仅在 debug 源集中提供,但依赖它们的 ViewModel 位于 main 源集。当前没有 release 绑定,执行 release 变体的 Hilt 组件生成时会出现 MissingBinding;需要提供 release 实现或将相关消费者限制在 debug 变体。 <!-- pr-agent-dedup: 9b2e0a4e82db --> <!-- pr-agent-key-issue-location: f99d94b4cef4 -->
pr-agent-bot bot reviewed 2026-08-24 11:31:04 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -53,5 +53,5 @@
) {
val scenes by viewModel.scenes.collectAsStateWithLifecycle()
val pendingDelete by viewModel.pendingDelete.collectAsStateWithLifecycle()
Scaffold(

实验 API

此处使用了 Material 3 的 TopAppBar,但文件没有像另外两个新增页面一样声明 ExperimentalMaterial3Api opt-in。在该 API 仍为实验状态的依赖版本下,这会导致编译失败;应为对应 Composable 添加 @OptIn(ExperimentalMaterial3Api::class)。

**实验 API** 此处使用了 Material 3 的 `TopAppBar`,但文件没有像另外两个新增页面一样声明 `ExperimentalMaterial3Api` opt-in。在该 API 仍为实验状态的依赖版本下,这会导致编译失败;应为对应 Composable 添加 `@OptIn(ExperimentalMaterial3Api::class)`。 <!-- pr-agent-dedup: 08c1d62de7aa --> <!-- pr-agent-key-issue-location: 4ba0616d8fbc -->
pr-agent-bot bot reviewed 2026-08-24 11:31:04 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table>
Moler added 3 commits 2026-08-25 11:47:58 +08:00
Introduce the scene module skeleton that satisfies phase 1 exit conditions for
tzh's responsibility in project_plan.md section 6.5: scene list, editor and Fake-driven
execution that can be tested independently.

Domain layer (domain/):
- Scene, SceneAction (SocketPower / InfraredKey), SceneIcon, SceneColor
- SceneExecutionResult with per-action outcome aggregation
- SceneRepository port (observe/get/create/update/delete, provider-isolated)
- SceneExecutor port (sequential execution, per-action failure continues)

Fakes (testing/fakes/):
- FakeSceneRepository: in-memory, provider-isolated, seeds local-experience catalog
- FakeSceneExecutor: drives existing SocketGateway/InfraredGateway fakes,
  rejects concurrent execution of the same scene

Android app (android/app/):
- SceneListScreen / SceneListViewModel: list, empty state, delete confirm
- SceneEditorScreen / SceneEditorViewModel: name/icon/color, action add/remove/reorder
- SceneExecutionScreen / SceneExecutionViewModel: run, progress, per-action summary
- SceneRoutes + NavGraphBuilder extensions wired into ABoxManagerApp
- SceneModule (debug) binds FakeSceneRepository, FakeSceneExecutor and a
  LocalProfileId qualifier that feat/provider-login will retire later

Build (settings.gradle.kts):
- Apply foojay-resolver-convention so the JVM 17 toolchain is auto-provisioned

Resources: zh-rCN / en strings for all scene surfaces
Tests: 20 unit tests covering repository isolation, CRUD, executor ordering,
failure continuation and concurrent-execution rejection
Moler force-pushed feat/scene-editor from c9dc3e7a77 to 6b4ef585fb 2026-08-25 11:47:58 +08:00 Compare
pr-agent-bot bot reviewed 2026-08-25 11:51:52 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -56,5 +56,5 @@
androidx.compose.runtime.LaunchedEffect(state.isSaved) {
if (state.isSaved) onSaved()
}
androidx.compose.runtime.LaunchedEffect(sceneId) {

状态丢失

LaunchedEffect(sceneId) 会在 Activity 配置变更后重新执行,但 Hilt ViewModel 会被保留;此时 initialize 会重置新场景状态或从仓库覆盖编辑状态,导致用户旋转屏幕后丢失尚未保存的名称、颜色和动作。应确保同一 sceneId 只初始化一次,或通过 SavedStateHandle 保存编辑状态。

**状态丢失** `LaunchedEffect(sceneId)` 会在 Activity 配置变更后重新执行,但 Hilt ViewModel 会被保留;此时 `initialize` 会重置新场景状态或从仓库覆盖编辑状态,导致用户旋转屏幕后丢失尚未保存的名称、颜色和动作。应确保同一 `sceneId` 只初始化一次,或通过 `SavedStateHandle` 保存编辑状态。 <!-- pr-agent-dedup: 6e081e02d9e0 --> <!-- pr-agent-key-issue-location: 70ce30b89dc9 -->
pr-agent-bot bot reviewed 2026-08-25 11:51:52 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -25,5 +25,5 @@
override suspend fun createScene(
profileId: ProviderProfileId,
scene: Scene,
): DomainResult<Scene> {
require(scene.profileId == profileId) {

隔离失效

创建和更新时只校验 Scene.profileId,没有校验每个 SceneAction.profileId。传入包含其他配置动作的场景会被保存并发布到当前配置,违反 SceneRepository 声明的动作隔离约束;配置切换期间保存编辑状态即可触发这种混合数据。请同时校验所有动作均属于传入的配置,并同步修复 fake 实现。

**隔离失效** 创建和更新时只校验 `Scene.profileId`,没有校验每个 `SceneAction.profileId`。传入包含其他配置动作的场景会被保存并发布到当前配置,违反 `SceneRepository` 声明的动作隔离约束;配置切换期间保存编辑状态即可触发这种混合数据。请同时校验所有动作均属于传入的配置,并同步修复 fake 实现。 <!-- pr-agent-dedup: 65dc5433fee6 --> <!-- pr-agent-key-issue-location: 322602ac4b24 -->
pr-agent-bot bot reviewed 2026-08-25 11:51:52 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -28,5 +28,5 @@
): DomainResult<Scene> {
require(scene.profileId == profileId) {
"Scene profileId must match the supplied profileId"
}
val scenes = currentScenes(profileId).orEmpty()

并发覆盖

创建操作采用非原子的“读取、检查、发布”流程。两个线程同时为同一配置创建不同场景时,可能读取相同旧列表,后一次发布会覆盖前一次结果并静默丢失场景;更新和删除也有相同问题。ConcurrentHashMap 只保护映射本身,应使用按配置的互斥锁或原子状态更新包住完整的读改写操作。

**并发覆盖** 创建操作采用非原子的“读取、检查、发布”流程。两个线程同时为同一配置创建不同场景时,可能读取相同旧列表,后一次发布会覆盖前一次结果并静默丢失场景;更新和删除也有相同问题。`ConcurrentHashMap` 只保护映射本身,应使用按配置的互斥锁或原子状态更新包住完整的读改写操作。 <!-- pr-agent-dedup: 1e12daa75cf2 --> <!-- pr-agent-key-issue-location: 82d394f22953 -->
pr-agent-bot bot reviewed 2026-08-25 11:51:52 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table>
KremeCN added 1 commit 2026-08-25 15:48:33 +08:00
pr-agent-bot bot reviewed 2026-08-25 15:53:20 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -56,5 +56,5 @@
androidx.compose.runtime.LaunchedEffect(state.isSaved) {
if (state.isSaved) onSaved()
}
androidx.compose.runtime.LaunchedEffect(sceneId) {

草稿丢失

配置更改导致界面重建时,此副作用会在保留原 Hilt ViewModel 的情况下再次调用 initialize。该方法会重置新建场景或重新加载已有场景,因此用户尚未保存的名称、颜色和动作会被静默覆盖。应让初始化针对同一 sceneId 保持幂等,或通过 SavedStateHandle 只初始化一次。

**草稿丢失** 配置更改导致界面重建时,此副作用会在保留原 Hilt ViewModel 的情况下再次调用 `initialize`。该方法会重置新建场景或重新加载已有场景,因此用户尚未保存的名称、颜色和动作会被静默覆盖。应让初始化针对同一 `sceneId` 保持幂等,或通过 `SavedStateHandle` 只初始化一次。 <!-- pr-agent-dedup: 5221aa87cb7b --> <!-- pr-agent-key-issue-location: d864255316c6 -->
pr-agent-bot bot reviewed 2026-08-25 15:53:20 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -36,5 +36,5 @@
fun initialize(sceneId: SceneId) {
_uiState.value = SceneExecutionUiState.Loading
viewModelScope.launch {
val profile = profileStore.activeProfile()

跨配置执行

活动配置文件仅在初始化时读取,加载后的场景会一直保存在 Ready 状态。若用户停留在执行页时切换提供方,随后仍会使用旧场景的 profileId 向网关发送命令。应监听配置文件切换并使当前场景失效,或在执行前重新校验活动配置文件与 scene.profileId 一致。

**跨配置执行** 活动配置文件仅在初始化时读取,加载后的场景会一直保存在 `Ready` 状态。若用户停留在执行页时切换提供方,随后仍会使用旧场景的 `profileId` 向网关发送命令。应监听配置文件切换并使当前场景失效,或在执行前重新校验活动配置文件与 `scene.profileId` 一致。 <!-- pr-agent-dedup: 353f16aeadc1 --> <!-- pr-agent-key-issue-location: 4c701012d709 -->
pr-agent-bot bot reviewed 2026-08-25 15:53:21 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -50,5 +50,5 @@
}
}
}
fun execute() {

重复执行

execute 明确接受 Running 状态,因此快速重复点击或重复事件会启动第二次执行。执行器会拒绝第二次调用,而该失败会把仍在正常运行的界面状态覆盖为 Failed。此处应仅允许从 Ready 状态开始执行。

**重复执行** `execute` 明确接受 `Running` 状态,因此快速重复点击或重复事件会启动第二次执行。执行器会拒绝第二次调用,而该失败会把仍在正常运行的界面状态覆盖为 `Failed`。此处应仅允许从 `Ready` 状态开始执行。 <!-- pr-agent-dedup: d0b9a0bd3b44 --> <!-- pr-agent-key-issue-location: 5bbdc5f5b2c9 -->
pr-agent-bot bot reviewed 2026-08-25 15:53:21 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 Security concerns

跨配置文件操作:执行页缓存初始化时加载的场景,提供方切换后不会重新校验活动 ProviderProfile,可能向非当前活动提供方的设备发送控制命令。应在执行前验证 scene.profileId,并在活动配置文件变化时退出或重新加载场景。

## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 4 🔵🔵🔵🔵⚪</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>Security concerns</strong><br><br> 跨配置文件操作:执行页缓存初始化时加载的场景,提供方切换后不会重新校验活动 `ProviderProfile`,可能向非当前活动提供方的设备发送控制命令。应在执行前验证 `scene.profileId`,并在活动配置文件变化时退出或重新加载场景。</td></tr> </table>
KremeCN added 1 commit 2026-08-25 15:54:40 +08:00
pr-agent-bot bot reviewed 2026-08-25 16:00:03 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -44,5 +44,5 @@
private val _state = MutableStateFlow(SceneEditorState())
val state: StateFlow<SceneEditorState> = _state.asStateFlow()
fun initialize(sceneId: SceneId?) {

状态重置

initialize 对相同参数也会无条件重置或重新加载状态。Activity 配置变更后 ViewModel 会保留,但界面的 LaunchedEffect 会再次调用此方法,导致新建场景的输入全部丢失,或编辑中的修改被仓库版本覆盖。应记录已初始化的场景 ID,或使用 SavedStateHandle 保存编辑状态,避免重复初始化。

**状态重置** `initialize` 对相同参数也会无条件重置或重新加载状态。Activity 配置变更后 ViewModel 会保留,但界面的 `LaunchedEffect` 会再次调用此方法,导致新建场景的输入全部丢失,或编辑中的修改被仓库版本覆盖。应记录已初始化的场景 ID,或使用 `SavedStateHandle` 保存编辑状态,避免重复初始化。 <!-- pr-agent-dedup: cf3b7ac56d25 --> <!-- pr-agent-key-issue-location: ce4b9c356768 -->
pr-agent-bot bot reviewed 2026-08-25 16:00:03 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -33,5 +33,5 @@
private val _uiState = MutableStateFlow<SceneExecutionUiState>(SceneExecutionUiState.Loading)
val uiState: StateFlow<SceneExecutionUiState> = _uiState.asStateFlow()
fun initialize(sceneId: SceneId) {

执行状态丢失

对同一场景重复调用 initialize 会把 Running 或 Completed 无条件重置为 Loading,随后变为 Ready。配置变更会重新触发界面初始化,因此已完成的结果会消失;执行过程中旋转设备还会重新显示执行按钮,而原协程仍在运行。应让初始化对同一场景幂等,并保留进行中及完成状态。

**执行状态丢失** 对同一场景重复调用 `initialize` 会把 `Running` 或 `Completed` 无条件重置为 `Loading`,随后变为 `Ready`。配置变更会重新触发界面初始化,因此已完成的结果会消失;执行过程中旋转设备还会重新显示执行按钮,而原协程仍在运行。应让初始化对同一场景幂等,并保留进行中及完成状态。 <!-- pr-agent-dedup: b97d8304ffda --> <!-- pr-agent-key-issue-location: 666775487b17 -->
pr-agent-bot bot reviewed 2026-08-25 16:00:03 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -28,5 +28,5 @@
): DomainResult<Scene> {
require(scene.profileId == profileId) {
"Scene profileId must match the supplied profileId"
}
val scenes = currentScenes(profileId).orEmpty()

更新丢失

场景修改采用非原子的“读取列表后再发布”流程。两个协程同时为同一配置创建场景时,可能都基于相同旧列表计算结果,最后一次发布会覆盖前一次,静默丢失一个场景;更新和删除也存在同类竞争。应使用按配置隔离的互斥锁或其他原子事务保护完整的校验与写入过程。

**更新丢失** 场景修改采用非原子的“读取列表后再发布”流程。两个协程同时为同一配置创建场景时,可能都基于相同旧列表计算结果,最后一次发布会覆盖前一次,静默丢失一个场景;更新和删除也存在同类竞争。应使用按配置隔离的互斥锁或其他原子事务保护完整的校验与写入过程。 <!-- pr-agent-dedup: e041f0bf6d5e --> <!-- pr-agent-key-issue-location: b2be8d2de054 -->
pr-agent-bot bot reviewed 2026-08-25 16:00:03 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table>
KremeCN added 1 commit 2026-08-25 16:15:43 +08:00
- Support manual one-tap commands and automatic condition-based rules
- Add modular triggers (time, temperature, humidity, device state)
- Add modular conditions (environment thresholds, socket state, time range)
- Upgrade actions to support virtual remotes, sockets, delays, and notifications
- Support sequential/parallel concurrency mode and error execution strategies
- Refactor SceneListScreen with filter tabs, quick execution, and enable switches
- Refactor SceneEditorScreen with modular builders and preset pickers
- Enhance SceneExecutionScreen with step timing, skips, and rerun capability
- Add comprehensive domain and UI ViewModel unit tests
pr-agent-bot bot reviewed 2026-08-25 16:18:41 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -31,5 +31,5 @@
val startTime = System.currentTimeMillis()
val results = mutableListOf<SceneActionExecutionResult>()
var aborted = false
return try {

并行失效

执行逻辑完全未读取 scene.concurrencyMode,所有动作始终通过循环串行执行。用户选择 PARALLEL 后,延时等动作仍会阻塞后续动作,使编辑器中的并行设置失效,并显著改变执行时长和预期行为。

**并行失效** 执行逻辑完全未读取 `scene.concurrencyMode`,所有动作始终通过循环串行执行。用户选择 `PARALLEL` 后,延时等动作仍会阻塞后续动作,使编辑器中的并行设置失效,并显著改变执行时长和预期行为。 <!-- pr-agent-dedup: 7e22b393cc56 --> <!-- pr-agent-key-issue-location: 2a617e7fe21b -->
pr-agent-bot bot reviewed 2026-08-25 16:18:41 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -107,5 +107,5 @@
)
}
}
is SceneAction.Delay -> {

取消失效

catch (e: Exception) 会捕获协程的 CancellationException 并将其转换为普通动作失败。执行在延时期间被取消时,CONTINUE_ON_ERROR 场景可能继续触发后续设备动作;应显式重新抛出取消异常。

**取消失效** `catch (e: Exception)` 会捕获协程的 `CancellationException` 并将其转换为普通动作失败。执行在延时期间被取消时,`CONTINUE_ON_ERROR` 场景可能继续触发后续设备动作;应显式重新抛出取消异常。 <!-- pr-agent-dedup: 72ba712345d4 --> <!-- pr-agent-key-issue-location: 9b9cee04d1aa -->
pr-agent-bot bot reviewed 2026-08-25 16:18:41 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -90,5 +90,5 @@
)
toExecutionResult(action, res)
}
is SceneAction.VirtualRemoteButton -> {

虚假成功

构造器允许省略 sendVirtualRemoteAction,但此时虚拟遥控动作不会发送任何命令,却仍被记录为成功。任何未注入该依赖的运行环境都会向用户报告执行成功,而实际设备没有响应;应将依赖设为必需或返回明确失败。

**虚假成功** 构造器允许省略 `sendVirtualRemoteAction`,但此时虚拟遥控动作不会发送任何命令,却仍被记录为成功。任何未注入该依赖的运行环境都会向用户报告执行成功,而实际设备没有响应;应将依赖设为必需或返回明确失败。 <!-- pr-agent-dedup: 18747106f32d --> <!-- pr-agent-key-issue-location: 70d29b931d85 -->
pr-agent-bot bot reviewed 2026-08-25 16:18:41 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModelTest.kt
  • android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneEditorViewModelTest.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/Scene.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepository.kt
  • testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneExecutorTest.kt
  • testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneRepositoryTest.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneNavigation.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModel.kt
  • android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModelTest.kt
  • domain/src/test/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepositoryTest.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModel.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/di/SceneIntegrationModule.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecution.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneRepository.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecutor.kt
  • settings.gradle.kts
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • gradle.properties
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModelTest.kt` - `android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneEditorViewModelTest.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/Scene.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepository.kt` - `testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneExecutorTest.kt` - `testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneRepositoryTest.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneNavigation.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModel.kt` - `android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModelTest.kt` - `domain/src/test/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepositoryTest.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModel.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/di/SceneIntegrationModule.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecution.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneRepository.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecutor.kt` - `settings.gradle.kts` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `gradle.properties`
KremeCN added 1 commit 2026-08-25 16:21:37 +08:00
pr-agent-bot bot reviewed 2026-08-25 16:25:18 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -896,5 +896,5 @@
var notifyMsg by remember { mutableStateOf("自动化任务执行完成") }
val coroutineScope = rememberCoroutineScope()
LaunchedEffect(availableRemotes, selectedRemoteIndex) {

绑定竞态

LaunchedEffect 内又通过独立的 rememberCoroutineScope 启动查询,切换遥控器时旧查询不会随 Effect 取消,较慢的旧结果可能覆盖当前遥控器的按键列表。用户快速切换后确认时,可能保存“当前遥控器 + 另一遥控器按键”的错误组合;应直接在 LaunchedEffect 中执行挂起查询,并在切换时清空旧绑定。

**绑定竞态** `LaunchedEffect` 内又通过独立的 `rememberCoroutineScope` 启动查询,切换遥控器时旧查询不会随 Effect 取消,较慢的旧结果可能覆盖当前遥控器的按键列表。用户快速切换后确认时,可能保存“当前遥控器 + 另一遥控器按键”的错误组合;应直接在 `LaunchedEffect` 中执行挂起查询,并在切换时清空旧绑定。 <!-- pr-agent-dedup: 535be9484938 --> <!-- pr-agent-key-issue-location: 8ddcb697f4a2 -->
pr-agent-bot bot reviewed 2026-08-25 16:25:18 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -53,5 +53,5 @@
val isSaving: Boolean = false,
val isSaved: Boolean = false,
val errorMessage: String? = null,
) {
val isValid: Boolean get() = name.isNotBlank() && actions.isNotEmpty()

校验缺失

isValid 没有要求自动场景至少包含一个触发器,因此用户只填写名称和动作就能保存一个已启用但永远不会被触发的自动场景。应在类型为 AUTOMATIC 时要求 triggers 非空,或将其明确保存为禁用草稿。

**校验缺失** `isValid` 没有要求自动场景至少包含一个触发器,因此用户只填写名称和动作就能保存一个已启用但永远不会被触发的自动场景。应在类型为 `AUTOMATIC` 时要求 `triggers` 非空,或将其明确保存为禁用草稿。 <!-- pr-agent-dedup: 3de3aa0f9568 --> <!-- pr-agent-key-issue-location: 0ace8ec93357 -->
pr-agent-bot bot reviewed 2026-08-25 16:25:18 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -54,5 +54,5 @@
): DomainResult<Scene> {
require(scene.profileId == profileId) {
"Scene profileId must match the supplied profileId"
}
val scenes = currentScenes(profileId).orEmpty()

并发丢失

创建场景采用非原子的“读取列表后再发布”流程。同一配置下两个并发 createScene 都可能基于相同旧列表追加并返回成功,但后发布的结果会覆盖前一个,导致其中一个场景丢失。应对每个配置使用互斥锁,或将重复检查和列表更新合并为原子操作。

**并发丢失** 创建场景采用非原子的“读取列表后再发布”流程。同一配置下两个并发 `createScene` 都可能基于相同旧列表追加并返回成功,但后发布的结果会覆盖前一个,导致其中一个场景丢失。应对每个配置使用互斥锁,或将重复检查和列表更新合并为原子操作。 <!-- pr-agent-dedup: aea6a8725121 --> <!-- pr-agent-key-issue-location: 5ec14616b43d -->
pr-agent-bot bot reviewed 2026-08-25 16:25:18 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModelTest.kt
  • android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneEditorViewModelTest.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/Scene.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepository.kt
  • testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneExecutorTest.kt
  • testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneRepositoryTest.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/DefaultSceneExecutor.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneNavigation.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModel.kt
  • domain/src/test/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepositoryTest.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModel.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/di/SceneIntegrationModule.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecution.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneRepository.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecutor.kt
  • domain/build.gradle.kts
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • gradle.properties
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModelTest.kt` - `android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneEditorViewModelTest.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/Scene.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepository.kt` - `testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneExecutorTest.kt` - `testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneRepositoryTest.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/DefaultSceneExecutor.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneNavigation.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModel.kt` - `domain/src/test/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepositoryTest.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModel.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/di/SceneIntegrationModule.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecution.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneRepository.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecutor.kt` - `domain/build.gradle.kts` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `gradle.properties`
KremeCN added 1 commit 2026-08-25 17:17:15 +08:00
pr-agent-bot bot reviewed 2026-08-25 17:22:19 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -289,5 +289,5 @@
deviceName = action.deviceName,
key = action.key,
)
is SceneAction.VirtualRemoteButton,

虚假成功

VirtualRemoteButton、Delay 和 Notification 都被直接当作成功处理,实际没有发送遥控、等待指定时长或发出通知。用户添加这些动作后,执行页会显示成功但副作用未发生,尤其顺序场景中的延时完全失效。请实现对应行为,或将不支持的动作返回明确失败。

**虚假成功** `VirtualRemoteButton`、`Delay` 和 `Notification` 都被直接当作成功处理,实际没有发送遥控、等待指定时长或发出通知。用户添加这些动作后,执行页会显示成功但副作用未发生,尤其顺序场景中的延时完全失效。请实现对应行为,或将不支持的动作返回明确失败。 <!-- pr-agent-dedup: 63f37a006989 --> <!-- pr-agent-key-issue-location: b4168a0be833 -->
pr-agent-bot bot reviewed 2026-08-25 17:22:20 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -759,5 +759,5 @@
FilterChip(selected = selectedType == 2, onClick = { selectedType = 2 }, label = { Text("💧 湿度") })
}
when (selectedType) {
0 -> {
OutlinedTextField(

输入校验

timeInput 可以被编辑为任意字符串,确认按钮始终可用并直接创建触发器。输入 abc、25:99 等值时,可能因领域构造校验异常而崩溃,或保存出无法调度的自动场景。应先按 HH:mm 解析和校验,无效时阻止提交并展示错误。

**输入校验** `timeInput` 可以被编辑为任意字符串,确认按钮始终可用并直接创建触发器。输入 `abc`、`25:99` 等值时,可能因领域构造校验异常而崩溃,或保存出无法调度的自动场景。应先按 `HH:mm` 解析和校验,无效时阻止提交并展示错误。 <!-- pr-agent-dedup: 1c5e4d106443 --> <!-- pr-agent-key-issue-location: 2ebe58455c22 -->
pr-agent-bot bot reviewed 2026-08-25 17:22:20 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -324,5 +324,5 @@
)
}
}
if (state.actions.isEmpty()) {

本地化缺失

新界面仍包含大量用户可见的中文硬编码,例如空动作提示,以及后续对话框标签和描述。在英文 locale 下会出现中英混排,新增的英文字符串资源也无法生效。请将这些文案统一移入 strings.xml 和 values-zh-rCN/strings.xml。

**本地化缺失** 新界面仍包含大量用户可见的中文硬编码,例如空动作提示,以及后续对话框标签和描述。在英文 locale 下会出现中英混排,新增的英文字符串资源也无法生效。请将这些文案统一移入 `strings.xml` 和 `values-zh-rCN/strings.xml`。 <!-- pr-agent-dedup: 8f469db38886 --> <!-- pr-agent-key-issue-location: 9a477b791860 -->
pr-agent-bot bot reviewed 2026-08-25 17:22:20 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • domain/src/test/kotlin/com/flagship/abox/manager/domain/DefaultSceneExecutorTest.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepository.kt
  • testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneRepositoryTest.kt
  • android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModelTest.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/Scene.kt
  • testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneExecutorTest.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/DefaultSceneExecutor.kt
  • android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModelTest.kt
  • domain/src/test/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepositoryTest.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneNavigation.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModel.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModel.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/di/SceneIntegrationModule.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecution.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneRepository.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecutor.kt
  • settings.gradle.kts
  • domain/build.gradle.kts
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • gradle.properties
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `domain/src/test/kotlin/com/flagship/abox/manager/domain/DefaultSceneExecutorTest.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepository.kt` - `testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneRepositoryTest.kt` - `android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModelTest.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/Scene.kt` - `testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneExecutorTest.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/DefaultSceneExecutor.kt` - `android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModelTest.kt` - `domain/src/test/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepositoryTest.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneNavigation.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModel.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModel.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/di/SceneIntegrationModule.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecution.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneRepository.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecutor.kt` - `settings.gradle.kts` - `domain/build.gradle.kts` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `gradle.properties`
KremeCN added 1 commit 2026-08-25 17:35:45 +08:00
pr-agent-bot bot reviewed 2026-08-25 17:41:41 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -103,5 +103,5 @@
)
}
return
}
viewModelScope.launch {

编辑竞态

编辑现有场景时,sceneId 只有在异步查询成功后才写入状态,且没有加载态阻止编辑或保存。仓库响应较慢或失败时,页面会按“新建”处理;用户若完成有效输入并保存,save() 将调用 createScene,产生重复场景而不是更新目标。应先绑定待编辑的 ID,并在加载完成前禁用保存。

**编辑竞态** 编辑现有场景时,`sceneId` 只有在异步查询成功后才写入状态,且没有加载态阻止编辑或保存。仓库响应较慢或失败时,页面会按“新建”处理;用户若完成有效输入并保存,`save()` 将调用 `createScene`,产生重复场景而不是更新目标。应先绑定待编辑的 ID,并在加载完成前禁用保存。 <!-- pr-agent-dedup: 41ec446dd682 --> <!-- pr-agent-key-issue-location: 2c71b6e33002 -->
pr-agent-bot bot reviewed 2026-08-25 17:41:41 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -224,5 +224,5 @@
)
}
val startTime = System.currentTimeMillis()
return try {

策略失效

PARALLEL 分支一次性启动所有动作,完全未处理 scene.strategy。当用户组合选择并行与 ABORT_ON_ERROR 时,即使一个动作失败,其余插座或红外等副作用仍会执行,使“遇错中止”失效。应禁止该组合,或在失败后取消尚未完成的任务并明确已启动动作的语义。

**策略失效** `PARALLEL` 分支一次性启动所有动作,完全未处理 `scene.strategy`。当用户组合选择并行与 `ABORT_ON_ERROR` 时,即使一个动作失败,其余插座或红外等副作用仍会执行,使“遇错中止”失效。应禁止该组合,或在失败后取消尚未完成的任务并明确已启动动作的语义。 <!-- pr-agent-dedup: 7ef796cc7fb8 --> <!-- pr-agent-key-issue-location: d6ce8a1fade9 -->
pr-agent-bot bot reviewed 2026-08-25 17:41:41 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -76,5 +76,5 @@
}
private fun observeAvailableRemotes() {
viewModelScope.launch {
profileStore.observeActiveProfile().collectLatest { profile ->

设备硬编码

observeRemotes 将红外设备名固定为中文字符串 红外控制器。只要当前配置中的实际设备名不同,例如英文名称、自定义名称或默认设备 ID,仓库查询就无法加载该设备的虚拟遥控器,遥控动作选择器会错误显示为空。应从当前配置或设备列表取得真实的 DeviceName。

**设备硬编码** `observeRemotes` 将红外设备名固定为中文字符串 `红外控制器`。只要当前配置中的实际设备名不同,例如英文名称、自定义名称或默认设备 ID,仓库查询就无法加载该设备的虚拟遥控器,遥控动作选择器会错误显示为空。应从当前配置或设备列表取得真实的 `DeviceName`。 <!-- pr-agent-dedup: 2b0abea93edd --> <!-- pr-agent-key-issue-location: 10a59afea5f1 -->
pr-agent-bot bot reviewed 2026-08-25 17:41:41 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • domain/src/test/kotlin/com/flagship/abox/manager/domain/DefaultSceneExecutorTest.kt
  • android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneEditorViewModelTest.kt
  • testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneExecutorTest.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepository.kt
  • testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneRepositoryTest.kt
  • android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModelTest.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/Scene.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/DefaultSceneExecutor.kt
  • domain/src/test/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepositoryTest.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneNavigation.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModel.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModel.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/di/SceneIntegrationModule.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecution.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneRepository.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecutor.kt
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • gradle.properties
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `domain/src/test/kotlin/com/flagship/abox/manager/domain/DefaultSceneExecutorTest.kt` - `android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneEditorViewModelTest.kt` - `testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneExecutorTest.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepository.kt` - `testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneRepositoryTest.kt` - `android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModelTest.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/Scene.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/DefaultSceneExecutor.kt` - `domain/src/test/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepositoryTest.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneNavigation.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModel.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModel.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/di/SceneIntegrationModule.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecution.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneRepository.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecutor.kt` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `gradle.properties`
KremeCN added 1 commit 2026-08-25 18:00:39 +08:00
pr-agent-bot bot reviewed 2026-08-25 18:03:52 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -229,5 +229,5 @@
return try {
val results: List<SceneActionExecutionResult> = if (scene.concurrencyMode == ConcurrencyMode.PARALLEL) {
if (scene.strategy == ExecutionStrategy.ABORT_ON_ERROR) {
coroutineScope {
lateinit var deferreds: List<Deferred<SceneActionExecutionResult>>

并发崩溃

并行且遇错终止时,子协程启动后会访问尚未完成赋值的 deferreds。在 Main.immediate 或 Unconfined 等可立即执行子协程的调度器上,首个动作同步失败会抛出 UninitializedPropertyAccessException,导致执行器直接异常退出而不是返回失败结果。应先以惰性启动方式构建完整列表,再统一启动任务。

**并发崩溃** 并行且遇错终止时,子协程启动后会访问尚未完成赋值的 `deferreds`。在 `Main.immediate` 或 `Unconfined` 等可立即执行子协程的调度器上,首个动作同步失败会抛出 `UninitializedPropertyAccessException`,导致执行器直接异常退出而不是返回失败结果。应先以惰性启动方式构建完整列表,再统一启动任务。 <!-- pr-agent-dedup: 2fe123f90bd7 --> <!-- pr-agent-key-issue-location: ed9914d39478 -->
pr-agent-bot bot reviewed 2026-08-25 18:03:52 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -1104,5 +1104,5 @@
}
}
},
confirmButton = {
val canConfirm = when (actionType) {

无效遥控器

当没有可用虚拟遥控器时,确认按钮仍默认可用,并创建仓库中不存在的硬编码 default-ir 遥控器动作。没有配置遥控器的用户因此可以保存一个必然可能在执行时失败的场景;应禁用确认操作,或在确实需要原始红外回退时创建对应的 InfraredKey 动作。

**无效遥控器** 当没有可用虚拟遥控器时,确认按钮仍默认可用,并创建仓库中不存在的硬编码 `default-ir` 遥控器动作。没有配置遥控器的用户因此可以保存一个必然可能在执行时失败的场景;应禁用确认操作,或在确实需要原始红外回退时创建对应的 `InfraredKey` 动作。 <!-- pr-agent-dedup: ed6602c26582 --> <!-- pr-agent-key-issue-location: a0b114ebdbfa -->
pr-agent-bot bot reviewed 2026-08-25 18:03:52 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -342,5 +342,5 @@
val state = _state.value
if (!state.isValid || state.isSaving) return
_state.update { it.copy(isSaving = true, errorMessage = null) }
viewModelScope.launch {
val profile = profileStore.activeProfile()

配置串用

保存时重新读取当前活动配置,而已加载场景及其动作仍属于打开编辑器时的配置。如果用户在编辑器保留期间切换配置,代码会用新配置 ID 保存包含旧配置动作的场景;仓库的配置一致性校验可能直接抛出异常,或把更新提交到错误的配置。应在初始化时固定场景所属的 profileId,并在配置变化时关闭或重新初始化编辑器。

**配置串用** 保存时重新读取当前活动配置,而已加载场景及其动作仍属于打开编辑器时的配置。如果用户在编辑器保留期间切换配置,代码会用新配置 ID 保存包含旧配置动作的场景;仓库的配置一致性校验可能直接抛出异常,或把更新提交到错误的配置。应在初始化时固定场景所属的 `profileId`,并在配置变化时关闭或重新初始化编辑器。 <!-- pr-agent-dedup: b0b3f2622977 --> <!-- pr-agent-key-issue-location: e10e4609eba9 -->
pr-agent-bot bot reviewed 2026-08-25 18:03:52 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionScreen.kt
  • testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneExecutorTest.kt
  • android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneEditorViewModelTest.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepository.kt
  • testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneRepositoryTest.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/DefaultSceneExecutor.kt
  • android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModelTest.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/Scene.kt
  • android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModelTest.kt
  • domain/src/test/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepositoryTest.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneNavigation.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModel.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModel.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecution.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneRepository.kt
  • domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecutor.kt
  • settings.gradle.kts
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • gradle.properties
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionScreen.kt` - `testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneExecutorTest.kt` - `android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneEditorViewModelTest.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepository.kt` - `testing/fakes/src/test/kotlin/com/flagship/abox/manager/testing/fakes/FakeSceneRepositoryTest.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/DefaultSceneExecutor.kt` - `android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModelTest.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/Scene.kt` - `android/app/src/test/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModelTest.kt` - `domain/src/test/kotlin/com/flagship/abox/manager/domain/InMemorySceneRepositoryTest.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneNavigation.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListViewModel.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneExecutionViewModel.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecution.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneRepository.kt` - `domain/src/main/kotlin/com/flagship/abox/manager/domain/SceneExecutor.kt` - `settings.gradle.kts` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `gradle.properties`
KremeCN merged commit 1570a95f30 into main 2026-08-25 20:20:19 +08:00
KremeCN deleted branch feat/scene-editor 2026-08-25 20:20:19 +08:00
Sign in to join this conversation.
No Reviewers
No labels
4 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Flagship/abox-mgr#5