feat(engine): RunEvent 增 executor 驻地字段(ADR-R6 补记:双端透明调度设计——三条纪律+确定性路由表+数据不动原则)

This commit is contained in:
yu 2026-11-06 15:29:34 +08:00
parent 3a0ff42ac6
commit 81ce4bc9b8
4 changed files with 29 additions and 1 deletions

View File

@ -342,7 +342,7 @@
| R3 | 桌面执行安全模型:RPA PowerShell 驱动 vs 对面"受信 C# 原生进程"红线 | **C# 扩展 actuate**:把 RPA 五域执行逻辑移植进 windows-runner 原生进程;PowerShell 通道仅限本 demo,不进宿主。**原型已产出并编译验证(2026-11-06)**:`contrib/windows-runner-actuate/` 补丁包(协议扩展 + NativeInput + ActuateService + 逐原语移植对照表 + 合入指引含门禁合规清单);对面 csproj 原样合编 0 警告 0 错误(.NET 10.0.403),协议冒烟 5 例通过(安全门错误码/双路分发/observe 无回归);对面 README Windows 行"click/type 或随本轮交付"与该路线吻合 |
| R4 | 浏览器双路径 | **按场景分工**:RPA 场景走咱们的 CDP 接管路线(复用用户登录态/真实页面——RPA 核心价值);对面 PlaywrightBrowserRunner 留给 AI 智能体场景(无头自管浏览器)。约束:①咱们 CDP 路线必须接对面 BrowserContextPolicy 白名单与证据体系,不自建;②迁移前修完 transport 超时与定位器不统一两缺陷;③需对面在插件清单给咱们的 runner 备案(rpa.browser) |
| R5 | 包消费方式 | **源码并入 + 锁版本**:git subtree 进对面仓库统一编译,Bun 锁 1.4.3(与对面全家 workspace:* 源码消费现状一致) |
| R6 | 部署拓扑:rpa-core 各域跑在哪 | **按能力域拆分部署**:纯 Node 域→backend plugin-worker;桌面/视觉/弹窗域→用户电脑 Electron 宿主;同一包按 PLATFORM_GROUPS 分组加载,不维护两份代码 |
| R6 | 部署拓扑:rpa-core 各域跑在哪 | **按能力域拆分部署**:纯 Node 域→backend plugin-worker;桌面/视觉/弹窗域→用户电脑 Electron 宿主;同一包按 PLATFORM_GROUPS 分组加载,不维护两份代码。**补记(2026-11-06 用户拍板:双端并存+透明调度)**——同一能力本地/后端两驻地并存,SaaS 与本地部署两形态通用,用户无感知;防乱设计见下方专条 |
| R2 | 集成形态:RPA 流程=单个 capability(方案 A)还是节点=step 映射(方案 B) | **方案 A + 两补丁**:RPA 流程=`rpa.flow.execute` 单能力,对面一个 step 一次租约一次结算;节点级事件走 event-bus 旁路(保透明、不进权威账本);人审/高危节点照常升级对面审批。**不用 B 的理由**(记录在案):双账本一致性(崩溃窗口两边分歧、破坏对面 recovery 的单账本假设)、控制流节点无副作用无法映射 CapabilityRef、审批/重试粒度与业务语义错位。**B 的触发条件**:对面产品确认需要"逐步控制 RPA"(单步执行/任意步回退)时局部启用,事件流已备好数据不返工。**A++ 补记(2026-11-06 二次拍板)**:方案 A 骨架不变,节点事件流从"透明度日志"升级为**检查点协议**(nodeId+状态+幂等键+可恢复标记),崩溃后 `rpa.flow.execute` 带 resumeToken 重入、从最近检查点续跑,权威账本仍为单 step、无双账本;B 的隐藏触发条件(长流程断点续跑,RPA 桌面场景高发)由此覆盖而无需节点=step。业界对照:UiPath Orchestrator=Job 黑盒+日志旁路(≈A);Camunda/Zeebe External Task 的"单引擎假设"不适用于双引擎集成;AWS Step Functions callback≈补丁 2。两侧改动清单与落地节奏见 §7-A5 |
### 待对面对确认(仅剩小问题,不猜测)
@ -354,6 +354,29 @@
> 已回答:Q3(human-dialog 弹窗落点)已由 R6 回答——用户电脑 Electron 宿主窗口;仅样式/交互细节与对面 UI 对齐。
### R6 补记:双端透明调度设计(2026-11-06 用户拍板:前端留、后端也留)
同一能力允许本地(用户电脑宿主)与后端(plugin-worker)两驻地并存,调度对用户无感知,SaaS 与本地部署两形态通用(本地部署时"后端"即客户自有机房,规则不变)。防乱靠三条纪律与一张确定性路由表:
**三条纪律:**
1. **一个脑决策**:路由只在宿主侧 Resolver 一处发生(本质是 A1 注入机制的扩展——registry 条目的 handler 绑定二选一:本地实现 or 自动生成的远端代理,远端代理走对面 gateway 授权/租约/审计,不绕行)。能力代码与流程定义里**禁止**出现任何驻地 if-else,规则全部来自 catalog 元数据。
2. **一份契约两处实现**:能力签名/参数/返回/错误码同一套(catalog 单一源已保证);本地与远端实现跑同一套契约测试;远端代理自动生成而非手写第二份逻辑,杜绝漂移。
3. **数据不动原则**:能力在哪个驻地执行由"数据已经在哪里"决定(代码就数据,不搬数据)。需要搬数据时必须是流程中的显式节点(上传/下载),**路由器不得隐式搬运用户本地文件到后端**——用户无感知的是"在哪算",不是"数据去哪"(隐私红线)。
**路由规则表(按序匹配,确定性,不留裁量空间):**
| 序 | 条件 | 执行驻地 |
|---|---|---|
| 1 | platformTag 限宿主平台 / 需要桌面会话 / 弹窗人审 | 仅本地(无远端选项) |
| 2 | 输入数据在本地(本地文件路径) | 本地 |
| 3 | 输入数据在云端(对面 tenant-files 等) | 后端 |
| 4 | 数据两驻地均有副本 | 默认本地(隐私+省带宽);流程可显式声明 `prefer: backend`(如夜间批量) |
| 5 | 本地不可用(宿主离线)且能力无本地数据依赖且标可远端 | 后端兜底 |
| 6 | 均不可用 | 节点失败,错误码 `location_unavailable` |
**用户无感知 ≠ 审计无感知**:`node.finished` 事件携带 `executor: "local" | "backend"` 字段(已定稿进 RunEvent 并有契约测试,当前恒为 `local`,远端代理落地后由代理覆盖)——用户界面不展示,审计与排障必须可分辨。该字段同时是对面 event-bus 入审计链的取用点。
---
## 11. 本指南边界

View File

@ -87,6 +87,8 @@ describe("A++ 检查点事件负载", () => {
expect(first.checkpointId).toBe("first");
expect(first.resumable).toBe(true);
expect(first.idempotencyKey).toBe("run_cp:first");
// 双端透明调度(ADR-R6 补记):本地执行驻地必须随事件上报,供审计区分
expect(first.executor).toBe("local");
const second = finished.find((e) => e.nodeId === "second")!;
expect(second.checkpointId).toBe("second");

View File

@ -370,6 +370,7 @@ async function executeNode(node: FlowNode, ctx: RunContext, states: Map<string,
evidence: state.evidence,
checkpointId: node.id,
resumable,
executor: "local",
...(resumable ? { idempotencyKey: `${ctx.runId}:${node.id}` } : {})
});
return result;

View File

@ -185,6 +185,8 @@ export interface RunEvent {
checkpointId?: string;
idempotencyKey?: string;
resumable?: boolean;
// 双端透明调度(ADR-R6 补记):本次执行驻地;用户界面不展示,审计/排障必须可见
executor?: "local" | "backend";
startedAt?: string;
nodeLabel?: string;
total?: number;