diff --git a/.doc/backlog/2026-09-17-rdev-phase2-backlog.md b/.doc/backlog/2026-09-17-rdev-phase2-backlog.md new file mode 100644 index 00000000..c161144e --- /dev/null +++ b/.doc/backlog/2026-09-17-rdev-phase2-backlog.md @@ -0,0 +1,45 @@ +# rdev 研发工作流二期增强 Backlog + +> 来源:[07-研发工作流rdev接入-设计与实施计划](../system-design/project-workflow/07-研发工作流rdev接入-设计与实施计划.md) §8 决策 3——首期 N/A 采用「激活规划期关闭」,以下增强项不阻塞首期交付,**用户要求记录以便随时续推进**。 +> 创建:2026-09-17。每项建议独立 short-term 任务认领。 + +## 优先级排序(建议) + +### R1 N/A 不适用页生成模式(对齐参考项目行为) + +- 现状:skippable 任务在激活规划期由创建者关闭 = 不适用; +- 目标:保留任务但可生成「不适用说明页」(完整论述理由而非一句话),仍走审核/批准,再进入下一阶段(参考项目 rdev_software overlay 语义); +- 涉及:manifest 不适用模式标记、bizdev 执行 N/A 生成路径、squad N/A 编排、前端规划 UI「保留但声明不适用」选项; +- 验收:软件类任务声明不适用 → 生成完整不适用页 → 审批通过 → 阶段可推进。 + +### R2 UR/RS 结构化质量门禁(复用 PM requirements.json 模式) + +- 范围:`rd_user_needs`、`rd_system_reqs`(及 `pm_system_reqs`)输出结构化需求集合;确定性门禁:ID 唯一连续、不随机抽取、缺验证准则拒绝、正文/JSON 不一致拒绝; +- 参考先例:`quality.system_reqs.v2`。 + +### R3 review_minutes / upload_index 专用确定性门禁 + +- review_minutes(6 个评审记录):问题-决议-行动项闭环检查(拒绝空洞「同意通过」、行动项无责任人/期限); +- upload_index(7 个附件清单):附件索引表存在性、缺失影响分析存在性检查;禁止伪造附件内容靠 Accept 纪律 + 门禁抽查「待上传后更新」结论。 + +### R4 depends_on 精确血缘与 stale 传播 + +- 现状:rd 任务 prior docs 全量注入 + 摘要级血缘; +- 目标:per-task upstream 声明表(对齐参考项目 rdev.yaml depends_on:rd_planning_review ← rd_proposal+rd_design_plan、rd_verification_review ← rd_design_vv+rd_risk_report_v 等),上游新版本批准 → 下游 stale 且不能完成; +- 参考先例:test_manager 对 approved SR 的 stale 传播(projectworkflow/service.go)。 + +### R5 DMR 双记录归档 + +- 参考项目 `records: [dhf, dmr]` 的设计输出/转换文档在本仓库无对应物;需评估文档「记录归属」字段扩展 + 归档/导出按 DHF/DMR 分类(本仓库无云盘,先落 docgov 元数据与导出分类)。 + +### R6 软件工作流任务键 `sw_` 前缀盘点(并入 P4 或其后) + +- `software_bootstrap`/`software_delivery`(executor `software.doc.v1`)任务键盘点并纳入统一命名空间规约(D12 的遗留尾巴)。 + +### R7 高复杂度 regulatory 步注入策略 + +- 现状:D7 中 regulatory 按任务清单静态注入;目标:接 `complexity_tier`(complexity_high/medium 分层)动态注入,对齐参考项目 `when: complexity_tier=high`。 + +## 依赖关系 + +- R2/R3 可并行;R4 依赖 manifest 落地后的 upstream 声明表;R1 独立;R5/R6/R7 低优先级。 diff --git a/.doc/ops/CURRENT.md b/.doc/ops/CURRENT.md index 87969feb..2b5d9086 100644 --- a/.doc/ops/CURRENT.md +++ b/.doc/ops/CURRENT.md @@ -13,6 +13,7 @@ | owner | task | branch | worktree | mutex | status | |-------|------|--------|----------|-------|--------| +| Cursor | 2026-09-17-rdev-workflow-integration | feature/rdev-core | - | workflow-def | in_progress | | Cursor | 2026-09-17-initial-load-perf-u2 | feature/initial-load-perf-u2 | - | web + static-delivery | done | | Codex | 2026-09-17-initial-load-performance-plan | feature/initial-load-performance-plan | .worktrees/feature-initial-load-performance-plan | loading-performance-audit | done | | Kimi | 2026-09-17-system-admin-console | feature/system-admin-console | - | web + sysadmin | done | diff --git a/.doc/ops/research/2026-09-17-rdev-workflow-reference-analysis.md b/.doc/ops/research/2026-09-17-rdev-workflow-reference-analysis.md new file mode 100644 index 00000000..cf1971be --- /dev/null +++ b/.doc/ops/research/2026-09-17-rdev-workflow-reference-analysis.md @@ -0,0 +1,93 @@ +--- +title: "参考项目 weichen-agent-system 研发工作流(rdev)文档关系与阶段组成梳理" +created: 2026-09-17 +status: done +confidence: high +--- + +# 调研:参考项目 weichen-agent-system 研发工作流(rdev)文档关系与阶段组成梳理 + +## 结论 + +参考项目 `.refs/weichen-agent-system` 中的「研发流程」(`key: rdev`)是与商务对接(bizdev)、测试经理(test_manager)、产品经理(product_manager)并列的第 4 个工作流,`kind: followon`、`after: bizdev`:商务对接末阶段 CDO 报价批准后才可启动。rdev 共 **60 个阶段(文档任务)**,按 **13 个 phase(01–13)** 组织成 ISO 13485 设计控制链(策划 → 输入 → 输出 → 验证 → 转换 → 确认),每阶段恰好对应 `templates/docs/.md` 一份自描述模板,并有 overlays(工作流级 `rdev.md` + 阶段簇 `rdev_phNN.md` + 文档类型 `kind_*.md`)与 `configs/squads/rdev.yaml` 五步小队编排三层配套。 + +## 证据 + +| 项 | 路径/命令 | 结果 | +|----|-----------|------| +| 工作流定义 | `.refs/weichen-agent-system/configs/workflows/rdev.yaml` | 1473 行;`key: rdev` `kind: followon` `after: bizdev`;60 个 stage | +| 阶段齐套校验 | `.refs/weichen-agent-system/scripts/e2e-rdev.sh` | 脚本内置断言 `want 60 rdev stages`,逐 stage 校验 `templates/docs/.md` 存在且非空,末模板须含「签署」 | +| 小队编排 | `.refs/weichen-agent-system/configs/squads/rdev.yaml` | `default_steps`:organizer → engineer(+regulatory 可选并行)→ writer → leader;关键 stage 覆盖专属 steps | +| 任务叠加 | `.refs/weichen-agent-system/configs/overlays/rdev.md`、`rdev_ph01…ph13.md`、`rdev_software.md`、`kind_review_minutes.md`、`kind_upload_index.md`、`kind_design_record.md` | 工作流级 / 阶段簇 / 文档类型三层叠加,`### role:` 按专员过滤 | +| 研发负责人人设 | `.refs/weichen-agent-system/configs/agents/rdev_leader.md` | 「研发/设计控制小队负责人…按 ISO 13485 把本阶段做成可追溯的 DHF 记录」 | +| 插件 | `.refs/weichen-agent-system/plugins/rdev/plugin.go` | 不注册生成意图,启动走生命周期 `start_workflow`(说法「启动研发流程」) | +| 官方说明 | `.refs/weichen-agent-system/docs/workflows.md` §研发流程、`docs/handoff.md` §研发流程(followon) | 约 60 阶段、每阶段一份 Word(RD-…)、不适用须完整理由并仍走审核/批准 | +| Canonical 归档 | `docs/cloud-drive.md` | `archive/dhf/{NN-阶段名}/{nn-产出}`;`records` 含 `dmr` 且已批准再拷 `archive/dmr/` | + +## 研发工作流的文档关系(五层结构) + +```text +configs/workflows/rdev.yaml ← 阶段 SSOT:key/标题/phase/records/artifact_kind/docx 元数据/fields/depends_on/skippable + │ 一一对应(stage key = 模板文件名) + ▼ +templates/docs/.md ← 自描述模板:文件编号 RD-…、记录归属 DHF(/DMR)、阶段 NN、 + │ 结论/适用性/正文/追溯表/材料摘要/待确认清单/签署 固定骨架 + ▼ +configs/squads/rdev.yaml ← 小队编排:default_steps + 个别 stage 覆盖(五步编制流水线) + ▼ +configs/overlays/*.md ← 任务叠加(注入 TASK 的阶段级撰写指导,多层): + ① rdev.md(工作流级全员质量红线) + ② rdev_phNN.md(阶段簇,ph01–ph13) + ③ kind_review_minutes / kind_upload_index / kind_design_record(文档类型) + ④ rdev_software.md(软件类阶段专项)+ 个别 stage 特例 overlay + ▼ +plugins/rdev/plugin.go ← 运行时入口:启动走生命周期 start_workflow(followon 门禁), + 不注册生成意图;启动时从 bizdev 拷贝已填字段(产品类型/预期用途/目标市场等) +``` + +- **模板即文档**:每份模板头部自带「文件编号 / 记录归属 / 版本 / 阶段」元数据,与 rdev.yaml 中 stage 的 `docx.code`(RD-…)和 `phase` 一致;`scripts/e2e-rdev.sh` 强制校验 60 个 key 与模板一一对应。 +- **记录归属双轨**:`records: [dhf]`(设计历史文件)为主;设计输出/转换类文档 `records: [dhf, dmr]`(同时属器械主记录),批准后额外拷贝到 `archive/dmr/`。 +- **追溯链**:每份模板含固定「追溯」表(本文件 ID ↔ 上游输入 ↔ 下游验证/确认),需求类要求 UR→RS 追溯主表(见 `kind_design_record.md`)。 +- **评审依赖**:评审记录类 stage 用 `depends_on` 显式声明上游(如 `verification_review` depends_on `design_vv`、`risk_report_v`)。 +- **不适用机制**:每 stage 都有 `applicability` / `na_rationale` 字段;软件类/动物试验/临床等 15 个 stage 标 `skippable: true` + `skip_hint`,不适用仍须生成完整不适用页并走审核/批准。 + + +## 工作流阶段组成(13 个 phase / 60 个 stage) + +| Phase | 阶段簇 overlay | Stage(key → 文档) | 数量 | +|-------|--------|--------------------|------| +| 01 策划 | rdev_ph01 | `proposal` 项目建议书、`design_plan` 设计开发计划任务书 | 2 | +| 02 策划评审 | rdev_ph02 | `planning_review` 策划评审记录(depends: proposal, design_plan) | 1 | +| 03 设计输入 | rdev_ph03 | `user_needs` 用户需求规格书(UN)、`regs_stds` 法规和标准清单、`adverse_events` 同类产品不良事件、`system_reqs` 系统需求规格书(SR)、`risk_plan` 风险管理计划、`hazard_analysis` 危险分析、`sw_plan`*/`sw_reqs`*/`sw_config`* 软件三件套、`design_input_spec` 设计输入规格书、`design_input_list` 设计输入清单、`system_design` 系统设计方案 | 12 | +| 04 需求评审 | — | `req_review` 需求评审记录(depends: user_needs, system_reqs) | 1 | +| 05 输入阶段评审 | — | `input_phase_review` 阶段3和4评审记录(depends: design_input_spec/list, req_review) | 1 | +| 06 设计输出 | rdev_ph06 | `drawings_3d` 图纸3D、`product_bom` BOM、`ee_bom`*/`pcba`*/`electrical_sys`* 电气三件套、`sw_arch`*/`sw_ui`* 软件、`purchase_spec` 采购规范、`process_flow` 工艺流程、`packaging` 包装、`ifu` 说明书、`incoming_iqc`/`ipqc`/`fqc` 三级检验、`tech_req` 产品技术要求、`risk_report` 风险管理报告、`design_output_list` 设计输出清单 | 17 | +| 07 输出评审 | — | `output_review` 阶段6评审(depends: design_output_list) | 1 | +| 08 验证 | rdev_ph08 | `prototype_photos` 样机照片、`design_vv` 设计验证方案、`shelf_life` 货架寿命、`lifetime` 使用期限、`biocompatibility` 生物学、`cleaning` 清洁消毒、`emc_safety`* EMC安规、`risk_report_v` 验证风险报告、`sw_release`*/`sw_unit_test`*/`sw_sys_test`* 软件三件套、`qualified_suppliers` 合格供方 | 12 | +| 09 验证评审 | — | `verification_review`(depends: design_vv, risk_report_v) | 1 | +| 10 转换 | — | `dmr_list` DMR文件清单、`pack_ship` 包装运输、`risk_report_t` 转换风险报告 | 3 | +| 11 转换评审 | — | `transfer_review`(depends: dmr_list, pack_ship, risk_report_t) | 1 | +| 12 确认 | — | `validation_plan` 设计确认方案、`use_error` 使用错误、`animal_study`* 动物试验、`sw_quality`* 软件质量、`clinical`* 临床试验报告、`risk_report_val` 确认风险报告、`user_test` 用户测试 | 7 | +| 13 设计确认报告 | — | `design_validation_report` 设计确认报告(末阶段,批准后项目 done) | 1 | + +合计 60,与 `e2e-rdev.sh` 的 `want 60 rdev stages` 断言一致。(`*` = `skippable: true`,实测 grep 共 15 个:软件 9 个(sw_plan/sw_reqs/sw_config/sw_arch/sw_ui/sw_release/sw_unit_test/sw_sys_test/sw_quality)+ 电气 3 个(ee_bom/pcba/electrical_sys)+ emc_safety + animal_study + clinical) + +**artifact_kind 三类**(grep 实测):`document`(常规成文,47 个)、`review_minutes`(评审记录,6 个)、`upload_index`(附件清单/上传索引,7 个,只建索引不伪造内容)。各 kind 有独立 overlay 质量要求。 + +## 小队与治理机制 + +- **小队编制**:复用整理(organizer)/技术(engineer)/撰写(writer)/法规(regulatory)/检索(searcher)专员,种子化「研发流程负责人」(rdev_leader);市场与财务不进研发小队。 +- **默认五步**:organizer(抽材料事实+回填字段+blackboard facts/gaps/implications)→ engineer(技术内容与追溯,可并行)→ regulatory(仅 complexity_tier=high,可选并行)→ writer(按模板成稿)→ leader(二审完整性/粒度/不适用理由/有无编造,不达标退回)。 +- **门禁**:启动前必须 bizdev CDO 已批准(e2e 脚本专门验证「CDO 前启动应被拒绝」);启动时从商务对接拷贝产品类型/预期用途/目标市场等字段。 +- **状态机**:与 bizdev 同一套编制/审核/批准 FSM;末阶段批准后项目标 `done`。 + +## 开放问题 + +- 本仓库(Agent-System)当前尚无 rdev 工作流模块;若要移植,需评估 60 stage × 模板 × overlay × 小队步骤的落地范围与分期(bizdev 已有移植先例)。 +- 参考项目 `records: [dhf, dmr]` 双归属与云盘 canonical 归档机制在本仓库尚无对应物,移植时需决策是否引入。 +- `complexity_tier=high` 才注入 regulatory 步;本仓库复杂度分层(complexity_high/medium overlay)如何对齐需另调研。 + +## 建议动作 + +- 若立项移植 rdev:在 `ops/short-term/` 新建任务,按「工作流定义 → 模板 → 小队 → overlays → 生命周期门禁」顺序分批实施,先做 phase 01–03(策划/输入)切片。 +- 移植时保留「stage key = 模板文件名」一一对应约束,并复刻 e2e 齐套校验(60 stage、模板非空、末模板含签署)。 diff --git a/.doc/ops/short-term/2026-09-17-rdev-workflow-integration.md b/.doc/ops/short-term/2026-09-17-rdev-workflow-integration.md new file mode 100644 index 00000000..0bb385dd --- /dev/null +++ b/.doc/ops/short-term/2026-09-17-rdev-workflow-integration.md @@ -0,0 +1,65 @@ +--- +title: "研发工作流(rdev)接入:13 阶段 / 60 文档任务" +status: in_progress +created: 2026-09-17 +updated: 2026-09-17 +owner: Cursor +branch: fix/docgov-stage-dynamic → feature/rdev-core +worktree: "-" +mutex: workflow-def +--- + +# 研发工作流(rdev)接入:13 阶段 / 60 文档任务 + +## 目标 + +在现有 5 个正式工作流外新增医疗第 4 工作流「研发设计控制流程」(key: `rdev`),完整承载参考项目 13 phase / 60 文档任务的 ISO 13485 DHF 设计控制链,并通过真实 PI 验收;随后完成全工作流任务键命名空间化(P4)。 + +## 范围 + +- 方案:[07-研发工作流rdev接入-设计与实施计划](../../system-design/project-workflow/07-研发工作流rdev接入-设计与实施计划.md)(P0–P4 五期;§8 四项决策已确认) +- P0:docgov Stage 去硬编码 + stage key 透传 +- P1:rdev.v2.json manifest(13×60 一次到位)+ 60 份 rd_ 前缀模板 + catalog/seed/registry + squad kind 编排 + main-completed precheck + 齐套测试 + stub PG 全链路 +- P2:前端激活规划/切换/前置 Banner + intent 文档名匹配 + prior docs 注入与血缘 +- P3:真实 PI Agent E2E(rd_proposal / rd_planning_review / rd_sw_plan 三样本)+ CI 门禁扩四 +- P4:现有工作流(main/pm/tm + 软件两盘点)任务键 `bd_`/`pm_`/`tm_`(`sw_`)空间化,alias 兼容式改名(D12),独立分支独立验收 + +## 非目标 + +- 二期增强全部移入 backlog:[2026-09-17-rdev-phase2-backlog](../../backlog/2026-09-17-rdev-phase2-backlog.md)(N/A 不适用页生成、UR/RS 结构化门禁、kind 专用门禁、depends_on 精确血缘、DMR 归档、sw_ 键盘点、complexity regulatory 注入) +- 云盘 canonical DHF/DMR 归档(本仓库无云盘模块) +- 软件域(non_medical_software)开放 rdev + +## 步骤 + +1. [x] 用户确认方案 §8 四项决策(2026-09-17:名称「研发设计控制流程」/ 60 任务一次到位 / N/A 规划期关闭 + backlog / rd_ 前缀并扩展到现有工作流键名空间化) +2. [x] P0 分支 `fix/docgov-stage-dynamic`(StageKey 透传;已叠入 `feature/rdev-core`) +3. [x] P1 分支 `feature/rdev-core`(manifest/模板/seed/编排/precheck/齐套测试) +4. [ ] P2 分支 `feature/rdev-web-governance`(前端 + intent + 血缘)— **骨架已定,待独立认领** +5. [ ] P3 真实 PI 验收 + 报告归档 + FEATURE-MATRIX 更新 — **骨架已定,待独立认领** +6. [ ] P4 分支 `feature/workflow-key-namespace`(D12 alias 兼容式改名 + 双路径回归)— **骨架已定,待独立认领** + +## 验收标准 + +- [x] `go vet ./...` && `go test -short` 相关包 PASS(P0+P1) +- [x] 齐套断言:manifest 60 任务 ↔ `templates/docs/rd_*.md` 存在非空;rd_ 键与既有任务键不相交 +- [x] precheck:main 未 completed 时 rd 任务启动被拒且文案含 CDO +- [ ] PG 集成:rdev 实例 planning→live→推进(`TestPG_RDevActivateWithNAClose`,需 `TEST_DATABASE_URL`) +- [ ] web `npm test && npm run build` PASS;Playwright 抽检激活/推进(P2) +- [ ] 真实 PI + MiniMax 三样本 E2E 报告归档 `.doc/test-reports/`(P3) +- [ ] P4:旧 live 实例(旧键)+ 新实例(新键)双路径回归 PASS;全工作流任务键集合无交集 + +## 相关链接 + +- 设计方案: [07-研发工作流rdev接入-设计与实施计划](../../system-design/project-workflow/07-研发工作流rdev接入-设计与实施计划.md) +- 参考调研: [2026-09-17-rdev-workflow-reference-analysis](../research/2026-09-17-rdev-workflow-reference-analysis.md) +- 二期 backlog: [2026-09-17-rdev-phase2-backlog](../../backlog/2026-09-17-rdev-phase2-backlog.md) +- Checklist: - +- Handoff: - +- Test report: - + + +## 证据 / 备注 + +- 参考项目:`.refs/weichen-agent-system`(configs/workflows/rdev.yaml 60 stage、templates/docs 60 模板、squads/overlays 配套) +- 关键约束:WF-DEF-06(无流程级依赖,precheck 先例 test_manager);doc_id `doc_proj{pid}_{taskKey}` 平铺命名 → rd_ 前缀 diff --git a/.doc/status/FEATURE-MATRIX.md b/.doc/status/FEATURE-MATRIX.md index 1a86d3a1..a1dcbc59 100644 --- a/.doc/status/FEATURE-MATRIX.md +++ b/.doc/status/FEATURE-MATRIX.md @@ -55,6 +55,7 @@ | 对话数据治理(D7) | 留存/删除口径、库·备份·日志控制、敏感字段加密边界 | deferred | deferred_enhancement(违规则升 blocker) | [计划](../remediation/2026-09-16-feature-matrix-production-readiness-plan.md) Scope Boundaries;[收口 §7.1](../remediation/2026-09-16-partial-deferred-completion-plan.md);[D7 ADR 草案](../adr/2026-09-17-d7-chat-data-retention.md);[W7 盘点](../audit/2026-09-17-d7-data-inventory.md);[报告](../test-reports/2026-09-17-w7-d7-inventory-test-report.md) | **after W7**:只读盘点完成(sessions/messages/KB/日志/备份/event)。**未发现**已确认基线违规,未写 gap-matrix。口径仍 `pending-compliance-signoff`;禁止按 365/180/30 物理删除。本行不升 done | `chat` | | 智能体小队 | 角色-任务-工作区-黑板 | done | non_blocker | [agent-squad](../system-design/agent-squad/);[test 2026-09-07](../test-reports/2026-09-07-dynamic-squad-planning-test-report.md) | 非关键步骤降级(A5/A6)属 backlog | `squad` | | 工作流治理 | 三正式工作流、设置页/DutyMode、动作级鉴权、真实 PI 三工作流 E2E、full/none 混合长链 | done | non_blocker | [三工作流整改](../remediation/2026-09-12-three-workflows-strict-governance-remediation.md);[409/职责](../test-reports/2026-09-14-workflow-plan-409-frozen-and-optional-duties-test-report.md);[鉴权](../test-reports/2026-09-14-governance-action-level-authz-test-report.md);[PI E2E](../test-reports/2026-09-16-pi-three-workflow-e2e-test-report.md);[Wave2 full/none](../test-reports/2026-09-16-wave2-real-pi-evidence-test-report.md) | full 跨用户审批 + none 免审批不阻塞已签。纯软件自举仍独立 planned | `workflow` | +| 研发设计控制(rdev) | 医疗第 4 工作流:13×60 manifest/模板、`squad.rdev_doc.v1` kind 编排、main-completed 硬 precheck、StageKey 动态化;不含前端/真 PI/键名空间化 | partial | deferred_enhancement | [设计](../system-design/project-workflow/07-研发工作流rdev接入-设计与实施计划.md);[short-term](../ops/short-term/2026-09-17-rdev-workflow-integration.md);[backlog R1–R7](../backlog/2026-09-17-rdev-phase2-backlog.md) | **P0+P1 done**(齐套/precheck/stub)。关闭条件:P2 前端治理 + P3 真 PI 三样本报告 + CI 四工作流门禁。P4 键名空间化独立行/任务 | `workflow-def` | | 纯软件项目自举 | 软件项目创建、Bootstrap、Delivery、受控工程交接 | partial | deferred_enhancement(明确不挡首发) | [设计](../system-design/2026-09-14-software-project-bootstrap-workflow.md);[ADR](../adr/2026-09-16-software-project-bootstrap-keys.md);[L1 报告](../test-reports/2026-09-16-software-project-bootstrap-test-report.md);[W5a](../test-reports/2026-09-17-w5a-software-bootstrap-ui-pi-test-report.md) | **before**:L1 + UI 仍克隆医疗模板。**after W5a**:catalog 驱动创建、软件不挂医疗 key、docs_only 真 PI 建议 delivery 且不自动激活。**SWBOOT-10 仍 stub**(W5b);本行不升 done | `workflow-design` | | IAM / 鉴权 | 项目级 API 鉴权 + RBAC;会话/阶段授权 | done | 加固 | [v3 full inventory](../audit/authorization-matrix-v3-full.md);[session 中断](../test-reports/2026-09-16-iam-route-inventory-test-report.md);[fail-closed](../test-reports/2026-09-16-wave1-iam-fail-closed-test-report.md);[410 策略](../audit/2026-09-16-bug11-410-retire-decision.md);[R13](../audit/2026-09-16-r13-data-safety-checklist.md) | 全路由 inventory + session 中断负例 + 410 冻结 + R13 基线已签 | `authz` | | 文档治理 | 法定文档生命周期 + 租约 + 受控正文读取 | done | non_blocker | [docgov](../test-reports/2026-09-08-docgov-lifecycle-and-claiming-framework-test-report.md);[正文 UI](../test-reports/2026-09-14-controlled-content-ui-acceptance.md);[canonical hash](../remediation/2026-09-14-controlled-content-canonical-hash-remediation.md) | 项目 143 只读;不终止 | `docgov` | diff --git a/.doc/system-design/project-workflow/07-研发工作流rdev接入-设计与实施计划.md b/.doc/system-design/project-workflow/07-研发工作流rdev接入-设计与实施计划.md new file mode 100644 index 00000000..26b86a6b --- /dev/null +++ b/.doc/system-design/project-workflow/07-研发工作流rdev接入-设计与实施计划.md @@ -0,0 +1,254 @@ +# 07 研发工作流(rdev)接入——设计与实施计划 + +- **版本**: v1.1(四项关键决策已由用户确认,2026-09-17;见 §8) +- **日期**: 2026-09-17 +- **来源**: 用户需求「在系统中加入研发工作流」;参考项目调研见 [research/2026-09-17-rdev-workflow-reference-analysis.md](../../ops/research/2026-09-17-rdev-workflow-reference-analysis.md) +- **范围**: 在现有 5 个正式工作流(医疗三 + 软件两)之外,新增医疗第 4 工作流「**研发设计控制流程**」(key: `rdev`,**13 阶段 / 60 文档任务一次到位**,ISO 13485 DHF/DMR 设计控制链);并随后对现有工作流任务键做统一命名空间化(D12,P4) +- **分支建议**: `feature/rdev-workflow`(从 dev 切出);键名空间化独立分支 `feature/workflow-key-namespace` + +--- + +## 1. 结论摘要 + +参考项目的 rdev 工作流与本系统的「workflowdef 版本化定义 + projectinstance 冻结实例 + bizdev/squad 执行链」架构高度同构,接入以**数据与配置为主、代码为辅**: + +1. **一个新 manifest**(`rdev.v2.json`,13 stage × 60 doc task)+ **60 份模板** + **一个通用执行器键** `squad.rdev_doc.v1` + **一个数据驱动的 rdev 任务目录表**(squad 包内)即可跑通主链; +2. **两处前置修复**:docgov 文档 Stage 字段去硬编码(现为写死 `V0.5_feasibility`);rdev 启动前置门禁(main 实例 completed,即 CDO 已批准)按 test_manager 先例做**执行器级 precheck**,不恢复流程级依赖(遵循 [WF-DEF-06 整改](../../remediation/2026-09-12-three-workflows-strict-governance-remediation.md)); +3. **一个关键命名决策**:60 个任务键统一加 `rd_` 前缀(含模板文件名),避免与既有任务键 `system_reqs` 在 doc_id(`doc_proj{id}_{taskKey}`)、模板加载(`GetDocTemplate(taskKey)` 平铺目录)两层冲突; +4. 分四期实施(P0 前置修复 → P1 核心接入 → P2 治理与体验 → P3 真实 PI 验收),预估 2–3 周(单人)。 + +## 2. 参考项目 rdev 要点(回顾) + +见 [调研笔记](../../ops/research/2026-09-17-rdev-workflow-reference-analysis.md)。核心结构: + +- **13 phase / 60 stage**:01 策划(2) → 02 策划评审(1) → 03 设计输入(12) → 04 需求评审(1) → 05 输入阶段评审(1) → 06 设计输出(17) → 07 输出评审(1) → 08 验证(12) → 09 验证评审(1) → 10 转换(3) → 11 转换评审(1) → 12 确认(7) → 13 设计确认报告(1); +- **15 个 skippable**(软件 9 + 电气 3 + EMC + 动物试验 + 临床); +- **三种 artifact_kind**:`document` 47 / `review_minutes` 6 / `upload_index` 7; +- **记录归属**:`dhf` 为主,设计输出/转换类 `dhf+dmr`; +- **followon 门禁**:商务对接 CDO 批准后才可启动;启动时拷贝 bizdev 已填字段; +- **质量纪律**:模板写满、结论先行、UR→RS 追溯、缺项「待确认」、不适用须完整理由仍走审批、不编造数据/签字。 + +## 3. 现系统机制盘点(与本任务相关) + +| 机制 | 现状 | 对 rdev 的影响 | +|------|------|----------------| +| 工作流定义 | `internal/workflowdef`:Definition + Version(schema v2 JSONB 快照、definition_hash、published);正式 key 白名单 `catalog_policy.go`;seed 走 `manifests/*.json` 内嵌 + `BootstrapSeeds` 幂等发布 | 新增第 6 个正式 key;manifest/seed/白名单/目录顺序同步 | +| 模板资产 | `templates/docs/*.md` 启动时全量上传(asset_key=文件名);运行时 `template.GetDocTemplate(taskKey)` 平铺读 `templates/docs/.md` | **平铺命名 → 必须无碰撞**;rd_ 前缀方案 | +| 项目实例 | `projectinstance`:planning→live 冻结 ConfigSnapshot;阶段线性 position;`AdvanceStage` 要求全部 blocking doc task approved;末阶段完成 → InstanceDone | 13 stage 线性推进天然匹配 phase 序;depends_on 语义由阶段序覆盖 | +| 执行链 | server `task_run_handlers` → `taskrun.Manager`(单一 Executor = `bizdev.ExecuteTask`)→ `squad.ExecuteTask`(AdaptivePlanner 按 taskKey 出步骤)→ PI Agent Runner | executor_key 目前是**发布校验元数据**,运行时统一走 bizdev;新增任务主要是 squad 侧编排数据 | +| squad 编排 | `SupportedTasks` 白名单(9 个);`DefaultWorkflowSteps`/`DefaultSquadAgents` 按 taskKey **硬编码 switch**;非白名单任务回落 autonomous adhoc | 60 任务若逐个 switch 不可维护 → 改为按 artifact_kind 三套通用编排 + rdev 数据目录表 | +| 文档账本 | docgov:`doc_id = doc_proj{pid}_{taskKey}`;**Stage 字段写死 `V0.5_feasibility`**(bizdev/service.go:483,489 + docgov/repository.go:28 兜底) | **P0 必修**:改为从冻结实例解析 StageKey | +| 前置依赖 | 无流程级依赖(WF-DEF-06 已移除);test_manager 用执行器级 precheck(approved SRS)+ `WorkflowPrerequisiteBanner` | rdev 同模式:precheck「main 实例 completed」 | +| 血缘 stale | `projectworkflow/service.go` MarkDownstreamStale;test_manager 记录单一上游 hash | 首期统一项目上下文注入;精确 depends_on 血缘列二期 | +| 意图 | L1 `extractDocName` 硬编码 9 个文档名;U8 多意图 task-generate 从 live 实例数据驱动 | rd 文档名匹配需增补或实例驱动化 | +| 前端 | `ProjectWorkflowSwitcher`(目录 API 驱动)+ 激活规划 UI + StageAdvance/Prerequisite Banner | 13×60 规划 UI 需折叠分组与批量三权指派 | + +## 4. 关键设计决策 + +### D1 工作流标识与目录 + +- `OfficialKeyRDev = "rdev"`;对外名称建议 **「研发设计控制流程」**(与「商务对接流程」命名风格一致;备选「研发流程」); +- `product_domains: ["medical_device"]`(软件域不可选); +- `catalog_order = 40`(排在 test_manager 之后、software_bootstrap 之前);`medicalWorkflowKeys` 扩为四个; +- validator 文案「仅允许选择正式工作流(医疗三或软件自举/交付)」同步更新;审计口径「项目选择页只见 N 个工作流」从 5 → 6。 + + +### D2 任务键与模板命名(关键决策) + +**问题**:`doc_id`、模板加载、docgov task_key 三层都以 taskKey 平铺命名。rdev 的 `system_reqs`(SR)与现有 PM 任务 `system_reqs` 直接冲突;且未来任何工作流任务键都共享同一命名空间。 + +**推荐:60 个任务键统一加 `rd_` 前缀**(`rd_proposal`、`rd_design_plan`、`rd_system_reqs`…),模板文件同步 `rd_*.md`: + +- 彻底消除 doc_id/模板/任务键三层冲突,跨工作流血缘清晰; +- 转换机械:从参考项目 `rdev.yaml` 逐行映射生成(可写一次性生成脚本,人工复核); +- manifest 中 `title`/`output_doc_title` 保留中文文档名,用户可见名称不变; +- 模板头部「文件编号 RD-…」保留参考项目编号(RD-PROPOSAL 等),不受键名影响。 + +**备选**:仅 `system_reqs` 一键改名、其余保持原键。缺点:命名空间纪律弱,未来再加工作流仍会踩。 + +### D3 manifest 结构(rdev.v2.json) + +13 个 StageDef 对应 13 个 phase(键名风格沿用本仓库 `V0.1_triage` 惯例)。示例(完整 60 任务 = 参考项目 rdev.yaml 逐行映射): + +```json +{ + "schema_version": 2, + "workflow_key": "rdev", + "product_domains": ["medical_device"], + "stages": [ + {"key": "RD01_planning", "name": "01 设计和开发策划", "position": 10, "optional": false, + "doc_tasks": [ + {"key": "rd_proposal", "title": "项目建议书", "default_enabled": true, "creator_configurable": true, + "blocking_when_enabled": true, "executor_key": "squad.rdev_doc.v1", "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_proposal", "output_doc_type": "rd_proposal", "output_doc_title": "项目建议书"}, + {"key": "rd_design_plan", "title": "设计开发计划任务书", "default_enabled": true, "creator_configurable": true, + "blocking_when_enabled": true, "executor_key": "squad.rdev_doc.v1", "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_design_plan", "output_doc_type": "rd_design_plan", "output_doc_title": "设计开发计划任务书"} + ]}, + {"key": "RD02_planning_review", "name": "02 策划评审", "position": 20, + "doc_tasks": [{"key": "rd_planning_review", "title": "设计和开发策划评审记录", "...": "同上模式"}]}, + {"key": "RD03_design_input", "name": "03 设计输入", "position": 30, "doc_tasks": ["12 个任务"]}, + {"key": "RD04_requirements_review", "position": 40, "doc_tasks": ["rd_req_review"]}, + {"key": "RD05_input_phase_review", "position": 50, "doc_tasks": ["rd_input_phase_review"]}, + {"key": "RD06_design_output", "position": 60, "doc_tasks": ["17 个任务"]}, + {"key": "RD07_output_review", "position": 70, "doc_tasks": ["rd_output_review"]}, + {"key": "RD08_verification", "position": 80, "doc_tasks": ["12 个任务"]}, + {"key": "RD09_verification_review", "position": 90, "doc_tasks": ["rd_verification_review"]}, + {"key": "RD10_transfer", "position": 100, "doc_tasks": ["3 个任务"]}, + {"key": "RD11_transfer_review", "position": 110, "doc_tasks": ["rd_transfer_review"]}, + {"key": "RD12_validation", "position": 120, "doc_tasks": ["7 个任务"]}, + {"key": "RD13_validation_report", "position": 130, "doc_tasks": ["rd_design_validation_report"]} + ] +} +``` + +- 全部任务 `default_enabled/creator_configurable/blocking_when_enabled = true`(与本仓库 normalize 惯例一致); +- **skippable 语义**:参考项目的 15 个可声明不适用任务与本仓库「激活规划期可关闭」语义等价(creator 在规划时关闭 = 不适用);「生成完整不适用页仍走审批」列为二期增强(D9); +- `output_doc_type` = 任务键(本仓库惯例 doc type = task key)。 + +### D4 执行器与质量门禁 + +- 新增唯一执行器键 **`squad.rdev_doc.v1`**(`workflow/registry.go`),运行时仍统一由 bizdev.ExecuteTask 按 taskKey 分派——与 `squad.test_manager.v1` 模式一致,避免 60 个注册项; +- 质量门禁首期 **`quality.generic.v1`**(全部 60 任务);kind 专用门禁列二期(D9)。 + +### D5 启动前置门禁(followon 语义) + +遵循 WF-DEF-06:**不做流程级 RequiredUpstreamWorkflow**,改为执行器级 precheck(先例:test_manager 查 approved SRS): + +- `bizdev.ExecuteTask` 对 `rd_` 前缀任务启动前校验:本项目 **main 工作流 live 实例 `completed`**(即 CDO 报价已批准)。查不到/未完成 → 明确错误文案(「请先完成商务对接流程(CDO 报价批准)后再启动研发设计控制流程」); +- 激活 rdev 实例时(planning 允许,运行由 precheck 兜底):`WorkflowPrerequisiteBanner` 增加 rdev 文案;目录推荐逻辑「main 完成后推荐 PM」扩展「main 完成后推荐 rdev」; +- 启动时拷贝 bizdev 字段:现有 `TaskRunRequest` 已带 project 字段(device_type/intended_use/target_market 来自项目实体),无需额外拷贝机制。 + +### D6 docgov Stage 字段动态化(P0 必修) + +- `bizdev/service.go:483/489` 与 `docgov/repository.go:28` 写死 `V0.5_feasibility` → 改为从请求/冻结实例解析 StageKey 传入(`workflow/runtime/orchestrator.go` 的 `TaskResolution.StageKey` 已具备;`server/task_run_request_build` 把 stage key 带进 `bizdev.TaskRunRequest` 新增的 `StageKey` 字段并透传); +- 兜底策略:解析不到时回落 main 三阶段映射,再回落原值(兼容历史数据,不做历史迁移); +- `maturity` 包的 V0.1/V0.5/V1.0 成熟度映射仅对 main 任务生效,rd_ 任务首期跳过成熟度标签。 + +### D7 squad 编排(数据驱动重构) + +新增 `internal/squad/rdev_catalog.go`:60 项任务目录表(taskKey → title/phase/kind/docCode/skippable/描述),生成来源 = 参考项目 rdev.yaml(一次性转换 + 齐套测试锁定): + +- `SupportedTasks` 合并 rdev 目录(`isStandardTask` 命中,避免回落 adhoc); +- `DefaultWorkflowSteps`/`DefaultSquadAgents` 增加 `rd_` 前缀分支,**按 artifact_kind 三套通用编排**(对齐参考项目 squads/rdev.yaml 的 default_steps + 覆盖逻辑): + +| kind | 步骤 | 小队 | +|------|------|------| +| document(47) | organizer → engineer(可并行)→ regulatory(可选)→ writer → leader | organizer/engineer/writer/leader;regulatory 注入软件/法规风险类(`rd_sw_*`、`rd_regs_stds`、`rd_adverse_events`、`rd_biocompatibility`、`rd_clinical`、`rd_animal_study`、`rd_risk_*`、`rd_emc_safety`) | +| review_minutes(6) | organizer → writer → leader | organizer/writer/leader(对齐参考项目评审阶段三步) | +| upload_index(7) | organizer → engineer → writer → leader | organizer/engineer/writer/leader | + +- leader 角色名「研发设计控制负责人」(对应参考项目 rdev_leader);角色提示复用现有 `configs/agents/*.md`(organizer/engineer/writer/leader/regulatory 已有),rdev 语境差异放进步骤 `Accept` 与 TASK 说明; +- 参考项目 overlays 的核心纪律(写满、结论先行、追溯表必填、缺项「待确认」、不编造数据/签字/附件)合并进:a) 三套 kind 步骤的 `Accept`;b) `configs/shared/writing.md` 增补 DHF 纪律段(GetAgentInstruction 已自动附加该文件,rd 任务天然注入)。 + +### D8 上游文档注入与血缘 + +- 首期:`bizdev.ExecuteTask` 对 `rd_` 任务注入 **项目上下文快照**(`projectcontext.BuildSnapshot`,先例 test_manager)+ **同项目已批准文档清单**(bizdev 七件 + 已批准 rd_ 文档,作为 prior_docs 全量注入,交小队自行引用); +- 血缘:沿用 test_manager 模式在 finalize 时记录上游 hash(首期摘要级);精确 `depends_on`(如 rd_verification_review ← rd_design_vv + rd_risk_report_v)列二期(见 [backlog](../../backlog/2026-09-17-rdev-phase2-backlog.md)); +- `rd_system_reqs` 与 PM `system_reqs` 的关系:PM 输出若已批准,作为 prior doc 优先注入(不强制、不独占)。 + +### D9 不适用(N/A)与二期增强 + +- 首期:N/A = 激活规划期关闭任务(creator_configurable),推进语义由 readiness 门禁保证; +- 二期增强(参考项目行为):保留任务但生成「不适用说明页」(完整理由、走审批);UR/RS 结构化门禁(复用 PM `requirements.json` 模式于 rd_user_needs/rd_system_reqs);review_minutes/upload_index 专用确定性门禁。 + +### D10 意图与聊天 + +- U8 多意图 task-generate 从 live 实例 doc tasks 数据驱动——rd 任务应自动可用(P2 验证 60 任务名消歧与澄清卡); +- L1 `extractDocName` 硬编码名表:增补 rd 高频文档名(项目建议书、设计开发计划、风险管理计划、设计验证方案、设计确认报告 等),并**评估改为从 live 实例任务标题动态匹配**(推荐,彻底消除硬编码)。 + +### D11 前端 + +- `ProjectWorkflowSwitcher`:rdev 名称/顺序/描述来自目录 API(数据驱动)+ 本地 label 常量增补; +- 激活规划 UI:13 阶段 × 60 任务 × 三权指派是大列表——需按阶段折叠分组、三权「按阶段批量指派 / 继承上一阶段」操作、15 个 skippable 任务预设关闭建议; +- 文档工作台:展示 doc Stage 为 RD 阶段键(依赖 D6);`WorkflowPrerequisiteBanner` rdev 文案; +- 前端测试:workflow 相关既有断言(工作流数量/名称)同步更新。 + +## 5. 资产与代码改动清单 + +### 5.1 数据/配置资产 + +| 资产 | 说明 | +|------|------| +| `internal/workflowdef/manifests/rdev.v2.json` | 13 stage / 60 doc task(D3) | +| `templates/docs/rd_*.md` × 60 | 从参考项目 `templates/docs/*.md` 转换:改名 rd_ 前缀、保留 RD 编号/DHF 归属骨架;核对模板变量(`{{project_name}}/{{generated_at}}/{{snapshot_id}}/{{applicability}}/{{na_rationale}}/{{materials}}` 均为 RenderDoc 可用变量,缺失自动「待确认」) | +| `configs/shared/writing.md` 增补 | DHF 设计控制纪律段(rd 任务注入) | +| `internal/squad/rdev_catalog.go` | 60 项任务目录(title/phase/kind/docCode/skippable) | + +### 5.2 Go 代码(按包) + +| 包 | 改动 | +|------|------| +| `internal/workflowdef` | catalog_policy.go(常量/名称/顺序/域/医疗四键)、manifest_loader.go(V2ManifestFiles)、bootstrap.go(officialSeedMeta)、validator 文案、publish/catalog 测试更新 | +| `internal/workflow` | registry.go 注册 `squad.rdev_doc.v1` | +| `internal/squad` | rdev_catalog.go 新增;service.go SupportedTasks 合并;workspace.go rd_ 分支(按 kind 三套 DefaultWorkflowSteps/DefaultSquadAgents) | +| `internal/bizdev` | TaskRunRequest 增加 StageKey;ExecuteTask:rd_ precheck(main completed)、prior docs 注入、CreateOrGetDocument 传动态 Stage、ListStages/maturity 对 rd_ 的处理 | +| `internal/server` | task_run_request_build 透传 stage key;workflow_catalog_handlers 名称;齐套校验 Go test(60 manifest 任务 ↔ 模板文件存在非空 ↔ 末模板含「签署」,对齐参考 e2e-rdev.sh 断言 + 「rd_ 键与既有任务键集合不相交」断言) | +| `internal/docgov` | repository.go stage 兜底改为入参优先 | +| `internal/intent` | extractDocName 增补/实例驱动(D10) | +| `web/` | D11 各项 | + +### 5.3 文档 + +- `.doc/ops/short-term/2026-09-17-rdev-workflow-integration.md`(任务条目,随本方案创建) +- FEATURE-MATRIX 新增行(planned → done 演进);验收报告归档 `.doc/test-reports/` + +## 6. 分期实施计划 + +| Phase | 内容 | 分支/PR | 验收标准 | +|-------|------|--------|----------| +| **P0 前置修复** | D6 docgov Stage 动态化 + stage key 透传 + 回归 | `fix/docgov-stage-dynamic` | 现有三工作流全链路 PG 回归 PASS;新文档 Stage 字段 = 实例阶段键 | +| **P1 核心接入** | manifest + catalog/seed + registry + 60 模板 + squad 数据目录与 kind 编排 + rd_ precheck + 齐套 Go test + stub PG 全链路(激活 13 阶段 → rd_proposal 生成/审/批 → 推进 → N/A 关闭路径 → precheck 拒绝路径) | `feature/rdev-core` | `go vet`/`go test -short` PASS;PG 集成测试 PASS;齐套断言 60/60;main 未完成时 rd 任务启动被拒且文案正确 | +| **P2 治理与体验** | 前端(规划 UI 折叠分组/批量三权/PrerequisiteBanner/文档 Stage 展示)+ intent 名称匹配 + prior docs 注入与摘要级血缘/stale + web 测试 | `feature/rdev-web-governance` | web `npm test`/`npm run build` PASS;Playwright 抽检激活与推进;聊天「生成项目建议书」命中 rd_proposal | +| **P3 真实验收** | 真实 PI Agent + MiniMax 全链路:rd_proposal(document)+ rd_planning_review(review_minutes)+ rd_sw_plan 软件类 N/A 三样本;报告归档;CI「PI 三工作流」门禁扩为四 | — | `.doc/test-reports/` rdev 真实 PI E2E 报告;CI green receipt | +| **P4 键名空间化**(决策 4 扩展,D12) | main/pm/tm(+软件两)任务键改 `bd_`/`pm_`/`tm_`(`sw_`)前缀:manifest 新版本 + doc_id 归一映射 + 模板 alias + squad 旧键兼容 + 双路径回归 | `feature/workflow-key-namespace`(**独立验收,不与 P1–P3 合并**) | 旧 live 实例(旧键)与新建实例(新键)双路径 stub 全链路 PASS;全工作流任务键集合无交集断言 PASS;存量文档旧 doc_id 仍可读 | + +依赖:P0 → P1 → P2 → P3;P1 与 P2 可部分并行(先冻结前后端契约);P4 在 P3 验收后启动(也可与 P2 并行,但合入 dev 须占集成槽串行)。 + +## 7. 风险与缓解 + +| 风险 | 影响 | 缓解 | +|------|------|------| +| 60 任务三权指派 UI 负担 | 激活体验差、误配 | 批量指派 + 阶段继承 + 三权默认创建人兜底(现状) | +| generic 质量门禁误杀评审记录/附件清单类 | 任务被错误打回 | P1 用 stub 对 6+7 个 kind 任务逐一回归;必要时豁免清单 | +| U8/意图对 60 文档名消歧 | 生成错任务 | P2 实例驱动匹配 + 澄清卡;高频名称抽检 | +| 命名空间遗漏 | doc_id/模板冲突回归 | 齐套 test 增加「rd_ 键不与既有任务键相交」断言 | +| CI 门禁与审计口径过时 | nightly 误报 | 同步「PI 三工作流」→ 四;审计 checklist 引用本方案 | +| 大 manifest / 冻结快照体积 | 性能 | 60 任务 JSONB 量级小,P1 PG 测试观察即可 | + +## 8. 决策记录(已与用户确认,2026-09-17) + +| # | 决策 | 结论 | +|---|------|------| +| 1 | 对外名称 | **「研发设计控制流程」**(key: `rdev`) | +| 2 | 首期范围 | **manifest 一次到位 60 任务 / 13 阶段**,不切试点 | +| 3 | N/A 语义 | 首期「激活规划期关闭」;「生成完整不适用页走审批」等二期增强**记入 backlog**:[2026-09-17-rdev-phase2-backlog](../../backlog/2026-09-17-rdev-phase2-backlog.md) | +| 4 | 任务键前缀 | rdev 60 键统一 `rd_` 前缀;且**现有工作流(main/pm/tm)任务键、模板、阶段等同样做键名空间化处理**(见 D12,独立分期 P4,避免与 rdev 接入耦合) | + +### D12 现有工作流键名空间化(决策 4 扩展,P4 实施) + +**目标**:全系统任务键命名规约统一为 `<工作流前缀>_<任务>`,杜绝跨工作流 doc_id/模板/任务键冲突: + +| 工作流 | 现键(9 个) | 新键 | +|--------|-------------|------| +| main(商务对接) | intake / research / feasibility_dev / feasibility_reg / user_requirements / prelim_design / cdo_quote | `bd_intake` / `bd_research` / `bd_feasibility_dev` / `bd_feasibility_reg` / `bd_user_requirements` / `bd_prelim_design` / `bd_cdo_quote` | +| product_manager | system_reqs | `pm_system_reqs` | +| test_manager | test_manager | `tm_system_verification` | +| rdev(新) | — | `rd_*`(P1 落地,60 个) | +| software_* | (另行盘点 `software.doc.v1` 任务键后纳入 `sw_` 前缀,P4 一并处理) | + +**兼容策略(alias 兼容式改名,不破坏存量数据)**: + +1. **manifest 版本升级**:main/pm/tm manifest 任务键改为新键 → `UpsertPublishedSeed` 按内容 hash 发布新版本;**冻结实例不迁移**——存量 live 实例继续持有旧键直至 completed(冻结快照是 SSOT,禁止改写); +2. **doc_id 兼容**:`bizdev/doc_id.go normalizeDocID` 增加旧→新映射表(`doc_proj{pid}_intake` → `doc_proj{pid}_bd_intake`),读路径旧 ID 自动归一;**文档账本不批量迁移**,新文档用新键,旧文档旧 ID 保留可查(已有 legacy pattern 归一先例); +3. **模板**:`templates/docs/` 文件改名(bd_intake.md 等),template loader 增加旧名→新名 alias(旧 live 实例运行仍按旧 taskKey 取模板→alias 命中新文件); +4. **squad 白名单**:`SupportedTasks`/`DefaultWorkflowSteps`/`DefaultSquadAgents` 增加旧键 alias 分支(存量实例执行路径不动),新键为主实现; +5. **intent/前端**:`extractDocName` 按文档名匹配不涉键名(无改动);前端断言与 label 按新键更新; +6. **回归**:旧 live 实例(旧键)+ 新规划实例(新键)双路径 stub 全链路回归;齐套断言扩展为「全工作流任务键集合无交集 + 各自前缀正确」。 + +**风险控制**:P4 独立分支 `feature/workflow-key-namespace`、独立验收,**不与 rdev P1–P3 合并**;若 P4 验收发现存量实例兼容缺口,允许只回退 manifest(DB seed 幂等)。 + +### 二期 backlog(决策 3,随时可续推进) + +见 [2026-09-17-rdev-phase2-backlog](../../backlog/2026-09-17-rdev-phase2-backlog.md):N/A 不适用页生成模式、UR/RS 结构化门禁、review_minutes/upload_index 专用确定性门禁、depends_on 精确血缘与 stale 传播、DMR 双记录归档、软件工作流 sw_ 键盘点等。 + diff --git a/cmd/engineering-dashboard/main.go b/cmd/engineering-dashboard/main.go index 43edb2a3..bec4d7ab 100644 --- a/cmd/engineering-dashboard/main.go +++ b/cmd/engineering-dashboard/main.go @@ -91,7 +91,7 @@ func runServe(args []string) error { if err != nil { return err } - fmt.Printf("engineering-dashboard listening on http://%s/?token=%s\n", addr, srv.Token) + fmt.Printf("engineering-dashboard listening on http://%s/\n", addr) fmt.Printf("repo=%s\n", root) return engineeringdashboard.ListenAndServe(addr, srv.Handler()) } diff --git a/configs/shared/writing.md b/configs/shared/writing.md index b291899c..ded153be 100644 --- a/configs/shared/writing.md +++ b/configs/shared/writing.md @@ -31,3 +31,14 @@ 3. 文中所有「待确认」是否已在文末清单汇总。 4. 是否有未标注的推测、疗效宣称或绝对化用语。 5. 专员已标注的出处是否保留;无依据的分析是否被误删。 + +## DHF 设计控制纪律(研发设计控制流程 rd_* 任务) + +适用于 ISO 13485 设计历史文件(DHF)类受控文档: + +- 模板各章节必须写满;禁止残留括号提示或空表壳。 +- 结论先行:文首给出适用性/通过/不适用结论,后文论证不得与结论矛盾。 +- 追溯表必填:本文件 ID、上游输入、下游验证/确认;未知项写入「待确认」而非留空。 +- 缺项统一标「待确认」,并在文末待确认清单汇总。 +- 严禁编造试验数据、标准号、URL、签字、附件正文;附件清单类只建索引。 +- 声明不适用时须写完整理由(首期若任务在规划期关闭则不生成正文)。 diff --git a/internal/bizdev/rdev_flow_test.go b/internal/bizdev/rdev_flow_test.go new file mode 100644 index 00000000..d874e647 --- /dev/null +++ b/internal/bizdev/rdev_flow_test.go @@ -0,0 +1,44 @@ +package bizdev + +import ( + "context" + "errors" + "strings" + "testing" + + "chat-agent-system/internal/config" + "chat-agent-system/internal/projectworkflow" + "chat-agent-system/internal/squad" +) + +// TestRDev_PrecheckAndKindOrchestration 覆盖 rdev 硬门禁与 kind 编排(stub,无真实 PI)。 +func TestRDev_PrecheckAndKindOrchestration(t *testing.T) { + cfg := &config.Config{DataDir: t.TempDir(), AllowInMemory: true, AgentBackend: "stub"} + svc, err := NewService(cfg) + if err != nil { + t.Fatal(err) + } + + _, err = svc.ExecuteTask(context.Background(), TaskRunRequest{ + TaskKey: "rd_proposal", + ProjectID: 9001, + Backend: "stub", + WorkDir: t.TempDir(), + }) + if err == nil || !errors.Is(err, projectworkflow.ErrPrerequisiteNotMet) { + t.Fatalf("want ErrPrerequisiteNotMet, got %v", err) + } + if !strings.Contains(err.Error(), "CDO") { + t.Fatalf("message: %v", err) + } + + if n := len(squad.DefaultWorkflowSteps("rd_proposal")); n < 4 { + t.Fatalf("document steps=%d", n) + } + if n := len(squad.DefaultWorkflowSteps("rd_planning_review")); n != 3 { + t.Fatalf("review steps=%d", n) + } + if n := len(squad.DefaultWorkflowSteps("rd_prototype_photos")); n != 4 { + t.Fatalf("upload steps=%d", n) + } +} diff --git a/internal/bizdev/rdev_precheck_test.go b/internal/bizdev/rdev_precheck_test.go new file mode 100644 index 00000000..18828547 --- /dev/null +++ b/internal/bizdev/rdev_precheck_test.go @@ -0,0 +1,43 @@ +package bizdev + +import ( + "context" + "errors" + "strings" + "testing" + + "chat-agent-system/internal/projectworkflow" +) + +func TestEnsureRDevMainCompleted_RejectsWhenMissing(t *testing.T) { + s := &Service{ + completedMainHook: func(context.Context, int64) (bool, error) { return false, nil }, + } + err := s.ensureRDevMainCompleted(context.Background(), 42) + if err == nil { + t.Fatal("expected prerequisite error") + } + if !errors.Is(err, projectworkflow.ErrPrerequisiteNotMet) { + t.Fatalf("want ErrPrerequisiteNotMet, got %v", err) + } + if !strings.Contains(err.Error(), "CDO") { + t.Fatalf("message missing CDO hint: %v", err) + } +} + +func TestEnsureRDevMainCompleted_AllowsWhenCompleted(t *testing.T) { + s := &Service{ + completedMainHook: func(context.Context, int64) (bool, error) { return true, nil }, + } + if err := s.ensureRDevMainCompleted(context.Background(), 42); err != nil { + t.Fatal(err) + } +} + +func TestResolveDocStage_SkipsMaturityForRD(t *testing.T) { + // rd_ tasks without StageKey fall back to V0.5 (not maturity labels); StageKey wins. + got := resolveDocStage(TaskRunRequest{TaskKey: "rd_proposal", StageKey: "RD01_planning"}) + if got != "RD01_planning" { + t.Fatalf("got %q", got) + } +} diff --git a/internal/bizdev/resolve_doc_stage_test.go b/internal/bizdev/resolve_doc_stage_test.go new file mode 100644 index 00000000..fa5c1e72 --- /dev/null +++ b/internal/bizdev/resolve_doc_stage_test.go @@ -0,0 +1,30 @@ +package bizdev + +import "testing" + +func TestResolveDocStage_PrefersStageKey(t *testing.T) { + got := resolveDocStage(TaskRunRequest{TaskKey: "intake", StageKey: "RD01_planning"}) + if got != "RD01_planning" { + t.Fatalf("got %q, want RD01_planning", got) + } +} + +func TestResolveDocStage_MainTaskFallback(t *testing.T) { + cases := []struct { + task string + want string + }{ + {"intake", string(StageV01_Triage)}, + {"research", string(StageV05_Feasibility)}, + {"feasibility_dev", string(StageV05_Feasibility)}, + {"cdo_quote", string(StageV10_Quoting)}, + {"system_reqs", string(StageV05_Feasibility)}, + {"", string(StageV05_Feasibility)}, + } + for _, tc := range cases { + got := resolveDocStage(TaskRunRequest{TaskKey: tc.task}) + if got != tc.want { + t.Fatalf("task=%q got %q want %q", tc.task, got, tc.want) + } + } +} diff --git a/internal/bizdev/service.go b/internal/bizdev/service.go index 78869523..5ae6a87e 100644 --- a/internal/bizdev/service.go +++ b/internal/bizdev/service.go @@ -51,6 +51,8 @@ type Service struct { stageTaskHook func(context.Context, TaskRunRequest) (*TaskRunResult, error) // createDocumentHook 仅测试注入:覆盖账本创建失败路径。 createDocumentHook func(ctx context.Context, docID string, projectID int64, taskKey, title, stage, workDir string, authorID int64, authorName string, reviewerID int64, reviewerName string, approverID int64, approverName string) (*docgov.DocumentRecord, error) + // completedMainHook 仅测试注入:覆盖「main 实例已 completed」查询。 + completedMainHook func(ctx context.Context, projectID int64) (bool, error) } // NewService 构造业务服务;生产/test 环境数据库不可用且未显式 ALLOW_IN_MEMORY 时返回 error。 @@ -287,6 +289,59 @@ func (s *Service) ListTasks() []TaskMeta { return s.squad.ListTasks() } +// resolveDocStage 解析写入文档账本的 Stage 字段。 +// 优先请求透传的冻结实例阶段键;否则按 main 三阶段任务映射;再回落 V0.5_feasibility。 +func resolveDocStage(req TaskRunRequest) string { + if sk := strings.TrimSpace(req.StageKey); sk != "" { + return sk + } + switch strings.TrimSpace(req.TaskKey) { + case "intake": + return string(StageV01_Triage) + case "research", "feasibility_dev", "feasibility_reg", "user_requirements", "prelim_design": + return string(StageV05_Feasibility) + case "cdo_quote": + return string(StageV10_Quoting) + default: + return string(StageV05_Feasibility) + } +} + +const rdevPrerequisiteMessage = "请先完成商务对接流程(CDO 报价批准)后再启动研发设计控制流程" + +func (s *Service) ensureRDevMainCompleted(ctx context.Context, projectID int64) error { + if projectID <= 0 { + return fmt.Errorf("%w: %s", projectworkflow.ErrPrerequisiteNotMet, rdevPrerequisiteMessage) + } + ok, err := s.hasCompletedMainInstance(ctx, projectID) + if err != nil { + return err + } + if !ok { + return fmt.Errorf("%w: %s", projectworkflow.ErrPrerequisiteNotMet, rdevPrerequisiteMessage) + } + return nil +} + +func (s *Service) hasCompletedMainInstance(ctx context.Context, projectID int64) (bool, error) { + if s.completedMainHook != nil { + return s.completedMainHook(ctx, projectID) + } + if s.pool == nil { + return false, nil + } + var ok bool + err := s.pool.QueryRow(ctx, ` + SELECT EXISTS ( + SELECT 1 FROM project_workflow_instances + WHERE project_id = $1 + AND status = 'completed' + AND config_snapshot->>'workflow_definition_key' = 'main' + ) + `, projectID).Scan(&ok) + return ok, err +} + func (s *Service) ListStages() []StageMeta { return SupportedStages } @@ -300,6 +355,16 @@ func (s *Service) ExecuteTask(ctx context.Context, req TaskRunRequest) (*TaskRun }, err } + if strings.HasPrefix(strings.TrimSpace(req.TaskKey), "rd_") { + if err := s.ensureRDevMainCompleted(ctx, req.ProjectID); err != nil { + return &TaskRunResult{ + TaskKey: req.TaskKey, + Success: false, + ErrorReason: err.Error(), + }, err + } + } + materials, inputManifest, err := s.ResolveMaterials(ctx, req) if err != nil { return nil, err @@ -456,6 +521,54 @@ func (s *Service) ExecuteTask(ctx context.Context, req TaskRunRequest) (*TaskRun } } + // 研发设计控制:项目上下文快照 + 同项目已批准文档摘要注入 + if strings.HasPrefix(strings.TrimSpace(req.TaskKey), "rd_") && req.ProjectID > 0 { + priorDocs := make([]squad.PriorDocItem, 0) + facts := map[string]string{} + if req.Fields != nil { + for k, v := range req.Fields { + facts[k] = v + } + } + if s.ctxMgr != nil { + if coll, err := s.getProjectContextForUser(ctx, req.ProjectID, req.UserID); err == nil && coll != nil { + for k, v := range coll.BlackboardFacts { + facts[k] = v + } + } + } + // 优先注入已批准 SR(PM 或 rd_system_reqs) + for _, key := range []string{"system_reqs", "rd_system_reqs"} { + docID := fmt.Sprintf("doc_proj%d_%s", req.ProjectID, key) + if s.docGov == nil { + continue + } + doc, err := s.docGov.GetDocument(ctx, docID) + if err != nil || doc == nil || doc.CurrentState != docgov.StateApproved || doc.WorkDir == "" { + continue + } + if b, readErr := os.ReadFile(filepath.Join(doc.WorkDir, "output.md")); readErr == nil && len(b) > 0 { + priorDocs = append(priorDocs, squad.PriorDocItem{ + DocKey: key, Title: doc.Title, Content: string(b), Stage: "approved", + }) + } + } + dialogueInputs := s.collectSnapshotDialogueInputs(ctx, req.ProjectID, req.UserID) + ctxSvc := projectcontext.NewService(s.pool) + snap, _ := ctxSvc.BuildSnapshot(ctx, projectcontext.BuildInput{ + ProjectID: req.ProjectID, TaskKey: req.TaskKey, UserID: req.UserID, + ProjectName: req.ProjectName, Facts: facts, Materials: materials, PriorDocs: priorDocs, + Knowledge: hits, Dialogue: dialogueInputs.Dialogue, Decisions: dialogueInputs.Decisions, + }) + if snap != nil { + snap.ExcludedPrivateCount += dialogueInputs.ExcludedPrivate + snap.ExcludedNoAccessCount += dialogueInputs.ExcludedNoAccess + if extra := projectcontext.MaterialsForWorkspace(snap, 12); len(extra) > 0 { + materials = append(materials, extra...) + } + } + } + duties := resolveTaskDocumentDuties(ctx, s.pool, req) authorID, authorName := duties.Author.UserID, duties.Author.Name reviewerID, reviewerName := duties.Reviewer.UserID, duties.Reviewer.Name @@ -476,17 +589,18 @@ func (s *Service) ExecuteTask(ctx context.Context, req TaskRunRequest) (*TaskRun var appliedProposalID string if s.docGov != nil && strings.TrimSpace(docID) != "" { + docStage := resolveDocStage(req) var createErr error if s.createDocumentHook != nil { docRec, createErr = s.createDocumentHook( ctx, docID, req.ProjectID, req.TaskKey, artifactTitle, - "V0.5_feasibility", req.WorkDir, + docStage, req.WorkDir, authorID, authorName, reviewerID, reviewerName, approverID, approverName, ) } else { docRec, createErr = s.docGov.CreateOrGetDocument( ctx, docID, req.ProjectID, req.TaskKey, artifactTitle, - "V0.5_feasibility", req.WorkDir, + docStage, req.WorkDir, authorID, authorName, reviewerID, reviewerName, approverID, approverName, ) } diff --git a/internal/bizdev/types.go b/internal/bizdev/types.go index e8f65d14..bb2ea531 100644 --- a/internal/bizdev/types.go +++ b/internal/bizdev/types.go @@ -63,6 +63,7 @@ type TaskRunRequest struct { UserContext string `json:"user_context,omitempty"` MemoryBankFiles map[string]string `json:"memory_bank_files,omitempty"` DocID string `json:"doc_id,omitempty"` + StageKey string `json:"stage_key,omitempty"` // 冻结实例阶段键;空则 resolveDocStage 回落 AuthorID int64 `json:"author_id,omitempty"` AuthorName string `json:"author_name,omitempty"` ReviewerID int64 `json:"reviewer_id,omitempty"` diff --git a/internal/engineeringdashboard/assets/app.js b/internal/engineeringdashboard/assets/app.js index db7f347c..ee50e710 100644 --- a/internal/engineeringdashboard/assets/app.js +++ b/internal/engineeringdashboard/assets/app.js @@ -1,13 +1,4 @@ (function () { - function resolveToken() { - const fromURL = new URL(window.location.href).searchParams.get("token"); - if (fromURL) return fromURL; - const meta = document.querySelector('meta[name="engineering-dashboard-token"]'); - const fromMeta = meta && meta.getAttribute("content"); - if (fromMeta && fromMeta !== "__TOKEN__") return fromMeta; - return ""; - } - const token = resolveToken(); const state = { snap: null, view: "overview", @@ -33,7 +24,7 @@ function syncURL() { const u = new URL(window.location.href); - if (token) u.searchParams.set("token", token); + u.searchParams.delete("token"); u.searchParams.set("view", state.view); if (state.q) u.searchParams.set("q", state.q); else u.searchParams.delete("q"); if (state.selectedDoc) u.searchParams.set("doc", state.selectedDoc); else u.searchParams.delete("doc"); @@ -52,13 +43,8 @@ } async function fetchSnapshot(force) { - if (!token) { - el.status.textContent = "缺少访问令牌"; - renderHardError("缺少 token:请使用服务启动时打印的完整 URL(含 ?token=)打开页面"); - return false; - } el.status.textContent = state.snap ? "刷新中…" : "首次加载中…"; - const url = "/api/v1/snapshot?token=" + encodeURIComponent(token) + (force ? "&refresh=1" : ""); + const url = "/api/v1/snapshot" + (force ? "?refresh=1" : ""); const headers = {}; if (state.snap && state.snap.revision_id) headers["If-None-Match"] = '"' + state.snap.revision_id + '"'; const res = await fetch(url, { headers, credentials: "same-origin" }); @@ -294,10 +280,6 @@ async function loop() { loadURL(); - if (!token) { - renderHardError("缺少 token:请使用服务启动时打印的完整 URL(含 ?token=)打开页面"); - return; - } try { const changed = await fetchSnapshot(true); if (changed || state.snap) render(); diff --git a/internal/engineeringdashboard/assets/index.html b/internal/engineeringdashboard/assets/index.html index 63d53af2..8a735932 100644 --- a/internal/engineeringdashboard/assets/index.html +++ b/internal/engineeringdashboard/assets/index.html @@ -3,9 +3,8 @@ - 个人工程中控台 - + @@ -38,6 +37,6 @@
- + diff --git a/internal/engineeringdashboard/server.go b/internal/engineeringdashboard/server.go index 51dd7475..0f9ce0df 100644 --- a/internal/engineeringdashboard/server.go +++ b/internal/engineeringdashboard/server.go @@ -1,8 +1,6 @@ package engineeringdashboard import ( - "crypto/rand" - "encoding/hex" "encoding/json" "fmt" "net" @@ -13,27 +11,15 @@ import ( ) // Server is the local read-only HTTP surface. +// Access control is loopback bind + Host/Origin checks (personal local tool; no startup token). type Server struct { Collector *Collector - Token string mu sync.Mutex } -// NewServer creates a server with a random startup token. +// NewServer creates a local read-only server. func NewServer(c *Collector) (*Server, error) { - tok, err := randomToken(16) - if err != nil { - return nil, err - } - return &Server{Collector: c, Token: tok}, nil -} - -func randomToken(n int) (string, error) { - b := make([]byte, n) - if _, err := rand.Read(b); err != nil { - return "", err - } - return hex.EncodeToString(b), nil + return &Server{Collector: c}, nil } // Handler returns the HTTP handler. @@ -76,17 +62,6 @@ func (s *Server) secure(next http.Handler) http.Handler { http.Error(w, "cross-site fetch denied", http.StatusForbidden) return } - // token required for API and page (query or header) - if strings.HasPrefix(r.URL.Path, "/api/") || r.URL.Path == "/" || strings.HasPrefix(r.URL.Path, "/assets") { - tok := r.URL.Query().Get("token") - if tok == "" { - tok = r.Header.Get("X-Dashboard-Token") - } - if tok != s.Token { - http.Error(w, "unauthorized", http.StatusUnauthorized) - return - } - } w.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src 'self'; img-src 'none'; frame-src 'none'; object-src 'none'; base-uri 'none'") w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("Referrer-Policy", "no-referrer") @@ -117,7 +92,6 @@ func (s *Server) handleSnapshot(w http.ResponseWriter, r *http.Request) { return } } else { - // soft refresh if TTL expired if time.Since(snap.GeneratedAt) > s.Collector.TTL { go func() { _, _ = s.Collector.Refresh(ctx) }() } @@ -176,9 +150,8 @@ func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), 500) return } - html := strings.ReplaceAll(string(b), "__TOKEN__", s.Token) w.Header().Set("Content-Type", "text/html; charset=utf-8") - _, _ = w.Write([]byte(html)) + _, _ = w.Write(b) case "/assets/app.js": b, err := readAsset("assets/app.js") if err != nil { diff --git a/internal/engineeringdashboard/server_test.go b/internal/engineeringdashboard/server_test.go index 3d828328..811ad8cd 100644 --- a/internal/engineeringdashboard/server_test.go +++ b/internal/engineeringdashboard/server_test.go @@ -22,7 +22,7 @@ func TestServer_RejectsNonGETAndBadHost(t *testing.T) { } h := srv.Handler() - req := httptest.NewRequest(http.MethodPost, "/api/v1/snapshot?token="+srv.Token, nil) + req := httptest.NewRequest(http.MethodPost, "/api/v1/snapshot", nil) req.Host = "127.0.0.1:8787" rr := httptest.NewRecorder() h.ServeHTTP(rr, req) @@ -30,7 +30,7 @@ func TestServer_RejectsNonGETAndBadHost(t *testing.T) { t.Fatalf("POST: %d", rr.Code) } - req = httptest.NewRequest(http.MethodGet, "/api/v1/snapshot?token="+srv.Token, nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/snapshot", nil) req.Host = "evil.example" rr = httptest.NewRecorder() h.ServeHTTP(rr, req) @@ -39,7 +39,7 @@ func TestServer_RejectsNonGETAndBadHost(t *testing.T) { } } -func TestServer_TokenAndSnapshotIdentity(t *testing.T) { +func TestServer_SnapshotWithoutToken(t *testing.T) { c := NewCollector(t.TempDir(), &FakeRunner{Map: map[string]FakeResult{}}) snap := Snapshot{ SchemaVersion: SchemaVersion, GenerationID: "gen_9", RevisionID: "rev_abc", @@ -59,16 +59,8 @@ func TestServer_TokenAndSnapshotIdentity(t *testing.T) { req.Host = "127.0.0.1:8787" rr := httptest.NewRecorder() h.ServeHTTP(rr, req) - if rr.Code != http.StatusUnauthorized { - t.Fatalf("no token: %d", rr.Code) - } - - req = httptest.NewRequest(http.MethodGet, "/api/v1/snapshot?token="+srv.Token, nil) - req.Host = "127.0.0.1:8787" - rr = httptest.NewRecorder() - h.ServeHTTP(rr, req) if rr.Code != 200 { - t.Fatalf("snapshot: %d %s", rr.Code, rr.Body.String()) + t.Fatalf("snapshot without token: %d %s", rr.Code, rr.Body.String()) } body := rr.Body.String() if !strings.Contains(body, `"generation_id": "gen_9"`) || !strings.Contains(body, `"revision_id": "rev_abc"`) { @@ -78,7 +70,7 @@ func TestServer_TokenAndSnapshotIdentity(t *testing.T) { t.Fatalf("etag %q", rr.Header().Get("ETag")) } - req = httptest.NewRequest(http.MethodGet, "/api/v1/snapshot?token="+srv.Token, nil) + req = httptest.NewRequest(http.MethodGet, "/api/v1/snapshot", nil) req.Host = "127.0.0.1:8787" req.Header.Set("If-None-Match", `"rev_abc"`) rr = httptest.NewRecorder() @@ -96,7 +88,7 @@ func TestServer_DocPathTraversalDenied(t *testing.T) { if err != nil { t.Fatal(err) } - req := httptest.NewRequest(http.MethodGet, "/api/v1/doc/not-a-real.md?token="+srv.Token, nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/doc/not-a-real.md", nil) req.Host = "127.0.0.1:8787" req.URL.Path = "/api/v1/doc/../../.env" rr := httptest.NewRecorder() @@ -119,7 +111,7 @@ func TestServer_CrossOriginDenied(t *testing.T) { c.current = &Snapshot{RevisionID: "r", GenerationID: "g", GeneratedAt: time.Now().UTC()} c.mu.Unlock() srv, _ := NewServer(c) - req := httptest.NewRequest(http.MethodGet, "/api/v1/snapshot?token="+srv.Token, nil) + req := httptest.NewRequest(http.MethodGet, "/api/v1/snapshot", nil) req.Host = "127.0.0.1:8787" req.Header.Set("Origin", "https://evil.example") rr := httptest.NewRecorder() @@ -129,7 +121,7 @@ func TestServer_CrossOriginDenied(t *testing.T) { } } -func TestServer_IndexUsesMetaTokenNoInlineScript(t *testing.T) { +func TestServer_IndexHasNoTokenMachinery(t *testing.T) { c := NewCollector(t.TempDir(), &FakeRunner{}) c.mu.Lock() c.current = &Snapshot{RevisionID: "r", GenerationID: "g", GeneratedAt: time.Now().UTC()} @@ -138,7 +130,7 @@ func TestServer_IndexUsesMetaTokenNoInlineScript(t *testing.T) { if err != nil { t.Fatal(err) } - req := httptest.NewRequest(http.MethodGet, "/?token="+srv.Token, nil) + req := httptest.NewRequest(http.MethodGet, "/", nil) req.Host = "127.0.0.1:8787" rr := httptest.NewRecorder() srv.Handler().ServeHTTP(rr, req) @@ -146,25 +138,20 @@ func TestServer_IndexUsesMetaTokenNoInlineScript(t *testing.T) { t.Fatalf("index: %d %s", rr.Code, rr.Body.String()) } body := rr.Body.String() - if strings.Contains(body, "window.__DASHBOARD_TOKEN__") { - t.Fatal("index must not use inline token script (CSP script-src 'self')") + for _, banned := range []string{"__DASHBOARD_TOKEN__", "engineering-dashboard-token", "?token="} { + if strings.Contains(body, banned) { + t.Fatalf("index still contains %q", banned) + } } - meta := `name="engineering-dashboard-token" content="` + srv.Token + `"` - if !strings.Contains(body, meta) { - t.Fatalf("missing token meta: %s", body[:min(400, len(body))]) - } - if !strings.Contains(rr.Header().Get("Content-Security-Policy"), "script-src 'self'") { - t.Fatalf("unexpected CSP: %s", rr.Header().Get("Content-Security-Policy")) - } - // app.js must not reference the removed global js, err := readAsset("assets/app.js") if err != nil { t.Fatal(err) } - if strings.Contains(string(js), "__DASHBOARD_TOKEN__") { - t.Fatal("app.js still references __DASHBOARD_TOKEN__") + jsBody := string(js) + if strings.Contains(jsBody, "token=") || strings.Contains(jsBody, "__DASHBOARD_TOKEN__") { + t.Fatal("app.js still references token auth") } - if !strings.Contains(string(js), "engineering-dashboard-token") { - t.Fatal("app.js should read meta engineering-dashboard-token") + if !strings.Contains(rr.Header().Get("Content-Security-Policy"), "script-src 'self'") { + t.Fatalf("unexpected CSP: %s", rr.Header().Get("Content-Security-Policy")) } } diff --git a/internal/projectinstance/rdev_pg_test.go b/internal/projectinstance/rdev_pg_test.go new file mode 100644 index 00000000..29639701 --- /dev/null +++ b/internal/projectinstance/rdev_pg_test.go @@ -0,0 +1,123 @@ +package projectinstance_test + +import ( + "context" + "testing" + "time" + + "chat-agent-system/internal/projectinstance" + "chat-agent-system/internal/projectworkflow" + "chat-agent-system/internal/workflowdef" +) + +// TestPG_RDevActivateWithNAClose 激活 rdev(60 任务)并将 rd_sw_plan 规划关闭。 +func TestPG_RDevActivateWithNAClose(t *testing.T) { + pool := openPGPool(t) + defer pool.Close() + ctx := context.Background() + + wfStore := workflowdef.NewStore(pool) + def, err := workflowdef.LoadManifestDefinition("rdev.v2.json") + if err != nil { + t.Fatalf("load rdev manifest: %v", err) + } + if err := wfStore.UpsertPublishedSeed(ctx, workflowdef.CreateDefinitionInput{ + Key: workflowdef.OfficialKeyRDev, + Name: "研发设计控制流程", + Definition: def, + }, 1); err != nil { + t.Fatalf("seed rdev: %v", err) + } + + pwStore := projectworkflow.NewStore(pool) + instStore := projectinstance.NewStore(pool) + svc := projectinstance.NewService(instStore, wfStore, pwStore, nil) + + var projectID int64 + code := "pg-rdev-" + time.Now().Format("150405.000") + err = pool.QueryRow(ctx, ` + INSERT INTO projects (code, name, status, creator_user_id, lifecycle_status, active_workflow_key, workflow_selection_version, workflow_plan_revision) + VALUES ($1, 'PG RDev Activation', 'active', 1, 'planning', '', 1, 0) RETURNING id + `, code).Scan(&projectID) + if err != nil { + t.Fatalf("insert project: %v", err) + } + t.Cleanup(func() { + _, _ = pool.Exec(context.Background(), `DELETE FROM projects WHERE id = $1`, projectID) + }) + + plan, err := svc.BuildDefaultPlan(ctx, workflowdef.OfficialKeyRDev, 0) + if err != nil { + t.Fatalf("default plan: %v", err) + } + if len(plan.Stages) != 13 { + t.Fatalf("want 13 stages, got %d", len(plan.Stages)) + } + taskCount := 0 + for i := range plan.Stages { + for j := range plan.Stages[i].DocTasks { + taskCount++ + plan.Stages[i].DocTasks[j].Duties = projectinstance.DocDuty{ + AuthorUserID: 1, ReviewerUserID: 2, ApproverUserID: 3, + } + if plan.Stages[i].DocTasks[j].TaskKey == "rd_sw_plan" { + plan.Stages[i].DocTasks[j].Enabled = false + } + } + } + if taskCount != 60 { + t.Fatalf("want 60 tasks, got %d", taskCount) + } + + if _, err := svc.SaveWorkflowPlan(ctx, projectID, 1, true, projectinstance.SavePlanInput{ + Plan: *plan, ExpectedPlanRevision: 0, ExpectedSelectionVersion: 1, + }); err != nil { + t.Fatalf("save plan: %v", err) + } + preview, err := svc.ActivationPreview(ctx, projectID, 1, true) + if err != nil { + t.Fatalf("preview: %v", err) + } + activated, err := svc.Activate(ctx, projectID, 1, true, projectinstance.ActivateInput{ + ConfirmationToken: preview.ConfirmationToken, + ExpectedProjectVersion: preview.ExpectedProjectVersion, + ExpectedPlanRevision: preview.ExpectedPlanRevision, + }) + if err != nil { + t.Fatalf("activate: %v", err) + } + if activated.Instance.Status != projectinstance.InstanceActive { + t.Fatalf("expected active, got %q", activated.Instance.Status) + } + if activated.Instance.ConfigSnapshot.WorkflowDefinitionKey != workflowdef.OfficialKeyRDev { + t.Fatalf("key=%s", activated.Instance.ConfigSnapshot.WorkflowDefinitionKey) + } + + var executorKey string + var enabled bool + err = pool.QueryRow(ctx, ` + SELECT dti.executor_key, dti.enabled + FROM project_doc_task_instances dti + JOIN project_stage_instances si ON si.id = dti.stage_instance_id + WHERE si.workflow_instance_id = $1 AND dti.task_key = 'rd_proposal' + `, activated.Instance.ID).Scan(&executorKey, &enabled) + if err != nil { + t.Fatalf("query rd_proposal: %v", err) + } + if executorKey != "squad.rdev_doc.v1" || !enabled { + t.Fatalf("rd_proposal executor=%q enabled=%v", executorKey, enabled) + } + + err = pool.QueryRow(ctx, ` + SELECT dti.enabled + FROM project_doc_task_instances dti + JOIN project_stage_instances si ON si.id = dti.stage_instance_id + WHERE si.workflow_instance_id = $1 AND dti.task_key = 'rd_sw_plan' + `, activated.Instance.ID).Scan(&enabled) + if err != nil { + t.Fatalf("query rd_sw_plan: %v", err) + } + if enabled { + t.Fatal("rd_sw_plan should be disabled (N/A planning close)") + } +} diff --git a/internal/projectinstance/validator.go b/internal/projectinstance/validator.go index fb33db0e..ac83437d 100644 --- a/internal/projectinstance/validator.go +++ b/internal/projectinstance/validator.go @@ -24,7 +24,7 @@ func validatePlan(plan WorkflowPlan, version *workflowdef.Version) workflowdef.V if !workflowdef.IsOfficialWorkflowKey(plan.WorkflowDefinitionKey) { issues = append(issues, workflowdef.ValidationIssue{ Code: "WORKFLOW_NOT_SELECTABLE", Field: "workflow_definition_key", - Message: "仅允许选择正式工作流(医疗三或软件自举/交付)", + Message: "仅允许选择正式工作流(医疗四或软件自举/交付)", }) } if plan.WorkflowDefinitionKey != version.WorkflowKey { diff --git a/internal/projectworkflow/catalog.go b/internal/projectworkflow/catalog.go index 246f0a96..36a0b58e 100644 --- a/internal/projectworkflow/catalog.go +++ b/internal/projectworkflow/catalog.go @@ -28,11 +28,11 @@ type memoryCatalog struct { order []string } -// NewDefaultCatalog 创建默认工作流目录(医疗三 + 软件自举/交付) +// NewDefaultCatalog 创建默认工作流目录(医疗四 + 软件自举/交付) func NewDefaultCatalog() Catalog { c := &memoryCatalog{ definitions: make(map[string]*Definition), - order: []string{"main", "product_manager", "test_manager", "software_bootstrap", "software_delivery"}, + order: []string{"main", "product_manager", "test_manager", "rdev", "software_bootstrap", "software_delivery"}, } c.definitions["main"] = &Definition{ @@ -69,6 +69,17 @@ func NewDefaultCatalog() Catalog { Enabled: true, } + c.definitions["rdev"] = &Definition{ + Key: "rdev", + Name: "研发设计控制流程", + Description: "ISO 13485 设计控制:13 阶段 / 60 受控文档(DHF/DMR)", + StageKey: "RD01_planning", + TaskKey: "rd_proposal", + OutputDocType: "rd_proposal", + OutputDocTitle: "项目建议书", + Enabled: true, + } + c.definitions["software_bootstrap"] = &Definition{ Key: "software_bootstrap", Name: "软件项目自举流程", @@ -101,7 +112,7 @@ func WorkflowKeysForProductDomain(domain string) []string { case "non_medical_software": return []string{"software_bootstrap", "software_delivery"} case "medical_device": - return []string{"main", "product_manager", "test_manager"} + return []string{"main", "product_manager", "test_manager", "rdev"} default: return nil } diff --git a/internal/projectworkflow/service_test.go b/internal/projectworkflow/service_test.go index 0241949c..2bea9f7a 100644 --- a/internal/projectworkflow/service_test.go +++ b/internal/projectworkflow/service_test.go @@ -72,7 +72,7 @@ func TestProjectWorkflowService_CreateAndCreatorACL(t *testing.T) { if err != nil { t.Fatalf("list keyed workflows: %v", err) } - if len(keyedWFs) != 5 { + if len(keyedWFs) != 6 { t.Fatalf("catalog must still mount after keyed create, got %d", len(keyedWFs)) } @@ -81,8 +81,8 @@ func TestProjectWorkflowService_CreateAndCreatorACL(t *testing.T) { if err != nil { t.Fatalf("ListProjectWorkflows failed: %v", err) } - if len(wfs) != 5 { - t.Fatalf("expected 5 default workflows, got %d", len(wfs)) + if len(wfs) != 6 { + t.Fatalf("expected 6 default workflows, got %d", len(wfs)) } // 4. 非创建人尝试切换工作流 -> 必须被拒绝 @@ -278,7 +278,7 @@ func TestCreateSoftwareProject_NoMedicalDefaults(t *testing.T) { if !got["software_bootstrap"] || !got["software_delivery"] { t.Fatalf("expected software keys, got %v", got) } - if got["main"] || got["product_manager"] || got["test_manager"] { + if got["main"] || got["product_manager"] || got["test_manager"] || got["rdev"] { t.Fatalf("software project must not mount medical keys: %v", got) } @@ -296,8 +296,8 @@ func TestCreateSoftwareProject_NoMedicalDefaults(t *testing.T) { for _, wf := range mwfs { mgot[wf.WorkflowKey] = true } - if !mgot["main"] || !mgot["product_manager"] || !mgot["test_manager"] { - t.Fatalf("medical three keys missing: %v", mgot) + if !mgot["main"] || !mgot["product_manager"] || !mgot["test_manager"] || !mgot["rdev"] { + t.Fatalf("medical project must mount four medical keys: %v", mgot) } _, err = svc.CreateProject(ctx, creator, CreateProjectInput{ diff --git a/internal/server/handlers.go b/internal/server/handlers.go index 130846a6..bd334210 100644 --- a/internal/server/handlers.go +++ b/internal/server/handlers.go @@ -68,6 +68,7 @@ type TaskRunRequestDTO struct { UserID int64 `json:"user_id,omitempty"` ProjectID int64 `json:"project_id,omitempty"` DocID string `json:"doc_id,omitempty"` + StageKey string `json:"stage_key,omitempty"` // 冻结实例阶段键(服务端 ResolveDocTask 注入) AuthorID int64 `json:"author_id,omitempty"` AuthorName string `json:"author_name,omitempty"` ReviewerID int64 `json:"reviewer_id,omitempty"` diff --git a/internal/server/project_workflow_api_test.go b/internal/server/project_workflow_api_test.go index 5af9cd78..cd20c7aa 100644 --- a/internal/server/project_workflow_api_test.go +++ b/internal/server/project_workflow_api_test.go @@ -83,7 +83,7 @@ func TestProjectWorkflowAPI_Lifecycle(t *testing.T) { var wfList []*projectworkflow.ProjectWorkflow err = json.Unmarshal(recWFs.Body.Bytes(), &wfList) require.NoError(t, err) - assert.Equal(t, 5, len(wfList)) + assert.Equal(t, 6, len(wfList)) // 5. planning 项目禁止旧式切换 -> 409 Conflict bodySwitchBlocked, _ := json.Marshal(projectworkflow.ActivateWorkflowInput{ @@ -119,7 +119,7 @@ func TestProjectWorkflowAPI_Lifecycle(t *testing.T) { require.Equal(t, http.StatusOK, recKeyedWFs.Code) var keyedWFs []*projectworkflow.ProjectWorkflow require.NoError(t, json.Unmarshal(recKeyedWFs.Body.Bytes(), &keyedWFs)) - assert.Equal(t, 5, len(keyedWFs)) + assert.Equal(t, 6, len(keyedWFs)) bodySwitchCreator, _ := json.Marshal(projectworkflow.ActivateWorkflowInput{ WorkflowKey: "product_manager", @@ -141,7 +141,7 @@ func TestProjectWorkflowAPI_Lifecycle(t *testing.T) { var defs []*projectworkflow.Definition err = json.Unmarshal(recDefs.Body.Bytes(), &defs) require.NoError(t, err) - assert.Equal(t, 5, len(defs)) + assert.Equal(t, 6, len(defs)) // 9. 触发工作流运行 -> 200 OK bodyRun, _ := json.Marshal(map[string]any{ diff --git a/internal/server/task_run_handlers.go b/internal/server/task_run_handlers.go index 190fbe54..587da2d4 100644 --- a/internal/server/task_run_handlers.go +++ b/internal/server/task_run_handlers.go @@ -201,6 +201,9 @@ func (s *Server) handleCreateTaskRun(w http.ResponseWriter, r *http.Request) { } taskReq := s.buildBizdevTaskRequest(req.TaskRunRequestDTO, req.SessionID, req.ClientTurnID, claimed.RunID) + if taskScope != nil && strings.TrimSpace(taskScope.StageKey) != "" { + taskReq.StageKey = taskScope.StageKey + } resumeDir := req.WorkDir stageReq := s.buildBizdevStageRequest(req.TaskRunRequestDTO, req.SessionID, req.ClientTurnID, claimed.RunID) createdRun, err := s.taskRuns.StartClaimedRun(r.Context(), claimed, taskReq, stageReq, resumeDir) @@ -592,6 +595,7 @@ func (s *Server) handleResumeTaskRun(w http.ResponseWriter, r *http.Request) { UserID: rec.UserID, ProjectID: rec.ProjectID, TaskKey: rec.TaskKey, + StageKey: rec.WorkflowStageKey, }, rec.SessionID, rec.ClientTurnID, rec.RunID) stageReq := s.buildBizdevStageRequest(TaskRunRequestDTO{ UserID: rec.UserID, @@ -653,6 +657,7 @@ func (s *Server) buildBizdevTaskRequest(dto TaskRunRequestDTO, sessionID, client ClientTurnID: clientTurnID, RunID: runID, DocID: dto.DocID, + StageKey: dto.StageKey, AuthorID: dto.AuthorID, AuthorName: dto.AuthorName, ReviewerID: dto.ReviewerID, diff --git a/internal/server/task_run_request_build_test.go b/internal/server/task_run_request_build_test.go index 1caeb2d0..53042e56 100644 --- a/internal/server/task_run_request_build_test.go +++ b/internal/server/task_run_request_build_test.go @@ -45,3 +45,21 @@ func TestBuildBizdevStageRequest_PreservesIdentityAndRoles(t *testing.T) { t.Fatalf("stage=%q", got.Stage) } } + +func TestBuildBizdevTaskRequest_PassesStageKey(t *testing.T) { + s := &Server{} + dto := TaskRunRequestDTO{ + TaskKey: "rd_proposal", + Backend: "stub", + UserID: 1, + ProjectID: 100, + StageKey: "RD01_planning", + } + got := s.buildBizdevTaskRequest(dto, "sess", "turn", "run") + if got.StageKey != "RD01_planning" { + t.Fatalf("StageKey=%q, want RD01_planning", got.StageKey) + } + if got.TaskKey != "rd_proposal" { + t.Fatalf("TaskKey=%q", got.TaskKey) + } +} diff --git a/internal/squad/rdev_catalog.go b/internal/squad/rdev_catalog.go new file mode 100644 index 00000000..410c3d77 --- /dev/null +++ b/internal/squad/rdev_catalog.go @@ -0,0 +1,367 @@ +package squad + +// RDevArtifactKind 研发文档产物类别(对齐参考项目 artifact_kind)。 +type RDevArtifactKind string + +const ( + RDevKindDocument RDevArtifactKind = "document" + RDevKindReviewMinutes RDevArtifactKind = "review_minutes" + RDevKindUploadIndex RDevArtifactKind = "upload_index" +) + +// RDevTaskMeta rdev 任务目录项。 +type RDevTaskMeta struct { + Key string + Title string + Phase string + PhaseTitle string + Kind RDevArtifactKind + DocCode string + Skippable bool + StageKey string +} + +// RDevCatalog 60 项研发设计控制任务(SSOT 来源:refs rdev.yaml → rd_ 前缀)。 +var RDevCatalog = []RDevTaskMeta{ + { + Key: "rd_proposal", Title: "项目建议书", Phase: "01", + PhaseTitle: "策划", Kind: RDevArtifactKind("document"), + DocCode: "RD-PROPOSAL", Skippable: false, StageKey: "RD01_planning", + }, + { + Key: "rd_design_plan", Title: "设计开发计划任务书", Phase: "01", + PhaseTitle: "策划", Kind: RDevArtifactKind("document"), + DocCode: "RD-DESIGN-PLAN", Skippable: false, StageKey: "RD01_planning", + }, + { + Key: "rd_planning_review", Title: "设计和开发策划评审记录", Phase: "02", + PhaseTitle: "策划评审", Kind: RDevArtifactKind("review_minutes"), + DocCode: "RD-PLAN-REV", Skippable: false, StageKey: "RD02_planning_review", + }, + { + Key: "rd_user_needs", Title: "用户需求规格书", Phase: "03", + PhaseTitle: "设计输入", Kind: RDevArtifactKind("document"), + DocCode: "RD-UN", Skippable: false, StageKey: "RD03_design_input", + }, + { + Key: "rd_regs_stds", Title: "法规和标准清单", Phase: "03", + PhaseTitle: "设计输入", Kind: RDevArtifactKind("document"), + DocCode: "RD-REGS", Skippable: false, StageKey: "RD03_design_input", + }, + { + Key: "rd_adverse_events", Title: "同类产品不良事件信息", Phase: "03", + PhaseTitle: "设计输入", Kind: RDevArtifactKind("document"), + DocCode: "RD-AE", Skippable: false, StageKey: "RD03_design_input", + }, + { + Key: "rd_system_reqs", Title: "系统需求规格书", Phase: "03", + PhaseTitle: "设计输入", Kind: RDevArtifactKind("document"), + DocCode: "RD-SR", Skippable: false, StageKey: "RD03_design_input", + }, + { + Key: "rd_risk_plan", Title: "风险管理计划", Phase: "03", + PhaseTitle: "设计输入", Kind: RDevArtifactKind("document"), + DocCode: "RD-RISK-PLAN", Skippable: false, StageKey: "RD03_design_input", + }, + { + Key: "rd_hazard_analysis", Title: "安全性问题清单及可能危险分析表", Phase: "03", + PhaseTitle: "设计输入", Kind: RDevArtifactKind("document"), + DocCode: "RD-HAZARD", Skippable: false, StageKey: "RD03_design_input", + }, + { + Key: "rd_sw_plan", Title: "软件开发计划", Phase: "03", + PhaseTitle: "设计输入", Kind: RDevArtifactKind("document"), + DocCode: "RD-SW-PLAN", Skippable: true, StageKey: "RD03_design_input", + }, + { + Key: "rd_sw_reqs", Title: "软件需求规格书", Phase: "03", + PhaseTitle: "设计输入", Kind: RDevArtifactKind("document"), + DocCode: "RD-SW-REQ", Skippable: true, StageKey: "RD03_design_input", + }, + { + Key: "rd_sw_config", Title: "软件配置及维护计划", Phase: "03", + PhaseTitle: "设计输入", Kind: RDevArtifactKind("document"), + DocCode: "RD-SW-CFG", Skippable: true, StageKey: "RD03_design_input", + }, + { + Key: "rd_design_input_spec", Title: "设计输入规格书", Phase: "03", + PhaseTitle: "设计输入", Kind: RDevArtifactKind("document"), + DocCode: "RD-DI-SPEC", Skippable: false, StageKey: "RD03_design_input", + }, + { + Key: "rd_design_input_list", Title: "设计输入清单", Phase: "03", + PhaseTitle: "设计输入", Kind: RDevArtifactKind("document"), + DocCode: "RD-DI-LIST", Skippable: false, StageKey: "RD03_design_input", + }, + { + Key: "rd_system_design", Title: "系统设计方案", Phase: "03", + PhaseTitle: "设计输入", Kind: RDevArtifactKind("document"), + DocCode: "RD-SYS-DES", Skippable: false, StageKey: "RD03_design_input", + }, + { + Key: "rd_req_review", Title: "需求评审记录", Phase: "04", + PhaseTitle: "需求评审", Kind: RDevArtifactKind("review_minutes"), + DocCode: "RD-REQ-REV", Skippable: false, StageKey: "RD04_requirements_review", + }, + { + Key: "rd_input_phase_review", Title: "设计开发评审记录(阶段3和4)", Phase: "05", + PhaseTitle: "输入阶段评审", Kind: RDevArtifactKind("review_minutes"), + DocCode: "RD-IN-REV", Skippable: false, StageKey: "RD05_input_phase_review", + }, + { + Key: "rd_drawings_3d", Title: "2D图纸和3D模型", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("upload_index"), + DocCode: "RD-DWG", Skippable: false, StageKey: "RD06_design_output", + }, + { + Key: "rd_product_bom", Title: "产品BOM", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("upload_index"), + DocCode: "RD-BOM", Skippable: false, StageKey: "RD06_design_output", + }, + { + Key: "rd_ee_bom", Title: "电子电气BOM", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("upload_index"), + DocCode: "RD-EE-BOM", Skippable: true, StageKey: "RD06_design_output", + }, + { + Key: "rd_pcba", Title: "PCBA原理图及Layout", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("upload_index"), + DocCode: "RD-PCBA", Skippable: true, StageKey: "RD06_design_output", + }, + { + Key: "rd_electrical_sys", Title: "系统电气设计图", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("upload_index"), + DocCode: "RD-ELEC", Skippable: true, StageKey: "RD06_design_output", + }, + { + Key: "rd_sw_arch", Title: "软件概要设计方案", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("document"), + DocCode: "RD-SW-ARCH", Skippable: true, StageKey: "RD06_design_output", + }, + { + Key: "rd_sw_ui", Title: "软件UI设计文档", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("document"), + DocCode: "RD-SW-UI", Skippable: true, StageKey: "RD06_design_output", + }, + { + Key: "rd_purchase_spec", Title: "采购技术规范", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("document"), + DocCode: "RD-PUR", Skippable: false, StageKey: "RD06_design_output", + }, + { + Key: "rd_process_flow", Title: "工艺流程图及作业指导", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("document"), + DocCode: "RD-PROC", Skippable: false, StageKey: "RD06_design_output", + }, + { + Key: "rd_packaging", Title: "包装图纸和设计要求", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("document"), + DocCode: "RD-PACK", Skippable: false, StageKey: "RD06_design_output", + }, + { + Key: "rd_ifu", Title: "产品使用说明书", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("document"), + DocCode: "RD-IFU", Skippable: false, StageKey: "RD06_design_output", + }, + { + Key: "rd_incoming_iqc", Title: "原材料进货检验规范", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("document"), + DocCode: "RD-IQC", Skippable: false, StageKey: "RD06_design_output", + }, + { + Key: "rd_ipqc", Title: "生产过程检验规范", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("document"), + DocCode: "RD-IPQC", Skippable: false, StageKey: "RD06_design_output", + }, + { + Key: "rd_fqc", Title: "成品检验规范", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("document"), + DocCode: "RD-FQC", Skippable: false, StageKey: "RD06_design_output", + }, + { + Key: "rd_tech_req", Title: "产品技术要求", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("document"), + DocCode: "RD-TECH", Skippable: false, StageKey: "RD06_design_output", + }, + { + Key: "rd_risk_report", Title: "风险管理报告", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("document"), + DocCode: "RD-RISK-RPT", Skippable: false, StageKey: "RD06_design_output", + }, + { + Key: "rd_design_output_list", Title: "设计输出清单", Phase: "06", + PhaseTitle: "设计输出", Kind: RDevArtifactKind("document"), + DocCode: "RD-DO-LIST", Skippable: false, StageKey: "RD06_design_output", + }, + { + Key: "rd_output_review", Title: "设计开发评审记录(阶段6)", Phase: "07", + PhaseTitle: "输出评审", Kind: RDevArtifactKind("review_minutes"), + DocCode: "RD-OUT-REV", Skippable: false, StageKey: "RD07_output_review", + }, + { + Key: "rd_prototype_photos", Title: "样机照片", Phase: "08", + PhaseTitle: "验证", Kind: RDevArtifactKind("upload_index"), + DocCode: "RD-PHOTO", Skippable: false, StageKey: "RD08_verification", + }, + { + Key: "rd_design_vv", Title: "设计验证方案", Phase: "08", + PhaseTitle: "验证", Kind: RDevArtifactKind("document"), + DocCode: "RD-VV", Skippable: false, StageKey: "RD08_verification", + }, + { + Key: "rd_shelf_life", Title: "货架寿命方案", Phase: "08", + PhaseTitle: "验证", Kind: RDevArtifactKind("document"), + DocCode: "RD-SHELF", Skippable: false, StageKey: "RD08_verification", + }, + { + Key: "rd_lifetime", Title: "产品使用期限研究方案", Phase: "08", + PhaseTitle: "验证", Kind: RDevArtifactKind("document"), + DocCode: "RD-LIFE", Skippable: false, StageKey: "RD08_verification", + }, + { + Key: "rd_biocompatibility", Title: "生物学检测方案", Phase: "08", + PhaseTitle: "验证", Kind: RDevArtifactKind("document"), + DocCode: "RD-BIO", Skippable: false, StageKey: "RD08_verification", + }, + { + Key: "rd_cleaning", Title: "清洁消毒验证方案", Phase: "08", + PhaseTitle: "验证", Kind: RDevArtifactKind("document"), + DocCode: "RD-CLEAN", Skippable: false, StageKey: "RD08_verification", + }, + { + Key: "rd_emc_safety", Title: "EMC及安规摸底报告", Phase: "08", + PhaseTitle: "验证", Kind: RDevArtifactKind("upload_index"), + DocCode: "RD-EMC", Skippable: true, StageKey: "RD08_verification", + }, + { + Key: "rd_risk_report_v", Title: "风险管理报告(验证阶段)", Phase: "08", + PhaseTitle: "验证", Kind: RDevArtifactKind("document"), + DocCode: "RD-RISK-V", Skippable: false, StageKey: "RD08_verification", + }, + { + Key: "rd_sw_release", Title: "软件发布清单", Phase: "08", + PhaseTitle: "验证", Kind: RDevArtifactKind("document"), + DocCode: "RD-SW-REL", Skippable: true, StageKey: "RD08_verification", + }, + { + Key: "rd_sw_unit_test", Title: "软件单元测试报告", Phase: "08", + PhaseTitle: "验证", Kind: RDevArtifactKind("document"), + DocCode: "RD-SW-UT", Skippable: true, StageKey: "RD08_verification", + }, + { + Key: "rd_sw_sys_test", Title: "软件系统测试报告", Phase: "08", + PhaseTitle: "验证", Kind: RDevArtifactKind("document"), + DocCode: "RD-SW-ST", Skippable: true, StageKey: "RD08_verification", + }, + { + Key: "rd_qualified_suppliers", Title: "合格供方名录", Phase: "08", + PhaseTitle: "验证", Kind: RDevArtifactKind("document"), + DocCode: "RD-SUP", Skippable: false, StageKey: "RD08_verification", + }, + { + Key: "rd_verification_review", Title: "设计开发评审记录(验证阶段)", Phase: "09", + PhaseTitle: "验证评审", Kind: RDevArtifactKind("review_minutes"), + DocCode: "RD-VER-REV", Skippable: false, StageKey: "RD09_verification_review", + }, + { + Key: "rd_dmr_list", Title: "DMR文件清单", Phase: "10", + PhaseTitle: "转换", Kind: RDevArtifactKind("document"), + DocCode: "RD-DMR-LIST", Skippable: false, StageKey: "RD10_transfer", + }, + { + Key: "rd_pack_ship", Title: "包装运输方案", Phase: "10", + PhaseTitle: "转换", Kind: RDevArtifactKind("document"), + DocCode: "RD-SHIP", Skippable: false, StageKey: "RD10_transfer", + }, + { + Key: "rd_risk_report_t", Title: "风险管理报告(转换阶段)", Phase: "10", + PhaseTitle: "转换", Kind: RDevArtifactKind("document"), + DocCode: "RD-RISK-T", Skippable: false, StageKey: "RD10_transfer", + }, + { + Key: "rd_transfer_review", Title: "设计开发评审记录(转换阶段)", Phase: "11", + PhaseTitle: "转换评审", Kind: RDevArtifactKind("review_minutes"), + DocCode: "RD-XFER-REV", Skippable: false, StageKey: "RD11_transfer_review", + }, + { + Key: "rd_validation_plan", Title: "设计确认方案", Phase: "12", + PhaseTitle: "确认", Kind: RDevArtifactKind("document"), + DocCode: "RD-VAL-PLAN", Skippable: false, StageKey: "RD12_validation", + }, + { + Key: "rd_use_error", Title: "使用错误评估报告", Phase: "12", + PhaseTitle: "确认", Kind: RDevArtifactKind("document"), + DocCode: "RD-USE-ERR", Skippable: false, StageKey: "RD12_validation", + }, + { + Key: "rd_animal_study", Title: "动物试验方案", Phase: "12", + PhaseTitle: "确认", Kind: RDevArtifactKind("document"), + DocCode: "RD-ANIMAL", Skippable: true, StageKey: "RD12_validation", + }, + { + Key: "rd_sw_quality", Title: "软件质量测试报告", Phase: "12", + PhaseTitle: "确认", Kind: RDevArtifactKind("document"), + DocCode: "RD-SW-QA", Skippable: true, StageKey: "RD12_validation", + }, + { + Key: "rd_clinical", Title: "临床试验报告", Phase: "12", + PhaseTitle: "确认", Kind: RDevArtifactKind("document"), + DocCode: "RD-CLIN", Skippable: true, StageKey: "RD12_validation", + }, + { + Key: "rd_risk_report_val", Title: "风险管理报告(确认阶段)", Phase: "12", + PhaseTitle: "确认", Kind: RDevArtifactKind("document"), + DocCode: "RD-RISK-VAL", Skippable: false, StageKey: "RD12_validation", + }, + { + Key: "rd_user_test", Title: "用户测试报告", Phase: "12", + PhaseTitle: "确认", Kind: RDevArtifactKind("document"), + DocCode: "RD-USER-T", Skippable: false, StageKey: "RD12_validation", + }, + { + Key: "rd_design_validation_report", Title: "设计确认报告", Phase: "13", + PhaseTitle: "设计确认报告", Kind: RDevArtifactKind("document"), + DocCode: "RD-VAL-RPT", Skippable: false, StageKey: "RD13_validation_report", + }, +} + +var rdevCatalogByKey map[string]RDevTaskMeta + +func init() { + rdevCatalogByKey = make(map[string]RDevTaskMeta, len(RDevCatalog)) + for _, t := range RDevCatalog { + rdevCatalogByKey[t.Key] = t + } +} + +// LookupRDevTask 按任务键查找 rdev 目录项。 +func LookupRDevTask(taskKey string) (RDevTaskMeta, bool) { + t, ok := rdevCatalogByKey[taskKey] + return t, ok +} + +// RDevNeedsRegulatory 是否注入法规专员步骤。 +func RDevNeedsRegulatory(taskKey string) bool { + switch taskKey { + case "rd_sw_plan", "rd_sw_reqs", "rd_sw_config", "rd_sw_arch", "rd_sw_ui", + "rd_sw_release", "rd_sw_unit_test", "rd_sw_sys_test", "rd_sw_quality", + "rd_regs_stds", "rd_adverse_events", "rd_biocompatibility", "rd_clinical", "rd_animal_study", + "rd_risk_plan", "rd_hazard_analysis", "rd_risk_report", "rd_risk_report_v", + "rd_risk_report_t", "rd_risk_report_val", "rd_emc_safety": + return true + default: + return false + } +} + +// rdevSupportedTasks 将目录转为 SupportedTasks 片段。 +func rdevSupportedTasks() []TaskMeta { + out := make([]TaskMeta, 0, len(RDevCatalog)) + for _, t := range RDevCatalog { + out = append(out, TaskMeta{ + Key: t.Key, Title: t.Title, ArtifactTitle: t.Title, + Description: "研发设计控制文档:" + t.Title, + }) + } + return out +} diff --git a/internal/squad/rdev_catalog_test.go b/internal/squad/rdev_catalog_test.go new file mode 100644 index 00000000..32745c7b --- /dev/null +++ b/internal/squad/rdev_catalog_test.go @@ -0,0 +1,53 @@ +package squad + +import "testing" + +func TestRDevCatalogSizeAndKinds(t *testing.T) { + if len(RDevCatalog) != 60 { + t.Fatalf("want 60 catalog entries, got %d", len(RDevCatalog)) + } + var doc, rev, up int + for _, e := range RDevCatalog { + switch e.Kind { + case RDevKindDocument: + doc++ + case RDevKindReviewMinutes: + rev++ + case RDevKindUploadIndex: + up++ + default: + t.Fatalf("unknown kind %q for %s", e.Kind, e.Key) + } + } + if doc != 47 || rev != 6 || up != 7 { + t.Fatalf("kind counts document=%d review=%d upload=%d", doc, rev, up) + } + if len(SupportedTasks) < 69 { + t.Fatalf("SupportedTasks should include base+rdev, got %d", len(SupportedTasks)) + } +} + +func TestRDevWorkflowStepsByKind(t *testing.T) { + docSteps := DefaultWorkflowSteps("rd_proposal") + if len(docSteps) < 4 { + t.Fatalf("document steps want >=4, got %d", len(docSteps)) + } + revSteps := DefaultWorkflowSteps("rd_planning_review") + if len(revSteps) != 3 { + t.Fatalf("review_minutes want 3 steps, got %d", len(revSteps)) + } + upSteps := DefaultWorkflowSteps("rd_prototype_photos") + if len(upSteps) != 4 { + t.Fatalf("upload_index want 4 steps, got %d", len(upSteps)) + } + agents := DefaultSquadAgents("rd_proposal") + foundLeader := false + for _, a := range agents { + if a.Role == "leader" && a.Name == "研发设计控制负责人" { + foundLeader = true + } + } + if !foundLeader { + t.Fatal("leader display name missing") + } +} diff --git a/internal/squad/rdev_steps.go b/internal/squad/rdev_steps.go new file mode 100644 index 00000000..f4be2989 --- /dev/null +++ b/internal/squad/rdev_steps.go @@ -0,0 +1,214 @@ +package squad + +const rdevDHFAccept = "模板写满;结论先行;追溯表必填;缺项标「待确认」;不编造数据/签字/附件" + +func rdevSquadAgents(taskKey string) []AgentSpec { + meta, _ := LookupRDevTask(taskKey) + agents := []AgentSpec{ + { + Role: "organizer", + Name: "材料整理专员", + Model: "claude-3-7-sonnet", + Tools: []string{"read", "write", "edit"}, + BashPermission: "deny", + Responsibilities: []string{"材料事实抽取", "适用性与缺口识别", "字段回填"}, + ForbiddenActions: []string{"严禁编造试验数据", "严禁伪造签字"}, + }, + } + switch meta.Kind { + case RDevKindReviewMinutes: + agents = append(agents, + AgentSpec{ + Role: "writer", + Name: "文档撰写专员", + Model: "claude-3-7-sonnet", + Tools: []string{"read", "write", "edit"}, + BashPermission: "deny", + Responsibilities: []string{"按评审记录模板成文", "问题-决议-行动项闭环"}, + }, + rdevLeaderAgent(), + ) + case RDevKindUploadIndex: + agents = append(agents, + AgentSpec{ + Role: "engineer", + Name: "技术专员", + Model: "claude-3-7-sonnet", + Tools: []string{"read", "write", "edit"}, + BashPermission: "deny", + Responsibilities: []string{"附件索引完整性", "缺失影响分析"}, + }, + AgentSpec{ + Role: "writer", + Name: "文档撰写专员", + Model: "claude-3-7-sonnet", + Tools: []string{"read", "write", "edit"}, + BashPermission: "deny", + Responsibilities: []string{"按附件清单模板成文", "标明待上传项"}, + }, + rdevLeaderAgent(), + ) + default: + agents = append(agents, + AgentSpec{ + Role: "engineer", + Name: "技术专员", + Model: "claude-3-7-sonnet", + Tools: []string{"read", "write", "edit"}, + BashPermission: "deny", + Responsibilities: []string{"技术内容与追溯", "设计控制论证"}, + }, + ) + if RDevNeedsRegulatory(taskKey) { + agents = append(agents, AgentSpec{ + Role: "regulatory", + Name: "法规专员", + Model: "claude-3-7-sonnet", + Tools: []string{"read", "write", "edit"}, + BashPermission: "deny", + Responsibilities: []string{"法规/标准适用性", "软件与风险管理合规要点"}, + }) + } + agents = append(agents, + AgentSpec{ + Role: "writer", + Name: "文档撰写专员", + Model: "claude-3-7-sonnet", + Tools: []string{"read", "write", "edit"}, + BashPermission: "deny", + Responsibilities: []string{"按 RD 模板成文", "结论先行与待确认清单"}, + }, + rdevLeaderAgent(), + ) + } + return agents +} + +func rdevLeaderAgent() AgentSpec { + return AgentSpec{ + Role: "leader", + Name: "研发设计控制负责人", + Model: "claude-3-7-sonnet", + Tools: []string{"read", "write", "edit"}, + BashPermission: "deny", + Responsibilities: []string{"DHF 完整性终审", "不适用理由审核", "禁止编造检查"}, + ForbiddenActions: []string{"严禁代签", "严禁伪造附件内容"}, + } +} + +func rdevWorkflowSteps(taskKey string) []StepSpec { + meta, ok := LookupRDevTask(taskKey) + kind := RDevKindDocument + title := taskKey + if ok { + kind = meta.Kind + title = meta.Title + } + + switch kind { + case RDevKindReviewMinutes: + return []StepSpec{ + { + ID: "step_01_organize", Role: "organizer", Title: "整理评审输入与议题", + SubWorkspace: "tasks/01_organize", Mode: "execute", + Accept: []string{"汇总上游批准文档要点", "列出待评审议题与缺口", rdevDHFAccept}, + }, + { + ID: "step_02_draft", Role: "writer", Title: "撰写评审记录", + SubWorkspace: "tasks/02_draft", Mode: "execute", + Needs: []string{"tasks/01_organize/output.md"}, + Accept: []string{"按模板写满评审记录", "问题-决议-行动项可追溯", rdevDHFAccept}, + }, + { + ID: "step_03_review", Role: "leader", Title: "研发设计控制终审", + SubWorkspace: "tasks/03_review", Mode: "execute", + Needs: []string{"tasks/02_draft/output.md"}, + Accept: []string{"完整性与粒度达标", "无编造签字/数据", rdevDHFAccept}, + }, + } + case RDevKindUploadIndex: + return []StepSpec{ + { + ID: "step_01_organize", Role: "organizer", Title: "整理附件材料线索", + SubWorkspace: "tasks/01_organize", Mode: "execute", + Accept: []string{"列出已知附件与缺失项", rdevDHFAccept}, + }, + { + ID: "step_02_engineer", Role: "engineer", Title: "完善附件索引与影响分析", + SubWorkspace: "tasks/02_engineer", Mode: "execute", + Needs: []string{"tasks/01_organize/output.md"}, + Accept: []string{"索引表完整", "缺失影响已说明", rdevDHFAccept}, + }, + { + ID: "step_03_draft", Role: "writer", Title: "撰写附件清单", + SubWorkspace: "tasks/03_draft", Mode: "execute", + Needs: []string{"tasks/02_engineer/output.md"}, + Accept: []string{"按 upload_index 模板成文", "禁止伪造附件正文", rdevDHFAccept}, + }, + { + ID: "step_04_review", Role: "leader", Title: "研发设计控制终审", + SubWorkspace: "tasks/04_review", Mode: "execute", + Needs: []string{"tasks/03_draft/output.md"}, + Accept: []string{"索引与待上传结论一致", rdevDHFAccept}, + }, + } + default: + return rdevDocumentSteps(taskKey, title) + } +} + +func rdevDocumentSteps(taskKey, title string) []StepSpec { + steps := []StepSpec{ + { + ID: "step_01_organize", Role: "organizer", Title: "抽取材料事实并回填", + SubWorkspace: "tasks/01_organize", Mode: "execute", + Accept: []string{"facts/gaps/implications 入黑板", "适用性字段回填", rdevDHFAccept}, + }, + { + ID: "step_02_engineer", Role: "engineer", Title: "技术内容与追溯编制", + SubWorkspace: "tasks/02_engineer", Mode: "execute", + Needs: []string{"tasks/01_organize/output.md"}, + Accept: []string{"技术论证完整", "追溯表可填则填", rdevDHFAccept}, + }, + } + draftNeeds := "tasks/02_engineer/output.md" + if RDevNeedsRegulatory(taskKey) { + steps = append(steps, StepSpec{ + ID: "step_03_regulatory", Role: "regulatory", Title: "法规与风险合规要点", + SubWorkspace: "tasks/03_regulatory", Mode: "execute", + Needs: []string{"tasks/02_engineer/output.md"}, + Accept: []string{"法规适用性明确", "风险类引用可追溯", rdevDHFAccept}, + }) + draftNeeds = "tasks/03_regulatory/output.md" + steps = append(steps, + StepSpec{ + ID: "step_04_draft", Role: "writer", Title: "撰写《" + title + "》", + SubWorkspace: "tasks/04_draft", Mode: "execute", + Needs: []string{draftNeeds}, + Accept: []string{"严格按 rd_ 模板成文", "结论先行", rdevDHFAccept}, + }, + StepSpec{ + ID: "step_05_review", Role: "leader", Title: "研发设计控制终审", + SubWorkspace: "tasks/05_review", Mode: "execute", + Needs: []string{"tasks/04_draft/output.md"}, + Accept: []string{"DHF 粒度达标", "无编造", rdevDHFAccept}, + }, + ) + return steps + } + steps = append(steps, + StepSpec{ + ID: "step_03_draft", Role: "writer", Title: "撰写《" + title + "》", + SubWorkspace: "tasks/03_draft", Mode: "execute", + Needs: []string{draftNeeds}, + Accept: []string{"严格按 rd_ 模板成文", "结论先行", rdevDHFAccept}, + }, + StepSpec{ + ID: "step_04_review", Role: "leader", Title: "研发设计控制终审", + SubWorkspace: "tasks/04_review", Mode: "execute", + Needs: []string{"tasks/03_draft/output.md"}, + Accept: []string{"DHF 粒度达标", "无编造", rdevDHFAccept}, + }, + ) + return steps +} diff --git a/internal/squad/service.go b/internal/squad/service.go index 1a250c05..a90f42a6 100644 --- a/internal/squad/service.go +++ b/internal/squad/service.go @@ -125,7 +125,7 @@ func NewService(cfg *config.Config) *Service { } } -var SupportedTasks = []TaskMeta{ +var baseSupportedTasks = []TaskMeta{ {Key: "intake", Title: "用户初始需求收集", ArtifactTitle: "用户初始需求收集表", Description: "梳理客户原始需求、提炼事实清单与缺口"}, {Key: "research", Title: "调研", ArtifactTitle: "调研报告", Description: "市场竞品同维度对比、法规要点与技术方案初筛"}, {Key: "feasibility_dev", Title: "设计开发可行性评估", ArtifactTitle: "设计开发可行性评估报告", Description: "工程化差距、技术路线与系统架构可行性推导"}, @@ -137,6 +137,13 @@ var SupportedTasks = []TaskMeta{ {Key: "cdo_quote", Title: "CDO报价单", ArtifactTitle: "CDO报价单(含定价核算 Excel)", Description: "基于公司定价模型的工时拆解、成本核算与法定说明"}, } +// SupportedTasks 标准小队任务白名单(商务九键 + rdev 六十键)。 +var SupportedTasks []TaskMeta + +func init() { + SupportedTasks = append(append([]TaskMeta{}, baseSupportedTasks...), rdevSupportedTasks()...) +} + func (s *Service) ListTasks() []TaskMeta { return SupportedTasks } diff --git a/internal/squad/workspace.go b/internal/squad/workspace.go index fa3a9a60..b2d951dc 100644 --- a/internal/squad/workspace.go +++ b/internal/squad/workspace.go @@ -589,6 +589,9 @@ func (e *WorkspaceEngine) buildSubWorkspaces(workDir string, wf WorkflowPlan, sq // DefaultSquadAgents 根据任务类型返回默认小队成员编制 func DefaultSquadAgents(taskKey string) []AgentSpec { + if strings.HasPrefix(taskKey, "rd_") { + return rdevSquadAgents(taskKey) + } switch taskKey { case "intake": return []AgentSpec{ @@ -796,6 +799,9 @@ func DefaultSquadAgents(taskKey string) []AgentSpec { // DefaultWorkflowSteps 根据任务类型返回最佳实践编排步骤 func DefaultWorkflowSteps(taskKey string) []StepSpec { + if strings.HasPrefix(taskKey, "rd_") { + return rdevWorkflowSteps(taskKey) + } switch taskKey { case "intake": return []StepSpec{ diff --git a/internal/workflow/registry.go b/internal/workflow/registry.go index 3ddba2ed..d06c4aa4 100644 --- a/internal/workflow/registry.go +++ b/internal/workflow/registry.go @@ -7,6 +7,7 @@ var ExecutorRegistry = map[string]struct{}{ "bizdev.stage.v1": {}, "squad.system_reqs.v1": {}, "squad.test_manager.v1": {}, + "squad.rdev_doc.v1": {}, "bizdev.intake.v1": {}, "bizdev.research.v1": {}, "bizdev.feasibility.v1": {}, diff --git a/internal/workflowdef/bootstrap.go b/internal/workflowdef/bootstrap.go index e283131c..1d0ab329 100644 --- a/internal/workflowdef/bootstrap.go +++ b/internal/workflowdef/bootstrap.go @@ -31,6 +31,11 @@ var officialSeedMeta = map[string]struct { Description: "基于项目上下文独立生成系统验证方案及计划", Domain: "medical_device", }, + OfficialKeyRDev: { + Name: "研发设计控制流程", + Description: "ISO 13485 设计控制:13 阶段 / 60 受控文档(DHF/DMR)", + Domain: "medical_device", + }, OfficialKeySoftwareBootstrap: { Name: "软件项目自举流程", Description: "S0–S4:章程、现状、需求、架构、风险、路线与就绪评审(无医疗语义)", diff --git a/internal/workflowdef/catalog_policy.go b/internal/workflowdef/catalog_policy.go index d6a41c31..838e1fea 100644 --- a/internal/workflowdef/catalog_policy.go +++ b/internal/workflowdef/catalog_policy.go @@ -1,11 +1,12 @@ package workflowdef // 正式项目可选工作流(唯一对外目录)。 -// 包含医疗三工作流 + 纯软件 bootstrap/delivery;不得把医疗语义硬塞进软件 key。 +// 包含医疗四工作流 + 纯软件 bootstrap/delivery;不得把医疗语义硬塞进软件 key。 const ( OfficialKeyMain = "main" OfficialKeyProductManager = "product_manager" OfficialKeyTestManager = "test_manager" + OfficialKeyRDev = "rdev" OfficialKeySoftwareBootstrap = "software_bootstrap" OfficialKeySoftwareDelivery = "software_delivery" ) @@ -14,6 +15,7 @@ var officialWorkflowKeys = map[string]struct{}{ OfficialKeyMain: {}, OfficialKeyProductManager: {}, OfficialKeyTestManager: {}, + OfficialKeyRDev: {}, OfficialKeySoftwareBootstrap: {}, OfficialKeySoftwareDelivery: {}, } @@ -22,12 +24,14 @@ var medicalWorkflowKeys = map[string]struct{}{ OfficialKeyMain: {}, OfficialKeyProductManager: {}, OfficialKeyTestManager: {}, + OfficialKeyRDev: {}, } var officialWorkflowNames = map[string]string{ OfficialKeyMain: "商务对接流程", OfficialKeyProductManager: "智能产品经理流程", OfficialKeyTestManager: "智能测试工程师流程", + OfficialKeyRDev: "研发设计控制流程", OfficialKeySoftwareBootstrap: "软件项目自举流程", OfficialKeySoftwareDelivery: "软件交付迭代流程", } @@ -36,25 +40,27 @@ var officialCatalogOrder = map[string]int{ OfficialKeyMain: 10, OfficialKeyProductManager: 20, OfficialKeyTestManager: 30, - OfficialKeySoftwareBootstrap: 40, - OfficialKeySoftwareDelivery: 50, + OfficialKeyRDev: 40, + OfficialKeySoftwareBootstrap: 50, + OfficialKeySoftwareDelivery: 60, } var officialProductDomains = map[string][]string{ OfficialKeyMain: {"medical_device", "non_medical_software"}, OfficialKeyProductManager: {"medical_device", "non_medical_software"}, OfficialKeyTestManager: {"medical_device", "non_medical_software"}, + OfficialKeyRDev: {"medical_device"}, OfficialKeySoftwareBootstrap: {"non_medical_software"}, OfficialKeySoftwareDelivery: {"non_medical_software"}, } -// IsOfficialWorkflowKey 是否为正式可选工作流 key(医疗三 + 软件两)。 +// IsOfficialWorkflowKey 是否为正式可选工作流 key(医疗四 + 软件两)。 func IsOfficialWorkflowKey(key string) bool { _, ok := officialWorkflowKeys[key] return ok } -// IsMedicalWorkflowKey 是否为医疗既有三工作流(固定文档数等门禁仍仅约束它们)。 +// IsMedicalWorkflowKey 是否为医疗正式工作流(固定文档数等门禁约束)。 func IsMedicalWorkflowKey(key string) bool { _, ok := medicalWorkflowKeys[key] return ok @@ -87,14 +93,15 @@ func OfficialWorkflowKeys() []string { OfficialKeyMain, OfficialKeyProductManager, OfficialKeyTestManager, + OfficialKeyRDev, OfficialKeySoftwareBootstrap, OfficialKeySoftwareDelivery, } } -// MedicalWorkflowKeys 返回医疗三 key(稳定顺序)。 +// MedicalWorkflowKeys 返回医疗四 key(稳定顺序)。 func MedicalWorkflowKeys() []string { - return []string{OfficialKeyMain, OfficialKeyProductManager, OfficialKeyTestManager} + return []string{OfficialKeyMain, OfficialKeyProductManager, OfficialKeyTestManager, OfficialKeyRDev} } // SoftwareWorkflowKeys 返回软件两 key(稳定顺序)。 diff --git a/internal/workflowdef/catalog_policy_test.go b/internal/workflowdef/catalog_policy_test.go index 9919a775..7fc20641 100644 --- a/internal/workflowdef/catalog_policy_test.go +++ b/internal/workflowdef/catalog_policy_test.go @@ -7,20 +7,32 @@ import ( func TestOfficialWorkflowKeys(t *testing.T) { keys := OfficialWorkflowKeys() - if len(keys) != 5 { - t.Fatalf("expected 5 official keys, got %d", len(keys)) + if len(keys) != 6 { + t.Fatalf("expected 6 official keys, got %d", len(keys)) } for _, k := range keys { if !IsOfficialWorkflowKey(k) { t.Fatalf("key %q not official", k) } } - if len(MedicalWorkflowKeys()) != 3 { - t.Fatalf("expected 3 medical keys") + if len(MedicalWorkflowKeys()) != 4 { + t.Fatalf("expected 4 medical keys") } if len(SoftwareWorkflowKeys()) != 2 { t.Fatalf("expected 2 software keys") } + if OfficialCatalogOrder(OfficialKeyRDev) != 40 { + t.Fatalf("rdev catalog_order want 40") + } + if OfficialCatalogOrder(OfficialKeySoftwareBootstrap) != 50 { + t.Fatalf("software_bootstrap catalog_order want 50") + } + if !CompatibleWithProductDomain(OfficialKeyRDev, "medical_device") { + t.Fatal("rdev should accept medical_device") + } + if CompatibleWithProductDomain(OfficialKeyRDev, "non_medical_software") { + t.Fatal("rdev must reject non_medical_software") + } } func TestCompatibleWithProductDomain(t *testing.T) { diff --git a/internal/workflowdef/catalog_store.go b/internal/workflowdef/catalog_store.go index 9d8f0b78..35054da7 100644 --- a/internal/workflowdef/catalog_store.go +++ b/internal/workflowdef/catalog_store.go @@ -59,8 +59,9 @@ func (s *Store) RetireNonOfficialDefinitions(ctx context.Context) error { WHEN 'main' THEN 10 WHEN 'product_manager' THEN 20 WHEN 'test_manager' THEN 30 - WHEN 'software_bootstrap' THEN 40 - WHEN 'software_delivery' THEN 50 + WHEN 'rdev' THEN 40 + WHEN 'software_bootstrap' THEN 50 + WHEN 'software_delivery' THEN 60 ELSE catalog_order END, updated_at = now() diff --git a/internal/workflowdef/manifest_loader.go b/internal/workflowdef/manifest_loader.go index 420ba14f..d30f4974 100644 --- a/internal/workflowdef/manifest_loader.go +++ b/internal/workflowdef/manifest_loader.go @@ -25,11 +25,12 @@ func LoadManifestDefinition(filename string) (VersionDefinition, error) { return def, nil } -// V2ManifestFiles 正式工作流 v2 manifest 文件名(医疗三 + 软件两)。 +// V2ManifestFiles 正式工作流 v2 manifest 文件名(医疗四 + 软件两)。 var V2ManifestFiles = map[string]string{ "main": "main.v2.json", "product_manager": "product_manager.v2.json", "test_manager": "test_manager.v2.json", + "rdev": "rdev.v2.json", "software_bootstrap": "software_bootstrap.v2.json", "software_delivery": "software_delivery.v2.json", } diff --git a/internal/workflowdef/manifests/rdev.v2.json b/internal/workflowdef/manifests/rdev.v2.json new file mode 100644 index 00000000..ca486eee --- /dev/null +++ b/internal/workflowdef/manifests/rdev.v2.json @@ -0,0 +1,833 @@ +{ + "schema_version": 2, + "workflow_key": "rdev", + "product_domains": [ + "medical_device" + ], + "stages": [ + { + "key": "RD01_planning", + "name": "01 设计和开发策划", + "position": 10, + "optional": false, + "doc_tasks": [ + { + "key": "rd_proposal", + "title": "项目建议书", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_proposal", + "output_doc_type": "rd_proposal", + "output_doc_title": "项目建议书" + }, + { + "key": "rd_design_plan", + "title": "设计开发计划任务书", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_design_plan", + "output_doc_type": "rd_design_plan", + "output_doc_title": "设计开发计划任务书" + } + ] + }, + { + "key": "RD02_planning_review", + "name": "02 策划评审", + "position": 20, + "optional": false, + "doc_tasks": [ + { + "key": "rd_planning_review", + "title": "设计和开发策划评审记录", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_planning_review", + "output_doc_type": "rd_planning_review", + "output_doc_title": "设计和开发策划评审记录" + } + ] + }, + { + "key": "RD03_design_input", + "name": "03 设计输入", + "position": 30, + "optional": false, + "doc_tasks": [ + { + "key": "rd_user_needs", + "title": "用户需求规格书", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_user_needs", + "output_doc_type": "rd_user_needs", + "output_doc_title": "用户需求规格书" + }, + { + "key": "rd_regs_stds", + "title": "法规和标准清单", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_regs_stds", + "output_doc_type": "rd_regs_stds", + "output_doc_title": "法规和标准清单" + }, + { + "key": "rd_adverse_events", + "title": "同类产品不良事件信息", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_adverse_events", + "output_doc_type": "rd_adverse_events", + "output_doc_title": "同类产品不良事件信息" + }, + { + "key": "rd_system_reqs", + "title": "系统需求规格书", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_system_reqs", + "output_doc_type": "rd_system_reqs", + "output_doc_title": "系统需求规格书" + }, + { + "key": "rd_risk_plan", + "title": "风险管理计划", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_risk_plan", + "output_doc_type": "rd_risk_plan", + "output_doc_title": "风险管理计划" + }, + { + "key": "rd_hazard_analysis", + "title": "安全性问题清单及可能危险分析表", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_hazard_analysis", + "output_doc_type": "rd_hazard_analysis", + "output_doc_title": "安全性问题清单及可能危险分析表" + }, + { + "key": "rd_sw_plan", + "title": "软件开发计划", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_sw_plan", + "output_doc_type": "rd_sw_plan", + "output_doc_title": "软件开发计划" + }, + { + "key": "rd_sw_reqs", + "title": "软件需求规格书", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_sw_reqs", + "output_doc_type": "rd_sw_reqs", + "output_doc_title": "软件需求规格书" + }, + { + "key": "rd_sw_config", + "title": "软件配置及维护计划", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_sw_config", + "output_doc_type": "rd_sw_config", + "output_doc_title": "软件配置及维护计划" + }, + { + "key": "rd_design_input_spec", + "title": "设计输入规格书", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_design_input_spec", + "output_doc_type": "rd_design_input_spec", + "output_doc_title": "设计输入规格书" + }, + { + "key": "rd_design_input_list", + "title": "设计输入清单", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_design_input_list", + "output_doc_type": "rd_design_input_list", + "output_doc_title": "设计输入清单" + }, + { + "key": "rd_system_design", + "title": "系统设计方案", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_system_design", + "output_doc_type": "rd_system_design", + "output_doc_title": "系统设计方案" + } + ] + }, + { + "key": "RD04_requirements_review", + "name": "04 需求评审", + "position": 40, + "optional": false, + "doc_tasks": [ + { + "key": "rd_req_review", + "title": "需求评审记录", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_req_review", + "output_doc_type": "rd_req_review", + "output_doc_title": "需求评审记录" + } + ] + }, + { + "key": "RD05_input_phase_review", + "name": "05 输入阶段评审", + "position": 50, + "optional": false, + "doc_tasks": [ + { + "key": "rd_input_phase_review", + "title": "设计开发评审记录(阶段3和4)", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_input_phase_review", + "output_doc_type": "rd_input_phase_review", + "output_doc_title": "设计开发评审记录(阶段3和4)" + } + ] + }, + { + "key": "RD06_design_output", + "name": "06 设计输出", + "position": 60, + "optional": false, + "doc_tasks": [ + { + "key": "rd_drawings_3d", + "title": "2D图纸和3D模型", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_drawings_3d", + "output_doc_type": "rd_drawings_3d", + "output_doc_title": "2D图纸和3D模型" + }, + { + "key": "rd_product_bom", + "title": "产品BOM", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_product_bom", + "output_doc_type": "rd_product_bom", + "output_doc_title": "产品BOM" + }, + { + "key": "rd_ee_bom", + "title": "电子电气BOM", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_ee_bom", + "output_doc_type": "rd_ee_bom", + "output_doc_title": "电子电气BOM" + }, + { + "key": "rd_pcba", + "title": "PCBA原理图及Layout", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_pcba", + "output_doc_type": "rd_pcba", + "output_doc_title": "PCBA原理图及Layout" + }, + { + "key": "rd_electrical_sys", + "title": "系统电气设计图", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_electrical_sys", + "output_doc_type": "rd_electrical_sys", + "output_doc_title": "系统电气设计图" + }, + { + "key": "rd_sw_arch", + "title": "软件概要设计方案", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_sw_arch", + "output_doc_type": "rd_sw_arch", + "output_doc_title": "软件概要设计方案" + }, + { + "key": "rd_sw_ui", + "title": "软件UI设计文档", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_sw_ui", + "output_doc_type": "rd_sw_ui", + "output_doc_title": "软件UI设计文档" + }, + { + "key": "rd_purchase_spec", + "title": "采购技术规范", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_purchase_spec", + "output_doc_type": "rd_purchase_spec", + "output_doc_title": "采购技术规范" + }, + { + "key": "rd_process_flow", + "title": "工艺流程图及作业指导", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_process_flow", + "output_doc_type": "rd_process_flow", + "output_doc_title": "工艺流程图及作业指导" + }, + { + "key": "rd_packaging", + "title": "包装图纸和设计要求", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_packaging", + "output_doc_type": "rd_packaging", + "output_doc_title": "包装图纸和设计要求" + }, + { + "key": "rd_ifu", + "title": "产品使用说明书", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_ifu", + "output_doc_type": "rd_ifu", + "output_doc_title": "产品使用说明书" + }, + { + "key": "rd_incoming_iqc", + "title": "原材料进货检验规范", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_incoming_iqc", + "output_doc_type": "rd_incoming_iqc", + "output_doc_title": "原材料进货检验规范" + }, + { + "key": "rd_ipqc", + "title": "生产过程检验规范", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_ipqc", + "output_doc_type": "rd_ipqc", + "output_doc_title": "生产过程检验规范" + }, + { + "key": "rd_fqc", + "title": "成品检验规范", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_fqc", + "output_doc_type": "rd_fqc", + "output_doc_title": "成品检验规范" + }, + { + "key": "rd_tech_req", + "title": "产品技术要求", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_tech_req", + "output_doc_type": "rd_tech_req", + "output_doc_title": "产品技术要求" + }, + { + "key": "rd_risk_report", + "title": "风险管理报告", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_risk_report", + "output_doc_type": "rd_risk_report", + "output_doc_title": "风险管理报告" + }, + { + "key": "rd_design_output_list", + "title": "设计输出清单", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_design_output_list", + "output_doc_type": "rd_design_output_list", + "output_doc_title": "设计输出清单" + } + ] + }, + { + "key": "RD07_output_review", + "name": "07 输出评审", + "position": 70, + "optional": false, + "doc_tasks": [ + { + "key": "rd_output_review", + "title": "设计开发评审记录(阶段6)", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_output_review", + "output_doc_type": "rd_output_review", + "output_doc_title": "设计开发评审记录(阶段6)" + } + ] + }, + { + "key": "RD08_verification", + "name": "08 验证", + "position": 80, + "optional": false, + "doc_tasks": [ + { + "key": "rd_prototype_photos", + "title": "样机照片", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_prototype_photos", + "output_doc_type": "rd_prototype_photos", + "output_doc_title": "样机照片" + }, + { + "key": "rd_design_vv", + "title": "设计验证方案", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_design_vv", + "output_doc_type": "rd_design_vv", + "output_doc_title": "设计验证方案" + }, + { + "key": "rd_shelf_life", + "title": "货架寿命方案", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_shelf_life", + "output_doc_type": "rd_shelf_life", + "output_doc_title": "货架寿命方案" + }, + { + "key": "rd_lifetime", + "title": "产品使用期限研究方案", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_lifetime", + "output_doc_type": "rd_lifetime", + "output_doc_title": "产品使用期限研究方案" + }, + { + "key": "rd_biocompatibility", + "title": "生物学检测方案", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_biocompatibility", + "output_doc_type": "rd_biocompatibility", + "output_doc_title": "生物学检测方案" + }, + { + "key": "rd_cleaning", + "title": "清洁消毒验证方案", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_cleaning", + "output_doc_type": "rd_cleaning", + "output_doc_title": "清洁消毒验证方案" + }, + { + "key": "rd_emc_safety", + "title": "EMC及安规摸底报告", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_emc_safety", + "output_doc_type": "rd_emc_safety", + "output_doc_title": "EMC及安规摸底报告" + }, + { + "key": "rd_risk_report_v", + "title": "风险管理报告(验证阶段)", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_risk_report_v", + "output_doc_type": "rd_risk_report_v", + "output_doc_title": "风险管理报告(验证阶段)" + }, + { + "key": "rd_sw_release", + "title": "软件发布清单", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_sw_release", + "output_doc_type": "rd_sw_release", + "output_doc_title": "软件发布清单" + }, + { + "key": "rd_sw_unit_test", + "title": "软件单元测试报告", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_sw_unit_test", + "output_doc_type": "rd_sw_unit_test", + "output_doc_title": "软件单元测试报告" + }, + { + "key": "rd_sw_sys_test", + "title": "软件系统测试报告", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_sw_sys_test", + "output_doc_type": "rd_sw_sys_test", + "output_doc_title": "软件系统测试报告" + }, + { + "key": "rd_qualified_suppliers", + "title": "合格供方名录", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_qualified_suppliers", + "output_doc_type": "rd_qualified_suppliers", + "output_doc_title": "合格供方名录" + } + ] + }, + { + "key": "RD09_verification_review", + "name": "09 验证评审", + "position": 90, + "optional": false, + "doc_tasks": [ + { + "key": "rd_verification_review", + "title": "设计开发评审记录(验证阶段)", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_verification_review", + "output_doc_type": "rd_verification_review", + "output_doc_title": "设计开发评审记录(验证阶段)" + } + ] + }, + { + "key": "RD10_transfer", + "name": "10 转换", + "position": 100, + "optional": false, + "doc_tasks": [ + { + "key": "rd_dmr_list", + "title": "DMR文件清单", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_dmr_list", + "output_doc_type": "rd_dmr_list", + "output_doc_title": "DMR文件清单" + }, + { + "key": "rd_pack_ship", + "title": "包装运输方案", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_pack_ship", + "output_doc_type": "rd_pack_ship", + "output_doc_title": "包装运输方案" + }, + { + "key": "rd_risk_report_t", + "title": "风险管理报告(转换阶段)", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_risk_report_t", + "output_doc_type": "rd_risk_report_t", + "output_doc_title": "风险管理报告(转换阶段)" + } + ] + }, + { + "key": "RD11_transfer_review", + "name": "11 转换评审", + "position": 110, + "optional": false, + "doc_tasks": [ + { + "key": "rd_transfer_review", + "title": "设计开发评审记录(转换阶段)", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_transfer_review", + "output_doc_type": "rd_transfer_review", + "output_doc_title": "设计开发评审记录(转换阶段)" + } + ] + }, + { + "key": "RD12_validation", + "name": "12 确认", + "position": 120, + "optional": false, + "doc_tasks": [ + { + "key": "rd_validation_plan", + "title": "设计确认方案", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_validation_plan", + "output_doc_type": "rd_validation_plan", + "output_doc_title": "设计确认方案" + }, + { + "key": "rd_use_error", + "title": "使用错误评估报告", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_use_error", + "output_doc_type": "rd_use_error", + "output_doc_title": "使用错误评估报告" + }, + { + "key": "rd_animal_study", + "title": "动物试验方案", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_animal_study", + "output_doc_type": "rd_animal_study", + "output_doc_title": "动物试验方案" + }, + { + "key": "rd_sw_quality", + "title": "软件质量测试报告", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_sw_quality", + "output_doc_type": "rd_sw_quality", + "output_doc_title": "软件质量测试报告" + }, + { + "key": "rd_clinical", + "title": "临床试验报告", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_clinical", + "output_doc_type": "rd_clinical", + "output_doc_title": "临床试验报告" + }, + { + "key": "rd_risk_report_val", + "title": "风险管理报告(确认阶段)", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_risk_report_val", + "output_doc_type": "rd_risk_report_val", + "output_doc_title": "风险管理报告(确认阶段)" + }, + { + "key": "rd_user_test", + "title": "用户测试报告", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_user_test", + "output_doc_type": "rd_user_test", + "output_doc_title": "用户测试报告" + } + ] + }, + { + "key": "RD13_validation_report", + "name": "13 设计确认报告", + "position": 130, + "optional": false, + "doc_tasks": [ + { + "key": "rd_design_validation_report", + "title": "设计确认报告", + "default_enabled": true, + "creator_configurable": true, + "blocking_when_enabled": true, + "executor_key": "squad.rdev_doc.v1", + "quality_gate_key": "quality.generic.v1", + "template_asset_key": "rd_design_validation_report", + "output_doc_type": "rd_design_validation_report", + "output_doc_title": "设计确认报告" + } + ] + } + ] +} diff --git a/internal/workflowdef/rdev_completeness_test.go b/internal/workflowdef/rdev_completeness_test.go new file mode 100644 index 00000000..b93b4b64 --- /dev/null +++ b/internal/workflowdef/rdev_completeness_test.go @@ -0,0 +1,68 @@ +package workflowdef + +import ( + "os" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func TestRDevManifestCompleteness(t *testing.T) { + def, err := LoadManifestDefinition("rdev.v2.json") + if err != nil { + t.Fatal(err) + } + normalizeManifestDefinition(&def) + res := ValidateDefinitionV2(def) + if !res.Valid { + t.Fatalf("rdev v2 invalid: %+v", res.Issues) + } + + keys := make([]string, 0, 60) + for _, st := range def.Stages { + for _, dt := range st.DocTasks { + keys = append(keys, dt.Key) + } + } + if len(keys) != 60 { + t.Fatalf("want 60 doc tasks, got %d", len(keys)) + } + if len(def.Stages) != 13 { + t.Fatalf("want 13 stages, got %d", len(def.Stages)) + } + + existing := map[string]struct{}{ + "intake": {}, "research": {}, "feasibility_dev": {}, "feasibility_reg": {}, + "user_requirements": {}, "system_reqs": {}, "test_manager": {}, "prelim_design": {}, "cdo_quote": {}, + } + _, thisFile, _, _ := runtime.Caller(0) + repoRoot := filepath.Clean(filepath.Join(filepath.Dir(thisFile), "../..")) + templatesDir := filepath.Join(repoRoot, "templates", "docs") + + for _, key := range keys { + if !strings.HasPrefix(key, "rd_") { + t.Fatalf("rdev task key must use rd_ prefix: %s", key) + } + if _, clash := existing[key]; clash { + t.Fatalf("rdev key %s intersects existing task keys", key) + } + path := filepath.Join(templatesDir, key+".md") + b, err := os.ReadFile(path) + if err != nil { + t.Fatalf("template missing for %s: %v", key, err) + } + if len(strings.TrimSpace(string(b))) == 0 { + t.Fatalf("template empty: %s", key) + } + } + + last := filepath.Join(templatesDir, "rd_design_validation_report.md") + b, err := os.ReadFile(last) + if err != nil { + t.Fatal(err) + } + if !strings.Contains(string(b), "签署") { + t.Fatal("rd_design_validation_report.md must contain 签署") + } +} diff --git a/internal/workflowdef/validator_v2.go b/internal/workflowdef/validator_v2.go index 4dec223b..c65cf27b 100644 --- a/internal/workflowdef/validator_v2.go +++ b/internal/workflowdef/validator_v2.go @@ -10,6 +10,7 @@ var officialDocCounts = map[string]int{ OfficialKeyMain: 7, OfficialKeyProductManager: 1, OfficialKeyTestManager: 1, + OfficialKeyRDev: 60, } // ValidateDefinitionV2 schema v2 发布校验。 @@ -69,7 +70,7 @@ func ValidateDefinitionV2(def VersionDefinition) ValidationResult { if len(def.Stages) == 0 { issues = append(issues, ValidationIssue{Code: "NO_STAGES", Message: "at least one stage required"}) } - // 固定文档数仅约束医疗三工作流;软件工作流按自身 manifest 完整性校验。 + // 固定文档数约束医疗正式工作流;软件工作流按自身 manifest 完整性校验。 if def.WorkflowKey != "" && IsMedicalWorkflowKey(def.WorkflowKey) { if want, ok := officialDocCounts[def.WorkflowKey]; ok && docCount != want { issues = append(issues, ValidationIssue{ diff --git a/templates/docs/rd_adverse_events.md b/templates/docs/rd_adverse_events.md new file mode 100644 index 00000000..8d8eb7b9 --- /dev/null +++ b/templates/docs/rd_adverse_events.md @@ -0,0 +1,48 @@ +# 同类产品不良事件信息 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-AE +- 记录归属:DHF +- 版本:A +- 阶段:03 设计输入 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 同类产品不良事件信息正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-AE | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_animal_study.md b/templates/docs/rd_animal_study.md new file mode 100644 index 00000000..074741e4 --- /dev/null +++ b/templates/docs/rd_animal_study.md @@ -0,0 +1,50 @@ +# 动物试验方案 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-ANIMAL +- 记录归属:DHF +- 版本:A +- 阶段:12 确认 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +本阶段可声明不适用:不开展动物试验时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 动物试验方案正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-ANIMAL | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_biocompatibility.md b/templates/docs/rd_biocompatibility.md new file mode 100644 index 00000000..b63422ed --- /dev/null +++ b/templates/docs/rd_biocompatibility.md @@ -0,0 +1,48 @@ +# 生物学检测方案 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-BIO +- 记录归属:DHF +- 版本:A +- 阶段:08 验证 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 生物学检测方案正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-BIO | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_cleaning.md b/templates/docs/rd_cleaning.md new file mode 100644 index 00000000..fa7a19fb --- /dev/null +++ b/templates/docs/rd_cleaning.md @@ -0,0 +1,48 @@ +# 清洁消毒验证方案 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-CLEAN +- 记录归属:DHF +- 版本:A +- 阶段:08 验证 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 清洁消毒验证方案正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-CLEAN | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_clinical.md b/templates/docs/rd_clinical.md new file mode 100644 index 00000000..4498d2eb --- /dev/null +++ b/templates/docs/rd_clinical.md @@ -0,0 +1,50 @@ +# 临床试验报告 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-CLIN +- 记录归属:DHF +- 版本:A +- 阶段:12 确认 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +本阶段可声明不适用:不开展临床试验时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 临床试验报告正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-CLIN | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_design_input_list.md b/templates/docs/rd_design_input_list.md new file mode 100644 index 00000000..cff0ecb9 --- /dev/null +++ b/templates/docs/rd_design_input_list.md @@ -0,0 +1,48 @@ +# 设计输入清单 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-DI-LIST +- 记录归属:DHF +- 版本:A +- 阶段:03 设计输入 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 设计输入清单正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-DI-LIST | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_design_input_spec.md b/templates/docs/rd_design_input_spec.md new file mode 100644 index 00000000..1730887e --- /dev/null +++ b/templates/docs/rd_design_input_spec.md @@ -0,0 +1,48 @@ +# 设计输入规格书 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-DI-SPEC +- 记录归属:DHF +- 版本:A +- 阶段:03 设计输入 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 设计输入规格书正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-DI-SPEC | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_design_output_list.md b/templates/docs/rd_design_output_list.md new file mode 100644 index 00000000..3d28c532 --- /dev/null +++ b/templates/docs/rd_design_output_list.md @@ -0,0 +1,50 @@ +# 设计输出清单 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-DO-LIST +- 记录归属:DHF +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 进入 DMR 的输出 + +| 文件 | 是否进入 DMR | 备注 | +| --- | --- | --- | +| | | | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-DO-LIST | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_design_plan.md b/templates/docs/rd_design_plan.md new file mode 100644 index 00000000..b928c596 --- /dev/null +++ b/templates/docs/rd_design_plan.md @@ -0,0 +1,50 @@ +# 设计开发计划任务书 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-DESIGN-PLAN +- 记录归属:DHF +- 版本:A +- 阶段:01 策划 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## DHF 文件计划清单 + +| 阶段 | 文件 | 记录归属 | 计划完成 | +| --- | --- | --- | --- | +| 策划 | 项目建议书、设计开发计划任务书 | DHF | | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-DESIGN-PLAN | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_design_validation_report.md b/templates/docs/rd_design_validation_report.md new file mode 100644 index 00000000..a244cb49 --- /dev/null +++ b/templates/docs/rd_design_validation_report.md @@ -0,0 +1,48 @@ +# 设计确认报告 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-VAL-RPT +- 记录归属:DHF +- 版本:A +- 阶段:13 设计确认报告 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 设计确认报告正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-VAL-RPT | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_design_vv.md b/templates/docs/rd_design_vv.md new file mode 100644 index 00000000..efc1f71d --- /dev/null +++ b/templates/docs/rd_design_vv.md @@ -0,0 +1,48 @@ +# 设计验证方案 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-VV +- 记录归属:DHF +- 版本:A +- 阶段:08 验证 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 设计验证方案正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-VV | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_dmr_list.md b/templates/docs/rd_dmr_list.md new file mode 100644 index 00000000..8a124eed --- /dev/null +++ b/templates/docs/rd_dmr_list.md @@ -0,0 +1,50 @@ +# DMR文件清单 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-DMR-LIST +- 记录归属:DHF / DMR +- 版本:A +- 阶段:10 转换 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 已批准且进入 DMR 的文件 + +| 文件 | 版本 | 批准状态 | +| --- | --- | --- | +| | | 待确认 | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-DMR-LIST | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_drawings_3d.md b/templates/docs/rd_drawings_3d.md new file mode 100644 index 00000000..41cc3cf5 --- /dev/null +++ b/templates/docs/rd_drawings_3d.md @@ -0,0 +1,52 @@ +# 2D图纸和3D模型(附件清单) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-DWG +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(附件是否齐套。无附件标「待确认」。不适用则写「本文件不适用」及理由。不编造图纸内容。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 附件齐套性 + +结论写附件是否齐套。无附件则标「待确认」。**不要编造图纸、BOM、Gerber 或照片内容。** + +| 序号 | 图号/物料号 | 文件名 | 版本 | 说明 | 齐套 | +| --- | --- | --- | --- | --- | --- | +| 1 | | | | | 待确认 | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-DWG | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_ee_bom.md b/templates/docs/rd_ee_bom.md new file mode 100644 index 00000000..03114f9a --- /dev/null +++ b/templates/docs/rd_ee_bom.md @@ -0,0 +1,54 @@ +# 电子电气BOM(附件清单) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-EE-BOM +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(附件是否齐套。无附件标「待确认」。不适用则写「本文件不适用」及理由。不编造图纸内容。) + +本阶段可声明不适用:无电气部分时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 附件齐套性 + +结论写附件是否齐套。无附件则标「待确认」。**不要编造图纸、BOM、Gerber 或照片内容。** + +| 序号 | 图号/物料号 | 文件名 | 版本 | 说明 | 齐套 | +| --- | --- | --- | --- | --- | --- | +| 1 | | | | | 待确认 | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-EE-BOM | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_electrical_sys.md b/templates/docs/rd_electrical_sys.md new file mode 100644 index 00000000..a3692a1e --- /dev/null +++ b/templates/docs/rd_electrical_sys.md @@ -0,0 +1,54 @@ +# 系统电气设计图(附件清单) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-ELEC +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(附件是否齐套。无附件标「待确认」。不适用则写「本文件不适用」及理由。不编造图纸内容。) + +本阶段可声明不适用:无电气系统时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 附件齐套性 + +结论写附件是否齐套。无附件则标「待确认」。**不要编造图纸、BOM、Gerber 或照片内容。** + +| 序号 | 图号/物料号 | 文件名 | 版本 | 说明 | 齐套 | +| --- | --- | --- | --- | --- | --- | +| 1 | | | | | 待确认 | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-ELEC | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_emc_safety.md b/templates/docs/rd_emc_safety.md new file mode 100644 index 00000000..cf58b1c2 --- /dev/null +++ b/templates/docs/rd_emc_safety.md @@ -0,0 +1,54 @@ +# EMC及安规摸底报告(附件清单) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-EMC +- 记录归属:DHF +- 版本:A +- 阶段:08 验证 + +## 结论 + +(附件是否齐套。无附件标「待确认」。不适用则写「本文件不适用」及理由。不编造图纸内容。) + +本阶段可声明不适用:无电气部分时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 附件齐套性 + +结论写附件是否齐套。无附件则标「待确认」。**不要编造图纸、BOM、Gerber 或照片内容。** + +| 序号 | 图号/物料号 | 文件名 | 版本 | 说明 | 齐套 | +| --- | --- | --- | --- | --- | --- | +| 1 | | | | | 待确认 | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-EMC | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_fqc.md b/templates/docs/rd_fqc.md new file mode 100644 index 00000000..9ac8533e --- /dev/null +++ b/templates/docs/rd_fqc.md @@ -0,0 +1,48 @@ +# 成品检验规范 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-FQC +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 成品检验规范正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-FQC | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_hazard_analysis.md b/templates/docs/rd_hazard_analysis.md new file mode 100644 index 00000000..cfc711fb --- /dev/null +++ b/templates/docs/rd_hazard_analysis.md @@ -0,0 +1,50 @@ +# 安全性问题清单及可能危险分析表 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-HAZARD +- 记录归属:DHF +- 版本:A +- 阶段:03 设计输入 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 危害-原因-控制-验证 + +| 危害 | 原因 | 控制措施 | 验证 | 剩余风险 | +| --- | --- | --- | --- | --- | +| | | | | 待确认 | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-HAZARD | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_ifu.md b/templates/docs/rd_ifu.md new file mode 100644 index 00000000..5c0873d8 --- /dev/null +++ b/templates/docs/rd_ifu.md @@ -0,0 +1,48 @@ +# 产品使用说明书 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-IFU +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 产品使用说明书正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-IFU | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_incoming_iqc.md b/templates/docs/rd_incoming_iqc.md new file mode 100644 index 00000000..09e6b3b1 --- /dev/null +++ b/templates/docs/rd_incoming_iqc.md @@ -0,0 +1,48 @@ +# 原材料进货检验规范 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-IQC +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 原材料进货检验规范正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-IQC | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_input_phase_review.md b/templates/docs/rd_input_phase_review.md new file mode 100644 index 00000000..e27382cb --- /dev/null +++ b/templates/docs/rd_input_phase_review.md @@ -0,0 +1,72 @@ +# 设计开发评审记录(阶段3和4) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-IN-REV +- 记录归属:DHF +- 版本:A +- 阶段:05 输入阶段评审 + +## 结论 + +(通过 / 有条件通过 / 不通过。信息不足标「待确认」。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 评审目的 + +(本阶段评审要确认什么。) + +## 评审对象 + +(前序已批准文档列表。没有则标「待确认」。) + +| 文件 | 版本 | 批准状态 | +| --- | --- | --- | +| | | | + +## 人员 + +| 角色 | 姓名 | 职责 | +| --- | --- | --- | +| | | | + +## 问题与决议 + +| 编号 | 问题 | 决议 | 责任人 | 期限 | +| --- | --- | --- | --- | --- | +| 1 | | | | | + +## 评审结论 + +(通过 / 有条件通过 / 不通过。信息不足标「待确认」。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-IN-REV | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_ipqc.md b/templates/docs/rd_ipqc.md new file mode 100644 index 00000000..e02a3dae --- /dev/null +++ b/templates/docs/rd_ipqc.md @@ -0,0 +1,48 @@ +# 生产过程检验规范 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-IPQC +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 生产过程检验规范正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-IPQC | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_lifetime.md b/templates/docs/rd_lifetime.md new file mode 100644 index 00000000..ae3cd5e0 --- /dev/null +++ b/templates/docs/rd_lifetime.md @@ -0,0 +1,48 @@ +# 产品使用期限研究方案 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-LIFE +- 记录归属:DHF +- 版本:A +- 阶段:08 验证 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 产品使用期限研究方案正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-LIFE | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_output_review.md b/templates/docs/rd_output_review.md new file mode 100644 index 00000000..ecda15f6 --- /dev/null +++ b/templates/docs/rd_output_review.md @@ -0,0 +1,72 @@ +# 设计开发评审记录(阶段6) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-OUT-REV +- 记录归属:DHF +- 版本:A +- 阶段:07 输出评审 + +## 结论 + +(通过 / 有条件通过 / 不通过。信息不足标「待确认」。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 评审目的 + +(本阶段评审要确认什么。) + +## 评审对象 + +(前序已批准文档列表。没有则标「待确认」。) + +| 文件 | 版本 | 批准状态 | +| --- | --- | --- | +| | | | + +## 人员 + +| 角色 | 姓名 | 职责 | +| --- | --- | --- | +| | | | + +## 问题与决议 + +| 编号 | 问题 | 决议 | 责任人 | 期限 | +| --- | --- | --- | --- | --- | +| 1 | | | | | + +## 评审结论 + +(通过 / 有条件通过 / 不通过。信息不足标「待确认」。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-OUT-REV | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_pack_ship.md b/templates/docs/rd_pack_ship.md new file mode 100644 index 00000000..15b20790 --- /dev/null +++ b/templates/docs/rd_pack_ship.md @@ -0,0 +1,48 @@ +# 包装运输方案 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SHIP +- 记录归属:DHF / DMR +- 版本:A +- 阶段:10 转换 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 包装运输方案正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SHIP | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_packaging.md b/templates/docs/rd_packaging.md new file mode 100644 index 00000000..ffb6465e --- /dev/null +++ b/templates/docs/rd_packaging.md @@ -0,0 +1,48 @@ +# 包装图纸和设计要求 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-PACK +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 包装图纸和设计要求正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-PACK | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_pcba.md b/templates/docs/rd_pcba.md new file mode 100644 index 00000000..6390fff0 --- /dev/null +++ b/templates/docs/rd_pcba.md @@ -0,0 +1,54 @@ +# PCBA原理图及Layout(附件清单) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-PCBA +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(附件是否齐套。无附件标「待确认」。不适用则写「本文件不适用」及理由。不编造图纸内容。) + +本阶段可声明不适用:无电路板时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 附件齐套性 + +结论写附件是否齐套。无附件则标「待确认」。**不要编造图纸、BOM、Gerber 或照片内容。** + +| 序号 | 图号/物料号 | 文件名 | 版本 | 说明 | 齐套 | +| --- | --- | --- | --- | --- | --- | +| 1 | | | | | 待确认 | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-PCBA | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_planning_review.md b/templates/docs/rd_planning_review.md new file mode 100644 index 00000000..ab3b2c28 --- /dev/null +++ b/templates/docs/rd_planning_review.md @@ -0,0 +1,72 @@ +# 设计和开发策划评审记录 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-PLAN-REV +- 记录归属:DHF +- 版本:A +- 阶段:02 策划评审 + +## 结论 + +(通过 / 有条件通过 / 不通过。信息不足标「待确认」。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 评审目的 + +(本阶段评审要确认什么。) + +## 评审对象 + +(前序已批准文档列表。没有则标「待确认」。) + +| 文件 | 版本 | 批准状态 | +| --- | --- | --- | +| | | | + +## 人员 + +| 角色 | 姓名 | 职责 | +| --- | --- | --- | +| | | | + +## 问题与决议 + +| 编号 | 问题 | 决议 | 责任人 | 期限 | +| --- | --- | --- | --- | --- | +| 1 | | | | | + +## 评审结论 + +(通过 / 有条件通过 / 不通过。信息不足标「待确认」。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-PLAN-REV | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_process_flow.md b/templates/docs/rd_process_flow.md new file mode 100644 index 00000000..3283dac3 --- /dev/null +++ b/templates/docs/rd_process_flow.md @@ -0,0 +1,48 @@ +# 工艺流程图及作业指导 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-PROC +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 工艺流程图及作业指导正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-PROC | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_product_bom.md b/templates/docs/rd_product_bom.md new file mode 100644 index 00000000..351fb86b --- /dev/null +++ b/templates/docs/rd_product_bom.md @@ -0,0 +1,52 @@ +# 产品BOM(附件清单) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-BOM +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(附件是否齐套。无附件标「待确认」。不适用则写「本文件不适用」及理由。不编造图纸内容。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 附件齐套性 + +结论写附件是否齐套。无附件则标「待确认」。**不要编造图纸、BOM、Gerber 或照片内容。** + +| 序号 | 图号/物料号 | 文件名 | 版本 | 说明 | 齐套 | +| --- | --- | --- | --- | --- | --- | +| 1 | | | | | 待确认 | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-BOM | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_proposal.md b/templates/docs/rd_proposal.md new file mode 100644 index 00000000..c346873f --- /dev/null +++ b/templates/docs/rd_proposal.md @@ -0,0 +1,48 @@ +# 项目建议书 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-PROPOSAL +- 记录归属:DHF +- 版本:A +- 阶段:01 策划 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 项目建议书正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-PROPOSAL | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_prototype_photos.md b/templates/docs/rd_prototype_photos.md new file mode 100644 index 00000000..6b3e5d68 --- /dev/null +++ b/templates/docs/rd_prototype_photos.md @@ -0,0 +1,52 @@ +# 样机照片(附件清单) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-PHOTO +- 记录归属:DHF +- 版本:A +- 阶段:08 验证 + +## 结论 + +(附件是否齐套。无附件标「待确认」。不适用则写「本文件不适用」及理由。不编造图纸内容。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 附件齐套性 + +结论写附件是否齐套。无附件则标「待确认」。**不要编造图纸、BOM、Gerber 或照片内容。** + +| 序号 | 图号/物料号 | 文件名 | 版本 | 说明 | 齐套 | +| --- | --- | --- | --- | --- | --- | +| 1 | | | | | 待确认 | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-PHOTO | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_purchase_spec.md b/templates/docs/rd_purchase_spec.md new file mode 100644 index 00000000..c4ac26b1 --- /dev/null +++ b/templates/docs/rd_purchase_spec.md @@ -0,0 +1,48 @@ +# 采购技术规范 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-PUR +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 采购技术规范正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-PUR | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_qualified_suppliers.md b/templates/docs/rd_qualified_suppliers.md new file mode 100644 index 00000000..4b90c536 --- /dev/null +++ b/templates/docs/rd_qualified_suppliers.md @@ -0,0 +1,48 @@ +# 合格供方名录 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SUP +- 记录归属:DHF / DMR +- 版本:A +- 阶段:08 验证 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 合格供方名录正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SUP | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_regs_stds.md b/templates/docs/rd_regs_stds.md new file mode 100644 index 00000000..b62d491c --- /dev/null +++ b/templates/docs/rd_regs_stds.md @@ -0,0 +1,48 @@ +# 法规和标准清单 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-REGS +- 记录归属:DHF +- 版本:A +- 阶段:03 设计输入 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 法规和标准清单正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-REGS | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_req_review.md b/templates/docs/rd_req_review.md new file mode 100644 index 00000000..0b192b5d --- /dev/null +++ b/templates/docs/rd_req_review.md @@ -0,0 +1,72 @@ +# 需求评审记录 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-REQ-REV +- 记录归属:DHF +- 版本:A +- 阶段:04 需求评审 + +## 结论 + +(通过 / 有条件通过 / 不通过。信息不足标「待确认」。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 评审目的 + +(本阶段评审要确认什么。) + +## 评审对象 + +(前序已批准文档列表。没有则标「待确认」。) + +| 文件 | 版本 | 批准状态 | +| --- | --- | --- | +| | | | + +## 人员 + +| 角色 | 姓名 | 职责 | +| --- | --- | --- | +| | | | + +## 问题与决议 + +| 编号 | 问题 | 决议 | 责任人 | 期限 | +| --- | --- | --- | --- | --- | +| 1 | | | | | + +## 评审结论 + +(通过 / 有条件通过 / 不通过。信息不足标「待确认」。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-REQ-REV | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_risk_plan.md b/templates/docs/rd_risk_plan.md new file mode 100644 index 00000000..9bf669f4 --- /dev/null +++ b/templates/docs/rd_risk_plan.md @@ -0,0 +1,48 @@ +# 风险管理计划 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-RISK-PLAN +- 记录归属:DHF +- 版本:A +- 阶段:03 设计输入 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 风险管理范围与职责 + +(范围、职责、准则、评审节点。缺项写「待确认」。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-RISK-PLAN | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_risk_report.md b/templates/docs/rd_risk_report.md new file mode 100644 index 00000000..b1779fb5 --- /dev/null +++ b/templates/docs/rd_risk_report.md @@ -0,0 +1,48 @@ +# 风险管理报告 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-RISK-RPT +- 记录归属:DHF +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 风险评价摘要 + +(可接受性结论。缺项写「待确认」。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-RISK-RPT | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_risk_report_t.md b/templates/docs/rd_risk_report_t.md new file mode 100644 index 00000000..65fd8156 --- /dev/null +++ b/templates/docs/rd_risk_report_t.md @@ -0,0 +1,48 @@ +# 风险管理报告(转换阶段) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-RISK-T +- 记录归属:DHF +- 版本:A +- 阶段:10 转换 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 转换阶段风险评价 + +(生产转换相关剩余风险。缺项写「待确认」。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-RISK-T | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_risk_report_v.md b/templates/docs/rd_risk_report_v.md new file mode 100644 index 00000000..edb62e09 --- /dev/null +++ b/templates/docs/rd_risk_report_v.md @@ -0,0 +1,48 @@ +# 风险管理报告(验证阶段) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-RISK-V +- 记录归属:DHF +- 版本:A +- 阶段:08 验证 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 验证阶段风险评价 + +(验证证据是否支撑风险控制。缺项写「待确认」。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-RISK-V | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_risk_report_val.md b/templates/docs/rd_risk_report_val.md new file mode 100644 index 00000000..7ce56e91 --- /dev/null +++ b/templates/docs/rd_risk_report_val.md @@ -0,0 +1,48 @@ +# 风险管理报告(确认阶段) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-RISK-VAL +- 记录归属:DHF +- 版本:A +- 阶段:12 确认 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 确认阶段风险评价 + +(确认证据是否支撑综合剩余风险可接受。缺项写「待确认」。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-RISK-VAL | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_shelf_life.md b/templates/docs/rd_shelf_life.md new file mode 100644 index 00000000..1fe41c97 --- /dev/null +++ b/templates/docs/rd_shelf_life.md @@ -0,0 +1,48 @@ +# 货架寿命方案 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SHELF +- 记录归属:DHF +- 版本:A +- 阶段:08 验证 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 货架寿命方案正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SHELF | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_sw_arch.md b/templates/docs/rd_sw_arch.md new file mode 100644 index 00000000..68a512ee --- /dev/null +++ b/templates/docs/rd_sw_arch.md @@ -0,0 +1,50 @@ +# 软件概要设计方案 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SW-ARCH +- 记录归属:DHF +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +本阶段可声明不适用:无软件成分时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 软件概要设计方案正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SW-ARCH | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_sw_config.md b/templates/docs/rd_sw_config.md new file mode 100644 index 00000000..d8bc101a --- /dev/null +++ b/templates/docs/rd_sw_config.md @@ -0,0 +1,50 @@ +# 软件配置及维护计划 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SW-CFG +- 记录归属:DHF +- 版本:A +- 阶段:03 设计输入 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +本阶段可声明不适用:无软件成分时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 软件配置及维护计划正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SW-CFG | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_sw_plan.md b/templates/docs/rd_sw_plan.md new file mode 100644 index 00000000..5d9733a3 --- /dev/null +++ b/templates/docs/rd_sw_plan.md @@ -0,0 +1,50 @@ +# 软件开发计划 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SW-PLAN +- 记录归属:DHF +- 版本:A +- 阶段:03 设计输入 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +本阶段可声明不适用:无软件成分时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 软件开发计划正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SW-PLAN | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_sw_quality.md b/templates/docs/rd_sw_quality.md new file mode 100644 index 00000000..7648f53c --- /dev/null +++ b/templates/docs/rd_sw_quality.md @@ -0,0 +1,50 @@ +# 软件质量测试报告 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SW-QA +- 记录归属:DHF +- 版本:A +- 阶段:12 确认 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +本阶段可声明不适用:无软件成分时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 软件质量测试报告正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SW-QA | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_sw_release.md b/templates/docs/rd_sw_release.md new file mode 100644 index 00000000..fcb0831d --- /dev/null +++ b/templates/docs/rd_sw_release.md @@ -0,0 +1,50 @@ +# 软件发布清单 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SW-REL +- 记录归属:DHF / DMR +- 版本:A +- 阶段:08 验证 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +本阶段可声明不适用:无软件成分时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 软件发布清单正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SW-REL | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_sw_reqs.md b/templates/docs/rd_sw_reqs.md new file mode 100644 index 00000000..fee69b5b --- /dev/null +++ b/templates/docs/rd_sw_reqs.md @@ -0,0 +1,50 @@ +# 软件需求规格书 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SW-REQ +- 记录归属:DHF +- 版本:A +- 阶段:03 设计输入 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +本阶段可声明不适用:无软件成分时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 软件需求规格书正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SW-REQ | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_sw_sys_test.md b/templates/docs/rd_sw_sys_test.md new file mode 100644 index 00000000..9d1c26d6 --- /dev/null +++ b/templates/docs/rd_sw_sys_test.md @@ -0,0 +1,50 @@ +# 软件系统测试报告 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SW-ST +- 记录归属:DHF +- 版本:A +- 阶段:08 验证 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +本阶段可声明不适用:无软件成分时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 软件系统测试报告正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SW-ST | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_sw_ui.md b/templates/docs/rd_sw_ui.md new file mode 100644 index 00000000..ce1b3120 --- /dev/null +++ b/templates/docs/rd_sw_ui.md @@ -0,0 +1,50 @@ +# 软件UI设计文档 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SW-UI +- 记录归属:DHF +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +本阶段可声明不适用:无软件界面时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 软件UI设计文档正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SW-UI | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_sw_unit_test.md b/templates/docs/rd_sw_unit_test.md new file mode 100644 index 00000000..803b3dd0 --- /dev/null +++ b/templates/docs/rd_sw_unit_test.md @@ -0,0 +1,50 @@ +# 软件单元测试报告 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SW-UT +- 记录归属:DHF +- 版本:A +- 阶段:08 验证 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +本阶段可声明不适用:无软件成分时可声明不适用。 + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 软件单元测试报告正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SW-UT | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_system_design.md b/templates/docs/rd_system_design.md new file mode 100644 index 00000000..52b5c3db --- /dev/null +++ b/templates/docs/rd_system_design.md @@ -0,0 +1,48 @@ +# 系统设计方案 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SYS-DES +- 记录归属:DHF +- 版本:A +- 阶段:03 设计输入 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 系统设计方案正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SYS-DES | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_system_reqs.md b/templates/docs/rd_system_reqs.md new file mode 100644 index 00000000..64f6a0e2 --- /dev/null +++ b/templates/docs/rd_system_reqs.md @@ -0,0 +1,50 @@ +# 系统需求规格书(SR) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-SR +- 记录归属:DHF +- 版本:A +- 阶段:03 设计输入 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 系统需求 + +| ID | 需求陈述 | 追溯 UN | 验证方式 | +| --- | --- | --- | --- | +| SR-001 | | UN-001 | | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-SR | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_tech_req.md b/templates/docs/rd_tech_req.md new file mode 100644 index 00000000..c9caa9d2 --- /dev/null +++ b/templates/docs/rd_tech_req.md @@ -0,0 +1,48 @@ +# 产品技术要求 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-TECH +- 记录归属:DHF / DMR +- 版本:A +- 阶段:06 设计输出 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 产品技术要求正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-TECH | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_transfer_review.md b/templates/docs/rd_transfer_review.md new file mode 100644 index 00000000..089d926a --- /dev/null +++ b/templates/docs/rd_transfer_review.md @@ -0,0 +1,72 @@ +# 设计开发评审记录(转换阶段) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-XFER-REV +- 记录归属:DHF +- 版本:A +- 阶段:11 转换评审 + +## 结论 + +(通过 / 有条件通过 / 不通过。信息不足标「待确认」。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 评审目的 + +(本阶段评审要确认什么。) + +## 评审对象 + +(前序已批准文档列表。没有则标「待确认」。) + +| 文件 | 版本 | 批准状态 | +| --- | --- | --- | +| | | | + +## 人员 + +| 角色 | 姓名 | 职责 | +| --- | --- | --- | +| | | | + +## 问题与决议 + +| 编号 | 问题 | 决议 | 责任人 | 期限 | +| --- | --- | --- | --- | --- | +| 1 | | | | | + +## 评审结论 + +(通过 / 有条件通过 / 不通过。信息不足标「待确认」。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-XFER-REV | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_use_error.md b/templates/docs/rd_use_error.md new file mode 100644 index 00000000..ea5a675e --- /dev/null +++ b/templates/docs/rd_use_error.md @@ -0,0 +1,48 @@ +# 使用错误评估报告 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-USE-ERR +- 记录归属:DHF +- 版本:A +- 阶段:12 确认 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 使用错误评估报告正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-USE-ERR | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_user_needs.md b/templates/docs/rd_user_needs.md new file mode 100644 index 00000000..b15514c9 --- /dev/null +++ b/templates/docs/rd_user_needs.md @@ -0,0 +1,50 @@ +# 用户需求规格书(UN) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-UN +- 记录归属:DHF +- 版本:A +- 阶段:03 设计输入 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 用户需求 + +| ID | 需求陈述 | 来源 | 优先级 | 验证方式 | +| --- | --- | --- | --- | --- | +| UN-001 | | | | | + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-UN | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_user_test.md b/templates/docs/rd_user_test.md new file mode 100644 index 00000000..e7a36362 --- /dev/null +++ b/templates/docs/rd_user_test.md @@ -0,0 +1,48 @@ +# 用户测试报告 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-USER-T +- 记录归属:DHF +- 版本:A +- 阶段:12 确认 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 用户测试报告正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-USER-T | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_validation_plan.md b/templates/docs/rd_validation_plan.md new file mode 100644 index 00000000..b7e0f79e --- /dev/null +++ b/templates/docs/rd_validation_plan.md @@ -0,0 +1,48 @@ +# 设计确认方案 + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-VAL-PLAN +- 记录归属:DHF +- 版本:A +- 阶段:12 确认 + +## 结论 + +(一句话。信息不足标「待确认」。不适用则写「本文件不适用」及理由。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 设计确认方案正文 + +(按本阶段专业结构写。缺项写「待确认」。不做疗效宣称。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-VAL-PLAN | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。 diff --git a/templates/docs/rd_verification_review.md b/templates/docs/rd_verification_review.md new file mode 100644 index 00000000..d30080a4 --- /dev/null +++ b/templates/docs/rd_verification_review.md @@ -0,0 +1,72 @@ +# 设计开发评审记录(验证阶段) + +- 项目:{{project_name}} +- 生成时间:{{generated_at}} +- 快照:{{snapshot_id}} +- 文件编号:RD-VER-REV +- 记录归属:DHF +- 版本:A +- 阶段:09 验证评审 + +## 结论 + +(通过 / 有条件通过 / 不通过。信息不足标「待确认」。) + +## 适用性 + +- 适用 / 不适用:{{applicability}} +- 不适用理由:{{na_rationale}} + +## 评审目的 + +(本阶段评审要确认什么。) + +## 评审对象 + +(前序已批准文档列表。没有则标「待确认」。) + +| 文件 | 版本 | 批准状态 | +| --- | --- | --- | +| | | | + +## 人员 + +| 角色 | 姓名 | 职责 | +| --- | --- | --- | +| | | | + +## 问题与决议 + +| 编号 | 问题 | 决议 | 责任人 | 期限 | +| --- | --- | --- | --- | --- | +| 1 | | | | | + +## 评审结论 + +(通过 / 有条件通过 / 不通过。信息不足标「待确认」。) + +## 追溯 + +| 本文件 ID | 上游(输入/需求) | 下游(输出/验证/确认) | +| --- | --- | --- | +| RD-VER-REV | | | + +## 已有材料摘要 + +{{materials}} + +## 待确认清单 + +- (列出本阶段仍缺、会影响后续设计/验证/确认的项) + +## 签署 + +| 角色 | 姓名 | 结论 | 日期 | +| --- | --- | --- | --- | +| 编制 | | 已编制 | {{generated_at}} | +| 审核 | | | | +| 批准 | | | | + +## 说明 + +本文档由研发小队在任务快照上生成,属于设计历史记录。后续补充的材料不会改变本次内容。