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 c2b4abcd..8de3a66d 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 @@ -39,6 +39,18 @@ public class DigitalEmployeePolicySnapshotRowDto { private Integer notMappedCount; @JsonProperty("intervention_conflict_status") private String interventionConflictStatus; + @JsonProperty("approval_policy_id") + private String approvalPolicyId; + @JsonProperty("conflict_policy_id") + private String conflictPolicyId; + @JsonProperty("decision_guard_status") + private String decisionGuardStatus; + @JsonProperty("batch_accept_remote_disabled") + private Boolean batchAcceptRemoteDisabled; + @JsonProperty("auto_decision_disabled") + private Boolean autoDecisionDisabled; + @JsonProperty("policy_reason") + private String policyReason; @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 6aed5767..e2ee1302 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 @@ -608,6 +608,12 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye .missingContextCount(routeInterventionCount(routeInterventionViews, "missing_context")) .notMappedCount(routeInterventionCount(routeInterventionViews, "not_mapped")) .interventionConflictStatus(interventionConflictStatus) + .approvalPolicyId(firstString(latestView.get("approval_policy_id"), latestView.get("approvalPolicyId"))) + .conflictPolicyId(firstString(latestView.get("conflict_policy_id"), latestView.get("conflictPolicyId"))) + .decisionGuardStatus(firstString(latestView.get("decision_guard_status"), latestView.get("decisionGuardStatus"))) + .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"))) .syncRecordUrl(syncRecordUrl(latest.getOutboxId())) .clientKeyMasked(maskClientKey(latest.getClientKey())) .deviceId(latest.getDeviceId()) @@ -684,7 +690,13 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye "catch_up_on_startup", "approval_required_risk_levels", "approval_required_actions", - "auto_reject_actions" + "auto_reject_actions", + "approval_policy_id", + "conflict_policy_id", + "decision_guard_status", + "batch_accept_remote_disabled", + "auto_decision_disabled", + "policy_reason" ); } 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 f2c7b7c7..7b161402 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 @@ -1816,6 +1816,72 @@ class DigitalEmployeeSyncApplicationServiceImplTest { assertThat(row.getConflictKeys()).contains("route_intervention_state"); } + @Test + void queryAdminPolicySnapshotsSummarizesApprovalPolicyConflicts() { + DigitalEmployeeSyncRecord localApprovalPolicy = record("outbox-approval-policy-a", "event", "event-approval-policy-a", 101L, new Date(10_000), + Map.ofEntries( + Map.entry("entity_type", "event"), + Map.entry("category", "policy_snapshot"), + Map.entry("kind", "approval_decision_policy_snapshot"), + Map.entry("policy_kind", "approval_decision"), + Map.entry("policy_key", "approval_decision"), + Map.entry("title", "审批裁决策略"), + Map.entry("approval_policy_id", "approval-policy-local"), + Map.entry("conflict_policy_id", "conflict-policy-local"), + Map.entry("decision_guard_status", "protected"), + Map.entry("batch_accept_remote_disabled", true), + Map.entry("auto_decision_disabled", true), + Map.entry("policy_reason", "manual_review_required") + ), + Map.of("kind", "approval_decision_policy_snapshot")); + localApprovalPolicy.setClientKey("client-key-a"); + localApprovalPolicy.setDeviceId("device-a"); + DigitalEmployeeSyncRecord remoteApprovalPolicy = record("outbox-approval-policy-b", "event", "event-approval-policy-b", 101L, new Date(11_000), + Map.ofEntries( + Map.entry("entity_type", "event"), + Map.entry("category", "policy_snapshot"), + Map.entry("kind", "approval_decision_policy_snapshot"), + Map.entry("policy_kind", "approval_decision"), + Map.entry("policy_key", "approval_decision"), + Map.entry("title", "审批裁决策略"), + Map.entry("approval_policy_id", "approval-policy-remote"), + Map.entry("conflict_policy_id", "conflict-policy-remote"), + Map.entry("decision_guard_status", "manual_only"), + Map.entry("batch_accept_remote_disabled", true), + Map.entry("auto_decision_disabled", false), + Map.entry("policy_reason", "remote_policy_changed") + ), + Map.of("kind", "approval_decision_policy_snapshot")); + remoteApprovalPolicy.setClientKey("client-key-b"); + remoteApprovalPolicy.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( + localApprovalPolicy, + remoteApprovalPolicy, + record("outbox-notification", "notification", "notification-1", 101L, new Date(12_000), + Map.of("entity_type", "notification", "title", "普通通知", "status", "unread"), + Map.of("id", "notification-1", "status", "unread")) + ))); + + DigitalEmployeePolicySnapshotPageDto result = service.queryAdminPolicySnapshots(null, null, "conflict", "approval_decision", null, null, 1L, 20L); + + assertThat(result.getTotal()).isEqualTo(1); + DigitalEmployeePolicySnapshotRowDto row = result.getRecords().get(0); + assertThat(row.getPolicyKind()).isEqualTo("approval_decision"); + assertThat(row.getStatus()).isEqualTo("conflict"); + assertThat(row.getClientCount()).isEqualTo(2); + assertThat(row.getDeviceCount()).isEqualTo(2); + assertThat(row.getConflictKeys()).contains("approval_policy_id", "conflict_policy_id", "decision_guard_status", "auto_decision_disabled", "policy_reason"); + assertThat(row.getApprovalPolicyId()).isEqualTo("approval-policy-remote"); + assertThat(row.getConflictPolicyId()).isEqualTo("conflict-policy-remote"); + assertThat(row.getDecisionGuardStatus()).isEqualTo("manual_only"); + assertThat(row.getBatchAcceptRemoteDisabled()).isTrue(); + assertThat(row.getAutoDecisionDisabled()).isFalse(); + assertThat(row.getPolicyReason()).isEqualTo("remote_policy_changed"); + assertThat(row.getRecommendedResolution()).isEqualTo("review_latest_policy_snapshot"); + } + @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 e31264fc..64f82008 100644 --- a/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx +++ b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx @@ -720,6 +720,12 @@ const DigitalEmployeeOps: React.FC = () => { missing_context_count: record.missing_context_count, not_mapped_count: record.not_mapped_count, intervention_conflict_status: record.intervention_conflict_status, + approval_policy_id: record.approval_policy_id, + conflict_policy_id: record.conflict_policy_id, + decision_guard_status: record.decision_guard_status, + batch_accept_remote_disabled: record.batch_accept_remote_disabled, + auto_decision_disabled: record.auto_decision_disabled, + policy_reason: record.policy_reason, latest_synced_at: record.latest_synced_at, synced_at: record.latest_synced_at, client_key_masked: record.client_key_masked, @@ -894,6 +900,14 @@ const DigitalEmployeeOps: React.FC = () => { : '暂无冲突字段'} {detailRecord.intervention_conflict_status && ` · 跨设备路由干预冲突 ${detailRecord.intervention_conflict_status}`} + {(detailRecord.approval_policy_id || detailRecord.conflict_policy_id || detailRecord.decision_guard_status) && + ` · 跨设备审批策略冲突`} + {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.batch_accept_remote_disabled && ' · 批量采纳远端禁用'} + {detailRecord.auto_decision_disabled && ' · 自动裁决禁用'} {detailRecord.route_decision_count != null && ` · 路由 ${detailRecord.route_decision_count} 条`} {detailRecord.blocked_count != null && diff --git a/qiming/src/types/interfaces/systemManage.ts b/qiming/src/types/interfaces/systemManage.ts index 2f3e2fc5..977e8de3 100644 --- a/qiming/src/types/interfaces/systemManage.ts +++ b/qiming/src/types/interfaces/systemManage.ts @@ -722,6 +722,12 @@ export interface DigitalEmployeePolicySnapshotRow { missing_context_count?: number; not_mapped_count?: number; intervention_conflict_status?: string; + approval_policy_id?: string; + conflict_policy_id?: string; + decision_guard_status?: string; + batch_accept_remote_disabled?: boolean; + auto_decision_disabled?: boolean; + policy_reason?: string; 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 71ffed99..be7364fc 100644 --- a/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js +++ b/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js @@ -1278,21 +1278,30 @@ function checkDigitalAdminPolicySnapshots() { [backendController, "/snapshots", "backend policy snapshot endpoint"], [backendService, "routeInterventionConflictStatus", "backend route intervention conflict aggregation"], [backendService, "route_intervention_state", "backend route intervention conflict key"], + [backendService, "approval_policy_id", "backend approval policy conflict field"], + [backendService, "decision_guard_status", "backend approval decision guard conflict field"], [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, "route_decision_count", "backend route decision count field"], [backendTest, "queryAdminPolicySnapshotsSummarizesRouteInterventionConflicts", "backend route intervention conflict test"], + [backendTest, "queryAdminPolicySnapshotsSummarizesApprovalPolicyConflicts", "backend approval 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, "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 decision guard conflict copy"], [qimingOps, "跨设备路由干预冲突", "qiming route intervention conflict copy"], [qimingOps, "不恢复执行", "qiming route recovery guard copy"], [qimingOps, "不绕过审批", "qiming approval guard copy"], [qimingOps, "不远程改策略", "qiming remote policy edit guard copy"], [docs, "跨设备策略冲突视图", "docs policy conflict absorption"], + [docs, "跨设备审批策略冲突预览", "docs approval 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 4c455a30..99ec3790 100644 --- a/qimingclaw/docs/digital-employee-frontend-integration-plan.md +++ b/qimingclaw/docs/digital-employee-frontend-integration-plan.md @@ -551,7 +551,7 @@ 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、依赖状态、租约、派发事件和策略拉取审计,只记录标记已看、忽略和重试策略检查意图,不远程派发、不抢占租约、不释放租约。 - - 后续缺口:跨设备审批策略 UI、跨设备策略冲突合并、远程策略编辑安全模型、真实派发/租约裁决和跨设备策略合并仍待吸收。 + - 后续缺口:跨设备审批策略冲突预览已开始吸收,策略冲突页可展示审批策略、冲突策略、裁决保护和自动裁决保护差异;远程策略编辑安全模型、真实派发/租约裁决和跨设备策略自动合并仍待吸收。 - 建议:下一轮围绕管理端正式路由 UI 或跨端审批策略 UI,把本地可治理调度继续推进到跨端治理策略。 2. **调度 / 定时 / 周期任务** @@ -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 深链。正式管理端策略中心预览和正式管理端调度运营预览已开始吸收:`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` 和策略原因纳入冲突字段和详情解释,但不自动合并策略、不远程编辑策略。正式管理端策略中心预览和正式管理端调度运营预览已开始吸收:`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 物理业务表。