test(governance): PI Agent PG 全链路实测通过并固化运行条件

修复 runner 鉴权与 data/dev 工作区挂载,使用正式 main 工作流完成 intake→批准→推进。
This commit is contained in:
weichen
2026-09-13 02:50:23 +00:00
committed by weichen
parent d6f5e0e28a
commit 784d852ac0
3 changed files with 75 additions and 39 deletions
@@ -69,6 +69,32 @@ PASS ok chat-agent-system/internal/server 0.514s
**评价判定**: PASS — 正式工作流 `test_manager` 末阶段完成→`planning`;正式工作流 `main` 激活/DocGov 批准/阶段推进链通过
### 5.5 PI Agent 真实全链路(隔离 test 库 + MiniMax-M3)
**环境**:
- `TEST_DATABASE_URL` → `agent_system_test@127.0.0.1:5434`
- `RUNNER_AUTH_TOKEN` → dev runner(`18865/health` 200)
- 工作区挂载 → `data/dev/workspaces`(与 pi-agent 容器共享)
**命令**:
```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_FullChain -count=1 -timeout 1h -v
```
**实际输出**:
```
✓ [1. 项目创建] id=10 code=pg-piagent-治-dbbbb9f7
✓ [2. 工作流激活] instance_id=6
✓ [3. pi-agent intake] doc=doc_proj10_intake title=用户初始需求收集表 elapsed=6m28s
✓ [4. 文档批准] stage ready hook 已触发
✓ [5. 阶段推进] PG + pi-agent 治理全链路完成
--- PASS: TestWorkflowGovernance_PiAgent_PG_FullChain (387.93s)
```
**评价判定**: PASS — 正式工作流 `main` + 真实 pi-agent intake 三步骤 + DocGov 批准 + 阶段推进
## 六、 自动化测试套件汇总 (Test Suite Execution)
```
@@ -79,6 +105,6 @@ TEST_DATABASE_URL=... go test ./internal/server/... -run 'TestE2E_WorkflowGovern
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
- Phase A–C 核心路径与 PG 治理 E2E 已通过;`test_manager`/`main` 正式工作流在隔离 test 库验证 PASS。
- 仍待:三流程 + 真实 PI Agent(MiniMax-M3)全链路实测(`TestWorkflowGovernance_PiAgent_PG_FullChain`)写入本报告后再合 main。
- Phase A–C 核心路径、PG 治理 E2E 与 PI Agent 真实全链路均已 PASS。
- 仍待:`product_manager` 正式工作流单独 PG+PI 链路(可选补测)后合 main。
- 建议每周运行 `scripts/fix-completed-instance-lifecycle.sql` dry-run 检查历史不一致。
+24
View File
@@ -9,6 +9,7 @@ import (
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
@@ -117,6 +118,29 @@ func envOr(key, fallback string) string {
return fallback
}
func piAgentSharedDataDir(t *testing.T) string {
t.Helper()
if d := strings.TrimSpace(os.Getenv("DATA_DIR")); d != "" {
return d
}
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("getwd: %v", err)
}
for _, rel := range []string{".", "..", "../..", "../../.."} {
base := filepath.Clean(filepath.Join(cwd, rel))
for _, envName := range []string{"dev", "test"} {
candidate := filepath.Join(base, "data", envName)
ws := filepath.Join(candidate, "workspaces")
if st, statErr := os.Stat(ws); statErr == nil && st.IsDir() {
return candidate
}
}
}
t.Skip("shared pi-agent DATA_DIR not found (mount data/test/workspaces or data/dev/workspaces)")
return ""
}
func seedOfficialWorkflowManifest(t *testing.T, pool *pgxpool.Pool, workflowKey string) {
t.Helper()
filename, ok := workflowdef.V2ManifestFiles[workflowKey]
@@ -17,7 +17,6 @@ import (
"chat-agent-system/internal/projectinstance"
"chat-agent-system/internal/projectworkflow"
"chat-agent-system/internal/server"
"chat-agent-system/internal/squad"
"chat-agent-system/internal/workflowdef"
)
@@ -34,10 +33,17 @@ func piAgentRunnerHealthy() bool {
func setupPGPiAgentServer(t *testing.T, pool *pgxpool.Pool) (http.Handler, *server.Server) {
t.Helper()
cfg := configFromTestDatabaseURL(t)
cfg.DataDir = piAgentSharedDataDir(t)
cfg.AgentBackend = "pi-agent"
cfg.PiAgentRunnerURL = "http://127.0.0.1:18865"
cfg.AgentTimeout = 60 * time.Minute
cfg.PiAgentRunnerURL = envOr("PI_AGENT_RUNNER_URL", "http://127.0.0.1:18865")
cfg.PiAgentRunnerAuthToken = envOr("PI_AGENT_RUNNER_AUTH_TOKEN", envOr("RUNNER_AUTH_TOKEN", ""))
cfg.PiAgentModel = "MiniMax-M3"
cfg.PiAgentProvider = "minimax"
if cfg.PiAgentRunnerAuthToken == "" {
t.Skip("RUNNER_AUTH_TOKEN not set; skipping pi-agent PG E2E")
}
mergePiAgentE2EKbConfig(cfg)
svc := bizdev.MustNewService(t, cfg)
if svc.Pool() == nil {
t.Skip("bizdev service not connected to postgres")
@@ -70,33 +76,8 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) {
defer ts.Close()
client := &http.Client{Timeout: 50 * time.Minute}
wfSvc := workflowdef.NewService(pool, nil)
wfKey := "pg_pi_p5_" + time.Now().Format("150405.000")
_, ver, err := wfSvc.CreateDefinition(ctx, workflowdef.CreateDefinitionInput{
Key: wfKey, Name: "PG PiAgent Governance WF",
Definition: workflowdef.VersionDefinition{
SchemaVersion: 1,
Stages: []workflowdef.StageDef{{
Key: "biz_intake", Name: "Intake", Position: 0,
DocTasks: []workflowdef.DocTaskDef{{
Key: "intake", Required: true, Blocking: true, DefaultEnabled: true,
OutputDocType: "intake", OutputDocTitle: "Customer Intake",
}},
}, {
Key: "biz_feasibility", Name: "Feasibility", Position: 1, Optional: true,
DocTasks: []workflowdef.DocTaskDef{{
Key: "feasibility_reg", Required: false, Blocking: false, DefaultEnabled: true,
OutputDocType: "feasibility", OutputDocTitle: "Regulatory Path",
}},
}},
},
}, 1)
if err != nil {
t.Fatalf("create workflow def: %v", err)
}
if _, err := wfSvc.PublishVersion(ctx, ver.WorkflowID, ver.Version, 1); err != nil {
t.Fatalf("publish workflow: %v", err)
}
wfKey := workflowdef.OfficialKeyMain
seedOfficialWorkflowManifest(t, pool, wfKey)
creatorToken := loginTestUser(t, handler, "zhanggong", "123456")
reviewerToken := loginTestUser(t, handler, "lizhuguan", "123456")
@@ -128,7 +109,7 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) {
t.Logf("✓ [1. 项目创建] id=%d code=%s", projectID, proj.Code)
// 规划 + 激活
reqDefault := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/projects/%d/workflow-plan/default?workflow_key=%s&version=1", projectID, wfKey), nil)
reqDefault := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/projects/%d/workflow-plan/default?workflow_key=%s&version=0", projectID, wfKey), nil)
withBearerAuth(reqDefault, creatorToken)
recDefault := httptest.NewRecorder()
handler.ServeHTTP(recDefault, reqDefault)
@@ -176,7 +157,7 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) {
instanceID := activated.Instance.ID
t.Logf("✓ [2. 工作流激活] instance_id=%d", instanceID)
// pi-agent 执行 intake(精简单步 WBS)
// pi-agent 执行 intake(默认三步骤 WBS,工作区挂载 data/*/workspaces 供 runner 读取)
taskStart := time.Now()
taskBody, _ := json.Marshal(server.TaskRunRequestDTO{
TaskKey: "intake",
@@ -189,10 +170,15 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) {
AuthorID: creatorID,
ReviewerID: reviewerID,
ApproverID: approverID,
CustomSteps: []squad.StepSpec{{
ID: "step_01_intake", Role: "writer", Title: "Intake snapshot",
SubWorkspace: "tasks/01_intake", Mode: "execute",
Accept: []string{"Deliver output.md with project intake summary"},
Fields: map[string]string{
"customer_name": "PG PiAgent 治理链客户",
"device_type": proj.DeviceType,
"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"),
}},
})
reqTask, _ := http.NewRequest(http.MethodPost, ts.URL+"/api/v1/tasks/run", bytes.NewReader(taskBody))
@@ -259,9 +245,9 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) {
}
advanceBody, _ := json.Marshal(projectinstance.AdvanceStageInput{
StageKey: "biz_intake", ExpectedConfigRevision: activated.Instance.ConfigRevision,
StageKey: "V0.1_triage", ExpectedConfigRevision: activated.Instance.ConfigRevision,
})
reqAdvance := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/api/v1/workflow-instances/%d/stages/biz_intake/advance", instanceID), bytes.NewReader(advanceBody))
reqAdvance := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/api/v1/workflow-instances/%d/stages/V0.1_triage/advance", instanceID), bytes.NewReader(advanceBody))
withBearerAuth(reqAdvance, creatorToken)
recAdvance := httptest.NewRecorder()
handler.ServeHTTP(recAdvance, reqAdvance)