diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeePolicySnapshotRowDto.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeePolicySnapshotRowDto.java index 8de3a66d..5d64d22e 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeePolicySnapshotRowDto.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeePolicySnapshotRowDto.java @@ -51,6 +51,16 @@ public class DigitalEmployeePolicySnapshotRowDto { private Boolean autoDecisionDisabled; @JsonProperty("policy_reason") private String policyReason; + @JsonProperty("dispatch_policy_key") + private String dispatchPolicyKey; + @JsonProperty("max_per_sweep") + private Long maxPerSweep; + @JsonProperty("auto_dispatch_ready_steps") + private Boolean autoDispatchReadySteps; + @JsonProperty("catch_up_on_startup") + private Boolean catchUpOnStartup; + @JsonProperty("max_run_history") + private Long maxRunHistory; @JsonProperty("sync_record_url") private String syncRecordUrl; @JsonProperty("client_key_masked") diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java index e2ee1302..738c2099 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java @@ -614,6 +614,11 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye .batchAcceptRemoteDisabled(nullableBooleanValue(firstPresent(latestView.get("batch_accept_remote_disabled"), latestView.get("batchAcceptRemoteDisabled")))) .autoDecisionDisabled(nullableBooleanValue(firstPresent(latestView.get("auto_decision_disabled"), latestView.get("autoDecisionDisabled")))) .policyReason(firstString(latestView.get("policy_reason"), latestView.get("policyReason"))) + .dispatchPolicyKey(firstString(latestView.get("dispatch_policy_key"), latestView.get("dispatchPolicyKey"))) + .maxPerSweep(numberValue(firstPresent(latestView.get("max_per_sweep"), latestView.get("maxPerSweep")))) + .autoDispatchReadySteps(nullableBooleanValue(firstPresent(latestView.get("auto_dispatch_ready_steps"), latestView.get("autoDispatchReadySteps")))) + .catchUpOnStartup(nullableBooleanValue(firstPresent(latestView.get("catch_up_on_startup"), latestView.get("catchUpOnStartup")))) + .maxRunHistory(numberValue(firstPresent(latestView.get("max_run_history"), latestView.get("maxRunHistory")))) .syncRecordUrl(syncRecordUrl(latest.getOutboxId())) .clientKeyMasked(maskClientKey(latest.getClientKey())) .deviceId(latest.getDeviceId()) @@ -687,7 +692,9 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye "policy_source", "auto_create_governance_commands", "max_per_sweep", + "auto_dispatch_ready_steps", "catch_up_on_startup", + "max_run_history", "approval_required_risk_levels", "approval_required_actions", "auto_reject_actions", diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java index 7b161402..a0786f5c 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java @@ -1882,6 +1882,82 @@ class DigitalEmployeeSyncApplicationServiceImplTest { assertThat(row.getRecommendedResolution()).isEqualTo("review_latest_policy_snapshot"); } + @Test + void queryAdminPolicySnapshotsSummarizesDispatchAndSchedulerPolicyConflicts() { + DigitalEmployeeSyncRecord dispatchA = record("outbox-dispatch-policy-a", "event", "event-dispatch-policy-a", 101L, new Date(10_000), + Map.of( + "entity_type", "event", + "category", "policy_snapshot", + "kind", "plan_step_dispatch_policy_pulled", + "policy_kind", "dispatch", + "policy_key", "plan_step_dispatch", + "enabled", true, + "max_per_sweep", 3, + "auto_dispatch_ready_steps", true, + "dispatch_policy_key", "plan_step_dispatch" + ), + Map.of("kind", "plan_step_dispatch_policy_pulled")); + dispatchA.setDeviceId("device-a"); + DigitalEmployeeSyncRecord dispatchB = record("outbox-dispatch-policy-b", "event", "event-dispatch-policy-b", 101L, new Date(11_000), + Map.of( + "entity_type", "event", + "category", "policy_snapshot", + "kind", "plan_step_dispatch_policy_pulled", + "policy_kind", "dispatch", + "policy_key", "plan_step_dispatch", + "enabled", true, + "max_per_sweep", 8, + "auto_dispatch_ready_steps", false, + "dispatch_policy_key", "plan_step_dispatch" + ), + Map.of("kind", "plan_step_dispatch_policy_pulled")); + dispatchB.setDeviceId("device-b"); + DigitalEmployeeSyncRecord schedulerA = record("outbox-scheduler-policy-a", "event", "event-scheduler-policy-a", 101L, new Date(12_000), + Map.of( + "entity_type", "event", + "category", "policy_snapshot", + "kind", "scheduler_policy_snapshot", + "policy_kind", "scheduler", + "policy_key", "scheduler", + "enabled", true, + "catch_up_on_startup", true, + "max_run_history", 20 + ), + Map.of("kind", "scheduler_policy_snapshot")); + schedulerA.setDeviceId("device-a"); + DigitalEmployeeSyncRecord schedulerB = record("outbox-scheduler-policy-b", "event", "event-scheduler-policy-b", 101L, new Date(13_000), + Map.of( + "entity_type", "event", + "category", "policy_snapshot", + "kind", "scheduler_policy_snapshot", + "policy_kind", "scheduler", + "policy_key", "scheduler", + "enabled", true, + "catch_up_on_startup", false, + "max_run_history", 50 + ), + Map.of("kind", "scheduler_policy_snapshot")); + schedulerB.setDeviceId("device-b"); + + when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq("event"), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(100L))) + .thenReturn(page(List.of(dispatchA, dispatchB, schedulerA, schedulerB))); + + DigitalEmployeePolicySnapshotPageDto dispatchResult = service.queryAdminPolicySnapshots(null, null, "conflict", "dispatch", null, null, 1L, 20L); + DigitalEmployeePolicySnapshotPageDto schedulerResult = service.queryAdminPolicySnapshots(null, null, "conflict", "scheduler", null, null, 1L, 20L); + + DigitalEmployeePolicySnapshotRowDto dispatchRow = dispatchResult.getRecords().get(0); + assertThat(dispatchRow.getPolicyKind()).isEqualTo("dispatch"); + assertThat(dispatchRow.getConflictKeys()).contains("max_per_sweep", "auto_dispatch_ready_steps"); + assertThat(dispatchRow.getDispatchPolicyKey()).isEqualTo("plan_step_dispatch"); + assertThat(dispatchRow.getMaxPerSweep()).isEqualTo(8L); + assertThat(dispatchRow.getAutoDispatchReadySteps()).isFalse(); + DigitalEmployeePolicySnapshotRowDto schedulerRow = schedulerResult.getRecords().get(0); + assertThat(schedulerRow.getPolicyKind()).isEqualTo("scheduler"); + assertThat(schedulerRow.getConflictKeys()).contains("catch_up_on_startup", "max_run_history"); + assertThat(schedulerRow.getCatchUpOnStartup()).isFalse(); + assertThat(schedulerRow.getMaxRunHistory()).isEqualTo(50L); + } + @Test void recordDailyReportSendResultStoresRecordedObservation() { DigitalEmployeeAdminRecordRequestDto request = adminRecordRequest("daily_report", "report-1"); diff --git a/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx index 64f82008..31625544 100644 --- a/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx +++ b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx @@ -726,6 +726,11 @@ const DigitalEmployeeOps: React.FC = () => { batch_accept_remote_disabled: record.batch_accept_remote_disabled, auto_decision_disabled: record.auto_decision_disabled, policy_reason: record.policy_reason, + dispatch_policy_key: record.dispatch_policy_key, + max_per_sweep: record.max_per_sweep, + auto_dispatch_ready_steps: record.auto_dispatch_ready_steps, + catch_up_on_startup: record.catch_up_on_startup, + max_run_history: record.max_run_history, latest_synced_at: record.latest_synced_at, synced_at: record.latest_synced_at, client_key_masked: record.client_key_masked, @@ -902,10 +907,26 @@ const DigitalEmployeeOps: React.FC = () => { ` · 跨设备路由干预冲突 ${detailRecord.intervention_conflict_status}`} {(detailRecord.approval_policy_id || detailRecord.conflict_policy_id || detailRecord.decision_guard_status) && ` · 跨设备审批策略冲突`} + {(detailRecord.policy_kind === 'dispatch' || + detailRecord.dispatch_policy_key || + detailRecord.max_per_sweep != null || + detailRecord.auto_dispatch_ready_steps != null) && + ` · 跨设备派发策略冲突`} + {(detailRecord.policy_kind === 'scheduler' || + detailRecord.catch_up_on_startup != null || + detailRecord.max_run_history != null) && + ` · 跨设备调度策略冲突`} {detailRecord.approval_policy_id && ` · 审批策略 ${detailRecord.approval_policy_id}`} {detailRecord.conflict_policy_id && ` · 冲突策略 ${detailRecord.conflict_policy_id}`} {detailRecord.decision_guard_status && ` · 裁决保护 ${detailRecord.decision_guard_status}`} {detailRecord.policy_reason && ` · 策略原因 ${detailRecord.policy_reason}`} + {detailRecord.dispatch_policy_key && ` · 派发策略 ${detailRecord.dispatch_policy_key}`} + {detailRecord.max_per_sweep != null && ` · max_per_sweep ${detailRecord.max_per_sweep}`} + {detailRecord.auto_dispatch_ready_steps != null && + ` · auto_dispatch_ready_steps ${detailRecord.auto_dispatch_ready_steps ? '开启' : '关闭'}`} + {detailRecord.catch_up_on_startup != null && + ` · catch_up_on_startup ${detailRecord.catch_up_on_startup ? '开启' : '关闭'}`} + {detailRecord.max_run_history != null && ` · max_run_history ${detailRecord.max_run_history}`} {detailRecord.batch_accept_remote_disabled && ' · 批量采纳远端禁用'} {detailRecord.auto_decision_disabled && ' · 自动裁决禁用'} {detailRecord.route_decision_count != null && diff --git a/qiming/src/types/interfaces/systemManage.ts b/qiming/src/types/interfaces/systemManage.ts index 977e8de3..1878da85 100644 --- a/qiming/src/types/interfaces/systemManage.ts +++ b/qiming/src/types/interfaces/systemManage.ts @@ -600,6 +600,10 @@ export interface DigitalEmployeeAdminWorkbenchRow { lease_source?: string; dispatch_status?: string; dispatch_policy_key?: string; + max_per_sweep?: number; + auto_dispatch_ready_steps?: boolean; + catch_up_on_startup?: boolean; + max_run_history?: number; orchestration_id?: string; trigger_source?: string; approval_id?: string; @@ -728,6 +732,11 @@ export interface DigitalEmployeePolicySnapshotRow { batch_accept_remote_disabled?: boolean; auto_decision_disabled?: boolean; policy_reason?: string; + dispatch_policy_key?: string; + max_per_sweep?: number; + auto_dispatch_ready_steps?: boolean; + catch_up_on_startup?: boolean; + max_run_history?: number; sync_record_url?: string; client_key_masked?: string; device_id?: string; diff --git a/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js b/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js index be7364fc..dd53fde6 100644 --- a/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js +++ b/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js @@ -1283,18 +1283,25 @@ function checkDigitalAdminPolicySnapshots() { [backendDto, "intervention_conflict_status", "backend route intervention conflict status field"], [backendDto, "approval_policy_id", "backend policy snapshot approval policy field"], [backendDto, "auto_decision_disabled", "backend policy snapshot auto decision field"], + [backendDto, "max_per_sweep", "backend policy snapshot max per sweep field"], + [backendDto, "catch_up_on_startup", "backend policy snapshot catch up field"], [backendDto, "route_decision_count", "backend route decision count field"], [backendTest, "queryAdminPolicySnapshotsSummarizesRouteInterventionConflicts", "backend route intervention conflict test"], [backendTest, "queryAdminPolicySnapshotsSummarizesApprovalPolicyConflicts", "backend approval policy conflict test"], + [backendTest, "queryAdminPolicySnapshotsSummarizesDispatchAndSchedulerPolicyConflicts", "backend dispatch scheduler policy conflict test"], [qimingService, "apiDigitalEmployeeAdminPolicySnapshots", "qiming policy snapshot service"], [qimingService, "/api/digital-employee/admin/policies/snapshots", "qiming policy snapshot endpoint"], [qimingTypes, "DigitalEmployeePolicySnapshotRow", "qiming policy snapshot row type"], [qimingTypes, "policy_conflicts", "qiming policy conflicts view type"], [qimingTypes, "approval_policy_id", "qiming approval policy snapshot type field"], + [qimingTypes, "max_per_sweep", "qiming dispatch policy snapshot type field"], + [qimingTypes, "catch_up_on_startup", "qiming scheduler policy snapshot type field"], [qimingTypes, "intervention_conflict_status", "qiming route intervention conflict type field"], [qimingOps, "策略冲突", "qiming policy conflict tab text"], [qimingOps, "conflict_keys", "qiming conflict key display"], [qimingOps, "跨设备审批策略冲突", "qiming approval policy conflict copy"], + [qimingOps, "跨设备派发策略冲突", "qiming dispatch policy conflict copy"], + [qimingOps, "跨设备调度策略冲突", "qiming scheduler policy conflict copy"], [qimingOps, "裁决保护", "qiming decision guard conflict copy"], [qimingOps, "跨设备路由干预冲突", "qiming route intervention conflict copy"], [qimingOps, "不恢复执行", "qiming route recovery guard copy"], @@ -1302,6 +1309,7 @@ function checkDigitalAdminPolicySnapshots() { [qimingOps, "不远程改策略", "qiming remote policy edit guard copy"], [docs, "跨设备策略冲突视图", "docs policy conflict absorption"], [docs, "跨设备审批策略冲突预览", "docs approval policy conflict absorption"], + [docs, "跨设备调度派发策略冲突预览", "docs dispatch scheduler policy conflict absorption"], [docs, "跨设备路由干预冲突可视化", "docs route intervention conflict absorption"], ]; const missing = requiredSnippets diff --git a/qimingclaw/docs/digital-employee-frontend-integration-plan.md b/qimingclaw/docs/digital-employee-frontend-integration-plan.md index 99ec3790..05ba1f02 100644 --- a/qimingclaw/docs/digital-employee-frontend-integration-plan.md +++ b/qimingclaw/docs/digital-employee-frontend-integration-plan.md @@ -551,13 +551,13 @@ GET /api/digital-employee/approvals 1. **PlanStep 与依赖图(依赖图调度已开始)** - 已吸收:Plan / Task / Run 记录、计划模板、`create_plan` 中的 steps/tasks 可物化为任务;PlanStep 已开始作为正式本地实体落入 `digital_plan_steps`。 - 当前能力:PlanStep 会进入 `digital_sync_outbox`,entity type 为 `plan_step`;任务中心 flow 和 debug store 优先读取真实 PlanStep 生成步骤图;Task Agent 的 retry / skip / cancel / pause / resume / input / run 控制已可更新真实 Task、PlanStep、Run、Event 和 outbox;前置步骤跳过、取消或重试后会重新评估后继步骤,推进为 ready / blocked / pending 并写入依赖图事件;scheduler sweep 会识别可派发的 ready PlanStep,派发前先通过管理端 `POST /api/digital-employee/leases/plan-step-dispatch/acquire` 做跨设备强租约仲裁,管理端拒绝会以 `remote_lease_rejected` 跳过,管理端异常会降级为 5 分钟本地软租约 `payload.dispatchLease.source = local_fallback` 并记录 `remote_error`;调用本地 `/computer/chat` 后会写入 `plan_step_dispatch_*` 与 `governance_start_run` 事件,派发完成或失败会先尝试 `release` 管理端租约再释放本地 lease 并写入 `plan_step_lease_*` 事件;已有未过期租约的步骤仍会以 `lease_active` 跳过;auto sweep 会按节流从管理端 `GET /api/digital-employee/policies/plan-step-dispatch` 拉取远端派发策略,拉取失败时保留本地策略并记录 `last_pull_error`;embedded 已提供 `/api/digital-employee/plan-steps`、`/api/digital-employee/plan-step-graph`、`/api/digital-employee/plans/:planId/dispatch-ready-steps`、`/api/plan-step/dispatch-policy` 和 `/api/plan-step/dispatch-policy/pull`,ready PlanStep 安全派发、计划级跨任务推进、管理端依赖图视图、本地/远端派发策略、可观察租约状态和首页阻塞依赖处理入口已开始闭环。派发策略管理 UI 已开始吸收,embedded 新增“策略中心”集中展示和编辑 `enabled/max_per_sweep/auto_dispatch_ready_steps`。正式管理端策略中心预览已开始吸收,qiming-backend 新增 `policy_center` 运营台视图聚合派发、风险审批、入口路由和技能策略快照,qiming“数字员工运营台”的“策略中心”tab 可记录批量拉取策略、标记已看和忽略意图,不开放远程策略编辑。正式管理端步骤调度预览已开始吸收,qiming“数字员工运营台”的“步骤调度”tab 会聚合 PlanStep fact、依赖状态、租约、派发事件和策略拉取审计,只记录标记已看、忽略和重试策略检查意图,不远程派发、不抢占租约、不释放租约。 - - 后续缺口:跨设备审批策略冲突预览已开始吸收,策略冲突页可展示审批策略、冲突策略、裁决保护和自动裁决保护差异;远程策略编辑安全模型、真实派发/租约裁决和跨设备策略自动合并仍待吸收。 + - 后续缺口:跨设备审批策略冲突预览已开始吸收,策略冲突页可展示审批策略、冲突策略、裁决保护和自动裁决保护差异;跨设备调度派发策略冲突预览已开始吸收,策略冲突页可展示 `max_per_sweep`、`auto_dispatch_ready_steps`、`catch_up_on_startup` 和 `max_run_history` 差异;远程策略编辑安全模型、真实派发/租约裁决和跨设备策略自动合并仍待吸收。 - 建议:下一轮围绕管理端正式路由 UI 或跨端审批策略 UI,把本地可治理调度继续推进到跨端治理策略。 2. **调度 / 定时 / 周期任务** - 已吸收:`digital_schedules`、`digital_schedule_runs` 已进入 qimingclaw 本地 SQLite 与 outbox,entity type 为 `schedule` / `schedule_run`;旧 `/api/cron`、`/api/scheduler/jobs` 和治理命令会优先读取真实本地调度记录。 - 当前能力:主进程 `DigitalEmployeeScheduler` 可按本机 5 字段 cron 到点触发本地 `/computer/chat`,支持启动 catch-up、立即运行、暂停/恢复/删除、失败重试、运行历史和同步诊断。多调度并发策略 UI 已开始吸收,embedded 新增“调度运营”入口,可查看 scheduler jobs、最近运行、`catch_up_on_startup`、`max_run_history`,并触发一次立即检查调度。正式管理端调度运营预览已开始吸收,qiming-backend 新增 `scheduler_operations` 运营台视图聚合 scheduler job、cron settings、lease、dispatch sweep 和 recent run 摘要,qiming“数字员工运营台”的“调度运营”tab 只开放立即检查调度、标记已看和忽略意图,不开放 start/stop 或高风险立即运行。 - - 后续缺口:更细的秒级 cron、复杂日历排除规则、正式外部管理端调度策略下发和跨设备调度冲突合并仍待吸收。 + - 后续缺口:跨设备调度派发策略冲突预览已开始吸收,管理端策略冲突页只读展示调度 catch-up、运行历史和派发 sweep 策略差异,不立即调度、不接管租约、不自动合并策略;更细的秒级 cron、复杂日历排除规则、正式外部管理端调度策略下发和跨设备调度冲突合并仍待吸收。 - 建议:下一轮围绕入口路由/任务分流,把 schedule 触发的计划与具体 Skill Policy、Task Agent 状态机更精确地绑定。 3. **MemoryEntry / 长期记忆(已开始)** @@ -609,7 +609,7 @@ GET /api/digital-employee/approvals - 建议:下一轮围绕管理端正式审计视图和诊断修复动作,把本地可追溯审计继续推进到可运营处置。 12. **管理端业务查询模型** - - 已吸收:管理端已有通用 sync record 接收、查询、payload 摘要和深链;后端 sync record 已开始保存客户端顶层 `contract_version`、`entity_summary` 和 `business_view`,并基于现有 `digital_employee_sync_record` 拆出 `/api/digital-employee/plans`、`/plans/{plan_id}`、`/tasks`、`/runs`、`/events`、`/artifacts`、`/approvals` 管理端业务查询模型;列表支持客户端 Key、设备、实体 ID、状态、同步状态和同步时间范围过滤,业务 row 会提炼 title/status/summary、Plan/Task/Run 关联、发生/开始/结束时间、sync record 深链和脱敏客户端 Key;Plan 视图会聚合 task/run 计数与最近事件,Plan 详情会返回关联 tasks/runs/events/artifacts/approvals 摘要。embedded adapter 已拆出同名只读业务查询接口,从 qimingclaw runtime、artifact、approval 和 canonical event 投影生成统一分页视图;embedded 数字员工页已新增“业务视图”入口,支持计划/任务/运行/事件/产物/审批分段、实体/状态/同步状态/时间筛选、分页和详情 JSON 查看;客户端 outbox 的 event `business_view` 已开始按 route decision、governance command、approval action、artifact lifecycle/access、skill call audit 和 ready PlanStep dispatch 细分常用字段,本地与管理端业务视图投影已开始闭环。正式外部管理端台面已开始吸收:qiming-backend 新增 `/api/digital-employee/admin/workbench/{view}`,qiming 管理端新增“数字员工运营台”,统一展示业务视图、介入台、产物治理、通知运营、审计诊断、日报运营和路由治理,只读消费 sync record `business_view` 并提供同步记录深链。管理端低风险动作闭环已开始吸收:后端新增 `/api/digital-employee/admin/actions` 记录动作意图和 `/pending` 拉取队列,qimingclaw 只应用保留本地、标记复核、通知已读/忽略、产物保留/到期、日报交付/确认/审批请求等低风险动作并写入 `admin_action_applied` / `admin_action_rejected` / `admin_action_failed`,qiming 运营台抽屉只提示“已记录,等待客户端拉取”。跨设备策略冲突视图已开始吸收:qimingclaw 将派发、风险审批和入口路由策略同步事件投影为 `policy_snapshot` business view,后端新增 `/api/digital-employee/admin/policies/snapshots` 聚合 client/device 维度差异并输出 `conflict_keys`,qiming 运营台新增“策略冲突” tab 展示 policy drift 与 sync record 深链。跨设备审批策略冲突预览已开始吸收:策略冲突页会把 `approval_policy_id`、`conflict_policy_id`、`decision_guard_status`、`batch_accept_remote_disabled`、`auto_decision_disabled` 和策略原因纳入冲突字段和详情解释,但不自动合并策略、不远程编辑策略。正式管理端策略中心预览和正式管理端调度运营预览已开始吸收:`admin/workbench` 新增 `policy_center` 与 `scheduler_operations` 视图,统一消费策略快照、scheduler job、cron settings、lease、dispatch sweep 和 recent run 摘要,并通过低风险 admin action 记录策略拉取、立即检查调度、复核和忽略意图。业务事实物化查询已开始吸收:后端新增 `digital_employee_business_fact`、`digital_employee_artifact_lifecycle`、`digital_employee_daily_report_fact` 三张增量物化表,同步入库时 upsert 安全业务摘要,业务查询优先返回 `materialized: true` 行,旧记录继续回退 sync record。 + - 已吸收:管理端已有通用 sync record 接收、查询、payload 摘要和深链;后端 sync record 已开始保存客户端顶层 `contract_version`、`entity_summary` 和 `business_view`,并基于现有 `digital_employee_sync_record` 拆出 `/api/digital-employee/plans`、`/plans/{plan_id}`、`/tasks`、`/runs`、`/events`、`/artifacts`、`/approvals` 管理端业务查询模型;列表支持客户端 Key、设备、实体 ID、状态、同步状态和同步时间范围过滤,业务 row 会提炼 title/status/summary、Plan/Task/Run 关联、发生/开始/结束时间、sync record 深链和脱敏客户端 Key;Plan 视图会聚合 task/run 计数与最近事件,Plan 详情会返回关联 tasks/runs/events/artifacts/approvals 摘要。embedded adapter 已拆出同名只读业务查询接口,从 qimingclaw runtime、artifact、approval 和 canonical event 投影生成统一分页视图;embedded 数字员工页已新增“业务视图”入口,支持计划/任务/运行/事件/产物/审批分段、实体/状态/同步状态/时间筛选、分页和详情 JSON 查看;客户端 outbox 的 event `business_view` 已开始按 route decision、governance command、approval action、artifact lifecycle/access、skill call audit 和 ready PlanStep dispatch 细分常用字段,本地与管理端业务视图投影已开始闭环。正式外部管理端台面已开始吸收:qiming-backend 新增 `/api/digital-employee/admin/workbench/{view}`,qiming 管理端新增“数字员工运营台”,统一展示业务视图、介入台、产物治理、通知运营、审计诊断、日报运营和路由治理,只读消费 sync record `business_view` 并提供同步记录深链。管理端低风险动作闭环已开始吸收:后端新增 `/api/digital-employee/admin/actions` 记录动作意图和 `/pending` 拉取队列,qimingclaw 只应用保留本地、标记复核、通知已读/忽略、产物保留/到期、日报交付/确认/审批请求等低风险动作并写入 `admin_action_applied` / `admin_action_rejected` / `admin_action_failed`,qiming 运营台抽屉只提示“已记录,等待客户端拉取”。跨设备策略冲突视图已开始吸收:qimingclaw 将派发、风险审批和入口路由策略同步事件投影为 `policy_snapshot` business view,后端新增 `/api/digital-employee/admin/policies/snapshots` 聚合 client/device 维度差异并输出 `conflict_keys`,qiming 运营台新增“策略冲突” tab 展示 policy drift 与 sync record 深链。跨设备审批策略冲突预览已开始吸收:策略冲突页会把 `approval_policy_id`、`conflict_policy_id`、`decision_guard_status`、`batch_accept_remote_disabled`、`auto_decision_disabled` 和策略原因纳入冲突字段和详情解释,但不自动合并策略、不远程编辑策略。跨设备调度派发策略冲突预览已开始吸收:策略冲突页会把 `dispatch_policy_key`、`max_per_sweep`、`auto_dispatch_ready_steps`、`catch_up_on_startup` 和 `max_run_history` 纳入冲突字段和详情解释,但不立即运行、不接管租约、不远程改策略。正式管理端策略中心预览和正式管理端调度运营预览已开始吸收:`admin/workbench` 新增 `policy_center` 与 `scheduler_operations` 视图,统一消费策略快照、scheduler job、cron settings、lease、dispatch sweep 和 recent run 摘要,并通过低风险 admin action 记录策略拉取、立即检查调度、复核和忽略意图。业务事实物化查询已开始吸收:后端新增 `digital_employee_business_fact`、`digital_employee_artifact_lifecycle`、`digital_employee_daily_report_fact` 三张增量物化表,同步入库时 upsert 安全业务摘要,业务查询优先返回 `materialized: true` 行,旧记录继续回退 sync record。 - 缺口:复杂组合检索、跨设备聚合图、远程裁决/策略编辑动作和物化业务表仍待吸收。 - 建议:先使用 sync record 派生业务查询模型和 embedded 业务视图预览承接联调,再按容量和查询压力决定是否拆 Plan / Task / Run / Event 物理业务表。