diff --git a/HITL_PRD_ambiguities.md b/HITL_PRD_ambiguities.md new file mode 100644 index 00000000000..1c2b01716dd --- /dev/null +++ b/HITL_PRD_ambiguities.md @@ -0,0 +1,281 @@ +# HITL PRD Review + +Verdict: Fail + +这份 PRD 还没有达到可直接进入实现的状态。主要问题不是“功能点不够多”,而是几个关键对象和边界还没有收敛: + +- 现有实现的核心对象是 `HumanInputForm` / `HumanInputDelivery` / `HumanInputFormRecipient`,而 PRD 讨论的是 `Contact` / `Human Roster` / `Recipient` / `HITL task` / `Delivery Record` / `Recipient Resolution Record`。以下评审按 `HITL task == form` 理解。 +- 当前运行时只有 `webapp` 和 `email` 两类 delivery method;PRD 已经把 IM、审计、URL 鉴权、组织级通讯录、跨 workspace 联系人、通知中心一起拉进来了。 +- PRD 内部对“谁是 approver、谁能打开链接、哪些变化影响 pending task、dynamic email 要不要反查 member”等规则还存在直接冲突。 + +以下结论基于我对当前实现的阅读,重点参考: + +- `api/models/human_input.py` +- `api/core/repositories/human_input_repository.py` +- `api/core/workflow/human_input_adapter.py` +- `api/services/human_input_service.py` +- `api/controllers/web/human_input_form.py` +- `api/tasks/mail_human_input_delivery_task.py` +- `web/app/components/workflow/nodes/human-input/types.ts` +- `web/app/components/workflow/nodes/human-input/default.ts` +- `web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx` +- `web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/index.tsx` + +## Blocking Issues + +### 1. `HITL task == form` 这个前提需要在 PRD 中显式写清,否则研发实现时仍会歧义 + +- PRD 位置:`10.1`、`11.1`、`16.*`、`17.*` +- 当前实现:运行时实体是 `HumanInputForm`,子实体是 `HumanInputDelivery` 和 `HumanInputFormRecipient`,表单状态是 `WAITING` / `SUBMITTED` / `TIMEOUT` / `EXPIRED`。 +- 问题: + - 按当前约束,我将 `task` 直接理解为 `form`;但 PRD 正文没有把这个等价关系写死。 + - `delivery_records`、`recipient_resolution_records`、`allowed_approvers`、`notified_recipients` 也都被当成 task 字段提出,但没有和现有表、现有 token 模型做映射。 + - 如果这层术语不在 PRD 里显式固定,后面的迁移、API 路由、日志、鉴权仍然容易被不同研发解释成“新增 task 聚合”。 +- 需要补齐: + - 在术语定义处显式写明:`HITL task` 是产品术语,对应实现层的 `HumanInputForm`。 + - 给出一张对象映射表:`task(form)/delivery/recipient/contact` 各自是什么、谁持久化、谁是运行时快照。 + +### 2. PRD 当前写的是“原地改造旧 Human Input 节点”,但你说明实际方案是“引入新节点” + +- PRD 位置:`5.4`、`8.*`、`11.1` +- 当前实现: + - 节点存储主结构是 `delivery_methods[]`。 + - Email 配置里包含 `recipients / subject / body / debug_mode`。 + - 当前支持 `webapp`、`email`,并且 Email 配置里还有 `whole_workspace`。 +- 问题: + - PRD `5.0` 明确写的是“对现有 Human Input 节点进行原地改造”“旧 Human Input 节点升级为 HITL 2 的配置结构”“旧 workflow DSL 需要保持兼容”。 + - 你现在给出的真实方案是:引入一套新的节点配置模型,只复用表单相关的 `inputs` / `actions` 定义,老代码兼容性不是主要问题。 + - 这意味着当前 PRD 的节点策略、迁移策略、回滚策略和兼容性表述都需要重写;否则研发会按“原地升级旧节点”去做错误拆分。 +- 需要补齐: + - 在 PRD 中显式写明:这是一个新节点,不是旧 `Human Input` 节点的原地升级。 + - 明确新节点的 node type、DSL schema、前端入口、后端 runtime path。 + - 同步清理或重写 `5.0`、`11.3`、`11.4` 里所有建立在“升级旧节点”前提上的描述。 + +### 3. URL 鉴权设计和当前实现的安全模型正面冲突 + +- PRD 位置:`17.*` +- 当前实现: + - `api/controllers/web/human_input_form.py` 里 Web 表单接口当前是未鉴权的 token access。 + - 注释直接写了 `this endpoint is unauthenticated on purpose for now`。 + - GET/POST 只要拿到 `form_token` 就能访问或提交。 +- 问题: + - PRD 明确说 URL 不能代表审批权限,打开和提交都要校验访问者身份。 + - 这不只是“小改鉴权逻辑”,而是现有 Web form 交互范式的根本变化。 + - PRD 没说明哪些 recipient type 还能继续 token-only,哪些必须登录、OTP、Magic Link 或 IM identity。 +- 需要补齐: + - 按 recipient type 给出访问校验矩阵。 + - 明确“打开页”和“提交表单”两次校验各自依赖什么证据。 + - 明确历史旧 token 的兼容策略和失效策略。 + +### 4. `dynamic email` 的 canonicalization 规则前后冲突 + +- PRD 位置:`8.3`、`7.5`、`18.6` +- 当前实现: + - Email recipient 当前只分 `member` 和 `external`,并按 workspace member / external email 构造 recipient。 +- 问题: + - `8.3` 写的是:动态 Email 不反查 member,只按 Email recipient 处理。 + - `18.6` 又写:如果 dynamic Email 和已有 workspace member email 相同,应优先匹配已有联系人,以获得更强身份校验。 + - `7.5` 又强调:如果 Email 能匹配 Dify Account,不允许创建 external contact。 + - 这三条组合起来,dynamic email 到底是“永远按裸 email”还是“尽可能升级成 account/contact”,现在是冲突的。 +- 需要补齐: + - 为 `dynamic email` 单独定义 resolution pipeline。 + - 明确它的 canonical key、审计身份、通知行为、鉴权行为是否允许升级成 account/contact。 + +### 5. `Current Initiator` 的身份模型没有闭环 + +- PRD 位置:`8.5`、`17.3`、`18.6` +- 当前实现: + - 现有 recipient type 里有 `STANDALONE_WEB_APP`、`CONSOLE`、`BACKSTAGE`、`EMAIL_MEMBER`、`EMAIL_EXTERNAL`。 + - `HumanInputService` 提交时只记录 `submission_user_id` 或 `submission_end_user_id`。 +- 问题: + - PRD 同时提到 WebApp 发起者、CLI/API 调用方、匿名 WebApp session、明确 caller identity。 + - 但没有定义 initiator 到底是 `account`、`end_user`、`session`、`API key owner` 还是一个新的 identity union。 + - 也没有定义它在 allowed approver 去重、审计、URL 鉴权、重复提交判定里用哪个 key。 +- 需要补齐: + - 先定义 initiator identity schema。 + - 再定义它如何参与 recipient canonicalization 和 submit authorization。 + +### 6. 部署形态实际上是两套模型,但 PRD 还没有把这两套模型写清 + +- PRD 位置:`4.*`、`6.2`、`6.5`、`14.2`、`18.4` +- 问题: + - 按你最新给出的前提,部署并不是三套完全独立模型,而是两套: + - `CE / EE` 共用一套模型。 + - `SaaS` 使用另一套模型。 + - 但当前 PRD 还是把 `CE`、`EE`、`SaaS` 混在同一层叙述,导致阅读者很难判断哪些规则是共模,哪些是 SaaS 特有规则。 + - 例如: + - `4.1` 的 SaaS 凭据作用域是 tenant/workspace 级。 + - `6.2` 和 `18.4` 的企业版描述更接近 deployment-level / workspace-override 模型。 + - `6.5` 虽然标注本期不做,但它实际上属于 `CE / EE` 这一套模型的扩展能力,而不应和 SaaS 放在同一层讨论。 +- 为什么是阻塞: + - 这决定了 IM integration 配在哪里、表怎么分 tenant/workspace/deployment、回调 URL 怎么生成、权限由谁持有。 + - 如果 PRD 不先把“哪条规则属于 `CE/EE`,哪条属于 `SaaS`”标出来,研发实现时仍然会把本来应该分叉的规则揉在一起。 +- 需要补齐: + - 先在 PRD 中显式声明:部署策略只有两套模型,`CE/EE` 一套,`SaaS` 一套。 + - 给出两张矩阵,或者一张带 `model_family` 列的矩阵: + - `CE/EE`:渠道、配置入口、凭据作用域、是否支持 override。 + - `SaaS`:渠道、安装方式、tenant/workspace 作用域、ISV/自建边界。 + - 明确本期是否允许“同一部署启用多个 IM 渠道”,并分别说明该结论是否对 `CE/EE` 与 `SaaS` 同时成立。 + +### 7. IM 能力矩阵是空表,但大量规则依赖它 + +- PRD 位置:`9.2`、`17.3`、`18.5` +- 问题: + - PRD 频繁依赖“可在 IM 内取身份”“可卡片内审批”“可通讯录同步”“否则 fallback URL”。 + - 但 `18.5` 的能力矩阵几乎是空的。 + - 在没有矩阵的情况下,`IM binding`、`callback`、`identity proof`、`fallback auth` 都无从落地。 +- 需要补齐: + - 至少先填完本期声称支持的渠道:Slack、钉钉、飞书/Lark、Teams、企业微信。 + - 每个渠道要明确:是否能取用户身份、是否能卡片提交、是否只支持链接跳转、是否能拉通讯录。 + +### 8. `pending task` 在联系人变化后的行为没有统一规则 + +- PRD 位置:`17.2`、`17.4`、`18.2`、`18.9` +- 问题: + - 一处说提交时要重新校验成员仍在 workspace、IM binding 仍有效。 + - 一处说历史 task 保留 snapshot,pending task 默认重新校验。 + - 另一处又提“转为 external contact”“保留旧 IM snapshot”“是否允许旧链接继续审批,需要确认”。 + - 这会直接影响 submit 判定、通知补发、审计解释。 +- 需要补齐: + - 给出一张变更影响真值表,至少覆盖: + - member 离开 workspace + - account disabled + - external contact 删除 + - IM binding 修改/删除 + - recipient email 修改 + - credential rotation + - 每种变化要分别说明:历史 task、pending task、新 task 的行为。 + +### 9. Debug 行为定义和现有实现不一致 + +- PRD 位置:`8.6`、`10.5` +- 当前实现: + - Email config 里已经有 `debug_mode`,且是 channel-level 配置。 + - Debug preview / single-run 路径已经存在。 +- 问题: + - PRD 把它提成 node-level `Only notify me during debug`,但没说明: + - 是否覆盖 webapp / email / IM 全部渠道; + - 是否仍创建真实 form/task; + - audit 和 delivery record 是否记录“原计划对象”与“debug 替换对象”。 +- 需要补齐: + - 明确 debug recipient rewrite 的作用域、日志规则和 snapshot 行为。 + +### 10. 审计日志与运行日志边界不清晰 + +- PRD 位置:`10.*`、`16.*` +- 问题: + - `10.*` 关注 Last Run / Debug 可见性。 + - `16.*` 又要求能回答“谁、什么时候、通过什么身份验证、填了什么、为什么被允许或拒绝”。 + - 但 `16.2` 的“访问与身份校验信息”又写了“可能不需要审计日志记录”。 + - 这和审计目标本身冲突。 +- 需要补齐: + - 明确哪些字段属于用户可见运行日志,哪些属于审计日志,哪些只保留数据库不可在 UI 暴露。 + - 明确敏感字段脱敏规则,否则“记录原始 dynamic email 值”和“最小化暴露”也会冲突。 + +### 11. 后台权限矩阵完全缺失 + +- PRD 位置:`5.1`、`5.2`、`5.3`、`6.*`、`7.*`、`18.10` +- 问题: + - 谁能看 Contact Directory。 + - 谁能看完整 Human Roster。 + - 谁能添加其他 workspace member。 + - 谁能添加 external contact。 + - 谁能配 IM credential。 + - 谁能改 IM binding / override。 + - 这些都没有定。 +- 为什么是阻塞: + - 没有权限矩阵,就没法设计 controller、service 和数据查询范围。 + +## Non-blocking Issues + +### 1. `8.7 节点预览(不确定)` 仍是占位 + +- 这是 UI 细节,不阻塞后端对象建模。 +- 但它现在混在正式章节里,容易让评审误以为已定稿。 + +### 2. `12.3 通知中心` 只给了方向,没有边界 + +- 说“可优先考虑提供接口”,但没有明确是否本期内要有 API contract。 +- 建议要么移到后续专题,要么明确只预留领域对象,不出用户接口。 + +### 3. `16` 和 `18.11` 对指标/guardrail 基本没有落地定义 + +- 已经意识到 abuse 风险,但没有最小阈值、限流对象、失败告警口径。 +- 这会让 SaaS 风控落在实现阶段临时补洞。 + +### 4. `6.3 IM identity` 章节信息量过低 + +- “具体参数待确定”基本等于还没设计。 +- 如果本期真的要支持多个 IM provider,这一节至少需要一个最小 schema。 + +### 5. `10.4` 的节点失败规则没有和 pause/resume 状态机对齐 + +- “全部 recipient 都无法通知时节点报错”是产品结论。 +- 但没有定义它发生在 form/task 创建前、创建后、部分渠道失败但仍有 webapp token 时的处理顺序。 + +### 6. `11.3` / `11.4` 迁移章节和“新节点方案”不一致 + +- 如果采用新节点方案,现有迁移章节就不能再默认按“旧节点升级到新节点”来写。 +- 这部分不一定需要复杂迁移设计,但至少要明确: + - 旧节点继续存在还是隐藏; + - 新旧节点能否并存; + - 是否需要任何自动迁移; + - 发布后旧 workflow 的编辑与运行策略。 + +## Vague Language + +以下表述需要收紧,否则实现时会被不同团队各自解释: + +- `支持为联系人绑定一个 IM 身份` + - 问题:是手动输入、搜索选择、同步导入,还是都支持? + +- `默认通过 IM + Email 双渠道触达` + - 问题:是所有 recipient 都双发,还是仅“可映射到 contact 且具备两种渠道”的人双发? + +- `若调用方有明确身份,可作为 allowed approver` + - 问题:什么叫“明确身份”?API key owner、end_user、session、SSO subject 都算吗? + +- `需要记录额外的审计信息` + - 问题:额外到什么程度?字段范围、查询权限、保留周期都没定义。 + +- `如果 IM 支持在应用内获取当前用户身份` + - 问题:哪些 IM 支持、支持到什么粒度、是否可作为强身份凭据,都没定。 + +- `帮助排错` + - 问题:需要对谁可见、保留多久、是否脱敏,没有约束。 + +## Missing Scenarios + +当前 PRD 对以下场景缺少明确规则: + +- 同一个人同时以 `workspace member`、`dynamic email`、`current initiator` 三种来源命中时如何去重。 +- 一个 `dynamic email` 同时命中多个联系人,或命中一个 disabled account 时如何处理。 +- IM 已送达、Email 发送失败,但 Web form 仍可打开时,task/form 是否继续等待。 +- 用户在打开表单后离开 workspace,再提交时的报错文案和状态变化。 +- `external contact` 被删除后,已经打开过页面但尚未提交的浏览器会话如何处理。 +- 文件上传表单在 external recipient 场景下如何鉴权,上传 token 是否绑定 recipient identity。 +- Email OTP / Magic Link 的重放、防爆破、重试次数、锁定策略。 +- 部分 recipient 解析成功、部分解析失败时,通知对象列表和 allowed approver 列表谁是审计真相来源。 +- Debug run 替换通知对象后,是否允许被替换掉的原 recipient 仍通过历史链接提交。 +- CLI/API 发起但无 end_user identity 时,`Allow Current Initiator to Approve` 是否应自动失效。 + +## Recommended Next Step + +建议不要继续往 UI 文案和页面草图细化。先在 PRD 开头补一段“节点策略声明”:这是一个新节点,只复用表单 `inputs` / `actions` 定义,不做旧 `Human Input` 节点的原地升级。然后再冻结下面 5 份规则表: + +1. 对象模型表 + - `Contact / Roster / Recipient / Task(form) / DeliveryRecord / ResolutionRecord` 的边界和映射。 + +2. Recipient resolution + canonicalization 表 + - 每种来源如何解析、如何去重、最终产生什么 approver identity。 + +3. 状态机表 + - `pending / submitted / timeout / expired / canceled / delivery_failed` 与现有 `HumanInputFormStatus` 的对应关系。 + +4. 访问控制矩阵 + - 打开页与提交时,按 recipient type 和 channel 列出必须满足的身份校验条件。 + +5. 部署/渠道能力矩阵 + - 每个部署形态、每个 IM 渠道的配置入口、凭据作用域、身份能力、卡片能力、fallback 策略。 + +在这 5 张表定下来之前,直接开始改节点 UI 或设计数据库,很容易做出一版内部自相矛盾的实现。 diff --git a/docs/design/human-in-the-loop/hitl-contact-im-domain-model.md b/docs/design/human-in-the-loop/hitl-contact-im-domain-model.md new file mode 100644 index 00000000000..10cc024c68a --- /dev/null +++ b/docs/design/human-in-the-loop/hitl-contact-im-domain-model.md @@ -0,0 +1,369 @@ +# HITL Approval Domain, Contact And IM Domain Model + +## 背景 + +HITL 二期需要在 CE、EE、SaaS 三种部署形态下统一处理联系人、Human Roster、IM 绑定、IM 凭据和运行时审批人解析。 + +核心张力来自企业边界不同: + +| 形态 | 物理部署 | 真实企业边界 | Workspace 语义 | +| --- | --- | --- | --- | +| SaaS | 多个企业共用一个 Dify 部署 | 一个 `Tenant` / workspace 通常就是一个 SMB 企业 | 企业本身 | +| CE | 一个部署对应一个企业 | deployment | 单 workspace facade | +| EE | 一个部署对应一个企业 | deployment | 企业内部门、团队或业务单元 | + +因此不要让运行时直接判断 CE / EE / SaaS。领域层应先抽象出稳定的业务边界,再用策略对象处理部署形态差异。 + +## 直接结论 + +先引入 `ApprovalDomain` 作为产品层和应用层主抽象。它表示一次 HITL 配置与运行所属的审批域,用来统一回答: + +- 当前 workflow 可以从哪个联系人目录和 Human Roster 选择审批人。 +- 其他 workspace 的 Dify member 是否在当前审批域内可见。 +- External contact 由谁管理,workflow editor 是否能创建。 +- IM identity 和 IM app credential 应如何解析。 +- 提交时如何校验身份、当前成员状态和 binding 状态。 +- 审计时按哪个组织边界解释“谁审批了什么”。 + +`workspace_id (tenant_id)` 只应作为系统入口 locator:controller、现有 DB 存储、迁移和 adapter 可以使用它;进入应用服务后,应先解析为 `ApprovalDomainContext`,后续领域接口优先传 `ApprovalDomainContext`、`ContactDirectoryId`、`HumanRosterId`、`HITLTaskId` 等业务对象,而不是继续层层传 `workspace_id`。 + +当前 SaaS 没有“一个企业多个 workspace”的产品计划,因此不建议现在新建 `approval_domains` 表。`ApprovalDomain` 可以先由 `ApprovalDomainResolver` 从 workspace locator 和部署形态推导出来;如果未来 SaaS 需要一个企业多个 workspace,再将 `ApprovalDomain` 或其背后的企业边界持久化。 + +这样保留产品语言的一致性,同时避免把 CE / EE / SaaS 差异泄漏到 Contact、Roster、Recipient、Task 的核心模型中。 + +## 领域划分 + +| Bounded Context | 职责 | 不负责 | +| --- | --- | --- | +| Identity Context | 对齐 `Account`、`TenantAccountJoin`、账号状态和 workspace 成员关系 | 发送通知 | +| Approval Domain Context | 解析当前审批域、联系人目录、Human Roster、IM 集成策略和授权策略 | 发送通知 | +| Contact Directory Context | 管理审批域内可见的 Dify account-backed contact 和 IM identity binding | HITL task 生命周期 | +| Roster Context | 管理当前审批域下可选择的联系人集合、external contact、成员离开后的可选择状态 | IM 凭据存储 | +| IM Integration Context | 管理 IM provider、app installation、credential owner、IM identity binding、通讯录同步 | 表单提交 | +| HITL Runtime Context | 从节点配置解析 recipients,创建 task 快照,发送通知,校验提交人 | 管理联系人目录 | +| Audit Context | 记录解析、投递、访问、提交、拒绝和敏感数据处理策略 | 决定业务权限 | + +## 核心对象 + +### Approval Domain + +| 对象 | 类型 | 说明 | +| --- | --- | --- | +| `ApprovalDomain` | Product Concept | 审批域。产品和应用服务讨论“谁可被通知、谁可提交、用哪套集成能力”的主语。 | +| `ApprovalDomainContext` | Application Context | 由 resolver 生成的运行时上下文,聚合联系人目录、Human Roster、IM 集成和授权策略。 | +| `ContactDirectoryId` | Value Object | 当前审批域可见的联系人目录引用。 | +| `HumanRosterId` | Value Object | 当前审批域下 workflow editor 可选择的联系人集合引用。 | +| `IMIntegrationPolicy` | Value Object | 当前审批域解析 IM provider、installation、credential chain 和 channel capability 的策略。 | +| `RosterPolicy` | Value Object | 当前审批域的 roster 初始化、添加、可选择和成员离开处理规则。 | +| `AuthorizationPolicy` | Value Object | 当前审批域打开、提交和 pending task 再校验规则。 | +| `AuditPolicy` | Value Object | 当前审批域审计字段、脱敏和保留策略。 | + +`ApprovalDomainContext` 是代码中主要传递的上下文。它可以包含底层 owner / locator,但这些细节不应扩散到普通领域服务。 + +推荐形态: + +```text +ApprovalDomainContext + id + contact_directory_id + human_roster_id + im_integration_policy + roster_policy + authorization_policy + audit_policy +``` + +内部实现仍然需要知道当前目录或凭据来自 workspace、deployment、ISV application 还是 tenant self-built app。但这些应封装在 resolver、repository adapter 或 policy 内部,避免让业务代码到处传底层 owner / locator。 + +当前映射建议: + +| 形态 | `ApprovalDomain` 入口 | Contact Directory owner | Human Roster owner | IM credential policy | +| --- | --- | --- | --- | --- | +| SaaS | 当前 `Tenant` / workspace | 当前 `Tenant` | 当前 `Tenant` | Slack ISV workspace installation 或 tenant self-built app | +| CE | 默认 workspace facade | deployment | 默认 `Tenant` | deployment 或 default workspace facade | +| EE | 当前 workspace 的审批域 | deployment | 当前 `Tenant` | 默认 deployment-level,未来可 workspace override | + +### Contact Directory + +| 对象 | 类型 | 聚合边界 | 关键字段 | +| --- | --- | --- | --- | +| `ContactDirectory` | Aggregate Root | `ApprovalDomain` | `id`、`owner_ref`、`visibility_policy` | +| `ContactIdentity` | Entity | `ContactDirectory` | `id`、`directory_id`、`kind`、`account_id`、`external_contact_id`、`normalized_email`、`display_name`、`status` | +| `ExternalContact` | Entity | `HumanRoster` | `id`、`roster_id`、`normalized_email`、`display_name`、`status` | +| `IMIdentityBinding` | Entity | `ContactIdentity` | `id`、`contact_identity_id`、`provider`、`provider_user_id`、`binding_owner_ref`、`status`、`verified_at` | + +建模原则: + +- 只要能匹配到 Dify `Account`,就必须建模为 account-backed `ContactIdentity`,不能建模为 `ExternalContact`。 +- 其他 workspace 的 member 仍是 Dify member,只是当前审批域里“不属于当前 roster owner”的 account-backed contact。 +- `ExternalContact` 只表示不属于 Dify 系统内 `Account` 的外部人员,并且只属于当前 Human Roster。 +- Workflow editor 不能创建 `ExternalContact`;编辑节点时只能选择已有 roster entry 或输入 one-time email。 +- 当前产品只允许一个联系人绑定一个 IM 身份,但数据模型不应假设永远只有一个 provider;唯一约束应放在 active binding 的策略上。 + +### Human Roster + +| 对象 | 类型 | 聚合边界 | 关键字段 | +| --- | --- | --- | --- | +| `HumanRoster` | Aggregate Root | `ApprovalDomain` | `id`、`owner_ref`、`policy` | +| `RosterEntry` | Entity | `HumanRoster` | `id`、`roster_id`、`contact_identity_id`、`entry_kind`、`status`、`joined_at`、`snapshot` | + +`RosterEntry.id` 是 HITL 节点静态 recipient 应保存的稳定引用。产品和 API 可以称为 `contact_id`,但领域语义上它是“当前 Human Roster 中的可选择联系人”,不是全局人。 + +`entry_kind` 是面向当前 Human Roster 的视图: + +| `entry_kind` | 语义 | +| --- | --- | +| `workspace_member` | `Account` 当前属于该 Human Roster 的 owner workspace | +| `dify_member` | `Account` 属于当前审批域的 Contact Directory,但不属于当前 roster owner | +| `external_contact` | 当前 Human Roster 下的外部联系人 | + +`status` 至少需要区分: + +| `status` | 是否可被新节点选择 | 说明 | +| --- | --- | --- | +| `active` | 是 | 可通知、可审批 | +| `left_workspace` | 否 | 曾是 workspace member,已离开但保留历史引用 | +| `account_disabled` | 否 | 账号被禁用或删除 | +| `removed` | 否 | 被管理员从 roster 移除 | +| `pending_review` | 否 | 需要管理员处理,例如成员离开后选择移除或转 external | + +### IM Integration + +| 对象 | 类型 | 说明 | +| --- | --- | --- | +| `IMProvider` | Value Object | Slack、DingTalk、Lark、Teams、WeCom、Email | +| `IMProviderApplication` | Aggregate Root | IM app registration。SaaS ISV 的 `app_id` / `app_secret` 属于部署者;企业自建 app 属于 workspace 或 enterprise。 | +| `IMInstallation` | Aggregate Root | 某个 provider 在某个 owner 下的可用安装。运行时从这里得到可发送能力和 credential handle。 | +| `IMCredentialHandle` | Value Object | Secret store 中的引用,不直接暴露 secret。 | +| `IMChannelCapability` | Value Object | 是否支持卡片审批、fallback URL、平台身份获取、通讯录同步等。 | + +IM identity 一期通过 directory sync / search 选择 provider user,不支持让管理员直接手动输入 provider user id。这个决定影响产品交互和 provider gateway 能力要求,但不改变 `IMIdentityBinding` 的领域模型。 + +凭据解析优先级由 `IMIntegrationPolicy` 给出,运行时只调用 resolver,不直接判断 owner 类型。默认顺序为: + +1. `workspace` / `tenant` scoped installation。 +2. `enterprise` / `deployment` scoped installation。 +3. no IM installation,降级到 Email。 + +不同形态只影响 installation 的来源: + +| 场景 | `IMProviderApplication` owner | `IMInstallation` owner | +| --- | --- | --- | +| SaaS Slack ISV | deployment operator | workspace OAuth installation | +| SaaS DingTalk self-built | workspace admin | workspace | +| CE | deployment operator 或 default workspace admin | deployment 或 workspace facade | +| EE | enterprise admin | deployment / enterprise,本期默认 deployment | + +### HITL Runtime + +| 对象 | 类型 | 现有模型映射 | 说明 | +| --- | --- | --- | --- | +| `NotificationPolicy` | Value Object | `HumanInputNodeData` extension | 节点配置中的通知策略。 | +| `RecipientSelector` | Value Object | node DSL | 静态 roster contact、one-time email、email variable、current initiator。 | +| `ResolvedRecipient` | Value Object | runtime only | 解析后的通知与审批对象,进入 task 前仍可去重。 | +| `AllowedApprover` | Value Object | `HumanInputFormRecipient` snapshot extension | 提交权限的 canonical identity。 | +| `HITLTask` | Aggregate Root | `HumanInputForm` | 一个等待人工输入的持久化 task。 | +| `TaskRecipientSnapshot` | Entity | `HumanInputFormRecipient` | task 创建时的 recipient、contact、email、IM identity、auth requirement 快照。 | +| `DeliveryAttempt` | Entity | `HumanInputDelivery` / future delivery record | 每个 recipient 每个 channel 的投递记录。 | +| `RecipientResolutionRecord` | Entity | future runtime log | 记录 dynamic email 等解析过程。 | + +`HumanInputFormRecipient` 不应升级为 Contact。它是运行时 task 下的 recipient snapshot / access token holder。 + +## 关键不变量 + +1. `ExternalContact` 创建前必须用 normalized email 查找可见 `Account`;命中时只能添加 Dify member。 +2. HITL 节点静态 recipient 只能引用当前 `ApprovalDomainContext.human_roster_id` 下 selectable 的 `RosterEntry`。 +3. one-time email 和 dynamic email 不写入 Human Roster。 +4. task 创建后,`TaskRecipientSnapshot` 不可变,用于审计和历史显示。 +5. 新 task 使用最新 contact、IM binding、credential 和 channel capability。 +6. pending task 提交时必须重新校验安全敏感状态:account disabled、left workspace、removed external contact、deleted IM binding、IM identity mismatch。 +7. 同一个 Dify `Account` 只能生成一个 `AllowedApprover`。 +8. 同一个 normalized email 只能生成一个 email recipient;如果能匹配 roster contact,应优先转成更强身份的 contact recipient。 +9. IM + Email 双渠道触达是 delivery 策略,不代表两个 approver。 +10. Secret 不进入 audit、delivery record、task snapshot,只保存 credential handle 和 provider metadata。 +11. `workspace_id` 是入口 locator,不是领域服务的默认上下文;进入应用服务后应优先传 `ApprovalDomainContext`。 + +## 策略接口 + +以下接口是领域层与部署形态差异之间的边界。实现可以在 CE / EE / SaaS 中不同,但调用方使用同一组 port。 + +```python +class ApprovalDomainResolver(Protocol): + # Resolves the product-level approval context from a system boundary locator. + # workspace_id should normally stop here and not leak into downstream services. + def resolve_for_workspace(self, workspace_id: WorkspaceId) -> ApprovalDomainContext: ... + # Rebuilds the current approval context for submission, retry, audit, or callback flows. + def resolve_for_task(self, task_id: HITLTaskId) -> ApprovalDomainContext: ... + + +class ContactDirectoryRepository(Protocol): + # Persists person identities visible in one approval domain. + def get_by_id(self, contact_identity_id: ContactIdentityId) -> ContactIdentity | None: ... + # Finds the contact identity backed by a Dify Account inside a contact directory. + def find_account_contact(self, directory_id: ContactDirectoryId, account_id: AccountId) -> ContactIdentity | None: ... + # Used to prevent creating ExternalContact when the email belongs to a Dify Account. + def find_by_normalized_email(self, directory_id: ContactDirectoryId, email: NormalizedEmail) -> ContactIdentity | None: ... + def save(self, contact: ContactIdentity) -> None: ... + + +class HumanRosterRepository(Protocol): + # Persists selectable entries used by HITL node configuration. + def get_entry(self, roster_id: HumanRosterId, roster_entry_id: RosterEntryId) -> RosterEntry | None: ... + # Helps keep one roster row per contact identity within a roster. + def find_by_contact_identity(self, roster_id: HumanRosterId, contact_identity_id: ContactIdentityId) -> RosterEntry | None: ... + # Returns only entries that can be selected by new HITL nodes. + def list_selectable_entries(self, roster_id: HumanRosterId, query: RosterQuery) -> Sequence[RosterEntry]: ... + def save_entry(self, entry: RosterEntry) -> None: ... + + +class AccountDirectoryPort(Protocol): + # Read-only adapter over Dify Account and TenantAccountJoin. + def get_account(self, account_id: AccountId) -> AccountProfile | None: ... + # Used by contact creation, IM sync, import, and recipient canonicalization. + def find_account_by_email(self, email: NormalizedEmail) -> AccountProfile | None: ... + # Uses the domain policy to choose the underlying TenantAccountJoin query. + def list_roster_source_members(self, domain: ApprovalDomainContext) -> Sequence[WorkspaceMemberProfile]: ... + # Used at submission time to reject approvers who no longer satisfy domain membership rules. + def has_active_membership(self, domain: ApprovalDomainContext, account_id: AccountId) -> bool: ... + + +class IMCredentialResolver(Protocol): + # Resolves the effective IM installation without exposing where credentials came from. + def resolve_installation(self, domain: ApprovalDomainContext, provider: IMProvider) -> IMInstallation | None: ... + # Returns provider, credential handle, callback metadata, and capability context for sending. + def resolve_sender(self, domain: ApprovalDomainContext, provider: IMProvider) -> IMSenderContext | None: ... + + +class IMBindingRepository(Protocol): + # Resolves the effective binding according to the domain's override policy. + def get_effective_binding(self, domain: ApprovalDomainContext, contact_identity_id: ContactIdentityId, provider: IMProvider) -> IMIdentityBinding | None: ... + # Used by IM card callbacks to map platform user identity back to a Dify contact. + def find_by_provider_user(self, domain: ApprovalDomainContext, provider: IMProvider, provider_user_id: str) -> IMIdentityBinding | None: ... + def save(self, binding: IMIdentityBinding) -> None: ... + + +class IMDirectorySyncGateway(Protocol): + # Provider-specific gateway. It returns directory data but does not create contacts. + def list_members(self, installation: IMInstallation) -> Sequence[IMDirectoryMember]: ... + + +class RecipientResolver(Protocol): + # Converts node selectors into canonical runtime recipients and resolution records. + def resolve(self, domain: ApprovalDomainContext, request: RecipientResolutionRequest) -> RecipientResolutionResult: ... + + +class NotificationPlanner(Protocol): + # Chooses IM card, IM fallback URL, Email, or skipped delivery per recipient. + def plan(self, domain: ApprovalDomainContext, task: HITLTask, recipients: Sequence[ResolvedRecipient]) -> NotificationPlan: ... + + +class NotificationDispatcher(Protocol): + # Performs side effects and returns durable delivery attempts for audit and Last Run. + def dispatch(self, plan: NotificationPlan) -> Sequence[DeliveryAttempt]: ... + + +class RecipientAuthenticator(Protocol): + # Identifies the actor from Dify login, IM platform identity, magic link, or OTP. + def authenticate(self, domain: ApprovalDomainContext, request: ApprovalAccessRequest) -> SubmissionIdentity: ... + # Checks the authenticated actor against allowed approvers and current safety state. + def authorize_submission(self, domain: ApprovalDomainContext, task: HITLTask, identity: SubmissionIdentity) -> ApprovalDecision: ... + + +class HITLTaskRepository(Protocol): + # Persists immutable recipient snapshots when the task is created. + def create(self, task: HITLTask) -> HITLTaskId: ... + def get(self, task_id: HITLTaskId) -> HITLTask | None: ... + # Must be atomic so only one allowed approver can submit a pending task. + def mark_submitted(self, task_id: HITLTaskId, submission: SubmissionRecord) -> None: ... + + +class AuditLogPort(Protocol): + # Stores domain audit events without leaking secrets or raw sensitive content. + def record(self, event: AuditEvent) -> None: ... + + +class SecretStore(Protocol): + # Stores provider credentials and returns opaque handles for domain records. + def put(self, owner: CredentialOwnerRef, secret: SecretPayload) -> IMCredentialHandle: ... + # Secret access should be limited to infrastructure code that actually sends requests. + def get(self, handle: IMCredentialHandle) -> SecretPayload: ... +``` + +## 应用服务 + +| 服务 | 说明 | +| --- | --- | +| `ApprovalDomainResolver` | 从 workspace locator 或 task 解析 `ApprovalDomainContext`,隔离 CE / EE / SaaS 拓扑差异。 | +| `RosterBootstrapService` | 根据 `ApprovalDomainContext.roster_policy` 初始化和同步 Human Roster。SaaS / CE 通常同步 workspace member;EE 可使用不同 policy,不默认添加企业全量联系人。 | +| `ContactDirectoryService` | 创建 account-backed contact、拒绝错误 external contact、管理企业级 IM binding。 | +| `HumanRosterService` | 添加 Dify member、添加 external contact、移除或转换 roster entry、计算 selectable 状态。 | +| `IMIntegrationService` | 保存 IM app installation、测试连接、解析 callback URL、触发通讯录同步。 | +| `IMDirectorySyncService` | 从 provider 通讯录同步成员,优先匹配 IM user id,再按 email 匹配 `Account`,未匹配进入 unmatched list。 | +| `RecipientResolutionService` | 将节点配置中的 `RecipientSelector` 解析为 `ResolvedRecipient`,执行 canonicalization 与去重。 | +| `HITLTaskApplicationService` | 创建 `HITLTask`、冻结 snapshot、调用 notification planner 和 dispatcher。 | +| `SubmissionApplicationService` | 处理 IM card、fallback URL、Email OTP、Dify login 等提交入口,执行 authenticate 和 authorize。 | +| `HITLMigrationService` | 迁移旧 email recipient;能匹配 workspace member 则转 contact selector,否则保留 one-time email。 | + +## 主要流程 + +### Roster 初始化 + +1. 系统边界拿到 `workspace_id` 后调用 `ApprovalDomainResolver.resolve_for_workspace`。 +2. `RosterBootstrapService` 读取 `domain.roster_policy` 和 `domain.human_roster_id`。 +3. SaaS / CE:adapter 基于现有 `TenantAccountJoin` 查询 roster source members,为成员创建或更新 `ContactIdentity` 与 `RosterEntry`。 +4. EE:不导入企业全量联系人;workspace admin 通过 `HumanRosterService.add_dify_member` 添加。 +5. 成员离开时不物理删除 `RosterEntry`,改为 `left_workspace` 或 `pending_review`。 + +### 创建 HITL task + +1. workflow 运行上下文用 `workspace_id` 解析 `ApprovalDomainContext`,后续服务不再直接传 `workspace_id`。 +2. 从 `HumanInputNodeData.notification_policy` 读取 `RecipientSelector`。 +3. `RecipientResolutionService.resolve(domain, request)` 解析 roster entry、one-time email、dynamic email、current initiator。 +4. 对解析结果做 canonicalization,合并同一 approver 的多渠道 endpoint。 +5. `IMCredentialResolver` 与 `IMBindingRepository` 基于 `domain.im_integration_policy` 为每个 contact 解析 IM endpoint。 +6. 创建 `HITLTask` 和 immutable `TaskRecipientSnapshot`。 +7. `NotificationPlanner` 按能力矩阵选择 IM card、IM fallback URL 和 Email。 +8. `NotificationDispatcher` 发送并写入 `DeliveryAttempt`。 +9. 解析、跳过和失败原因进入 `RecipientResolutionRecord` 与 audit log。 + +### 提交 HITL task + +1. 打开 URL 或 IM card callback 时先定位 `HITLTask`,但 URL 本身不代表审批权限。 +2. `ApprovalDomainResolver.resolve_for_task` 恢复当前审批域上下文。 +3. `RecipientAuthenticator.authenticate(domain, request)` 基于 Dify login、IM platform identity、Magic link 或 Email OTP 识别提交人。 +4. `authorize_submission(domain, task, identity)` 用 `AllowedApprover` 和当前安全状态重新校验。 +5. 已提交、超时、取消、账号禁用、成员离开、external contact 删除、IM binding 删除、IM identity mismatch 均拒绝提交。 +6. 成功提交后只允许一次状态转移到 `submitted`。 + +## 与现有模型映射 + +| 现有对象 | 建议领域语义 | +| --- | --- | +| `Account` | account-backed `ContactIdentity` 的身份来源。 | +| `Tenant` | 系统入口 locator 和现有存储 owner;应用服务入口用它解析 `ApprovalDomainContext`。 | +| `TenantAccountJoin` | membership 数据源,用于 resolver / adapter 推导 roster source members 和提交时成员状态。 | +| `HumanInputNodeData` | 继续承载表单 schema;新增 `NotificationPolicy`,不要把 Contact 逻辑塞进旧 `EmailRecipients`。 | +| `HumanInputForm` | `HITLTask` 的持久化实现;应能恢复或重建 task 所属 `ApprovalDomainContext`。 | +| `HumanInputDelivery` | delivery method / delivery batch。后续需要补足 per recipient per channel record。 | +| `HumanInputFormRecipient` | `TaskRecipientSnapshot` 和 access token holder。它不是 Contact。 | + +## 分阶段落地建议 + +1. 先实现 `ApprovalDomainResolver` 和 `ApprovalDomainContext`,把 `workspace_id` 限制在系统入口和 adapter 内。 +2. 增加 `ContactDirectory`、`ContactIdentity`、`HumanRoster`、`RosterEntry`,让节点配置保存 roster-scoped contact reference。 +3. 扩展 HITL node config,引入 `NotificationPolicy` 和 `RecipientSelector`,兼容旧 `EmailRecipients`。 +4. 实现 `RecipientResolutionService.resolve(domain, request)`,把旧 email member / external email 迁移到新 recipient model。 +5. 在 `HumanInputFormRecipient` payload 中增加 snapshot 结构,保留旧 payload 兼容读取。 +6. 引入 `IMIntegrationService`、`IMCredentialResolver`、`IMBindingRepository` 和 directory sync / search,所有解析均基于 `ApprovalDomainContext`。 +7. 补齐 per recipient per channel delivery record、resolution record 和 audit event。 + +## 已确认决策与剩余风险 + +以下产品规则已经确认,不影响核心抽象,只影响 policy、权限矩阵或交互实现: + +1. Workflow editor 禁止创建 `ExternalContact`,仅允许输入 one-time email。 +2. Pending task 在 external contact 删除或 IM binding 删除后拒绝提交。 +3. IM identity 一期通过 directory sync / search 选择 provider user。 +4. SaaS dynamic email、Magic link、OTP、单 task recipient 数量和每日发送量属于 guardrail 配置,不影响 Contact / Roster / Recipient 抽象。 + +剩余会影响抽象演进的问题只有一个:SaaS 未来是否会出现“一个企业多个 workspace”。当前没有这个计划,所以 `ApprovalDomain` 暂不需要独立持久化;但领域层仍通过 `ApprovalDomainContext` 隔离拓扑细节,避免未来从 `tenant_id` 迁移到独立企业 ID 时扩散到 Contact、Roster、IM 和 HITL Runtime。 diff --git a/hitl_phase2_backend_linear_task_list.md b/hitl_phase2_backend_linear_task_list.md new file mode 100644 index 00000000000..bbc2b9f3ec5 --- /dev/null +++ b/hitl_phase2_backend_linear_task_list.md @@ -0,0 +1,589 @@ +# HITL Phase 2 后端 Linear 任务清单 + +目标:生成后续可同步到 Linear 的后端 issue 列表;本文件只写入本地,不写入 Linear。 + +## 信息来源 + +| 来源 | 读取结果 | +|-|-| +| PRD | [HITL:IM 通知与 Contact PRD](https://langgenius.feishu.cn/wiki/GMFdwe40Oi2rC9klP2HcNjnHnwd),最新读取到 `revision_id = 1959`,读取时间 2026-07-10。 | +| Linear Project | [HITL IM 支持](https://linear.app/dify/project/hitl-im-支持-cd5c0fe0752d),项目 ID `882439e3-f6f5-4474-9fee-255e11e16c51`,团队 `WTA`。 | +| Linear 里程碑 | `冻结产品文档` 2026-07-10;`确定前后端交互协议及 DSL schema` 2026-07-14;`连调` 2026-07-22;`测试` 2026-07-27。 | +| 现有 Linear issue | `WTA-1255 HITL IM 集成接口及 DSL 设计` 仍为待办;旧 IM 表单消息探索任务大多已取消;`WTA-735 为 Graphon 的 HITL 抽象一套接口` 已完成。 | +| 后端代码基线 | 已有 `human-input` 节点、`HumanInputForm`、`HumanInputDelivery`、`HumanInputFormRecipient`、Email delivery、form token、file upload token、暂停恢复、多 surface 表单提交。Phase 2 应扩展现有模型和运行时,不重复建设基础 HITL。 | + +## 拆分原则 + +1. 只包含后端工作,不包含前端、QA 明细和纯产品任务。 +2. 保留 `WTA-1255` 作为已有契约任务,建议更新描述和验收标准,不再新建重复契约 issue。 +3. 按 PRD 里程碑和 Linear 现有 milestone 对齐;后端实现类任务归入 `连调`,不放进 `确定前后端交互协议及 DSL schema`。 +4. `导入导出 / DSL ID-Email 转换` 已被 PRD 移出本期,不放入本期后端任务。 +5. 任务描述以 Linear issue 可直接复制为目标;依赖字段使用本文件临时编号,真正导入 Linear 后再替换为 Linear issue id。 + +## 里程碑归属口径 + +| Linear 里程碑 | 后端任务归属 | +|-|-| +| 冻结产品文档 | 只放 PRD / scope / blocker 收敛任务,不放数据模型设计或代码实现。 | +| 确定前后端交互协议及 DSL schema | 只放前后端解耦所需的 DSL schema、API contract、错误码、mock fixture、provider contract。 | +| 连调 | 放后端数据模型、migration、Contact / IM / HITL runtime 实现、provider adapter、审计、Last Run 和安全鉴权等实现任务。 | +| 测试 | 放后端测试支持、migration dry run、provider sandbox 证据、安全回归和 ready-to-test 交付物。 | + +## 依赖梳理口径 + +| 任务类型 | 依赖原则 | +|-|-| +| PRD / scope 收敛 | 不依赖实现任务,用来冻结本期边界和阻塞项。 | +| DSL / API / mock 契约 | 只依赖 PRD / scope 和高层 DSL 决策,不依赖数据库设计、migration 或 runtime 实现。完成后前端可以基于 mock 并行开发。 | +| 数据模型 / migration | 依赖 PRD / scope 和 DSL 决策,属于后端实现前置任务,挂 `连调` milestone。 | +| Runtime / provider / 安全实现 | 依赖数据模型、API contract 和对应基础能力,按可并行方向拆分。 | +| 测试准入 | 依赖核心实现和 provider adapter,输出 dry run、sandbox、安全回归和 ready-to-test 证据。 | + +## 建议同步策略 + +| 本地编号 | Linear 操作 | 说明 | +|-|-|-| +| BE-HITL-001 | 更新现有 `WTA-1255` | 保留已有契约 issue,补齐 DSL、API、错误码、mock、provider contract 的验收口径。 | +| 其他任务 | 新建 issue | 新建在项目 `HITL IM 支持`,团队 `WTA`,按对应 milestone 挂载。 | + +## 任务总览 + +| 本地编号 | 标题 | Linear 操作 | 里程碑 | 优先级 | 截止日期 | 依赖 | +|-|-|-|-|-|-|-| +| BE-HITL-001 | HITL IM 集成接口及 DSL 设计 | 更新 `WTA-1255` | 确定前后端交互协议及 DSL schema | 高 | 2026-07-14 | - | +| BE-HITL-002 | 确认后端范围与 PRD 决策回写差异 | 新建 | 冻结产品文档 | 高 | 2026-07-10 | - | +| BE-HITL-003 | 设计 Contact / IM / HITL 数据模型与迁移方案 | 新建 | 连调 | 高 | 2026-07-14 | BE-HITL-001、BE-HITL-002 | +| BE-HITL-004 | 定义后端 API contract、错误码和 mock fixture | 新建 | 确定前后端交互协议及 DSL schema | 高 | 2026-07-14 | BE-HITL-001、BE-HITL-002 | +| BE-HITL-005 | 实现 Organization / Approval Domain 解析策略 | 新建 | 连调 | 高 | 2026-07-17 | BE-HITL-003 | +| BE-HITL-006 | 实现 Contact Directory / Workspace Contact migration | 新建 | 连调 | 高 | 2026-07-18 | BE-HITL-003、BE-HITL-005 | +| BE-HITL-007 | 实现 Contact / External Contact 管理 API | 新建 | 连调 | 高 | 2026-07-19 | BE-HITL-006 | +| BE-HITL-008 | 实现 IM Binding 与 Workspace IM override 数据层 | 新建 | 连调 | 高 | 2026-07-19 | BE-HITL-003、BE-HITL-006 | +| BE-HITL-009 | 实现 IM Integration credential 与连接状态机 | 新建 | 连调 | 高 | 2026-07-19 | BE-HITL-003、BE-HITL-005 | +| BE-HITL-010 | 实现 IM 通讯录同步与 unmatched list | 新建 | 连调 | 高 | 2026-07-20 | BE-HITL-008、BE-HITL-009 | +| BE-HITL-011 | 扩展 HITL 节点 Notification Policy 与兼容 adapter | 新建 | 连调 | 高 | 2026-07-18 | BE-HITL-001、BE-HITL-004 | +| BE-HITL-012 | 实现 recipient resolver v2 与 Contact-centric 去重 | 新建 | 连调 | 高 | 2026-07-19 | BE-HITL-006、BE-HITL-008、BE-HITL-011 | +| BE-HITL-013 | 扩展 HITL task snapshot、allowed approver 与 resolution records | 新建 | 连调 | 高 | 2026-07-20 | BE-HITL-012 | +| BE-HITL-014 | 实现 Request Message Template 与 Email always-on 发送策略 | 新建 | 连调 | 高 | 2026-07-20 | BE-HITL-011、BE-HITL-013 | +| BE-HITL-015 | 实现 Email OTP 与 Web 独立页鉴权后端 | 新建 | 连调 | 高 | 2026-07-20 | BE-HITL-013、BE-HITL-014 | +| BE-HITL-016 | 实现提交时动态授权与原子提交 | 新建 | 连调 | 高 | 2026-07-20 | BE-HITL-013、BE-HITL-015 | +| BE-HITL-017 | | 新建 | 连调 | 中 | 2026-07-21 | BE-HITL-016 | +| BE-HITL-018 | 实现 delivery records 状态机与投递失败规则 | 新建 | 连调 | 高 | 2026-07-20 | BE-HITL-013、BE-HITL-014 | +| BE-HITL-019 | 实现审计数据落库与脱敏边界 | 新建 | 连调 | 高 | 2026-07-21 | BE-HITL-013、BE-HITL-016、BE-HITL-018 | +| BE-HITL-020 | 实现 Last Run / Debug 后端日志投影 | 新建 | 连调 | 中 | 2026-07-21 | BE-HITL-012、BE-HITL-018、BE-HITL-019 | +| BE-HITL-021 | 实现节点级错误、warning 和 timeout / expired 分流 | 新建 | 连调 | 高 | 2026-07-21 | BE-HITL-012、BE-HITL-016、BE-HITL-018 | +| BE-HITL-022 | 实现 IM provider runtime、callback 与身份校验框架 | 新建 | 连调 | 高 | 2026-07-20 | BE-HITL-009、BE-HITL-013、BE-HITL-016 | +| BE-HITL-023 | 实现 Slack 后端 adapter | 新建 | 连调 | 高 | 2026-07-22 | BE-HITL-010、BE-HITL-022 | +| BE-HITL-024 | 实现钉钉后端 adapter | 新建 | 连调 | 高 | 2026-07-22 | BE-HITL-010、BE-HITL-022 | +| BE-HITL-025 | 实现飞书 / Lark 后端 adapter | 新建 | 连调 | 高 | 2026-07-22 | BE-HITL-010、BE-HITL-022 | +| BE-HITL-026 | 实现 Microsoft Teams 后端 adapter | 新建 | 连调 | 高 | 2026-07-22 | BE-HITL-010、BE-HITL-022 | +| BE-HITL-027 | 实现企业微信后端 adapter 与 Web fallback | 新建 | 连调 | 中 | 2026-07-22 | BE-HITL-010、BE-HITL-022 | +| BE-HITL-028 | 实现旧 Email recipient 与 SaaS / CE Contact 初始化迁移 | 新建 | 测试 | 高 | 2026-07-24 | BE-HITL-006、BE-HITL-012、BE-HITL-013 | +| BE-HITL-029 | 实现 SaaS abuse guardrails 后端基础能力 | 新建 | 测试 | 高 | 2026-07-24 | BE-HITL-014、BE-HITL-015、BE-HITL-018 | +| BE-HITL-030 | 实现审批人是 member 的通知中心后端接口 | 新建 | 测试 | 中 | 2026-07-25 | BE-HITL-013、BE-HITL-016 | +| BE-HITL-031 | 后端联调支持与测试证据包 | 新建 | 测试 | 高 | 2026-07-27 | BE-HITL-018、BE-HITL-019、BE-HITL-023、BE-HITL-024、BE-HITL-025、BE-HITL-026、BE-HITL-027、BE-HITL-028、BE-HITL-029 | + +## Issue 明细 + +### BE-HITL-001:HITL IM 集成接口及 DSL 设计 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 更新现有 `WTA-1255` | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `确定前后端交互协议及 DSL schema` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-14 | +| 标签 | backend、contract、dsl、api | +| 描述 | 基于 PRD revision 1959 冻结后端 DSL、API contract、recipient selector、Notification Policy、provider contract、错误码、feature flag 和兼容策略。需要明确旧 `human-input` / 现有 Email delivery / WebApp delivery 如何兼容,避免 Phase 2 重建一套基础 HITL。 | +| 验收标准 | 1. DSL 覆盖 static Contact、one-time Email、dynamic Email、current initiator、debug only notify me、Request Message Template、Notification Policy。
2. API contract 覆盖 Contact、IM Integration、IM Binding、Workspace IM override、OTP、Last Run、delivery records。
3. 错误码覆盖 dynamic Email 解析异常、无可通知对象、OTP 失败、权限失败、task 已完成或过期。
4. 明确旧 published workflow 不自动改写,新旧运行路径分流策略清楚。 | + +### BE-HITL-002:确认后端范围与 PRD 决策回写差异 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `冻结产品文档` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-10 | +| 标签 | backend、prd、scope | +| 描述 | 对 PRD revision 1959 做后端实现范围确认,标出已进入本期、已移出本期、仍需产品或安全确认但不阻塞实现的事项。 | +| 验收标准 | 1. 明确 Workspace IM override 本期做,但不覆盖 IM Integration credential。
2. 明确导入导出 / DSL ID-Email 转换移出本期。
3. 明确 Web 独立页审批按审批主体鉴权,不再按统一 OTP 或 Magic Link。
4. 明确 form/task status 复用 `WAITING / SUBMITTED / TIMEOUT / EXPIRED`。
5. 输出后端 scope note,可贴到 `WTA-1255` 或项目文档。 | + +### BE-HITL-003:设计 Contact / IM / HITL 数据模型与迁移方案 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-14 | +| 标签 | backend、database、migration | +| 依赖 | BE-HITL-001、BE-HITL-002 | +| 描述 | 设计 Contact Directory、Workspace Contact、External Contact、IM Binding、Workspace IM override、IM Integration credential、recipient snapshot、delivery record、resolution record、audit record 的后端数据模型。需要复用现有 `HumanInputForm`、`HumanInputDelivery`、`HumanInputFormRecipient`,只做必要扩展。 | +| 验收标准 | 1. 表结构、唯一约束、索引、状态枚举和 owner scope 明确。
2. CE / SaaS 当前 workspace 即 Organization,EE deployment 为 Organization 的映射清楚。
3. Contact 类型覆盖 workspace member、Organization contact、External contact。
4. `HumanInputFormRecipient` 明确作为 task recipient snapshot,不被误建模为 Contact。
5. migration dry run 与 rollback 方案明确。 | + +### BE-HITL-004:定义后端 API contract、错误码和 mock fixture + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `确定前后端交互协议及 DSL schema` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-14 | +| 标签 | backend、api、contract、mock | +| 依赖 | BE-HITL-001、BE-HITL-002 | +| 描述 | 输出供前端并行开发使用的后端 API contract、response shape、error code、mock response 和 fixture。范围包括 Contact、IM Integration、IM Binding、Workspace override、recipient resolution preview、OTP、Last Run、provider status。 | +| 验收标准 | 1. 前端无需等待真实实现即可基于 mock 开发。
2. mock 字段与后续真实 API 保持一致。
3. provider connection status 六态可表达:Not configured、Configured、Connected、Permission issue、Callback error、Connection error。
4. API 明确权限失败、资源不存在、不可选择、不可提交、OTP 失败、provider 失败的稳定错误码。 | + +### BE-HITL-005:实现 Organization / Approval Domain 解析策略 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-17 | +| 标签 | backend、domain、organization | +| 依赖 | BE-HITL-003 | +| 描述 | 实现从 `tenant_id / workspace_id` 到当前 Organization / Contact scope / IM credential scope 的解析策略。代码内部可保留审批域 resolver 边界,但对外使用 PRD 的 Organization / Contact 口径。 | +| 验收标准 | 1. CE / SaaS 使用当前 workspace 作为 Organization。
2. EE 使用 deployment 级 Organization,并能解析当前 workspace 的 Contact scope。
3. 后续 Contact、IM Integration、recipient resolver 和提交鉴权不直接散落判断部署形态。
4. 单测覆盖 CE / SaaS / EE 策略分支。 | + +### BE-HITL-006:实现 Contact Directory / Workspace Contact migration + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-18 | +| 标签 | backend、contact、migration | +| 依赖 | BE-HITL-003、BE-HITL-005 | +| 描述 | 实现 Contact Directory 和 Workspace Contact 的持久化与初始化迁移。SaaS / CE workspace members 自动进入 Contact;EE 支持从 Organization Contact 添加到 workspace Contact。 | +| 验收标准 | 1. workspace member 新增后可进入 Contact。
2. CE / SaaS 成员移除后从新配置不可选择,历史引用可解释。
3. EE 支持 Organization contact 语义,不把其他 workspace 的 Dify member 建成 external contact。
4. normalized email 使用完整 email lower-case 后完全相等。
5. migration 可重复执行,索引和唯一约束能防止重复联系人。 | + +### BE-HITL-007:实现 Contact / External Contact 管理 API + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-19 | +| 标签 | backend、api、contact、rbac | +| 依赖 | BE-HITL-006 | +| 描述 | 实现 Contact 列表、搜索、添加 Dify member、添加 External contact、删除 External contact、状态查询等 API。预留 `Manage contacts` 能力边界。 | +| 验收标准 | 1. 添加 External contact 前必须先按 normalized email 查找可见 Dify Account / Contact,命中时不能创建 external contact。
2. workflow editor 不能通过节点配置直接创建 External contact。
3. 普通 member 不获得完整 Contact 管理权限。
4. 删除 External contact 后新配置不可选择,历史 workflow 和历史 task snapshot 可读。 | + +### BE-HITL-008:实现 IM Binding 与 Workspace IM override 数据层 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-19 | +| 标签 | backend、im、binding | +| 依赖 | BE-HITL-003、BE-HITL-006 | +| 描述 | 实现 Organization 全局 IM identity、Workspace IM override 的持久化、查询和解析优先级。Workspace override 只影响当前 workspace 内联系人 IM 身份 / 通知行为,不覆盖 provider credential。 | +| 验收标准 | 1. 运行时优先级为 workspace override > Organization IM identity > Email。
2. 同一个联系人在不同 workspace 可有不同 override。
3. pending task 提交时必须按当前 IM Binding 动态授权。
4. 删除或修改 binding 不破坏历史 snapshot,但会影响 pending task 提交资格。 | + +### BE-HITL-009:实现 IM Integration credential 与连接状态机 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-19 | +| 标签 | backend、im、credential、security | +| 依赖 | BE-HITL-003、BE-HITL-005 | +| 描述 | 实现 IM Integration credential 存储、脱敏读取、test connection、connection status 和 reason。凭据归属 Organization,secret 不进入响应、日志、delivery record 或 audit。 | +| 验收标准 | 1. 支持 Not configured、Configured、Connected、Permission issue、Callback error、Connection error。
2. 保存凭据后进入 Configured,test connection 成功后进入 Connected。
3. 权限不足、callback 校验失败、凭据错误、provider 不可达能写入可排查 reason。
4. credential 轮换记录操作日志,旧回调失败可定位。 | + +### BE-HITL-010:实现 IM 通讯录同步与 unmatched list + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-20 | +| 标签 | backend、im、sync、contact | +| 依赖 | BE-HITL-008、BE-HITL-009 | +| 描述 | 实现 IM directory sync 服务:从 provider 拉成员,优先按 provider user id 匹配已有 IM Binding,其次按 Email 匹配 Dify Account;未匹配进入 unmatched list,不自动创建 external contact。 | +| 验收标准 | 1. sync 结果包含 matched、created / updated bindings、unmatched、skipped、failed。
2. 未匹配 IM 成员不自动创建 external contact。
3. 能匹配 Dify Account 的对象不落为 external contact。
4. sync 失败可写入 provider status reason 和 audit / operation log。 | + +### BE-HITL-011:扩展 HITL 节点 Notification Policy 与兼容 adapter + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-18 | +| 标签 | backend、dsl、workflow | +| 依赖 | BE-HITL-001、BE-HITL-004 | +| 描述 | 扩展现有 `HumanInputNodeData` / adapter,支持 Notification Policy、Notified recipients、dynamic Email、one-time Email、current initiator、debug only notify me、Request Message Template,同时保持旧 `delivery_methods` 兼容读取。 | +| 验收标准 | 1. 新 DSL 可被保存、发布、执行。
2. 旧 published workflow 的 `human-input` 不被自动改写。
3. 旧 Email / WebApp delivery 继续兼容。
4. 新旧 schema adapter 有单测覆盖。 | + +### BE-HITL-012:实现 recipient resolver v2 与 Contact-centric 去重 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-19 | +| 标签 | backend、resolver、contact | +| 依赖 | BE-HITL-006、BE-HITL-008、BE-HITL-011 | +| 描述 | 实现 recipient resolver v2,解析 static Contact、one-time Email、dynamic Email、current initiator、debug override,并以 Contact 为中心归并 allowed approver。 | +| 验收标准 | 1. dynamic Email 只支持单个合法 email,不支持数组 / object / number / boolean。
2. dynamic Email 命中 Contact 时升级为 Contact recipient;未命中时按 one-time Email。
3. 同一个 Dify Account 只生成一个 allowed approver。
4. 同一个 normalized email 去重。
5. current initiator 与 notified recipients 命中同一人时合并。
6. 解析异常覆盖 variable_missing、variable_empty、unsupported_type、invalid_email、duplicated_email、matched_contact_unavailable、recipient_no_available_channel。 | + +### BE-HITL-013:扩展 HITL task snapshot、allowed approver 与 resolution records + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-20 | +| 标签 | backend、runtime、database | +| 依赖 | BE-HITL-012 | +| 描述 | 扩展现有 `HumanInputForm` / `HumanInputFormRecipient` 作为 HITL task 和 recipient snapshot 的持久化实现,记录 allowed approvers、matched sources、recipient snapshot、auth requirement 和 recipient resolution records。 | +| 验收标准 | 1. task 创建时冻结 recipient snapshot,用于审计和历史展示。
2. snapshot 不作为提交授权的唯一依据。
3. resolution record 可用于 Last Run 展示 raw value、status、error code、resolved recipient type。
4. 兼容现有 form token 和 recipient token。 | + +### BE-HITL-014:实现 Request Message Template 与 Email always-on 发送策略 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-20 | +| 标签 | backend、email、notification | +| 依赖 | BE-HITL-011、BE-HITL-013 | +| 描述 | 将现有 Email delivery 升级为通用 Request Message Template 语义。对于有 IM binding 且 Email 可用的 recipient,默认并行创建 IM 与 Email delivery attempt;Email 本期不可关闭。 | +| 验收标准 | 1. Email 内容包含足够任务上下文:app、workflow、node / request title、message、有效期、短 task reference。
2. 有 IM binding 时仍创建 Email delivery attempt。
3. 无 IM binding 时 Email 可单独触达。
4. Email 发送失败写入 delivery status 和 reason。 | + +### BE-HITL-015:实现 Email OTP 与 Web 独立页鉴权后端 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-20 | +| 标签 | backend、security、otp | +| 依赖 | BE-HITL-013、BE-HITL-014 | +| 描述 | 实现 Web 独立审批页的后端鉴权:Dify Account / workspace member / Organization contact 走 Dify 登录;无 Dify Account 的 External contact、one-time Email、未命中 Contact 的 dynamic Email 走 Email OTP。 | +| 验收标准 | 1. OTP 支持发送、重发、校验、过期、次数限制和稳定错误码。
2. Email token 只用于定位 task / recipient,不单独代表审批权限。
3. 打开页面和提交表单都校验 task 状态、过期时间、allowed approver。
4. Magic Link 不作为本期方案。 | + +### BE-HITL-016:实现提交时动态授权与原子提交 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-20 | +| 标签 | backend、security、concurrency | +| 依赖 | BE-HITL-013、BE-HITL-015 | +| 描述 | 重构提交路径,提交时按当前 Dify identity、Contact 状态、IM Binding 状态和 allowed approver 动态授权,并将提交更新改为 first-writer-wins 原子更新。 | +| 验收标准 | 1. 成员离开 workspace、Account disabled、External contact 删除、IM Binding 删除或 mismatch 时拒绝 pending task 提交。
2. 同一 task 并发提交只有一个成功,其余返回已完成。
3. `WAITING -> SUBMITTED` 原子化;`TIMEOUT / EXPIRED / SUBMITTED` 不可再提交。
4. 单测覆盖 race、身份不匹配、旧 binding 失效、task 已过期。 | + +### BE-HITL-017:补齐文件访问授权与 HITL task 绑定校验 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 中 | +| 状态 | 待办 | +| 截止日期 | 2026-07-21 | +| 标签 | backend、file、security | +| 依赖 | BE-HITL-016 | +| 描述 | 在现有 HITL standalone upload token 基础上补齐审批页文件预览 / 下载授权:不能只依赖 URL token,必须同时校验 task 可访问、访问者身份已验证、访问者命中 allowed approver、文件属于该 HITL task。 | +| 验收标准 | 1. 文件访问在 task submitted / timeout / expired / revoked 后失效或受限。
2. 跨 task / 跨 tenant 文件不可访问。
3. 失败原因可区分 task expired、not assigned to you、file unavailable。
4. 不改变现有 WebApp / Service API 文件上传模型。 | + +### BE-HITL-018:实现 delivery records 状态机与投递失败规则 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-20 | +| 标签 | backend、delivery、runtime | +| 依赖 | BE-HITL-013、BE-HITL-014 | +| 描述 | 补齐 per recipient / per channel delivery record。每个 recipient 每个渠道记录 pending、sent、failed、skipped、provider message id、error code、error message、sent_at。 | +| 验收标准 | 1. IM + Email 双发时生成两个 delivery attempt,但只对应同一个 allowed approver。
2. delivery_failed 不进入 `HumanInputFormStatus`。
3. 全部渠道失败且没有可继续等待入口时,节点失败并记录原因。
4. 部分失败时 task 继续等待,Last Run 可展示未通知对象和失败原因。 | + +### BE-HITL-019:实现审计数据落库与脱敏边界 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-21 | +| 标签 | backend、audit、security | +| 依赖 | BE-HITL-013、BE-HITL-016、BE-HITL-018 | +| 描述 | 本期不提供审计 UI,但后端必须保留审计相关数据,使管理员可通过数据库查询回答通知对象、投递渠道、身份校验、提交人、提交结果和拒绝原因。 | +| 验收标准 | 1. 记录 task 所属 workspace、app、workflow、workflow run、node。
2. 记录运行时通知对象、recipient snapshot、channels、delivery result。
3. 记录提交人身份类型、提交渠道、是否为 allowed approver、提交时间和结果。
4. 非指定人提交、身份校验失败、成员退出、账号禁用、task 已提交或过期等拒绝事件有记录。
5. secret 不入库;Email / IM 正文不完整进入审计;表单敏感内容按策略脱敏或摘要。 | + +### BE-HITL-020:实现 Last Run / Debug 后端日志投影 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 中 | +| 状态 | 待办 | +| 截止日期 | 2026-07-21 | +| 标签 | backend、last-run、debug | +| 依赖 | BE-HITL-012、BE-HITL-018、BE-HITL-019 | +| 描述 | 为前端 Last Run / Debug 提供后端数据投影,展示 resolved recipients、dynamic email raw value、delivery channel、sent / failed / skipped、failure reason、submitted_by、submitted_at。 | +| 验收标准 | 1. Last Run API 可展示 recipient resolution records 和 delivery records。
2. Debug 变量池没有值时,后端支持手动变量值进入 resolver。
3. raw dynamic value 的可见范围与脱敏策略一致。
4. 输出结构稳定,可供前端联调。 | + +### BE-HITL-021:实现节点级错误、warning 和 timeout / expired 分流 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-21 | +| 标签 | backend、runtime、workflow | +| 依赖 | BE-HITL-012、BE-HITL-016、BE-HITL-018 | +| 描述 | 按 PRD 固化节点执行失败、partial warning、timeout、global expired 的后端行为。form/task status 固定为 WAITING、SUBMITTED、TIMEOUT、EXPIRED。 | +| 验收标准 | 1. notified recipients 为空且 current initiator 不可用时,节点直接失败,不创建等待 task。
2. 全部 recipient 无法通知时节点失败。
3. 部分 recipient 可通知时 task 继续等待,并记录未通知对象。
4. 节点 timeout 进入 timeout handle;global expired 中止 workflow run,不进入 timeout handle。 | + +### BE-HITL-022:实现 IM provider runtime、callback 与身份校验框架 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-20 | +| 标签 | backend、provider、im、security | +| 依赖 | BE-HITL-009、BE-HITL-013、BE-HITL-016 | +| 描述 | 实现统一 IM provider runtime:credential resolver、capability matrix、message rendering、callback signature / replay protection、IM identity extraction、card submit、Web fallback URL。 | +| 验收标准 | 1. Provider adapter 具备统一接口:test connection、sync members、send request、verify callback、extract identity、handle submit。
2. 支持卡片内审批的 provider 走 IM identity -> IM Binding -> Contact -> allowed approver。
3. 不支持卡片内审批时发送 request message + Web 审批页 URL,Web 页面按审批主体鉴权。
4. callback 失败写入 delivery / audit reason,secret 不进日志。 | + +### BE-HITL-023:实现 Slack 后端 adapter + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-22 | +| 标签 | backend、provider、slack | +| 依赖 | BE-HITL-010、BE-HITL-022 | +| 描述 | 实现 Slack 后端 adapter,覆盖 SaaS Slack ISV / OAuth 和 EE 企业自建 App 的 credential、通讯录同步、消息发送、身份校验、卡片提交、Email 双发记录。 | +| 验收标准 | 1. OAuth / self-built credential 可 test connection。
2. sync 可建立或更新 IM Binding。
3. Slack card submit 可通过 IM identity 动态授权。
4. 失败写入 delivery status、provider reason 和 audit。 | + +### BE-HITL-024:实现钉钉后端 adapter + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-22 | +| 标签 | backend、provider、dingtalk | +| 依赖 | BE-HITL-010、BE-HITL-022 | +| 描述 | 实现钉钉后端 adapter,覆盖 SaaS / EE 企业自建应用 credential、通讯录同步、消息发送、callback 校验、用户身份映射和 Web fallback。 | +| 验收标准 | 1. 租户自建应用 credential 可配置并 test connection。
2. 通讯录同步不自动创建 external contact。
3. 卡片内审批或 fallback URL 均能记录 delivery / audit。
4. 权限缺失和 callback error 可定位到 connection status reason。 | + +### BE-HITL-025:实现飞书 / Lark 后端 adapter + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-22 | +| 标签 | backend、provider、lark | +| 依赖 | BE-HITL-010、BE-HITL-022 | +| 描述 | 实现飞书 / Lark EE 企业自建 App adapter,覆盖 credential、通讯录同步、卡片通知、身份校验、提交回调和失败记录。 | +| 验收标准 | 1. 企业自建 App 可 test connection。
2. 卡片提交按 IM identity 动态授权。
3. fallback URL 不被当作 IM 身份链路,按 Web 独立页鉴权。
4. sandbox E2E 记录可进入后端测试证据包。 | + +### BE-HITL-026:实现 Microsoft Teams 后端 adapter + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-22 | +| 标签 | backend、provider、teams | +| 依赖 | BE-HITL-010、BE-HITL-022 | +| 描述 | 实现 Microsoft Teams EE 企业自建 Teams App adapter,覆盖 credential、成员同步、Adaptive Card 或 fallback message、身份校验、提交回调和 delivery record。 | +| 验收标准 | 1. Teams App credential 可 test connection。
2. Adaptive Card 支持的表单路径可提交;不支持时走 Web fallback。
3. 身份校验失败、权限不足、callback error 均有稳定 reason。
4. sandbox E2E 记录可进入后端测试证据包。 | + +### BE-HITL-027:实现企业微信后端 adapter 与 Web fallback + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `连调` | +| 优先级 | 中 | +| 状态 | 待办 | +| 截止日期 | 2026-07-22 | +| 标签 | backend、provider、wecom | +| 依赖 | BE-HITL-010、BE-HITL-022 | +| 描述 | 实现企业微信 EE 企业自建应用 adapter。PRD 标注企业微信卡片内审批能力有限,本期以后端通知 + Web 审批页 fallback 为硬门禁,卡片内完整审批不作为必交。 | +| 验收标准 | 1. 企业微信 credential 可 test connection。
2. 可发送 request message + Web approval URL。
3. Web fallback 按审批主体鉴权,不依赖企业微信身份 fallback。
4. 卡片能力限制在 provider capability 中明确表达。 | + +### BE-HITL-028:实现旧 Email recipient 与 SaaS / CE Contact 初始化迁移 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `测试` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-24 | +| 标签 | backend、migration、compatibility | +| 依赖 | BE-HITL-006、BE-HITL-012、BE-HITL-013 | +| 描述 | 实现旧 Email recipient 迁移和 SaaS / CE Contact 初始化迁移。本期不做导入导出 / DSL ID-Email 转换。 | +| 验收标准 | 1. 旧 Email recipient 匹配当前 workspace member 时迁移为 member / Contact recipient。
2. 匹配到 Dify Account 但不属于当前 workspace 时,不自动创建 external contact。
3. 无法匹配 Dify Account 时保留 one-time Email。
4. 不自动改写已发布 workflow;运行中 task 使用创建时 snapshot。
5. dry run、rollback 和重复执行有记录。 | + +### BE-HITL-029:实现 SaaS abuse guardrails 后端基础能力 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `测试` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-24 | +| 标签 | backend、saas、security、rate-limit | +| 依赖 | BE-HITL-014、BE-HITL-015、BE-HITL-018 | +| 描述 | PRD 将具体阈值交给 SaaS 团队确认;后端需要先提供可配置的基础 guardrail 能力,覆盖 OTP、dynamic Email、单 task 收件人数、发送量和发送日志。 | +| 验收标准 | 1. OTP 重试、重发、校验失败可限流。
2. dynamic Email 发送和单 task recipient 数量有可配置上限入口。
3. 发送日志足以支持 abuse 排查。
4. 阈值未最终确认时可使用保守默认值或 feature flag,不阻塞后续调整。 | + +### BE-HITL-030:实现审批人是 member 的通知中心后端接口 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `测试` | +| 优先级 | 中 | +| 状态 | 待办 | +| 截止日期 | 2026-07-25 | +| 标签 | backend、api、notification-center | +| 依赖 | BE-HITL-013、BE-HITL-016 | +| 描述 | 按 PRD Milestone 4 提供“审批人是 member”的最小通知中心后端接口:查询待处理 HITL task、查看 task 状态、提交 task。完整站内通知中心和 CLI 待办能力后续专题讨论。 | +| 验收标准 | 1. 只覆盖 Dify member / workspace member 审批人。
2. 查询结果只返回当前登录用户可审批 task。
3. 提交仍复用动态授权和原子提交逻辑。
4. 不扩展完整 CLI 待办能力。 | + +### BE-HITL-031:后端联调支持与测试证据包 + +| 字段 | 内容 | +|-|-| +| Linear 操作 | 新建 | +| 团队 | `WTA` | +| 项目 | `HITL IM 支持` | +| 里程碑 | `测试` | +| 优先级 | 高 | +| 状态 | 待办 | +| 截止日期 | 2026-07-27 | +| 标签 | backend、test、release | +| 依赖 | BE-HITL-018、BE-HITL-019、BE-HITL-023、BE-HITL-024、BE-HITL-025、BE-HITL-026、BE-HITL-027、BE-HITL-028、BE-HITL-029 | +| 描述 | 整理后端联调环境、provider sandbox、migration dry run、单测 / 集成测试结果、安全回归记录和已知限制,供 2026-07-27 测试 milestone 接手。 | +| 验收标准 | 1. 后端 API contract 与前端联调范围一致。
2. 每个纳入范围的 provider 有 sandbox E2E 或明确降级 / out-of-scope 结论。
3. migration dry run 和 rollback 记录齐备。
4. P0/P1 后端缺陷为 0 或有明确 no-go。
5. 测试证据可贴入 PR 描述或 Linear 项目文档。 | + +## 不进入本次后端 Linear 清单的事项 + +| 事项 | 原因 | +|-|-| +| 前端 Contact / IM Integration / HITL 节点配置 / Last Run UI | 用户本次要求只包含后端工作。 | +| QA 具体执行用例 | QA 可以单独维护测试明细;本清单只保留后端测试支持与证据包。 | +| 导入导出 / DSL ID-Email 转换 | PRD revision 1959 明确移出本期 Milestone 4。 | +| 完整站内通知中心和 CLI 待办能力 | PRD 仅建议优先考虑 member 通知中心接口,完整能力后续专题讨论。 | +| SaaS 除 Slack 外的其他 IM ISV / Marketplace 接入 | PRD 非目标;本期 SaaS 仅 Slack ISV / OAuth 与钉钉企业自建应用。 | diff --git a/hitl_phase2_decisions.md b/hitl_phase2_decisions.md new file mode 100644 index 00000000000..7d82df907bf --- /dev/null +++ b/hitl_phase2_decisions.md @@ -0,0 +1,378 @@ +# HITL Phase 2 Decisions + +## Decision 1: Email Matching + +决策日期:2026-07-09 + +适用范围: + +- `external contact` 创建时的 Dify Account 匹配 +- 旧 Email recipient 迁移 +- `dynamic Email` 与现有联系人匹配 +- `Recipient canonicalization` / Email recipient 去重 + +决策: + +- 本期将 `normalized email` 定义为:对完整 email 字符串执行 `str.lower()` 后得到的结果。 +- 比较规则为:两个 email 在分别执行 `str.lower()` 后,结果完全相等,则视为同一个 email identity。 + +示意: + +```text +normalized_email = email.str.lower() +same_email = left.str.lower() == right.str.lower() +``` + +本期明确不做: + +- 不做 `+tag` 消解 +- 不做 `.` 折叠 +- 不做 provider-specific 规则 +- 不做 domain / local-part 分段差异化处理 + +例子: + +- `Alice@Example.com` 和 `alice@example.com`:视为相同 +- `alice+ops@example.com` 和 `alice@example.com`:视为不同 +- `alice.smith@example.com` 和 `alicesmith@example.com`:视为不同 + +影响说明: + +- 这是一条最小规则,目标是先让 `external contact` 判定、旧数据迁移、recipient 去重和 `dynamic Email` 匹配行为一致。 +- 该规则不试图表达邮箱协议或特定 provider 的全部语义,只定义本期产品比较规则。 + +待回写 PRD 的章节: + +- §7.5 外部联系人 +- §11.3 旧数据迁移 +- §18.6 Recipient canonicalization 与去重 +- §18.7 Sync、导入导出与迁移一致性 + +--- + +## Decision 2: IM Card Approval vs Web App Approval Auth + +决策日期:2026-07-09 + +适用范围: + +- IM 卡片内审批 +- IM 通知后跳转到 Web App 独立页面审批 +- external contact / one-time Email / dynamic Email 的 Web App 审批入口 +- PRD 中 `IM identity`、`IM fallback URL`、`Email OTP` 的关系定义 + +决策: + +- `IM 卡片内审批` 与 `Web App 独立页面审批` 是两条不同的鉴权链路。 +- `IM 卡片内审批`:通过 IM 平台身份识别用户,再映射到 Dify contact / IM binding,提交时按 IM 身份链路鉴权。 +- `Web App 独立页面审批`:不依赖 IM 平台身份,统一走 OTP 作为验证手段。 +- 因此,`Web App 独立页面审批` 不应再被表述为 “IM 身份获取失败后的 fallback 鉴权”,而应被表述为一条独立的审批入口与鉴权规则。 + +本期明确不做: + +- 不把 `Web App 独立页面审批` 建模为 IM 身份 fallback +- 不要求 Web App 打开页复用 IM provider identity +- 不在这条决策里同时保留 `Magic Link 或 OTP` 的双方案表述 + +影响说明: + +- IM provider 能力矩阵中的 “可获取平台身份(鉴权)” 应只描述 IM 卡片内审批或 IM 原生交互场景,不应外溢到 Web App 独立页面审批。 +- PRD 中涉及 `IM fallback URL` 的文案需要收敛,避免把 “独立 Web 页面审批” 与 “IM 身份 fallback” 混写。 +- external contact / one-time Email / dynamic Email 的 Web 页面审批规则应统一引用 OTP。 + +待回写 PRD 的章节: + +- §17 URL 鉴权与访问控制 +- §17.3 不同对象的鉴权方式 +- §17.5 不同场景的策略 +- §18.5 IM 渠道能力矩阵 + +--- + +## Decision 3: Default Dual-Channel Delivery + +决策日期:2026-07-09 + +适用范围: + +- §9.1 默认双渠道触达 +- §9.2 IM 通知 +- §18.5 IM 渠道能力矩阵 +- HITL task 运行时通知派发语义 + +决策: + +- 对于具有 IM binding 且 Email 可用的 recipient,本期默认并行发送 IM 与 Email。 +- 这里的“并行发送”指两个渠道都被创建为当前 task 的 delivery attempt;不会等待某一侧成功或失败后再决定是否发送另一侧。 +- provider 能力只影响 IM 侧的投递形态: + - 支持卡片内审批:发送 IM 卡片。 + - 不支持卡片内审批但支持 IM 通知:发送 IM 消息 + fallback URL。 +- Email 在本期仍然是必发渠道;不会因为 IM 已发送、IM 成功、IM 失败或 provider 能力差异而被跳过。 +- 多渠道触达不改变审批主体语义:同一个 recipient 仍只对应一个 `allowed approver`,IM 与 Email 只是同一审批对象的多个 delivery channel。 + +本期明确不做: + +- 不做“先 IM,后 Email”的串行降级。 +- 不做“只有 IM 失败才发 Email”的条件发送。 +- 不做按 provider 能力关闭 Email 双发。 +- 不做按用户、workspace 或 provider 配置通知优先级。 + +影响说明: + +- 这条规则消除了“默认双渠道触达”在运行时语义上的歧义,避免实现层把它理解为串行降级或 provider 决定是否发 Email。 +- `provider capability` 只负责决定 IM 通知长什么样,不负责决定 Email 是否发送。 + +待回写 PRD 的章节: + +- §9.1 默认双渠道触达 +- §9.2 IM 通知 +- §18.5 IM 渠道能力矩阵 + +--- + +## Decision 4: Dynamic Authorization at Submit Time + +决策日期:2026-07-09 + +适用范围: + +- pending HITL task 的最终提交资格判定 +- IM 绑定联系人通过 IM 卡片或其他 IM 身份链路提交 +- 提交时 `Dify identity -> IM Binding -> valid contact` 的再校验规则 +- 联系人、IM binding、contact 记录在 task 创建后发生变化时的提交行为 + +决策: + +- 本期采用 `动态授权`。 +- HITL task 是否允许提交,始终以 `提交当时` 的 Dify 身份与 IM Binding 状态为准,而不是只依赖 task 创建时 snapshot。 +- 对于需要按 IM 身份提交的链路,系统在提交时必须使用当前的 `im_user_id` 去查找当前有效的 IM Binding。 +- 如果提交当时找不到 `im_user_id` 对应的 binding,则不允许提交。 +- 如果能找到 binding,但该 binding 已经无法继续提交,例如已经没有有效的 contact 记录,则不允许提交。 +- 历史 snapshot 仍然用于审计、展示和问题排查,但不单独构成提交授权。 + +本期明确不做: + +- 不采用 “task 创建时拿到链接就永久可提” 的快照授权模型 +- 不允许旧 binding 在提交时绕过当前 binding / contact 状态继续提交 +- 不把历史 snapshot 当作提交权限本身 + +影响说明: + +- 这条规则把 pending task 的提交资格收敛为“实时安全状态判定”,会直接影响成员离开 workspace、IM binding 删除/替换、contact 失效后的提交结果。 +- PRD 中凡是写到 “pending task 默认建议重新校验” 的地方,都应升级为明确规则,而不是保留为建议。 +- 提交校验应区分“审计快照保留”和“实时提交授权”两件事:前者保留历史,后者只看当前有效身份链路。 + +待回写 PRD 的章节: + +- §17.2 基本原则 +- §17.3 不同对象的鉴权方式 +- §17.4 打开页面与提交表单的校验 +- §18.2 联系人生命周期 +- §18.9 变更影响矩阵 + +--- + +## Decision 5: Import/Export and ID-Email Conversion Out of Scope + +决策日期:2026-07-09 + +适用范围: + +- §11.2 导入导出 +- §15 Milestone 4 中的 “DSL 导入导出 ID / Email 转换” +- 任何将联系人 ID、external contact、one-time Email、dynamic Email 在环境之间导入、导出、转换的实现范围判断 + +决策: + +- `导入导出 / DSL ID-Email 转换` 不作为本期目标。 +- 本期只保留与现网兼容直接相关的: + - 旧 Email recipient 到新模型的迁移 + - SaaS / CE 初始化迁移 +- 不应把跨环境导入、导出、ID 映射恢复、Email 转换恢复视为本期交付范围。 + +本期明确不做: + +- 不做 DSL 导出时的联系人快照映射规则 +- 不做 DSL 导入时的联系人恢复、ID 匹配、Email 回填 +- 不做 external contact / one-time Email / dynamic Email 的跨环境转换策略 +- 不把 `Milestone 4` 中的 `DSL 导入导出 ID / Email 转换` 视为当前版本目标 + +影响说明: + +- 当前缺的不是范围决策,而是 PRD 回写一致性。 +- §11.2 已经整体划掉,因此 Milestone 4 中对应条目应删除、后移,或显式标注为后续阶段。 +- 评审文档里不应再把这个问题列为“待产品回答的 blocker”,而应标记为“已决策但 PRD 未回写一致”。 + +待回写 PRD 的章节: + +- §11.2 导入导出 +- §15 Milestone 4 + +--- + +## Decision 6: Allowed Approver Is Contact-Centric and Fixed at Delivery Time + +决策日期:2026-07-09 + +适用范围: + +- `allowed approver` 的产品语义 +- 通知发送 / Email 发送时的审批主体确定与记录 +- `Contact`、`Dify Account`、`External Contact` 在审批时的关系 +- Member Contact / External Contact 的提交鉴权方式 + +决策: + +- `allowed approver` 在发送通知 / Email 时确定,并作为 task 运行时记录的一部分保留下来。 +- 从产品语义上,审批主体以 `Contact` 为中心,而不是以单纯的 `email`、`session` 或裸 `account_id` 为中心。 +- 系统向每个 `Contact` 发送通知;该 `Contact` 在运行时对应一个被记录的 `allowed approver`。 +- 如果 `Contact` 关联了 `Dify Account`,则它属于 `Workspace Member Contact` 或 `Org-wide Member Contact`: + - 审批时需要登录 Dify; + - 或者在 IM 链路中,先用 IM user identity resolve 到对应的 `Dify Account`,再按该账号完成审批。 +- 如果 `Contact` 没有关联 `Dify Account`,则它属于 `External Contact`: + - 审批时只能通过独立表单页提交; + - 提交表单时必须使用 `Email OTP` 证明对该邮箱的控制权。 +- `IM` 与 `Email` 是 delivery channel,不改变审批主体本身;同一个 `Contact` 即使双渠道触达,也仍然只对应一个审批主体。 +- `Current initiator` 仍然是单独的 allowlist 来源;本决策只澄清“由通知发送产生的 allowed approver”的主体语义。 + +本期明确不做: + +- 不把 `allowed approver` 建模为“谁拿到链接谁都能批” +- 不把 `allowed approver` 简化为裸 `email` identity +- 不把 `IM` 与 `Email` 视为两个独立审批主体 + +影响说明: + +- 这条决策收敛了 `allowed approver` 的主体粒度:通知目标先落到 `Contact`,审批时再根据该 `Contact` 是否关联 `Dify Account` 决定鉴权链路。 +- 它会直接影响 recipient resolution、allowed approver 持久化、审计、去重以及提交鉴权。 +- PRD 中如果继续只写“任意一个 allowed approver 成功提交”而不解释 allowed approver 的记录时机和主体粒度,读者仍会误解成账号级或邮箱级概念。 + +待回写 PRD 的章节: + +- §8.5 Allow Current Initiator to Approve +- §9.4 提交规则 +- §10.1 HITL Task +- §16.2 需要记录的信息 +- §17.3 不同对象的鉴权方式 +- §18.6 Recipient canonicalization 与去重 + +--- + +## Decision 7: Task Accessibility Follows Existing HumanInputFormStatus + +决策日期:2026-07-09 + +适用范围: + +- `task 是否可访问` +- `task 是否可提交` +- Web App 独立页面打开与提交 +- §10.1 HITL Task +- §17.4 打开页面与提交表单的校验 + +决策: + +- 本期 `task 可访问状态` 直接复用现有 `HumanInputFormStatus` 语义,不再额外发明一套页面状态。 +- 只有 `WAITING` 状态的 form/task 允许继续打开和提交。 +- `SUBMITTED`、`TIMEOUT`、`EXPIRED` 均视为不可访问 / 不可提交状态。 +- 即使 form.status 仍为 `WAITING`,只要已经超过节点级 `expiration_time`,也应视为不可访问 / 不可提交。 +- 即使 form.status 仍为 `WAITING`,只要已经超过全局超时 deadline,也应视为不可访问 / 不可提交。 +- `task 不存在` 与 `task 不可访问` 是两类不同结果: + - 不存在:`not found` + - 已提交 / 已超时 / 已过期 / 已过 deadline:`expired/submitted` + +示意: + +```text +accessible = ( + status == WAITING + and now < expiration_time + and now < global_deadline + and not submitted +) +``` + +本期明确不做: + +- 不新增独立于 `HumanInputFormStatus` 之外的“可访问状态枚举” +- 不把 `canceled`、`delivery_failed` 直接当作现有页面访问状态使用 +- 不允许 `WAITING` 但已过节点/全局 deadline 的 form 继续打开或提交 + +影响说明: + +- 这条决策解决的是 `§17.4` 中“task 是否仍处于可访问状态”的落地语义。 +- 它不解决 `§10.1` 中更大的状态机冲突;`canceled`、`delivery_failed` 是否进入本期、以及如何映射到现有实现,仍然需要单独收敛。 +- 在现有代码里,这条规则已经存在于 `HumanInputService.ensure_form_active()` 和 `HumanInputFileUploadService._ensure_form_model_active()` 中,PRD 只需要回写一致。 + +待回写 PRD 的章节: + +- §10.1 HITL Task +- §17.4 打开页面与提交表单的校验 + +--- + +## Decision 8: Task State Machine Follows Existing HumanInputFormStatus + +决策日期:2026-07-09 + +适用范围: + +- HITL form/task 状态机 +- §10.1 HITL Task +- 节点级 timeout 与全局 expired 的分层 +- `canceled` / `delivery_failed` 在本期中的归属 + +决策: + +- 本期 form/task 状态机直接复用现有 `HumanInputFormStatus`,状态集合固定为: + - `WAITING` + - `SUBMITTED` + - `TIMEOUT` + - `EXPIRED` +- 允许的核心转移为: + - `WAITING -> SUBMITTED` + - `WAITING -> TIMEOUT` + - `WAITING -> EXPIRED` +- `TIMEOUT` 表示节点级超时: + - human input 节点沿 timeout 语义恢复; + - 在 agent ask-human 语义中映射为 `timeout` 结果。 +- `EXPIRED` 表示全局超时: + - workflow run 视为全局截止; + - 该 form 不允许继续 resume。 +- `SUBMITTED` 表示已有 recipient 成功提交;进入终态。 +- 本期不把 `canceled` 纳入现有 form 状态机。 +- 本期不把 `delivery_failed` 纳入现有 form 状态机。 +- 如果产品仍需要 `canceled` 或 `delivery_failed`,它们只能作为: + - workflow run / node outcome + - 或 delivery / error reason + 进行建模,而不是新增为当前 `HumanInputFormStatus`。 + +示意: + +```text +WAITING -> SUBMITTED +WAITING -> TIMEOUT +WAITING -> EXPIRED +``` + +本期明确不做: + +- 不新增 `CANCELED` form 状态 +- 不新增 `DELIVERY_FAILED` form 状态 +- 不保留一套与 `HumanInputFormStatus` 平行的 task/form 状态枚举 + +影响说明: + +- 这条决策把“form/task 的状态机”和“workflow run 的失败结果”拆开了。 +- 它解决的是 `§10.1` 中 form 状态与转移的落地语义。 +- 它不替产品决定是否需要 `canceled`、`delivery_failed` 这两个概念;只限定它们不应直接进入当前 form 状态机。 +- 现有代码已经体现这条规则: + - `mark_submitted()` 只会把 form 置为 `SUBMITTED` + - `mark_timeout()` 只允许 `TIMEOUT / EXPIRED` + - `ask_human_resume` 中 `WAITING` 重新 pause,`TIMEOUT` 映射 `timeout`,`EXPIRED` 不可 resume + +待回写 PRD 的章节: + +- §10.1 HITL Task +- §10.4 节点报错规则 +- §18.9 变更影响矩阵 diff --git a/hitl_phase2_linear_task_list.md b/hitl_phase2_linear_task_list.md new file mode 100644 index 00000000000..3748819e1af --- /dev/null +++ b/hitl_phase2_linear_task_list.md @@ -0,0 +1,156 @@ +# HITL Phase 2 Linear 任务清单 + +目标:2026-07-31 达到 PR ready to merge,不要求 SaaS 生产上线。 +来源排期:[hitl_phase2_schedule_plan.md](hitl_phase2_schedule_plan.md) +QA 明细:[hitl_phase2_qa_task_list.md](hitl_phase2_qa_task_list.md) + +## 使用口径 + +这个文件按“后续导入 Linear”的口径编写:每一行尽量对应一个 Linear issue。字段使用中文,但语义贴近 Linear:`任务编号`、`标题`、`团队`、`标签`、`优先级`、`状态`、`开始日期`、`截止日期`、`依赖`、`描述`、`验收标准`。 + +核心排期原则: + +1. 先在 2026-07-10 至 2026-07-13 冻结前后端契约、API schema、mock fixture 和 provider contract,前端不能等后端全部实现完才开始。 +2. 2026-07-13 起,后端实现领域模型和 runtime,前端基于契约和 mock 并行开发 Human Roster、Contact Directory、节点配置、审批页和 Last Run。 +3. 2026-07-15 起,provider 后端 adapter 和 provider 前端配置 / 状态 UI 并行推进。 +4. 2026-07-22 至 2026-07-26 作为前后端真实接口联调、provider UI 收敛和前端 RC handoff 窗口。 +5. Linear 里只保留一个大的 QA 任务;具体测试拆分维护在 [hitl_phase2_qa_task_list.md](hitl_phase2_qa_task_list.md),QA 同事从 2026-07-27 进场。 + +## 当前代码基线 + +| 能力 | 当前状态 | Phase 2 处理 | +|-|-|-| +| Human Input 节点 DSL | 已有 `human-input` 节点,包含 form content、inputs、actions、timeout、delivery_methods。 | 保持旧 published DSL 兼容;Phase 2 新 schema 需要明确 node version / adapter / rollback。 | +| 表单持久化 | 已有 `HumanInputForm`、`HumanInputDelivery`、`HumanInputFormRecipient`。 | 优先做 schema delta,不重复建设同义 task / delivery / recipient 表;如新增 task 表,必须给出桥接关系。 | +| WebApp / Console / Service API / OpenAPI 表单提交 | 已有 token 获取、提交、暂停恢复和 surface 隔离。 | 增加 OTP、actor 校验、原子提交、audit,不重做基础 endpoint。 | +| Email delivery | 已有 Email 配置、test send、Celery 发送、URL placeholder、feature gate。 | 增加 OTP、发送日志、投递状态、失败原因、限流和 secret redaction。 | +| 文件上传 | 已有表单 upload token 和文件归属校验。 | 纳入安全回归,不作为 Phase 2 主路径新增项。 | +| Provider | 后端没有 HITL provider adapter;前端 Slack / Teams 只是占位。 | Provider framework、provider adapter 和 provider 配置 UI 都是新增任务。 | +| Human Roster | 现有 Agent Roster 与 Phase 2 Human Roster 语义不同,Human tab 禁用。 | 新增 Human Roster / Contact Directory / IM Binding 模型、API 和 UI。 | + +## 里程碑 + +| 里程碑 | 日期 | 目标 | +|-|-|-| +| M1 范围冻结 | 2026-07-10 | 冻结节点策略、身份规则、OTP、RBAC、provider gate。 | +| M2 契约冻结 | 2026-07-13 | 冻结 schema、API contract、mock fixture、provider contract,解除前端开发阻塞。 | +| M3 Core alpha | 2026-07-18 | 后端 Email-only / Roster / resolver / OTP / 原子提交闭环,前端核心页面可基于 mock 跑通。 | +| M4 前端主路径可联调 | 2026-07-21 | Human Roster、Contact Directory、节点配置、审批页、Last Run 前端主路径完成。 | +| M5 Provider readiness | 2026-07-24 | Provider 后端和前端配置入口完成 gate 判定。 | +| M6 前端 RC handoff | 2026-07-26 | 前后端真实接口联调、provider UI 收敛、前端 smoke 和 QA handoff 包完成。 | +| M7 QA entry | 2026-07-27 | QA 接收可测环境、前端 RC build、provider sandbox 和测试矩阵。 | +| M8 Merge readiness | 2026-07-31 | 测试证据、文档、回滚、安全、CI、review 全部 ready。 | + +## Linear 任务 + +### M1 范围冻结 + +| 任务编号 | 标题 | 团队 | 标签 | 优先级 | 状态 | 开始日期 | 截止日期 | 依赖 | 描述 | 验收标准 | +|-|-|-|-|-|-|-|-|-|-|-| +| HITL-P2-001 | 冻结 Phase 2 节点兼容策略 | 后端、前端、工作流 | 决策、兼容性 | P0 | 待办 | 2026-07-10 | 2026-07-10 | - | 明确新逻辑节点、DSL version、导入导出、执行分流和回滚策略。 | 旧 `human-input` published workflow 不自动改写;旧运行路径可继续恢复;新 DSL 的执行入口明确。 | +| HITL-P2-002 | 冻结现有表模型复用边界 | 后端、数据库 | 决策、迁移 | P0 | 待办 | 2026-07-10 | 2026-07-10 | HITL-P2-001 | 确认 `human_input_forms`、`human_input_form_deliveries`、`human_input_form_recipients` 是否作为 Phase 2 基表继续扩展。 | 若新增 task 表,必须列出与现有 form / delivery / recipient 的映射和迁移桥接;不能出现两套状态源。 | +| HITL-P2-003 | 冻结身份与审批 actor 规则 | 后端、安全 | 决策、安全 | P0 | 待办 | 2026-07-10 | 2026-07-10 | HITL-P2-001 | 明确 WebApp、Service API、OpenAPI、Console、CLI、anonymous session、IM callback 的提交身份规则。 | `submitted_by`、allowed approver、anonymous session 边界和 current initiator 规则写入决策记录。 | +| HITL-P2-004 | 冻结 Email OTP 与 fallback 鉴权规则 | 后端、前端、安全 | 决策、安全、OTP | P0 | 待办 | 2026-07-10 | 2026-07-10 | HITL-P2-003 | 明确 Email / fallback URL 沿用现有 token 定位表单,提交时如何二次校验。 | External、one-time、dynamic Email 提交必须校验 OTP;成员或 IM 提交是否需要 OTP 有明确裁决。 | +| HITL-P2-005 | 冻结 Organization / RBAC 默认矩阵 | 后端、前端、安全、企业版、SaaS | 决策、RBAC | P0 | 待办 | 2026-07-10 | 2026-07-10 | - | 明确 EE / CE deployment 与 SaaS workspace 的 Organization 映射和默认权限矩阵。 | workspace admin 管 Human Roster;enterprise admin 管 EE Contact Directory 和 IM Integration;普通 member 权限边界明确。 | +| HITL-P2-006 | 冻结 provider 首发 gate 与降级策略 | Provider、安全、发布 | 决策、Provider | P0 | 待办 | 2026-07-10 | 2026-07-10 | HITL-P2-004、HITL-P2-005 | 定义 Slack、钉钉、飞书 / Lark、Teams、企业微信的首发准入和 fallback 策略。 | 每个 provider 的 sync、send、identity verify、callback、Web fallback、delivery status 最小门禁明确;企业微信允许通知 + Web fallback。 | + +### M2 契约冻结与前端解耦 + +| 任务编号 | 标题 | 团队 | 标签 | 优先级 | 状态 | 开始日期 | 截止日期 | 依赖 | 描述 | 验收标准 | +|-|-|-|-|-|-|-|-|-|-|-| +| HITL-P2-007 | 设计 Contact Directory / Human Roster / IM Binding 模型 | 后端、数据库 | 模型、Roster、IM | P0 | 待办 | 2026-07-10 | 2026-07-13 | HITL-P2-005 | 定义 workspace member、Dify account、external contact、Organization-scoped IM identity 的领域模型。 | 字段、唯一约束、索引、状态枚举、历史 snapshot 读取策略明确。 | +| HITL-P2-008 | 设计 HITL task schema delta | 后端、数据库、工作流 | 模型、迁移 | P0 | 待办 | 2026-07-10 | 2026-07-13 | HITL-P2-002、HITL-P2-007 | 明确现有 form/delivery/recipient 字段哪些复用、哪些新增。 | 覆盖 recipient snapshot、delivery status、resolution、audit、provider callback metadata。 | +| HITL-P2-009 | 定义 recipient resolver v2 契约 | 后端、前端、工作流 | API 契约、Resolver | P0 | 待办 | 2026-07-10 | 2026-07-13 | HITL-P2-003、HITL-P2-007、HITL-P2-008 | 定义前端保存的 recipient 配置 schema 和后端解析输出 schema。 | static contact、external contact、one-time Email、dynamic Email、current initiator、debug only notify me 的输入、输出、去重和错误语义明确。 | +| HITL-P2-010 | 定义 Email OTP API 契约 | 后端、前端、安全 | API 契约、OTP | P0 | 待办 | 2026-07-10 | 2026-07-13 | HITL-P2-004、HITL-P2-008 | 定义 OTP 发送、重发、校验、错误码、rate limit 和前端状态机。 | 前端可基于 mock 开发;错误码覆盖过期、次数超限、email 不匹配、form 已完成。 | +| HITL-P2-011 | 定义 delivery / audit / Last Run API 契约 | 后端、前端、工作流 | API 契约、Last Run、审计 | P0 | 待办 | 2026-07-10 | 2026-07-13 | HITL-P2-008 | 定义 Last Run 展示所需的数据结构和审计事件字段。 | resolved recipients、变量值、投递状态、失败原因、submitted_by、submitted_at、channel 字段明确。 | +| HITL-P2-012 | 定义 Provider adapter contract | 后端、Provider、前端 | API 契约、Provider | P0 | 待办 | 2026-07-10 | 2026-07-13 | HITL-P2-006、HITL-P2-007、HITL-P2-008 | 定义 credential、test connection、Organization sync、send message、identity verify、callback、fallback URL、secret redaction 的接口。 | 后端 adapter 和前端 provider 配置 UI 可并行开发;mock provider schema 明确。 | +| HITL-P2-013 | 输出前端 mock fixture 与接口 stub | 前端、后端 | 前端、Mock、API 契约 | P0 | 待办 | 2026-07-11 | 2026-07-13 | HITL-P2-009、HITL-P2-010、HITL-P2-011、HITL-P2-012 | 为前端提供 Human Roster、Contact Directory、recipient selector、OTP、Last Run、provider 配置的 mock 数据。 | 前端可以不等待真实后端完成即开始主路径开发;mock fixture 与 API 契约字段一致。 | +| HITL-P2-014 | 定义 migration dry run 与回滚方案 | 后端、数据库、发布 | 迁移、回滚 | P0 | 待办 | 2026-07-10 | 2026-07-13 | HITL-P2-008 | 明确 schema migration、backfill、dry run、rollback 和运行中 task snapshot 策略。 | 旧 workflow 不失效;运行中 form/task 使用创建时 snapshot;migration 可 dry run、可回滚、可重复执行。 | + +### M3 后端核心开发 + +| 任务编号 | 标题 | 团队 | 标签 | 优先级 | 状态 | 开始日期 | 截止日期 | 依赖 | 描述 | 验收标准 | +|-|-|-|-|-|-|-|-|-|-|-| +| HITL-P2-015 | 实现 Contact Directory / Human Roster / IM Binding migration | 后端、数据库 | 迁移、Roster、IM | P0 | 待办 | 2026-07-13 | 2026-07-16 | HITL-P2-007、HITL-P2-014 | 新增或扩展 Contact、Roster、IM Binding 相关表结构。 | 新表、索引、唯一约束和回滚脚本完成;Organization scope 不泄露跨 workspace / deployment 数据。 | +| HITL-P2-016 | 实现 Human Roster API | 后端、安全 | API、Roster、RBAC | P0 | 待办 | 2026-07-13 | 2026-07-17 | HITL-P2-015 | 实现 Human Roster 初始化、列表、搜索、external contact 管理和禁用状态过滤。 | workspace admin 可管理;普通 member 不可浏览完整 Roster;成员离开后不可新选。 | +| HITL-P2-017 | 实现 Contact Directory 与 IM Integration 管理 API | 后端、安全、Provider | API、IM、RBAC | P0 | 待办 | 2026-07-13 | 2026-07-17 | HITL-P2-015、HITL-P2-012 | 实现 provider credential、test connection、sync trigger、IM binding 状态查询 API。 | enterprise admin 可操作;secret 不出响应和日志;前端可接真实接口。 | +| HITL-P2-018 | 实现 recipient resolver v2 | 后端、工作流 | Resolver、运行时 | P0 | 待办 | 2026-07-13 | 2026-07-18 | HITL-P2-009、HITL-P2-016 | 实现各类 recipient 配置解析、去重、snapshot 和错误处理。 | dynamic Email 不反查 Account / Contact;current initiator 解析符合决策;输出 durable snapshot。 | +| HITL-P2-019 | 接入 Phase 2 节点执行分流 | 后端、工作流 | DSL、兼容性、运行时 | P0 | 待办 | 2026-07-14 | 2026-07-18 | HITL-P2-001、HITL-P2-008、HITL-P2-018 | 按 node version / schema 将新节点路由到 v2 resolver 和新 delivery pipeline。 | 旧 `human-input` 行为不变;新 DSL 走 v2 path;导入旧 DSL 可兼容。 | +| HITL-P2-020 | 实现 Email OTP 后端能力 | 后端、安全 | OTP、Email、安全 | P0 | 待办 | 2026-07-13 | 2026-07-18 | HITL-P2-010、HITL-P2-018 | 实现 OTP 生成、发送、重发、校验、过期和次数限制。 | External、one-time、dynamic Email 提交必须带有效 OTP;错误码稳定。 | +| HITL-P2-021 | 实现原子 first-writer-wins 提交 | 后端、数据库、安全 | 并发、提交 | P0 | 待办 | 2026-07-14 | 2026-07-18 | HITL-P2-008、HITL-P2-020 | 修复现有提交路径不是原子更新的问题。 | 同一 task/form 并发提交只有一个成功;失败方返回已完成状态;单测覆盖 race。 | +| HITL-P2-022 | 扩展 delivery record 状态机 | 后端、工作流、Provider | Delivery、状态机 | P0 | 待办 | 2026-07-14 | 2026-07-18 | HITL-P2-008、HITL-P2-012 | 记录 pending/sent/failed/submitted、provider message id、failure reason、sent_at、submitted_by、channel。 | 现有 Email delivery 写入状态;provider 可复用同一状态机。 | +| HITL-P2-023 | 实现 audit record 写入与脱敏 | 后端、安全 | 审计、脱敏 | P0 | 待办 | 2026-07-15 | 2026-07-18 | HITL-P2-011、HITL-P2-020、HITL-P2-021、HITL-P2-022 | 记录提交、拒绝、校验失败、投递失败等审计事件。 | 能回答谁在什么时候、通过什么身份、用什么 channel 提交或被拒绝;审计不记录完整敏感正文和 secret。 | +| HITL-P2-024 | 实现 Email 必发与最小发送 guardrails | 后端、SaaS、安全 | Email、限流、风控 | P0 | 待办 | 2026-07-15 | 2026-07-18 | HITL-P2-020、HITL-P2-022 | 实现 IM + Email 双发策略、dynamic Email 发送记录和最小限流。 | 有 IM binding 时 IM + Email 双发;无 IM binding 时 Email;发送失败原因可见。 | + +### M4 前端核心开发 + +| 任务编号 | 标题 | 团队 | 标签 | 优先级 | 状态 | 开始日期 | 截止日期 | 依赖 | 描述 | 验收标准 | +|-|-|-|-|-|-|-|-|-|-|-| +| HITL-P2-025 | 实现 Human Roster 前端页面 | 前端 | 前端、Roster、RBAC | P0 | 待办 | 2026-07-13 | 2026-07-19 | HITL-P2-013、HITL-P2-016 | 启用 Roster 的 Human tab,接入 mock 后再切真实 API。 | workspace admin 可管理 Human Roster;成员离开/禁用状态可见且不可新选;普通 member 受限。 | +| HITL-P2-026 | 实现 Contact Directory / IM Integration 前端页面 | 前端、企业版、Provider | 前端、IM、Provider | P0 | 待办 | 2026-07-13 | 2026-07-20 | HITL-P2-013、HITL-P2-017 | 实现 credential 表单、test connection、sync trigger、IM binding 状态和错误态。 | secret 输入不可回显;provider 不可用态可见;mock 和真实 API 字段兼容。 | +| HITL-P2-027 | 实现 HITL 节点 recipient selector v2 | 前端、工作流 | 前端、节点配置、Resolver | P0 | 待办 | 2026-07-13 | 2026-07-20 | HITL-P2-009、HITL-P2-013 | 重做节点收件人选择器,支持新 recipient schema。 | 支持 static contact、external contact、one-time Email、dynamic Email variable chip、current initiator、debug only notify me;保存和回显正确。 | +| HITL-P2-028 | 实现 Notification Policy 配置 UI | 前端、工作流、Provider | 前端、通知策略 | P0 | 待办 | 2026-07-14 | 2026-07-20 | HITL-P2-012、HITL-P2-013、HITL-P2-027 | 实现 Email 必发、IM + Email 双发、fallback 行为和 provider 不可用错误态配置。 | 旧 WebApp / Email 配置兼容显示;新 schema 可保存、回显、导入导出。 | +| HITL-P2-029 | 实现 Web approval OTP 前端 | 前端、安全 | 前端、OTP、审批页 | P0 | 待办 | 2026-07-13 | 2026-07-20 | HITL-P2-010、HITL-P2-013 | 在独立审批页实现 OTP 输入、重发、错误态和提交状态机。 | 支持过期、次数超限、已提交、无权限、提交中、提交成功;移动端可用。 | +| HITL-P2-030 | 实现 Last Run delivery / resolution 展示 | 前端、工作流 | 前端、Last Run | P1 | 待办 | 2026-07-14 | 2026-07-21 | HITL-P2-011、HITL-P2-013 | 在 Last Run 中展示 resolved recipients、投递状态、失败原因和提交人。 | 信息可读且不泄露敏感字段;空态和失败态明确。 | +| HITL-P2-031 | 实现前端 i18n、空态、错误态和基础可访问性 | 前端 | 前端、i18n、可访问性 | P1 | 待办 | 2026-07-17 | 2026-07-21 | HITL-P2-025、HITL-P2-026、HITL-P2-027、HITL-P2-029、HITL-P2-030 | 补齐新增页面和组件的文案、空态、错误态、loading、disabled 和键盘可用性。 | 用户可见文案走 `web/i18n/en-US/`;关键按钮、输入框、错误提示在桌面和移动端不遮挡。 | +| HITL-P2-032 | 前端核心路径 mock smoke | 前端 | 前端、Mock、Smoke | P1 | 待办 | 2026-07-19 | 2026-07-21 | HITL-P2-025、HITL-P2-026、HITL-P2-027、HITL-P2-028、HITL-P2-029、HITL-P2-030 | 使用 mock fixture 跑通主要前端路径,提前暴露 UI 和状态机问题。 | Human Roster、Contact Directory、节点配置、审批页、Last Run 均可在 mock 数据下完成 smoke。 | + +### M5 Provider 后端与前端并行开发 + +| 任务编号 | 标题 | 团队 | 标签 | 优先级 | 状态 | 开始日期 | 截止日期 | 依赖 | 描述 | 验收标准 | +|-|-|-|-|-|-|-|-|-|-|-| +| HITL-P2-033 | 实现 provider adapter runtime 与 mock provider | 后端、Provider | Provider、运行时 | P0 | 待办 | 2026-07-15 | 2026-07-19 | HITL-P2-012、HITL-P2-022、HITL-P2-024 | 实现 provider runtime 基类、mock provider 和 pipeline 单测。 | mock provider 覆盖 credential、sync、send、identity verify、callback、fallback。 | +| HITL-P2-034 | 实现 provider credential 与 callback 安全框架 | 后端、Provider、安全 | Provider、安全、Callback | P0 | 待办 | 2026-07-15 | 2026-07-20 | HITL-P2-017、HITL-P2-033 | 实现 credential 加密/脱敏、callback signature、state、replay 保护。 | 失败写入 delivery status 和 audit;secret 不进日志。 | +| HITL-P2-035 | 实现 Slack 后端 adapter | Provider、后端 | Slack、Provider | P0 | 待办 | 2026-07-16 | 2026-07-22 | HITL-P2-033、HITL-P2-034 | 实现 Slack EE 自建应用与 SaaS OAuth 的 sync、send、verify、callback。 | 两条部署形态 sandbox E2E 通过;delivery status 和 audit 完整。 | +| HITL-P2-036 | 实现 Slack 前端配置与状态 UI | 前端、Provider | Slack、前端、Provider | P1 | 待办 | 2026-07-18 | 2026-07-23 | HITL-P2-026、HITL-P2-028、HITL-P2-035 | 实现 Slack credential / OAuth 状态、test connection、sync 状态和 fallback 文案。 | Slack 配置入口可用;错误态可定位;未配置时节点给出清晰提示。 | +| HITL-P2-037 | 实现钉钉后端 adapter | Provider、后端 | 钉钉、Provider | P0 | 待办 | 2026-07-16 | 2026-07-22 | HITL-P2-033、HITL-P2-034 | 实现 EE 与 SaaS 企业自建应用的 sync、send、callback、identity mapping。 | SaaS 租户接入路径可跑通;Web fallback 和 delivery record 完整。 | +| HITL-P2-038 | 实现钉钉前端配置与状态 UI | 前端、Provider | 钉钉、前端、Provider | P1 | 待办 | 2026-07-18 | 2026-07-23 | HITL-P2-026、HITL-P2-028、HITL-P2-037 | 实现钉钉 credential、test connection、sync 状态、租户配置提示和 fallback 状态。 | 配置步骤与文档一致;错误态和权限不足提示明确。 | +| HITL-P2-039 | 实现飞书 / Lark 后端 adapter | Provider、后端 | 飞书、Lark、Provider | P0 | 待办 | 2026-07-16 | 2026-07-22 | HITL-P2-033、HITL-P2-034 | 实现 EE 自建应用 sync、卡片通知、身份校验、提交回调。 | sandbox E2E 通过;失败回调写入 delivery status。 | +| HITL-P2-040 | 实现飞书 / Lark 前端配置与状态 UI | 前端、Provider | 飞书、Lark、前端 | P1 | 待办 | 2026-07-18 | 2026-07-23 | HITL-P2-026、HITL-P2-028、HITL-P2-039 | 实现飞书 / Lark credential、test connection、sync 状态和 fallback 状态。 | 配置入口、错误态、同步结果展示可用。 | +| HITL-P2-041 | 实现 Microsoft Teams 后端 adapter | Provider、后端 | Teams、Provider | P0 | 待办 | 2026-07-17 | 2026-07-23 | HITL-P2-033、HITL-P2-034 | 实现 Teams App、Adaptive Card 或 fallback message、identity verify、submit callback。 | Teams sandbox E2E 通过;fallback URL 可安全提交。 | +| HITL-P2-042 | 实现 Microsoft Teams 前端配置与状态 UI | 前端、Provider | Teams、前端 | P1 | 待办 | 2026-07-19 | 2026-07-24 | HITL-P2-026、HITL-P2-028、HITL-P2-041 | 实现 Teams credential、test connection、sync 状态和 fallback 状态。 | Teams 配置入口可用;Adaptive Card / fallback 差异展示清楚。 | +| HITL-P2-043 | 实现企业微信后端 adapter | Provider、后端 | 企业微信、Provider | P0 | 待办 | 2026-07-17 | 2026-07-23 | HITL-P2-033、HITL-P2-034 | 实现企业微信 sync、通知、身份校验和 Web fallback。 | 通知 + Web 审批页 E2E 通过;卡片内完整审批不作为硬门禁。 | +| HITL-P2-044 | 实现企业微信前端配置与状态 UI | 前端、Provider | 企业微信、前端 | P1 | 待办 | 2026-07-19 | 2026-07-24 | HITL-P2-026、HITL-P2-028、HITL-P2-043 | 实现企业微信 credential、test connection、sync 状态、有限交互说明和 fallback 状态。 | UI 清楚表达企业微信能力边界;fallback 路径可见。 | +| HITL-P2-045 | Provider readiness gate | Provider、后端、前端、发布 | Provider、Gate、发布 | P0 | 待办 | 2026-07-24 | 2026-07-24 | HITL-P2-035、HITL-P2-036、HITL-P2-037、HITL-P2-038、HITL-P2-039、HITL-P2-040、HITL-P2-041、HITL-P2-042、HITL-P2-043、HITL-P2-044 | 对每个 provider 做 go / fallback / out of scope 判定。 | 未通过 gate 的 provider 从 7.31 ready-to-merge 范围移出或在 UI 中降级;结论同步给 QA 和文档。 | + +### M6 前后端联调与前端 RC handoff + +| 任务编号 | 标题 | 团队 | 标签 | 优先级 | 状态 | 开始日期 | 截止日期 | 依赖 | 描述 | 验收标准 | +|-|-|-|-|-|-|-|-|-|-|-| +| HITL-P2-046 | Human Roster / Contact Directory 前后端真实接口联调 | 前端、后端 | 联调、Roster、IM | P0 | 待办 | 2026-07-22 | 2026-07-25 | HITL-P2-016、HITL-P2-017、HITL-P2-025、HITL-P2-026 | 将前端 mock 切到真实 API,处理分页、权限、错误码和空态。 | Roster、Contact Directory、IM Integration 主路径在真实环境可用。 | +| HITL-P2-047 | HITL 节点配置与 runtime 前后端联调 | 前端、后端、工作流 | 联调、节点配置、运行时 | P0 | 待办 | 2026-07-22 | 2026-07-25 | HITL-P2-018、HITL-P2-019、HITL-P2-027、HITL-P2-028 | 将节点 recipient selector 和 Notification Policy 接入真实保存、发布、运行流程。 | 新 DSL 保存、回显、导入、发布、运行一致;旧 DSL 不回归。 | +| HITL-P2-048 | Web approval OTP 与提交链路联调 | 前端、后端、安全 | 联调、OTP、审批页 | P0 | 待办 | 2026-07-22 | 2026-07-25 | HITL-P2-020、HITL-P2-021、HITL-P2-029 | 将审批页 OTP、提交、重复提交、过期、错误态接入真实 API。 | Email / fallback 表单在真实环境可安全打开和提交;并发提交只有一个成功。 | +| HITL-P2-049 | Last Run / delivery / audit 联调 | 前端、后端、工作流 | 联调、Last Run、审计 | P1 | 待办 | 2026-07-23 | 2026-07-25 | HITL-P2-022、HITL-P2-023、HITL-P2-030 | 将 Last Run 展示接入真实 delivery / audit 数据。 | resolved recipients、投递状态、失败原因、submitted_by、submitted_at、channel 展示准确且脱敏。 | +| HITL-P2-050 | Provider 配置 UI 与真实 adapter 联调 | 前端、Provider、后端 | 联调、Provider | P0 | 待办 | 2026-07-24 | 2026-07-26 | HITL-P2-045 | 按 gate 结论收敛 provider 配置入口、不可用态、fallback 文案、图标、test connection 和 sync 状态。 | 通过 gate 的 provider UI 可用;未通过 gate 的 provider 隐藏或降级;QA 不会拿到半成品入口。 | +| HITL-P2-051 | 前端 RC 自测与质量门禁 | 前端 | 前端、Smoke、i18n | P0 | 待办 | 2026-07-25 | 2026-07-26 | HITL-P2-046、HITL-P2-047、HITL-P2-048、HITL-P2-049、HITL-P2-050 | 完成前端 smoke、移动端审批页检查、i18n key 检查、loading / empty / error / disabled 状态检查。 | 输出可测 build;`pnpm lint:fix` / `pnpm type-check` 或等价 CI 结果可追溯;关键页面截图或录屏齐备。 | +| HITL-P2-052 | 前端 QA handoff 包 | 前端、QA、发布 | QA、Handoff | P0 | 待办 | 2026-07-26 | 2026-07-26 | HITL-P2-051 | 汇总前端可测范围、feature flag、配置步骤、已知限制、smoke checklist 和关键路径说明。 | QA 可在 2026-07-27 直接接手;handoff 包包含 build、账号/权限要求、provider UI 状态和已知问题。 | + +### M7 QA 与发布准备 + +| 任务编号 | 标题 | 团队 | 标签 | 优先级 | 状态 | 开始日期 | 截止日期 | 依赖 | 描述 | 验收标准 | +|-|-|-|-|-|-|-|-|-|-|-| +| HITL-P2-053 | QA 进场与验收执行统筹 | QA、后端、前端、Provider、安全、发布 | QA、验收 | P0 | 待办 | 2026-07-27 | 2026-07-31 | HITL-P2-024、HITL-P2-045、HITL-P2-052 | QA 同事按 [hitl_phase2_qa_task_list.md](hitl_phase2_qa_task_list.md) 执行测试矩阵,P0/P1 缺陷进入每日 triage。 | 2026-07-28 前形成 RC1 结论;2026-07-31 前补齐测试证据包;P0/P1 为 0 或明确 no-go。 | +| HITL-P2-054 | 完成配置与运维文档 | 文档、Provider、前端、发布 | 文档、运维 | P1 | 待办 | 2026-07-29 | 2026-07-31 | HITL-P2-045、HITL-P2-052、HITL-P2-053 | 完成 provider 配置、SaaS 钉钉自建应用、callback URL、credential 权限、前端 feature flag / 已知限制、migration、回滚文档。 | 文档可由 QA 按步骤复现;已知限制和 fallback 范围清楚。 | +| HITL-P2-055 | 完成 PR 测试证据包 | QA、发布 | QA、发布 | P0 | 待办 | 2026-07-29 | 2026-07-31 | HITL-P2-053 | 汇总 CI、单测、集成测试、provider sandbox、migration dry run、安全测试结果。 | 证据集中到 PR 描述或附件;每个 ready-to-merge 范围内 provider 都有记录。 | +| HITL-P2-056 | 完成 merge readiness review | 发布、后端、前端、安全、Provider | 发布、Review | P0 | 待办 | 2026-07-31 | 2026-07-31 | HITL-P2-054、HITL-P2-055 | 做最终 ready-to-merge review,确认测试、文档、迁移、回滚、安全和配置均就绪。 | 无 P0/P1;CI 通过;回滚方案明确;未通过 gate 的 provider 已移出 scope 或降级。 | + +## Stretch / 非阻塞任务 + +| 任务编号 | 标题 | 团队 | 标签 | 优先级 | 状态 | 开始日期 | 截止日期 | 依赖 | 描述 | 验收标准 | +|-|-|-|-|-|-|-|-|-|-|-| +| HITL-P2-S1 | workspace 级 email service 配置 | 后端、前端、SaaS | Stretch、Email | P3 | 待办 | 2026-07-24 | 2026-07-31 | HITL-P2-024 | 支持 workspace 级 email service 配置,第一期只支持 Resend。 | 仅在 P0 安全和 provider gate 完成后推进;不阻塞 2026-07-31 merge readiness。 | +| HITL-P2-S2 | 企业微信卡片内完整审批 | Provider、前端、后端 | Stretch、企业微信 | P3 | 待办 | 2026-07-24 | 2026-07-31 | HITL-P2-043、HITL-P2-044 | 尝试支持企业微信卡片内完整审批。 | 不作为首发硬门禁;通知 + Web fallback 已通过即可进入 ready-to-merge scope。 | +| HITL-P2-S3 | Provider 自助诊断增强 | Provider、前端 | Stretch、诊断 | P3 | 待办 | 2026-07-24 | 2026-07-31 | HITL-P2-045 | 在 credential test 基础上增加权限缺失、callback URL、sync scope 的诊断提示。 | 不阻塞 merge;作为后续体验增强。 | + +## Go / No-go Gate + +| 日期 | Gate | Go 条件 | No-go 条件 | +|-|-|-|-| +| 2026-07-13 | 契约冻结 | schema、API contract、mock fixture、provider contract、migration 方案均冻结,前端可以并行开发。 | 前端仍缺 mock / API contract,或后端模型复用边界不清。 | +| 2026-07-18 | Core alpha | 后端 Email-only HITL、Roster、resolver、OTP、原子提交闭环;前端核心页面 mock 主路径跑通。 | 核心模型、OTP、first-writer-wins 或前端主路径仍不可演示。 | +| 2026-07-21 | 前端主路径可联调 | Human Roster、Contact Directory、节点配置、Web approval OTP、Last Run 前端主路径完成。 | 前端仍缺关键页面或状态机,无法接真实 API。 | +| 2026-07-24 | Provider readiness | 纳入首发的 provider 至少完成 sync、send、identity verify、submit/fallback,前端配置入口具备可联调形态。 | 任一 provider 无法同步、发送或校验提交身份,且没有可接受 fallback。 | +| 2026-07-26 | Frontend RC handoff | 前端完成真实接口联调、provider UI 收敛、自测、i18n / type-check 和 QA handoff 包。 | 前端仍缺可测 build、关键页面未联通、provider 降级态不明确、i18n / type-check 未过。 | +| 2026-07-27 | QA entry | QA 同事进场,测试矩阵、环境、账号、provider sandbox、前端 RC build、缺陷 triage 节奏就绪。 | 仍缺可测环境、前端 RC build、关键 provider sandbox、测试账号或 RC 范围。 | +| 2026-07-28 | RC1 | P0/P1 清零;migration dry run、RBAC、安全和核心集成测试通过。 | 存在数据丢失、越权提交、旧 workflow 失效、审计关键字段缺失风险。 | +| 2026-07-31 | Merge readiness | CI、code review、测试证据、迁移说明、配置文档、监控建议、回滚方案、最小限流和发送日志均就绪。 | provider 大面积失败、SaaS abuse guardrail 未生效、审计缺关键字段,或仍有未关闭 P0/P1。 | diff --git a/hitl_phase2_qa_task_list.md b/hitl_phase2_qa_task_list.md new file mode 100644 index 00000000000..ab342418b4c --- /dev/null +++ b/hitl_phase2_qa_task_list.md @@ -0,0 +1,118 @@ +# HITL Phase 2 QA 明细任务 + +目标:为 QA 同事提供执行明细;这些条目不逐条提交到 Linear。Linear 只保留 [hitl_phase2_linear_task_list.md](hitl_phase2_linear_task_list.md) 中的 `HITL-P2-053 — QA 进场与验收执行统筹` 一个大 QA 任务。 + +## QA 进场口径 + +| 项目 | 内容 | +|-|-| +| 进场日期 | 2026-07-27 | +| 目标 | 2026-07-28 给出 RC1 结论,2026-07-31 前完成 ready-to-merge 测试证据包。 | +| 输入物 | 可测环境、前端 RC build、前端 QA handoff 包、provider sandbox、测试账号、测试 workspace、migration dry run 环境、PR / branch、配置文档草稿。 | +| 输出物 | 测试矩阵执行结果、P0/P1 缺陷列表、provider gate 结论、migration dry run 记录、安全回归记录、最终测试证据包。 | + +## 进场前置条件 + +| # | 检查项 | 负责人 | 验收标准 | +|-|-|-|-| +| Q0.1 | 确认 RC scope | Product / Backend / Frontend / Provider | 明确哪些 provider 纳入 2026-07-31 ready-to-merge,哪些降级到 fallback,哪些移出范围。 | +| Q0.2 | 准备测试环境 | Backend / DevOps | 环境包含最新 migration、前端 RC build、Celery、mail、provider callback URL、feature flags 和基础 seed data。 | +| Q0.3 | 准备测试账号与权限 | Backend / QA | 至少覆盖普通 member、workspace admin、enterprise admin、Service API token、anonymous WebApp session。 | +| Q0.4 | 准备 provider sandbox | Provider owners | 每个纳入范围的 provider 都有 credential、通讯录测试数据、可回调 URL 和失败注入方式。 | +| Q0.5 | 准备缺陷 triage 节奏 | QA / Release | P0/P1 每日同步;blocker 有 owner、复现步骤、期望修复日期。 | +| Q0.6 | 准备前端 QA handoff 包 | Frontend | 2026-07-26 前提供可测 build、feature flag / 配置说明、前端 smoke checklist、已知限制、关键页面截图或录屏。 | + +## 测试执行矩阵 + +### 1. HITL 核心运行时 + +| # | 测试域 | 覆盖范围 | 验收标准 | +|-|-|-|-| +| Q1.1 | Email-only HITL | Email delivery、表单打开、OTP、提交、workflow resume、Last Run 展示。 | 一条 Email-only 工作流可端到端完成;提交后 downstream outputs 正确。 | +| Q1.2 | WebApp HITL | WebApp delivery、Service API / OpenAPI surface、token 权限隔离。 | 只有允许 surface 可以拿到可提交 token;内部 console token 不被公开接口提交。 | +| Q1.3 | Current initiator | WebApp、Service API、CLI / non-interactive initiator。 | current initiator snapshot 正确;anonymous WebApp session 只在原会话有效。 | +| Q1.4 | Dynamic Email | 变量解析、发送、OTP、审计、日志。 | 不反查 Account / Contact;审计记录变量值、发送结果和提交校验结果。 | +| Q1.5 | One-time Email | 节点配置、发送、OTP、提交。 | 不写入 Contact;重复提交只有一个成功。 | +| Q1.6 | Debug only notify me | Debug mode、收件人覆盖、生产配置不受影响。 | Debug 只通知当前操作者;published run 使用真实配置。 | +| Q1.7 | Form file inputs | 单文件、多文件、upload token、过期、租户归属。 | 文件上传只能绑定当前 form / recipient,跨租户文件不可提交。 | + +### 2. 安全与滥用防护 + +| # | 测试域 | 覆盖范围 | 验收标准 | +|-|-|-|-| +| Q2.1 | OTP required | External、one-time、dynamic Email、fallback URL。 | 无 OTP、错误 OTP、过期 OTP、email 不匹配均不能提交。 | +| Q2.2 | OTP retry limit | 重试次数、重发频率、过期后重发。 | 达到限制后返回稳定错误码,并写入 audit / rate limit 记录。 | +| Q2.3 | Link forwarding | 收件人 A 将链接转发给 B。 | B 无法在没有匹配 OTP / identity 的情况下提交。 | +| Q2.4 | Concurrent submit | 同一 task/form 多人或多请求同时提交。 | 只有一个 first-writer 成功,其余返回已完成状态。 | +| Q2.5 | Rate limit | 表单打开、表单提交、OTP、dynamic Email 发送。 | 超限返回明确错误;不会继续发送邮件或写入成功提交。 | +| Q2.6 | Secret redaction | Provider credential、Email body、IM body、日志和审计。 | secret 不进日志;消息正文不完整进入审计。 | +| Q2.7 | Cross-tenant isolation | Roster、Contact Directory、IM Binding、form token、upload token。 | 不同 workspace / Organization / deployment 间不可越权读写。 | + +### 3. Human Roster 与 Contact Directory + +| # | 测试域 | 覆盖范围 | 验收标准 | +|-|-|-|-| +| Q3.1 | Roster initialization | workspace member 初始化、新成员进入、离职成员状态。 | 可新选成员只包含有效成员;历史 snapshot 仍可读。 | +| Q3.2 | External contact | 创建、去重、禁用、搜索、权限。 | workspace admin 可管理;普通 member 不可浏览完整 Roster。 | +| Q3.3 | Contact Directory | Organization-scoped contact、IM identity、同步结果。 | enterprise admin 可查看;SaaS workspace 不串数据。 | +| Q3.4 | IM Binding | provider user id、email、Dify account/contact 映射。 | 绑定变更不影响运行中 task snapshot。 | + +### 4. Provider 端到端测试 + +| # | Provider | 覆盖范围 | 验收标准 | +|-|-|-|-| +| Q4.1 | Slack | EE 自建应用、SaaS OAuth、sync、send、identity verify、submit、Email 双发。 | 两条部署形态 E2E 通过,delivery status 和 audit 完整。 | +| Q4.2 | 钉钉 | EE / SaaS 企业自建应用、sync、send、callback、Web fallback。 | SaaS 租户接入文档可按步骤跑通。 | +| Q4.3 | 飞书 / Lark | EE 自建应用、sync、卡片通知、身份校验、提交。 | sandbox E2E 通过;失败回调写入 delivery status。 | +| Q4.4 | Microsoft Teams | Teams App、Adaptive Card 或 fallback、identity verify、submit。 | sandbox E2E 通过;fallback URL 可安全提交。 | +| Q4.5 | 企业微信 | sync、通知、身份校验、Web fallback。 | 通知 + Web 审批页 E2E 通过;卡片内完整审批不是硬门禁。 | +| Q4.6 | Provider failure | credential 错误、权限不足、rate limit、callback signature 错误。 | 失败原因可见;不会误标为 sent/submitted。 | + +### 5. 迁移与兼容性 + +| # | 测试域 | 覆盖范围 | 验收标准 | +|-|-|-|-| +| Q5.1 | Existing published workflow | 旧 `human-input` DSL、旧 Email recipient、旧 WebApp token。 | 不自动改写;已发布 workflow 仍可执行和恢复。 | +| Q5.2 | Running pause state | migration 前已有 waiting form/task。 | 使用创建时 snapshot;配置变更不影响运行中 task。 | +| Q5.3 | Migration dry run | schema migration、seed / backfill、rollback。 | dry run 可重复执行;回滚步骤可复现。 | +| Q5.4 | Import / export | workflow DSL 导入导出、版本分流。 | 旧 DSL 导入不丢字段;新 DSL 能正确落到 v2 path。 | + +### 6. 前端回归 + +| # | 测试域 | 覆盖范围 | 验收标准 | +|-|-|-|-| +| Q6.1 | HITL node config | recipient selector、dynamic Email chip、one-time Email、current initiator、debug only notify me。 | 保存、回显、复制、导入、发布后配置一致。 | +| Q6.2 | Human Roster UI | Human tab、权限、搜索、添加 external contact、状态展示。 | workspace admin 可操作;普通 member 受限。 | +| Q6.3 | Contact Directory / IM UI | credential、test connection、sync、binding 状态、错误态。 | secret 不回显;失败提示可定位。 | +| Q6.4 | Web approval page | OTP 输入、重发、过期、已提交、无权限、移动端。 | 状态文案清晰;不能出现表单内容遮挡或按钮不可点击。 | +| Q6.5 | Last Run view | delivery、resolution、submitted_by、failure reason。 | 信息可读且不泄露敏感字段。 | + +## 日期安排 + +| 日期 | QA 重点 | 输出物 | +|-|-|-| +| 2026-07-27 | 进场、环境核验、smoke、核心 HITL / OTP / Roster 首轮。 | 环境可测结论、P0 blocker、首轮 smoke 结果。 | +| 2026-07-28 | RC1:核心 runtime、安全、migration、前端主路径、provider gate 回归。 | RC1 go / no-go、P0/P1 清单、provider 纳入范围。 | +| 2026-07-29 | Provider sandbox E2E、安全与 abuse hardening 回归、migration dry run 复验。 | Provider E2E 记录、安全回归记录、migration 记录。 | +| 2026-07-30 | 缺陷修复回归、文档步骤验收、测试证据整理。 | 测试证据包草稿、剩余风险列表。 | +| 2026-07-31 | 最终验证、merge readiness review 支持。 | 最终 QA sign-off 或 no-go 理由。 | + +## 缺陷分级 + +| 等级 | 定义 | Merge gate | +|-|-|-| +| P0 | 数据丢失、越权提交、跨租户泄露、旧 workflow 大面积失效、provider callback 可被伪造。 | 必须关闭。 | +| P1 | 核心路径不可用、OTP / Email / provider 某一首发路径不可闭环、migration dry run 不可回滚。 | 必须关闭或从 scope 移出。 | +| P2 | 单个非首发 provider 体验问题、可绕过的 UI 缺陷、非关键日志缺字段。 | 可带已知限制进入 review。 | +| P3 | 文案、样式、诊断增强、非阻塞易用性问题。 | 不阻塞。 | + +## QA 退出标准 + +| 标准 | 所需证据 | +|-|-| +| 核心运行时验收 | Email-only、WebApp、current initiator、dynamic Email、one-time Email、debug only notify me、并发提交测试记录。 | +| 安全验收 | OTP、rate limit、cross-tenant、secret redaction、audit 测试记录。 | +| Provider 验收 | 纳入 scope 的每个 provider sandbox E2E 记录和失败场景记录。 | +| 迁移验收 | dry run、rollback、旧 DSL / running pause state 兼容记录。 | +| 前端验收 | 节点配置、Roster、Contact Directory、Web approval、Last Run 展示截图或录屏。 | +| 发布验收 | P0/P1 为 0;P2/P3 有 owner、风险说明和后续处理计划。 | diff --git a/hitl_phase2_schedule_plan.md b/hitl_phase2_schedule_plan.md new file mode 100644 index 00000000000..1f3aabe88aa --- /dev/null +++ b/hitl_phase2_schedule_plan.md @@ -0,0 +1,113 @@ +# HITL Phase 2 Schedule Plan + +来源 PRD: +读取版本:revision 1389 +生成日期:2026-07-09 +目标交付窗口:2026-07-31(PR ready to merge,不要求 SaaS 生产上线) + +## 排期结论 + +7 月底目标调整为代码合并准备就绪,可以按“通用 HITL/Roster/Email 能力先闭环,IM provider adapter 并行接入”的方式推进。关键约束是:2026-07-10 必须冻结 PRD 中仍未定稿的安全、身份、RBAC、provider 能力矩阵和新旧节点策略;2026-07-24 必须完成 provider readiness gate,否则 2026-07-31 只能将已通过 gate 的渠道纳入 ready-to-merge 范围。 + +## 首发范围 + +| 部署形态 | 首发 provider | 首发定义 | +|-|-|-| +| EE | Slack、飞书 / Lark、Microsoft Teams、钉钉、企业微信 | 管理后台可配置 provider credential;可发送 HITL 通知;可完成身份校验与提交;支持卡片审批的 provider 优先卡片内提交,不支持时走 Web 审批页 fallback。 | +| SaaS | Slack ISV / OAuth、钉钉企业自建应用 | Slack 走 OAuth 安装;钉钉由 tenant / workspace 配置企业自建应用凭据;均接入统一 HITL task、delivery record、审计和 Email 双发链路。 | + +本排期默认 CE 只复用通用 Human Roster、Email、HITL task 和迁移能力,不作为 provider 首发验收门禁。 + +## 核心假设 + +| 假设 | 说明 | +|-|-| +| Email 必发 | 与 PRD 保持一致:有 IM binding 时发送 IM + Email;无 IM binding 时发送 Email。 | +| EE 单部署选择一个 IM provider | PRD 中“企业版一期只支持配置一个 IM 渠道”按“每个 EE deployment 同时启用一个 provider”理解;首发清单表示支持的可选 provider 集合。 | +| IM identity 通过手动 IM 同步建立 | 首发需要支持管理员手动触发 IM 同步;同步 scope 是 Organization。EE / CE 中一个部署对应一个 Organization,SaaS 中一个 workspace 对应一个 Organization。 | +| Email recipient 提交时使用 Email OTP | Email 发送的 Web Form URL 沿用现有 token,不新增额外 token;External / one-time / dynamic Email 在提交表单时必须包含 Email OTP。 | +| 企业微信允许降级形态 | PRD 标注企业微信卡片能力有限,首发可按通知 + Web 审批页 fallback 验收,除非产品明确要求企业微信卡片内完整审批。 | + +## 已定决策与待确认项 + +| 决策项 | 状态 / 截止日期 | 影响范围 | 排期处理 | +|-|-|-|-| +| 新节点还是原 Human Input 原地改造 | 已确定 | DSL、迁移、回滚、前端入口、后端 runtime path | 使用新节点和新 DSL 定义;保留旧节点代码和逻辑不变;Workflow 执行时根据 DSL 中的 node version 选择实现。 | +| External / one-time / dynamic Email 鉴权方式 | 已确定 | Email 审批页、OTP、风控、审计 | 使用 Email OTP;现有 Email Web Form URL token 继续用于定位表单,提交表单时必须携带 OTP。 | +| Dynamic Email 是否反查 Account / Contact | 已确定 | recipient resolution、去重、审计、allowed approver | 不反查 Account / Contact,只发送邮件;审计记录变量值、校验结果和邮件投递结果。 | +| Current initiator identity schema | 已确定 | WebApp、Service API、CLI、匿名 session、去重、审计 | WebApp、Service API 和 CLI 都有身份,均可作为 current initiator;匿名 WebApp session 仅在原会话内有效。 | +| RBAC 权限矩阵 | 已确定 | Contact Directory、Human Roster、IM credential、IM binding | 默认 workspace admin 管理 Human Roster;enterprise admin 管理 EE Contact Directory 与 IM Integration。 | +| Provider 能力矩阵 | 已确定 | 卡片审批、fallback URL、IM 身份校验、通讯录同步 | 所有首发 provider 都支持通讯录同步;除企业微信外,其余 provider 基本可映射 Dify HITL 表单页。企业微信按有限交互或 Web fallback 验收。 | +| SaaS abuse guardrails | 已确定 | dynamic Email、发送量、OTP、租户限流、发送日志 | 首发做最小限流和日志记录;有时间则追加 workspace 级 email service 配置,第一期只支持 Resend。 | + +## 主排期计划表 + +| 阶段 | 日期 | 负责方向 | 工作内容 | 交付物 / 验收门禁 | +|-|-|-|-|-| +| 范围冻结 | 2026-07-10 | Product、Backend、Frontend、Security、SaaS | 冻结首发范围、节点策略、Email 鉴权、RBAC、provider 首发定义、provider 能力矩阵。 | PRD delta / decision log 完成;所有 blocker 有 owner 和默认裁决。 | +| 架构与 schema | 2026-07-10 至 2026-07-13 | Backend、Architecture | 确认 Contact、Human Roster、IM Binding、Recipient snapshot、HITL task、Delivery Record、Recipient Resolution Record、Audit Record 的表结构与服务边界。 | Migration 草案、领域对象映射、状态机和回滚策略通过评审。 | +| 契约与 mock handoff | 2026-07-10 至 2026-07-13 | Backend、Frontend、Provider owners | 输出 DSL / DTO / API shape / error code / feature flag / permission matrix / provider mock contract,使前端可不等待后端完整实现就开始开发。 | Human Roster、Contact Directory、IM Integration、recipient selector、OTP、Last Run、provider 状态的 mock response 和接口契约齐备。 | +| 后端核心开发 | 2026-07-13 至 2026-07-18 | Backend | 实现 Roster CRUD、external contact 校验、recipient resolver、dedupe、HITL task 持久化、delivery record、request message template、Email delivery、OTP 和原子提交。 | 单测覆盖 resolver / lifecycle / task transition / 并发提交;Email-only HITL 可端到端运行。 | +| 前端核心开发 | 2026-07-13 至 2026-07-21 | Frontend | 基于 contract / mock 并行实现 Human Roster、Contact Directory / IM Integration、HITL 节点 Notification Policy、recipient selector、dynamic Email chip、one-time Email、debug only notify me、Web approval OTP、Last Run delivery 展示。 | 前端核心页面和状态先以 mock 跑通;节点配置可保存、回显、调试;关键 i18n key、空态、错误态和权限态齐备。 | +| Web approval 与鉴权 | 2026-07-14 至 2026-07-18 | Backend、Frontend、Security | 实现独立审批页、打开页校验、提交二次校验、Email OTP、重复提交处理、错误态文案。 | 外部联系人 / one-time Email / dynamic Email 可安全打开和提交;转发链接不能在无 OTP 时越权提交。 | +| Provider framework | 2026-07-14 至 2026-07-19 | Backend、Provider owners | 抽象 provider adapter、credential model、Organization-scoped IM sync、callback / request URL、test connection、message payload、identity verification、fallback URL。 | Provider contract 冻结;mock provider 和手动同步流程测试通过;Secret 不进入日志。 | +| Provider 后端 adapter | 2026-07-15 至 2026-07-23 | Provider owners、Backend | 并行实现 Slack、钉钉、飞书 / Lark、Teams、企业微信后端接入与通讯录同步。 | 每个 provider 至少完成 credential 保存、Organization-scoped sync、test connection、发送通知、回调 / 提交、失败记录。 | +| Provider 前端配置与状态 UI | 2026-07-18 至 2026-07-24 | Frontend、Provider owners | 并行实现各 provider credential 配置入口、test connection、sync status、provider availability、fallback 文案和错误态。 | 通过 gate 的 provider UI 可用;未通过 gate 的 provider 可隐藏或降级;配置步骤与文档一致。 | +| 前后端 API 联调 | 2026-07-22 至 2026-07-25 | Frontend、Backend、Provider owners | 将前端 mock 替换为真实 API;接入真实 RBAC、feature flag、provider availability、test connection、sync status、OTP 和 Last Run 数据。 | Roster、Contact Directory、节点配置、OTP 页面、Last Run、provider 管理 UI 均可在联调环境跑通。 | +| 日志与审计 | 2026-07-18 至 2026-07-23 | Backend、Frontend | 实现 Last Run delivery 展示、recipient resolution 展示、失败原因、submitted_by、审计数据落库。 | 能回答“通知给谁、是否送达、谁提交、为何允许或拒绝”。 | +| 迁移与兼容 | 2026-07-20 至 2026-07-26 | Backend、Frontend | SaaS / CE workspace member 初始化到 Roster;旧 Email recipient 兼容读取;运行中 task snapshot 不受配置变更影响;输出 dry run 和回滚证据供 QA 进场使用。 | migration dry run 通过;旧 workflow 不失效;回滚路径明确;证据可交给 QA。 | +| 功能冻结 | 2026-07-24 | All | 停止新增功能,只接受 P0/P1 缺陷、安全问题和 provider 兼容修复。 | Provider readiness gate 完成;未通过 provider 从 7 月底 ready-to-merge 范围移出或降级到 fallback。 | +| 前端 RC 稳定与 QA handoff | 2026-07-25 至 2026-07-26 | Frontend、Backend、Provider owners | 收敛 provider 配置入口、fallback 状态、HITL 节点配置、Roster、Contact Directory、Web approval OTP、Last Run 展示;完成前端 smoke、i18n、type-check、已知限制和可测 build。 | 2026-07-26 前交付前端 RC build、feature flag / 配置说明、smoke checklist、关键页面截图或录屏,QA 可在 2026-07-27 接手。 | +| 集成 QA | 2026-07-27 至 2026-07-28 | QA、Backend、Frontend、Provider owners | 覆盖 Roster、Email、Web approval、各 provider、debug、migration、RBAC、异常状态、并发提交。 | RC1 缺陷清零到可接受阈值;所有 P0/P1 关闭。 | +| 安全与 abuse hardening | 2026-07-24 至 2026-07-29 | Security、SaaS、Backend | Email OTP 有效期、重试次数、提交校验、最小限流、发送日志、发送人数限制、dynamic Email 限制、secret redaction、审计敏感字段处理;有时间则补 workspace 级 Resend email service 配置。 | Security sign-off;SaaS guardrail 生效;敏感信息不出日志;Resend 配置不作为 7.31 merge 硬门禁。 | +| Merge readiness | 2026-07-29 至 2026-07-30 | Backend、Frontend、Docs、Product、Security | PR 描述、配置文档、provider 配置指南、迁移说明、回滚方案、监控告警建议、已知限制说明。 | Ready-to-merge review 通过;后续灰度、发布和回滚责任人明确。 | +| Final PR hardening | 2026-07-30 至 2026-07-31 | Backend、Frontend、QA、Provider owners | 处理最后一轮 code review、补齐测试证据、确认 CI、provider sandbox E2E、migration dry run 和安全准入。 | 2026-07-31 达到 PR ready to merge;SaaS 生产上线另行排期。 | + +## Provider 并行排期 + +| Provider | 部署形态 | 日期 | 必须完成 | Provider gate | +|-|-|-|-|-| +| Slack | EE 企业自建应用;SaaS ISV / OAuth | 2026-07-15 至 2026-07-21 | EE 自建应用 credential、SaaS OAuth 安装、Organization-scoped sync、发送 HITL 卡片、获取 Slack 用户身份、卡片提交、Email 双发、delivery record。 | 2026-07-24 前完成 EE 企业自建应用和 SaaS ISV / OAuth 两条 E2E,且同步结果可落到对应 Organization。 | +| 钉钉 | EE 企业自建应用;SaaS 企业自建应用 | 2026-07-15 至 2026-07-22 | EE 与 SaaS tenant / workspace 自建应用凭据配置、Organization-scoped sync、回调校验、发送卡片 / 消息、用户身份映射、Web fallback、delivery record。 | 2026-07-24 前完成 EE 企业自建应用和 SaaS 企业自建应用 E2E;SaaS 需补齐租户接入文档。 | +| 飞书 / Lark | EE 企业自建应用 | 2026-07-16 至 2026-07-22 | 企业自建应用 credential、Organization-scoped sync、回调 / 请求 URL、卡片通知、用户身份校验、提交鉴权、delivery record。 | 2026-07-24 前完成 EE 企业自建应用 E2E。 | +| Microsoft Teams | EE 企业自建应用 | 2026-07-16 至 2026-07-23 | 企业自建 Teams App credential、Organization-scoped sync、Adaptive Card / fallback message、身份校验、提交回调、delivery record。 | 2026-07-24 前完成 EE 企业自建应用 sandbox E2E。 | +| 企业微信 | EE 企业自建应用 | 2026-07-17 至 2026-07-23 | 企业自建应用 credential、Organization-scoped sync、消息发送、身份校验、有限交互或 Web fallback、delivery record、错误态。 | 2026-07-24 前完成 EE 企业自建应用通知 + Web 审批页 E2E;卡片内完整审批不作为硬门禁。 | + +## 验收清单 + +| 类别 | 验收标准 | +|-|-| +| Human Roster | workspace member 初始化、新成员自动进入、成员离开后不可新选、external contact 校验、Dify Account 不误建 external contact。 | +| HITL 节点 | 使用新节点和新 DSL;执行时根据 node version 路由到新旧实现;支持静态 contact、external contact、one-time Email、dynamic Email、current initiator、debug only notify me;配置可保存、导入、回显。 | +| 通知 | Email 必发;有 IM binding 时 IM + Email 双发;部分失败继续,全部不可触达按规则失败;失败原因可见。 | +| 审批与鉴权 | 打开页校验 task 状态;提交页再次校验 task 状态、approver 身份和 Email OTP;重复提交只有一个成功;链接转发不能在无 OTP 时越权提交。 | +| 日志与审计 | 记录 resolved recipients、delivery records、recipient resolution、submitted_by、submitted_at、提交渠道、拒绝 / 失败原因。 | +| Provider | 首发 provider 均通过 Organization-scoped sync、发送、身份校验、提交、失败记录、credential test、回调校验 E2E。 | +| 迁移与兼容 | 旧节点代码和逻辑保持不变;旧 Email recipient 不导致已发布 workflow 失效;运行中 task 使用创建时 snapshot;migration dry run 可回滚。 | +| 安全 | secret 不进日志;Email / IM 正文不完整入审计;Email OTP 有效期、重试次数、提交校验、最小限流和发送日志生效;敏感表单字段有脱敏策略。 | +| Stretch scope | 有时间则支持 workspace 级 email service 配置;第一期只支持 Resend;不作为 2026-07-31 ready-to-merge 硬门禁。 | + +## 风险与降级策略 + +| 风险 | 影响 | 截止点 | 降级策略 | +|-|-|-|-| +| PRD blocker 未在 2026-07-10 冻结 | 后端模型、DSL、鉴权和 RBAC 返工 | 2026-07-10 | 按本文默认裁决推进;延期未冻结项到后续版本。 | +| Provider 能力与预期不一致 | 单个 provider 不能完整映射 Dify HITL 表单页,或身份校验存在限制 | 2026-07-24 | 降级到 request message + Web 审批页;企业微信按有限交互或 Web fallback 验收;仍需通过身份校验。 | +| SaaS 钉钉企业自建应用配置成本高 | SaaS 首发接入和支持成本上升 | 2026-07-22 | ready-to-merge 范围可限定为试点租户路径;配置文档和自检工具先行。 | +| URL 鉴权实现复杂 | Email / fallback 审批安全风险 | 2026-07-18 | 不允许无 OTP 的 Email recipient 提交;未完成安全闭环则该 recipient 类型不进入 ready-to-merge 范围。 | +| RBAC 缺失 | Contact Directory / Roster 泄露跨 workspace 信息 | 2026-07-10 | 默认最小权限:普通 member 不可浏览完整 Roster;workspace admin 管理 workspace;enterprise admin 管理 deployment。 | +| Dynamic Email 滥用 | SaaS 邮件滥发和安全投诉 | 2026-07-12 | merge 前启用最小限流和发送日志;workspace 级 Resend email service 配置作为 stretch scope。 | +| 迁移影响旧 workflow | 线上 workflow 失效或回滚困难 | 2026-07-24 | 不自动改写已发布 DSL;兼容读取优先;旧节点保留运行路径。 | + +## Go / No-go 标准 + +| 日期 | Gate | Go 条件 | No-go 条件 | +|-|-|-|-| +| 2026-07-13 | 契约冻结 | schema、API contract、mock fixture、provider contract、migration 方案均冻结,前端可以并行开发。 | 前端仍缺 mock / API contract,或后端模型复用边界不清。 | +| 2026-07-18 | Core alpha | 后端 Email-only HITL、Roster、task、approval page、OTP 和原子提交端到端跑通;前端核心页面 mock 主路径跑通。 | 核心模型、Email 审批、OTP、first-writer-wins 或前端主路径仍无法闭环。 | +| 2026-07-21 | 前端主路径可联调 | Human Roster、Contact Directory、节点配置、Web approval OTP、Last Run 前端主路径完成。 | 前端仍缺关键页面或状态机,无法接真实 API。 | +| 2026-07-24 | Feature freeze | 所有首发 provider 至少完成 Organization-scoped sync 和 sandbox E2E;前端配置入口具备可联调形态;P0 安全项关闭。 | 任一 provider 无法同步、发送或校验提交身份,且没有可接受 fallback。 | +| 2026-07-26 | Frontend RC handoff | 前端完成 provider 接入收敛、HITL 节点配置、Roster、Contact Directory、OTP 页面、Last Run 展示和 QA handoff 包。 | 前端仍缺可测 build、关键页面未联通、provider 降级态不明确、i18n / type-check 未过。 | +| 2026-07-27 | QA entry | QA 同事进场,测试矩阵、环境、账号、provider sandbox、前端 RC build、缺陷 triage 节奏就绪。 | 仍缺可测环境、前端 RC build、关键 provider sandbox、测试账号或 RC 范围。 | +| 2026-07-28 | RC1 | P0/P1 清零;迁移 dry run 和回滚演练通过。 | 仍有数据丢失、越权提交、旧 workflow 失效风险。 | +| 2026-07-31 | Merge readiness | CI、code review、测试证据、迁移说明、配置文档、监控建议、回滚方案、最小限流和发送日志均就绪。 | provider 大面积失败、SaaS abuse guardrail 未生效、审计缺失关键字段,或仍有未关闭的 P0/P1。 | diff --git a/prd_requirements_review_hitl_phase2.md b/prd_requirements_review_hitl_phase2.md new file mode 100644 index 00000000000..103e4ecc50e --- /dev/null +++ b/prd_requirements_review_hitl_phase2.md @@ -0,0 +1,270 @@ +# HITL Phase 2 PRD Review + +来源: +本次复读基于 `revision_id = 1918`(读取时间:2026-07-10,Asia/Shanghai)。 + +## 直接结论 + +这版 PRD 相比上一版已经明显收敛,尤其是: + +- `Human Roster` 已统一重命名为 `Contact` +- `Workspace IM override` 已从非目标改为本期范围 +- `Dynamic Email`、`Current initiator`、`form/task` 状态层级、`Web 独立页面审批` 都补进了显式决策 +- `Magic link` 基本被收回,外部邮箱路径开始统一收敛到 `Email OTP` + +但仍然**不建议直接无保护进入实现**。主要原因已从“规则缺失”转为“规则虽有,但仍有若干跨章节冲突、未落成矩阵、以及边界不完整”。当前最值得继续盯住的点是: + +- `RBAC` 仍未完整定义 +- `Connection status` 虽有枚举,但没有生命周期 +- `Contact / Organization / Dify member` 的边界仍有灰区 +- `Web 鉴权链路` 已收敛,但不同审批主体的失败路径和回退策略还不完整 +- `abuse guardrails` 仍未量化 + +下面按你要求的 10 个维度更新。 + +## 1. 显式需求 + +- 系统已从 `Human Roster` 切换到 `Contact` 概念,且产品信息架构中明确将左侧模块拆为 `Agent` 与 `Contact` 两个独立模块。 + 引用:§2 “提供 Contact,作为 HITL 可通知联系人的来源。”;§5 “Agent 与 Contact 模块拆分”;§5.3 “Workspace:Contact” + +- `Contact` 的产品类型本期收敛为三类:`Dify member`、`Organization contact`、`External contact`。 + 引用:§3.1 “本期 Contact 类型收敛为三类:Dify member、Organization contact、External contact。” + +- `Organization` 在本期被定义为统一抽象:`CE / SaaS` 中当前 workspace 即 `Organization`,`EE` 中整个部署内所有 workspace 共同属于同一个 `Organization`。 + 引用:§3.3 “本期用 Organization 统一 CE / SaaS / EE 的产品抽象:CE / SaaS 中,当前 workspace 即 Organization;EE 中,整个部署内所有 workspace 共同属于同一个 Organization。” + +- 本期明确支持 `Workspace IM override`,但它只覆盖 workspace 内 contact 的 `IM identity / 通知行为`,不覆盖 IM Integration 的凭据。 + 引用:§2 非目标中 “~~workspace级别的 im identify override~~ Workspace IM override 本期进入范围。”;§6.5 “Workspace IM override 本期需要支持,但它不是 IM 平台接入凭据 override。” + +- `IM credential` 的产品作用域已明确为 `Organization` 级,而不是 workspace 级凭据 override。 + 引用:§18.4 “IM channel 接入配置中的 App ID、App Secret…统一归属 Organization。”;同节 “不再采用:~~Workspace / tenant-level credential override 优先于 deployment-level credential 的凭据优先级。~~” + +- `Connection status` 已被明确扩展为 `Not configured / Configured / Connected / Permission issue / Callback error / Connection error`。 + 引用:§6.2 “Connection status 需要从粗粒度 Error 拆成可排查的产品状态”;同节列出六个状态 + +- `Dynamic Email` 本期只支持单个合法 email,不支持数组变量批量输入;并且命中现有 `Contact` 后要升级为 `Contact recipient` 而不是继续当作裸邮箱。 + 引用:§8.3 “不再支持…通过数组变量一次传入多个 dynamic Email recipients。”;§8.3 “Dynamic Email 命中 Contact 后升级为 Contact recipient” + +- `email matching` 规则已被明确为:对完整 email 执行 lower-case 后做完全相等比较;本期不做 `+tag` 消解或点号折叠。 + 引用:§7.5 “先对完整 email 字符串执行 lower-case,再做完全相等比较。”;同节 “本期不做 provider-specific 规则:~~+tag 消解、点号折叠…~~” + +- 默认通知规则已明确为:具备 `IM binding + Email` 的 recipient 并行走 `IM + Email`;Email 默认不可关闭。 + 引用:§2 “默认通过 IM + Email 双渠道触达;Email 默认不可关闭。”;§9.1 “默认双渠道触达” + +- `Current initiator` 与 `allowed approver` 的关系已被明确为:`Current initiator` 是独立 allowlist 来源,但通知发送产生的审批主体仍以 `Contact` 为中心记录。 + 引用:§8.5 “Current initiator 与 allowed approver”;§9.4 “提交主体” + +- `Web 独立页面审批` 与 `IM 卡片审批` 已被拆成两条鉴权链路: + - `IM 卡片审批`:IM identity -> IM Binding -> Contact + - `Web 独立页面审批`:按审批主体走 Dify 登录或 Email OTP + 引用:§17.3 “IM 卡片审批与 Web 独立页审批是两条鉴权链路”;§17.5 “不同审批主体的 Web 鉴权” + +- `form/task` 状态机在产品文案上已明确只保留 `WAITING / SUBMITTED / TIMEOUT / EXPIRED`,`canceled / delivery_failed` 不进入 form status。 + 引用:§10.1 “form/task 状态层级”;同节 “本期 form status 只使用 WAITING、SUBMITTED、TIMEOUT、EXPIRED。canceled 不进入 form status;delivery_failed 不进入 form status。” + +- `导入导出 / DSL ID-Email 转换` 已被明确移出本期目标。 + 引用:§11.2 “导入导出不作为本期目标”;§15 “Milestone 4 范围调整:移出本期 ~~DSL 导入导出 ID / Email 转换~~” + +- `Success metrics / failure metrics` 已明确不在本期范围;`SaaS abuse guardrails` 单独成章,但仍待 SaaS 团队确认。 + 引用:§18.11 “Success metrics和Failure metrics / 暂不考虑”;§19 “Saas Abuse guardrails / 由saas团队确认” + +## 2. 隐藏假设 + +- [PRODUCT_DECISION] PRD 假设 `Organization` 作为统一抽象足以覆盖 `SaaS / CE / EE`,但这实际上隐含了“当前阶段不处理 SaaS/CE 未来多 workspace 语义”的假设。 + 引用:§3.3 “CE / SaaS 中,当前 workspace 即 Organization”;§18.3 “CE 默认只有一个 workspace” + +- [ENGINEERING_DECISION] PRD 假设“审批主体以 Contact 为中心”足以消解 IM / Email 双渠道重复通知问题,但这依赖 `Contact`、`IM Binding`、`normalized email` 的映射始终可逆。 + 引用:§8.5 “allowed approver 以 Contact 为中心记录”;§18.6 “Contact-centric canonicalization” + +- [PRODUCT_DECISION] PRD 假设 `Organization contact` 在 Web 独立页审批时可以可靠走 `Dify 登录`,这隐含了这些对象在实际产品里都拥有可用 Dify 身份的前提。 + 引用:§17.5 “Dify member / Organization contact:Web 独立页要求 Dify 登录” + +- [PRODUCT_DECISION] PRD 假设 `Email always-on` 对所有部署形态和客户约束都成立,但没有给出任何合规例外或部署级开关。 + 引用:§2 “Email 默认不可关闭。”;§9.1 “Email 默认不可关闭” + +- [ENGINEERING_DECISION] PRD 假设 `dynamic authorization` 能覆盖 pending task 的所有身份漂移问题,但没有写清与 snapshot 的边界如何在查询、展示、重发中共存。 + 引用:§18.9 “pending task 的提交资格采用动态授权”;同节 “历史 task 永远保留创建时快照” + +- [PRODUCT_DECISION] PRD 假设 `DingTalk self-managed integration` 虽然运维重,但仍是 SaaS 可接受路径;这实际上把大量接入负担转给租户管理员。 + 引用:§4.1 “每个 tenant / workspace 需要自行创建钉钉企业自建应用”;同节 “必须提供 Test connection” + +## 3. 模糊术语 + +- [BLOCKER] `Organization contact` 与 `Dify member`、`workspace member` 的边界还不够干净,尤其是在“离开 workspace 但仍在 organization 中”时。 + 引用:§3.1 “Organization contact 表示仍属于当前 Organization 联系人池,但不一定是当前 workspace member。”;§18.2 “若联系人已不在当前 workspace,仍在 dify 系统中,联系人类型自动转为 dify member” + +- [BLOCKER] `valid Contact` 仍是隐式术语。PRD 在动态授权链中大量使用它,但没有单独定义“valid”的判定条件集合。 + 引用:§17.4 “动态授权判定链:… current IM Binding -> valid Contact -> allowed approver -> allow / deny” + +- [ENGINEERING_DECISION] `Connection status` 现在有枚举,但“什么时候从 Configured 进入 Connected / Permission issue / Callback error / Connection error”仍是模糊的。 + 引用:§6.2 “Connection status 需要从粗粒度 Error 拆成…”;同节没有给出状态迁移条件 + +- [BLOCKER] `Contact list / 联系人列表 / Contact` 在文档里交替出现,集合概念与单条记录概念仍有轻微混用风险。 + 引用:§5 “后续文档中的联系人名录相关表述统一更新为 Contact;当需要强调集合含义时,使用 Contact list / 联系人列表。”;§3.2 标题 “Contact” + +- [PRODUCT_DECISION] `Only notify me during debug` 的“me”在多人协作、代理触发、无可通知渠道时的语义仍不够明确。 + 引用:§8.6 “实际通知对象替换为当前调试用户。”;全文未定义调试用户无有效渠道时的行为 + +## 4. 缺失的状态迁移 + +- [BLOCKER] `Connection status` 虽然有状态枚举,但缺少正式状态迁移图。 + 引用:§6.2 列出六种状态;未说明 `Save/Test connection/Disable` 如何驱动迁移 + +- [ENGINEERING_DECISION] `IM Binding` 仍缺生命周期:手工配置、同步建立、覆盖、删除、失效、恢复之间没有状态机。 + 引用:§6.3 “配置 IM identity”;§6.4 “IM 成员同步”;§18.2 “IM Binding 修改 / 删除” + +- [PRODUCT_DECISION] `unmatched list` 仍缺生命周期与可逆性定义。 + 引用:§6.4 “管理员可手动处理 unmatched 成员,例如忽略、稍后处理,或作为 external contact 添加。” + +- [BLOCKER] `URL / token lifecycle` 虽然列出了 `issued / opened / verified / submitted / expired / revoked` 的概念,但没有定义重发、刷新、再次打开、再次验证后的状态转移。 + 引用:§17.4 “URL / Token 生命周期” + +- [BLOCKER] `Workspace member 离开`、`转为 external contact`、`从 Contact 移除` 三条分支仍缺一张统一状态图。 + 引用:§18.2 “管理员可选择:转为 external contact / 从 Human Roster 移除”;同章仍混用 `Contact` 与旧术语 + +- [ENGINEERING_DECISION] `Workspace IM override` 现在已进入范围,但 create / update / reset / delete 对 pending task 和已发通知的影响只在 §18.9 的表格里部分定义,没有完整生命周期文案。 + 引用:§6.5 “Workspace IM override 本期进入范围”;§18.9 表格 “Workspace IM override 修改” + +## 5. 缺失的错误 / 边界场景 + +- [BLOCKER] `Current initiator` 在 API / CLI 场景下“没有明确身份”时仍未定义节点行为。 + 引用:§8.5 “CLI/API 发起时,若调用方有明确身份,可作为 allowed approver。”;未覆盖“无明确身份” + +- [BLOCKER] `Dynamic Email` 只明确不支持数组,但没有明确多值字符串、逗号分隔、空白值、重复值的前端与运行时拒绝策略。 + 引用:§8.3 “不再支持…数组变量一次传入多个 dynamic Email recipients”;§10.3 “Dynamic Email 解析异常类型” + +- [BLOCKER] `dynamic Email 命中多个 Contact` 或命中跨 workspace 同邮箱主体时的唯一归属规则没有单独写明。 + 引用:§8.3 “命中 Contact 后升级为 Contact recipient”;§18.6 “优先匹配已存在联系人” + +- [BLOCKER] `Dify member / Organization contact` 若不能完成 Dify 登录,是否允许退化为 Email OTP,PRD 当前明确否定统一 OTP,但没有定义失败后是否还有补救路径。 + 引用:§17.5 “Dify member / Organization contact:Web 独立页要求 Dify 登录”;§17.5 同节未定义登录失败后替代路径 + +- [PRODUCT_DECISION] `企业微信` 被标为“不支持卡片内审批,只能做选择”,但复杂表单、文件表单、超出 action 数量限制时的用户体验路径仍未定义。 + 引用:§18.5 企业微信行 “不支持 / 只能做选择(对应 Dify 表单的 action,并且 action 数量有限制)” + +- [BLOCKER] `No one can approve this step yet` 已出现在节点预览示意,但真正的保存校验、运行时错误、可发布性规则还没写全。 + 引用:§8.7 节点预览示意图;§10.4 “无可通知对象时报错” + +- [PRODUCT_DECISION] `文件访问权限` 已增加原则,但没有具体说明文件对象本身的授权失败提示、日志记录和重试策略。 + 引用:§9.3 “文件访问必须同时校验 task、访问者身份、allowed approver 和文件所属 task。” + +## 6. 安全与权限问题 + +- [PRODUCT_DECISION] 完整 `RBAC` 矩阵已不再作为本期 blocker,但细粒度权限依然未定:谁能查看完整 Contact、谁能看 raw dynamic value、谁能管理 external contact,仍需后续确定。 + 引用:§18.10 “不再作为当前 PRD blocker:~~在本期完整定义所有后台操作权限矩阵。~~”;同节 “普通 member 是否能查看完整 Contact…后续结合 RBAC 再定” + +- [BLOCKER] 外部邮箱审批已基本收敛到 `Email OTP`,但 `OTP` 的速率限制、次数限制、锁定规则、重发策略仍未定义。 + 引用:§17.5 “Email OTP 规则”;§19 “Saas Abuse guardrails / 由saas团队确认” + +- [BLOCKER] `raw dynamic Email`、`form snapshot`、`submission content` 的可见范围与脱敏边界仍未收敛。 + 引用:§16.4 “敏感信息边界待定”;同节 “后续需要结合安全与研发方案单独确认” + +- [BLOCKER] 审计可见性虽然开始收敛到 `enterprise admin / workspace admin / workflow 编排者`,但不同主体可见字段范围没有定义。 + 引用:§16.2 “审计可见性”;§16.2 “审计主体” + +- [BLOCKER] 文件访问安全原则已写出,但对象级授权模型仍未定义,例如外部联系人是否能看其被要求审批的文件预览。 + 引用:§9.3 “文件访问必须同时校验 task、访问者身份、allowed approver 和文件所属 task。” + +- [PRODUCT_DECISION] `Email always-on` 在受监管场景、私有化场景是否允许例外,仍无权限/合规策略。 + 引用:§2 “Email 默认不可关闭。”;全文未给出例外 + +## 7. 平台能力风险 + +- [BLOCKER] `IM 能力矩阵` 仍未填完,尤其 `通讯录同步` 仍为空,意味着产品已经决定依赖它,但没有明确哪些 provider 真支持。 + 引用:§18.5 矩阵中 `通讯录同步` 列为空 + +- [ENGINEERING_DECISION] PRD 虽然把 `fallback URL` 从“身份 fallback”中剥离了,但仍在模板映射和产品规则中保留该术语,容易继续混淆“通知入口”和“鉴权链路”。 + 引用:§18.5 “不能渲染卡片时,发送 request message + fallback URL。”;§18.8 表格列 “IM fallback” + +- [BLOCKER] SaaS 钉钉仍是高运维负担路径,产品虽然补了约束,但没有给出失败后的支持边界、可观测性或 self-serve 预期。 + 引用:§4.1 “必须提供 Test connection”;同节仍要求租户自备 app id / secret + +- [BLOCKER] `Dify 登录` 被用作 `Organization contact` 的独立页鉴权前提,但对“跨 workspace 的内部人是否默认都有 Dify 登录路径”没有产品确认。 + 引用:§17.5 “Dify member / Organization contact:Web 独立页要求 Dify 登录” + +- [PRODUCT_DECISION] `企业微信` 能力限制已知,但没有补充对应的 PM 降级策略,例如强制转网页审批、禁止复杂表单、还是只支持 approve/reject。 + 引用:§18.5 企业微信行说明 + +## 8. 应明确写出的 Non-goals + +- [PRODUCT_DECISION] `Magic link` 不再是本期外部邮箱审批方案。 + 引用:§9.3 “~~Magic link。~~ / Email OTP。”;§17.5 “不再采用:~~External contact…Magic link 或 Email OTP。~~” + +- [PRODUCT_DECISION] `导入导出 / DSL ID-Email 转换` 不属于本期目标。 + 引用:§11.2 “导入导出不作为本期目标”;§15 “Milestone 4 范围调整” + +- [PRODUCT_DECISION] 不支持多个 IM 绑定、通知优先级、个人通知偏好、复杂审批规则、群聊通知。 + 引用:§2 非目标对应条目 + +- [PRODUCT_DECISION] `Success metrics / failure metrics` 不属于本期目标。 + 引用:§18.11 “暂不考虑” + +- [PRODUCT_DECISION] 完整后台操作权限矩阵不作为本期 PRD blocker。 + 引用:§18.10 “不再作为当前 PRD blocker:~~在本期完整定义所有后台操作权限矩阵。~~” + +- [PRODUCT_DECISION] 不支持把 `dynamic Email` 解析为 Contact entity / member id / group id。 + 引用:§2 非目标 “动态 recipient 解析为 Contact entity / member id / group id。” + +- [PRODUCT_DECISION] 不支持把数组变量一次传入多个 `dynamic Email recipients`。 + 引用:§8.3 “不再支持…数组变量一次传入多个 dynamic Email recipients。” + +## 9. Blocking questions for product + +- [BLOCKER] `Organization contact` 与 `Dify member` 的产品边界最终如何面向用户解释?它们是展示层类型、鉴权层类型,还是数据层类型? + 引用:§3.1 Contact 类型;§17.5 “Dify member / Organization contact:Web 独立页要求 Dify 登录” + +- [BLOCKER] `Current initiator` 在 API / CLI 无明确身份时,节点究竟是禁止运行、禁止审批,还是降级为“无 initiator approver”? + 引用:§8.5 “若调用方有明确身份,可作为 allowed approver。” + +- [BLOCKER] `Organization contact` 在 Web 独立页上若无法完成 Dify 登录,是否允许任何补救路径? + 引用:§17.5 “Organization contact:Web 独立页要求 Dify 登录” + +- [BLOCKER] `Contact 管理权限` 的最小可交付口径是什么?当前只说“拥有管理 Contact 权限的人可以访问 Contact”,但没有更细。 + 引用:§18.10 “拥有该权限的人可以访问 Contact,并可搜索跨 workspace 的 Dify member。” + +- [BLOCKER] `OTP` 的限流、失效时间、错误提示、重试次数、锁定策略是什么? + 引用:§17.5 “Email OTP 规则”;§19 “Saas Abuse guardrails” + +- [BLOCKER] `Connection status` 中 `Permission issue / Callback error / Connection error` 的产品判定条件是什么? + 引用:§6.2 “Connection status 状态机” + +- [BLOCKER] `通讯录同步` 哪些 provider 本期真正支持?如果不支持,UI 上是隐藏还是禁用? + 引用:§6.4 “如果当前 IM 能力支持通讯录读取,可提供同步能力。”;§18.5 `通讯录同步` 列为空 + +- [BLOCKER] `Contact remove / convert / disable` 三条路径对 pending task 的统一 UX 是什么?目前文案分散在生命周期和变更矩阵中。 + 引用:§18.2 联系人生命周期;§18.9 变更影响矩阵 + +- [BLOCKER] `企业微信` 的 action 数量限制是否会导致功能范围收缩?需要明确哪些表单或操作不支持。 + 引用:§18.5 企业微信行 + +## 10. Engineering decisions that should not be left to implementation agents + +- [ENGINEERING_DECISION] `Contact-centric canonicalization` 的 canonical key、优先级、日志归并和 UI 呈现必须统一设计。 + 引用:§18.6 “多来源命中同一人的归并展示”;§18.6 “Contact-centric canonicalization” + +- [ENGINEERING_DECISION] `form status / workflow outcome / delivery reason` 三层状态模型必须明确拆层,不能在实现时临场决定。 + 引用:§10.1 “form/task 状态层级” + +- [ENGINEERING_DECISION] `Organization-level credential` 与 `Workspace IM override` 的数据模型和运行时优先级必须先定稿。 + 引用:§6.5 “override 只用于当前 workspace 内联系人 IM 身份 / 通知行为的覆盖”;§18.4 “IM 凭据作用域” + +- [ENGINEERING_DECISION] `Connection status` 到 provider 错误、权限错误、回调错误的映射规则必须统一。 + 引用:§6.2 “Connection status 状态机” + +- [ENGINEERING_DECISION] `Dynamic authorization chain` 必须以统一校验链实现,而不是让每个入口各自拼接。 + 引用:§17.4 “动态授权判定链” + +- [ENGINEERING_DECISION] `URL / token lifecycle` 的一次性使用、重放保护、失效和重发策略必须单独设计。 + 引用:§17.4 “URL / Token 生命周期” + +- [ENGINEERING_DECISION] `OTP` token 方案、rate limit、错误码、审计字段不能留给实现层自行发明。 + 引用:§17.5 “Email OTP 规则”;§16.4 “敏感信息边界待定” + +- [ENGINEERING_DECISION] `审计 schema`、可见字段、脱敏边界、PII 存储策略必须统一设计。 + 引用:§16.2 “审计可见性 / 审计主体”;§16.4 “敏感信息边界待定” + +- [ENGINEERING_DECISION] `文件访问授权` 需要明确对象级校验模型,不应只靠 task token + allowed approver 做隐式实现。 + 引用:§9.3 “文件访问必须同时校验 task、访问者身份、allowed approver 和文件所属 task。” + +- [ENGINEERING_DECISION] `迁移` 与 `旧 DSL 兼容读取` 规则必须统一,而不是散落在节点实现和 workflow 兼容层。 + 引用:§5.0 “旧 workflow DSL 需要保持兼容”;§11.3 “旧 Email recipient 迁移匹配”