From eb5634a06e4c12b64c36d68fa7f7e6ff3ac70031 Mon Sep 17 00:00:00 2001 From: weichen Date: Sat, 12 Sep 2026 16:19:14 +0000 Subject: [PATCH] =?UTF-8?q?test(governance):=20=E8=A1=A5=E8=B7=91=20produc?= =?UTF-8?q?t=5Fmanager=20PG+PI=20=E5=85=A8=E9=93=BE=E8=B7=AF=E5=B9=B6?= =?UTF-8?q?=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 抽取 runPiAgentGovernanceE2E 共用 harness,新增 system_reqs 真实 Agent 验收并更新测试报告。 --- .doc/2026-09-12-remediation-gap-matrix.md | 4 +- ...flow-governance-remediation-test-report.md | 28 ++++- .../workflow_governance_piagent_pg_test.go | 100 ++++++++++++------ 3 files changed, 93 insertions(+), 39 deletions(-) diff --git a/.doc/2026-09-12-remediation-gap-matrix.md b/.doc/2026-09-12-remediation-gap-matrix.md index ce0eb541..f7251f22 100644 --- a/.doc/2026-09-12-remediation-gap-matrix.md +++ b/.doc/2026-09-12-remediation-gap-matrix.md @@ -20,9 +20,9 @@ | BUG-12 | 前端名称硬编码 | PASS | `ProjectWorkflowSwitcher` catalog API | 已完成 | | MISSING-01 | 上下文快照 0% | PASS | `ctxmgr.CollectProjectDialogue` + bizdev `collectSnapshotDialogueInputs` → `BuildSnapshot` | 已完成 | | MISSING-02 | 终止 0% | PASS | `GetTerminationPreview` + 取消在途 + 前端 | 已完成 | -| MISSING-04 | E2E 0% | PASS | PG+PI:`test_manager`/`main` stub E2E + `main` pi-agent intake 全链路(~8m PASS) | 可选补 `product_manager` PG+PI | +| MISSING-04 | E2E 0% | PASS | 三正式工作流:`test_manager`/`main`/`product_manager` PG+PI 全链路实测 PASS | 已完成 | -**结论**: Phase A 全部 PASS;Phase B/C 核心项 PASS,E2E 与上下文快照仍有 PARTIAL。test 分支合并前需 `TEST_DATABASE_URL` + 真实 PI Agent 全链路验收。 +**结论**: Phase A/B/C 核心项与三正式工作流 PG+PI E2E 均已 PASS。test 分支合并前建议配置 CI `TEST_DATABASE_URL` + `RUNNER_AUTH_TOKEN` 门禁。 ## 发布门禁(test → main) diff --git a/.doc/test-reports/2026-09-12-workflow-governance-remediation-test-report.md b/.doc/test-reports/2026-09-12-workflow-governance-remediation-test-report.md index 580331ba..29b865be 100644 --- a/.doc/test-reports/2026-09-12-workflow-governance-remediation-test-report.md +++ b/.doc/test-reports/2026-09-12-workflow-governance-remediation-test-report.md @@ -95,16 +95,38 @@ TEST_DATABASE_URL='postgres://app:test_secret_db_pass@127.0.0.1:5434/agent_syste **评价判定**: PASS — 正式工作流 `main` + 真实 pi-agent intake 三步骤 + DocGov 批准 + 阶段推进 +### 5.6 PI Agent 产品经理工作流(product_manager + system_reqs) + +**命令**: +```bash +RUNNER_AUTH_TOKEN='dev_runner_secret_token' \ +TEST_DATABASE_URL='postgres://app:test_secret_db_pass@127.0.0.1:5434/agent_system_test' \ + go test ./internal/server/... -run TestWorkflowGovernance_PiAgent_PG_ProductManager_FullChain -count=1 -timeout 1h -v +``` + +**实际输出**: +``` +✓ [1. 项目创建] id=15 code=pg-piagent-产-f61d0a40 wf=product_manager +✓ [2. 工作流激活] instance_id=11 +✓ [3. pi-agent system_reqs] doc=doc_proj15_system_reqs title=系统需求规格书 elapsed=18m54s +✓ [4. 文档批准] stage ready hook 已触发 +✓ [5. 阶段推进] PG + pi-agent 治理全链路完成 wf=product_manager +--- PASS: TestWorkflowGovernance_PiAgent_PG_ProductManager_FullChain (1134.40s) +``` + +**评价判定**: PASS — 单阶段完成后 `lifecycle_status=planning`;上下文快照经 bizdev Dialogue 集成注入 + ## 六、 自动化测试套件汇总 (Test Suite Execution) ``` go test ./internal/projectinstance/... ./internal/coordinator/... ./internal/projectcontext/... \ ./internal/ctxmgr/... ./internal/bizdev/... -count=1 -TEST_DATABASE_URL=... go test ./internal/server/... -run 'TestE2E_WorkflowGovernance|TestWorkflowGovernance_PG' -count=1 +TEST_DATABASE_URL=... RUNNER_AUTH_TOKEN=... go test ./internal/server/... \ + -run 'TestE2E_WorkflowGovernance|TestWorkflowGovernance' -count=1 -timeout 2h ``` ## 七、 测试结论与后续优化建议 (Conclusion & Next Steps) -- Phase A–C 核心路径、PG 治理 E2E 与 PI Agent 真实全链路均已 PASS。 -- 仍待:`product_manager` 正式工作流单独 PG+PI 链路(可选补测)后合 main。 +- Phase A–C 核心路径与三正式工作流(`main`/`product_manager`/`test_manager`)PG+PI 全链路均已 PASS。 +- 建议 CI 配置 `TEST_DATABASE_URL` + `RUNNER_AUTH_TOKEN` 作为 test→main 合并门禁。 - 建议每周运行 `scripts/fix-completed-instance-lifecycle.sql` dry-run 检查历史不一致。 diff --git a/internal/server/workflow_governance_piagent_pg_test.go b/internal/server/workflow_governance_piagent_pg_test.go index bb402761..d60f4653 100644 --- a/internal/server/workflow_governance_piagent_pg_test.go +++ b/internal/server/workflow_governance_piagent_pg_test.go @@ -52,11 +52,17 @@ func setupPGPiAgentServer(t *testing.T, pool *pgxpool.Pool) (http.Handler, *serv return srv.Routes(), srv } -// TestWorkflowGovernance_PiAgent_PG_FullChain -// PG 治理链 + pi-agent 真实 Agent:activate → pi-agent intake → approve → advance -// -// go test ./internal/server/ -run TestWorkflowGovernance_PiAgent_PG -timeout 1h -v -func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) { +type piAgentGovernanceCase struct { + WfKey string + TaskKey string + StageKey string + ProjectName string + ExpectPlanningDone bool + Materials []bizdev.MaterialInput +} + +func runPiAgentGovernanceE2E(t *testing.T, spec piAgentGovernanceCase) { + t.Helper() if testing.Short() { t.Skip("skipping slow pi-agent PG E2E in short mode") } @@ -76,8 +82,7 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) { defer ts.Close() client := &http.Client{Timeout: 50 * time.Minute} - wfKey := workflowdef.OfficialKeyMain - seedOfficialWorkflowManifest(t, pool, wfKey) + seedOfficialWorkflowManifest(t, pool, spec.WfKey) creatorToken := loginTestUser(t, handler, "zhanggong", "123456") reviewerToken := loginTestUser(t, handler, "lizhuguan", "123456") @@ -86,9 +91,8 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) { reviewerID := fetchAuthUserID(t, handler, reviewerToken) approverID := fetchAuthUserID(t, handler, approverToken) - // HTTP 创建中文名项目(验证 UTF-8 修复) createBody, _ := json.Marshal(projectworkflow.CreateProjectInput{ - Name: "PG PiAgent 治理链中文项目", + Name: spec.ProjectName, DeviceType: "Ultrasonic Device", IntendedUse: "Tissue resection", TargetMarket: "NMPA Class III", @@ -106,10 +110,9 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) { } t.Cleanup(func() { cleanupProject(t, pool, proj.ID) }) projectID := proj.ID - t.Logf("✓ [1. 项目创建] id=%d code=%s", projectID, proj.Code) + t.Logf("✓ [1. 项目创建] id=%d code=%s wf=%s", projectID, proj.Code, spec.WfKey) - // 规划 + 激活 - reqDefault := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/projects/%d/workflow-plan/default?workflow_key=%s&version=0", projectID, wfKey), nil) + reqDefault := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/projects/%d/workflow-plan/default?workflow_key=%s&version=0", projectID, spec.WfKey), nil) withBearerAuth(reqDefault, creatorToken) recDefault := httptest.NewRecorder() handler.ServeHTTP(recDefault, reqDefault) @@ -157,10 +160,9 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) { instanceID := activated.Instance.ID t.Logf("✓ [2. 工作流激活] instance_id=%d", instanceID) - // pi-agent 执行 intake(默认三步骤 WBS,工作区挂载 data/*/workspaces 供 runner 读取) taskStart := time.Now() taskBody, _ := json.Marshal(server.TaskRunRequestDTO{ - TaskKey: "intake", + TaskKey: spec.TaskKey, ProjectName: proj.Name, DeviceType: proj.DeviceType, IntendedUse: "Tissue resection", @@ -176,10 +178,7 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) { "intended_use": "Tissue resection", "target_market": "NMPA Class III", }, - Materials: []bizdev.MaterialInput{{ - Filename: "project_brief.md", - Content: []byte("# 项目简报\n\n- 产品:超声软组织手术系统\n- 目标:NMPA III 类注册路径摸底\n- 关键需求:双频刀头、灌注与负压吸引\n"), - }}, + Materials: spec.Materials, }) reqTask, _ := http.NewRequest(http.MethodPost, ts.URL+"/api/v1/tasks/run", bytes.NewReader(taskBody)) withBearerAuth(reqTask, creatorToken) @@ -194,26 +193,20 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) { t.Fatalf("pi-agent task HTTP %d: %s", respTask.StatusCode, buf.String()) } var taskRes struct { - Success bool `json:"success"` - DocRecord *struct { - DocID string `json:"doc_id"` - } `json:"doc_record"` + Success bool `json:"success"` + DocRecord *struct{ DocID string `json:"doc_id"` } `json:"doc_record"` ArtifactTitle string `json:"artifact_title"` } _ = json.NewDecoder(respTask.Body).Decode(&taskRes) if !taskRes.Success { - t.Fatal("pi-agent intake task failed") + t.Fatalf("pi-agent %s task failed", spec.TaskKey) } - docID := "" - if taskRes.DocRecord != nil { + docID := fmt.Sprintf("doc_proj%d_%s", projectID, spec.TaskKey) + if taskRes.DocRecord != nil && taskRes.DocRecord.DocID != "" { docID = taskRes.DocRecord.DocID } - if docID == "" { - docID = fmt.Sprintf("doc_proj%d_intake", projectID) - } - t.Logf("✓ [3. pi-agent intake] doc=%s title=%s elapsed=%s", docID, taskRes.ArtifactTitle, time.Since(taskStart).Round(time.Second)) + t.Logf("✓ [3. pi-agent %s] doc=%s title=%s elapsed=%s", spec.TaskKey, docID, taskRes.ArtifactTitle, time.Since(taskStart).Round(time.Second)) - // 文档批准链 docEngine := srv.BizdevService().DocGov() _ = docEngine.MarkDocumentContentUpdated(ctx, docID, "hash_pi_pg", false) _, _ = docEngine.SubmitForReview(ctx, docID, creatorID, "author", "Author") @@ -224,7 +217,6 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) { } t.Log("✓ [4. 文档批准] stage ready hook 已触发") - // 通知 + 阶段推进 reqNotify := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/projects/%d/notifications", projectID), nil) withBearerAuth(reqNotify, creatorToken) recNotify := httptest.NewRecorder() @@ -245,14 +237,54 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) { } advanceBody, _ := json.Marshal(projectinstance.AdvanceStageInput{ - StageKey: "V0.1_triage", ExpectedConfigRevision: activated.Instance.ConfigRevision, + StageKey: spec.StageKey, ExpectedConfigRevision: activated.Instance.ConfigRevision, }) - reqAdvance := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/api/v1/workflow-instances/%d/stages/V0.1_triage/advance", instanceID), bytes.NewReader(advanceBody)) + reqAdvance := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/api/v1/workflow-instances/%d/stages/%s/advance", instanceID, spec.StageKey), bytes.NewReader(advanceBody)) withBearerAuth(reqAdvance, creatorToken) recAdvance := httptest.NewRecorder() handler.ServeHTTP(recAdvance, reqAdvance) if recAdvance.Code != http.StatusOK { t.Fatalf("advance: %d %s", recAdvance.Code, recAdvance.Body.String()) } - t.Log("✓ [5. 阶段推进] PG + pi-agent 治理全链路完成") + if spec.ExpectPlanningDone { + reqSettings := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/projects/%d/settings", projectID), nil) + withBearerAuth(reqSettings, creatorToken) + recSettings := httptest.NewRecorder() + handler.ServeHTTP(recSettings, reqSettings) + var settings projectinstance.ProjectSettings + _ = json.Unmarshal(recSettings.Body.Bytes(), &settings) + if settings.LifecycleStatus != projectinstance.LifecyclePlanning { + t.Fatalf("expected planning after completion, got %q", settings.LifecycleStatus) + } + } + t.Logf("✓ [5. 阶段推进] PG + pi-agent 治理全链路完成 wf=%s", spec.WfKey) +} + +// TestWorkflowGovernance_PiAgent_PG_FullChain — 正式 main + pi-agent intake +func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) { + runPiAgentGovernanceE2E(t, piAgentGovernanceCase{ + WfKey: workflowdef.OfficialKeyMain, + TaskKey: "intake", + StageKey: "V0.1_triage", + ProjectName: "PG PiAgent 治理链中文项目", + Materials: []bizdev.MaterialInput{{ + Filename: "project_brief.md", + Content: []byte("# 项目简报\n\n- 产品:超声软组织手术系统\n- 目标:NMPA III 类注册路径摸底\n- 关键需求:双频刀头、灌注与负压吸引\n"), + }}, + }) +} + +// TestWorkflowGovernance_PiAgent_PG_ProductManager_FullChain — 正式 product_manager + pi-agent system_reqs +func TestWorkflowGovernance_PiAgent_PG_ProductManager_FullChain(t *testing.T) { + runPiAgentGovernanceE2E(t, piAgentGovernanceCase{ + WfKey: workflowdef.OfficialKeyProductManager, + TaskKey: "system_reqs", + StageKey: "requirements_baseline", + ProjectName: "PG PiAgent 产品经理治理链", + ExpectPlanningDone: true, + Materials: []bizdev.MaterialInput{{ + Filename: "intake_summary.md", + Content: []byte("# 立项输入\n\n| REQ-01 | 双频超声刀头 24/35kHz | 高 |\n| REQ-02 | 灌注流量 10-50 mL/min | 高 |\n| REQ-03 | NMPA III 类注册路径 | 高 |\n"), + }}, + }) }