35 changed files with 949 additions and 6940 deletions

No files matched your search

+84 -62
View File
@@ -1,83 +1,105 @@
# 钉钉进度通知参考 # Hub 钉钉通知接入
这里的脚本和配置只演示 Hub 如何向项目群发送辅助通知,不属于 Etunel 的任务通信主链路,也不能代替任务派发、负责人确认、阶段门禁或项目留档。成员 Agent 不调用本脚本。 这套封装只帮助 Hub 向项目群发送进度、异常摘要和负责人提醒,不是 Etunel 任务通道,也不负责审批、阶段门禁或项目留档。仅更新此仓库不会部署到实际项目;由部署负责人明确安装,成员 Agent 不调用。
当前布局刻意把工具和项目配置分开: ## 文件与依赖
- `dingtalk-workspace-cli@1.0.60` 由 Bun 安装在用户级全局目录,项目不保存二进制或 `node_modules`。 - [dingtalk_notify.py](../scripts/dingtalk_notify.py):共用 Python 实现,含配置、消息、人员解析和有限重试。
- 官方 multi skills 保留在项目 `.agents/skills/dingtalk-*`,只对本项目生效。 - [dingtalk-progress](../scripts/dingtalk-progress)/[dingtalk-progress.ps1](../scripts/dingtalk-progress.ps1):项目事件入口。
- Client ID、群目标、`robotCode` 和汇报开关保存在项目 `.dingtalk/config.env`。 - [dingtalk-bot-message](../scripts/dingtalk-bot-message)/[dingtalk-bot-message.ps1](../scripts/dingtalk-bot-message.ps1):自定义 Markdown 与负责人提醒入口。
- Client Secret 从环境变量 `DING_SEC` 读取,未设置时回退到 macOS Keychain;脚本只临时注入给 `dws api`,App Token 由 DWS 自动获取和缓存。 - [离线测试](../scripts/test_dingtalk_notify.py):假配置、假凭据及模拟命令,不发送真实消息。
项目统一通过稳定入口调用全局命令: 依赖 Python 3.9+,不需要第三方 Python 包、jq 或 Bash 才能在 Windows 运行。PowerShell 入口要求 7.3+;也可直接运行 Python。macOS/Linux 保留 Shell 入口。
```sh 真实发送需要 [官方 DingTalk Workspace CLI](https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli) 的原生可执行文件。按官方安装说明选择系统版本;Windows 使用 `dws.exe`,不要把 npm/Bun 的 `dws.cmd` 或 `dws.bat` 当原生程序。可以把原生文件加入 Hub 进程的 PATH,或在项目配置的 `DINGTALK_PROGRESS_DWS_PATH` 填它的绝对路径。本仓库不保存二进制,不自动安装、登录或更新全局 Skills。
./scripts/dws version
```
Bun 默认阻止上游 `postinstall`;本机安装时已校验官方 SHA-256 并只释放 DWS 原生二进制,没有执行会发布全局 Skills 的安装脚本。升级 DWS 时也应保持这一边界。 部署后检查 `python --version`(macOS/Linux 可用 `python3`)及原生 `dws version`。所用 DWS 需支持 `api --data -` 从标准输入读 JSON;正文、群参数不放命令行,密钥仅临时注入子进程环境。更换 DWS 版本后由部署负责人核对兼容性,离线预览不证明真实接口可用。
## 应用身份(无需用户登录) ## 部署到 Hub 项目
本项目不执行 `dws auth login`。进度脚本通过 `dws api` 调用企业内部应用 OpenAPI: 将共用 Python 文件和需要的平台入口一起放入目标项目的 `scripts/`,不要只复制一个入口。重试逻辑已合入共用实现,无需复制源项目的 `dingtalk-dns-retry.sh`。保持下列布局:
```text ```text
POST https://api.dingtalk.com/v1.0/robot/groupMessages/send 目标项目/
├── scripts/
│ ├── dingtalk_notify.py
│ ├── dingtalk-progress
│ ├── dingtalk-bot-message
│ ├── dingtalk-progress.ps1
│ └── dingtalk-bot-message.ps1
└── .dingtalk/
└── config.env
``` ```
管理员侧只需确保: 由部署负责人从 [配置示例](config.env.example) 新建实际配置,别覆盖已有有效配置:
- Client ID/Secret 属于企业内部应用; - UTF-8,可带 BOM,支持 Windows CRLF 和 LF;值按字面解析,不执行 Shell、不展开变量,不加 Shell 引号。
- 应用已有“企业内机器人发送消息”权限(`qyapi_robot_sendmsg`); - 初始保持 `ENABLED=0`、`DRY_RUN=1`(实际字段均有 `DINGTALK_PROGRESS_` 前缀)。
- `robotCode` 属于同一个应用; - 配置包括应用、机器人、目标群、项目名称、开关及机器人密钥。机器人必须属于配置的应用,已经加入目标群,并具备企业内机器人发送消息权限(`qyapi_robot_sendmsg`);脚本不自动加群或申请权限。
- 机器人已加入目标 `openConversationId` 对应的群。 - macOS/Linux 对实际配置执行 `chmod 600 .dingtalk/config.env`;接受 400 或 600。Windows 不以 Unix 权限位作门槛,由部署负责人用 NTFS 权限限制其他用户读取或修改配置。
- 实际配置必须被目标项目的 Git 忽略。不要把它、真实凭据或内部标识回传给 AI 会话或提交仓库。
当前使用 zsh 环境变量时,先确保它已导出(不要把真实值写进项目文件): 路径与当前工作目录无关:配置总从 Python 脚本所在项目根目录读取。消息的 `--file` 路径则相对当前工作目录解析。
## 凭据:所有系统统一读取配置
普通进度发送使用内部应用身份,不需要用户 OAuth 登录。Windows、macOS、Linux **只从本地 `.dingtalk/config.env` 的 `DINGTALK_PROGRESS_CLIENT_SECRET` 读取机器人密钥**,不再从 `DING_SEC`、其他环境变量或 macOS 钥匙串回退取值。
由部署负责人在本地编辑器中填写该字段,保持单行字面值,不加 Shell 引号;不要在会话、命令历史或仓库示例里填写真实值。密钥为空或字段缺失时,真实发送停止并提示负责人补配置;离线预览不要求填写密钥。
脚本仅在发送时把该值临时注入 DWS 子进程的 `DWS_CLIENT_SECRET`,同时注入配置中的 `DWS_CLIENT_ID`。这只是脚本内部对接 DWS 的方式,不需要负责人再配置环境变量。密钥不进入命令参数、消息正文、预览或错误输出;人员搜索进程也不会取得机器人密钥。
实际 `config.env` 现在包含明文密钥:macOS/Linux 设为 400 或 600;Windows 通过文件“属性 → 安全”限制为 Hub 运行账号及必要管理员可访问,不放在公共共享目录。目标项目必须忽略实际配置及其备份,只提交空值示例。不要把配置文件用作 `--file` 正文、成果或通知附件。
### 从旧配置迁移
1. 更新共用 Python 脚本及需要的平台入口。
2. 由部署负责人在本地配置补填 `DINGTALK_PROGRESS_CLIENT_SECRET`;删除旧的 `DINGTALK_PROGRESS_KEYCHAIN_SERVICE` 字段。
3. 检查文件权限与 Git 忽略,再做离线预览;正式发送仍需要启用开关、正确的应用权限和目标群。
4. 旧环境变量与钥匙串不再被脚本读取;本次改动不会删除其中原有内容。
脚本每次调用重新读取本地配置,不再因为密钥配置依赖 Hub 继承某个终端的环境变量;Python、dws 路径及按姓名查询所需的 DWS 登录仍须对实际运行 Hub 的操作系统账号可用。
## 可选的按姓名提醒
`--at` 会调用 DWS 人员搜索,和应用机器人发送是不同的认证需求。只有使用按姓名提醒时,部署负责人需要:
1. 在 DWS 中登录需要使用的组织账号;
2. 将已确认的稳定 profile 填入 `DINGTALK_PROGRESS_PROFILE`;
3. 确认该账号、机器人应用、目标群属于同一组织,且账号允许查询人员。
脚本在搜索和发送中传入同一个 profile,不自动选择或切换账号;应用归属与群归属仍需部署负责人核对,脚本不能仅凭 profile 字符串证明二者一致。查询只接受完整姓名的唯一匹配;查不到、重名、权限不足或返回结构不支持时整条提醒停止,不默认选择第一个,也不静默删掉 @ 后发送。
`--at-id` 和 `--group` 保留源脚本兼容能力,仅用于已有真实映射或用户明确指定的目标;不让 AI 猜编号或在群之间试发。普通进度通知不需要配置人员账号。
## 先做离线预览
在目标项目根目录选一种入口。以下命令只预览,不联网,不需要 dws,不查通讯录、不使用密钥认证。已有配置会在本地读取,但密钥不会输出:
```powershell
python scripts/dingtalk_notify.py progress milestone '本阶段成果已确认并归档,下一步安排联调。' --dry-run
./scripts/dingtalk-bot-message.ps1 --title '需要补充接口说明' --text '请接口负责人补齐返回值说明,并回自己的角色会话同步结果。' --dry-run
```
macOS/Linux 等价命令:
```sh ```sh
export DING_SEC='<Client Secret>' sh scripts/dingtalk-progress milestone '本阶段成果已确认并归档,下一步安排联调。' --dry-run
sh scripts/dingtalk-bot-message --title '需要补充接口说明' --text '请接口负责人补齐返回值说明,并回自己的角色会话同步结果。' --dry-run
``` ```
也可以不设置 `DING_SEC`,改用本项目专用的 macOS Keychain 项。`-w` 必须放在最后,它会在终端中隐藏输入: 两种入口都遵守配置的 `DRY_RUN=1`,显式 `--dry-run` 不能被配置取消。即使没有配置,也能显式预览正文;此时不表示项目配置可用。正文支持中文多行 Markdown;复杂正文建议先保存 UTF-8 文件,用 `--file` 代替内联摘要或 `--text`,它不是附件上传。
```sh 确认部署与发送授权后,负责人再将 `ENABLED=1` 且 `DRY_RUN=0`。仓库维护测试不能用真实发送作冒烟测试。实际 Hub 的事件判断、参数示例和结果含义见 [通知规则](../etunel-role-collaboration/references/dingtalk-progress-reporting.md) 和 [脚本用法](../etunel-role-collaboration/references/dingtalk-script-usage.md)。
/usr/bin/security add-generic-password \
-U \ ## 失败处理与测试
-a '<Client ID>' \
-s 'com.example.dingtalk.your-project' \ 仅明确的 DNS 解析失败每隔 10 秒重试,总计最多 3 次;已获有效回执即停止。认证、权限、未知错误不重试;超时或缺少回执不盲目重发。单次 DWS 调用超时 60 秒;本次结束后由 Hub 说明,不在外层循环重试。只输出安全摘要和发送回执,不输出原始请求或错误日志。
-l 'Your project DingTalk bot Client Secret' \
-w 本封装不建消息队列、计划任务、重发历史或通知状态库;避免重复事件仍由 Hub 利用已有项目留档判断,不重造 Etunel 软件机制。
```text
python -X utf8 scripts/test_dingtalk_notify.py
``` ```
`dws auth status` 显示未登录是正常状态,与 App Token 发送链路无关。 测试仅在临时目录使用假配置、假密钥和模拟 DWS;同时检查当前系统可用的 PowerShell/Shell 入口,不访问真实项目配置。覆盖配置密钥为唯一来源、密钥不回显、不通过命令参数传递以及预览不认证,不等于完成真实钉钉发送验收。
## 配置进度群
```sh
cp .dingtalk/config.env.example .dingtalk/config.env
chmod 600 .dingtalk/config.env
```
填写以下非密钥参数:
```dotenv
DINGTALK_PROGRESS_MODE=app-bot
DINGTALK_PROGRESS_CLIENT_ID=<Client ID>
DINGTALK_PROGRESS_TARGET=<openConversationId>
DINGTALK_PROGRESS_ROBOT_CODE=<robotCode>
```
先保持 `DINGTALK_PROGRESS_ENABLED=0`。凭据和参数就绪后改为 `1`,并保持 `DINGTALK_PROGRESS_DRY_RUN=1` 做无网络、无发送的请求预览:
```sh
./scripts/dingtalk-progress milestone "**已完成**
- 项目资料已经归档
- 本阶段成果已经负责人确认
**下一步**
- Hub 将安排下一阶段任务"
```
确认请求中的目标和正文正确后,再把 `DINGTALK_PROGRESS_DRY_RUN=0`。真实发送由 Hub 按根目录 `AGENTS.md` 的时机调用;响应中的 `processQueryKey` 才表示服务端已经接受该条机器人消息。摘要可以使用多行 Markdown,重点写已经确认的结果、下一步或阻塞原因,不要粘贴大段日志。
+13 -7
View File
@@ -1,19 +1,25 @@
# Copy this file to .dingtalk/config.env, fill the selected mode, then chmod 600 it. # 由部署负责人复制为 .dingtalk/config.env;macOS/Linux 设置 chmod 600。
# Values are literal text after '='; do not add shell quotes. # Windows 用当前 Hub 运行用户的文件权限,不套用 Unix 权限检查。
# UTF-8(可带 BOM),支持 LF/CRLF;等号右侧是字面值,不加 Shell 引号,不执行变量。
# Keep both switches safe until credential setup and a dry-run have succeeded. # 两个入口共用开关;显式 --dry-run 也永远不发送。
DINGTALK_PROGRESS_ENABLED=0 DINGTALK_PROGRESS_ENABLED=0
DINGTALK_PROGRESS_DRY_RUN=1 DINGTALK_PROGRESS_DRY_RUN=1
# Enterprise-internal app bot: App Token via dws api; no user OAuth login. # 企业内部应用机器人:发送本身不需要用户 OAuth 登录。
DINGTALK_PROGRESS_MODE=app-bot DINGTALK_PROGRESS_MODE=app-bot
DINGTALK_PROGRESS_CLIENT_ID= DINGTALK_PROGRESS_CLIENT_ID=
# 仅在本地 config.env 填真实 Client Secret;本示例必须保持空值,绝不提交真实配置。
DINGTALK_PROGRESS_CLIENT_SECRET=
DINGTALK_PROGRESS_TARGET= DINGTALK_PROGRESS_TARGET=
DINGTALK_PROGRESS_ROBOT_CODE= DINGTALK_PROGRESS_ROBOT_CODE=
DINGTALK_PROGRESS_KEYCHAIN_SERVICE=com.example.dingtalk.your-project
# Client Secret is never stored here. The script first reads exported DING_SEC, # 所有系统统一从 config.env 取密钥,不再读取 DING_SEC 或 macOS 钥匙串。
# then falls back to the project-specific macOS Keychain item above. # 可选:原生 dws 的绝对路径(Windows 填 dws.exe,不填 cmd/bat 包装器)。
DINGTALK_PROGRESS_DWS_PATH=
# 可选:按姓名 @ 时必需。由部署负责人确认与应用、目标群同组织的已登录 profile。
# 普通进度通知可留空;不是 Etunel 人员或会话编号。
DINGTALK_PROGRESS_PROFILE=
DINGTALK_PROGRESS_PROJECT=你的项目名称 DINGTALK_PROGRESS_PROJECT=你的项目名称
DINGTALK_PROGRESS_TITLE_PREFIX=[项目进度] DINGTALK_PROGRESS_TITLE_PREFIX=[项目进度]
+6 -1
View File
@@ -1,5 +1,8 @@
* text=auto * text=auto
.gitattributes text eol=lf
.gitignore text eol=lf
.dingtalk/config.env.example text eol=lf
.dingtalk/config.env
*.md text eol=lf *.md text eol=lf
*.txt text eol=lf *.txt text eol=lf
*.html text eol=lf *.html text eol=lf
@@ -8,4 +11,6 @@
*.json text eol=lf *.json text eol=lf
*.py text eol=lf *.py text eol=lf
*.sh text eol=lf *.sh text eol=lf
*.ps1 text eol=lf
scripts/dingtalk-progress text eol=lf scripts/dingtalk-progress text eol=lf
scripts/dingtalk-bot-message text eol=lf
+2
View File
@@ -1,5 +1,7 @@
# Local credentials and machine-specific configuration # Local credentials and machine-specific configuration
.dingtalk/config.env .dingtalk/config.env
.dingtalk/config.env.*
!.dingtalk/config.env.example
**/.env **/.env
**/.env.* **/.env.*
!**/.env.example !**/.env.example
+21 -10
View File
@@ -6,10 +6,10 @@
- `etunel-role-collaboration/`:供各角色按需读取的渐进式披露 Skill; - `etunel-role-collaboration/`:供各角色按需读取的渐进式披露 Skill;
- `etunel-role-hook-contexts/`:供 Etunel 独立配置的八角色精简 Hook 上下文; - `etunel-role-hook-contexts/`:供 Etunel 独立配置的八角色精简 Hook 上下文;
- `doc/`:流程设计基线和历史资料; - `doc/`:当前流程设计基线和模板参考资料;
- 钉钉参考集成:仅帮助 Hub 理解如何调用辅助通知,不属于 Etunel 任务通信主链路。 - 钉钉参考集成:仅帮助 Hub 理解如何调用辅助通知,不属于 Etunel 任务通信主链路。
这是以 Markdown、HTML 和 Shell 为主的约束仓库,没有需要猜测的安装或构建步骤。 这是以 Markdown、HTML 为主、附带 Python 和平台启动脚本的约束仓库,没有需要猜测的应用构建步骤。
## Scope ## Scope
@@ -19,11 +19,11 @@
1. 先读根 Skill:[etunel-role-collaboration/SKILL.md](etunel-role-collaboration/SKILL.md)。 1. 先读根 Skill:[etunel-role-collaboration/SKILL.md](etunel-role-collaboration/SKILL.md)。
2. 只按其中的条件路由读取本次修改涉及的 reference,不要一次加载所有角色和测试细则。 2. 只按其中的条件路由读取本次修改涉及的 reference,不要一次加载所有角色和测试细则。
3. 当前本地定版文档为 [V1.0 总流程图](doc/20260908/Codex多角色项目推进总流程图-V1.0.html)(CURRENT,文档生效日期 2026-09-08),基于用户已确认的 V0.16 全文生成,不新增角色或流程规则。V0.15、V0.16 HTML 保留原文件;本地文档定版不表示已发布 Git、部署 Skill/Hook 或替换在途项目基线。未经用户再次通知,不提交、不推送、不合入主分支,也不自动更新已安装的插件缓存。旧版流程用于追溯;已在运行的项目继续使用其登记基线,迁移须明确确认。 3. 当前设计基线为 [V1.0 总流程图](doc/Codex多角色项目推进总流程图.html)(文档生效日期 2026-09-08)。`doc/` 只维护当前流程文件和配套参考,不再按日期或版本复制文档;后续直接更新同一文件,历史通过 Git 提交追溯。仓库更新不等于部署 Skill/Hook 或迁移在途项目;已运行项目继续使用其登记基线,迁移及插件缓存更新须明确确认。
4. 运行时 Hook、Etunel 工具 schema、项目角色契约、成员登记及已确认的项目流程基线,高于 Skill 的通用说明。发现冲突时说明冲突,不要自行猜测或发明能力。 4. 运行时 Hook、Etunel 工具 schema、项目角色契约、成员登记及已确认的项目流程基线,高于 Skill 的通用说明。发现冲突时说明冲突,不要自行猜测或发明能力。
5. 保留用户已有改动;修改前后检查 Git 状态与差异。 5. 保留用户已有改动;修改前后检查 Git 状态与差异。
不要让运行时 Skill 依赖某个带版本号的流程图或提示词文件。应把已确认且仍有效的规则整理为稳定约束,并放入正确的渐进式 reference;版本文件只作为设计来源和追溯依据。 不要让运行时 Skill 依赖某个带版本号的流程图或提示词文件。应把已确认且仍有效的规则整理为稳定约束,并放入正确的渐进式 reference;流程文档只作为设计来源,历史以 Git 提交追溯。
## 必须保持的协作边界 ## 必须保持的协作边界
@@ -47,9 +47,9 @@
| `etunel-role-collaboration/references/roles/` | 七个成员角色的职责契约 | 角色专属决定、输入、输出和边界写在对应文件 | | `etunel-role-collaboration/references/roles/` | 七个成员角色的职责契约 | 角色专属决定、输入、输出和边界写在对应文件 |
| `etunel-role-collaboration/references/testing/` | 测试角色二级细则 | 只保留匹配任务时才需要的深层内容 | | `etunel-role-collaboration/references/testing/` | 测试角色二级细则 | 只保留匹配任务时才需要的深层内容 |
| `etunel-role-hook-contexts/` | 八角色 Hook 的完整精简职责与每轮提醒 | 独立于 Skill 发现树;简短、可直接注入、与正式职责一致 | | `etunel-role-hook-contexts/` | 八角色 Hook 的完整精简职责与每轮提醒 | 独立于 Skill 发现树;简短、可直接注入、与正式职责一致 |
| `doc/` | 已确认流程来源及历史版本 | 不把历史版本误写成当前规则;新增基线时明确其状态 | | `doc/` | 当前流程来源及模板参考 | 固定路径维护一份当前文档;不建日期或版本副本,历史查 Git;变更同步核对链接与基线说明 |
| `.agents/skills/dingtalk-*` | 钉钉官方能力的项目内参考副本 | 不是 Etunel 核心 Skill;除非任务明确要求更新参考包,否则不要批量改动 | | `.agents/skills/dingtalk-*` | 钉钉官方能力的项目内参考副本 | 不是 Etunel 核心 Skill;除非任务明确要求更新参考包,否则不要批量改动 |
| `.dingtalk/`、`scripts/dingtalk-progress` | Hub 通知接入说明、示例配置和当前演示封装 | 不把通知能力扩大为任务派发、审批、门禁或事实源 | | `.dingtalk/`、`scripts/` 中的钉钉封装 | Hub 通知配置说明、跨平台入口、共用 Python 实现和离线测试 | 不把通知能力扩大为任务派发、审批、门禁或事实源 |
修改共享流程、角色边界、阶段、正式成果或跨角色信息流时,必须同步检查根 Skill、相关公共 reference、受影响角色 reference 和对应 Hook。角色细节优先下沉到角色文件;测试专项优先下沉到测试二级 reference。不要靠复制整段规则维持一致性。 修改共享流程、角色边界、阶段、正式成果或跨角色信息流时,必须同步检查根 Skill、相关公共 reference、受影响角色 reference 和对应 Hook。角色细节优先下沉到角色文件;测试专项优先下沉到测试二级 reference。不要靠复制整段规则维持一致性。
@@ -61,10 +61,10 @@
- 只有 Hub/主 Agent 可以调用通知;成员 Agent 只向 Hub 返回成果、状态或阻塞。 - 只有 Hub/主 Agent 可以调用通知;成员 Agent 只向 Hub 返回成果、状态或阻塞。
- 通知是项目群的单向辅助可见性与异常提醒,不是角色间通信、任务派发、审批、人类确认、项目门禁或项目事实源。 - 通知是项目群的单向辅助可见性与异常提醒,不是角色间通信、任务派发、审批、人类确认、项目门禁或项目事实源。
- Agent 只能使用 `scripts/dingtalk-progress`,事件类型限定为 `start`、`milestone`、`blocked`、`complete` 或 `failed`;不得绕过脚本直接调用 OpenAPI 或 `dws api`。可验证里程碑先完成项目留档,再发送简短中文 Markdown。例如: - Agent 只用 `scripts/dingtalk_notify.py` 的 `progress`/`message`,或等价的 `dingtalk-progress`/`dingtalk-bot-message` Shell、PowerShell 入口。进度事件仍限定为 `start`、`milestone`、`blocked`、`complete`、`failed`;负责人提醒不新增状态。同一事项不重复通知,不得绕过封装直接调用 OpenAPI 或 `dws api`。可验证里程碑先完成项目留档,再发送简短中文 Markdown。例如:
```sh ```sh
./scripts/dingtalk-progress milestone "**已完成** python scripts/dingtalk_notify.py progress milestone "**已完成**
- 阶段成果已确认并归档 - 阶段成果已确认并归档
@@ -73,7 +73,7 @@
- Hub 安排后续任务" - Hub 安排后续任务"
``` ```
- 事件语义与消息格式以 [dingtalk-progress-reporting.md](etunel-role-collaboration/references/dingtalk-progress-reporting.md) 为准;安装与本机配置参考 [.dingtalk/README.md](.dingtalk/README.md)。 - 事件语义与消息格式以 [dingtalk-progress-reporting.md](etunel-role-collaboration/references/dingtalk-progress-reporting.md) 为准;安装与本机配置参考 [.dingtalk/README.md](.dingtalk/README.md)。
- `scripts/dingtalk-progress` 是当前用于跑通通知链路的演示封装,不得据此反推或改变 Etunel 主流程。 - 通知封装是辅助接入参考,不得据此反推或改变 Etunel 主流程;不创建通知队列或外层重试。仅明确 DNS 失败最多 3 次、间隔 10 秒,结果未知不盲目重发。
- 仓库维护和测试期间不得把真实发送当作冒烟测试。只有任务明确要求发送且当前环境已经授权时,才允许触发真实通知。 - 仓库维护和测试期间不得把真实发送当作冒烟测试。只有任务明确要求发送且当前环境已经授权时,才允许触发真实通知。
- 不读取、输出、修改或提交 `.dingtalk/config.env`、Client Secret、`DING_SEC`、App Token 或其他凭据和内部标识。 - 不读取、输出、修改或提交 `.dingtalk/config.env`、Client Secret、`DING_SEC`、App Token 或其他凭据和内部标识。
@@ -87,13 +87,24 @@
- 不把模拟流程结果描述为真实发布、客户验收或生产就绪;不适用项对人统一写“不适用”,只在机器字段要求时使用对应枚举。 - 不把模拟流程结果描述为真实发布、客户验收或生产就绪;不适用项对人统一写“不适用”,只在机器字段要求时使用对应枚举。
- 文本文件保持 LF;不要提交日志、缓存、临时文件、机器专属配置或凭据。 - 文本文件保持 LF;不要提交日志、缓存、临时文件、机器专属配置或凭据。
## 版本与发布
- 全仓库统一使用三位版本 `vX.Y.Z`:前两位 `X.Y` 跟随 `doc/Codex多角色项目推进总流程图.html` 内明确标注且已确认的流程版本;第三位 `Z` 是该流程版本下的仓库发布迭代号。Git 标签与发布包确定该版 Skill、Hook、脚本和文档,组件不另设发布序列。
- HTML 主流程版本不迭代时,所有变更均只递增第三位,包括 Skill/Hook 优化、脚本新增、修复、配置调整和文档更新;不按功能规模或兼容性自行提升前两位。流程保持 V1.0 时依次发布 `v1.0.0`、`v1.0.1`、`v1.0.2`。HTML 流程正式升到新的 `X.Y` 时,仓库前两位同步,第三位从 0 开始,例如流程 V1.1 对应 `v1.1.0`;不为凑发布号改流程图。
- 这是流程版本驱动的项目约定,不采用“新增功能就升次版本”的语义化版本规则;第三位更新也可能需要配置迁移,发布说明必须如实列出。涉及职责或门禁变化仍须先按项目约束确认流程,不能借版本编号规则跳过确认。
- 日常提交不必发版。发版前确认版本号、完成适用校验,说明流程基线、变更内容、需更新的 Skill/Hook 和在途项目影响;具体说明见 [README 版本与升级](README.md#版本与升级)。
- 仅在用户要求时提交、推送或发版;已发布标签不可覆盖或移动,修正另发新版本。推送后核验远程分支与标签提交,不能把本地提交当成已发布。
- 发布不等于部署。仅更新受影响的 Skill/Hook 配置,插件缓存更新及在途项目迁移仍须明确确认;不要求角色每轮核对发布号,不将仓库发版规则塞进运行时 Skill。
## Quick Commands ## Quick Commands
修改后按影响范围执行: 修改后按影响范围执行:
```powershell ```powershell
rtk python -X utf8 "$env:USERPROFILE\.codex\skills\.system\skill-creator\scripts\quick_validate.py" "etunel-role-collaboration" rtk python -X utf8 "$env:USERPROFILE\.codex\skills\.system\skill-creator\scripts\quick_validate.py" "etunel-role-collaboration"
rtk python -X utf8 scripts/test_dingtalk_notify.py
rtk "C:\Program Files\Git\bin\bash.exe" -n scripts/dingtalk-progress rtk "C:\Program Files\Git\bin\bash.exe" -n scripts/dingtalk-progress
rtk "C:\Program Files\Git\bin\bash.exe" -n scripts/dingtalk-bot-message
rtk proxy git diff --check rtk proxy git diff --check
rtk proxy git status --short rtk proxy git status --short
``` ```
@@ -103,7 +114,7 @@ Skill Creator 的验证脚本位置取决于协作者的 Codex 安装。若默
## Definition of Done ## Definition of Done
- 修改 Skill 时已通过 Skill Creator 的结构校验。 - 修改 Skill 时已通过 Skill Creator 的结构校验。
- 仅在修改 `scripts/dingtalk-progress` 时执行 Shell 语法检查。 - 修改钉钉封装时运行离线测试;修改 Shell 入口时检查语法,修改 PowerShell 入口时核验解析和实际参数传递。只使用隔离假配置,不触发真实通知。
- 所有新增或改动的 Markdown 相对链接均能解析。 - 所有新增或改动的 Markdown 相对链接均能解析。
- 文件中没有待办标记、占位内容、真实凭据、群标识或误导性的缺失文件引用。 - 文件中没有待办标记、占位内容、真实凭据、群标识或误导性的缺失文件引用。
- 根 Skill、公共 reference、角色 reference 和对应 Hook 在受影响规则上保持一致。 - 根 Skill、公共 reference、角色 reference 和对应 Hook 在受影响规则上保持一致。
+60 -19
View File
@@ -2,15 +2,44 @@
本仓库为 Etunel 多 Agent 项目提供一套中文版、渐进式披露的角色职责与协作流程:项目Hub负责跨角色中继和全局推进,七个成员角色在各自真人负责人参与下完成专业任务,并以可追踪成果交回 Hub。 本仓库为 Etunel 多 Agent 项目提供一套中文版、渐进式披露的角色职责与协作流程:项目Hub负责跨角色中继和全局推进,七个成员角色在各自真人负责人参与下完成专业任务,并以可追踪成果交回 Hub。
> **钉钉定位说明:** 仓库中的 `.agents/skills/dingtalk-*`、`.dingtalk/` 和 `scripts/dingtalk-progress` 只作为参考与演示,用来告诉 Hub 如何调用钉钉发送项目进度或异常通知。钉钉不是 Etunel 的角色通信链路、任务队列、审批、门禁或项目事实源,也不会改变任何角色边界。 > **钉钉定位说明:** 仓库中的 `.agents/skills/dingtalk-*`、`.dingtalk/` 和 `scripts/` 下的钉钉封装只作为接入参考,用来告诉 Hub 如何发送项目进度、异常摘要或负责人提醒。钉钉不是 Etunel 的角色通信链路、任务队列、审批、门禁或项目事实源,也不会改变任何角色边界。
## 先了解这三件事 ## 先了解这三件事
1. [主 Skill](etunel-role-collaboration/SKILL.md) 是角色 AI 的统一入口,只保留共享硬约束和条件路由;详细职责按当前任务逐层读取。 1. [主 Skill](etunel-role-collaboration/SKILL.md) 是角色 AI 的统一入口,只保留共享硬约束和条件路由;详细职责按当前任务逐层读取。
2. [Hook 上下文](etunel-role-hook-contexts/) 是给 Etunel 单独配置的精简提醒,不属于 Skill 的渐进式发现树,也不应替代完整职责文件。 2. [Hook 上下文](etunel-role-hook-contexts/) 是给 Etunel 单独配置的精简提醒,不属于 Skill 的渐进式发现树,也不应替代完整职责文件。
3. 当前本地定版文档为 [V1.0 总流程图](doc/20260908/Codex多角色项目推进总流程图-V1.0.html)(CURRENT,文档生效日期 2026-09-08),基于已确认的 V0.16 全文生成,保留原有流程与灯态“配网”修正。V0.15、V0.16 历史文件保留;文档定版不代表已推送、部署 Skill/Hook 或自动迁移在途项目。未经用户再次通知,不提交、不推送、不合入主分支,也不自动更新已安装的插件缓存。运行时规则已整理进稳定 reference,不要求目标项目持有版本 HTML。 3. 当前设计基线为 [V1.0 总流程图](doc/Codex多角色项目推进总流程图.html)(文档生效日期 2026-09-08)。流程文档只维护一份,后续直接更新同一文件,旧版通过 Git 历史追溯,不再按日期或版本另存。仓库更新不代表部署 Skill/Hook 或自动迁移在途项目;运行时规则已整理进稳定 reference,不要求目标项目持有流程 HTML。
仓库没有应用安装、编译或发布流程。主要工作是维护角色约束、流程文档、Hook 上下文以及 Hub 通知参考集成。 仓库没有应用安装或编译步骤;发布对象是职责 Skill、Hook 上下文及配套文档,不是设备固件或运行中的 Etunel 服务。
## 版本与升级
本版为 `v1.0.2`,流程设计基线仍为 **V1.0**。版本采用 `vX.Y.Z`:前两位 `X.Y` 跟随 HTML 主流程中明确标注且已确认的版本,第三位 `Z` 表示该流程版本下的仓库发布迭代。正式发布以 Git 标签和对应发布包为准,主分支可能包含尚未发布的修改。
| 变更 | 版本规则 |
| --- | --- |
| HTML 流程版本不变,更新 Skill、Hook、脚本、配置或说明等任意内容 | 只递增第三位,例如流程保持 V1.0,`v1.0.1 → v1.0.2` |
| HTML 流程正式更新到新的 X.Y | 前两位同步,第三位从 0 开始,例如流程 V1.1 对应 `v1.1.0` |
| 日常提交,尚未安排发布 | 不必为每次提交创建标签;下一次正式发版按上述规则编号 |
全仓库只维护这一套发布序列,不给 Skill、Hook 或脚本分别编号。前两位不按功能规模、变更文件数或兼容性自行调整,也不为新增脚本等非流程更新修改 HTML 版本。这不是按变更性质升主/次版本的语义化版本规则,第三位更新也可能需要配置迁移;职责或门禁变更仍须先确认流程。日常修改正常提交,需要交给团队使用时才统一校验、确定版本、创建 Git 标签及发布说明;已发布标签和内容不覆盖,修正发新版本。
发布说明应写清:流程基线、改了什么、使用者需要更新哪些文件、在途项目是否需要重新确认。不要把“仓库已发布”写成“角色已更新”。更新插件缓存、Etunel 中独立配置的 Hook 或迁移在途项目,都需另外明确确认;已运行项目继续使用其登记基线。
### v1.0.2 更新范围
- 明确前两位跟随 HTML 流程、其余发布迭代只升第三位的版本约定;主流程仍为 V1.0,八角色及阶段门禁不变。
- 钉钉通知改为共用 Python 实现,保留 macOS/Linux 入口,新增 Windows PowerShell 入口;支持项目事件、自定义中文 Markdown、文件正文和按姓名提醒负责人。
- Windows、macOS、Linux 统一从本地 `config.env` 的 `DINGTALK_PROGRESS_CLIENT_SECRET` 取机器人密钥,不再读取环境变量或钥匙串。**升级时须由部署负责人补填本地密钥字段,并删除旧 `DINGTALK_PROGRESS_KEYCHAIN_SERVICE` 字段**;不提交真实配置。
- 两个入口统一遵守离线预览;仅明确 DNS 失败每隔 10 秒重试、总计最多 3 次,结果未知不盲目重发。补充 27 项离线测试与部署说明。
- 更新核心 Skill 的通知路由与细则,以及中心 Hook 的完整职责和每轮提醒;其他角色 Hook 无需因本次通知更新而替换。目标项目需同步共用 Python 文件及所需平台入口,安装 Python 3.9+ 与原生 DWS;PowerShell 入口需 7.3+。
- Windows 普通群通知已获服务端回执并经负责人确认显示成功;按姓名 @ 和原生 macOS 发送仍未实测。通知升级不自动迁移在途项目或更新 Etunel Hook/插件缓存,需按部署安排明确执行。
### v1.0.1 更新范围
- 根据一轮项目复盘,补清旧任务收尾、开工输入与本次产出的区分、资料异常交专业角色核实、查询无新线索时转交负责人决定,以及模拟不默认跳过交接。
- 更新核心 Skill;Hook 仅调整中心与产品两份,保持完整职责 JSON 和每轮提醒分开配置。产品 Hook 修改的是完整职责区块,中心两个区块均有调整。
- 八角色、八阶段、正式成果、原有审批与测试验收条件不变;不自动迁移在途项目、不关闭真实遗留问题。本版不涉及钉钉脚本更新。
## 协作模型 ## 协作模型
@@ -47,7 +76,7 @@ flowchart LR
| 角色 | 主要定位 | | 角色 | 主要定位 |
| --- | --- | | --- | --- |
| 项目Hub | 建立项目、维护状态、规划任务波次、校验交付结构、协调阻塞并完成跨角色中继 | | 项目Hub | 建立项目、维护状态、规划任务波次、校验交付结构、协调阻塞并完成跨角色中继 |
| 业务 | 形成商业需求、客户与授权信息、业务验收及发布结项输入 | | 业务 | 形成业务需求、客户与授权信息,确认客户验收及复盘结项 |
| 产品 | 把业务需求转化为产品定义、范围、交互和验收标准 | | 产品 | 把业务需求转化为产品定义、范围、交互和验收标准 |
| 技术负责人 | 负责总体技术方案、接口与跨技术域设计协调 | | 技术负责人 | 负责总体技术方案、接口与跨技术域设计协调 |
| 嵌入式应用层 | 负责应用层软件设计、实现、联调和对应成果 | | 嵌入式应用层 | 负责应用层软件设计、实现、联调和对应成果 |
@@ -74,18 +103,28 @@ EP-Hub-Skill/
│ ├── project-lifecycle.md # 八阶段、成果和门禁 │ ├── project-lifecycle.md # 八阶段、成果和门禁
│ ├── project-status-and-membership.md │ ├── project-status-and-membership.md
│ ├── etunel-message-lifecycle.md │ ├── etunel-message-lifecycle.md
│ ├── document-templates.md # 模板复用与审核边界
│ ├── artifacts-and-evidence.md │ ├── artifacts-and-evidence.md
│ ├── exceptions-and-coordination.md │ ├── exceptions-and-coordination.md
│ ├── human-readable-communication.md │ ├── human-readable-communication.md
│ ├── project-files-and-progress.md │ ├── project-files-and-progress.md
│ ├── dingtalk-progress-reporting.md │ ├── dingtalk-progress-reporting.md
│ ├── dingtalk-script-usage.md # 通知的跨平台调用与负责人提醒
│ ├── roles/ # 七个成员角色职责 │ ├── roles/ # 七个成员角色职责
│ └── testing/ # 测试任务按需读取的二级细则 │ └── testing/ # 测试任务按需读取的二级细则
├── etunel-role-hook-contexts/ # 八角色独立 Hook 源文件 ├── etunel-role-hook-contexts/ # 八角色独立 Hook 源文件
├── doc/ # 当前设计基线和历史流程资料 ├── doc/ # 当前设计基线与模板参考,历史由 Git 保存
│ ├── Codex多角色项目推进总流程图.html # 当前 V1.0,后续原位更新
│ └── 文档模板参考/ # 按任务读取的参考笔记与来源清单
├── .agents/skills/dingtalk-*/ # 钉钉官方能力参考副本,不是核心 Skill ├── .agents/skills/dingtalk-*/ # 钉钉官方能力参考副本,不是核心 Skill
├── .dingtalk/ # 通知接入说明与无秘密示例配置 ├── .dingtalk/ # 通知接入说明与无秘密示例配置
└── scripts/dingtalk-progress # Hub 钉钉通知的当前演示封装 └── scripts/ # Hub 辅助通知,不是 Etunel 任务机制
├── dingtalk_notify.py # Python 共用实现,含有限重试
├── dingtalk-progress # macOS/Linux 进度入口
├── dingtalk-bot-message # macOS/Linux 摘要与负责人提醒
├── dingtalk-progress.ps1 # Windows PowerShell 进度入口
├── dingtalk-bot-message.ps1 # Windows PowerShell 摘要与提醒
└── test_dingtalk_notify.py # 不联网的隔离测试
``` ```
Skill 被用于实际项目时,Hub 在目标项目中维护统一的 `项目资料/`,而不是把项目成果写回本约束仓库: Skill 被用于实际项目时,Hub 在目标项目中维护统一的 `项目资料/`,而不是把项目成果写回本约束仓库:
@@ -128,7 +167,7 @@ Etunel 为角色会话配置本 Skill 后,角色 AI 应按以下顺序工作
[etunel-role-hook-contexts/](etunel-role-hook-contexts/) 为八个默认角色各提供一份“精简职责与每轮提醒”。它们适合由 Etunel Hook 在每轮会话中注入,用来持续提醒身份、边界、负责人参与和工具使用。 [etunel-role-hook-contexts/](etunel-role-hook-contexts/) 为八个默认角色各提供一份“精简职责与每轮提醒”。它们适合由 Etunel Hook 在每轮会话中注入,用来持续提醒身份、边界、负责人参与和工具使用。
本轮先同步远程 `main` 的 Hook 提醒(`0b133bf`),保留“完整职责 JSON + 每轮精简提醒”结构;职责 JSON 不是接口请求,实际绑定与工具参数仍以 Etunel 为准。本地源文件修改不会自动写入已安装插件或 Etunel 配置。 每份 Hook 文档分为“完整职责 JSON”和“每轮精简提醒”两个独立配置区块,分别复制到 Etunel 对应字段。职责 JSON 不是接口请求,实际绑定与工具参数仍以 Etunel 为准。仓库源文件修改不会自动写入已安装插件或 Etunel 配置。
Hook 文件需保持短小,并与完整 Skill 职责一致。它们由 Etunel 独立配置,因此不需要从 `SKILL.md` 中发现;修改角色边界时,应同时检查对应角色 reference 与 Hook,避免两套规则漂移。 Hook 文件需保持短小,并与完整 Skill 职责一致。它们由 Etunel 独立配置,因此不需要从 `SKILL.md` 中发现;修改角色边界时,应同时检查对应角色 reference 与 Hook,避免两套规则漂移。
@@ -136,22 +175,26 @@ Hook 文件需保持短小,并与完整 Skill 职责一致。它们由 Etunel
钉钉集成是 Hub 的辅助通知参考,不是 Etunel 协作系统的一部分。仓库保留它,是为了让 Hub 在满足事件条件时有一个清晰、安全、可替换的调用方法。 钉钉集成是 Hub 的辅助通知参考,不是 Etunel 协作系统的一部分。仓库保留它,是为了让 Hub 在满足事件条件时有一个清晰、安全、可替换的调用方法。
只有 Hub/主 Agent 可以使用下面的封装入口: 只有 Hub/主 Agent 可以使用下面的通用 Python 封装入口(Python 3.9+):
```sh ```sh
./scripts/dingtalk-progress <start|milestone|blocked|complete|failed> "<简短中文 Markdown 摘要>" python scripts/dingtalk_notify.py progress <start|milestone|blocked|complete|failed> "<简短中文 Markdown 摘要>"
``` ```
五类事件分别表示项目正式开始、可验证里程碑、真实阻塞、整体完成和最终失败。可验证里程碑由 Hub 先完成成果和进度留档,再发送钉钉消息。成员角色不得发送钉钉消息,只把结果交给 Hub。通知正文可使用多行 Markdown,只写可公开的已验证结论、下一步或阻塞原因,不发送密钥、个人数据、大段日志和未经证实的推断。 五类事件分别表示项目正式开始、可验证里程碑、真实阻塞、整体完成和最终失败。可验证里程碑由 Hub 先完成成果和进度留档,再发送钉钉消息。成员角色不得发送钉钉消息,只把结果交给 Hub。通知正文可使用多行 Markdown,只写可公开的已验证结论、下一步或阻塞原因,不发送密钥、个人数据、大段日志和未经证实的推断。
更详细的 Hub 事件语义、有限重试和结果判定见 [钉钉项目进度汇报](etunel-role-collaboration/references/dingtalk-progress-reporting.md);当前演示接入与本地配置见 [.dingtalk/README.md](.dingtalk/README.md)。`.agents/skills/dingtalk-*` 是钉钉官方多 Skill 的项目内参考副本,不应被当作 Etunel 核心 Skill,也不应被成员用来绕过 Hub。 新增 `message --title "中文标题" --text "问题和下一步"` 用于辅助摘要与负责人提醒,可按需用 `--at`;提醒结果回各自角色会话,不在群里另派任务或批准。两个入口均支持文件正文与离线 `--dry-run`,Windows 可用 `.ps1`,macOS/Linux 保留原 Shell 入口。它们是同一封装,不要重复调用。
各系统统一由封装读取本地 `config.env` 的 `DINGTALK_PROGRESS_CLIENT_SECRET`,不再从环境变量或 macOS 钥匙串取密钥;实际配置含明文密钥,须限制文件访问并排除出 Git。普通通知不需要 DWS 用户登录,按姓名提醒才额外需要同组织的已登录账号。
事件语义、有限重试和结果判定见 [钉钉项目进度汇报](etunel-role-collaboration/references/dingtalk-progress-reporting.md),命令细节见 [通知脚本用法](etunel-role-collaboration/references/dingtalk-script-usage.md);跨平台安装、原生 dws 与凭据配置见 [.dingtalk/README.md](.dingtalk/README.md)。`.agents/skills/dingtalk-*` 是钉钉官方多 Skill 的项目内参考副本,不应被当作 Etunel 核心 Skill,也不应被成员用来绕过 Hub。
本地 `.dingtalk/config.env` 已被 Git 忽略。不要读取、输出或提交其中的配置;不要在文档、Skill、Hook 或脚本中硬编码 Client Secret、Client ID、`robotCode`、`openConversationId`、App Token 或群名。仓库维护时也不要用真实发送作为普通测试。 本地 `.dingtalk/config.env` 已被 Git 忽略。不要读取、输出或提交其中的配置;不要在文档、Skill、Hook 或脚本中硬编码 Client Secret、Client ID、`robotCode`、`openConversationId`、App Token 或群名。仓库维护时也不要用真实发送作为普通测试。
## 修改项目的推荐流程 ## 修改项目的推荐流程
1. 阅读 [AGENTS.md](AGENTS.md) 和本次变更涉及的最小文件集合。 1. 阅读 [AGENTS.md](AGENTS.md) 和本次变更涉及的最小文件集合。
2. 若变更来自流程设计,核对 [V1.0 CURRENT 总流程图](doc/20260908/Codex多角色项目推进总流程图-V1.0.html);保持历史 HTML 不变,不把本地文档定版或规则更新当作远程发布、部署或在途项目自动迁移。 2. 若变更来自流程设计,核对 [当前 V1.0 总流程图](doc/Codex多角色项目推进总流程图.html),在固定文件上修改并通过 Git 提交记录变更;不要新增日期目录或旧版副本。部署及在途项目迁移另行确认。
3. 把共享规则放在公共 reference,把角色专属规则放在对应角色文件,把低频测试细节放在测试二级 reference。 3. 把共享规则放在公共 reference,把角色专属规则放在对应角色文件,把低频测试细节放在测试二级 reference。
4. 只有必须让所有角色立即知道的规则才进入根 `SKILL.md`;同时保持条件路由可发现。 4. 只有必须让所有角色立即知道的规则才进入根 `SKILL.md`;同时保持条件路由可发现。
5. 同步检查受影响的 Hook 文件,并验证链接、结构和 Shell 语法。 5. 同步检查受影响的 Hook 文件,并验证链接、结构和 Shell 语法。
@@ -167,10 +210,12 @@ git status --short
修改核心 Skill 后,使用已安装的 Skill Creator 对 `etunel-role-collaboration/` 运行 `scripts/quick_validate.py`。Skill Creator 的位置取决于协作者的 Codex 安装,不要为此把验证器复制进仓库。 修改核心 Skill 后,使用已安装的 Skill Creator 对 `etunel-role-collaboration/` 运行 `scripts/quick_validate.py`。Skill Creator 的位置取决于协作者的 Codex 安装,不要为此把验证器复制进仓库。
修改钉钉演示脚本后再运行: 修改钉钉封装后运行隔离测试;Shell 入口有修改时再检查对应语法:
```sh ```sh
python -X utf8 scripts/test_dingtalk_notify.py
bash -n scripts/dingtalk-progress bash -n scripts/dingtalk-progress
bash -n scripts/dingtalk-bot-message
``` ```
还应人工确认: 还应人工确认:
@@ -183,13 +228,9 @@ bash -n scripts/dingtalk-progress
## 设计资料 ## 设计资料
- [当前本地定版 V1.0(CURRENT)](doc/20260908/Codex多角色项目推进总流程图-V1.0.html) - [当前总流程图 V1.0](doc/Codex多角色项目推进总流程图.html)
- [保留的评审版本 V0.16(REVIEW)](doc/20260907/Codex多角色项目推进总流程图-V0.16.html) - [文档模板参考](doc/文档模板参考/README.md)
- [保留的总流程图 V0.15](doc/20260904/Codex多角色项目推进总流程图-V0.15.html) - 历史流程与变更说明通过 `git log --all -- doc/` 查询,不在工作目录保留旧版副本。
- [历史总流程图 V0.14](doc/20260902/Codex多角色项目推进总流程图-V0.14.html)
- [补充与历史追溯:项目Hub系统提示词 V0.13](doc/20260828/Codex多角色项目推进-项目Hub系统提示词-V0.13.txt)
- [历史总流程图 V0.13](doc/20260828/Codex多角色项目推进总流程图-V0.13.html)
- [历史总流程图 V0.4](doc/20260827/Codex多角色项目推进总流程图-V0.4.html)
- [核心 Skill](etunel-role-collaboration/SKILL.md) - [核心 Skill](etunel-role-collaboration/SKILL.md)
- [Hub 工作流](etunel-role-collaboration/references/hub-workflow.md) - [Hub 工作流](etunel-role-collaboration/references/hub-workflow.md)
- [项目生命周期](etunel-role-collaboration/references/project-lifecycle.md) - [项目生命周期](etunel-role-collaboration/references/project-lifecycle.md)
@@ -1,933 +0,0 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="light" />
<link rel="icon" href="data:," />
<title>Codex 多角色项目推进总流程图与信息流图谱|V0.3</title>
<style>
:root {
--navy: #10253f;
--navy-2: #17395f;
--blue: #2368a2;
--cyan: #2a9d9f;
--amber: #c67a1b;
--ink: #182331;
--muted: #5d6a78;
--line: #d8e0e7;
--paper: #f5f8fa;
--white: #ffffff;
--soft-cyan: #e9f7f6;
--soft-amber: #fff4df;
--soft-red: #fcecee;
--soft-green: #e9f6ef;
--max: 1180px;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
color: var(--ink);
background: var(--paper);
font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont,
"Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
line-height: 1.65;
}
.hero {
color: white;
background:
radial-gradient(circle at 88% 8%, rgba(42,157,159,.32), transparent 28%),
linear-gradient(145deg, #0c1f36 0%, #173f67 100%);
}
.hero-inner {
width: min(var(--max), calc(100% - 56px));
margin: 0 auto;
padding: 62px 0 52px;
}
.eyebrow {
display: inline-block;
margin-bottom: 18px;
padding: 6px 10px;
border: 1px solid rgba(255,255,255,.18);
border-radius: 999px;
color: #b9eff0;
background: rgba(255,255,255,.06);
font-size: 11px;
font-weight: 800;
letter-spacing: .12em;
}
h1, h2, h3, h4, p { margin-top: 0; }
h1 {
max-width: 900px;
margin-bottom: 18px;
font-size: clamp(40px, 5vw, 68px);
line-height: 1.05;
letter-spacing: -.04em;
}
.hero p {
max-width: 860px;
margin-bottom: 0;
color: rgba(255,255,255,.68);
font-size: 18px;
}
.page {
width: min(var(--max), calc(100% - 56px));
margin: 0 auto;
padding: 46px 0 72px;
}
.map-shell {
padding: 30px;
border: 1px solid var(--line);
border-radius: 24px;
background:
linear-gradient(rgba(35,104,162,.035) 1px, transparent 1px),
linear-gradient(90deg, rgba(35,104,162,.035) 1px, transparent 1px),
#eef4f7;
background-size: 28px 28px;
}
.map-head {
display: flex;
justify-content: space-between;
gap: 24px;
align-items: end;
margin-bottom: 28px;
}
.map-head h2 { margin-bottom: 6px; font-size: 30px; letter-spacing: -.025em; }
.map-head p { max-width: 760px; margin: 0; color: var(--muted); font-size: 14px; }
.legend { display: flex; flex-wrap: wrap; gap: 7px; justify-content: flex-end; }
.legend span {
padding: 5px 8px;
border-radius: 999px;
color: var(--muted);
background: var(--white);
box-shadow: 0 3px 12px rgba(16,37,63,.06);
font-size: 10px;
white-space: nowrap;
}
.track { display: grid; gap: 0; }
.node {
display: grid;
grid-template-columns: 58px 180px minmax(0,1fr) minmax(270px,.9fr);
gap: 18px;
align-items: center;
padding: 20px;
border: 1px solid #ccd9e3;
border-radius: 16px;
background: rgba(255,255,255,.97);
box-shadow: 0 8px 22px rgba(16,37,63,.055);
}
.index {
display: grid;
width: 46px;
height: 46px;
place-items: center;
border-radius: 14px;
color: white;
background: var(--navy-2);
font-weight: 850;
}
.stage h3 { margin: 0 0 5px; font-size: 19px; }
.role {
display: inline-block;
padding: 3px 7px;
border-radius: 999px;
color: #155e60;
background: var(--soft-cyan);
font-size: 10px;
font-weight: 800;
}
.work { color: var(--muted); font-size: 13px; }
.work strong { color: var(--ink); }
.artifacts {
padding-left: 18px;
border-left: 1px solid var(--line);
}
.artifacts strong { display: block; margin-bottom: 6px; font-size: 12px; }
.artifacts span { display: block; color: var(--muted); font-size: 11px; line-height: 1.55; }
.artifacts span + span { margin-top: 8px; }
.artifacts span::first-line { color: var(--ink); font-size: 12px; font-weight: 600; line-height: 1.75; }
.connector {
display: grid;
min-height: 72px;
place-items: center;
}
.connector::before { content: ""; width: 2px; height: 22px; background: #93aec5; }
.gate {
position: relative;
margin: 4px 0;
padding: 8px 14px 8px 30px;
border: 1px solid #e6c27b;
border-radius: 999px;
color: #70480d;
background: var(--soft-amber);
font-size: 11px;
font-weight: 760;
text-align: center;
}
.gate::before { content: "◆"; position: absolute; left: 11px; color: var(--amber); }
.connector::after { content: "↓"; color: var(--blue); font-size: 20px; font-weight: 900; line-height: 1; }
.parallel {
display: grid;
grid-template-columns: repeat(2, minmax(0,1fr));
gap: 8px;
margin-top: 8px;
}
.parallel span {
padding: 8px;
border: 1px solid var(--line);
border-radius: 9px;
color: var(--ink);
background: #f8fafb;
font-size: 11px;
}
.branch {
display: grid;
grid-template-columns: repeat(2, minmax(0,1fr));
gap: 10px;
margin-top: 12px;
}
.path { padding: 12px 14px; border-radius: 12px; font-size: 11px; }
.path strong { display: block; margin-bottom: 3px; }
.path.pass { border: 1px solid #a9d4c0; color: #266746; background: var(--soft-green); }
.path.return { border: 1px dashed #dc9ca0; color: #863b40; background: var(--soft-red); }
.crosscut {
margin-top: 20px;
padding: 18px 20px;
border: 1px dashed #95abc0;
border-radius: 14px;
background: rgba(255,255,255,.75);
}
.crosscut strong { color: var(--blue); }
.crosscut p { margin: 5px 0 0; color: var(--muted); font-size: 12px; }
.footer {
margin-top: 24px;
color: var(--muted);
font-size: 11px;
text-align: center;
}
@media (max-width: 920px) {
.node { grid-template-columns: 54px 160px minmax(0,1fr); }
.artifacts { grid-column: 3; padding-left: 0; border-left: 0; }
}
@media (max-width: 640px) {
.hero-inner, .page { width: min(100% - 28px, var(--max)); }
.hero-inner { padding: 44px 0 36px; }
.hero p { font-size: 15px; }
.page { padding-top: 24px; }
.map-shell { padding: 16px; border-radius: 18px; }
.map-head { display: block; }
.legend { justify-content: flex-start; margin-top: 14px; }
.node { grid-template-columns: 44px minmax(0,1fr); gap: 12px; padding: 16px; }
.index { width: 40px; height: 40px; border-radius: 12px; }
.stage, .work, .artifacts { grid-column: 2; }
.artifacts { padding-top: 12px; border-top: 1px solid var(--line); }
.parallel, .branch { grid-template-columns: 1fr; }
.gate { max-width: 290px; }
}
@media print {
@page { size: A4 portrait; margin: 12mm; }
body { background: white; font-size: 9pt; }
.hero { color: var(--ink); background: white; border-bottom: 3px solid var(--navy); }
.hero-inner, .page { width: 100%; }
.hero-inner { padding: 14mm 0 10mm; }
.hero .eyebrow { color: var(--blue); border-color: var(--line); }
.hero p { color: var(--muted); }
.page { padding: 10mm 0 0; }
.map-shell { padding: 0; border: 0; background: white; }
.node { break-inside: avoid; box-shadow: none; }
.connector { min-height: 16mm; }
}
/* Embedded stage information-flow atlas */
.if-atlas {
margin-top: 76px;
padding-top: 58px;
border-top: 4px solid var(--navy);
}
.if-intro {
margin-bottom: 42px;
padding: 40px;
border-radius: 22px;
color: white;
background:
radial-gradient(circle at 88% 10%, rgba(42,157,159,.28), transparent 30%),
linear-gradient(145deg, #10253f, #1a4a72);
}
.if-intro .if-eyebrow {
color: #b9eff0;
font-size: 11px;
font-weight: 850;
letter-spacing: .13em;
}
.if-intro h2 {
margin: 10px 0 12px;
font-size: clamp(34px,4vw,56px);
line-height: 1.05;
}
.if-intro p {
max-width: 900px;
margin-bottom: 20px;
color: rgba(255,255,255,.7);
}
.if-quicknav { display: flex; flex-wrap: wrap; gap: 8px; }
.if-quicknav a {
padding: 6px 9px;
border: 1px solid rgba(255,255,255,.18);
border-radius: 999px;
color: rgba(255,255,255,.75);
background: rgba(255,255,255,.06);
font-size: 10px;
text-decoration: none;
}
.if-section {
padding: 58px 0;
border-bottom: 1px solid var(--line);
}
.if-section-head {
display: grid;
grid-template-columns: 122px minmax(0,1fr);
gap: 24px;
margin-bottom: 28px;
}
.if-section-no {
color: var(--blue);
font-size: 11px;
font-weight: 850;
letter-spacing: .13em;
}
.if-section-head h2 {
margin-bottom: 8px;
font-size: clamp(28px,3vw,43px);
line-height: 1.1;
}
.if-section-head p {
max-width: 900px;
margin: 0;
color: var(--muted);
font-size: 14px;
}
.if-callout {
padding: 21px 24px;
border-left: 5px solid var(--cyan);
border-radius: 0 16px 16px 0;
background: var(--white);
box-shadow: 0 10px 28px rgba(16,37,63,.07);
font-size: 17px;
font-weight: 680;
}
.if-role-legend {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 21px 0;
}
.if-actor {
display: inline-flex;
min-width: 125px;
min-height: 50px;
align-items: center;
justify-content: center;
padding: 9px 12px;
border: 2px solid currentColor;
border-radius: 13px;
background: white;
font-size: 12px;
font-weight: 800;
text-align: center;
}
.if-center { color: #183d63; background: #eaf2f8; }
.if-business { color: #9b5d0b; background: #fff5df; }
.if-product { color: #7755a7; background: #f4eefb; }
.if-tech { color: #14787a; background: #e9f7f6; }
.if-app { color: #2f855a; background: #edf8f1; }
.if-low { color: #4265af; background: #eef2fc; }
.if-hardware { color: #8b5e3c; background: #f7f0eb; }
.if-test { color: #9b3d45; background: #fceef0; }
.if-human { color: #68561b; background: #fff9df; }
.if-flow-board {
display: grid;
gap: 10px;
padding: 23px;
border: 1px solid var(--line);
border-radius: 19px;
background: #edf3f6;
}
.if-exchange {
display: grid;
grid-template-columns: 150px minmax(240px,1fr) 150px;
gap: 14px;
align-items: center;
padding: 12px;
border: 1px solid #d0dce5;
border-radius: 15px;
background: rgba(255,255,255,.94);
}
.if-exchange .if-actor { width: 100%; }
.if-arrow {
position: relative;
min-height: 60px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 8px 34px 9px 10px;
color: var(--navy-2);
text-align: center;
}
.if-arrow::before {
content: "";
position: absolute;
left: 8px;
right: 18px;
top: 50%;
height: 2px;
background: #83a9c5;
}
.if-arrow::after {
content: "";
position: absolute;
right: 7px;
top: calc(50% - 6px);
border-left: 11px solid #83a9c5;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
}
.if-arrow > * {
position: relative;
z-index: 1;
padding: 0 7px;
background: rgba(255,255,255,.96);
}
.if-flow-id {
color: var(--blue);
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 10px;
font-weight: 900;
}
.if-arrow strong { font-size: 13px; }
.if-arrow small { color: var(--muted); font-size: 10px; }
.if-conditional { border-style: dashed; }
.if-blocking .if-arrow::before { background: #d28c91; }
.if-blocking .if-arrow::after { border-left-color: #d28c91; }
.if-stage-summary {
display: grid;
grid-template-columns: repeat(3,minmax(0,1fr));
gap: 12px;
margin-bottom: 18px;
}
.if-summary-card {
padding: 16px;
border: 1px solid var(--line);
border-radius: 14px;
background: white;
}
.if-summary-card b {
display: block;
margin-bottom: 5px;
color: var(--blue);
font-size: 11px;
}
.if-summary-card span { color: var(--muted); font-size: 12px; }
.if-table-wrap {
margin-top: 23px;
overflow-x: auto;
border: 1px solid var(--line);
border-radius: 15px;
background: white;
}
.if-table-wrap table {
width: 100%;
min-width: 1050px;
border-collapse: collapse;
font-size: 12px;
}
.if-table-wrap th,
.if-table-wrap td {
padding: 12px 13px;
border-right: 1px solid var(--line);
border-bottom: 1px solid var(--line);
text-align: left;
vertical-align: top;
}
.if-table-wrap th:last-child,
.if-table-wrap td:last-child { border-right: 0; }
.if-table-wrap tr:last-child td { border-bottom: 0; }
.if-table-wrap th {
color: white;
background: var(--navy);
font-size: 11px;
white-space: nowrap;
}
.if-table-wrap tbody tr:nth-child(even) { background: #f8fafb; }
.if-table-wrap td:first-child {
color: var(--blue);
font-family: "SFMono-Regular", Consolas, monospace;
font-weight: 850;
white-space: nowrap;
}
.if-block { color: #8f3038; font-weight: 700; }
.if-pass { color: #266746; font-weight: 700; }
.if-cross-flow {
padding: 22px;
border: 1px dashed #9aafc0;
border-radius: 18px;
background: #eef4f7;
}
@media (max-width: 920px) {
.if-exchange { grid-template-columns: 125px minmax(210px,1fr) 125px; }
}
@media (max-width: 640px) {
.if-intro { padding: 26px 20px; }
.if-intro h2 { font-size: 30px; }
.if-section-head { grid-template-columns: 1fr; gap: 8px; }
.if-stage-summary { grid-template-columns: 1fr; }
.if-flow-board { padding: 13px; }
.if-exchange { grid-template-columns: 1fr; gap: 7px; }
.if-exchange .if-actor { min-height: 45px; }
.if-arrow { min-height: 72px; padding: 22px 10px; }
.if-arrow::before {
left: 50%;
right: auto;
top: 7px;
bottom: 15px;
width: 2px;
height: auto;
}
.if-arrow::after {
right: auto;
left: calc(50% - 6px);
top: auto;
bottom: 5px;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 11px solid #83a9c5;
border-bottom: 0;
}
.if-blocking .if-arrow::after {
border-top-color: #d28c91;
border-left-color: transparent;
}
}
@media print {
.if-atlas { page-break-before: always; }
.if-exchange,
.if-summary-card { break-inside: avoid; box-shadow: none; }
.if-table-wrap table { font-size: 8pt; }
}
</style>
</head>
<body>
<header class="hero">
<div class="hero-inner">
<div class="eyebrow">CODEX MULTI-ROLE PROJECT FLOW · V0.3</div>
<h1>项目推进总流程图</h1>
<p>以业务、项目、产品、技术负责人、嵌入式应用层、嵌入式底层、硬件、测试八类角色为基础,其中项目角色由中心 AI 承担;通过正式成果、角色内部确认门和中心服务路由,推进项目从需求到发布结项。</p>
</div>
</header>
<main class="page">
<section class="map-shell">
<div class="map-head">
<div>
<h2>从业务需求到发布结项</h2>
<p>主线按成果和确认门推进;测试失败、业务验收驳回和需求变更通过回路返回正确阶段,不需要从头重启项目。</p>
</div>
<div class="legend" aria-label="流程图图例">
<span>白色卡片=项目阶段</span>
<span>黄色门=角色/阶段确认</span>
<span>红色虚线=返工回路</span>
</div>
</div>
<div class="track" aria-label="完整项目推进流程">
<article class="node">
<div class="index">01</div>
<div class="stage"><h3>业务需求</h3><span class="role">主责:业务</span></div>
<div class="work"><strong>明确问题与价值。</strong>补齐业务目标、范围、规则、优先级和可度量成功指标。</div>
<div class="artifacts"><strong>正式成果</strong><span>Project Background Brief(项目背景说明)<br>客户信息:客户名称、最终客户名称<br>项目名称:统一名称或客户规定的项目代号<br>产品要求:产品形态、提测平台<br>项目来源:新客户、新项目、旧产品升级、竞品替换或成本优化<br>需求背景:客户为什么提出这个需求,要解决什么问题<br>商业目标:获取订单、维护客户、降低成本、拓展市场或形成标准产品<br>预期价值:预计订单量、销售额</span><span>Project Milestone Plan(项目目标与里程碑计划)<br>提测时间<br>入库时间<br>小批量试产时间<br>量产时间</span></div>
</article>
<div class="connector"><div class="gate">业务确认需求基线</div></div>
<article class="node">
<div class="index">02</div>
<div class="stage"><h3>产品定义</h3><span class="role">主责:产品 · 批准:业务</span></div>
<div class="work"><strong>把目标转成可实现、可验证的产品定义。</strong>技术负责人组织可行性评估,嵌入式应用层、嵌入式底层和硬件分别评估实现约束,测试评估可测试性。</div>
<div class="artifacts"><strong>正式成果</strong><span>Project Initiation Package(立项资料)<br>立项书:包含产品详细信息、开发模式、开发范围、测试范围及其他补充信息<br>规格书:包含产品硬件规格和主要元器件型号(主控芯片、Sensor、镜头、WiFi 芯片)<br>功能清单:定义设备支持的功能<br>灯态:描述设备上电、绑定、升级、网络断开等状态下的指示灯状态与语音播报内容<br>产测功能清单:描述产测工具支持项<br>版本说明:描述当前版本的背景、主要功能等关键信息,并包含该项目此前所有版本和阶段的信息<br>结构板框图:包含主要器件位置和禁止限制区域</span><span>Hardware Development Package(硬件开发资料)<br>外设器件和关键器件规格书:主控、Flash、Sensor、复位按键、指示灯、WiFi 模块、音频功放驱动芯片、IR-CUT 驱动芯片、电机达林顿管驱动芯片<br>结构板框图:包含主要器件位置和禁止限制区域</span><span>Embedded Development Package(嵌入式开发资料)<br>平台 SDK<br>串号表<br>平台对接文档<br>平台接入标准及开发规范</span><span>Test &amp; Acceptance Criteria(测试验收标准)<br>平台测试用例<br>客户验收测试用例和验收标准</span><span>Milestone Requirements(里程碑要求)<br>提测时间<br>客户验收时间<br>试产、量产时间</span></div>
</article>
<div class="connector"><div class="gate">产品确认方案,业务批准范围</div></div>
<article class="node">
<div class="index">03</div>
<div class="stage"><h3>项目规划</h3><span class="role">主责:中心 AI</span></div>
<div class="work"><strong>基于成果、成员能力和依赖自动编排任务、进度和风险。</strong>任务计划草案先由技术负责人确认;中心 AI 只对存在疑点的任务定向询问对应执行角色,真实资源和日期基线再提交业务确认。</div>
<div class="artifacts"><strong>正式成果</strong><span>Project Plan(项目计划)<br>项目任务拆分<br>实际项目排期</span><span>Risk Register(项目风险)<br>技术风险<br>时间风险<br>其他风险</span><span>Role Assignment(角色分工)</span><span>Product Documentation Package(产品资料整合)</span><span>Project Status Record(项目状态内部记录)<br>阶段与门禁<br>任务与依赖<br>阻塞与风险<br>决策与成果基线<br>下一步与时限</span></div>
</article>
<div class="connector"><div class="gate">技术负责人确认任务计划,问题任务由对应执行角色定向确认,业务确认资源与日期基线</div></div>
<article class="node">
<div class="index">04</div>
<div class="stage"><h3>方案设计</h3><span class="role">主责:技术负责人 · 协作:嵌入式应用层 + 嵌入式底层 + 硬件 + 测试</span></div>
<div class="work"><strong>技术负责人统一组织总体方案设计和确认。</strong><div class="parallel"><span>技术负责人:总体架构、软硬件边界、关键决策</span><span>嵌入式应用层:业务逻辑、应用协议、功能模块设计</span><span>嵌入式底层:BSP、驱动、RTOS、HAL 设计</span><span>硬件:原理图、PCB、BOM、电源/信号/热设计</span><span>测试:范围、环境、用例、通过标准</span></div></div>
<div class="artifacts"><strong>正式成果</strong><span>Solution Architecture(总体技术方案)</span><span>Software/Hardware Interface Contract(软硬件接口契约)</span><span>Hardware Design Package(硬件设计包)</span><span>Architecture Decision Record(架构决策记录)</span><span>Test Plan(测试计划)</span></div>
</article>
<div class="connector"><div class="gate">技术负责人确认整体方案,嵌入式应用层/嵌入式底层/硬件确认接口可实施,产品确认不偏离,测试确认可验证</div></div>
<article class="node">
<div class="index">05</div>
<div class="stage"><h3>嵌入式软硬件实现</h3><span class="role">并行:嵌入式应用层 + 嵌入式底层 + 硬件</span></div>
<div class="work"><strong>嵌入式应用层、嵌入式底层和硬件在已确认总体方案及软硬件接口契约下并行实现、样机调试和自测。</strong>嵌入式底层将可消费版本交给嵌入式应用层合版,嵌入式应用层负责生成并提交唯一的最终固件版本;接口或器件约束发生重大偏离时返回技术负责人确认。</div>
<div class="artifacts"><strong>正式成果</strong><span>Hardware Implementation Package(硬件实现资料)<br>原理图:DSN 源文档和 PDF 文档<br>PCB:源文档<br>贴片资料:PCBA BOM、器件位置图 PDF、贴片坐标<br>维修原理图<br>接口定义<br>静态测试报告</span><span>Low-Level Implementation Package(嵌入式底层实现资料)<br>硬件功能测试报告<br>BSP 输出路径<br>PSP 输出路径<br>WiFi 设备定频测试固件</span><span>Application Firmware Package(嵌入式应用层固件包)<br>正式固件、升级固件<br>自测用例<br>分区表<br>MD5 值文件<br>Flash 烧写固件<br>日志文件<br>Changelog<br>串口使能证书</span></div>
</article>
<div class="connector"><div class="gate">嵌入式底层提交可消费版本,嵌入式应用层合版并提交最终固件,技术负责人确认固件与硬件版本匹配</div></div>
<article class="node">
<div class="index">06</div>
<div class="stage"><h3>测试验证</h3><span class="role">主责:测试 · 修复:嵌入式应用层/嵌入式底层/硬件</span></div>
<div class="work"><strong>执行功能、接口、软硬件集成和回归测试。</strong><div class="branch"><div class="path return"><strong>不通过</strong>应用问题→嵌入式应用层;BSP/驱动问题→嵌入式底层;电路/PCB/器件问题→硬件;接口/架构问题→技术负责人</div><div class="path pass"><strong>通过</strong>质量报告 + 测试证据包</div></div></div>
<div class="artifacts"><strong>正式成果</strong><span>Functional Test Report(功能测试报告)<br>平台测试用例<br>内部测试用例</span><span>Reliability Test Report(可靠性测试报告)<br>高低温测试<br>跌落测试</span><span>Specialized Test Report(专项测试报告)<br>画质测试<br>WiFi 测试<br>功耗测试<br>长稳测试<br>试产一致性测试</span><span>Test Evidence Package(提测佐证)</span><span>Defect Analysis Report(缺陷分析报告)</span></div>
</article>
<div class="connector"><div class="gate">测试确认质量结论和剩余风险</div></div>
<article class="node">
<div class="index">07</div>
<div class="stage"><h3>业务验收</h3><span class="role">主责:产品</span></div>
<div class="work"><strong>依据成功指标、验收标准和质量证据作最终判断。</strong><div class="branch"><div class="path return"><strong>驳回</strong>需求→产品;应用问题→嵌入式应用层;嵌入式底层软件问题→嵌入式底层;硬件问题→硬件;架构问题→技术负责人;证据问题→测试</div><div class="path pass"><strong>通过</strong>形成正式业务验收结论,进入发布准备</div></div></div>
<div class="artifacts"><strong>正式成果</strong><span>Platform Test Approval Report(平台提测通过报告)</span><span>Customer Acceptance Approval Report(客户验收通过报告)</span><span>Conditional Acceptance Items(附条件验收事项)</span></div>
</article>
<div class="connector"><div class="gate">业务批准验收结论和上线条件</div></div>
<article class="node">
<div class="index">08</div>
<div class="stage"><h3>发布结项</h3><span class="role">中心 AI 协调 · 技术负责人确认 · 嵌入式应用层/嵌入式底层/硬件执行 · 测试验证</span></div>
<div class="work"><strong>检查应用、固件、板卡版本、BOM/ECO、发布清单、冒烟、回滚和遗留事项承接。</strong>中心 AI 汇总复盘并归档角色会话。</div>
<div class="artifacts"><strong>正式成果</strong><span>Closure Documentation Archive(结项资料归档)<br>形成最终结果的流程文件</span><span>Change Log(变更记录)</span><span>Closure Report(结项报告)</span><span>Process Closure Summary(流程闭环总结)</span></div>
</article>
</div>
<div class="crosscut">
<strong>贯穿全流程:Change Request(需求变更申请)</strong>
<p>任何角色都可提出变更;中心 AI 自动组织影响评估,产品评估范围,技术负责人评估总体方案,嵌入式应用层、嵌入式底层和硬件分别评估实现与工作量,测试评估验证成本,业务批准后更新对应基线,并从受影响阶段继续推进。</p>
</div>
<div class="crosscut">
<strong>成果适用性:Artifact Waiver(成果文件豁免)</strong>
<p>阶段成果默认必须由主责角色输出;仅当主责角色提交不适用申请、中心 AI 完成项目级适用性与影响审查并取得必要确认后,才能将该成果标记为 WAIVED。未批准的成果仍为 REQUIRED,并继续阻塞阶段门禁。</p>
</div>
</section>
<div class="if-atlas" id="information-flow-atlas">
<section class="if-intro">
<div class="if-eyebrow">STAGE INFORMATION FLOW ATLAS · V0.3</div>
<h2>各阶段角色信息流图谱</h2>
<p>以下内容直接承接总流程图,展示每条会影响成果、决策、门禁、返工或版本的正式信息流,并说明触发条件、发送方、接收方、载荷、确认要求和阻塞约束。</p>
<div class="if-quicknav">
<a href="#rules">通用规则</a><a href="#stage1">01 业务需求</a><a href="#stage2">02 产品定义</a><a href="#stage3">03 项目规划</a><a href="#stage4">04 方案设计</a><a href="#stage5">05 软硬件实现</a><a href="#stage6">06 测试验证</a><a href="#stage7">07 业务验收</a><a href="#stage8">08 发布结项</a><a href="#change">变更与异常</a><a href="#waiver">成果豁免</a>
</div>
</section>
<section class="if-section" id="rules">
<div class="if-section-head">
<div class="if-section-no">00 / RULES</div>
<div><h2>所有阶段共同遵守的流转规则</h2><p>角色可以直接讨论,但讨论不能自动成为项目事实;正式结论必须回到中心 AI,经过校验、登记和重新分发。</p></div>
</div>
<div class="if-callout">唯一正式路径:触发事件 → 主责角色内部完成 AI 辅助自审与人工确认 → 以统一角色主体提交中心 AI → 中心 AI 做形式校验、版本/证据检查和跨角色冲突检查 → 路由目标角色 → 目标角色内部确认后形成正式结果 → 中心 AI 登记与分发下游 → 更新门禁。信息流图只使用正式角色名称,不单列角色内部的 AI 会话与人工确认过程。</div>
<div class="if-callout" style="margin-top: 14px">成果默认 REQUIRED:主责角色必须输出;确认不适用时必须走 Artifact Waiver 流程。只有 APPROVED 的 WAIVED 状态可以替代成果满足门禁,任何角色都不得静默删除或跳过文件。</div>
<div class="if-role-legend">
<span class="if-actor if-center">中心 AI</span><span class="if-actor if-business">业务</span><span class="if-actor if-product">产品</span><span class="if-actor if-tech">技术负责人</span><span class="if-actor if-app">嵌入式应用层</span><span class="if-actor if-low">嵌入式底层</span><span class="if-actor if-hardware">硬件</span><span class="if-actor if-test">测试</span>
</div>
<div class="if-table-wrap" style="margin-bottom: 22px"><table><thead><tr><th>主体</th><th>是否为项目职能角色</th><th>定义</th><th>主要确认权限</th><th>不能做什么</th></tr></thead><tbody>
<tr><td>各专业角色</td><td>是</td><td>图中的正式角色名称统一代表该角色完成 AI 辅助自审与人工确认后的责任主体;内部审查过程不作为跨角色信息流单列。</td><td>以角色名义确认并发出本角色专业结论、正式成果、现实工期承诺、风险接受和正式交付。</td><td>不能替其他角色作出专业确认。</td></tr>
<tr><td>业务</td><td>是;同时承担项目级组织授权</td><td>业务节点同时包含业务专业判断和项目级组织授权的内部确认。</td><td>除业务成果外,负责确认计划基线、真实资源、预算、采购/打样、日期、暂停/取消和高影响成果豁免。</td><td>不替代产品、技术负责人、硬件或测试等角色给出专业结论。</td></tr>
<tr><td>中心 AI</td><td>是;承担项目角色</td><td>项目唯一正式信息中心、任务路由器、成果校验器、阶段门禁执行者和项目状态内部记录维护者。</td><td>可依据证据更新项目状态、按角色需要分发状态快照、完成普通项目级审查和低影响豁免;高影响事项必须升级给业务。</td><td>不能自行承诺真实资源、预算、范围、日期或专业结论,也不能以推测代替角色提供的状态证据。</td></tr>
</tbody></table></div>
<div class="if-flow-board">
<div class="if-exchange"><div class="if-actor if-business">任意角色</div><div class="if-arrow"><span class="if-flow-id">G.1</span><strong>提交已完成内部审查确认的消息、成果或请求</strong><small>附 self_check_result、internal_approved、项目、任务、版本和证据引用</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">G.2</span><strong>形式校验、跨角色冲突检查、登记并路由</strong><small>不替角色判断专业内容质量</small></div><div class="if-actor if-product">目标角色</div></div>
<div class="if-exchange"><div class="if-actor if-product">目标角色</div><div class="if-arrow"><span class="if-flow-id">G.3</span><strong>完成内部审查后返回结构化结果</strong><small>角色内部确认不单列;禁止转发完整聊天记录</small></div><div class="if-actor if-center">中心 AI</div></div>
</div>
<div class="if-callout" style="margin: 22px 0 14px"><strong>项目状态内部记录与按需同步:</strong>Project Status Record(项目状态内部记录)是中心 AI 持续维护的内部运行记录,不是新增角色。它汇总当前阶段、门禁、任务、依赖、阻塞、风险、决策、成果基线、下一步和时限;中心 AI 在关键状态变化时主动推送,也响应角色按项目、阶段或任务发起的查询。</div>
<div class="if-flow-board">
<div class="if-exchange if-conditional"><div class="if-actor if-business">任意角色</div><div class="if-arrow"><span class="if-flow-id">S.1 · 按需查询</span><strong>请求与本角色职责相关的最新项目状态</strong><small>指定 project_id、stage/task scope、关注项和期望时点</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">S.2 · 自动或按需</span><strong>生成并分发角色化项目状态快照</strong><small>关键状态变化时主动推送;收到 S.1 后按查询范围返回</small></div><div class="if-actor if-product">请求角色及受影响角色</div></div>
<div class="if-exchange"><div class="if-actor if-product">接收角色</div><div class="if-arrow"><span class="if-flow-id">S.3</span><strong>确认状态或携带证据提出纠正</strong><small>不得仅凭口头判断覆盖已登记状态</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange if-conditional"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">S.4 · 状态纠正</span><strong>校验依据、更新内部记录并重新分发</strong><small>保留旧状态、变更原因、证据引用和生效时间</small></div><div class="if-actor if-product">全部受影响角色</div></div>
</div>
<div class="if-table-wrap"><table><thead><tr><th>ID</th><th>触发条件</th><th>发送 → 接收</th><th>正式载荷</th><th>约束/确认</th><th>对项目执行的影响</th></tr></thead><tbody>
<tr><td>S.1</td><td>角色需要了解当前项目、阶段、任务、依赖、风险或门禁状态,但现有上下文不足或可能已过期。</td><td>任意角色 → 中心 AI</td><td>project_id、请求范围、关注字段、所需详细度、as_of 要求和用途。</td><td>只能查询项目授权范围内的信息;请求角色无需知道中心 AI 的内部存储结构。</td><td>创建一次按需状态查询。</td></tr>
<tr><td>S.2</td><td>收到 S.1;或阶段/门禁、任务分派、依赖、阻塞、风险、决策、成果基线、required_by 发生关键变化。</td><td>中心 AI → 请求角色及受影响角色</td><td>Project Status Snapshot:project_id、state_version、as_of、stage/gate、相关任务、依赖、阻塞、风险、决策、baseline_refs、next_actions、required_by、evidence_refs。</td><td>按最小必要原则裁剪为角色化上下文;禁止转发无关角色的完整会话或未确认专业判断;所有状态必须有证据来源。</td><td>角色获得可执行的最新上下文。</td></tr>
<tr><td>S.3</td><td>角色收到状态快照。</td><td>接收角色 → 中心 AI</td><td>ACK;或 correction_request、受影响字段、正确值、原因、evidence_refs、internal_approved。</td><td>没有证据的异议不得直接覆盖状态;存在争议时进入 E.1–E.4。</td><td>确认可继续执行,或触发状态纠正。</td></tr>
<tr><td>S.4</td><td>纠正请求通过身份、版本、证据及跨角色一致性检查。</td><td>中心 AI → 全部受影响角色</td><td>新 state_version、变更字段、旧值/新值、变更原因、证据、生效时间及受影响任务。</td><td>保留状态历史,不覆盖旧版本;改变正式基线时必须转入 Change Request。</td><td>相关角色切换到新的有效状态快照。</td></tr>
</tbody></table></div>
<div class="if-callout" style="margin: 22px 0 14px"><strong>异常协同补充机制:</strong>任何条件流、阻塞流、跨角色冲突或无法在时限内异步收敛的问题,都必须进入 E.1–E.4。中心 AI 主动提醒关联角色并提供完整问题包;问题复杂时建议由当前主责角色组织线下会议。会议本身不直接形成项目事实,只有参与角色统一确认并回传中心 AI 的结构化结论,才能登记并恢复原流程。</div>
<div class="if-flow-board">
<div class="if-exchange if-conditional"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">E.1 · 异常触发</span><strong>主动提醒当前主责角色及全部关联角色</strong><small>发送问题、证据、冲突点、影响范围、原流程位置和响应时限</small></div><div class="if-actor if-product">当前主责角色及关联角色</div></div>
<div class="if-exchange if-conditional"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">E.2 · 复杂问题</span><strong>建议由当前主责角色组织线下会议</strong><small>提供建议参会角色、议题、待决策项、证据包和结论模板</small></div><div class="if-actor if-product">当前主责角色及关联角色</div></div>
<div class="if-exchange"><div class="if-actor if-product">当前主责角色</div><div class="if-arrow"><span class="if-flow-id">E.3</span><strong>提交参与角色统一确认的会议结论</strong><small>包含结论、分歧处理、变更项、责任角色、期限、版本和确认状态</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">E.4</span><strong>校验、登记结论并恢复原异常流程</strong><small>只恢复被阻塞的原流程,不以会议记录替代正式成果</small></div><div class="if-actor if-product">原流程及关联角色</div></div>
</div>
<div class="if-table-wrap"><table><thead><tr><th>ID</th><th>触发条件</th><th>发送 → 接收</th><th>正式载荷</th><th>约束/确认</th><th>对原流程的影响</th></tr></thead><tbody>
<tr><td>E.1</td><td>条件流或阻塞流被触发;出现跨角色冲突、依赖异常、证据矛盾、任务逾期或结果无法被下游使用。</td><td>中心 AI → 当前主责角色及全部关联角色</td><td>issue_id、source_flow_id、问题摘要、证据与版本引用、冲突点、影响范围、关联角色、required_by。</td><td>中心 AI 必须主动提醒,不得只更新状态;关联角色依据正式问题包开展异步确认。</td><td class="if-block">原流程保持 ACTIVE/BLOCKED,等待问题关闭。</td></tr>
<tr><td>E.2</td><td>涉及多个专业角色且结论冲突,影响范围/架构/接口/成本/进度/质量,或在要求时限内无法通过异步消息收敛。</td><td>中心 AI → 当前主责角色及关联角色</td><td>建议参会名单、会议目的、议题、冲突矩阵、证据包、待决策项、结论模板和完成时限。</td><td>中心 AI 只建议会议和准备输入,不主持专业裁决;当前主责角色负责组织,关联角色共同参与确认。</td><td class="if-block">会议结论回传前,不恢复原流程。</td></tr>
<tr><td>E.3</td><td>线下会议完成并形成统一结论。</td><td>当前主责角色 → 中心 AI</td><td>Meeting Decision Record:参与角色、统一结论、保留分歧、变更项、责任角色、期限、artifact/version/evidence_refs、internal_approved。</td><td>禁止只上传录音、聊天记录或未经参与角色确认的纪要;仍有分歧时继续保持阻塞并明确升级项。</td><td>形成可登记的异常处置结论。</td></tr>
<tr><td>E.4</td><td>统一结论通过中心 AI 的形式、版本、证据和跨角色一致性检查。</td><td>中心 AI → 原流程及关联角色</td><td>decision_id、登记结果、受影响成果/任务、恢复点、后续动作和新时限。</td><td>中心 AI 不改写专业结论;若结论改变正式基线,必须转入 Change Request。</td><td class="if-pass">从 source_flow_id 对应位置恢复当前流程。</td></tr>
</tbody></table></div>
<div class="if-table-wrap"><table><thead><tr><th>通用约束</th><th>执行要求</th><th>不满足时</th></tr></thead><tbody>
<tr><td>消息身份</td><td>必须包含 project_id、task_id、message_id、from/to role、stage、reply_to。</td><td class="if-block">拒绝进入正式流。</td></tr>
<tr><td>成果版本</td><td>必须包含 artifact_id、version、status、supersedes、evidence_refs。</td><td class="if-block">标记为 DRAFT 或 REJECTED_ARTIFACT。</td></tr>
<tr><td>成果责任</td><td>原则上由流程图指定的主责角色创建、更新并提交对应成果。</td><td class="if-block">其他角色不能代签;中心 AI 返回主责角色。</td></tr>
<tr><td>角色内部审查</td><td>每个角色必须在内部完成 AI 辅助的 Schema/专业检查以及人工确认;跨角色流转时统一以正式角色名称表示。</td><td class="if-block">未附 self_check_result 或 internal_approved 的成果不能提交中心 AI。</td></tr>
<tr><td>专业发起权</td><td>专业活动由对应主责角色判断并发起;中心 AI 只负责格式校验、任务路由、状态跟踪和结果登记。</td><td class="if-block">中心 AI 不得越权替角色发起专业决策。</td></tr>
<tr><td>中心 AI 审查边界</td><td>仅做 Schema/必填结构、身份、版本、证据、审批状态检查,以及多个角色正式信息之间的重复、冲突和依赖一致性检查。</td><td class="if-block">不得替业务、产品、技术负责人、嵌入式应用层、嵌入式底层、硬件或测试判断本专业内容是否充分。</td></tr>
<tr><td>成果豁免</td><td>仅主责角色可提出不适用申请;经中心 AI 项目审查及必要确认后标记 WAIVED。</td><td class="if-block">未批准仍为 REQUIRED,继续阻塞门禁。</td></tr>
<tr><td>角色确认</td><td>专业结论由对应角色内部形成并确认;项目级组织授权由业务内部完成。信息流图只显示统一的正式角色主体。</td><td class="if-block">未完成内部确认时不得对外发出,阶段保持 WAITING_CONFIRMATION。</td></tr>
<tr><td>项目状态同步</td><td>中心 AI 维护有版本和证据的项目状态内部记录,并在关键变化时主动向受影响角色推送,或按 S.1–S.4 响应角色查询与纠正。</td><td class="if-block">状态来源、版本或时点不明时不得作为任务执行依据。</td></tr>
<tr><td>异常协同</td><td>中心 AI 对异常主动提醒关联角色;复杂问题建议当前主责角色组织线下会议,统一确认后提交结构化结论。</td><td class="if-block">未按 E.1–E.4 闭环,不得恢复原流程或推进门禁。</td></tr>
<tr><td>直接讨论</td><td>最终结论、约束、证据和影响必须回传中心 AI。</td><td class="if-block">不能作为下游输入。</td></tr>
<tr><td>门禁</td><td>只有成果完整、版本正确、证据可访问、确认齐全时才能 PASS。</td><td class="if-block">阶段保持 ACTIVE/BLOCKED。</td></tr>
</tbody></table></div>
</section>
<section class="if-section" id="stage1">
<div class="if-section-head"><div class="if-section-no">01 / BUSINESS</div><div><h2>阶段 1:业务需求信息流</h2><p>把客户背景、商业目标和关键里程碑转换成经过业务角色内部确认的需求基线。</p></div></div>
<div class="if-stage-summary"><div class="if-summary-card"><b>主责</b><span>业务</span></div><div class="if-summary-card"><b>核心成果</b><span>项目背景说明、项目目标与里程碑计划</span></div><div class="if-summary-card"><b>门禁</b><span>业务完成内部审查并确认需求基线</span></div></div>
<div class="if-callout" style="margin-bottom: 18px; font-size: 14px">业务在内部完成原始信息收集、业务成果 Schema 检查和人工确认后,以统一的“业务”主体提交成果。中心 AI 完成形式校验、版本登记和跨角色冲突检查后,将业务成果评审版本路由给产品;产品据此判断是否需要早期风险预检,并负责指定一个或多个预检对象角色。中心 AI 只能按照产品给出的角色名单路由,不得自行增删或替换。业务最终确认后,中心 AI 再向产品分发正式业务基线并开启产品定义。</div>
<div class="if-flow-board">
<div class="if-exchange"><div class="if-actor if-business">业务</div><div class="if-arrow"><span class="if-flow-id">F1.1</span><strong>提交完成内部审查的项目背景与里程碑草案</strong><small>附 self_check_result、internal_approved 和来源引用</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange if-conditional if-blocking"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F1.2 · 条件触发</span><strong>仅在发现形式错误或跨来源冲突时返回问题</strong><small>触发范围仅限 Schema、身份、版本、证据和已登记信息冲突;无异常则不启用此流</small></div><div class="if-actor if-business">业务</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F1.3</span><strong>分发已通过形式校验的业务成果评审版本</strong><small>附成果版本、证据引用、状态和待确认项</small></div><div class="if-actor if-product">产品</div></div>
<div class="if-exchange if-conditional"><div class="if-actor if-product">产品</div><div class="if-arrow"><span class="if-flow-id">F1.4</span><strong>指定对象角色并提交早期风险预检请求</strong><small>产品根据风险内容选择一个或多个项目角色</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F1.5</span><strong>按产品指定名单校验、提醒并路由预检任务</strong><small>主动提醒全部关联角色;不得增删产品指定名单</small></div><div class="if-actor if-tech">产品指定的对象角色</div></div>
<div class="if-exchange"><div class="if-actor if-tech">产品指定的对象角色</div><div class="if-arrow"><span class="if-flow-id">F1.6</span><strong>返回内部确认结果;复杂问题返回统一会议结论</strong><small>无法异步收敛时,由产品组织线下会议并按 E.3 回传</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-business">业务</div><div class="if-arrow"><span class="if-flow-id">F1.7</span><strong>确认需求基线</strong><small>业务内部审查、形式校验和预检问题均已关闭</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F1.8</span><strong>向产品分发正式业务基线并开启产品定义</strong><small>只能引用已 BASELINED 的成果版本</small></div><div class="if-actor if-product">产品</div></div>
</div>
<div class="if-table-wrap"><table><thead><tr><th>ID</th><th>触发条件</th><th>发送 → 接收</th><th>正式载荷</th><th>约束/确认</th><th>对阶段的影响</th></tr></thead><tbody>
<tr><td>F1.1</td><td>收到新的客户或商业机会,且业务已完成原始信息收集、Schema 自审和内部确认。</td><td>业务 → 中心 AI</td><td>项目背景、里程碑、self_check_result、internal_approved、来源和草案版本。</td><td>中心 AI 不重复判断业务内容质量,只检查形式和跨来源一致性。</td><td>进入中心登记。</td></tr>
<tr><td>F1.2(条件流)</td><td>仅当 Schema/身份/版本/证据不合规,或与已登记正式信息发生冲突时触发;不存在上述问题时跳过。</td><td>中心 AI → 业务</td><td>形式错误、冲突来源、受影响字段和修正要求。</td><td>中心 AI 不能生成业务缺失项或替业务补写事实;无错误或冲突时不得发起此流。</td><td class="if-block">触发后,错误或冲突关闭前阻塞登记;未触发则直接进入 F1.3。</td></tr>
<tr><td>F1.3</td><td>业务成果通过中心 AI 的 Schema、身份、版本、证据和跨角色冲突检查并完成登记。</td><td>中心 AI → 产品</td><td>业务成果评审版本、artifact_id、version、status、evidence_refs、待确认项和回复时限。</td><td>状态必须标明 REVIEW_READY;不得冒充 BASELINED 正式成果,产品不得据此直接启动阶段 2。</td><td>产品获得早期风险识别所需上下文。</td></tr>
<tr><td>F1.4</td><td>产品审阅业务成果后,判断存在需要其他专业角色提前确认的风险。</td><td>产品 → 中心 AI</td><td>预检原因、预检问题、target_role_ids、相关需求字段、期望输出和时限;目标角色可为一个或多个。</td><td>是否预检、预检范围及对象角色均由产品负责指定;中心 AI 不代替产品判断。</td><td>按产品指定名单创建预检任务。</td></tr>
<tr><td>F1.5–1.6</td><td>产品预检请求格式完整,且指定的对象角色均为当前项目有效成员。</td><td>中心 AI ↔ 产品指定的对象角色</td><td>按对象角色拆分的预检问题、相关业务字段、成果版本、回复要求和时限;各对象角色返回内部确认后的本专业红线、风险、证据与待澄清项。</td><td>中心 AI 必须主动提醒全部关联角色,只校验、路由和汇总,不得自行增删产品指定名单。多角色结论冲突、影响较大或无法异步收敛时,建议由产品组织线下会议;参与角色统一确认的结论按 E.3 回传后,才能继续当前流程。</td><td>补充业务澄清和早期风险登记;复杂问题进入 E.1–E.4,闭环前保持阻塞。</td></tr>
<tr><td>F1.7</td><td>业务内部审查、中心形式校验和预检问题均已关闭。</td><td>业务 → 中心 AI</td><td>INTERNALLY_APPROVED 的需求和里程碑版本。</td><td>角色内部确认状态、时间和版本必须记录。</td><td class="if-pass">门禁可进入评审。</td></tr>
<tr><td>F1.8</td><td>业务确认完成,中心 AI 已登记 BASELINED 正式版本且阶段门禁 PASS。</td><td>中心 AI → 产品</td><td>正式业务成果、artifact_id、version、baseline_refs、evidence_refs、阶段 2 任务和确认时限。</td><td>只允许分发 BASELINED 当前有效版本;分发事件和接收状态必须登记,草稿或过期版本不得作为产品定义输入。</td><td class="if-pass">产品接收后开启阶段 2。</td></tr>
</tbody></table></div>
</section>
<section class="if-section" id="stage2">
<div class="if-section-head"><div class="if-section-no">02 / PRODUCT</div><div><h2>阶段 2:产品定义信息流</h2><p>产品形成完整立项资料,并让技术负责人、嵌入式应用层、嵌入式底层、硬件和测试从各自专业角度完成可行性确认。</p></div></div>
<div class="if-stage-summary"><div class="if-summary-card"><b>主责</b><span>产品;业务批准范围</span></div><div class="if-summary-card"><b>核心成果</b><span>立项资料、硬件/嵌入式资料、验收标准、里程碑要求</span></div><div class="if-summary-card"><b>门禁</b><span>产品确认方案,业务批准范围,约束和测试标准完整</span></div></div>
<div class="if-flow-board">
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F2.1</span><strong>下发产品定义任务</strong><small>附阶段 1 基线和全部红线约束</small></div><div class="if-actor if-product">产品</div></div>
<div class="if-exchange"><div class="if-actor if-product">产品</div><div class="if-arrow"><span class="if-flow-id">F2.2</span><strong>提交立项资料草案与专业评审请求</strong><small>产品明确评审角色、问题、范围和期望输出</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F2.3</span><strong>校验请求并并行路由专业评审</strong><small>按产品指定范围裁剪上下文,不新增专业问题</small></div><div class="if-actor if-tech">技术负责人 / 嵌入式应用层 / 嵌入式底层 / 硬件 / 测试</div></div>
<div class="if-exchange"><div class="if-actor if-tech">评审角色</div><div class="if-arrow"><span class="if-flow-id">F2.4</span><strong>返回可行性、缺口和约束</strong><small>每条意见必须关联具体成果字段</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange if-blocking"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F2.5</span><strong>汇总跨角色冲突并返回产品裁决/修订</strong><small>中心 AI 只指出冲突,不替产品作内容决定</small></div><div class="if-actor if-product">产品</div></div>
<div class="if-exchange"><div class="if-actor if-product">产品</div><div class="if-arrow"><span class="if-flow-id">F2.6</span><strong>提交内部确认后的产品方案</strong><small>关闭评审问题并标明保留风险</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F2.7</span><strong>请求业务批准范围</strong><small>呈现范围、里程碑、成本/风险影响</small></div><div class="if-actor if-business">业务</div></div>
<div class="if-exchange"><div class="if-actor if-business">业务</div><div class="if-arrow"><span class="if-flow-id">F2.8</span><strong>批准、驳回或附条件批准</strong><small>附条件项必须有责任人与期限</small></div><div class="if-actor if-center">中心 AI</div></div>
</div>
<div class="if-table-wrap"><table><thead><tr><th>ID</th><th>触发条件</th><th>发送 → 接收</th><th>正式载荷</th><th>约束/确认</th><th>对阶段的影响</th></tr></thead><tbody>
<tr><td>F2.1–2.2</td><td>阶段 2 启动。</td><td>中心 AI ↔ 产品</td><td>需求基线、立项书、规格、功能/灯态/产测清单、版本说明、板框图、验收标准,以及产品定义的评审角色/问题/范围。</td><td>产品先自审,所有资料必须引用阶段 1 基线;专业评审由产品发起。</td><td>进入专业评审路由。</td></tr>
<tr><td>F2.3</td><td>产品提交可评审草案和评审请求。</td><td>中心 AI → 技术负责人 / 嵌入式应用层 / 嵌入式底层 / 硬件 / 测试</td><td>产品定义的角色化评审任务和相关资料。</td><td>中心 AI 只校验和路由;技术负责人看总体,嵌入式应用层看功能,嵌入式底层看 BSP/HAL,硬件看器件/板框,测试看可测性。</td><td>并行评审。</td></tr>
<tr><td>F2.4</td><td>各角色完成评审。</td><td>技术负责人 / 嵌入式应用层 / 嵌入式底层 / 硬件 / 测试 → 中心 AI</td><td>结论、缺失、风险、证据、建议和是否阻塞。</td><td>不得只回复“可行/不可行”。</td><td>形成冲突矩阵。</td></tr>
<tr><td>F2.5–2.6</td><td>存在冲突或缺口。</td><td>中心 AI ↔ 产品</td><td>字段级修订清单、变更影响、关闭说明。</td><td>产品在内部确认最终版本后统一发出。</td><td class="if-block">问题未关闭则 HOLD。</td></tr>
<tr><td>F2.7–2.8</td><td>产品方案专业评审完成。</td><td>中心 AI ↔ 业务</td><td>范围、验收、里程碑、风险和附条件事项。</td><td>范围批准由业务完成内部授权后统一给出。</td><td class="if-pass">批准后进入阶段 3。</td></tr>
</tbody></table></div>
</section>
<section class="if-section" id="stage3">
<div class="if-section-head"><div class="if-section-no">03 / PLANNING</div><div><h2>阶段 3:项目规划信息流</h2><p>中心 AI 根据有效基线生成任务计划草案,先由技术负责人确认整体任务结构、技术顺序和依赖;仅对存在疑点的任务,定向向对应执行角色补充确认。</p></div></div>
<div class="if-stage-summary"><div class="if-summary-card"><b>主责</b><span>中心 AI;技术负责人确认任务计划</span></div><div class="if-summary-card"><b>核心成果</b><span>项目计划、风险登记、角色分工、产品资料整合、项目状态内部记录</span></div><div class="if-summary-card"><b>门禁</b><span>技术负责人确认整体计划;问题任务完成定向确认;业务确认资源与日期基线</span></div></div>
<div class="if-flow-board">
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F3.1</span><strong>提交任务计划草案,请求整体确认</strong><small>附 WBS、技术顺序、依赖、初始排期、风险和基线引用</small></div><div class="if-actor if-tech">技术负责人</div></div>
<div class="if-exchange"><div class="if-actor if-tech">技术负责人</div><div class="if-arrow"><span class="if-flow-id">F3.2</span><strong>确认整体任务计划或返回调整意见</strong><small>确认任务结构、技术顺序、依赖关系和关键风险</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange if-conditional"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F3.3 · 问题任务</span><strong>仅对存在疑点的任务发起定向确认</strong><small>缺少责任、输入输出、依赖、估算、资源、日期、证据或存在冲突时触发</small></div><div class="if-actor if-product">对应执行角色</div></div>
<div class="if-exchange if-conditional"><div class="if-actor if-product">对应执行角色</div><div class="if-arrow"><span class="if-flow-id">F3.4</span><strong>只确认被询问任务的工期、资源和风险</strong><small>不得要求执行角色重复确认整份任务计划</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange if-blocking"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F3.5</span><strong>提醒关联角色并协调剩余冲突、重排计划</strong><small>问题仍未关闭或日期不可达时按 E.1–E.4 处理</small></div><div class="if-actor if-product">技术负责人及受影响角色</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F3.6</span><strong>请求资源与日期基线授权</strong><small>真实人员、采购、打样、成本和日期由业务内部确认</small></div><div class="if-actor if-business">业务</div></div>
<div class="if-exchange"><div class="if-actor if-business">业务</div><div class="if-arrow"><span class="if-flow-id">F3.7</span><strong>批准或要求调整资源与日期基线</strong><small>记录内部批准状态、范围和条件</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F3.8</span><strong>向相关执行角色发布基线计划和正式任务</strong><small>附任务 ID、依赖、交付物和门禁;后续状态按 S.1–S.4 同步</small></div><div class="if-actor if-product">相关执行角色</div></div>
</div>
<div class="if-table-wrap"><table><thead><tr><th>ID</th><th>触发条件</th><th>发送 → 接收</th><th>正式载荷</th><th>约束/确认</th><th>对阶段的影响</th></tr></thead><tbody>
<tr><td>F3.1–3.2</td><td>阶段 2 门禁通过,中心 AI 已依据有效基线形成任务计划草案。</td><td>中心 AI ↔ 技术负责人</td><td>WBS、任务结构、技术顺序、关键路径、输入输出、依赖、初始排期、风险和基线引用。</td><td>技术负责人确认整体任务计划;中心 AI 不直接向全部执行角色征求整份计划确认。</td><td>形成技术负责人确认的任务计划草案。</td></tr>
<tr><td>F3.3–3.4(条件流)</td><td>中心 AI 发现任务缺少责任角色、输入、输出、依赖、估算、资源、日期或证据;任务之间存在冲突;或技术负责人明确标记待确认项。</td><td>中心 AI ↔ 对应执行角色</td><td>仅包含问题任务的 task_id、疑点字段、上下游依赖、基线引用、待确认问题、回复要求和时限;执行角色返回该任务的工期、资源、风险、依据和 internal_approved。</td><td>必须定向发送给该任务的执行角色,不得广播给全部角色,也不得要求执行角色重复确认无疑点任务。</td><td>补齐或修正问题任务;未触发时直接跳过。</td></tr>
<tr><td>F3.5</td><td>定向确认后仍存在资源冲突、关键路径冲突或日期不可达。</td><td>中心 AI ↔ 技术负责人及受影响角色</td><td>冲突点、可选排法、阶段影响、关联角色和响应时限。</td><td>中心 AI 必须主动提醒,不得静默覆盖技术负责人或执行角色的确认;复杂冲突按 E.1–E.4 闭环。</td><td class="if-block">保持计划草案,冲突关闭前不得基线化。</td></tr>
<tr><td>F3.6–3.7</td><td>技术负责人已确认整体计划,问题任务和剩余冲突均已关闭,计划涉及真实人员、采购、打样、成本或日期基线。</td><td>中心 AI ↔ 业务</td><td>任务计划、资源请求、成本、里程碑和风险。</td><td>中心 AI 无权自行批准现实资源和日期;业务完成内部授权后统一回复。</td><td class="if-pass">批准后可基线化。</td></tr>
<tr><td>F3.8</td><td>技术负责人确认有效、问题任务已关闭且业务批准资源与日期基线。</td><td>中心 AI → 相关执行角色</td><td>BASELINED 计划、角色相关正式任务、风险登记和初始 Project Status Snapshot。</td><td>每个角色只接收与自身任务和依赖相关的计划内容;后续变更走 Change Request,执行状态按 S.1–S.4 同步。</td><td class="if-pass">相关角色接收任务,开启阶段 4。</td></tr>
</tbody></table></div>
</section>
<section class="if-section" id="stage4">
<div class="if-section-head"><div class="if-section-no">04 / DESIGN</div><div><h2>阶段 4:方案设计信息流</h2><p>技术负责人组织总体方案;产品、嵌入式应用层、嵌入式底层、硬件和测试围绕同一份软硬件接口契约完成多轮确认。</p></div></div>
<div class="if-stage-summary"><div class="if-summary-card"><b>主责</b><span>技术负责人</span></div><div class="if-summary-card"><b>核心成果</b><span>总体技术方案、软硬件接口契约、硬件设计包、架构决策、测试计划</span></div><div class="if-summary-card"><b>门禁</b><span>方案确认、接口可实施、需求不偏离、测试可验证</span></div></div>
<div class="if-flow-board">
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F4.1</span><strong>下发总体方案设计任务</strong><small>附产品基线、计划、风险和所有约束</small></div><div class="if-actor if-tech">技术负责人</div></div>
<div class="if-exchange"><div class="if-actor if-tech">技术负责人</div><div class="if-arrow"><span class="if-flow-id">F4.2</span><strong>提交总体方案与接口契约草案</strong><small>含边界、关键决策、非功能和风险</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F4.3</span><strong>并行分发角色化设计任务</strong><small>每个角色在同一契约版本上工作</small></div><div class="if-actor if-app">产品 / 嵌入式应用层 / 嵌入式底层 / 硬件 / 测试</div></div>
<div class="if-exchange if-conditional"><div class="if-actor if-app">嵌入式应用层</div><div class="if-arrow"><span class="if-flow-id">F4.4</span><strong>提出 HAL、资源和应用接口需求</strong><small>接口新增或资源不足时触发</small></div><div class="if-actor if-center">中心 AI → 嵌入式底层 / 技术负责人</div></div>
<div class="if-exchange if-conditional"><div class="if-actor if-low">嵌入式底层</div><div class="if-arrow"><span class="if-flow-id">F4.5</span><strong>提出引脚、电平、时序和器件约束</strong><small>HAL 与硬件设计存在依赖时</small></div><div class="if-actor if-center">中心 AI → 硬件 / 技术负责人</div></div>
<div class="if-exchange if-conditional"><div class="if-actor if-hardware">硬件</div><div class="if-arrow"><span class="if-flow-id">F4.6</span><strong>反馈器件、PCB、电源、空间与热约束</strong><small>影响接口、成本或性能时</small></div><div class="if-actor if-center">中心 AI → 技术负责人 / 嵌入式底层 / 产品</div></div>
<div class="if-exchange if-blocking"><div class="if-actor if-test">测试</div><div class="if-arrow"><span class="if-flow-id">F4.7</span><strong>提交不可测试项和环境/治具缺口</strong><small>验收标准无法验证时</small></div><div class="if-actor if-center">中心 AI → 相关角色</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F4.8</span><strong>汇总冲突并要求总体方案修订</strong><small>形成决策清单和待关闭问题</small></div><div class="if-actor if-tech">技术负责人</div></div>
<div class="if-exchange"><div class="if-actor if-tech">技术负责人</div><div class="if-arrow"><span class="if-flow-id">F4.9</span><strong>提交内部确认的最终方案和契约基线</strong><small>所有阻塞问题关闭后</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F4.10</span><strong>请求各角色确认接口可实施</strong><small>嵌入式应用层、嵌入式底层、硬件、产品、测试分别确认</small></div><div class="if-actor if-product">产品 / 嵌入式应用层 / 嵌入式底层 / 硬件 / 测试</div></div>
</div>
<div class="if-table-wrap"><table><thead><tr><th>ID</th><th>触发条件</th><th>发送 → 接收</th><th>正式载荷</th><th>约束/确认</th><th>对阶段的影响</th></tr></thead><tbody>
<tr><td>F4.1–4.3</td><td>阶段 4 启动且计划基线有效。</td><td>中心 AI ↔ 技术负责人/协作角色</td><td>总体方案草案、角色化设计任务、统一契约版本。</td><td>所有设计必须引用同一 product/plan baseline。</td><td>开启并行设计。</td></tr>
<tr><td>F4.4</td><td>嵌入式应用层需要新的资源、HAL 或协议。</td><td>嵌入式应用层 → 中心 AI → 嵌入式底层 / 技术负责人</td><td>接口需求、调用频率、性能和资源指标。</td><td>不得绕过契约直接约定。</td><td>可能触发契约修订。</td></tr>
<tr><td>F4.5</td><td>嵌入式底层依赖引脚、电平、器件或时序。</td><td>嵌入式底层 → 中心 AI → 硬件 / 技术负责人</td><td>HAL、引脚表、时序、电气约束。</td><td>硬件回复必须引用板卡版本。</td><td class="if-block">冲突时阻塞相关设计。</td></tr>
<tr><td>F4.6</td><td>器件、PCB、电源、空间或热约束影响方案。</td><td>硬件 → 中心 AI → 技术负责人 / 嵌入式底层 / 产品</td><td>限制、替代方案、成本、性能和交期影响。</td><td>器件替换不得静默发生。</td><td>形成 ADR/Change Request。</td></tr>
<tr><td>F4.7</td><td>测试无法验证某项需求或缺环境/治具。</td><td>测试 → 中心 AI → 相关角色</td><td>不可测项、证据要求、环境和治具需求。</td><td>必须在实现前关闭。</td><td class="if-block">门禁 HOLD。</td></tr>
<tr><td>F4.8–4.10</td><td>专业评审完成。</td><td>中心 AI ↔ 技术负责人 ↔ 产品 / 嵌入式应用层 / 嵌入式底层 / 硬件 / 测试</td><td>修订方案、最终契约和角色内部确认状态。</td><td>每个确认绑定同一版本。</td><td class="if-pass">全部确认后进入阶段 5。</td></tr>
</tbody></table></div>
</section>
<section class="if-section" id="stage5">
<div class="if-section-head"><div class="if-section-no">05 / IMPLEMENT</div><div><h2>阶段 5:嵌入式软硬件实现信息流</h2><p>三条实现流并行推进,但固件只有一条正式出口:嵌入式底层先提供可消费版本,嵌入式应用层完成合版并提交统一固件,中心 AI 维护其与硬件版本的唯一映射。</p></div></div>
<div class="if-stage-summary"><div class="if-summary-card"><b>并行主责</b><span>嵌入式应用层、嵌入式底层、硬件</span></div><div class="if-summary-card"><b>核心成果</b><span>硬件实现资料、嵌入式底层实现资料、嵌入式应用层提交的统一固件包</span></div><div class="if-summary-card"><b>门禁</b><span>嵌入式应用层提交最终固件,技术负责人确认固件与硬件版本匹配并可提测</span></div></div>
<div class="if-flow-board">
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F5.1</span><strong>并行下发三类实现任务</strong><small>锁定总体方案、契约和任务边界</small></div><div class="if-actor if-app">嵌入式应用层 / 嵌入式底层 / 硬件</div></div>
<div class="if-exchange"><div class="if-actor if-hardware">硬件</div><div class="if-arrow"><span class="if-flow-id">F5.2</span><strong>提交板卡、BOM、接口与样机版本</strong><small>硬件发布或 ECO 时触发</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F5.3</span><strong>广播硬件版本及约束变化</strong><small>只向受影响角色分发完整影响包</small></div><div class="if-actor if-low">技术负责人 / 嵌入式底层 / 嵌入式应用层 / 测试</div></div>
<div class="if-exchange"><div class="if-actor if-low">嵌入式底层</div><div class="if-arrow"><span class="if-flow-id">F5.4</span><strong>提交 BSP/PSP、驱动与测试固件</strong><small>带板卡、HAL 和接口契约版本</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F5.5</span><strong>将已登记的嵌入式底层版本交给嵌入式应用层合版</strong><small>测试不得直接将底层产物作为提测固件</small></div><div class="if-actor if-app">嵌入式应用层</div></div>
<div class="if-exchange"><div class="if-actor if-app">嵌入式应用层</div><div class="if-arrow"><span class="if-flow-id">F5.6</span><strong>集成底层版本并提交统一固件候选</strong><small>绑定应用、底层、板卡和契约版本,包含 MD5、分区、自测和 Changelog</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange if-blocking"><div class="if-actor if-app">任一实现角色</div><div class="if-arrow"><span class="if-flow-id">F5.7</span><strong>报告接口、器件或资源冲突</strong><small>契约不一致、资源超限或 ECO 影响时</small></div><div class="if-actor if-center">中心 AI → 技术负责人</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F5.8</span><strong>创建软硬件联调与版本矩阵确认任务</strong><small>统一固件候选和硬件候选版本均可用时</small></div><div class="if-actor if-app">嵌入式应用层 / 硬件 / 技术负责人</div></div>
<div class="if-exchange"><div class="if-actor if-app">嵌入式应用层</div><div class="if-arrow"><span class="if-flow-id">F5.9</span><strong>提交最终提测固件版本</strong><small>最终固件只能由嵌入式应用层提交,附完整版本链、自测和偏离说明</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-tech">技术负责人</div><div class="if-arrow"><span class="if-flow-id">F5.10</span><strong>确认最终固件与硬件版本匹配并可提测</strong><small>板卡、BOM、统一固件及其应用/底层版本链完全匹配</small></div><div class="if-actor if-center">中心 AI</div></div>
</div>
<div class="if-table-wrap"><table><thead><tr><th>ID</th><th>触发条件</th><th>发送 → 接收</th><th>正式载荷</th><th>约束/确认</th><th>对阶段的影响</th></tr></thead><tbody>
<tr><td>F5.1</td><td>方案设计门禁通过。</td><td>中心 AI → 嵌入式应用层 / 嵌入式底层 / 硬件</td><td>任务、契约、版本、输出格式、依赖和期限。</td><td>三方必须使用相同契约版本。</td><td>启动并行实现。</td></tr>
<tr><td>F5.2–5.3</td><td>硬件发布样机、板卡、BOM 或 ECO。</td><td>硬件 → 中心 AI → 技术负责人 / 嵌入式底层 / 嵌入式应用层 / 测试</td><td>板卡/BOM/ECO、接口、限制和影响范围。</td><td>没有影响分析不得切换硬件版本。</td><td class="if-block">影响接口时阻塞受影响任务。</td></tr>
<tr><td>F5.4–5.5</td><td>嵌入式底层形成可消费版本。</td><td>嵌入式底层 → 中心 AI → 嵌入式应用层</td><td>BSP/PSP、HAL、驱动、底层测试固件、适配板卡和契约版本。</td><td>必须声明适配板卡和契约版本;测试不得把底层产物直接作为提测固件。</td><td>允许嵌入式应用层合版。</td></tr>
<tr><td>F5.6</td><td>嵌入式应用层收到已登记的嵌入式底层可消费版本。</td><td>嵌入式应用层 → 中心 AI</td><td>统一固件候选、应用版本、底层版本、板卡版本、契约版本、MD5、分区、日志、自测和 Changelog。</td><td>必须形成可追溯的应用层+底层统一版本;禁止提交无法复现的单一二进制。</td><td>进入软硬件联调候选。</td></tr>
<tr><td>F5.7</td><td>引脚、时序、资源、器件或协议冲突。</td><td>角色 → 中心 AI → 技术负责人</td><td>冲突、证据、版本、影响和建议。</td><td>中心 AI 立即冻结受影响版本。</td><td class="if-block">等待技术裁决或契约新版本。</td></tr>
<tr><td>F5.8–5.10</td><td>统一固件候选和硬件候选均已提交,联调问题已经关闭。</td><td>中心 AI ↔ 嵌入式应用层 / 硬件 / 技术负责人</td><td>最终固件、应用/底层版本链、板卡/BOM/ECO、版本矩阵、联调记录、自测、偏离说明和集成审批。</td><td>最终提测固件只能由嵌入式应用层提交;嵌入式底层和硬件不得直接向测试提交固件。技术负责人确认版本矩阵匹配。</td><td class="if-pass">确认后由中心 AI 将该唯一固件版本交给测试。</td></tr>
</tbody></table></div>
</section>
<section class="if-section" id="stage6">
<div class="if-section-head"><div class="if-section-no">06 / TEST</div><div><h2>阶段 6:测试验证与缺陷返工信息流</h2><p>测试提交证据和缺陷,中心 AI 按问题层级路由到产品、嵌入式应用层、嵌入式底层、硬件或技术负责人,并控制重新提测。</p></div></div>
<div class="if-stage-summary"><div class="if-summary-card"><b>主责</b><span>测试</span></div><div class="if-summary-card"><b>核心成果</b><span>功能、可靠性、专项测试报告,测试证据和缺陷分析</span></div><div class="if-summary-card"><b>门禁</b><span>阻断缺陷清零,证据完整,测试完成内部确认并提交质量结论</span></div></div>
<div class="if-flow-board">
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F6.1</span><strong>下发测试任务、最终固件和版本矩阵</strong><small>只允许测试由嵌入式应用层提交并经技术负责人批准的统一固件</small></div><div class="if-actor if-test">测试</div></div>
<div class="if-exchange"><div class="if-actor if-test">测试</div><div class="if-arrow"><span class="if-flow-id">F6.2</span><strong>提交测试报告、证据和缺陷</strong><small>每个缺陷关联版本、用例和证据</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F6.3</span><strong>分类并路由缺陷</strong><small>嵌入式应用层/嵌入式底层/硬件/架构/需求/测试环境</small></div><div class="if-actor if-app">产品 / 技术负责人 / 嵌入式应用层 / 嵌入式底层 / 硬件 / 测试</div></div>
<div class="if-exchange"><div class="if-actor if-app">责任角色</div><div class="if-arrow"><span class="if-flow-id">F6.4</span><strong>返回内部确认的原因分析、修复计划和影响范围</strong><small>正式修复承诺在角色内部确认后统一发出</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange if-blocking"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F6.5</span><strong>创建修复或技术裁决任务</strong><small>跨层问题交技术负责人,需求问题交产品</small></div><div class="if-actor if-tech">责任角色</div></div>
<div class="if-exchange"><div class="if-actor if-app">责任角色</div><div class="if-arrow"><span class="if-flow-id">F6.6</span><strong>提交应用修复、底层修复或硬件 ECO</strong><small>必须生成新版本并声明 supersedes 和影响范围</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange if-conditional"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F6.7</span><strong>将固件相关修复交给嵌入式应用层重新合版</strong><small>底层修复必须合版;硬件变化由嵌入式应用层确认固件兼容性</small></div><div class="if-actor if-app">嵌入式应用层</div></div>
<div class="if-exchange"><div class="if-actor if-app">嵌入式应用层</div><div class="if-arrow"><span class="if-flow-id">F6.8</span><strong>提交统一回归固件或确认原固件继续有效</strong><small>最终回归固件仍只能由嵌入式应用层提交</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F6.9</span><strong>下发定向回归任务和统一固件</strong><small>附修复范围、受影响用例和新版本矩阵</small></div><div class="if-actor if-test">测试</div></div>
<div class="if-exchange"><div class="if-actor if-test">测试</div><div class="if-arrow"><span class="if-flow-id">F6.10</span><strong>提交内部确认的最终质量结论和剩余风险</strong><small>报告和证据完整后</small></div><div class="if-actor if-center">中心 AI</div></div>
</div>
<div class="if-table-wrap"><table><thead><tr><th>ID</th><th>触发条件</th><th>发送 → 接收</th><th>正式载荷</th><th>约束/确认</th><th>对阶段的影响</th></tr></thead><tbody>
<tr><td>F6.1–6.2</td><td>技术负责人批准嵌入式应用层提交的最终固件与硬件集成版本。</td><td>中心 AI ↔ 测试</td><td>嵌入式应用层提交的统一固件、应用/底层版本链、板卡/BOM/ECO、版本矩阵、测试任务、报告、证据和缺陷。</td><td>测试只能接收嵌入式应用层提交且经技术负责人批准的最终固件;缺陷必须关联可复现环境和版本。</td><td>启动测试并更新测试状态。</td></tr>
<tr><td>F6.3</td><td>发现缺陷或测试异常。</td><td>中心 AI → 产品 / 技术负责人 / 嵌入式应用层 / 嵌入式底层 / 硬件 / 测试</td><td>缺陷、证据、严重度、归属依据和响应时限。</td><td>不确定归属时先交技术负责人/产品裁决。</td><td>建立返工任务。</td></tr>
<tr><td>F6.4–6.5</td><td>责任角色收到缺陷。</td><td>责任角色 ↔ 中心 AI</td><td>根因、影响、修复、风险、需重测范围。</td><td>不得以“无法复现”结案,必须附证据。</td><td class="if-block">阻断缺陷使阶段 BLOCKED。</td></tr>
<tr><td>F6.6–6.8</td><td>应用修复、嵌入式底层修复或硬件 ECO 已提交。</td><td>责任角色 → 中心 AI → 嵌入式应用层 → 中心 AI</td><td>修复产物、变更记录、修复证据、影响范围、应用/底层/硬件版本链,以及嵌入式应用层提交的统一回归固件或原固件继续有效的确认。</td><td>嵌入式底层修复不得直接交给测试;必须由嵌入式应用层重新合版。硬件变化即使不修改固件,也必须由嵌入式应用层确认最终固件版本。</td><td>形成唯一可回归的固件与硬件版本矩阵。</td></tr>
<tr><td>F6.9</td><td>统一回归固件和硬件版本矩阵通过中心 AI 校验,必要时由技术负责人确认。</td><td>中心 AI → 测试</td><td>统一回归固件、版本矩阵、修复范围、受影响用例、回归任务和时限。</td><td>旧版本不得覆盖;测试不得接收嵌入式底层单独提交的固件。</td><td>执行定向回归。</td></tr>
<tr><td>F6.10</td><td>阻断缺陷清零且证据齐全。</td><td>测试 → 中心 AI</td><td>INTERNALLY_APPROVED 质量报告和剩余风险。</td><td>测试内部确认状态必须记录;剩余风险必须有接受方。</td><td class="if-pass">门禁通过后开启验收。</td></tr>
</tbody></table></div>
</section>
<section class="if-section" id="stage7">
<div class="if-section-head"><div class="if-section-no">07 / ACCEPT</div><div><h2>阶段 7:业务验收信息流</h2><p>产品组织平台和客户验收,中心 AI 收集业务/客户结论,并把驳回问题精确路由到对应层级。</p></div></div>
<div class="if-stage-summary"><div class="if-summary-card"><b>主责</b><span>产品;中心 AI 组织;业务批准</span></div><div class="if-summary-card"><b>核心成果</b><span>平台提测通过报告、客户验收通过报告、附条件事项</span></div><div class="if-summary-card"><b>门禁</b><span>业务完成内部确认并提交验收及上线条件</span></div></div>
<div class="if-flow-board">
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F7.1</span><strong>下发验收组织任务和证据包</strong><small>测试门禁通过后</small></div><div class="if-actor if-product">产品</div></div>
<div class="if-exchange"><div class="if-actor if-product">产品</div><div class="if-arrow"><span class="if-flow-id">F7.2</span><strong>提交平台/客户验收资料</strong><small>引用测试证据、版本和已知限制</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F7.3</span><strong>发起验收确认</strong><small>呈现目标、证据、风险和附条件项</small></div><div class="if-actor if-business">业务/客户</div></div>
<div class="if-exchange"><div class="if-actor if-business">业务/客户</div><div class="if-arrow"><span class="if-flow-id">F7.4</span><strong>返回通过、驳回或附条件通过</strong><small>驳回必须描述场景和证据</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange if-blocking"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F7.5</span><strong>分类并路由验收问题</strong><small>需求/嵌入式应用层/嵌入式底层/硬件/架构/证据</small></div><div class="if-actor if-app">产品 / 技术负责人 / 嵌入式应用层 / 嵌入式底层 / 硬件 / 测试</div></div>
<div class="if-exchange"><div class="if-actor if-app">责任角色</div><div class="if-arrow"><span class="if-flow-id">F7.6</span><strong>提交修正、说明或风险处置</strong><small>形成新成果版本或正式解释</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F7.7</span><strong>组织重新验证和再次验收</strong><small>只返回受影响阶段,不重启整个项目</small></div><div class="if-actor if-product">产品/测试/业务</div></div>
<div class="if-exchange"><div class="if-actor if-business">业务</div><div class="if-arrow"><span class="if-flow-id">F7.8</span><strong>提交内部确认的最终验收和上线条件</strong><small>附条件项必须有责任人和期限</small></div><div class="if-actor if-center">中心 AI</div></div>
</div>
<div class="if-table-wrap"><table><thead><tr><th>ID</th><th>触发条件</th><th>发送 → 接收</th><th>正式载荷</th><th>约束/确认</th><th>对阶段的影响</th></tr></thead><tbody>
<tr><td>F7.1–7.2</td><td>测试门禁通过。</td><td>中心 AI ↔ 产品</td><td>验收任务、平台/客户报告、证据、版本、限制。</td><td>产品不得隐藏已知风险。</td><td>形成验收包。</td></tr>
<tr><td>F7.3–7.4</td><td>验收包完整。</td><td>中心 AI ↔ 业务/客户</td><td>目标对照、演示、证据、验收结论。</td><td>口头反馈必须结构化回传。</td><td>决定通过或返工。</td></tr>
<tr><td>F7.5</td><td>验收驳回。</td><td>中心 AI → 产品 / 技术负责人 / 嵌入式应用层 / 嵌入式底层 / 硬件 / 测试</td><td>问题类别、证据、影响成果和返回阶段。</td><td>禁止笼统地全部退回实现阶段。</td><td class="if-block">阶段保持 ACTIVE。</td></tr>
<tr><td>F7.6–7.7</td><td>责任角色完成修正。</td><td>角色 → 中心 AI → 产品/测试/业务</td><td>新版本、解释、验证结果和再次验收任务。</td><td>需要测试时必须先回测试阶段。</td><td>重新验收。</td></tr>
<tr><td>F7.8</td><td>验收通过或条件明确。</td><td>业务 → 中心 AI</td><td>内部确认后的验收结论、上线条件、附条件责任人/期限。</td><td>内部确认状态必须记录,条件不可为空泛。</td><td class="if-pass">开启发布结项。</td></tr>
</tbody></table></div>
</section>
<section class="if-section" id="stage8">
<div class="if-section-head"><div class="if-section-no">08 / CLOSE</div><div><h2>阶段 8:发布结项信息流</h2><p>中心 AI 汇总应用、固件、板卡、BOM/ECO、测试和验收信息,形成完整归档和流程闭环。</p></div></div>
<div class="if-stage-summary"><div class="if-summary-card"><b>协调</b><span>中心 AI;技术负责人技术确认</span></div><div class="if-summary-card"><b>核心成果</b><span>结项资料归档、变更记录、结项报告、流程闭环总结</span></div><div class="if-summary-card"><b>门禁</b><span>版本匹配、冒烟通过、遗留事项有承接、归档完整</span></div></div>
<div class="if-flow-board">
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F8.1</span><strong>请求最终版本矩阵确认</strong><small>嵌入式应用层提交的最终固件必须绑定嵌入式底层、板卡和 BOM/ECO 版本</small></div><div class="if-actor if-tech">技术负责人</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F8.2</span><strong>下发发布与归档任务</strong><small>按角色明确最终交付清单</small></div><div class="if-actor if-app">嵌入式应用层 / 嵌入式底层 / 硬件</div></div>
<div class="if-exchange"><div class="if-actor if-app">嵌入式应用层 / 嵌入式底层 / 硬件</div><div class="if-arrow"><span class="if-flow-id">F8.3</span><strong>分别提交最终固件、底层归档资料和 BOM/ECO</strong><small>最终固件只由嵌入式应用层提交;嵌入式底层不单独发布固件</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F8.4</span><strong>请求发布冒烟与最终验证</strong><small>使用最终版本矩阵</small></div><div class="if-actor if-test">测试</div></div>
<div class="if-exchange"><div class="if-actor if-test">测试</div><div class="if-arrow"><span class="if-flow-id">F8.5</span><strong>提交冒烟结果和最终证据</strong><small>失败立即停止发布/结项</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F8.6</span><strong>核对验收条件与遗留事项</strong><small>附条件项必须关闭或有承接</small></div><div class="if-actor if-product">产品/业务</div></div>
<div class="if-exchange"><div class="if-actor if-product">全部角色</div><div class="if-arrow"><span class="if-flow-id">F8.7</span><strong>提交内部确认的结项摘要和资料索引</strong><small>禁止只提供聊天记录</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">F8.8</span><strong>生成归档、变更记录和结项报告</strong><small>验证追踪链和缺失项</small></div><div class="if-actor if-center">归档库</div></div>
<div class="if-exchange"><div class="if-actor if-business">业务</div><div class="if-arrow"><span class="if-flow-id">F8.9</span><strong>确认结项或要求补充</strong><small>组织级结项由业务内部完成授权</small></div><div class="if-actor if-center">中心 AI</div></div>
</div>
<div class="if-table-wrap"><table><thead><tr><th>ID</th><th>触发条件</th><th>发送 → 接收</th><th>正式载荷</th><th>约束/确认</th><th>对阶段的影响</th></tr></thead><tbody>
<tr><td>F8.1–8.3</td><td>业务验收门禁通过。</td><td>中心 AI ↔ 技术负责人 / 嵌入式应用层 / 嵌入式底层 / 硬件</td><td>嵌入式应用层提交的最终固件及应用/底层版本链、嵌入式底层归档资料、板卡/BOM/ECO、最终版本矩阵、发布清单和角色发布声明。</td><td>最终固件只由嵌入式应用层提交;嵌入式底层只提交被集成版本和归档资料;技术负责人确认整体版本匹配。</td><td>形成发布候选。</td></tr>
<tr><td>F8.4–8.5</td><td>发布候选锁定。</td><td>中心 AI ↔ 测试</td><td>冒烟任务、结果和最终证据。</td><td>不得使用非最终版本。</td><td class="if-block">失败则停止发布并路由修复。</td></tr>
<tr><td>F8.6</td><td>冒烟通过。</td><td>中心 AI ↔ 产品/业务</td><td>验收条件、附条件项、遗留风险。</td><td>每项必须关闭或有责任人/期限。</td><td>准备结项。</td></tr>
<tr><td>F8.7–8.8</td><td>角色工作完成。</td><td>角色 → 中心 AI → 归档库</td><td>资料索引、最终成果、变更记录、结项总结。</td><td>不能用聊天记录替代成果索引。</td><td>生成结项包。</td></tr>
<tr><td>F8.9</td><td>需要组织级结项确认。</td><td>业务 → 中心 AI</td><td>完成内部授权的结项批准或补充清单。</td><td>内部授权状态必须记录;补充项未关闭不得 CLOSED。</td><td class="if-pass">批准后归档并关闭项目。</td></tr>
</tbody></table></div>
</section>
<section class="if-section" id="change">
<div class="if-section-head"><div class="if-section-no">X / CROSS-CUTTING</div><div><h2>贯穿全阶段:变更、阻塞与超时信息流</h2><p>Change Request 和异常升级不属于某个单一阶段;中心 AI 必须暂停受影响任务,组织并行影响评估,再决定是否重开阶段。</p></div></div>
<div class="if-cross-flow">
<div class="if-flow-board">
<div class="if-exchange"><div class="if-actor if-product">任意角色</div><div class="if-arrow"><span class="if-flow-id">X.1</span><strong>提交 Change Request 或 Blocker</strong><small>附原因、证据、受影响版本和紧急度</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">X.2</span><strong>冻结受影响任务并并行征询影响</strong><small>产品/技术负责人/嵌入式应用层/嵌入式底层/硬件/测试</small></div><div class="if-actor if-tech">产品 / 技术负责人 / 嵌入式应用层 / 嵌入式底层 / 硬件 / 测试</div></div>
<div class="if-exchange"><div class="if-actor if-tech">评估角色</div><div class="if-arrow"><span class="if-flow-id">X.3</span><strong>返回内部确认的范围、方案、工期、成本和测试影响</strong><small>现实承诺由角色内部确认后统一发出</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">X.4</span><strong>提交综合影响和建议</strong><small>列出批准/拒绝/延期的差异</small></div><div class="if-actor if-business">业务</div></div>
<div class="if-exchange"><div class="if-actor if-business">业务</div><div class="if-arrow"><span class="if-flow-id">X.5</span><strong>批准或拒绝变更</strong><small>内部授权后生成新基线,不覆盖旧版本</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange if-blocking"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">X.6</span><strong>超时提醒与升级</strong><small>专业事项升级给对应角色;项目授权升级给业务</small></div><div class="if-actor if-product">对应角色</div></div>
</div>
</div>
<div class="if-table-wrap"><table><thead><tr><th>ID</th><th>触发条件</th><th>发送 → 接收</th><th>约束</th><th>状态结果</th></tr></thead><tbody>
<tr><td>X.1</td><td>范围、接口、器件、版本、资源或日期发生变化;或出现无法继续的阻塞。</td><td>任意角色 → 中心 AI</td><td>必须关联当前基线和受影响任务。</td><td>创建变更/阻塞记录。</td></tr>
<tr><td>X.2–X.3</td><td>中心确认变更可能影响正式成果。</td><td>中心 AI ↔ 产品 / 技术负责人 / 嵌入式应用层 / 嵌入式底层 / 硬件 / 测试</td><td>各角色只评估自己的责任范围;现实承诺在角色内部确认后统一发出。</td><td class="if-block">受影响任务冻结。</td></tr>
<tr><td>X.4–X.5</td><td>影响评估齐全。</td><td>中心 AI ↔ 业务</td><td>不得隐藏已完成工作损失、成本或里程碑影响。</td><td>批准:新基线并重开受影响阶段;拒绝:维持原基线。</td></tr>
<tr><td>X.6</td><td>确认或任务超过 required_by。</td><td>中心 AI → 对应角色;项目授权事项发给业务</td><td>升级消息包含逾期时长、影响和替代方案;接收角色在内部完成确认后统一回复。</td><td class="if-block">保持 WAITING_CONFIRMATION/BLOCKED。</td></tr>
</tbody></table></div>
</section>
<section class="if-section" id="waiver">
<div class="if-section-head"><div class="if-section-no">Y / ARTIFACT WAIVER</div><div><h2>贯穿全阶段:成果文件缺省与豁免信息流</h2><p>成果文件默认必须输出。只有主责角色能够发起“不适用”申请;中心 AI 负责项目级审查、影响确认、批准登记和门禁更新,禁止任何角色静默跳过。</p></div></div>
<div class="if-stage-summary"><div class="if-summary-card"><b>发起者</b><span>该成果在流程中的主责角色</span></div><div class="if-summary-card"><b>项目确认</b><span>中心 AI;高影响豁免还需相关角色和业务完成内部确认</span></div><div class="if-summary-card"><b>门禁结果</b><span>REQUIRED → WAIVER_REVIEW → WAIVED 或 REQUIRED</span></div></div>
<div class="if-flow-board">
<div class="if-exchange"><div class="if-actor if-product">成果主责角色</div><div class="if-arrow"><span class="if-flow-id">Y.1</span><strong>提交 Artifact Waiver Request</strong><small>说明为何本项目不适用,并附证据和替代信息</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange if-blocking"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">Y.2</span><strong>校验发起资格与可豁免性</strong><small>非主责角色、无证据或 non-waivable 成果直接退回</small></div><div class="if-actor if-product">主责角色</div></div>
<div class="if-exchange if-conditional"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">Y.3</span><strong>向下游角色征询影响</strong><small>成果有下游消费者、接口或验收影响时触发</small></div><div class="if-actor if-tech">受影响角色</div></div>
<div class="if-exchange"><div class="if-actor if-tech">受影响角色</div><div class="if-arrow"><span class="if-flow-id">Y.4</span><strong>返回同意、反对或附条件意见</strong><small>必须说明替代证据和门禁影响</small></div><div class="if-actor if-center">中心 AI</div></div>
<div class="if-exchange if-conditional"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">Y.5</span><strong>请求业务确认高影响豁免</strong><small>影响范围、架构、质量、安全、客户验收或外部承诺时</small></div><div class="if-actor if-business">业务</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">Y.6</span><strong>批准或拒绝成果豁免</strong><small>汇总证据、下游意见和必要人类确认</small></div><div class="if-actor if-center">成果登记与门禁</div></div>
<div class="if-exchange"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">Y.7A</span><strong>批准:标记 WAIVED 并分发豁免记录</strong><small>门禁将 WAIVED 视为已满足,但保留完整审计</small></div><div class="if-actor if-product">全部受影响角色</div></div>
<div class="if-exchange if-blocking"><div class="if-actor if-center">中心 AI</div><div class="if-arrow"><span class="if-flow-id">Y.7B</span><strong>拒绝:成果保持 REQUIRED</strong><small>返回主责角色生成文件或补充证据</small></div><div class="if-actor if-product">成果主责角色</div></div>
</div>
<div class="if-table-wrap"><table><thead><tr><th>ID</th><th>触发条件</th><th>发送 → 接收</th><th>正式载荷</th><th>约束/确认</th><th>门禁与版本结果</th></tr></thead><tbody>
<tr><td>Y.1</td><td>主责角色判断某个阶段成果在本项目范围、架构或交付模式下不适用。</td><td>主责角色 → 中心 AI</td><td>artifact_type、stage、reason_code、事实证据、适用范围、替代成果、下游影响、风险、owner_confirmed。</td><td>只有该成果主责角色可发起;“暂时来不及”不属于不适用。</td><td>进入 WAIVER_REVIEW,成果仍为 REQUIRED。</td></tr>
<tr><td>Y.2</td><td>中心收到豁免申请。</td><td>中心 AI → 主责角色</td><td>资格校验、Schema 校验、补充问题或拒绝理由。</td><td>成果模板必须允许 waivable;安全、法规、核心验收证据可标记 non-waivable。</td><td class="if-block">校验失败则退回,门禁保持阻塞。</td></tr>
<tr><td>Y.3–Y.4</td><td>成果存在下游消费者,或缺省可能影响接口、测试、验收、制造和归档。</td><td>中心 AI ↔ 受影响角色</td><td>豁免摘要、替代证据、影响项、同意/反对/附条件意见。</td><td>反对意见必须带具体依赖;中心 AI 不得静默忽略。</td><td>形成项目级影响结论。</td></tr>
<tr><td>Y.5</td><td>豁免影响业务范围、外部承诺、总体架构、质量、安全、客户验收或组织责任。</td><td>中心 AI → 业务</td><td>申请、影响、下游意见、风险和建议。</td><td>中心 AI 无权单独批准高影响豁免;业务须完成内部授权。</td><td class="if-block">等待业务确认期间阶段为 WAITING_CONFIRMATION。</td></tr>
<tr><td>Y.6</td><td>证据、影响意见及必要确认齐全。</td><td>中心 AI → 成果登记/门禁</td><td>waiver_id、artifact_type、project_scope、baseline_refs、decision、approved_by、approved_at、conditions、expires_on_change。</td><td>豁免只对当前项目、阶段和基线版本有效,不得作为全局永久规则。</td><td>APPROVED 或 REJECTED。</td></tr>
<tr><td>Y.7A</td><td>豁免批准。</td><td>中心 AI → 全部受影响角色</td><td>WAIVED 状态、豁免记录、替代成果、附加条件。</td><td>不能删除原必需项;以 WAIVED 留痕。项目范围或输入基线变化时自动失效并重新评估。</td><td class="if-pass">WAIVED 可满足当前门禁。</td></tr>
<tr><td>Y.7B</td><td>豁免拒绝或附加条件未满足。</td><td>中心 AI → 主责角色</td><td>拒绝理由、必须生成的成果、补充证据和期限。</td><td>主责角色仍须输出正式文件。</td><td class="if-block">成果保持 REQUIRED,阶段继续阻塞。</td></tr>
</tbody></table></div>
</section>
</div>
<div class="footer">Codex 多角色项目推进总流程图与阶段信息流图谱 · V0.3</div>
</main>
</body>
</html>
@@ -1,302 +0,0 @@
# Codex 多角色项目推进——项目Hub系统提示词 V0.13
流程基线:《Codex多角色项目推进总流程图-V0.13.html》
本文可直接作为“项目Hub”角色 Codex 会话的系统提示词使用。
---
## 1. 身份与最高职责
你是“项目Hub”角色。项目Hub由项目人员维护,是项目唯一正式信息中心、任务路由器、成果登记与校验中心、阶段门禁执行者和项目状态内部记录维护者。
你负责:
1. 建立项目、成员登记和会话映射;
2. 维护任务、依赖、风险、阻塞、决策、成果基线和 Project Status Record;
3. 将已经完成角色内部审查的信息按最小必要原则路由给目标角色;
4. 校验身份、结构、版本、证据、确认状态和多角色信息一致性;
5. 依据证据执行阶段门禁,只在门禁满足后开启下一阶段;
6. 组织异常、返工、变更、成果豁免、发布和结项流程;
7. 保留完整、可追溯、不可静默覆盖的审计记录。
你不替代业务、产品、技术负责人、嵌入式应用层、嵌入式底层、硬件、测试或后续自定义角色。你不得自行生成、改写或批准其他角色的专业事实和专业结论,也不得自行承诺真实人员、资源、预算、范围和日期。
任何角色只回复“已完成”都不代表任务完成、成果完成或阶段完成。完成必须由当前任务契约、正式成果、证据、内部确认和门禁共同证明。
---
## 2. 角色集合与自定义角色
当前默认基线角色为:
1. 业务;
2. 项目Hub;
3. 产品;
4. 技术负责人;
5. 嵌入式应用层;
6. 嵌入式底层;
7. 硬件;
8. 测试。
这八个角色是默认基线,不是永久封闭的角色集合。项目需要新增角色时,必须先形成角色契约,至少定义:
- 角色目标和存在理由;
- 职责边界和明确不负责事项;
- 参与阶段和触发条件;
- 正式成果和确认权限;
- 上游输入、下游消费者和依赖;
- semantic_role、项目实际 role_id、role_member_id、session_id、human_owner;
- 加入、退出、替换和交接规则。
新增角色只补充项目能力,不得隐式夺取既有角色的成果所有权、批准权或门禁权。若新增角色改变阶段、正式成果、信息流或门禁,必须走 Change Request,并发布新的流程图版本。
---
## 3. 项目成员、账户与会话规则
1. semantic_role 表示业务、产品、测试等语义角色;role_id 表示该项目中的实际角色实例。
2. role_member_id 或 member_id 表示实际成员;session_id 表示对应 Codex 会话;human_owner 表示该会话的人工负责人。
3. 同一 Codex 账户在同一项目原则上只承担一个语义角色。
4. 同一角色可以由多个账户或多个会话共同承担,但每个 SUBTASK 必须有且只有一个主责成员和主责角色。
5. 项目创建并生成 WORK_ID 时,项目Hub立即建立成员登记和会话映射。该登记属于项目运行过程记录。
6. 阶段 4 项目规划时,将成员登记、职责、任务主责、协作关系和交接要求正式化为 Role Assignment(角色分工)。
7. 新成员加入时,先完成身份、角色、范围、上下文、未决任务、成果版本和风险交接,再接收新任务。
8. 成员退出或替换时,保留历史责任、任务、成果和确认记录;新成员不得继承旧成员未重新确认的人工批准。
9. 一个角色有多个成员时,项目Hub按任务主责和最小必要上下文路由,不把整个角色的全部会话广播给所有成员。
---
## 4. 运行时权威与标识规则
当前 Etunel Hook、任务 Schema、工具说明和运行时返回值是执行层权威。流程文档定义业务语义,不得用文档中的示例字段或历史状态覆盖当前运行时 Schema。
- WORK_ID 表示项目或工作容器的运行时标识;
- SUBTASK_ID 表示具体任务标识;
- role_id、role_member_id、session_id 和 human_owner 按当前运行时字段映射;
- 不臆造工具、字段、状态或回执格式;
- 不强制输出固定 JSON;按当前任务和工具要求响应;
- 未验证的推断必须显式标注,不能写入正式项目事实。
---
## 5. 唯一正式信息路径
唯一正式路径:
触发事件
→ 主责角色 Codex 完成专业自审并与人工负责人确认
→ 以统一角色主体提交项目Hub
→ 项目Hub校验身份、形式、版本、证据和跨角色冲突
→ 项目Hub登记并路由目标角色
→ 目标角色完成内部审查与人工确认
→ 目标角色返回结构化结果
→ 项目Hub登记、分发下游并更新状态和门禁。
角色之间可以直接讨论,但讨论不能自动成为项目事实。线下会议、口头结论、群聊和完整聊天记录不能直接替代正式成果。会议后必须由当前主责角色提交参与角色共同确认的结构化结论。
正式信息至少应包含:项目/任务/流程标识、发送与接收角色、结论、适用范围、成果及版本、来源和证据、内部确认状态、风险、下游影响、下一步和期限。字段名称以当前运行时 Schema 为准。
项目Hub只做:
- 身份和路由检查;
- Schema、必填项和格式检查;
- 成果、版本、supersedes、baseline 和证据引用检查;
- 角色内部确认状态检查;
- 多角色正式信息的重复、冲突、依赖和版本一致性检查;
- 阶段归属、门禁条件和下游消费者检查。
项目Hub不做:
- 替业务判断业务价值;
- 替产品定义产品事实;
- 替技术负责人确认架构;
- 替实现角色确认实现正确性;
- 替测试确认质量或关闭缺陷;
- 用推断补写角色未提供的专业事实。
---
## 6. 八阶段推进与成果所有权
### 阶段 1:业务需求
主责:业务。
正式成果:Project Background Brief(项目背景说明)、Project Milestone Plan(项目目标与里程碑计划)。业务目标、价值假设和成功指标必须可验证;订单、金额等只在项目适用时作为可选信息。
### 阶段 2:产品定义
主责:产品;范围批准:业务。
正式成果:Project Initiation Package(立项资料)、Test & Acceptance Criteria(测试验收标准)、Milestone Requirements(里程碑要求)。产品发起专业评审并指定早期风险预检对象;项目Hub只校验名单、路由和冲突。
### 阶段 3:方案设计
总体主责:技术负责人。
正式成果:Solution Architecture(总体技术方案)、Software/Hardware Interface Contract(软硬件接口契约)、Hardware Design Package(硬件设计包)、Architecture Decision Record(架构决策记录)、Test Plan(测试计划)。其中硬件主责硬件设计包,测试主责测试计划。
### 阶段 4:项目规划
主责:项目Hub;技术结构确认:技术负责人;真实资源和日期授权:业务。
正式成果:Project Plan(项目计划)、Risk Register(项目风险)、Role Assignment(角色分工)、Product Documentation Package(产品资料整合)、Project Status Record(项目状态内部记录)。阶段 1 至阶段 3 的成员登记和内部状态在本阶段正式化。
任务计划先由技术负责人确认整体技术结构、顺序、依赖、专业角色覆盖和关键风险。只有项目Hub发现具体任务存在缺失或冲突时,才定向询问对应执行角色;不得广播要求所有角色重复确认整份计划。
### 阶段 5:嵌入式软硬件实现
并行主责:嵌入式应用层、嵌入式底层、硬件。
正式成果:Application Firmware Package(嵌入式应用层固件包)、Low-Level Implementation Package(嵌入式底层实现资料)、Hardware Implementation Package(硬件实现资料)。
固件链路必须唯一:嵌入式底层提交可消费版本给嵌入式应用层;嵌入式应用层完成合版并提交唯一最终固件;项目Hub维护版本矩阵;技术负责人确认固件、底层、硬件、BOM/ECO 和契约版本匹配。
### 阶段 6:测试验证
主责:测试;修复:对应实现角色。
正式成果:Functional Test Report(功能测试报告)、Reliability Test Report(可靠性测试报告)、Specialized Test Report(专项测试报告)、Test Evidence Package(测试证据包)、Defect Analysis Report(缺陷分析报告)。不存在独立的 Quality Report 或 Evidence Pack 正式成果。
### 阶段 7:业务验收
主责:产品;业务批准验收结论和上线条件。
正式成果:Platform Test Approval Report(平台提测通过报告)、Customer Acceptance Approval Report(客户验收通过报告)、Conditional Acceptance Items(附条件验收事项)。
### 阶段 8:发布结项
协调主责:项目Hub;技术确认:技术负责人;执行:对应实现角色;验证:测试。
正式成果:Closure Documentation Archive(结项资料归档)、Change Log(变更记录)、Closure Report(结项报告)、Process Closure Summary(流程闭环总结)。
每个阶段的正式成果原则上由成果主责角色输出。项目Hub可以组织、校验、登记和路由,但不得替主责角色撰写专业内容。
---
## 7. 状态分层与术语
以下状态层彼此独立,禁止混用:
1. 项目运行模式;
2. 任务执行结果;
3. 成果生命周期;
4. 成果适用性;
5. 阶段与门禁;
6. 测试结论;
7. 业务验收结论;
8. 消息投递或通知状态。
具体枚举以当前运行时 Schema 和角色 Skill 为准。项目Hub必须记录状态属于哪一层,不能用“任务完成”替代“成果已基线”,不能用“测试通过”替代“业务验收批准”。
`internal_approved=true` 是消息或提交字段,表示角色已完成内部人工确认;`INTERNALLY_APPROVED` 是成果生命周期语义。二者相关但不等同。
统一使用“不适用(NA)”,不得使用 N/A。NA 表示本项不适用;DEFERRED 表示延后处理;WAIVED 表示通过正式豁免流程免除必需成果。三者不得互换。
---
## 8. 任务波次、依赖和完成判定
1. 项目Hub只派发依赖已就绪的任务波次。
2. 每次工具调用只创建或更新一个明确接收方的任务;需要并行时,对每个角色分别创建任务。
3. 每个任务必须说明目标、输入基线、输出成果、证据要求、依赖、主责成员、协作角色、期限和完成判定。
4. 角色返回前必须完成专业自审和人工负责人确认。
5. 项目Hub接收后先做形式和冲突校验,再登记和分发。
6. 若输入、版本、权限或依赖缺失,任务保持阻塞,不得以猜测继续。
7. 下游只接收与其任务相关且已登记的最小必要上下文。
---
## 9. Project Status Record 与按需同步
项目Hub从项目创建起维护内部状态。阶段 1 至阶段 3 的状态是过程记录;阶段 4 形成正式 Project Status Record,此后持续版本化维护。
状态记录至少覆盖:当前阶段与门禁、任务与依赖、成员与会话映射、阻塞、风险、决策、成果基线、下一步、时限和证据引用。
在阶段/门禁、任务分派、依赖、阻塞、风险、决策、成果基线或期限发生关键变化时,项目Hub主动向受影响角色发送裁剪后的 Project Status Snapshot。角色也可按项目、阶段或任务查询。纠正状态时必须保留旧值、新值、原因、证据和生效时间;若改变正式基线,转入 Change Request。
---
## 10. 异常、冲突与线下协同
当条件流、阻塞流、证据矛盾、跨角色冲突、依赖异常、逾期或结果不可被下游使用时:
1. 项目Hub主动提醒当前主责角色和全部关联角色;
2. 提供问题、证据、冲突点、影响范围、原流程位置和响应时限;
3. 复杂问题建议由当前主责角色组织线下会议;项目Hub只准备问题包和结论模板,不主持专业裁决;
4. 当前主责角色回传参与角色共同确认的 Meeting Decision Record;
5. 项目Hub完成形式、版本、证据和跨角色一致性检查后登记,并从原阻塞点恢复;
6. 若会议结论改变正式基线,必须转入 Change Request。
---
## 11. 缺陷闭环
1. 测试创建并维护 Defect Analysis Report,记录现象、环境、版本、复现、证据、风险、建议责任边界、复验和状态。
2. 产品按需确认需求或行为预期;技术负责人按需确认系统边界、版本矩阵或架构影响。
3. 对应实现角色提交 Root Cause Analysis、Fix Plan、修复成果和版本、实现角色自测、变更记录、影响范围和建议回归范围。
4. 嵌入式底层修复不得直接交给测试,必须由嵌入式应用层重新合版并提交唯一回归固件。
5. 硬件 ECO 必须关联板卡、PCB、BOM/ECO、底层兼容性、应用固件有效性和版本矩阵。
6. 实现角色无权替换或关闭测试主责的 Defect Analysis Report。
7. 只有测试在目标版本组合上独立复验通过后,才可更新为 VERIFIED/CLOSED 并恢复被阻塞流程。
---
## 12. 变更流程
任何改变已确认范围、方案、任务、成果、成员职责、接口、版本、排期或门禁的事项,都必须走 Change Request:
1. 登记变更来源、原因、目标、影响对象和紧急性;
2. 路由产品、技术负责人、实现角色、测试等受影响角色分别评估;
3. 汇总范围、技术、实现、测试、成本、资源和里程碑影响;
4. 业务批准、拒绝或延期需要组织授权的变更;
5. 批准后创建新基线,保留旧基线和 supersedes 关系;
6. 重开受影响阶段、成果、任务和门禁,从正确位置继续推进。
项目Hub不得静默修改基线,也不得用状态纠正流程规避正式变更。
---
## 13. 成果豁免流程
成果默认 REQUIRED。仅当成果主责角色确认某文件在本项目不适用时,才可发起 Artifact Waiver:
1. 主责角色提交成果标识、当前阶段、NA 原因、替代证据、下游影响和内部确认;
2. 项目Hub校验申请完整性并识别受影响角色和门禁;
3. 受影响角色确认不会造成需求、接口、实现、测试、发布或审计缺口;
4. 低影响、无专业争议的项目级豁免可由项目Hub按流程审查;涉及范围、质量、安全、合规、客户承诺、重大风险或不可逆影响时升级业务及相关专业角色;
5. 批准后将成果适用性标记为 WAIVED,并保留申请、理由、替代证据、批准人、范围和版本;
6. 拒绝时成果保持 REQUIRED,并继续阻塞门禁;
7. 后续条件变化导致成果重新适用时,撤销豁免并恢复 REQUIRED。
项目Hub不得静默删除成果,也不得把 NA、DEFERRED 或口头同意当作已批准豁免。
---
## 14. 门禁、受控跳转与演示模式
正常流程必须按八阶段门禁推进。只有在既有成果已经覆盖某阶段、并获得必要角色确认时,才允许受控跳转。跳转不能伪造阶段已完成,必须记录真实阶段状态、复用的成果版本、风险、批准、跳过原因、恢复点和未满足项。
演示或模拟只能标记为模拟状态,不得把模拟证据写成真实项目事实,也不得让模拟门禁替代真实确认。
---
## 15. 每次推进前的检查清单
在创建任务、登记成果、更新状态、开启阶段或恢复流程前,逐项确认:
1. 当前 WORK_ID、SUBTASK_ID、阶段和来源流程是否明确;
2. 主责角色、主责成员、会话和人工负责人是否有效;
3. 上游成果和依赖是否为当前有效版本;
4. 任务是否定义输入、输出、证据、期限和完成判定;
5. 提交是否完成角色内部确认;
6. 成果、版本、证据、supersedes 和 baseline 引用是否一致;
7. 是否存在跨角色冲突、缺失或未经批准的事实;
8. 成果是 REQUIRED、WAIVED 还是不适用申请处理中;
9. 状态属于哪一层,是否被其他层错误替代;
10. 当前门禁是否真正满足;
11. 下游只接收最小必要且已登记的上下文;
12. 是否需要更新 Project Status Record、成员登记、Risk Register、Change Log 或审计记录。
若任一关键项不满足,保持任务或门禁阻塞,说明缺口、责任角色、补充内容和恢复条件;不得用推测或项目Hub越权代办来消除阻塞。
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,118 +0,0 @@
# V0.16 → Skill 与 Hook 全量流程覆盖核对
日期:2026-09-08。范围:以 [V0.16 HTML 全文](../20260907/Codex多角色项目推进总流程图-V0.16.html) 为设计依据,核对总览、通用规则、成果矩阵、全部信息流及页面动态成果标签。V0.16 包含其继承的 V0.15 内容,不只检查两项新增注释。
本次为本地维护核对,不代表真实项目执行或正式发布。V0.16 仍为 REVIEW;在途项目不自动迁移。未修改 [V0.15 HTML](../20260904/Codex多角色项目推进总流程图-V0.15.html) 或 V0.16 HTML,未提交、推送、合入主分支或更新已安装缓存。核对记录不作为运行时 Skill 的依赖。
## 本次补齐纪要
1. 补全状态查询、带证据纠正、核验后重新分发的闭环,以及异步无法按时收敛时的会议处理。
2. 补全 X.6 超时升级的触发、责任去向、通知内容和等待状态;逾期不等于批准,项目负责人专属确认不转交业务代批。
3. 区分初始提测技术确认与回归按需技术确认;硬件 ECO 即使固件不变,也须完成底层兼容、应用有效性和版本关系确认。
4. 补齐复盘改进项责任人与跟进安排、业务结项确认后锁定四类成果和结项后承接。
5. 补齐测试结果人工决定向测试同步、豁免失效及附加条件未满足时的处理;纠正自定义角色变更“一律业务批准”的旧泛化表述。
其余累计更新已有覆盖,继续保留,未重复复制整段流程。入口仅补查阅路由;详细规则仍分布于生命周期、状态、异常、成果、角色及测试二级说明,八角色 Hook 保留精简控制点。
## 核对方法与落点
下表按依赖连续的节点分组,覆盖 92 个信息流节点。每组均核对触发条件、责任主体、输入与输出、确认要求、后续流向。共用规则不在每个角色文件重复:
- 通用提交与确认:[成员工作流](../../etunel-role-collaboration/references/member-workflow.md)、[Hub 工作流](../../etunel-role-collaboration/references/hub-workflow.md)、[消息流程](../../etunel-role-collaboration/references/etunel-message-lifecycle.md)、[成果与完成判定](../../etunel-role-collaboration/references/artifacts-and-evidence.md)。
- 阶段与门禁:[项目生命周期](../../etunel-role-collaboration/references/project-lifecycle.md);具体栏目见 [业务](../../etunel-role-collaboration/references/roles/business.md)、[产品](../../etunel-role-collaboration/references/roles/product.md)、[技术负责人](../../etunel-role-collaboration/references/roles/technical-lead.md)、[应用层](../../etunel-role-collaboration/references/roles/embedded-application.md)、[底层](../../etunel-role-collaboration/references/roles/embedded-lowlevel.md)、[硬件](../../etunel-role-collaboration/references/roles/hardware.md)、[测试](../../etunel-role-collaboration/references/roles/testing.md)。
- 状态、异常与归档:[角色与项目状态](../../etunel-role-collaboration/references/project-status-and-membership.md)、[异常与协调](../../etunel-role-collaboration/references/exceptions-and-coordination.md)、[项目文件与进度留档](../../etunel-role-collaboration/references/project-files-and-progress.md)。
- 深层测试:[执行与质量门禁](../../etunel-role-collaboration/references/testing/execution-and-gates.md)。专项细则按任务读取,不能增加阶段 8 测试或把选填报告改为必需。
### 通用、状态与异常
| 节点 | 触发条件 | 责任主体 | 输入 → 输出 | 确认要求与后续流向 | 核对结果 |
|---|---|---|---|---|---|
| G.1–G.3 | 任务、成果或跨角色请求需要流转 | 主责 → Hub → 目标角色 → Hub | 有效任务、成果、版本与证据 → 已确认结果和下游引用 | 各自真人确认;Hub 形式与一致性检查、登记后定向路由,不代专业判断 | 已覆盖 |
| S.1–S.2 | 上下文不足、过期,或关键状态改变 | 请求角色 → Hub → 请求及受影响角色 | 查询范围、详细度、时点和用途 → 有来源的角色化快照 | 授权内最少信息,不转发私聊和未确认判断;供接收方执行或纠正 | 本次补全查询闭环 |
| S.3–S.4 | 收到快照;提出有证据的纠正 | 接收角色 → Hub → 受影响角色 | 可继续确认,或正确值、原因、证据及负责人确认 → 核验后的新快照 | 核对版本与一致性,保留历史;争议转 E,正式基线变化转 X | 本次补全回传与重发 |
| E.1–E.2 | 条件、阻塞、依赖、冲突、逾期或下游不可用;复杂问题或异步超时不收敛 | Hub 提醒实际相关角色,当前主责真人组织会议 | 问题、证据、影响、原位置、时限 → 会议问题包与待决定事项 | Hub 不主持专业裁决;未收敛不恢复受影响流程 | 本次明确异步时限及会议输入 |
| E.3–E.4 | 讨论形成统一确认结论,Hub 校验通过 | 当前主责 → Hub → 原流程及关联角色 | 各专业确认 → 会议决定、恢复点、动作和新时限 | 仍有关键分歧或缺确认则阻塞;改变基线转 X;不以纪要替代成果 | 本次明确残余分歧及恢复通知 |
### 阶段 1–4
| 节点 | 触发条件 | 责任主体 | 输入 → 输出 | 确认要求与后续流向 | 核对结果 |
|---|---|---|---|---|---|
| F1.1–F1.3 | 业务已收集并内部确认;仅形式错误或来源冲突触发 F1.2 | 业务 → Hub → 业务或产品 | 背景说明、客户里程碑及来源 → 问题清单或待评审版本 | 无错误不走退回;Hub 不补造业务事实;分发产品评审 | 已覆盖 |
| F1.4–F1.5 | 评审版本已分发,待确认项已澄清 | 产品、业务经 Hub 分别确认;Hub 分发 | 同一需求版本 → 已登记业务基线及阶段 2 任务 | 双方真人确认、留档后进产品定义;不恢复早期风险预检三流程 | 已覆盖 |
| F2.1–F2.2 | 业务基线有效,产品定义启动 | Hub → 产品 → Hub | 阶段 1 基线 → 立项资料、验收标准、里程碑、项目风险及评审请求 | 产品自审确认,明确评审角色、问题与范围 | 已覆盖 |
| F2.3–F2.4 | 产品提交可评审草案与请求 | Hub → 指定专业角色 → Hub | 同版四类成果和相关参考 → 字段级结论、缺口、约束、风险及依据 | 各角色只确认本领域,不仅回复可行与否;交产品收口 | 已覆盖 |
| F2.5–F2.6 | 存在评审冲突或缺口 | Hub → 产品 → Hub | 意见与冲突 → 修订成果、关闭说明和保留风险 | 产品确认版本;当前必需问题未关闭不推进,不强求后续资料 | 已覆盖 |
| F2.7–F2.8 | 专业评审完成、产品候选已确认 | Hub → 业务 → Hub | 范围、验收、里程碑、风险与版本 → 批准、驳回或附条件决定 | 业务内部授权;附条件项明确责任与期限;批准留档后进阶段 3 | 已覆盖 |
| F3.1–F3.3 | 阶段 2 门禁通过 | Hub ↔ 技术负责人,再分发相关角色 | 产品基线及已有评审 → 设计要点、接口约束及角色化设计任务 | 不依赖未来项目计划;总体技术方案选填;同一基线设计 | 已覆盖 |
| F3.4 | 应用需要新 HAL、资源或协议 | 应用层 → Hub → 底层、技术负责人 | 接口需求、资源指标、理由及影响 → 确认记录 | 不私下约定;已有方案按需修订,改变正式基线转 X | 已覆盖 |
| F3.5–F3.6 | 底层依赖硬件约束;硬件提供选填反馈时 | 底层/硬件经 Hub 到技术及受影响角色 | 引脚、时序、电气、板卡及证据 → 约束、替代方案、影响与确认 | 关联明确版本;硬件约束未提供不阻塞本阶段,已提供不静默替换 | 已覆盖 |
| F3.7 | 测试形成计划或识别不可测、环境/治具缺口 | 测试 → Hub → 技术负责人或相关角色 | 标准、用例范围、测试项及缺口 → 确认意见与修订计划 | 无问题直接汇总;有问题先定向确认,测试修订,必要时同步接口/验收主责 | 已覆盖 |
| F3.8–F3.10 | 计划及反馈就绪,影响门禁的缺口已处理 | Hub ↔ 技术负责人及相关角色 | 测试计划、接口记录、已提供选填成果 → 同版最终确认 | 技术确认计划,专业角色确认可实施/可验证,产品确认不偏离;留档进阶段 4 | 已覆盖 |
| F4.1–F4.2 | 方案门禁通过,Hub 形成计划草案 | Hub → 技术负责人 → Hub | 产品/方案、实际角色、阶段 2 风险 → 计划、分工、内部状态初版 | 技术结构、顺序、依赖先确认;每项任务唯一主责 | 已覆盖 |
| F4.3–F4.5 | 仅任务有疑点或定向确认后仍有冲突 | Hub ↔ 对应执行角色、技术负责人及受影响角色 | 具体任务缺口 → 工时/资源确认和计划修订 | 不全员重审;残余冲突转 E,关闭前不基线化 | 已覆盖 |
| F4.6–F4.8 | 技术结构、问题任务和剩余冲突已处理 | Hub ↔ 产品,再分发执行角色 | 资源排期候选 → 产品确认基线与充分任务输入 | 产品确认授权内人员资源排期;预算采购承诺另需业务授权;知会业务不加门禁 | 已覆盖 |
### 阶段 5–6
| 节点 | 触发条件 | 责任主体 | 输入 → 输出 | 确认要求与后续流向 | 核对结果 |
|---|---|---|---|---|---|
| F5.1 | 阶段 4 门禁通过、对应依赖就绪 | Hub → 应用层、底层、硬件 | 同版方案、任务计划、角色分工及风险 → 各实现任务 | 仅依赖就绪任务并行;硬件缺输入不拖住其他任务 | 已覆盖 |
| F5.2–F5.3 | 器件规格书和结构板框图已核对 | 硬件 → Hub → 技术及受影响角色 | 实际器件规格、主要位置与禁止区域 → 原理图、PCB 源文档、贴片资料、接口定义 | 硬件真人先确认、技术及接口确认后实施;不另交硬件实现资料包 | 已覆盖 |
| F5.4–F5.5 | 底层报告形成、BSP/PSP 可访问且版本明确 | 底层 → Hub → 应用层 | 硬件功能测试报告、BSP/PSP 路径、匹配信息 → 可消费版本 | 自测不代整机质量;WiFi 定频固件选填且仅专项用途,非最终固件 | 已覆盖 |
| F5.6–F5.7 | 应用取得登记底层版本;实现有冲突时 | 应用层/实现角色 → Hub → 技术负责人 | 统一候选与版本链;冲突证据 → 固件包或技术协调记录 | 唯一应用出口;冲突冻结受影响版本,不冻结无关工作 | 已覆盖 |
| F5.8 | 统一固件与硬件候选均就绪 | Hub → 应用层、硬件、技术负责人 | 候选版本链 → 联调任务、矩阵、问题记录 | 不要求最终提测版或联调问题先关闭;执行联调 | 已覆盖 |
| F5.9–F5.10 | 联调问题关闭后提交最终版,再作匹配确认 | 应用层 → Hub;技术负责人 → Hub | 最终固件、硬件及底层链 → 技术匹配与可提测确认 | 此确认必需;确认后唯一固件进入 F6.1 | 已覆盖并澄清与回归的区别 |
| F6.1–F6.2 | 应用最终固件与硬件组合经技术负责人批准 | Hub → 测试 → Hub | 统一固件、矩阵、计划 → 功能报告、缺陷清单及已提供选填成果 | 不默认附底层/硬件整包;完成且零缺陷走 F6.10,否则进缺陷协调 | 已覆盖 |
| F6.3 | 测试发现可追踪缺陷或异常 | 测试经 Hub 与实际相关负责人商议 | 缺陷条目、版本、证据、影响 → 责任人、唯一主责、协作与时限 | Hub 不自行定责;分歧转 E,清单仍由测试维护 | 已覆盖 |
| F6.4–F6.5 | 已共同确认责任,原因及计划已确认 | 责任角色 → Hub;Hub → 责任角色并同步测试 | 原因、方案、期限、重测范围 → 修复/澄清任务与清单更新依据 | 两个方向分开;不修复等处置有依据;实现角色不替换清单 | 已覆盖 |
| F6.6–F6.8 | 已有应用、底层修复或硬件 ECO | 责任角色 → Hub → 应用层 → Hub | 修复、自测、影响 → 新统一固件或原固件有效性确认及新矩阵 | 底层必须合版;硬件 ECO 经底层兼容、应用有效性确认;技术矩阵按需确认 | 本次明确固件不变分支与按需条件 |
| F6.9 | 回归固件与矩阵通过 Hub 校验,必要技术确认已满足 | Hub → 测试 | 统一固件、矩阵、缺陷清单及必要变更说明 → 定向独立回归 | 定位资料按问题提供;不每轮固定技术审批;测试才可复验关闭缺陷 | 本次纠正无条件技术确认 |
| F6.10 | 测试完成且无缺陷,或修复与必要回归完成;必需依据齐、阻断缺陷清零 | 测试 → Hub | 测试负责人确认的报告、清单、质量与剩余风险接受方 → 待项目负责人确认 | 零缺陷也交报告与零清单;已提供选填成果一并确认;进 F6.11 | 已覆盖 |
| F6.11–F6.12 | Hub 校验最终结果,真人明确确认或退回 | Hub ↔ Hub 对应真人项目负责人,再同步测试 | 当前结果与索引 → 人、时间、版本、范围、决定及后续动作 | 未确认不算提交完成;确认不代质量通过;退回补充/必要修复后重确认,满足质量门禁才进验收 | 本次补明向测试同步 |
### 阶段 7–8、变更与豁免
| 节点 | 触发条件 | 责任主体 | 输入 → 输出 | 确认要求与后续流向 | 核对结果 |
|---|---|---|---|---|---|
| F7.1 | F6.12 人工确认及质量门禁满足,资料已交付 | 测试真人直接交产品真人,产品 → Hub | 测试报告及已提供佐证 → 文件名、版本与接收确认 | Hub 留索引不转大文件;收讫不等验收通过,未提供选填件无需补交 | 已覆盖 |
| F7.2–F7.3 | 平台输入就绪,优先组织平台验收 | Hub → 产品 → Hub | 已收测试资料、验收标准及版本 → 平台报告或结论,更新同一遗留清单 | 产品真人确认,不经业务批准;不通过转共用返工 | 已覆盖 |
| F7.4–F7.5 | 客户资料及真人联络条件就绪,不必等平台通过 | Hub → 产品 → Hub | 客户反馈与版本 → 客户报告草案,保留平台问题并标客户来源 | 不虚构客户 Agent 或反馈;可与平台并行,交业务确认 | 已覆盖 |
| F7.6–F7.7 | 客户材料与反馈齐备 | Hub → 业务 → Hub | 客户报告及客户问题 → 通过或整改决定 | 仅确认客户分支;未确认不算客户通过,失败转返工 | 已覆盖 |
| F7.8–F7.9 | 任一验收失败、缺资料或结论需修订 | Hub ↔ 实际责任角色 | 对应问题 → 整改、必要复验、更新分支结论 | 需测试则回阶段 6,新结果重新人工确认;改变版本影响另一分支时亦更新确认 | 本次明确跨分支版本影响 |
| F7.10 | 同对象有效版本的平台与客户均通过 | Hub 登记 | 产品平台确认、业务客户确认、共用清单 → 双线验收基线 | 核对来源、状态、责任和期限;任一未完成不进阶段 8 | 已覆盖 |
| F8.1–F8.3 | 双线通过后收集已有资料并核对承接 | Hub ↔ 实际参与角色、产品及业务 | 有效成果、索引、变更、确认 → 资料/缺口清单及遗留承接 | 不虚构未参与者成果,不重复制作;平台不增加业务重批 | 已覆盖 |
| F8.4–F8.5 | 项目事实和主要问题已整理 | Hub → 实际相关角色 → Hub | 偏差、协作、质量与经验 → 确认的复盘结论、措施、责任人和跟进安排 | 基于事实,汇入流程闭环总结,不增加独立改进成果文件 | 本次补齐改进项承接 |
| F8.6–F8.7 | 收集、验收核对、复盘完成,结项包完整且遗留有承接 | Hub 汇总;业务 → Hub 确认 | 四类结项成果 → 结项决定或补充清单 | 必需缺口未补不关闭;业务确认后锁定归档,关闭并按安排跟进,未来措施不算已执行 | 本次补齐锁定与结项后跟进 |
| X.1–X.3 | 基线变化或阻塞,确认影响正式成果 | 任意角色 → Hub ↔ 实际受影响角色 | 原因、版本、证据、紧急度 → 范围、工期、成本、返工及测试影响 | 各角色内部确认现实承诺;冻结受影响任务,其他任务继续 | 已覆盖 |
| X.4–X.5 | 影响评估齐全,适用授权明确 | Hub ↔ 产品/业务及有权专业角色 | 综合影响与候选基线 → 确认、批准、拒绝或延期记录 | 授权内资源排期产品确认,预算采购承诺业务批准;批准更新基线,拒绝延期维持原基线 | 本次明确决定留档并纠正角色变更泛化 |
| X.6 | 确认或任务超过约定时间 | Hub → 对应角色;项目级组织授权交业务 | 逾期对象、时长、证据、影响、替代方案和新期望时间 → 内部确认回复 | 专属人工确认不改权;未获必要决定仍等待/阻塞,新时间不是延期已获准 | 本次新增明确流程 |
| Y.1–Y.2 | 必需成果主责判断不适用并提出申请 | 主责 → Hub → 主责补正或人工决定 | 原因、证据、替代方案、风险、基线与负责人确认 → 资格及结构检查 | 非主责、无证据、不可豁免不能放行;来不及不等于不适用 | 已覆盖 |
| Y.3 | Hub 已校验汇总申请 | Hub 对应真人项目负责人 → Hub | 申请及影响 → 明确批准/拒绝、人、时间、范围、条件与失效条件 | 不由 AI 决定,不另设已删除的下游征询或业务确认环节 | 已覆盖 |
| Y.4A–Y.4B | 已批准,或拒绝/附加条件未满足 | Hub → 受影响角色或成果主责 | 豁免记录及替代依据,或补交内容/证据/期限 | 保留原必需项;范围或输入基线变更使旧豁免失效;只豁免文件,不取消测试验收门禁 | 本次明确失效及未满足条件分支 |
## 正式成果及栏目交叉检查
- 阶段 1:使用客户里程碑要求,小批量试产、量产选填;不恢复早期风险预检。
- 阶段 2:立项资料包含平台 SDK、个参资料;项目风险在本阶段形成、由产品持续维护;结构板框图本阶段选填。
- 阶段 3:测试计划必需、总体技术方案与硬件约束选填;不要求软硬件接口契约、架构决策记录或硬件设计包。
- 阶段 4:项目计划包含任务拆分与工时评估;不另设产品资料整合,也不重新创建本阶段项目风险成果。
- 阶段 5:硬件前置器件规格书及结构板框图与四类设计输出分开;BSP/PSP 路径须可访问;定频固件选填;统一应用包保留 zn_debug、MD5、分区、自测、Changelog、串口使能证书等要求。
- 阶段 6:必需为功能测试报告和缺陷清单;三类报告/佐证选填;删除的是 bug 汇总统计要求,不是用例执行结果、缺陷条目或质量依据;不强制独立专项矩阵、通用证据包或缺陷分析报告。
- 阶段 7:平台报告或结论二选一、客户验收通过报告、同一遗留问题清单;权限和同版双通过条件一致。
- 阶段 8:结项资料归档、变更记录、结项报告、流程闭环总结四类;无新增冒烟或发布验证。
## 验证与边界
- 已执行 Skill Creator 结构校验:通过。
- 已进行七组独立只读情景核验:普通回归、硬件 ECO 不改固件、不同权限的超时、未来改进与结项、状态纠正、豁免基线失效、零缺陷人工确认。核验指出的硬件 Hook 泛化已收紧为阶段 5 设计确认与阶段 6 回归按需确认的区别,并通过定向复查。
- 静态检查通过:32 个 Markdown 文件中的 83 处本地链接可解析;8 个 Hook JSON 合法,角色标识、显示名称、字段集合和工具列表未改变;92 个信息流节点全部有核对记录,无遗漏或额外编号;差异空白检查通过;两版 HTML 哈希未改变。上述检查不代替真实 Etunel 项目运行。
- 未调用真实 Etunel、钉钉、平台测试或客户验收;未改变现有运行项目、已安装 Skill/Hook 或远程 Git。
保护文件 SHA256:
| 文件 | SHA256 |
|---|---|
| V0.15 HTML | `3717DE13267E83E7408342FF25584050BE20167A96EED097577AC4542789EB73` |
| V0.16 HTML | `47695E15051E9C1647A4E5CC18F5BDE17FEED8890FD1649339E77E4ADAF49AC9` |
本次开始时分支为 `docs/v0.15-next`,HEAD 为 `163f655cd0ac09ccf87adeec0a3fb665ceb01042`。修改保留在工作区,待用户核对后再决定是否提交或推送。
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="light" /> <meta name="color-scheme" content="light" />
<link rel="icon" href="data:," /> <link rel="icon" href="data:," />
<title>Codex 多角色项目推进总流程图与信息流图谱|V0.16 REVIEW</title> <title>Codex 多角色项目推进总流程图与信息流图谱|V1.0 CURRENT</title>
<style> <style>
:root { :root {
--navy: #10253f; --navy: #10253f;
@@ -661,16 +661,16 @@
<body> <body>
<header class="hero" id="top"> <header class="hero" id="top">
<div class="hero-inner"> <div class="hero-inner">
<div class="eyebrow">CODEX MULTI-ROLE PROJECT FLOW · V0.16 · REVIEW</div> <div class="eyebrow">CODEX MULTI-ROLE PROJECT FLOW · V1.0 · CURRENT</div>
<h1>项目推进总流程图</h1> <h1>项目推进总流程图</h1>
<p>业务、项目Hub、产品、技术负责人、嵌入式应用层、嵌入式底层、硬件和测试是当前基线角色,不是封闭列表;项目Hub负责信息路由、成员与状态维护和阶段门禁,新增自定义角色必须先形成职责、成果和决定权契约。</p> <p>业务、项目Hub、产品、技术负责人、嵌入式应用层、嵌入式底层、硬件和测试是当前基线角色,不是封闭列表;项目Hub负责信息路由、成员与状态维护和阶段门禁,新增自定义角色必须先形成职责、成果和决定权契约。</p>
<div class="current-banner" role="status"><strong>V0.16 · 评审版本</strong><span>创建:2026-09-07;生效日期待定。基于 V0.15 保留副本创建,仅更新流程文档,尚未合入主分支或同步 Skill 与角色提醒。</span></div> <div class="current-banner" role="status"><strong>V1.0 · 当前定版</strong><span>文档生效:2026-09-08。当前基线包含八角色、八阶段、测试结果人工确认及跨阶段流程。此文件原位维护,历史通过 Git 追溯;部署 Skill/Hook 及迁移在途项目须另行确认。</span></div>
</div> </div>
</header> </header>
<nav class="global-nav" aria-label="文档全局导航"> <nav class="global-nav" aria-label="文档全局导航">
<div class="global-nav-inner"> <div class="global-nav-inner">
<a class="nav-brand" href="#top">V0.16 REVIEW · 顶部</a> <a class="nav-brand" href="#top">V1.0 CURRENT · 顶部</a>
<a href="#changelog">版本记录</a> <a href="#changelog">版本记录</a>
<a href="#overview">总流程</a> <a href="#overview">总流程</a>
<a href="#rules">通用规则</a> <a href="#rules">通用规则</a>
@@ -693,83 +693,23 @@
<div class="if-section-head"> <div class="if-section-head">
<div class="if-section-no">VERSION / CHANGELOG</div> <div class="if-section-no">VERSION / CHANGELOG</div>
<div> <div>
<h2>版本变更记录</h2> <h2>当前版本与维护方式</h2>
<p>每个新版本都保留上一版文件,并在当前版本中记录变更内容、影响范围和基线状态。</p> <p>只维护这一份当前流程文档,旧版内容与变更通过 Git 提交历史追溯。</p>
</div> </div>
</div> </div>
<div class="if-callout"> <div class="if-callout">
<strong>版本管理规则:</strong>从 V0.8 开始,新版本通过复制当前最新文件创建,禁止覆盖或删除历史版本。评审版本标记为 REVIEW,并记录创建日期和待定的生效日期;只有取得明确的定版确认后,才改为 CURRENT,并同步更新文件名、页面标题、导航、页脚、本表及仓库中的当前流程基线引用。 <strong>版本管理规则:</strong>文档固定保存在 <code>doc/Codex多角色项目推进总流程图.html</code>,后续直接修改同一文件,不再创建日期目录或版本副本。变更经确认后更新当前版本标识、生效日期及相关 Skill、Hook 和仓库说明;部署和在途项目迁移另行确认。
</div> </div>
<div class="if-callout" style="margin-top: 14px">
<strong>版本号口径:</strong>V0.x 用于验证期的角色、阶段、成果、信息流和文档管理调整;当角色责任与八阶段流程稳定且完成整体验证后,再升级为 V1.0。
</div>
<div class="if-table-wrap" style="margin-top: 22px"> <div class="if-table-wrap" style="margin-top: 22px">
<table> <table>
<thead> <thead>
<tr><th>版本</th><th>日期</th><th>状态</th><th>主要变更</th><th>影响范围</th></tr> <tr><th>版本</th><th>日期</th><th>状态</th><th>内容范围</th><th>历史追溯</th></tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td><b>V0.16</b></td><td>创建:2026-09-07<br>生效:待定</td><td>REVIEW</td> <td><b>V1.0</b></td><td>文档生效:2026-09-08</td><td>CURRENT</td>
<td>删除缺陷清单中的汇总统计要求;测试结果增加项目负责人(Hub 对应真人负责人)确认,统一无缺陷路径、阶段门禁和验收入口。</td> <td>八角色、八阶段、角色职责与成果、测试结果人工确认、跨阶段协调及模板复用规则。</td>
<td>阶段 6 总览与信息流、通用确认规则、成果矩阵及阶段 7 入口;仅文档评审,未同步 Skill。</td> <td>使用 <code>git log --all -- doc/</code> 查询历史;已运行项目保留其登记基线,不因仓库更新自动迁移。</td>
</tr>
<tr>
<td><b>V0.15</b></td><td>创建:2026-09-04<br>生效:2026-09-07</td><td>保留原版</td>
<td><ol><li>精简业务及产品资料与选填项;立项增加平台 SDK、个参资料,项目风险前移阶段 2,删除独立产品资料整合。</li><li>取消独立接口契约、架构决策记录和硬件实现资料;阶段 3 硬件约束选填,明确阶段 5 硬件、底层交付。</li><li>补充工时评估;产品确认授权范围内资源与排期,预算、采购或对外承诺由业务批准。</li><li>精简测试输入,改用缺陷清单并共同商议责任人;可靠性、专项报告及提测佐证选填。</li><li>平台与客户验收可按条件并行且须双通过;平台由产品出具结论、客户由业务确认,测试资料直交产品。</li><li>结项聚焦资料收集与复盘;豁免精简为项目负责人审批,统一编号、成果引用及 Skill、角色提醒。</li></ol></td>
<td>文档版本管理、阶段 1–8 总流程、成果矩阵、关联信息流、Skill 及八角色 Hook。</td>
</tr>
<tr>
<td><b>V0.14</b></td><td>2026-09-02</td><td>HISTORICAL</td>
<td>人工可读的正式成果、过程成果、标题、正文和结论统一使用中文,机器字段与枚举只保留在运行时工具参数中;项目Hub通过 Etunel 角色清单读取实际 <code>role</code>、<code>display_name</code> 和 <code>relationship</code>,成员角色不再向负责人追问成员、会话或任务编号;新增同源 JSON 和中文 HTML 两种版本的《项目成员清单》,并补充按阶段整理成果、过程记录、项目进度和成果索引的留档规则。</td>
<td>全局成果语言、项目创建、角色登记、Etunel 实际字段、项目文件与进度留档、项目Hub与各成员角色约束和内部培训材料。</td>
</tr>
<tr>
<td><b>V0.13</b></td><td>2026-08-28</td><td>HISTORICAL</td>
<td>完成八角色跨文件一致性审核;新增项目成员/多账户/自定义角色规则和状态术语词典;区分项目创建时成员登记与阶段 4 角色分工;统一 NA、测试证据包、软硬件接口契约 和业务术语;同步测试主责的缺陷报告闭环;将信息流图谱顺序统一为阶段 3 方案设计后进入阶段 4 项目规划;重新生成项目Hub系统提示词。</td>
<td>全局角色与成员管理、状态与成果术语、阶段 1 业务输入、阶段 4 成员/任务分工、阶段 6 缺陷闭环、项目Hub提示词。</td>
</tr>
<tr>
<td><b>V0.12</b></td><td>2026-08-28</td><td>HISTORICAL</td>
<td>校正 测试计划 的阶段 3 主责;统一 测试计划 和五类阶段 6 正式成果,完善共同最低结构、NA/BLOCKED/成果豁免 边界、缺陷分析报告 主责、平台提测与业务验收边界;把缺陷专业归类和根因责任从项目Hub/测试的越权表述中移回有权角色。</td>
<td>阶段 2 可测性评审、阶段 3 测试计划、阶段 5 提测准备、阶段 6 正式测试与缺陷闭环、阶段 7 验收证据、阶段 8 发布冒烟。</td>
</tr>
<tr>
<td><b>V0.11</b></td><td>2026-08-28</td><td>HISTORICAL</td>
<td>校正硬件角色的阶段 3 设计职责;拆分 硬件设计包 与 硬件实现资料;完善原理图、PCB、Gerber/钻孔、BOM、贴片/生产资料、板卡/批次标识、板级测量和硬件版本链;建立 ECO→底层兼容评估→应用固件确认→技术版本矩阵→测试复验闭环。</td>
<td>阶段 3 硬件设计、阶段 5 板卡/样机实现、阶段 6 硬件 ECO 与回归、阶段 7 验收返工、阶段 8 硬件归档。</td>
</tr>
<tr>
<td><b>V0.10</b></td><td>2026-08-28</td><td>HISTORICAL</td>
<td>校正嵌入式底层的阶段 3 设计职责;完善 嵌入式底层实现资料 最低内容、可消费/修复/归档版演进、板卡/BOM/ECO 与接口契约绑定、专项测试固件限制、应用层重新合版和发布归档边界;扩充流程图底层成果说明。</td>
<td>阶段 3 方案设计、阶段 5 底层实现与应用合版、阶段 6 底层缺陷回归、阶段 7 验收返工、阶段 8 底层归档。</td>
</tr>
<tr>
<td><b>V0.9</b></td><td>2026-08-28</td><td>HISTORICAL</td>
<td>校正嵌入式应用层的阶段 3 设计职责;完善 嵌入式应用层固件包 最低内容、候选/提测/回归/发布版演进、底层重新合版、硬件变化兼容确认和最终固件唯一出口约束;继续清理项目Hub旧称和广播表述。</td>
<td>阶段 3 方案设计、阶段 5 实现集成、阶段 6 缺陷回归、阶段 7 验收返工、阶段 8 发布归档。</td>
</tr>
<tr>
<td><b>V0.8</b></td><td>2026-08-28</td><td>HISTORICAL</td>
<td>新增文档内版本变更记录、版本号口径和历史文件保留规则;从本版本起不再通过重命名覆盖上一版。</td>
<td>全局导航、版本管理、项目Hub流程基线引用。</td>
</tr>
<tr>
<td><b>V0.7</b></td><td>2026-08-28</td><td>HISTORICAL</td>
<td>修正技术负责人在阶段 3 与阶段 4 的责任顺序;明确总体方案、软硬件接口契约和架构决策的主责;明确项目Hub维护版本矩阵、技术负责人确认版本匹配。</td>
<td>阶段 3 方案设计、阶段 4 项目规划、阶段 5 集成提测、技术缺陷与变更评估。</td>
</tr>
<tr>
<td><b>V0.6</b></td><td>2026-08-28</td><td>HISTORICAL</td>
<td>统一产品角色的阶段 2 与阶段 7 正式成果;补齐产品发起早期风险预检、产品定义评审和验收闭环;客户平台资料改为参考输入基线。</td>
<td>阶段 1 早期预检、阶段 2 产品定义、阶段 7 业务验收。</td>
</tr>
<tr>
<td><b>V0.5</b></td><td>未登记</td><td>HISTORICAL BASELINE</td>
<td>建立八阶段主流程、项目Hub中枢角色、角色化状态同步 S.1–S.4、异常协同 E.1–E.4、变更升级 X.1–X.6 和成果豁免 Y.1–Y.7;形成正式成果追踪矩阵。</td>
<td>总流程、项目Hub、全局信息流、门禁和成果追踪。</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -866,7 +806,7 @@
<div class="if-atlas" id="information-flow-atlas"> <div class="if-atlas" id="information-flow-atlas">
<section class="if-intro"> <section class="if-intro">
<div class="if-eyebrow">STAGE INFORMATION FLOW ATLAS · V0.16 · REVIEW</div> <div class="if-eyebrow">STAGE INFORMATION FLOW ATLAS · V1.0 · CURRENT</div>
<h2>各阶段角色信息流图谱</h2> <h2>各阶段角色信息流图谱</h2>
<p>以下内容直接承接总流程图,展示每条会影响成果、决策、门禁、返工或版本的正式信息流,并说明触发条件、发送方、接收方、载荷、确认要求和阻塞约束。</p> <p>以下内容直接承接总流程图,展示每条会影响成果、决策、门禁、返工或版本的正式信息流,并说明触发条件、发送方、接收方、载荷、确认要求和阻塞约束。</p>
<div class="if-quicknav"> <div class="if-quicknav">
@@ -1226,7 +1166,7 @@
</tbody></table></div> </tbody></table></div>
</section> </section>
</div> </div>
<div class="footer">Codex 多角色项目推进总流程图与阶段信息流图谱 · V0.16 · REVIEW</div> <div class="footer">Codex 多角色项目推进总流程图与阶段信息流图谱 · V1.0 · CURRENT</div>
</main> </main>
<script> <script>
(() => { (() => {
@@ -2,7 +2,7 @@
登记日期:2026-09-08。根据用户提供的 `D:/AI全流程辅助/项目推进总流程` 只读学习,供后续资料生成和审核时按需查阅。 登记日期:2026-09-08。根据用户提供的 `D:/AI全流程辅助/项目推进总流程` 只读学习,供后续资料生成和审核时按需查阅。
流程依据:[V1.0 总流程图](../Codex多角色项目推进总流程图-V1.0.html)。本参考不是新流程基线,不改变 Skill、Hook、角色职责或门禁,也不表示已部署或推送。 流程依据:[V1.0 总流程图](../Codex多角色项目推进总流程图.html)。本参考不是新流程基线,不改变 Skill、Hook、角色职责或门禁,也不表示已部署。
## 先读什么 ## 先读什么
@@ -65,6 +65,6 @@
- 应用包里的高低版本可用于升级测试;自测表明确要求高低版本。应区分测试配套版本与唯一最终提测版本,不能仅凭两个版本文件名就判错。 - 应用包里的高低版本可用于升级测试;自测表明确要求高低版本。应区分测试配套版本与唯一最终提测版本,不能仅凭两个版本文件名就判错。
- V1.0 灯态描述的“绑定”改“配网”不意味着把平台账号绑定、解绑等实际业务术语全局替换。 - V1.0 灯态描述的“绑定”改“配网”不意味着把平台账号绑定、解绑等实际业务术语全局替换。
## 本次未做的事 ## 维护与执行边界
没有修改、移动或删除原始模板;没有修改 V0.15、V0.16、V1.0 HTML、Skill、Hook 或已安装缓存;没有执行烧录/SDK/模板脚本、外部下载、真实通知或项目审批;没有 Git 提交、推送或合并。所有本次新增内容仅为本地参考笔记与源文件索引。 这里只维护参考笔记和源文件索引,不包含原始模板;源目录是登记时的来源,不保证在其他设备可访问。后续直接更新当前文件,通过 Git 保留历史,不另建日期或版本目录。参考资料不授权执行烧录、SDK 或模板脚本、外部下载、真实通知、项目审批,也不会自动修改 Etunel 配置或已安装缓存。
@@ -1,6 +1,6 @@
# 阶段 6 模板只读学习记录 # 阶段 6 模板只读学习记录
核对日期:2026-09-08。源目录:`D:/AI全流程辅助/项目推进总流程/6、测试验证`。下文路径均相对于源目录。流程基准为 `D:/codex/EP-Hub-Skill-v015-release/doc/20260908/Codex多角色项目推进总流程图-V1.0.html`。 核对日期:2026-09-08。源目录:`D:/AI全流程辅助/项目推进总流程/6、测试验证`。下文路径均相对于源目录。流程基准为[当前 V1.0 总流程图](../Codex多角色项目推进总流程图.html)。
## 阅读范围与边界 ## 阅读范围与边界
+2 -2
View File
@@ -56,10 +56,10 @@ Etunel 负责当前角色和会话绑定。不要向负责人询问或复述 rol
- 准备派发、回复、跨角色中继、完成或报告阻塞:读 [Etunel 任务消息流程](references/etunel-message-lifecycle.md)。 - 准备派发、回复、跨角色中继、完成或报告阻塞:读 [Etunel 任务消息流程](references/etunel-message-lifecycle.md)。
- 定义或校验成果、状态、版本、证据、额外产出或豁免:读 [成果与完成判定](references/artifacts-and-evidence.md)。 - 定义或校验成果、状态、版本、证据、额外产出或豁免:读 [成果与完成判定](references/artifacts-and-evidence.md)。
- 选择、复用或审核业务、产品、硬件、固件、测试及归档模板:读 [文档模板复用与审核](references/document-templates.md),再按实际任务定位原件;模板不自行改变职责、正式成果或门禁。 - 选择、复用或审核业务、产品、硬件、固件、测试及归档模板:读 [文档模板复用与审核](references/document-templates.md),再按实际任务定位原件;模板不自行改变职责、正式成果或门禁。
- 缺信息、错投、阻塞、超时升级、会议决定、返工、变更、流程跳转或模拟:读 [异常与协调](references/exceptions-and-coordination.md)。 - 缺信息、资料冲突、查询无新线索、旧任务收尾、错投、阻塞、超时升级、会议决定、返工、变更、流程跳转或模拟:读 [异常与协调](references/exceptions-and-coordination.md)。
- 编写给负责人、角色或群组的任务、提问、结果和状态消息:读 [人类可读沟通](references/human-readable-communication.md)。 - 编写给负责人、角色或群组的任务、提问、结果和状态消息:读 [人类可读沟通](references/human-readable-communication.md)。
- 创建项目资料目录、保存成员清单、归档成果、更新进度或阶段记录:读 [项目文件与进度留档](references/project-files-and-progress.md)。 - 创建项目资料目录、保存成员清单、归档成果、更新进度或阶段记录:读 [项目文件与进度留档](references/project-files-and-progress.md)。
- 仅当当前会话是 Hub 且发生钉钉汇报事件:读 [钉钉项目进度汇报](references/dingtalk-progress-reporting.md)。 - 仅当当前会话是 Hub 且需钉钉进度汇报或负责人辅助提醒:读 [钉钉项目进度汇报](references/dingtalk-progress-reporting.md),实际调用再读其中的脚本用法。
- 测试角色仅在任务类型匹配时,按 [测试职责](references/roles/testing.md) 中的二级链接读取更深细则。 - 测试角色仅在任务类型匹配时,按 [测试职责](references/roles/testing.md) 中的二级链接读取更深细则。
不要为“全面了解”一次加载全部引用或全部角色文件。Etunel Hook 源文件独立配置,不属于本 Skill 的渐进式发现树。 不要为“全面了解”一次加载全部引用或全部角色文件。Etunel Hook 源文件独立配置,不属于本 Skill 的渐进式发现树。
@@ -1,17 +1,15 @@
# Hub 钉钉项目进度汇报 # Hub 钉钉项目进度汇报
仅当当前会话是 Hub 且发生本文件定义的汇报事件时读取。钉钉是面向项目群组的单向辅助可见性与异常提醒,不是任务派发、跨角色沟通、审批、门禁、人类确认或项目记录的事实源。 仅当当前会话是 Hub 且发生汇报事件或需提醒负责人处理具体问题时读取。钉钉是面向项目群组的单向辅助可见性与异常提醒,不是任务派发、跨角色沟通、审批、门禁、人类确认或项目记录的事实源。
## 权限与唯一入口 ## 权限与封装入口
- 只有 Hub/主 Agent 可以发送;成员 Agent 只把成果、状态或阻塞交给 Hub。 - 只有 Hub/主 Agent 可以发送;成员 Agent 只把成果、状态或阻塞交给 Hub。
- 项目配置为启用时视为持续发送授权,不需每条消息再次询问。 - 项目配置为启用时视为持续发送授权,不需每条消息再次询问。
- 只能调用项目封装入口: - 只用项目封装:`dingtalk-progress` 处理五类项目事件,`dingtalk-bot-message` 处理自定义中文摘要或负责人提醒;二者共用 `scripts/dingtalk_notify.py`。Windows、macOS/Linux 的命令及文件正文、人员提醒用法见 [通知脚本用法](dingtalk-script-usage.md),实际调用前按需读取。
`./scripts/dingtalk-progress <start|milestone|blocked|complete|failed> "<中文 Markdown 摘要>"`
- Agent 不得直接调用底层 OpenAPI、`dws api` 或其他消息入口绕过封装脚本。 - Agent 不得直接调用底层 OpenAPI、`dws api` 或其他消息入口绕过封装脚本。
- Agent 不读取、修改、输出或请求 Client Secret、DING_SEC、App Token、Client ID、robotCode、openConversationId 等凭据和内部标识。 - Agent 不自行打开、展示或修改真实配置,不请求负责人在会话中提供密钥和内部标识;由封装在本地读取 `config.env` 中的 `DINGTALK_PROGRESS_CLIENT_SECRET` 并完成认证,不再使用环境变量或钥匙串取密钥。
- Skill 与消息正文不得硬编码项目 Client ID、robotCode、openConversationId、Client Secret 或群名;项目差异只由 `.dingtalk/config.env` 和封装脚本处理。 - Skill 与消息正文不得硬编码项目 Client ID、robotCode、openConversationId、Client Secret 或群名;项目差异只由 `.dingtalk/config.env` 和封装脚本处理。
## 汇报单位与顺序 ## 汇报单位与顺序
@@ -64,13 +62,15 @@ Hub 必须先确认进展真实有效,并完成成果文件、成果索引和
不在正文中写运行时 ID、英文成果名、机器状态码或内部字段。只写已验证结论,不得包含密钥、Token、个人数据、客户敏感信息、大段日志、完整内部对话或未经验证的根因推断。 不在正文中写运行时 ID、英文成果名、机器状态码或内部字段。只写已验证结论,不得包含密钥、Token、个人数据、客户敏感信息、大段日志、完整内部对话或未经验证的根因推断。
负责人提醒只使用项目协作必需的已确认姓名;脚本为钉钉原生 @ 自动生成的人员标记是技术参数,不是要求 Hub 把人员编号写进摘要。需要负责人行动时,说明缺什么、影响什么、请谁做什么,以及回哪个角色会话补充结果。不要在群里建立另一套任务或审批流程。
## 调用与结果 ## 调用与结果
1. Hub 判断事件并形成中文 Markdown 摘要。 1. Hub 判断事件并形成中文 Markdown 摘要。
2. 调用封装脚本一次;脚本自行处理配置检查、发送和有限重试。 2. 调用封装脚本一次;脚本自行处理配置检查、发送和有限重试。
3. 只有真实发送响应包含非空 `processQueryKey`,才表示钉钉服务端已经接受;不证明群成员已读。 3. 只有真实发送响应包含非空 `processQueryKey`,才表示钉钉服务端已经接受;不证明群成员已读。
4. 缺少或未启用配置时安全跳过;预览模式只检查请求,不视为真实发送。 4. 缺少或未启用配置时不真实发送;预览在本地读取已有配置并生成正文,不查通讯录、不使用密钥认证、不输出密钥、不联网,也不证明目标配置和 @ 有效。
5. 真实发送失败或没有有效 key 时,脚本最多总计尝试 3 次,间隔 10 秒;首次成功立即停止。 5. 只有明确的域名解析失败才自动重试,总计最多 3 次,间隔 10 秒;首次收到有效回执立即停止。认证、权限或未知错误不自动重试;超时、连接中断或缺少回执可能已发出,不盲目重发。单次命令最长等待 60 秒。
6. 三次仍失败只终止本次钉钉发送,Etunel 主任务继续。Hub 在本地结果中向负责人说明一次,不递归发送新的阻塞或失败通知,也不自动补发历史事件。 6. 有效回执优先于命令退出码,不因退出异常重复发送。发送停止或结果未知时,Etunel 主任务继续;Hub 在本地结果中向负责人说明一次,不递归发送新的阻塞或失败通知,不自动补发历史事件。
脚本输出和退出码只用于判断通知调用,不得把钉钉结果升级成项目门禁。Agent 不在脚本外追加重试循环。 脚本输出和退出码只用于判断通知调用,不得把钉钉结果升级成项目门禁。Agent 不在脚本外追加重试循环。
@@ -0,0 +1,61 @@
# Hub 通知脚本用法
仅在 Hub 已按 [通知规则](dingtalk-progress-reporting.md) 判断需要通知、准备调用脚本时读取。脚本须由部署负责人放在目标 Hub 项目的 `scripts/`;安装 Skill 本身不会部署脚本、配置或凭据。找不到入口时说明未部署并继续主任务,不猜绝对路径、不临时绕过封装。
## 选一个入口,不重复调用
共用实现为 `scripts/dingtalk_notify.py`,依赖 Python 3.9+;真实发送还需原生 `dws`。相对命令从目标项目根目录执行。
所有系统由封装统一从目标项目 `.dingtalk/config.env` 的 `DINGTALK_PROGRESS_CLIENT_SECRET` 读取机器人密钥,不回退到环境变量或钥匙串。配置由部署负责人本地维护;Hub 不打开或展示真实配置、不在会话索取密钥。普通发送不需要 DWS 用户登录,按姓名提醒才额外需要同组织的已登录账号配置。
| 环境 | 项目事件 | 中文摘要/负责人提醒 |
| --- | --- | --- |
| 通用 Python | `python scripts/dingtalk_notify.py progress ...` | `python scripts/dingtalk_notify.py message ...` |
| Windows PowerShell 7.3+ | `./scripts/dingtalk-progress.ps1 ...` | `./scripts/dingtalk-bot-message.ps1 ...` |
| macOS/Linux | `sh scripts/dingtalk-progress ...` | `sh scripts/dingtalk-bot-message ...` |
Python 在 macOS/Linux 通常叫 `python3`;Windows 也可用 `py -3`。这是同一实现的不同入口,不是失败后依次尝试的备选发送渠道。原有 `./scripts/dingtalk-progress` 调用仍可使用,前提是该文件有执行权限。
## 项目事件通知
```text
python scripts/dingtalk_notify.py progress milestone "**已完成**:本阶段成果已确认并归档。下一步安排联调。"
```
事件仍只有 `start`、`milestone`、`blocked`、`complete`、`failed`。脚本自动加项目名、中文状态和本机带时区时间;不自动判断项目阶段或完成状态。
多行 Markdown 可直接作为一个参数传入。较长或含引号的摘要优先保存为 UTF-8 正文文件,再用 `--file`;不要把大段成果或日志当通知:
```text
python scripts/dingtalk_notify.py progress milestone --file "项目资料/00-项目管理/通知摘要.md"
```
这里的路径是调用示例,须先在实际项目中生成相应摘要,不能假定文件存在。文件路径相对当前工作目录解析;`--file` 是读取消息正文,不是上传附件。进度摘要和 `--file` 二选一。
## 自定义摘要与负责人提醒
```text
python scripts/dingtalk_notify.py message --title "联调需补充接口说明" --text "缺少接口返回值说明,联调暂时无法继续。请负责该接口的人补齐,并在自己的角色会话中同步结果。"
```
这个入口允许自定义标题,但不新增项目状态,不替代应该记录的阻塞事件。需要 @ 已知负责人时追加 `--at "完整姓名"`;多个姓名用逗号分隔或重复传入 `--at`。正文也可用 `--file` 代替 `--text`。
- 负责人称呼来自 Etunel 已绑定的 `member_alias`,不要把角色名称 `display_name` 当人员姓名,也不要让人每轮确认身份。
- 按姓名查找由封装执行,只使用部署时配置的同组织账号。没有配置账号、查不到、重名或返回无法识别时,本条不发送;请负责人澄清姓名或让部署负责人修正映射,不选择第一个、不换组织碰运气。
- `--at-id "姓名=userId"` 仅兼容已有且同组织的有效映射,由真实工具结果或部署负责人提供;不从 Etunel 角色标识推导,不主动索要人员编号。
- 默认发项目配置群。`--group` 仅保留给用户明确指定其他目标群的情形,不由 AI 猜群或扩大通知范围。
- 同一事项只形成一条合并通知。已经由事件通知说明清楚时,不额外发一遍提醒;没有变化时不循环催促。
- 通知必须指向实际下一步。负责人在线下讨论后回自己的角色会话补充结果,由角色完成原任务并经 Etunel 返回 Hub;群里已读或口头回复都不自动完成任务。
## 预览与结果
两个入口均支持 `--dry-run`;项目配置的预览开关也同时约束两者。预览不要求安装 `dws` 或填写密钥;会读取已有配置,但不使用密钥认证、不输出密钥、不查人员、不发送,只显示本地正文,@ 对象及目标配置未验证。
标准输出为精简 JSON,面向负责人时转述中文结果,不粘贴机器输出:
- `accepted`:服务端已接受,并返回有效 `processQueryKey`;不代表已读。
- `preview`:只有预览,未发送。
- `skipped`:未配置或未启用,未发送。
- `error`:本次停止;按说明区分失败和结果未知,不声称已发送或自行补发。
前三种退出码为 0,错误为 1;错误参数可能返回 2,因此不能只凭退出码 0 宣布已发送。封装内部处理有限重试,不要在脚本外再套重试。缺配置、凭据、权限或安装问题交部署负责人处理;通知失败不影响 Etunel 主流程。
@@ -1,6 +1,6 @@
# 异常与协调 # 异常与协调
出现缺失信息、错投、职责冲突、阻塞、超时、复杂线下会议、缺陷、返工、变更、流程跳转、成果豁免或模拟流程时读取。所有成员 Agent 跨角色通信都经 Hub;现阶段没有成员直连。 出现缺失信息、资料冲突、查找无新线索、旧任务收尾、错投、职责冲突、阻塞、超时、复杂线下会议、缺陷、返工、变更、流程跳转、成果豁免或模拟流程时读取。所有成员 Agent 跨角色通信都经 Hub;现阶段没有成员直连。
## 缺失信息 ## 缺失信息
@@ -15,6 +15,22 @@
Hub 有登记答案时直接补足;没有时只向实际信息所有者角色创建必要查询或任务。取得确认结果后返回原任务,不广播无关角色。 Hub 有登记答案时直接补足;没有时只向实际信息所有者角色创建必要查询或任务。取得确认结果后返回原任务,不广播无关角色。
## 查找无新线索
已检查当前有效资料、可访问文件并向负责人合理查找后,若仍无新线索,角色汇总已确认事实、无法证明的内容、影响、继续所需条件及可行选择,由 Hub 交有权负责人决定下一步。没有新来源或新条件,不重复派发同内容查询,也不设统一查询次数或时长。无关工作继续;批准限定用途或选择替代方案不等于证明未知事实,更不能替代必要质量、安全条件。
## 资料冲突与文档纠错
文档参数、其他有效资料与实际实现相互冲突时,发现角色说明具体差异及受影响内容,经 Hub 定向交对应专业角色核实。产品需求和参数由产品确认,技术实现由相应技术角色确认;涉及多个领域时分别取得必要结论,Hub 不凭文件名、到达先后或自己的推断选定答案。只暂停依赖该争议结论的受影响修改,无关工作继续。
确认是文档错误时,由原主责角色修正并取得本角色负责人确认,Hub 按现有版本规则更新有效资料、保留纠错依据并同步受影响角色,不新增可视化评审或审批步骤。若纠正实际改变已确认要求或下游行动,则按实质影响走既有变更流程,不能用“文档纠错”绕过必要批准;已经按旧资料开展的工作一并检查是否受影响。
## 新决定与旧任务收尾
有权负责人更改决定后,Hub 在继续派发受影响工作前,合并向实际受影响角色说明:哪些停止、哪些继续、改用哪些输入,以及已有成果如何处理;同步现有阶段记录、进度及适用的成果索引,不另建任务清理表,也不等待全员再次确认。
晚到结果按当前决定核对适用性,有用部分保留供复用;不因刚收到结果就恢复旧要求、覆盖当前有效成果或重新催办已经停止的事项。不能沿用的部分说明原因,只安排当前仍必要的后续工作。这里约束业务安排及结果处理,不宣称已取消运行时队列消息,不重造 Etunel 取消、投递或去重机制。
## 结果不完整 ## 结果不完整
- 原任务目标不变;Hub 只列缺失成果、内容、证据、版本、自审或确认。 - 原任务目标不变;Hub 只列缺失成果、内容、证据、版本、自审或确认。
@@ -128,6 +144,7 @@ Etunel 投递、成员返回、会话或消息链路实际中断是流程阻塞
- 可以使用真实已知数据和为流程验证构造的数据,清楚区分真实与模拟; - 可以使用真实已知数据和为流程验证构造的数据,清楚区分真实与模拟;
- 模拟值影响结论时,相关成果整体标记为模拟; - 模拟值影响结论时,相关成果整体标记为模拟;
- 启用模拟默认只改变数据和执行方式,不自动跳过角色交接;Hub 用一句话说明接下来演练的顺序,不新增审批文件;
- 可以按明确指令临时跳过或进入后续节点,并记录假设、风险和恢复项,不让门禁卡死流程验证; - 可以按明确指令临时跳过或进入后续节点,并记录假设、风险和恢复项,不让门禁卡死流程验证;
- 负责人确认的是模拟使用和流程推进,不是数据真实性; - 负责人确认的是模拟使用和流程推进,不是数据真实性;
- 不静默切回正式模式,不让模拟结论进入客户承诺、真实测试或生产发布; - 不静默切回正式模式,不让模拟结论进入客户承诺、真实测试或生产发布;
@@ -135,6 +152,8 @@ Etunel 投递、成员返回、会话或消息链路实际中断是流程阻塞
模拟文件和阶段记录必须明确标注,不能混入正式成果目录中的当前有效版本。 模拟文件和阶段记录必须明确标注,不能混入正式成果目录中的当前有效版本。
中途转模拟时,保留真实进度、未解决问题和恢复点,按已授权范围暂停真实工作;模拟完成不关闭真实缺陷。提前形成的下游模拟材料可保存待用,依赖满足后只补必要输入,不全部重做,也不提前登记阶段完成。恢复真实工作须明确授权,从真实未完成处继续,不能沿用模拟通过结论。
## 真实授权事项 ## 真实授权事项
需要真实授权时,Hub 提供事实、选项、建议、影响和最晚决定点。授权范围内的资源与排期调整交产品确认,可同步业务;涉及预算、采购、对外承诺、业务范围、项目暂停或取消时交有权业务负责人或项目发起人批准。未决定前保持真实等待或阻塞,不解释为默认批准。 需要真实授权时,Hub 提供事实、选项、建议、影响和最晚决定点。授权范围内的资源与排期调整交产品确认,可同步业务;涉及预算、采购、对外承诺、业务范围、项目暂停或取消时交有权业务负责人或项目发起人批准。未决定前保持真实等待或阻塞,不解释为默认批准。
@@ -56,6 +56,8 @@ Hub 只检查角色、必填内容、成果、版本、证据、负责人确认
不要把 role ID、member ID、session ID、消息 ID 或机器状态放进人类任务正文。纯信息、澄清或授权请求不要求空文件,但要给足背景、问题、影响和需要的结论。详细写法见 [人类可读沟通](human-readable-communication.md)。 不要把 role ID、member ID、session ID、消息 ID 或机器状态放进人类任务正文。纯信息、澄清或授权请求不要求空文件,但要给足背景、问题、影响和需要的结论。详细写法见 [人类可读沟通](human-readable-communication.md)。
沿用有效资料,明确本次是新做、复用还是修改,实际使用哪份工程或资料;区分开工必需输入与本次要制作的产出,不把后者当成缺件退回上游。本地未找到不代表项目没有现成资料,归属或位置不明时再向对应负责人核实;不另设开工审批或检查表。
期限优先继承已批准的《客户里程碑要求》《里程碑要求》或《项目计划》。没有明确期限时集中询问负责人一次;不影响当前依赖时可写“待确认”并登记风险,影响关键依赖或门禁时保持阻塞。模拟项目可使用明确标注为模拟的假设日期。 期限优先继承已批准的《客户里程碑要求》《里程碑要求》或《项目计划》。没有明确期限时集中询问负责人一次;不影响当前依赖时可写“待确认”并登记风险,影响关键依赖或门禁时保持阻塞。模拟项目可使用明确标注为模拟的假设日期。
## Hub 中介的跨角色信息流 ## Hub 中介的跨角色信息流
@@ -71,6 +73,8 @@ Hub 只检查角色、必填内容、成果、版本、证据、负责人确认
普通问题默认一组聚合问题和一组答复。出现新的实质问题才追加;复杂异常改走 [异常与协调](exceptions-and-coordination.md) 的线下会议与《会议决定记录》。 普通问题默认一组聚合问题和一组答复。出现新的实质问题才追加;复杂异常改走 [异常与协调](exceptions-and-coordination.md) 的线下会议与《会议决定记录》。
资料或参数冲突交对应专业角色核实;合理查找后仍无新线索时,汇总事实、未知和选择交有权负责人决定,不重复派发相同查询。决定变化后的旧任务及晚到结果按 [新决定与旧任务收尾](exceptions-and-coordination.md#新决定与旧任务收尾) 处理。
## 派发、等待与入站处理 ## 派发、等待与入站处理
按 [Etunel 任务消息流程](etunel-message-lifecycle.md) 派发和结算: 按 [Etunel 任务消息流程](etunel-message-lifecycle.md) 派发和结算:
@@ -155,7 +159,7 @@ Hub 不把 AI 自主生成当成人类确认。Hub 自己的负责人在以下
## 钉钉进度 ## 钉钉进度
只有 Hub 可以发送项目进度。Hub 先完成成果和进度留档,再在项目开始、可验证里程碑、正式阻塞或 Etunel 中断、总体完成、总体最终失败时,按 [钉钉项目进度汇报](dingtalk-progress-reporting.md) 调用封装脚本。钉钉不参与任务派发、批准、门禁或完成判定。 只有 Hub 可以发送项目进度。Hub 先完成成果和进度留档,再在项目开始、可验证里程碑、正式阻塞或 Etunel 中断、总体完成、总体最终失败时,按 [钉钉项目进度汇报](dingtalk-progress-reporting.md) 调用进度封装。需要负责人处理具体问题或超过约定时间需提醒时,可按 [脚本用法](dingtalk-script-usage.md) 发送简短中文提醒;同一事项合并发送,不重复发进度和提醒。提醒负责人在自己的角色会话中补充结果,成员仍经 Etunel 向 Hub 返回;钉钉不参与任务派发、批准、门禁或完成判定,通知失败不阻塞主流程。
## Hub 禁止事项 ## Hub 禁止事项
@@ -25,6 +25,8 @@
同时理解 Hub 给出的本阶段完成约定,并在本角色范围内核对哪些是本阶段必须完成、哪些留到后续、哪些当前不需要。一次列出当前能预见的信息缺口。负责人确认理解、补足输入或明确可以开始后再执行。不要复述运行时 ID、英文状态和内部字段;详细写法见 [人类可读沟通](human-readable-communication.md)。 同时理解 Hub 给出的本阶段完成约定,并在本角色范围内核对哪些是本阶段必须完成、哪些留到后续、哪些当前不需要。一次列出当前能预见的信息缺口。负责人确认理解、补足输入或明确可以开始后再执行。不要复述运行时 ID、英文状态和内部字段;详细写法见 [人类可读沟通](human-readable-communication.md)。
在这次对齐中分清新做、复用或修改,认准实际使用的工程或资料,区分应先取得的输入与本次要制作的产出。已有明确答案直接沿用,不因本地未找到就断言项目没有现成工程,也不要求上游先交出本角色尚待制作的成果。
## 执行本角色任务 ## 执行本角色任务
1. 只使用 Hub 给出的当前有效基线,且只处理本角色范围。 1. 只使用 Hub 给出的当前有效基线,且只处理本角色范围。
@@ -32,7 +34,7 @@
3. 记录实际方法、环境、版本、构建、板卡、配置或其他可复查证据。 3. 记录实际方法、环境、版本、构建、板卡、配置或其他可复查证据。
4. 分开说明已确认事实、证据、专业判断、假设、批准决定、开放问题、依赖和剩余风险。 4. 分开说明已确认事实、证据、专业判断、假设、批准决定、开放问题、依赖和剩余风险。
5. 未执行验证说明原因和影响,不虚构构建、测试、设备结果或人类意见。 5. 未执行验证说明原因和影响,不虚构构建、测试、设备结果或人类意见。
6. 发现范围、接口、版本、角色责任、日期或验收变化时不静默修改基线,提交 Hub 走《变更申请》。 6. 发现资料、参数与实际实现冲突时,列明差异,经 Hub 交对应专业角色核实,不自行选一个答案。仅属文档错误时由原主责修正;实际改变已确认范围、接口、版本、角色责任、日期或验收时,提交 Hub 走《变更申请》。详见 [资料冲突与文档纠错](exceptions-and-coordination.md#资料冲突与文档纠错)。
7. 产出完成前不频繁发进度;只有必要补充、正式阻塞或 Hub 明确要求的关键状态才发送非终态消息。 7. 产出完成前不频繁发进度;只有必要补充、正式阻塞或 Hub 明确要求的关键状态才发送非终态消息。
8. 发现后续阶段会需要的资料时向 Hub 登记,但不自动扩大当前任务,也不把它写成当前阻塞。 8. 发现后续阶段会需要的资料时向 Hub 登记,但不自动扩大当前任务,也不把它写成当前阻塞。
@@ -49,6 +51,8 @@
不要逐句追问或绕过 Hub。普通问题默认一问一答;复杂异常按《会议决定记录》流程处理。 不要逐句追问或绕过 Hub。普通问题默认一问一答;复杂异常按《会议决定记录》流程处理。
合理查找后仍无新线索时,按 [查找无新线索](exceptions-and-coordination.md#查找无新线索) 汇总已知、未知及选择,不重复同样的查询。收到 Hub 更正安排后,仅继续仍有效的工作;旧任务已经产出的内容说明适用性并保留可复用部分,不自行恢复已停止工作,详见 [新决定与旧任务收尾](exceptions-and-coordination.md#新决定与旧任务收尾)。
只有缺失内容直接导致本次成果无法形成、完成条件无法判断、本角色无法作出当前决定,或构成当前必须处理的安全、合规、不可逆损失及已批准承诺风险时,才报告当前阻塞。仅供后续阶段使用的资料作为后续依赖返回,不阻塞本任务。 只有缺失内容直接导致本次成果无法形成、完成条件无法判断、本角色无法作出当前决定,或构成当前必须处理的安全、合规、不可逆损失及已批准承诺风险时,才报告当前阻塞。仅供后续阶段使用的资料作为后续依赖返回,不阻塞本任务。
## 状态与超时答复 ## 状态与超时答复
@@ -43,6 +43,8 @@
## 联调与最终提测版本 ## 联调与最终提测版本
认准实际产品工程、配套软件及最终固件输出位置,不把演示框架或旧镜像当成本轮成果。完整编译与打包尽量早验证,具体时机由研发与技术负责人按工程条件安排,不新增“第一轮必须完整编译通过”的门禁;组件编译成功不等于完整固件已经生成。
候选统一固件与硬件就绪时启动联调(F5.8),此时不要求已提交最终提测版本。联调问题关闭后,应用层提交最终提测版本(F5.9),再经 Hub 交技术负责人确认版本匹配与可提测(F5.10);不能倒置这两个条件。 候选统一固件与硬件就绪时启动联调(F5.8),此时不要求已提交最终提测版本。联调问题关闭后,应用层提交最终提测版本(F5.9),再经 Hub 交技术负责人确认版本匹配与可提测(F5.10);不能倒置这两个条件。
## 节点输出 ## 节点输出
@@ -48,6 +48,8 @@
5. 由 Hub 按产品指定范围派发同版本评估;产品汇总技术风险、时间风险及其他风险,形成并维护《项目风险》,再处置专业反馈,专业角色仍对自己的可行性和约束结论负责。仅供后续使用的资料不能自动成为当前阻塞。 5. 由 Hub 按产品指定范围派发同版本评估;产品汇总技术风险、时间风险及其他风险,形成并维护《项目风险》,再处置专业反馈,专业角色仍对自己的可行性和约束结论负责。仅供后续使用的资料不能自动成为当前阻塞。
6. 产品负责人确认基线;涉及客户范围、承诺或业务验收的内容取得业务批准。 6. 产品负责人确认基线;涉及客户范围、承诺或业务验收的内容取得业务批准。
产品对自己出具文档中的需求和参数把关。收到参数错误、同名资料内容不一致或实现与文档冲突的反馈时,核对确认依据;涉及技术事实,经 Hub 请对应专业角色核实,不自行猜测。产品修正自己的错误文档并经负责人确认后交 Hub 更新、定向同步;实质改变已确认要求或下游行动时按 [资料冲突与文档纠错](../exceptions-and-coordination.md#资料冲突与文档纠错) 处理,不把纠错变成额外评审。
《立项资料》纳入平台 SDK、个参资料。不另设《产品资料整合》成果,后续任务直接引用当前资料索引。器件、板框、串号及平台对接资料按实际范围以受控引用整合,不另立开发资料包,也不替代硬件设计包或测试计划。结构板框图在阶段 2 选填,进入依赖它的硬件任务前须补齐。 《立项资料》纳入平台 SDK、个参资料。不另设《产品资料整合》成果,后续任务直接引用当前资料索引。器件、板框、串号及平台对接资料按实际范围以受控引用整合,不另立开发资料包,也不替代硬件设计包或测试计划。结构板框图在阶段 2 选填,进入依赖它的硬件任务前须补齐。
## 立项资料与里程碑 ## 立项资料与里程碑
@@ -21,20 +21,21 @@
"核对成果可访问性、版本、依据、负责人确认及当前完成条件,不替专业角色作内容结论。按项目阶段目录留档,维护项目进度、成果索引和阶段记录;可直接交付的文件登记可查索引及接收确认,回退保留历史,已接受成果不因消息状态异常要求重复制作。", "核对成果可访问性、版本、依据、负责人确认及当前完成条件,不替专业角色作内容结论。按项目阶段目录留档,维护项目进度、成果索引和阶段记录;可直接交付的文件登记可查索引及接收确认,回退保留历史,已接受成果不因消息状态异常要求重复制作。",
"跳转、回退、暂缓由Hub负责人决定,记录原因、缺口、风险和补齐安排,不把跳过写成通过。选填成果不强制补交;必需成果豁免由主责提出,Hub对应真人负责人明确批准或拒绝,Hub AI只登记;批准仅满足该文件要求,其他测试、验收及阶段门禁仍须满足。模拟材料明确标注,不当成真实交付。", "跳转、回退、暂缓由Hub负责人决定,记录原因、缺口、风险和补齐安排,不把跳过写成通过。选填成果不强制补交;必需成果豁免由主责提出,Hub对应真人负责人明确批准或拒绝,Hub AI只登记;批准仅满足该文件要求,其他测试、验收及阶段门禁仍须满足。模拟材料明确标注,不当成真实交付。",
"收到状态查询按范围返回有依据的快照;纠正先核对证据、版本及一致性,再保留历史并同步受影响角色。确认或任务超过约定时间时带逾期对象、时长、证据、影响、替代方案和新期望时间升级给对应角色,项目级组织授权交业务;接收方内部确认后回复,未获必要确认仍等待或阻塞。", "收到状态查询按范围返回有依据的快照;纠正先核对证据、版本及一致性,再保留历史并同步受影响角色。确认或任务超过约定时间时带逾期对象、时长、证据、影响、替代方案和新期望时间升级给对应角色,项目级组织授权交业务;接收方内部确认后回复,未获必要确认仍等待或阻塞。",
"聚合信息缺口、定向协调;阻塞时请相应真人负责人参与并必要时线下沟通,结果返回各自角色会话。每条入站消息按实际工具回复或结算;可验证里程碑先留档再向钉钉汇报,项目开始、阻塞或中断、完成及最终失败按通知事件处理。", "聚合信息缺口,分清开工输入与本次产出;资料冲突交专业角色核实,无新线索交有权负责人决定,不反复查询。决定变化时说清哪些停止、继续及替换哪些输入,晚到结果不恢复旧要求。阻塞必要时请真人线下协调,结果回各自会话。入站消息按实际工具回复或结算;可验证里程碑先留档再报钉钉,开始、阻塞或中断、完成及最终失败按事件通知。",
"候选固件与硬件就绪启动联调,问题关闭后应用层提交最终提测版本,再由技术负责人确认。缺陷责任角色先返回原因与计划,Hub派发修复任务并同步确认结果给测试维护清单;回归只默认提供统一固件、版本矩阵、缺陷清单及必要变更说明,定位资料按问题提供;回归经Hub校验、技术负责人按需确认,不追加每轮必审。" "候选固件与硬件就绪启动联调,问题关闭后应用层提交最终提测版本,再由技术负责人确认。缺陷责任角色先返回原因与计划,Hub派发修复任务并同步确认结果给测试维护清单;回归只默认提供统一固件、版本矩阵、缺陷清单及必要变更说明,定位资料按问题提供;回归经Hub校验、技术负责人按需确认,不追加每轮必审。"
], ],
"non_goals": [ "non_goals": [
"不代专业角色开发、测试、验收或批准,不替成员完成任务,不自行批准豁免或创建Etunel角色。", "不代专业角色开发、测试、验收或批准,不替成员完成任务,不自行批准豁免或创建Etunel角色。",
"不重造Etunel的队列、传输、重试和去重机制;不以通知送达或消息结算替代成果完成。", "不重造Etunel的队列、传输、重试和去重机制;不以通知送达或消息结算替代成果完成。",
"钉钉只作辅助通知,不作任务通道、审批或事实来源;只使用项目封装脚本,不读取输出凭据,通知失败不阻塞主任务。" "钉钉只作辅助通知,不作任务通道、审批或事实来源;只用项目封装发送进度或负责人提醒,提醒讨论结果回各自角色会话;密钥由封装从本地config.env读取,不在会话索取或展示,不自行猜人员编号,通知失败不阻塞主任务。"
], ],
"completion_tools": [ "completion_tools": [
"etunel_list_roles", "etunel_list_roles",
"etunel_get_project_status", "etunel_get_project_status",
"etunel_send_to_role", "etunel_send_to_role",
"etunel_complete_coordination", "etunel_complete_coordination",
"./scripts/dingtalk-progress" "python scripts/dingtalk_notify.py progress",
"python scripts/dingtalk_notify.py message"
] ]
} }
``` ```
@@ -48,6 +49,6 @@
你是项目Hub,负责总体推进与成员Agent间的唯一中继,不替专业角色作结论。按需读取 etunel-role-collaboration 的相关细则;当前已确认流程和Etunel实际工具优先,不加载全部角色说明。 你是项目Hub,负责总体推进与成员Agent间的唯一中继,不替专业角色作结论。按需读取 etunel-role-collaboration 的相关细则;当前已确认流程和Etunel实际工具优先,不加载全部角色说明。
用 mcp__etunel__etunel_list_roles 获取实际角色;role用于路由,display_name用于显示,member_alias用于称呼已绑定负责人,不向人核对内部编号。按需用 mcp__etunel__etunel_get_project_status 查状态,但消息状态不等于成果完成。 用 mcp__etunel__etunel_list_roles 获取实际角色;role用于路由,display_name用于显示,member_alias用于称呼已绑定负责人,不向人核对内部编号。按需用 mcp__etunel__etunel_get_project_status 查状态,但消息状态不等于成果完成。
派发或回复用 mcp__etunel__etunel_send_to_role,每次一个实际角色;依赖就绪可多任务派发,同角色相关任务可打包。给足输入并写清成果要求,要求成员先与真人对齐、完成后自审并取得确认。已处理且无需回复的消息用 mcp__etunel__etunel_complete_coordination 结算;参数依当前schema,标识从入站消息取。 派发或回复用 mcp__etunel__etunel_send_to_role,每次一个实际角色;依赖就绪可多任务派发,同角色相关任务可打包。给足输入并写清成果要求,要求成员先与真人对齐、完成后自审并取得确认。已处理且无需回复的消息用 mcp__etunel__etunel_complete_coordination 结算;参数依当前schema,标识从入站消息取。
只检查当前必需条件,复用已确认成果,阶段通过后推进,后续资料按需索取。按阶段留档并更新进度、成果索引和阶段记录;允许真人直交的测试文件登记索引、版本及产品接收确认。阻塞时让相应负责人参与,必要时请Hub负责人协调;跳转、回退或暂缓记录真实状态。 只检查当前必需条件,复用成果,阶段通过后推进,后续资料按需索取。决定变化要说明旧任务停续和新输入;资料冲突交专业角色核实。按阶段留档并更新进度、成果索引和记录;直交测试文件登记索引、版本及产品收讫。阻塞请相应负责人协调;跳转、回退或暂缓如实记录,模拟默认不跳过交接、不关闭真实问题。
可验证里程碑留档后,调用 ./scripts/dingtalk-progress milestone "简短中文Markdown摘要" 汇报钉钉;开始、阻塞或中断、完成、最终失败按通知规则使用对应事件。用短段或列表说清结果、问题和下一步,不堆日志或中英混杂;失败不阻塞主流程,钉钉不能代替Etunel回复或结算。 可验证里程碑留档后,用 python scripts/dingtalk_notify.py progress milestone "中文Markdown摘要" 报钉钉;其他事件按通知规则。需负责人行动时用 message --title "中文标题" --text "问题与下一步",可按需读通知脚本细则使用 --file、--at 或对应系统入口;同一事项合并,不重复提醒。结果回各自角色会话;配置和密钥由封装读取,不打开真实config.env、不索要编号或凭据,不在脚本外重试,预览不算发送,通知失败不阻塞主流程、不代替Etunel回复或结算。
``` ```
@@ -11,7 +11,7 @@
"responsibilities": [ "responsibilities": [
"与业务共同确认需求基线;在产品定义阶段按问题选择评审角色,把需确认的问题交项目Hub定向派发,不自行联系成员Agent。", "与业务共同确认需求基线;在产品定义阶段按问题选择评审角色,把需确认的问题交项目Hub定向派发,不自行联系成员Agent。",
"形成立项资料(包括平台SDK、个参资料)、测试验收标准、里程碑要求和项目风险;组织产品定义确认并交业务批准范围,持续汇总各专业风险。立项和里程碑表的人力、费用、预算等仅作早期估算与适用性说明,不替代正式资源、排期和预算批准,也不自动增加阶段2门禁。", "形成立项资料(包括平台SDK、个参资料)、测试验收标准、里程碑要求和项目风险;组织产品定义确认并交业务批准范围,持续汇总各专业风险。立项和里程碑表的人力、费用、预算等仅作早期估算与适用性说明,不替代正式资源、排期和预算批准,也不自动增加阶段2门禁。",
"确认方案不偏离产品定义;在授权范围内确认实际人员、资源与排期,预算、采购和对外承诺交业务批准。", "对产品文档中的需求和参数把关,异常经Hub交对应专业角色核实,修正本角色文档并由负责人确认;实质改变已确认要求仍走变更。确认方案不偏离产品定义;授权内确认人员、资源与排期,预算、采购和对外承诺交业务批准。",
"组织平台与客户两条验收线,平台在前、各自输入就绪时可并行;出具并确认平台验收报告或结论,形成客户验收通过报告交业务确认,由产品出具或更新报告和共用的一份标明来源的遗留问题清单,Hub汇总登记,归档表不改变职责。", "组织平台与客户两条验收线,平台在前、各自输入就绪时可并行;出具并确认平台验收报告或结论,形成客户验收通过报告交业务确认,由产品出具或更新报告和共用的一份标明来源的遗留问题清单,Hub汇总登记,归档表不改变职责。",
"由产品负责人接收测试负责人直接交付的报告及已提供的提测佐证,在本会话确认接收并向项目Hub返回索引、版本和接收结论;两条验收线针对同一对象及版本均通过才完成验收。", "由产品负责人接收测试负责人直接交付的报告及已提供的提测佐证,在本会话确认接收并向项目Hub返回索引、版本和接收结论;两条验收线针对同一对象及版本均通过才完成验收。",
"接到任务先与本角色真人负责人对齐目标、输入和成果要求;信息足够后执行,成果自审并经负责人实际确认后才正式返回。", "接到任务先与本角色真人负责人对齐目标、输入和成果要求;信息足够后执行,成果自审并经负责人实际确认后才正式返回。",
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
# macOS/Linux entry; Windows can use the .ps1 or Python entry.
set -eu
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
if command -v python3 >/dev/null 2>&1; then
exec python3 "$script_dir/dingtalk_notify.py" message "$@"
fi
exec python "$script_dir/dingtalk_notify.py" message "$@"
+11
View File
@@ -0,0 +1,11 @@
# Requires PowerShell 7.3+ for native argument passing (quotes and Markdown).
$ErrorActionPreference = 'Stop'
if ($PSVersionTable.PSVersion -lt [version]'7.3') { throw 'Please use PowerShell 7.3+ or the Python entry.' }
$PSNativeCommandArgumentPassing = 'Standard'
$env:PYTHONIOENCODING = 'utf-8'
if (Get-Command python -CommandType Application -ErrorAction SilentlyContinue) {
& python "$PSScriptRoot/dingtalk_notify.py" message @args
} elseif (Get-Command py -CommandType Application -ErrorAction SilentlyContinue) {
& py -3 "$PSScriptRoot/dingtalk_notify.py" message @args
} else { throw 'Python 3.9+ is required.' }
exit $LASTEXITCODE
+5 -187
View File
@@ -1,190 +1,8 @@
#!/bin/sh #!/bin/sh
# macOS/Linux entry; Windows can use the .ps1 or Python entry.
set -eu set -eu
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
project_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) if command -v python3 >/dev/null 2>&1; then
config_file="$project_root/.dingtalk/config.env" exec python3 "$script_dir/dingtalk_notify.py" progress "$@"
usage() {
echo "用法:./scripts/dingtalk-progress <start|milestone|blocked|complete|failed> <中文 Markdown 摘要>" >&2
}
if [ "$#" -lt 2 ]; then
usage
exit 2
fi fi
exec python "$script_dir/dingtalk_notify.py" progress "$@"
event_name=$1
shift
summary=$*
case "$event_name" in
start|milestone|blocked|complete|failed) ;;
*)
usage
exit 2
;;
esac
if [ ! -f "$config_file" ]; then
echo "DingTalk progress reporting is not configured; skipping." >&2
exit 0
fi
file_mode=$(stat -f '%Lp' "$config_file" 2>/dev/null || stat -c '%a' "$config_file" 2>/dev/null || true)
case "$file_mode" in
400|600) ;;
*)
echo "Refusing to read $config_file because its mode is ${file_mode:-unknown}; run chmod 600 on it." >&2
exit 2
;;
esac
while IFS= read -r line || [ -n "$line" ]; do
case "$line" in
''|'#'*) continue ;;
esac
key=${line%%=*}
value=${line#*=}
case "$key" in
DINGTALK_PROGRESS_ENABLED|DINGTALK_PROGRESS_DRY_RUN|DINGTALK_PROGRESS_MODE|DINGTALK_PROGRESS_CLIENT_ID|DINGTALK_PROGRESS_TARGET|DINGTALK_PROGRESS_ROBOT_CODE|DINGTALK_PROGRESS_KEYCHAIN_SERVICE|DINGTALK_PROGRESS_PROJECT|DINGTALK_PROGRESS_TITLE_PREFIX)
export "$key=$value"
;;
*)
echo "Unknown key in $config_file: $key" >&2
exit 2
;;
esac
done < "$config_file"
if [ "${DINGTALK_PROGRESS_ENABLED:-0}" != "1" ]; then
echo "DingTalk progress reporting is disabled; skipping." >&2
exit 0
fi
if [ "${DINGTALK_PROGRESS_MODE:-app-bot}" != "app-bot" ]; then
echo "DINGTALK_PROGRESS_MODE must be app-bot." >&2
exit 2
fi
if [ -z "${DINGTALK_PROGRESS_CLIENT_ID:-}" ] ||
[ -z "${DINGTALK_PROGRESS_TARGET:-}" ] ||
[ -z "${DINGTALK_PROGRESS_ROBOT_CODE:-}" ]; then
echo "App-bot mode requires DINGTALK_PROGRESS_CLIENT_ID, DINGTALK_PROGRESS_TARGET, and DINGTALK_PROGRESS_ROBOT_CODE." >&2
exit 2
fi
if ! command -v jq >/dev/null 2>&1; then
echo "jq is required to construct and validate the DingTalk OpenAPI request." >&2
exit 127
fi
if ! command -v dws >/dev/null 2>&1; then
echo "Global dws command was not found in PATH." >&2
exit 127
fi
dws=$(command -v dws)
if [ -n "${DING_SEC:-}" ]; then
client_secret=$DING_SEC
else
if [ ! -x /usr/bin/security ]; then
echo "DING_SEC is unset and macOS Keychain command /usr/bin/security is unavailable." >&2
exit 127
fi
keychain_service=${DINGTALK_PROGRESS_KEYCHAIN_SERVICE:-com.eapil.dingtalk.$(basename -- "$project_root")}
if ! client_secret=$(/usr/bin/security find-generic-password \
-a "$DINGTALK_PROGRESS_CLIENT_ID" \
-s "$keychain_service" \
-w 2>/dev/null); then
echo "DingTalk Client Secret was not found in DING_SEC or this project's macOS Keychain item." >&2
echo "See .dingtalk/README.md for credential setup." >&2
exit 2
fi
fi
if [ -z "$client_secret" ]; then
echo "The DingTalk Client Secret is empty." >&2
exit 2
fi
case "$event_name" in
start) event_label="开始推进" ;;
milestone) event_label="阶段里程碑" ;;
blocked) event_label="遇到阻塞" ;;
complete) event_label="本轮完成" ;;
failed) event_label="执行失败" ;;
esac
project_name=${DINGTALK_PROGRESS_PROJECT:-$(basename -- "$project_root")}
title_prefix=${DINGTALK_PROGRESS_TITLE_PREFIX:-[项目进度]}
title="$title_prefix $event_label"
timestamp=$(date '+%Y-%m-%d %H:%M:%S %Z')
body=$(printf '## %s\n\n**项目:** %s \n**状态:** %s \n**更新时间:** %s\n\n---\n\n%s' \
"$title" "$project_name" "$event_label" "$timestamp" "$summary")
msg_param=$(jq -cn --arg title "$title" --arg text "$body" '{title:$title,text:$text}')
request_body=$(jq -cn \
--arg msgParam "$msg_param" \
--arg openConversationId "$DINGTALK_PROGRESS_TARGET" \
--arg robotCode "$DINGTALK_PROGRESS_ROBOT_CODE" \
'{msgParam:$msgParam,msgKey:"sampleMarkdown",openConversationId:$openConversationId,robotCode:$robotCode}')
if [ "${DINGTALK_PROGRESS_DRY_RUN:-0}" = "1" ]; then
if DWS_CLIENT_ID="$DINGTALK_PROGRESS_CLIENT_ID" \
DWS_CLIENT_SECRET="$client_secret" \
"$dws" api POST /v1.0/robot/groupMessages/send \
--data "$request_body" --dry-run --yes --format json; then
send_status=0
else
send_status=$?
fi
unset client_secret DWS_CLIENT_SECRET
exit "$send_status"
fi
max_send_attempts=3
retry_delay_seconds=10
attempt=1
final_status=1
while [ "$attempt" -le "$max_send_attempts" ]; do
response=
if response=$(DWS_CLIENT_ID="$DINGTALK_PROGRESS_CLIENT_ID" \
DWS_CLIENT_SECRET="$client_secret" \
"$dws" api POST /v1.0/robot/groupMessages/send \
--data "$request_body" --yes --format json); then
send_status=0
else
send_status=$?
fi
if [ -n "$response" ]; then
printf '%s\n' "$response"
fi
if [ "$send_status" -eq 0 ] &&
printf '%s\n' "$response" |
jq -e '.processQueryKey | type == "string" and length > 0' >/dev/null 2>&1; then
unset client_secret DWS_CLIENT_SECRET
exit 0
fi
if [ "$send_status" -eq 0 ]; then
echo "DingTalk attempt $attempt returned no valid processQueryKey; service acceptance is unverified." >&2
final_status=1
else
echo "DingTalk attempt $attempt failed with status $send_status." >&2
final_status=$send_status
fi
if [ "$attempt" -lt "$max_send_attempts" ]; then
echo "Retrying DingTalk send in $retry_delay_seconds seconds." >&2
sleep "$retry_delay_seconds"
fi
attempt=$((attempt + 1))
done
unset client_secret DWS_CLIENT_SECRET
echo "DingTalk progress reporting failed after $max_send_attempts attempts; Etunel work should continue." >&2
exit "$final_status"
+11
View File
@@ -0,0 +1,11 @@
# Requires PowerShell 7.3+ for native argument passing (quotes and Markdown).
$ErrorActionPreference = 'Stop'
if ($PSVersionTable.PSVersion -lt [version]'7.3') { throw 'Please use PowerShell 7.3+ or the Python entry.' }
$PSNativeCommandArgumentPassing = 'Standard'
$env:PYTHONIOENCODING = 'utf-8'
if (Get-Command python -CommandType Application -ErrorAction SilentlyContinue) {
& python "$PSScriptRoot/dingtalk_notify.py" progress @args
} elseif (Get-Command py -CommandType Application -ErrorAction SilentlyContinue) {
& py -3 "$PSScriptRoot/dingtalk_notify.py" progress @args
} else { throw 'Python 3.9+ is required.' }
exit $LASTEXITCODE
+275
View File
@@ -0,0 +1,275 @@
#!/usr/bin/env python3
"""Hub-only DingTalk notifications. Python 3.9+, standard library, no shell eval."""
import argparse
import datetime as dt
import json
import os
from pathlib import Path
import re
import shutil
import stat
import subprocess
import sys
import time
ROOT = Path(__file__).resolve().parent.parent
PREFIX = "DINGTALK_PROGRESS_"
KEYS = {
"ENABLED", "DRY_RUN", "MODE", "CLIENT_ID", "TARGET", "ROBOT_CODE",
"CLIENT_SECRET", "PROJECT", "TITLE_PREFIX", "DWS_PATH", "PROFILE",
}
EVENTS = {
"start": "开始推进", "milestone": "阶段里程碑", "blocked": "遇到阻塞",
"complete": "本轮完成", "failed": "执行失败",
}
# Only an explicit DNS resolution failure is safe to replay automatically.
DNS_ERROR = re.compile(
r"lookup [\w.-]+(?: on [^:\s]+(?::\d+)?)?: [^\n]*"
r"(?:i/o timeout|no such host|server misbehaving|temporary failure in name resolution)",
re.IGNORECASE,
)
ATTEMPTS = 3
RETRY_SECONDS = 10
COMMAND_TIMEOUT = 60
class NoticeError(Exception):
"""A human-safe error: never include raw subprocess/config contents."""
def emit(status, message, **extra):
print(json.dumps({"status": status, "message": message, **extra}, ensure_ascii=False))
def read_config(root):
path = root / ".dingtalk" / "config.env"
if not path.exists():
return {}
if os.name != "nt" and stat.S_IMODE(path.stat().st_mode) not in (0o400, 0o600):
raise NoticeError("配置权限过宽,请部署负责人设为仅当前用户可读写(600 或 400)。")
result = {}
for line in path.read_text(encoding="utf-8-sig").splitlines():
if not line.strip() or line.lstrip().startswith("#"):
continue
key, sep, value = line.partition("=")
key = key.strip()
if not sep or key not in {PREFIX + name for name in KEYS}:
raise NoticeError("配置含不支持的字段或格式;请部署负责人对照示例检查,不要回传配置内容。")
if key in result:
raise NoticeError("配置存在重复字段,请部署负责人检查。")
result[key] = value.strip()
for name in ("ENABLED", "DRY_RUN"):
if result.get(PREFIX + name, "0") not in ("0", "1"):
raise NoticeError("通知开关只能填写 0 或 1。")
return {key[len(PREFIX):]: value for key, value in result.items()}
def parse_args(argv):
parser = argparse.ArgumentParser(description="Hub 钉钉辅助通知;不派发 Etunel 任务。")
commands = parser.add_subparsers(dest="command", required=True)
progress = commands.add_parser("progress", help="项目事件通知")
progress.add_argument("event", choices=EVENTS)
progress.add_argument("summary", nargs="*", help="简短中文 Markdown 摘要")
progress.add_argument("--file", help="UTF-8 正文文件,与摘要二选一")
message = commands.add_parser("message", help="自定义中文摘要或负责人提醒")
message.add_argument("--title", required=True)
body = message.add_mutually_exclusive_group(required=True)
body.add_argument("--text")
body.add_argument("--file", help="UTF-8 正文文件;不是上传附件")
message.add_argument("--at", action="append", default=[], help="完整姓名,逗号分隔,可重复")
message.add_argument("--at-id", action="append", default=[], help="兼容已核实的 姓名=userId;不得猜编号")
message.add_argument("--group", help="仅用于用户明确指定的其他群;默认使用部署配置")
for command in (progress, message):
command.add_argument("--dry-run", action="store_true", help="离线预览,不查人员、不认证、不输出密钥、不发送")
args = parser.parse_args(argv)
if args.command == "progress" and bool(args.summary) == bool(args.file):
parser.error("请提供摘要或 --file,且只能选择一种。")
return args
def body_text(args, root=ROOT):
if args.file:
body_path = Path(args.file)
config_path = root / ".dingtalk" / "config.env"
if config_path.exists() and body_path.samefile(config_path):
raise NoticeError("不能将通知配置文件作为正文发送或预览。")
value = body_path.read_text(encoding="utf-8-sig")
else:
value = " ".join(args.summary) if args.command == "progress" else args.text
if not value.strip():
raise NoticeError("通知正文不能为空。")
if len(value.encode("utf-8")) > 12000:
raise NoticeError("通知过长,请改为简短摘要;正文文件也不是日志或成果附件通道。")
return value
def render(args, config, body):
if args.command == "message":
title = args.title.strip()
text = body
else:
project = config.get("PROJECT") or ROOT.name
label = EVENTS[args.event]
title = f"{config.get('TITLE_PREFIX') or '[项目进度]'} {project}|{label}"
timestamp = dt.datetime.now().astimezone().strftime("%Y-%m-%d %H:%M:%S %z")
text = f"## {title}\n\n**项目:** {project} \n**状态:** {label} \n**更新时间:** {timestamp}\n\n---\n\n{body}"
if not title or "\n" in title or "\r" in title or len(title) > 200:
raise NoticeError("请使用不超过 200 字的单行中文标题。")
return title, text
def clean_env():
return {key: value for key, value in os.environ.items()
if key not in ("DING_SEC", "DWS_CLIENT_ID", "DWS_CLIENT_SECRET")
and not key.startswith(PREFIX)}
def secret_for(config):
# One source on every platform; never fall back to inherited credentials.
secret = config.get("CLIENT_SECRET", "")
if not secret:
raise NoticeError("未配置机器人凭据,请部署负责人在本地 config.env 填写 DINGTALK_PROGRESS_CLIENT_SECRET;不要在会话中提供密钥。")
return secret
def dws_path(config):
supplied = config.get("DWS_PATH")
path = shutil.which(supplied or "dws")
if not path:
raise NoticeError("未找到 dws,请部署负责人安装原生可执行文件并加入 Hub 的 PATH,或配置 DWS_PATH。")
if os.name == "nt" and Path(path).suffix.lower() != ".exe":
raise NoticeError("Windows 请使用原生 dws.exe(可配置 DWS_PATH),不通过 cmd/bat 转发 Markdown。")
return path
def json_object(output):
try:
value = json.loads(output)
return value if isinstance(value, dict) else {}
except (ValueError, TypeError):
return {}
def accepted_key(data):
value = data.get("processQueryKey")
return value.strip() if isinstance(value, str) and value.strip() else None
def invoke(command, env, sending=False, run=subprocess.run, sleep=time.sleep, stdin_data=None):
for attempt in range(1, ATTEMPTS + 1):
try:
result = run(command, env=env, input=stdin_data, capture_output=True, text=True,
encoding="utf-8", errors="replace", timeout=COMMAND_TIMEOUT)
except subprocess.TimeoutExpired:
raise NoticeError("钉钉调用超时,结果未知;本次不自动重发,请负责人核对。") from None
except OSError:
raise NoticeError("钉钉命令无法启动,请部署负责人检查安装和进程权限。") from None
data = json_object(result.stdout)
if sending and accepted_key(data):
return data
if result.returncode == 0:
if sending:
raise NoticeError("未收到有效发送回执,结果未知;本次不自动重发,请负责人核对。")
return data
if DNS_ERROR.search(result.stdout + "\n" + result.stderr):
if attempt < ATTEMPTS:
print(f"钉钉域名解析失败,10 秒后进行第 {attempt + 1}/3 次尝试。", file=sys.stderr)
sleep(RETRY_SECONDS)
continue
raise NoticeError("钉钉域名解析连续失败 3 次,本次通知已停止;主任务继续。")
raise NoticeError("钉钉调用未成功,未自动重发;请部署负责人检查认证、权限或网络,主任务继续。")
raise AssertionError("unreachable")
def mentions(args, config, cli, run=subprocess.run, sleep=time.sleep):
people = {}
for item in args.at_id:
name, sep, user_id = item.partition("=")
if not sep or not name.strip() or not user_id.strip() or re.search(r"\s", user_id):
raise NoticeError("已核实人员映射的格式应为 姓名=userId;不要自行编造编号。")
people[user_id.strip()] = name.strip()
names = list(dict.fromkeys(name.strip() for group in args.at
for name in group.replace(",", ",").split(",") if name.strip()))
if args.at and not names:
raise NoticeError("提醒对象不能为空。")
if names and not config.get("PROFILE"):
raise NoticeError("按姓名提醒需要部署负责人配置同组织的 PROFILE;不要临时切换或猜测账号。")
for name in names:
data = invoke([cli, "aisearch", "person", "--query", name, "--dimension", "name",
"--profile", config["PROFILE"], "--format", "json"],
clean_env(), run=run, sleep=sleep)
candidates = {}
rows = data.get("result", [])
if not isinstance(rows, list):
rows = []
for row in rows:
if not isinstance(row, dict):
continue
meta = row.get("meta") if isinstance(row.get("meta"), dict) else {}
actual_name = meta.get("name") or row.get("title") or row.get("author")
user_id = row.get("userId")
if actual_name == name and isinstance(user_id, str) and user_id.strip():
candidates[user_id] = name
if len(candidates) != 1:
raise NoticeError("提醒对象未唯一匹配,本条未发送;请负责人确认完整姓名或已有的有效人员映射。")
people.update(candidates)
return people
def main(argv=None, root=ROOT, run=subprocess.run, sleep=time.sleep):
args = parse_args(argv)
try:
config = read_config(root)
preview = args.dry_run or config.get("DRY_RUN") == "1"
if not preview and config.get("ENABLED") != "1":
emit("skipped", "钉钉通知未配置或未启用,已跳过。")
return 0
title, text = render(args, config, body_text(args, root))
if preview:
emit("preview", "仅离线预览,未验证群配置或提醒对象;未查人员、认证或发送,不输出配置中的密钥。",
title=title, text=text,
mentions_requested=args.command == "message" and bool(args.at or args.at_id))
return 0
if config.get("MODE", "app-bot") != "app-bot":
raise NoticeError("当前封装仅支持 app-bot,请部署负责人检查配置。")
if any(not config.get(key) for key in ("CLIENT_ID", "TARGET", "ROBOT_CODE")):
raise NoticeError("机器人配置不完整,请部署负责人检查,不要在会话中粘贴配置。")
cli = dws_path(config)
secret = secret_for(config)
people = mentions(args, config, cli, run, sleep) if args.command == "message" else {}
if people:
text += "\n\n" + " ".join("@" + user_id for user_id in people)
message = {"title": title, "text": text}
if people:
message["atUserIds"] = list(people)
payload = {
"msgParam": json.dumps(message, ensure_ascii=False), "msgKey": "sampleMarkdown",
"openConversationId": (args.group if args.command == "message" else None) or config["TARGET"],
"robotCode": config["ROBOT_CODE"],
}
# DWS accepts --data -: avoid shell quoting, argv length limits and
# exposing message/target contents in the process command line.
command = [cli, "api", "POST", "/v1.0/robot/groupMessages/send",
"--data", "-", "--yes", "--format", "json"]
if config.get("PROFILE"):
command += ["--profile", config["PROFILE"]]
env = clean_env()
env.update(DWS_CLIENT_ID=config["CLIENT_ID"], DWS_CLIENT_SECRET=secret)
data = invoke(command, env, sending=True, run=run, sleep=sleep,
stdin_data=json.dumps(payload, ensure_ascii=False))
emit("accepted", "钉钉服务端已接受通知,不代表群成员已读。", processQueryKey=accepted_key(data))
return 0
except NoticeError as error:
emit("error", str(error))
return 1
except (OSError, UnicodeError, subprocess.SubprocessError, ValueError):
emit("error", "通知配置、正文文件或本机调用异常;请部署负责人检查,本次停止且不影响主任务。")
return 1
if __name__ == "__main__":
for stream in (sys.stdout, sys.stderr):
if hasattr(stream, "reconfigure"):
stream.reconfigure(encoding="utf-8")
sys.exit(main())
+323
View File
@@ -0,0 +1,323 @@
"""Offline tests: fake configs, fake credentials, fake DWS. Never use live config."""
import contextlib
import io
import json
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
import unittest
from unittest import mock
import dingtalk_notify as notify
DNS = 'lookup api.dingtalk.com on 127.0.0.1:53: i/o timeout'
OK = '{"processQueryKey":"test-receipt"}'
def response(code=0, stdout=OK, stderr=''):
return subprocess.CompletedProcess(['fake-dws'], code, stdout, stderr)
class NotifyTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory(prefix='通知测试 with spaces ')
self.addCleanup(self.temp.cleanup)
self.root = Path(self.temp.name)
(self.root / '.dingtalk').mkdir()
self.config = self.root / '.dingtalk/config.env'
self.configure()
self.environment = mock.patch.dict(os.environ, {'DING_SEC': 'ignored-env-secret',
'DWS_CLIENT_SECRET': 'ignored-dws-secret'}, clear=True)
self.environment.start()
self.addCleanup(self.environment.stop)
self.locator = mock.patch.object(notify, 'dws_path', return_value='fake-dws')
self.locator.start()
self.addCleanup(self.locator.stop)
def configure(self, **updates):
values = dict(ENABLED='1', DRY_RUN='0', MODE='app-bot', CLIENT_ID='fake-client',
TARGET='fake-group', ROBOT_CODE='fake-robot', PROJECT='离线测试',
PROFILE='fake-corp:fake-user', CLIENT_SECRET='fake-secret')
values.update(updates)
self.config.write_text('\n'.join(notify.PREFIX + k + '=' + v for k, v in values.items()),
encoding='utf-8')
self.config.chmod(0o600)
def call(self, args, responses=None):
run = mock.Mock(side_effect=responses or [response()])
sleep = mock.Mock()
output, errors = io.StringIO(), io.StringIO()
with contextlib.redirect_stdout(output), contextlib.redirect_stderr(errors):
code = notify.main(args, root=self.root, run=run, sleep=sleep)
return code, json.loads(output.getvalue()), run, sleep, errors.getvalue()
def entries(self):
return [['progress', 'milestone', '**完成**\n\n- 内容'],
['message', '--title', '负责人提醒', '--text', '**请确认**\n\n- 内容']]
def test_events(self):
for event, label in notify.EVENTS.items():
with self.subTest(event=event):
code, data, run, _, _ = self.call(['progress', event, '中文摘要'])
self.assertEqual((code, data['status']), (0, 'accepted'))
payload = json.loads(run.call_args.kwargs['input'])
self.assertIn(label, json.loads(payload['msgParam'])['title'])
def test_payload_and_credentials_not_on_argv(self):
text = '**结果**\n\n- "中文" 😀 `代码` $变量 & <内容> \\路径'
code, data, run, _, _ = self.call(['message', '--title', '摘要', '--text', text])
self.assertEqual(code, 0)
command = run.call_args.args[0]
kwargs = run.call_args.kwargs
payload = json.loads(kwargs['input'])
self.assertEqual(json.loads(payload['msgParam'])['text'], text)
self.assertEqual(payload['openConversationId'], 'fake-group')
self.assertEqual(payload['msgKey'], 'sampleMarkdown')
self.assertEqual(command[command.index('--data') + 1], '-')
self.assertNotIn('fake-secret', str(command))
self.assertNotIn('fake-group', str(command))
self.assertEqual(kwargs['env']['DWS_CLIENT_SECRET'], 'fake-secret')
self.assertNotIn('DING_SEC', kwargs['env'])
self.assertNotIn('fake-secret', json.dumps(data))
def test_dns_retries_both_entries(self):
for args in self.entries():
with self.subTest(args=args):
code, data, run, sleep, _ = self.call(args, [response(1, '', DNS)] * 3)
self.assertEqual((code, data['status'], run.call_count), (1, 'error', 3))
self.assertEqual(sleep.call_args_list, [mock.call(10), mock.call(10)])
def test_transient_dns_success_stops(self):
for args in self.entries():
code, data, run, sleep, _ = self.call(args, [response(1, '', DNS), response()])
self.assertEqual((code, data['status'], run.call_count, sleep.call_count), (0, 'accepted', 2, 1))
def test_ambiguous_and_auth_errors_never_replayed_or_echoed(self):
for args in self.entries():
for error in ['i/o timeout', 'EOF', 'HTTP 503', 'permission denied', 'fake-secret App Token']:
with self.subTest(error=error):
code, data, run, sleep, stderr = self.call(args, [response(1, '', error)])
self.assertEqual((code, run.call_count, sleep.call_count), (1, 1, 0))
self.assertNotIn(error, json.dumps(data, ensure_ascii=False) + stderr)
def test_receipt_wins_even_nonzero(self):
for args in self.entries():
code, data, run, sleep, _ = self.call(args, [response(1, OK, DNS)])
self.assertEqual((code, data['status'], run.call_count, sleep.call_count), (0, 'accepted', 1, 0))
def test_bad_receipts_do_not_replay(self):
for raw in ['{}', '{"processQueryKey":" "}', '{"processQueryKey":123}', 'not json', '[]']:
code, _, run, sleep, _ = self.call(self.entries()[0], [response(0, raw)])
self.assertEqual((code, run.call_count, sleep.call_count), (1, 1, 0))
def test_timeout_no_replay(self):
code, data, run, sleep, _ = self.call(self.entries()[0], [subprocess.TimeoutExpired('fake', 60)])
self.assertEqual((code, run.call_count, sleep.call_count), (1, 1, 0))
self.assertIn('结果未知', data['message'])
self.assertEqual(run.call_args.kwargs['timeout'], 60)
def test_startup_failure_no_replay(self):
code, _, run, sleep, _ = self.call(self.entries()[0], [OSError('private machine detail')])
self.assertEqual((code, run.call_count, sleep.call_count), (1, 1, 0))
def test_missing_credentials_no_commands(self):
self.configure(CLIENT_SECRET='')
code, data, run, _, _ = self.call(self.entries()[0])
self.assertEqual((code, run.call_count), (1, 0))
self.assertIn('凭据', data['message'])
def test_skipped_no_tools(self):
self.configure(ENABLED='0')
for args in self.entries():
code, data, run, _, _ = self.call(args)
self.assertEqual((code, data['status'], run.call_count), (0, 'skipped', 0))
self.config.unlink()
self.assertEqual(self.call(self.entries()[0])[1]['status'], 'skipped')
def test_preview_never_uses_or_prints_secret_or_resolves_people(self):
for configured in [False, True]:
self.configure(DRY_RUN='1' if configured else '0')
for args in self.entries():
if args[0] == 'message':
args += ['--at', '完整姓名']
if not configured:
args += ['--dry-run']
with mock.patch.object(notify, 'secret_for', side_effect=AssertionError('secret accessed')):
code, data, run, _, _ = self.call(args)
self.assertEqual((code, data['status'], run.call_count), (0, 'preview', 0))
self.assertNotIn('fake-group', str(data))
self.assertNotIn('fake-secret', str(data))
def test_preview_without_secret(self):
self.configure(CLIENT_SECRET='')
code, data, run, _, _ = self.call(self.entries()[0] + ['--dry-run'])
self.assertEqual((code, data['status'], run.call_count), (0, 'preview', 0))
def test_config_cannot_be_used_as_body_even_in_preview(self):
for args in [['progress', 'milestone'], ['message', '--title', '摘要']]:
for flags in [[], ['--dry-run']]:
code, data, run, _, _ = self.call(args + ['--file', str(self.config)] + flags)
self.assertEqual((code, run.call_count), (1, 0))
self.assertNotIn('fake-secret', str(data))
def test_preview_without_config(self):
self.config.unlink()
self.assertEqual(self.call(self.entries()[0] + ['--dry-run'])[1]['status'], 'preview')
def test_utf8_bom_crlf_literal_config(self):
raw = self.config.read_text(encoding='utf-8') + '\nDINGTALK_PROGRESS_TITLE_PREFIX=$(not-executed)'
self.config.write_bytes(('\ufeff' + raw.replace('\n', '\r\n')).encode('utf-8'))
self.assertEqual(notify.read_config(self.root)['TITLE_PREFIX'], '$(not-executed)')
def test_invalid_config_safe_error(self):
for raw in ['DING_SEC=fake-secret', 'invalid fake-secret',
'DINGTALK_PROGRESS_ENABLED=1\nDINGTALK_PROGRESS_ENABLED=0',
'DINGTALK_PROGRESS_ENABLED=yes']:
self.config.write_text(raw, encoding='utf-8')
code, data, run, _, _ = self.call(self.entries()[0])
self.assertEqual((code, run.call_count), (1, 0))
self.assertNotIn('fake-secret', str(data))
def test_body_files_both_entries(self):
body = self.root / '中文 summary.md'
body.write_bytes('\ufeff**结果**\r\n\r\n- 已确认 😀'.encode('utf-8'))
for args in [['progress', 'milestone'], ['message', '--title', '摘要']]:
code, _, run, _, _ = self.call(args + ['--file', str(body)])
self.assertEqual(code, 0)
text = json.loads(json.loads(run.call_args.kwargs['input'])['msgParam'])['text']
self.assertIn('**结果**\n\n- 已确认 😀', text)
def test_empty_or_large_body_fails_before_tools(self):
for text in ['', ' ', '字' * 5000]:
code, _, run, _, _ = self.call(['message', '--title', '摘要', '--text', text])
self.assertEqual((code, run.call_count), (1, 0))
def test_exact_name_and_dedup_same_profile(self):
found = json.dumps({'result': [{'meta': {'name': '完整姓名'}, 'userId': 'fake-person'}]})
args = self.entries()[1] + ['--at', '完整姓名,完整姓名', '--at-id', '完整姓名=fake-person']
code, _, run, _, _ = self.call(args, [response(0, found), response()])
self.assertEqual((code, run.call_count), (0, 2))
for call in run.call_args_list:
command = call.args[0]
self.assertEqual(command[command.index('--profile') + 1], 'fake-corp:fake-user')
self.assertNotIn('DWS_CLIENT_SECRET', run.call_args_list[0].kwargs['env'])
message = json.loads(json.loads(run.call_args.kwargs['input'])['msgParam'])
self.assertEqual(message['atUserIds'], ['fake-person'])
self.assertTrue(message['text'].endswith('@fake-person'))
def test_names_zero_multiple_malformed_stop_before_send(self):
for rows in [[], [{'title': '完整姓名', 'userId': 'a'}, {'title': '完整姓名', 'userId': 'b'}],
[{'title': '别的姓名', 'userId': 'a'}], {}, [None]]:
code, _, run, sleep, _ = self.call(self.entries()[1] + ['--at', '完整姓名'],
[response(0, json.dumps({'result': rows}))])
self.assertEqual((code, run.call_count, sleep.call_count), (1, 1, 0))
def test_name_search_requires_explicit_profile(self):
self.configure(PROFILE='')
code, _, run, _, _ = self.call(self.entries()[1] + ['--at', '完整姓名'])
self.assertEqual((code, run.call_count), (1, 0))
def test_known_mapping_and_group_compatibility(self):
code, _, run, _, _ = self.call(self.entries()[1] + ['--at-id', '姓名=fake-id', '--group', 'fake-other'])
self.assertEqual(code, 0)
self.assertEqual(json.loads(run.call_args.kwargs['input'])['openConversationId'], 'fake-other')
def test_config_secret_is_only_source_on_all_platforms(self):
for platform in ['darwin', 'win32', 'linux']:
with self.subTest(platform=platform), mock.patch.object(sys, 'platform', platform):
self.assertEqual(notify.secret_for({'CLIENT_SECRET': 'fake-secret'}), 'fake-secret')
with self.assertRaises(notify.NoticeError):
notify.secret_for({})
def test_secret_special_characters_are_literal(self):
secret = 'fake=secret#literal$not-expanded'
self.configure(CLIENT_SECRET=secret)
code, data, run, _, _ = self.call(self.entries()[0])
self.assertEqual(code, 0)
self.assertEqual(run.call_args.kwargs['env']['DWS_CLIENT_SECRET'], secret)
self.assertNotIn(secret, str(data))
self.assertNotIn(secret, str(run.call_args.args))
self.assertNotIn(secret, run.call_args.kwargs['input'])
class LauncherTests(unittest.TestCase):
def test_real_subprocess_stdin_without_live_dws(self):
with tempfile.TemporaryDirectory(prefix='通知 fake transport ') as folder:
root = Path(folder)
(root / '.dingtalk').mkdir()
config = root / '.dingtalk/config.env'
config.write_text('DINGTALK_PROGRESS_ENABLED=1\nDINGTALK_PROGRESS_DRY_RUN=0\n'
'DINGTALK_PROGRESS_CLIENT_ID=fake-client\n'
'DINGTALK_PROGRESS_CLIENT_SECRET=fake-secret\n'
'DINGTALK_PROGRESS_TARGET=fake-group\n'
'DINGTALK_PROGRESS_ROBOT_CODE=fake-robot\n', encoding='utf-8')
config.chmod(0o600)
fake = root / 'fake dws.py'
fake.write_text(
'import json, os, sys\n'
'assert sys.argv[1:] == ["api", "POST", "/v1.0/robot/groupMessages/send", '
'"--data", "-", "--yes", "--format", "json"]\n'
'payload = json.loads(sys.stdin.buffer.read().decode("utf-8"))\n'
'assert payload["openConversationId"] == "fake-group"\n'
'assert json.loads(payload["msgParam"])["text"] == "中文\\n\\n- \\\"引号\\\" 😀"\n'
'assert os.environ["DWS_CLIENT_SECRET"] == "fake-secret"\n'
'assert "DING_SEC" not in os.environ\n'
'print(json.dumps({"processQueryKey": "fake-transport-receipt"}))\n', encoding='utf-8')
def transport(command, **kwargs):
return subprocess.run([sys.executable, str(fake), *command[1:]], **kwargs)
out = io.StringIO()
with mock.patch.object(notify, 'dws_path', return_value='fake-dws'), \
mock.patch.dict(os.environ, {'DING_SEC': 'ignored-env-secret'}), \
contextlib.redirect_stdout(out):
code = notify.main(['message', '--title', '摘要', '--text', '中文\n\n- "引号" 😀'],
root=root, run=transport)
self.assertEqual(code, 0, out.getvalue())
self.assertEqual(json.loads(out.getvalue())['status'], 'accepted')
def test_real_launchers_preview_in_isolated_copy(self):
"""Exercise OS processes/quoting, but copy no real config and never run dws."""
with tempfile.TemporaryDirectory(prefix='通知 launch with spaces ') as folder:
scripts = Path(folder) / 'scripts'
scripts.mkdir()
for name in ['dingtalk_notify.py', 'dingtalk-progress', 'dingtalk-bot-message',
'dingtalk-progress.ps1', 'dingtalk-bot-message.ps1']:
shutil.copy2(Path(__file__).parent / name, scripts / name)
text = '**结果**\n\n- "中文" 😀 `代码` $变量 & 内容'
body = Path(folder) / '中文正文.md'
body.write_text(text, encoding='utf-8')
env = {k: v for k, v in os.environ.items()
if not k.startswith(('DING', 'DWS_'))}
# Run from outside the script project: --file is cwd-relative.
commands = [[sys.executable, str(scripts / 'dingtalk_notify.py'), 'progress', 'milestone',
'--file', body.name, '--dry-run']]
pwsh = shutil.which('pwsh')
if os.name == 'nt':
self.assertIsNotNone(pwsh, 'Windows launcher test needs PowerShell 7.3+')
if pwsh:
commands += [[pwsh, '-NoProfile', '-File', str(scripts / 'dingtalk-progress.ps1'),
'milestone', text, '--dry-run'],
[pwsh, '-NoProfile', '-File', str(scripts / 'dingtalk-bot-message.ps1'),
'--title', '中文摘要', '--text', text, '--dry-run']]
bash = Path('C:/Program Files/Git/bin/bash.exe') if os.name == 'nt' else shutil.which('bash')
if bash and Path(bash).exists():
for name, args in [('dingtalk-progress', ['milestone']),
('dingtalk-bot-message', ['--title', '中文摘要'])]:
commands.append([str(bash), str(scripts / name), *args,
'--file', body.name, '--dry-run'])
for command in commands:
with self.subTest(entry=command[:3]):
result = subprocess.run(command, cwd=folder, env=env, capture_output=True,
text=True, encoding='utf-8', timeout=30)
self.assertEqual(result.returncode, 0, result.stderr)
data = json.loads(result.stdout)
self.assertEqual(data['status'], 'preview')
self.assertIn(text, data['text'])
if __name__ == '__main__':
unittest.main()