From 0ab4a8bd2df84597030de59cf89297d99c153871 Mon Sep 17 00:00:00 2001 From: weichen Date: Fri, 25 Sep 2026 14:37:11 +0000 Subject: [PATCH] =?UTF-8?q?feat(server):=20=E6=8E=A5=E7=BA=BF=E5=AF=B9?= =?UTF-8?q?=E8=AF=9D=20workflow.run=20=E5=B9=B6=E8=A1=A5=20P0=20=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit server 侧窄端口适配 projectworkflow,消除 chat→projectworkflow 边界违规;记录单测与 arch-check 证据,PI 全链路待补证。 --- ...6-09-25-dialogue-office-p0-verification.md | 51 +++++++++++++++++++ internal/chat/action_executor.go | 31 +++++++---- internal/chat/action_executor_p0_test.go | 15 +++--- internal/server/chat_workflow_adapter.go | 36 +++++++++++++ internal/server/server.go | 1 + 5 files changed, 114 insertions(+), 20 deletions(-) create mode 100644 .doc/remediation/2026-09-25-dialogue-office-p0-verification.md create mode 100644 internal/server/chat_workflow_adapter.go diff --git a/.doc/remediation/2026-09-25-dialogue-office-p0-verification.md b/.doc/remediation/2026-09-25-dialogue-office-p0-verification.md new file mode 100644 index 00000000..12b5ec7c --- /dev/null +++ b/.doc/remediation/2026-09-25-dialogue-office-p0-verification.md @@ -0,0 +1,51 @@ +--- +title: "对话办公 P0 验证记录" +created: 2026-09-25 +updated: 2026-09-25 +owner: Cursor +task_id: dialogue-office-p0-2026-09-25 +status: done +policy_revision: cursor-governance-2026-09-24-r1 +--- + +# 对话办公 P0 验证记录 + +配套:[整改计划](./2026-09-25-dialogue-office-expansion-plan.md) P0 · [研究](../audit/2026-09-25-dialogue-office-capability-research.md) + +## 范围与授权 + +- 写路径:`internal/coordinator`、`internal/chat`、`internal/server`(Workflow 窄端口适配) +- 基线:建场 `7cfd6851`;本记录对应 feature tip 以提交收据为准 +- **产品 PI 全链路**:本轮 **未运行**(`.doc/test-reports` 被并行任务占用;未获独立 PI 环境授权)。按验证契约「Go/接口」层以单测 + arch-check 验收;PI 对话写路径标为 **待补证**,不冒充 PASS。 + +## 验收对照(计划清单) + +| 项 | 判定 | 证据 | +|----|------|------| +| 五处无「已成功」且状态未变 | PASS(单测) | `p0_fake_success_test.go`;假成功文案已从 handler 移除 | +| 失败路径明确 RejectReason | PASS | `WORKFLOW_SWITCH_UI_ONLY`、`SUBMIT_STATE_DENIED`、`DOC_RESOLVE_FAILED` | +| approve/reject/final 确认链不坏 | PASS | `go test ./internal/coordinator/` 全套;既有 DocGov approve/reject 用例 | +| `go test ./internal/coordinator/... ./internal/chat/...` | PASS | 见下方命令输出(跳过无关 flaky SemanticMemory) | +| 真实对话写路径 PI 报告 | 未运行 | 待获授权环境与 test-reports 写路径后补 | + +## 实际命令与结果 + +```text +$ go test ./internal/coordinator/ ./internal/chat/ ./internal/server/ -count=1 -skip 'SemanticMemory|PiAgent|RealPI|E2E|PG_' +ok chat-agent-system/internal/coordinator +ok chat-agent-system/internal/chat +ok chat-agent-system/internal/server + +$ ./scripts/arch-check.sh --mode block +arch-check block: production gate passed +``` + +## 接线说明 + +- 生产会话:`server.go` → `adaptChatWorkflow(s.pwSvc)` → `chat.WorkflowRunner` +- `chat` **不** import `projectworkflow`(边界门禁) +- `BindChatSession`(`internal/app`)当前无调用方;`u9u12-keys-rollout` 占用 app 时未改 + +## 结论 + +P0 代码与 Go/接口验收完成;**不合入 dev**(待用户集成指令)。产品 PI 对话写路径补证为收口后可选增强,不阻断本切片 code-complete。 diff --git a/internal/chat/action_executor.go b/internal/chat/action_executor.go index 8d786667..c92f54e8 100644 --- a/internal/chat/action_executor.go +++ b/internal/chat/action_executor.go @@ -10,23 +10,33 @@ import ( "chat-agent-system/internal/contracts/vocabulary" "chat-agent-system/internal/coordinator" "chat-agent-system/internal/docgov" - "chat-agent-system/internal/projectworkflow" "chat-agent-system/internal/taskrun" ) -// WorkflowRunner creates workflow run records (narrow port; composition injects projectworkflow.Service). +// WorkflowStartRequest is the narrow DTO for starting a workflow run from dialogue. +type WorkflowStartRequest struct { + RunID string + ProjectID int64 + WorkflowKey string + StartedBy int64 + Status string +} + +// WorkflowStartResult is the narrow receipt after CreateWorkflowRun. +type WorkflowStartResult struct { + RunID string +} + +// WorkflowRunner creates workflow run records (composition adapts projectworkflow.Service). type WorkflowRunner interface { - CreateWorkflowRun(ctx context.Context, run *projectworkflow.WorkflowRun) (*projectworkflow.WorkflowRun, error) + CreateWorkflowRun(ctx context.Context, req WorkflowStartRequest) (*WorkflowStartResult, error) } // ActionServices 对话层执行 PendingAction 所需的后端服务。 // 具体 chatflow / intentflow / taskrun 实现由组合根经窄端口注入。 // -// 合入接线(被 server-image-field-closeout-r4 占用 app/server 时延后): -// -// sess.Actions.Workflow = rt.PW // 或 s.pwSvc -// -// 未接线时 workflow.run 会诚实失败(「workflow service not available」),不会假成功。 +// 生产主路径(server 会话装配)已接线:Workflow = AdaptChatWorkflow(s.pwSvc)。 +// BindChatSession(internal/app)若启用亦应适配 rt.PW。 type ActionServices struct { DocGov *docgov.GovernanceEngine TaskRuns TaskStarter @@ -193,12 +203,11 @@ func (s *Session) executeAction(ctx context.Context, action *coordinator.Pending key = "main" } runID := fmt.Sprintf("wf_run_%d", time.Now().UnixNano()) - startedBy := uid - created, err := s.Actions.Workflow.CreateWorkflowRun(ctx, &projectworkflow.WorkflowRun{ + created, err := s.Actions.Workflow.CreateWorkflowRun(ctx, WorkflowStartRequest{ RunID: runID, ProjectID: s.ProjectID, WorkflowKey: key, - StartedBy: &startedBy, + StartedBy: uid, Status: "running", }) if err != nil { diff --git a/internal/chat/action_executor_p0_test.go b/internal/chat/action_executor_p0_test.go index 748cae37..6ed50d5b 100644 --- a/internal/chat/action_executor_p0_test.go +++ b/internal/chat/action_executor_p0_test.go @@ -8,20 +8,19 @@ import ( "chat-agent-system/internal/config" "chat-agent-system/internal/coordinator" "chat-agent-system/internal/docgov" - "chat-agent-system/internal/projectworkflow" ) type stubWorkflowRunner struct { - last *projectworkflow.WorkflowRun + last WorkflowStartRequest err error } -func (s *stubWorkflowRunner) CreateWorkflowRun(_ context.Context, run *projectworkflow.WorkflowRun) (*projectworkflow.WorkflowRun, error) { +func (s *stubWorkflowRunner) CreateWorkflowRun(_ context.Context, req WorkflowStartRequest) (*WorkflowStartResult, error) { if s.err != nil { return nil, s.err } - s.last = run - return run, nil + s.last = req + return &WorkflowStartResult{RunID: req.RunID}, nil } func TestExecuteAction_DocGovClaimAndSubmit(t *testing.T) { @@ -80,16 +79,14 @@ func TestExecuteAction_WorkflowRun(t *testing.T) { User: coordinator.UserInfo{ID: "7", Name: "运维"}, Project: coordinator.ProjectInfo{ActiveWorkflowKey: "main"}, } - res, err := sess.executeAction(ctxBackground(), &coordinator.PendingAction{Kind: "workflow.run", WorkflowKey: "main"}, userCtx) + res, err := sess.executeAction(context.Background(), &coordinator.PendingAction{Kind: "workflow.run", WorkflowKey: "main"}, userCtx) if err != nil { t.Fatalf("workflow.run: %v", err) } - if stub.last == nil || stub.last.ProjectID != 42 || stub.last.WorkflowKey != "main" { + if stub.last.ProjectID != 42 || stub.last.WorkflowKey != "main" || stub.last.StartedBy != 7 { t.Fatalf("unexpected run %+v", stub.last) } if res.RunID == "" { t.Fatal("expected run id") } } - -func ctxBackground() context.Context { return context.Background() } diff --git a/internal/server/chat_workflow_adapter.go b/internal/server/chat_workflow_adapter.go new file mode 100644 index 00000000..e53f8deb --- /dev/null +++ b/internal/server/chat_workflow_adapter.go @@ -0,0 +1,36 @@ +package server + +import ( + "context" + + "chat-agent-system/internal/chat" + "chat-agent-system/internal/projectworkflow" +) + +// chatWorkflowAdapter adapts projectworkflow.Service to chat.WorkflowRunner +// without letting the chat package import projectworkflow (module boundary). +type chatWorkflowAdapter struct { + svc *projectworkflow.Service +} + +func adaptChatWorkflow(svc *projectworkflow.Service) chat.WorkflowRunner { + if svc == nil { + return nil + } + return chatWorkflowAdapter{svc: svc} +} + +func (a chatWorkflowAdapter) CreateWorkflowRun(ctx context.Context, req chat.WorkflowStartRequest) (*chat.WorkflowStartResult, error) { + startedBy := req.StartedBy + created, err := a.svc.CreateWorkflowRun(ctx, &projectworkflow.WorkflowRun{ + RunID: req.RunID, + ProjectID: req.ProjectID, + WorkflowKey: req.WorkflowKey, + StartedBy: &startedBy, + Status: req.Status, + }) + if err != nil { + return nil, err + } + return &chat.WorkflowStartResult{RunID: created.RunID}, nil +} diff --git a/internal/server/server.go b/internal/server/server.go index d02a74a6..6d114e85 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -669,6 +669,7 @@ func (s *Server) getOrCreateSession( DocGov: s.gov, TaskRuns: app.AdaptChatTaskStarter(s.taskRuns), IntentPlans: app.AdaptChatIntentPlans(s.intentPlans), + Workflow: adaptChatWorkflow(s.pwSvc), Environment: env, } if s.vaultIngest != nil {