feat(squad): gate rdev regulatory steps on complexity tier
Listed tasks still get a regulatory step when complexity is unset or high. Medium and low skip it, and the squad model now comes from SQUAD_MODEL_ID or MINIMAX_MODEL_ID instead of a fixed name. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -49,7 +49,7 @@ updated: 2026-09-19
|
||||
| FE App.tsx | 部分。登录壳不再静态引用导出引擎、文件预览、管理台、块编辑器和图表。入口包 2.83 MB → 1.00 MB(br 626 KB → 235 KB)。`loginShellImports.test.ts`。工作台主体仍在 `App.tsx`,未按 hook 拆文件 |
|
||||
| H1-del | 关闭理由:`allowLegacyTestPrincipal` 仍被 `AllowInMemory && ContextDbHost==""` 的 dev/test 内存测试使用。删除会把这批测试变成未登录 401。保留告警,删除另开、先改测试夹具 |
|
||||
|
||||
仍开放:rdev S2/S3 与 P2–P4、首次加载 U1/U4/U6/U7、dev 容器重建与治理库 PG。U3 只完成登录壳拆包(入口包 1.00 MB / br 235 KB)。U5 已复用本请求身份并合并会话/用户状态查询;未节流 `last_seen`。H2 批注/审计的项目成员检查在 Postgres 上走 `RequireProjectAccess`;内存授权器在无库时把任意正用户 id 视为成员,这是 `TestAuthorizeDocRevision_ReviewerAndNonMemberCannotWrite` 的隔离前提,不能改成默认拒绝。H2 的 HTTP 负例因此要在 `TEST_DATABASE_URL` 上补,本机未设置该变量。M7 的投影意图已随账本提交(goose `00053`);容器 schema 仍是 52,要等占槽重建后才升到 53。
|
||||
仍开放:rdev S2 阶段步骤覆盖与 manifest aliases、S3、P2–P4、首次加载 U1/U4/U6/U7、dev 容器重建与治理库 PG。S2 已接复杂度与模型环境变量(`TestRDevRegulatoryFollowsComplexityTier`、`TestRDevModelFromEnv`)。H2 批注/审计的项目成员检查在 Postgres 上走 `RequireProjectAccess`;内存授权器在无库时把任意正用户 id 视为成员,这是 `TestAuthorizeDocRevision_ReviewerAndNonMemberCannotWrite` 的隔离前提,不能改成默认拒绝。H2 的 HTTP 负例因此要在 `TEST_DATABASE_URL` 上补,本机未设置该变量。M7 的投影意图已随账本提交(goose `00053`);容器 schema 仍是 52,要等占槽重建后才升到 53。
|
||||
|
||||
红线关闭(不是实现):
|
||||
|
||||
|
||||
@@ -57,7 +57,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](../archive/ops/2026-09-17-rdev-workflow-integration.md);[模板深度](../archive/ops/2026-09-17-rdev-template-depth.md);[S0+S1 首批报告](../test-reports/2026-09-17-rdev-template-depth-test-report.md);[S1 续批报告](../test-reports/2026-09-17-rdev-template-depth-s1-continued-test-report.md);[写作规范](../system-design/project-workflow/08-rdev模板写作规范.md);[backlog](../backlog/2026-09-17-rdev-phase2-backlog.md) | **P0+P1 done**。**模板深度 S0+S1 首批 + S1 续批 done**:60/60 模板已补强;真 PI 抽样 PASS(含 `rd_planning_review` + `rd_design_vv`,项目 6446)。剩余:S2/S3、P2 前端、P3 三样本 E2E、P4 键名空间化 | `workflow-def` / `rdev-templates` |
|
||||
| 研发设计控制(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](../archive/ops/2026-09-17-rdev-workflow-integration.md);[模板深度](../archive/ops/2026-09-17-rdev-template-depth.md);[S0+S1 首批报告](../test-reports/2026-09-17-rdev-template-depth-test-report.md);[S1 续批报告](../test-reports/2026-09-17-rdev-template-depth-s1-continued-test-report.md);[写作规范](../system-design/project-workflow/08-rdev模板写作规范.md);[backlog](../backlog/2026-09-17-rdev-phase2-backlog.md) | **P0+P1 done**。**模板深度 S0+S1 首批 + S1 续批 done**:60/60 模板已补强;真 PI 抽样 PASS(含 `rd_planning_review` + `rd_design_vv`,项目 6446)。剩余:S2 已接 `complexity_tier`(未声明保持名单;medium/low 不注入法规步)与 `SQUAD_MODEL_ID`/`MINIMAX_MODEL_ID`。S2 的阶段步骤覆盖和 manifest aliases、S3、P2 前端、P3 三样本 E2E、P4 键名空间化仍未做 | `workflow-def` / `rdev-templates` |
|
||||
| 纯软件项目自举 | 软件项目创建、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` |
|
||||
|
||||
@@ -72,10 +72,19 @@ func (p *AdaptivePlanner) planFixedAdaptive(req PlannerRequest) (*PlannerResult,
|
||||
|
||||
steps := req.CustomSteps
|
||||
if len(steps) == 0 {
|
||||
steps = DefaultWorkflowSteps(req.TaskKey)
|
||||
if strings.HasPrefix(req.TaskKey, "rd_") {
|
||||
steps = rdevWorkflowSteps(req.TaskKey, rdevComplexityTier(req.Fields))
|
||||
} else {
|
||||
steps = DefaultWorkflowSteps(req.TaskKey)
|
||||
}
|
||||
}
|
||||
|
||||
agents := DefaultSquadAgents(req.TaskKey)
|
||||
var agents []AgentSpec
|
||||
if strings.HasPrefix(req.TaskKey, "rd_") {
|
||||
agents = rdevSquadAgents(req.TaskKey, rdevComplexityTier(req.Fields))
|
||||
} else {
|
||||
agents = DefaultSquadAgents(req.TaskKey)
|
||||
}
|
||||
|
||||
// 材料嗅探感知 (Material Sniffing)
|
||||
var matBuilder strings.Builder
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package squad
|
||||
|
||||
import "strings"
|
||||
|
||||
// RDevArtifactKind 研发文档产物类别(对齐参考项目 artifact_kind)。
|
||||
type RDevArtifactKind string
|
||||
|
||||
@@ -340,8 +342,18 @@ func LookupRDevTask(taskKey string) (RDevTaskMeta, bool) {
|
||||
return t, ok
|
||||
}
|
||||
|
||||
// RDevNeedsRegulatory 是否注入法规专员步骤。
|
||||
// RDevNeedsRegulatory 是否注入法规专员步骤。未声明复杂度时保持任务名单,避免已有运行突然少一步。
|
||||
func RDevNeedsRegulatory(taskKey string) bool {
|
||||
return RDevNeedsRegulatoryAt(taskKey, "")
|
||||
}
|
||||
|
||||
// RDevNeedsRegulatoryAt 在任务名单之上再看 complexity_tier。
|
||||
// high 与未声明走名单;medium 与 low 不注入。对齐参考项目 when: complexity_tier=high。
|
||||
func RDevNeedsRegulatoryAt(taskKey, tier string) bool {
|
||||
switch strings.ToLower(strings.TrimSpace(tier)) {
|
||||
case "medium", "low":
|
||||
return false
|
||||
}
|
||||
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",
|
||||
|
||||
@@ -51,3 +51,64 @@ func TestRDevWorkflowStepsByKind(t *testing.T) {
|
||||
t.Fatal("leader display name missing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRDevRegulatoryFollowsComplexityTier(t *testing.T) {
|
||||
if !RDevNeedsRegulatory("rd_sw_plan") {
|
||||
t.Fatal("unset tier must keep the static regulatory list")
|
||||
}
|
||||
if !RDevNeedsRegulatoryAt("rd_sw_plan", "high") {
|
||||
t.Fatal("high tier must keep regulatory for listed tasks")
|
||||
}
|
||||
if RDevNeedsRegulatoryAt("rd_sw_plan", "medium") || RDevNeedsRegulatoryAt("rd_sw_plan", "low") {
|
||||
t.Fatal("medium and low must not inject regulatory")
|
||||
}
|
||||
if RDevNeedsRegulatoryAt("rd_proposal", "high") {
|
||||
t.Fatal("tasks outside the list stay without regulatory even at high")
|
||||
}
|
||||
|
||||
planner := NewAdaptivePlanner(nil)
|
||||
low, err := planner.Plan(PlannerRequest{
|
||||
TaskKey: "rd_sw_plan",
|
||||
Fields: map[string]string{"complexity_tier": "low"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, step := range low.Workflow.Steps {
|
||||
if step.Role == "regulatory" {
|
||||
t.Fatalf("low tier plan must not include regulatory step: %+v", low.Workflow.Steps)
|
||||
}
|
||||
}
|
||||
high, err := planner.Plan(PlannerRequest{
|
||||
TaskKey: "rd_sw_plan",
|
||||
Fields: map[string]string{"complexity_tier": "high"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
found := false
|
||||
for _, step := range high.Workflow.Steps {
|
||||
if step.Role == "regulatory" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("high tier plan must include regulatory step")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRDevModelFromEnv(t *testing.T) {
|
||||
t.Setenv("SQUAD_MODEL_ID", "")
|
||||
t.Setenv("MINIMAX_MODEL_ID", "")
|
||||
if got := rdevModel(); got != "MiniMax-M3" {
|
||||
t.Fatalf("default model want MiniMax-M3 got %s", got)
|
||||
}
|
||||
t.Setenv("MINIMAX_MODEL_ID", "MiniMax-M2.7-highspeed")
|
||||
if got := rdevModel(); got != "MiniMax-M2.7-highspeed" {
|
||||
t.Fatalf("MINIMAX_MODEL_ID want highspeed got %s", got)
|
||||
}
|
||||
t.Setenv("SQUAD_MODEL_ID", "custom-squad")
|
||||
if got := rdevModel(); got != "custom-squad" {
|
||||
t.Fatalf("SQUAD_MODEL_ID must win, got %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,41 @@
|
||||
package squad
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const rdevDHFAccept = "模板写满;结论先行;追溯表必填;缺项标「待确认」;不编造数据/签字/附件"
|
||||
|
||||
func rdevSquadAgents(taskKey string) []AgentSpec {
|
||||
func rdevModel() string {
|
||||
if v := strings.TrimSpace(os.Getenv("SQUAD_MODEL_ID")); v != "" {
|
||||
return v
|
||||
}
|
||||
if v := strings.TrimSpace(os.Getenv("MINIMAX_MODEL_ID")); v != "" {
|
||||
return v
|
||||
}
|
||||
return "MiniMax-M3"
|
||||
}
|
||||
|
||||
func rdevComplexityTier(fields map[string]string) string {
|
||||
if fields == nil {
|
||||
return ""
|
||||
}
|
||||
switch strings.ToLower(strings.TrimSpace(fields["complexity_tier"])) {
|
||||
case "high", "medium", "low":
|
||||
return strings.ToLower(strings.TrimSpace(fields["complexity_tier"]))
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func rdevSquadAgents(taskKey, tier string) []AgentSpec {
|
||||
meta, _ := LookupRDevTask(taskKey)
|
||||
agents := []AgentSpec{
|
||||
{
|
||||
Role: "organizer",
|
||||
Name: "材料整理专员",
|
||||
Model: "claude-3-7-sonnet",
|
||||
Model: rdevModel(),
|
||||
Tools: []string{"read", "write", "edit"},
|
||||
BashPermission: "deny",
|
||||
Responsibilities: []string{"材料事实抽取", "适用性与缺口识别", "字段回填"},
|
||||
@@ -21,7 +48,7 @@ func rdevSquadAgents(taskKey string) []AgentSpec {
|
||||
AgentSpec{
|
||||
Role: "writer",
|
||||
Name: "文档撰写专员",
|
||||
Model: "claude-3-7-sonnet",
|
||||
Model: rdevModel(),
|
||||
Tools: []string{"read", "write", "edit"},
|
||||
BashPermission: "deny",
|
||||
Responsibilities: []string{"按评审记录模板成文", "问题-决议-行动项闭环"},
|
||||
@@ -33,7 +60,7 @@ func rdevSquadAgents(taskKey string) []AgentSpec {
|
||||
AgentSpec{
|
||||
Role: "engineer",
|
||||
Name: "技术专员",
|
||||
Model: "claude-3-7-sonnet",
|
||||
Model: rdevModel(),
|
||||
Tools: []string{"read", "write", "edit"},
|
||||
BashPermission: "deny",
|
||||
Responsibilities: []string{"附件索引完整性", "缺失影响分析"},
|
||||
@@ -41,7 +68,7 @@ func rdevSquadAgents(taskKey string) []AgentSpec {
|
||||
AgentSpec{
|
||||
Role: "writer",
|
||||
Name: "文档撰写专员",
|
||||
Model: "claude-3-7-sonnet",
|
||||
Model: rdevModel(),
|
||||
Tools: []string{"read", "write", "edit"},
|
||||
BashPermission: "deny",
|
||||
Responsibilities: []string{"按附件清单模板成文", "标明待上传项"},
|
||||
@@ -53,17 +80,17 @@ func rdevSquadAgents(taskKey string) []AgentSpec {
|
||||
AgentSpec{
|
||||
Role: "engineer",
|
||||
Name: "技术专员",
|
||||
Model: "claude-3-7-sonnet",
|
||||
Model: rdevModel(),
|
||||
Tools: []string{"read", "write", "edit"},
|
||||
BashPermission: "deny",
|
||||
Responsibilities: []string{"技术内容与追溯", "设计控制论证"},
|
||||
},
|
||||
)
|
||||
if RDevNeedsRegulatory(taskKey) {
|
||||
if RDevNeedsRegulatoryAt(taskKey, tier) {
|
||||
agents = append(agents, AgentSpec{
|
||||
Role: "regulatory",
|
||||
Name: "法规专员",
|
||||
Model: "claude-3-7-sonnet",
|
||||
Model: rdevModel(),
|
||||
Tools: []string{"read", "write", "edit"},
|
||||
BashPermission: "deny",
|
||||
Responsibilities: []string{"法规/标准适用性", "软件与风险管理合规要点"},
|
||||
@@ -73,7 +100,7 @@ func rdevSquadAgents(taskKey string) []AgentSpec {
|
||||
AgentSpec{
|
||||
Role: "writer",
|
||||
Name: "文档撰写专员",
|
||||
Model: "claude-3-7-sonnet",
|
||||
Model: rdevModel(),
|
||||
Tools: []string{"read", "write", "edit"},
|
||||
BashPermission: "deny",
|
||||
Responsibilities: []string{"按 RD 模板成文", "结论先行与待确认清单"},
|
||||
@@ -88,7 +115,7 @@ func rdevLeaderAgent() AgentSpec {
|
||||
return AgentSpec{
|
||||
Role: "leader",
|
||||
Name: "研发设计控制负责人",
|
||||
Model: "claude-3-7-sonnet",
|
||||
Model: rdevModel(),
|
||||
Tools: []string{"read", "write", "edit"},
|
||||
BashPermission: "deny",
|
||||
InstructionsFile: "configs/agents/rdev_leader.md",
|
||||
@@ -97,7 +124,7 @@ func rdevLeaderAgent() AgentSpec {
|
||||
}
|
||||
}
|
||||
|
||||
func rdevWorkflowSteps(taskKey string) []StepSpec {
|
||||
func rdevWorkflowSteps(taskKey, tier string) []StepSpec {
|
||||
meta, ok := LookupRDevTask(taskKey)
|
||||
kind := RDevKindDocument
|
||||
title := taskKey
|
||||
@@ -154,11 +181,11 @@ func rdevWorkflowSteps(taskKey string) []StepSpec {
|
||||
},
|
||||
}
|
||||
default:
|
||||
return rdevDocumentSteps(taskKey, title)
|
||||
return rdevDocumentSteps(taskKey, title, tier)
|
||||
}
|
||||
}
|
||||
|
||||
func rdevDocumentSteps(taskKey, title string) []StepSpec {
|
||||
func rdevDocumentSteps(taskKey, title, tier string) []StepSpec {
|
||||
steps := []StepSpec{
|
||||
{
|
||||
ID: "step_01_organize", Role: "organizer", Title: "抽取材料事实并回填",
|
||||
@@ -173,7 +200,7 @@ func rdevDocumentSteps(taskKey, title string) []StepSpec {
|
||||
},
|
||||
}
|
||||
draftNeeds := "tasks/02_engineer/output.md"
|
||||
if RDevNeedsRegulatory(taskKey) {
|
||||
if RDevNeedsRegulatoryAt(taskKey, tier) {
|
||||
steps = append(steps, StepSpec{
|
||||
ID: "step_03_regulatory", Role: "regulatory", Title: "法规与风险合规要点",
|
||||
SubWorkspace: "tasks/03_regulatory", Mode: "execute",
|
||||
|
||||
@@ -597,7 +597,7 @@ func (e *WorkspaceEngine) buildSubWorkspaces(workDir string, wf WorkflowPlan, sq
|
||||
// DefaultSquadAgents 根据任务类型返回默认小队成员编制
|
||||
func DefaultSquadAgents(taskKey string) []AgentSpec {
|
||||
if strings.HasPrefix(taskKey, "rd_") {
|
||||
return rdevSquadAgents(taskKey)
|
||||
return rdevSquadAgents(taskKey, "")
|
||||
}
|
||||
switch taskKey {
|
||||
case "intake":
|
||||
@@ -807,7 +807,7 @@ func DefaultSquadAgents(taskKey string) []AgentSpec {
|
||||
// DefaultWorkflowSteps 根据任务类型返回最佳实践编排步骤
|
||||
func DefaultWorkflowSteps(taskKey string) []StepSpec {
|
||||
if strings.HasPrefix(taskKey, "rd_") {
|
||||
return rdevWorkflowSteps(taskKey)
|
||||
return rdevWorkflowSteps(taskKey, "")
|
||||
}
|
||||
switch taskKey {
|
||||
case "intake":
|
||||
|
||||
Reference in New Issue
Block a user