This is an automated email from the ASF dual-hosted git repository.

AlexStocks pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git


The following commit(s) were added to refs/heads/develop by this push:
     new 144d127e0 fix: update dubbogo CLI templates for Dubbo-Go 3.3 (#3587)
144d127e0 is described below

commit 144d127e099545880756f9d92efd15942f744a8a
Author: Xin.Zh <[email protected]>
AuthorDate: Sat Aug 1 14:16:23 2026 +0800

    fix: update dubbogo CLI templates for Dubbo-Go 3.3 (#3587)
    
    * fix: update dubbogo CLI templates for Dubbo-Go 3.3
    
    Regenerate the embedded Triple stubs for the current client and server 
APIs, update template dependencies, and compile generated projects in 
regression tests.
    
    Constraint: Limit the change to tools/dubbogo-cli templates, generated 
fixtures, dependency metadata, tests, and matching README file names.
    
    Confidence: High; generator-to-golden equality, direct generated-project 
compilation, module tests, vet, tidy-diff, formatting, and staged diff checks 
passed.
    
    Scope-risk: Generated applications now require Go 1.24 and Dubbo-Go v3.3.1; 
Linux was not locally exercised because WSL has no Go toolchain.
    
    Red: TestGeneratedProjectsCompile failed on the removed logger package, 
legacy NewGreeterClient signature, and legacy RegisterGreeterServer contract.
    
    Green: CLI, newApp, and newDemo tests and vet passed; both generated 
modules are tidy and compile against Dubbo-Go v3.3.1.
    
    Tested: go test ./... and go vet ./... in tools/dubbogo-cli, newApp, and 
newDemo; go mod tidy -diff for both generated modules; gofmt -l; git diff 
--cached --check.
    
    Not-tested: WSL/Linux because go is not installed in the available WSL 
environment.
    
    Co-authored-by: OmX <[email protected]>
    Signed-off-by: Xin.Zh <[email protected]>
    
    * fix: satisfy CLI template import formatting
    
    Group the generated project logger import according to the repository 
imports-formatter rules and keep generator sources synchronized with golden 
files.
    
    Constraint: Limit the change to the three affected generator templates and 
their three golden outputs.
    
    Confidence: High; the repository format steps produced no additional diff 
after the import grouping change.
    
    Scope-risk: Import layout only; generated application behavior and 
dependencies are unchanged.
    
    Red: The Linux CI Check Code Format step rewrote three golden files and 
failed make check-fmt.
    
    Green: The equivalent modernize, go fmt, imports-formatter, and CLI go fmt 
chain leaves no unstaged diff.
    
    Tested: go test ./... and go vet ./... in tools/dubbogo-cli; go vet ./... 
and go mod tidy -diff in both generated modules; git diff --cached --check.
    
    Not-tested: WSL/Linux locally because Go is not installed in WSL; GitHub 
Actions will provide the Linux confirmation.
    
    Co-authored-by: OmX <[email protected]>
    Signed-off-by: Xin.Zh <[email protected]>
    
    * docs: define PR 3587 reconciliation design
    
    Signed-off-by: Xin.Zh <[email protected]>
    
    * Document the approved PR 3587 implementation sequence
    
    Record the exact merge conflict policy, TDD steps, Linux E2E coverage, 
mutation proof, and race-safe push checks needed to reconcile the two CLI 
template pull requests.
    
    Constraint: Preserve the scaffold architecture already merged through PR 
3585
    
    Tested: plan placeholder scan and staged whitespace validation
    
    Co-authored-by: OmX <[email protected]>
    Signed-off-by: Xin.Zh <[email protected]>
    
    * Pin generated CLI projects to the stable Dubbo-Go release
    
    Replace the dated prerelease dependency in both generators and golden 
projects while retaining the removed-logger regression guard.
    
    Constraint: Keep the generated module language version at Go 1.25
    
    Tested: targeted generator version logger and golden consistency tests
    
    Related: Task 2/7
    
    Co-authored-by: OmX <[email protected]>
    Signed-off-by: Xin.Zh <[email protected]>
    
    * Make fresh CLI scaffolds self-contained under make test
    
    Generate protocol sources and tidy modules before compilation, then 
exercise that public workflow against newly generated app and demo projects.
    
    Constraint: Generated protobuf and Triple sources remain outside the 
templates
    
    Tested: DUBBOGO_CLI_E2E=1 generated-project compilation and golden 
consistency tests
    
    Related: Task 3/7
    
    Co-authored-by: OmX <[email protected]>
    Signed-off-by: Xin.Zh <[email protected]>
    
    * Recognize wrapped EOF errors in generated stream handlers
    
    Use errors.Is in both generator templates and golden outputs so wrapped 
end-of-stream errors terminate normally instead of being returned as failures.
    
    Constraint: Preserve the existing stream control flow outside EOF 
classification
    
    Tested: generated-output EOF assertions golden consistency and 
fresh-project compilation
    
    Related: Task 4/7
    
    Co-authored-by: OmX <[email protected]>
    Signed-off-by: Xin.Zh <[email protected]>
    
    * Gate CLI scaffold compilation in Linux CI
    
    Install the protocol generators used by fresh projects and run the explicit 
generated-project E2E target before the existing repository checks.
    
    Constraint: Ordinary CLI unit tests remain independent of a local protoc 
toolchain
    
    Tested: make generated-project target and environment-gated skip behavior
    
    Related: Task 5/7
    
    Co-authored-by: OmX <[email protected]>
    Signed-off-by: Xin.Zh <[email protected]>
    
    * Install protoc plugin from the locked module graph
    
    Resolve protoc-gen-go through the root go.mod and go.sum with -mod=readonly 
so the CI toolchain remains pinned to v1.34.2 without an inline version literal.
    
    Constraint: Keep the generated-project toolchain reproducible and satisfy 
SonarCloud githubactions:S8545 without suppressions
    
    Tested: isolated GOBIN install plus make -C tools/dubbogo-cli 
test-generated-projects
    
    Related: PR #3587 SonarCloud follow-up
    
    Co-authored-by: OmX <[email protected]>
    Signed-off-by: Xin.Zh <[email protected]>
    
    ---------
    
    Signed-off-by: Xin.Zh <[email protected]>
    Co-authored-by: OmX <[email protected]>
---
 .github/workflows/github-actions.yml               |  12 +
 .../plans/2026-08-01-unify-pr-3585-3587.md         | 792 +++++++++++++++++++++
 .../specs/2026-08-01-unify-pr-3585-3587-design.md  | 221 ++++++
 tools/dubbogo-cli/Makefile                         |   7 +-
 tools/dubbogo-cli/cmd/gen_test.go                  |  96 +++
 .../cmd/testGenCode/template/newApp/Makefile       |   2 +-
 .../cmd/testGenCode/template/newApp/go.mod         |   2 +-
 .../template/newApp/pkg/service/service.go         |   3 +-
 .../cmd/testGenCode/template/newDemo/Makefile      |   2 +-
 .../template/newDemo/go-server/cmd/server.go       |   3 +-
 .../cmd/testGenCode/template/newDemo/go.mod        |   2 +-
 tools/dubbogo-cli/generator/application/gomod.go   |   2 +-
 .../dubbogo-cli/generator/application/makefile.go  |   2 +-
 tools/dubbogo-cli/generator/application/pkg.go     |   3 +-
 tools/dubbogo-cli/generator/sample/gen_server.go   |   3 +-
 tools/dubbogo-cli/generator/sample/makefile.go     |   2 +-
 tools/dubbogo-cli/generator/sample/mod.go          |   2 +-
 17 files changed, 1143 insertions(+), 13 deletions(-)

diff --git a/.github/workflows/github-actions.yml 
b/.github/workflows/github-actions.yml
index a8de57c2b..ca2c8a0ce 100644
--- a/.github/workflows/github-actions.yml
+++ b/.github/workflows/github-actions.yml
@@ -54,6 +54,18 @@ jobs:
           restore-keys: |
             ${{ runner.os }}-go-
 
+      - name: Install generated-project toolchain
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y protobuf-compiler
+          go install -mod=readonly google.golang.org/protobuf/cmd/protoc-gen-go
+          cd tools/protoc-gen-go-triple
+          go install .
+          echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
+
+      - name: Test generated CLI projects
+        run: make -C tools/dubbogo-cli test-generated-projects
+
       - name: Check Code Format
         run: make check-fmt
 
diff --git a/docs/superpowers/plans/2026-08-01-unify-pr-3585-3587.md 
b/docs/superpowers/plans/2026-08-01-unify-pr-3585-3587.md
new file mode 100644
index 000000000..1e6d8ba66
--- /dev/null
+++ b/docs/superpowers/plans/2026-08-01-unify-pr-3585-3587.md
@@ -0,0 +1,792 @@
+# PR #3587 统一 #3585/#3587 实现计划
+
+> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 
superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。
+
+**目标:** 将 `develop@a62a44a266c63c00e9d22a2c018b4fdccae1dab1` 合入 PR 
#3587,并在不恢复内嵌生成文件的前提下补齐稳定依赖、fresh scaffold 编译、EOF 和 CI 回归。
+
+**架构:** 合并冲突先全部收敛到 #3585 已进入 develop 的脚手架树,使合并 commit 的代码树相对 develop 
只多设计/计划文档;随后用 TDD 分别加入稳定模块版本、真实 `make test` 生成项目验证和包装 EOF 处理。E2E 
测试通过显式环境变量启用,普通单测不要求本地安装 protoc 工具链,GitHub Linux CI 则必须安装并执行完整链路。
+
+**技术栈:** Go 1.25、GNU Make、Protocol Buffers `protoc`、`protoc-gen-go` 
v1.34.2、仓库内 `protoc-gen-go-triple`、GitHub Actions、PowerShell 与 WSL/Linux。
+
+---
+
+## 文件职责与变更结构
+
+- 
`docs/superpowers/specs/2026-08-01-unify-pr-3585-3587-design.md`:已经批准的设计约束,不再修改其语义。
+- `docs/superpowers/plans/2026-08-01-unify-pr-3585-3587.md`:本实施计划。
+- `tools/dubbogo-cli/cmd/gen_test.go`:generator/golden 一致性、旧 logger、稳定版本、fresh 
scaffold E2E 测试入口。
+- `tools/dubbogo-cli/generator/application/gomod.go`:newApp 的最小 `go.mod` 模板。
+- `tools/dubbogo-cli/generator/sample/mod.go`:newDemo 的最小 `go.mod` 模板。
+- `tools/dubbogo-cli/cmd/testGenCode/template/newApp/go.mod`:newApp golden 输出。
+- `tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go.mod`:newDemo golden 
输出。
+- `tools/dubbogo-cli/generator/application/makefile.go`:newApp 的公开 Makefile 模板。
+- `tools/dubbogo-cli/generator/sample/makefile.go`:newDemo 的公开 Makefile 模板。
+- `tools/dubbogo-cli/cmd/testGenCode/template/newApp/Makefile`:newApp Makefile 
golden 输出。
+- `tools/dubbogo-cli/cmd/testGenCode/template/newDemo/Makefile`:newDemo 
Makefile golden 输出。
+- `tools/dubbogo-cli/generator/application/pkg.go`:newApp 服务实现模板。
+- `tools/dubbogo-cli/generator/sample/gen_server.go`:newDemo 服务端实现模板。
+- 
`tools/dubbogo-cli/cmd/testGenCode/template/newApp/pkg/service/service.go`:newApp
 服务实现 golden 输出。
+- 
`tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-server/cmd/server.go`:newDemo
 服务端 golden 输出。
+- `tools/dubbogo-cli/Makefile`:本模块普通测试和显式 E2E target。
+- `.github/workflows/github-actions.yml`:Linux CI 安装协议生成工具链并运行 E2E。
+
+### 任务 1:合并 develop,并将冲突树收敛到 #3585 架构
+
+**文件:**
+- 保留 develop:`tools/dubbogo-cli/README.md`
+- 保留 develop:`tools/dubbogo-cli/README_CN.md`
+- 保留 develop:`tools/dubbogo-cli/cmd/gen_test.go`
+- 保留 develop:`tools/dubbogo-cli/cmd/testGenCode/template/newApp/cmd/app.go`
+- 保留 develop:`tools/dubbogo-cli/cmd/testGenCode/template/newApp/go.mod`
+- 保留 
develop:`tools/dubbogo-cli/cmd/testGenCode/template/newApp/pkg/service/service.go`
+- 保留 
develop:`tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-client/cmd/client.go`
+- 保留 
develop:`tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-server/cmd/server.go`
+- 保留 develop:`tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go.mod`
+- 保留 develop:`tools/dubbogo-cli/generator/application/api.go`
+- 保留 develop:`tools/dubbogo-cli/generator/application/cmd.go`
+- 保留 develop:`tools/dubbogo-cli/generator/application/gomod.go`
+- 保留 develop:`tools/dubbogo-cli/generator/application/pkg.go`
+- 保留 develop:`tools/dubbogo-cli/generator/sample/gen_client.go`
+- 保留 develop:`tools/dubbogo-cli/generator/sample/gen_server.go`
+- 保留 develop:`tools/dubbogo-cli/generator/sample/mod.go`
+- 删除:`tools/dubbogo-cli/cmd/testGenCode/template/newApp/api/api.triple.go`
+- 
删除:`tools/dubbogo-cli/cmd/testGenCode/template/newDemo/api/samples_api.triple.go`
+- 删除:`tools/dubbogo-cli/cmd/testGenCode/template/newApp/go.sum`
+- 删除:`tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go.sum`
+- 删除:`tools/dubbogo-cli/generator/internal/scaffold/gosum.go`
+- 删除:`tools/dubbogo-cli/generator/sample/api_tripe.go`
+
+- [ ] **步骤 1:重新固定远端 Head 和 develop**
+
+运行:
+
+```powershell
+gh pr view 3587 -R apache/dubbo-go --json 
state,baseRefName,baseRefOid,headRefName,headRefOid
+git ls-remote origin refs/heads/develop 
refs/heads/codex/fix-cli-template-logger
+```
+
+预期:PR 仍为 `OPEN`,PR Head 仍为 `23e50e5152c58d197317351dc835865ce26242a1`;远端 
develop 为 `a62a44a266c63c00e9d22a2c018b4fdccae1dab1`。
+
+- [ ] **步骤 2:获取最新远端 refs**
+
+运行:
+
+```powershell
+git fetch --no-tags origin `
+  refs/heads/develop:refs/remotes/origin/develop `
+  
refs/heads/codex/fix-cli-template-logger:refs/remotes/origin/codex/fix-cli-template-logger
+```
+
+预期:本地 `origin/develop` 与 `origin/codex/fix-cli-template-logger` 分别等于上一步固定的 OID。
+
+- [ ] **步骤 3:开始不自动提交的 merge**
+
+运行:
+
+```powershell
+git merge --no-ff --no-commit origin/develop
+```
+
+预期:命令因 `dubbogo-cli` 重叠实现返回冲突,且 `git diff --name-only --diff-filter=U` 包含 
README、模板 go.mod、模板服务实现、generator 模板和 modify/delete 文件。
+
+- [ ] **步骤 4:把所有重叠实现恢复到 develop 版本**
+
+运行:
+
+```powershell
+git restore --source=origin/develop --staged --worktree -- `
+  tools/dubbogo-cli/README.md `
+  tools/dubbogo-cli/README_CN.md `
+  tools/dubbogo-cli/cmd/gen_test.go `
+  tools/dubbogo-cli/cmd/testGenCode/template/newApp/cmd/app.go `
+  tools/dubbogo-cli/cmd/testGenCode/template/newApp/go.mod `
+  tools/dubbogo-cli/cmd/testGenCode/template/newApp/pkg/service/service.go `
+  tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-client/cmd/client.go `
+  tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-server/cmd/server.go `
+  tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go.mod `
+  tools/dubbogo-cli/generator/application/api.go `
+  tools/dubbogo-cli/generator/application/cmd.go `
+  tools/dubbogo-cli/generator/application/gomod.go `
+  tools/dubbogo-cli/generator/application/pkg.go `
+  tools/dubbogo-cli/generator/sample/gen_client.go `
+  tools/dubbogo-cli/generator/sample/gen_server.go `
+  tools/dubbogo-cli/generator/sample/mod.go
+
+git rm -- `
+  tools/dubbogo-cli/cmd/testGenCode/template/newApp/api/api.triple.go `
+  tools/dubbogo-cli/cmd/testGenCode/template/newDemo/api/samples_api.triple.go 
`
+  tools/dubbogo-cli/cmd/testGenCode/template/newApp/go.sum `
+  tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go.sum `
+  tools/dubbogo-cli/generator/internal/scaffold/gosum.go `
+  tools/dubbogo-cli/generator/sample/api_tripe.go
+```
+
+预期:`git diff --name-only --diff-filter=U` 无输出;相对 `origin/develop` 
的暂存树只包含已经提交到当前分支的设计和计划文档。
+
+- [ ] **步骤 5:验证合并树没有恢复内嵌产物**
+
+运行:
+
+```powershell
+git diff --cached --name-status origin/develop
+rg -n 
'api\.triple\.go|samples_api\.triple\.go|generator/internal/scaffold/gosum' 
tools/dubbogo-cli
+git diff --check
+```
+
+预期:第一个命令只列 `docs/superpowers/specs/...` 和 `docs/superpowers/plans/...`;`rg` 
不应在 generator/golden 文件中找到被删除产物,退出码可以为 1;`git diff --check` 退出 0。
+
+- [ ] **步骤 6:提交 merge commit**
+
+运行:
+
+```powershell
+git commit -s `
+  -m "Synchronize PR 3587 with the merged scaffold architecture" `
+  -m "Merge the latest develop history while resolving all overlapping CLI 
scaffold files to the architecture already accepted through PR 3585." `
+  -m "Constraint: Do not restore embedded generated sources or template go.sum 
files" `
+  -m "Tested: staged tree matches origin/develop except the approved design 
and plan documents" `
+  -m "Co-authored-by: OmX <[email protected]>"
+git show -s --format='%H %P %s' HEAD
+```
+
+预期:commit 有两个父提交,其中一个祖先链包含 `a62a44a266c63c00e9d22a2c018b4fdccae1dab1`。
+
+### 任务 2:用失败测试固定稳定模块版本和旧 logger 防线
+
+**文件:**
+- 修改:`tools/dubbogo-cli/cmd/gen_test.go:20-95`
+- 修改:`tools/dubbogo-cli/generator/application/gomod.go:23-27`
+- 修改:`tools/dubbogo-cli/generator/sample/mod.go:23-27`
+- 修改:`tools/dubbogo-cli/cmd/testGenCode/template/newApp/go.mod:3-7`
+- 修改:`tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go.mod:3-7`
+
+- [ ] **步骤 1:添加共享 generated-project case 和版本失败测试**
+
+在 `tools/dubbogo-cli/cmd/gen_test.go` 增加:
+
+```go
+type generatedProjectCase struct {
+       name     string
+       generate func(string) error
+}
+
+func generatedProjectCases() []generatedProjectCase {
+       return []generatedProjectCase{
+               {name: "newApp", generate: application.Generate},
+               {name: "newDemo", generate: sample.Generate},
+       }
+}
+
+func TestGeneratedModuleVersion(t *testing.T) {
+       const stableVersion = "dubbo.apache.org/dubbo-go/v3 v3.3.1"
+       const datedPrerelease = "v3.3.1-20260727"
+
+       for _, tc := range generatedProjectCases() {
+               t.Run(tc.name, func(t *testing.T) {
+                       genPath := filepath.Join(t.TempDir(), tc.name)
+                       require.NoError(t, tc.generate(genPath))
+
+                       content, err := os.ReadFile(filepath.Join(genPath, 
"go.mod"))
+                       require.NoError(t, err)
+                       require.Contains(t, string(content), stableVersion)
+                       require.NotContains(t, string(content), datedPrerelease)
+               })
+       }
+}
+```
+
+同时保留 #3587 的旧 logger 扫描,内容固定为:
+
+```go
+func TestGeneratedCodeDoesNotImportRemovedLoggerPackage(t *testing.T) {
+       const removedLoggerImport = 
`"dubbo.apache.org/dubbo-go/v3/common/logger"`
+
+       for _, dir := range []string{"../generator", "./testGenCode/template"} {
+               files, err := walkDir(dir)
+               require.NoError(t, err, "iterate generated code sources failed: 
%s", dir)
+
+               for _, file := range files {
+                       if filepath.Ext(file) != ".go" {
+                               continue
+                       }
+
+                       content, err := os.ReadFile(file)
+                       require.NoError(t, err, "read generated code source 
failed: %s", file)
+                       require.NotContains(t, string(content), 
removedLoggerImport,
+                               "generated code must not import the removed 
logger package: %s", file)
+               }
+       }
+}
+```
+
+- [ ] **步骤 2:运行版本测试并验证红灯**
+
+运行:
+
+```bash
+cd tools/dubbogo-cli
+go test ./cmd -run 
'TestGeneratedModuleVersion|TestGeneratedCodeDoesNotImportRemovedLoggerPackage' 
-count=1 -v
+```
+
+预期:logger 测试通过,`TestGeneratedModuleVersion/newApp` 和 `/newDemo` 因实际内容仍为 
`v3.3.1-20260727` 而失败。
+
+- [ ] **步骤 3:最小化修改四个 go.mod 模板**
+
+将以下四个文件中的:
+
+```text
+dubbo.apache.org/dubbo-go/v3 v3.3.1-20260727
+```
+
+统一替换为:
+
+```text
+dubbo.apache.org/dubbo-go/v3 v3.3.1
+```
+
+修改文件:
+
+```text
+tools/dubbogo-cli/generator/application/gomod.go
+tools/dubbogo-cli/generator/sample/mod.go
+tools/dubbogo-cli/cmd/testGenCode/template/newApp/go.mod
+tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go.mod
+```
+
+- [ ] **步骤 4:运行定向测试并验证绿灯**
+
+运行:
+
+```bash
+cd tools/dubbogo-cli
+go test ./cmd -run 
'TestNewApp|TestNewDemo|TestGeneratedModuleVersion|TestGeneratedCodeDoesNotImportRemovedLoggerPackage'
 -count=1 -v
+```
+
+预期:四组测试全部通过,且 `TestNewApp`/`TestNewDemo` 证明 generator 与 golden 同步。
+
+- [ ] **步骤 5:提交稳定版本和 logger 回归**
+
+运行:
+
+```powershell
+git add -- `
+  tools/dubbogo-cli/cmd/gen_test.go `
+  tools/dubbogo-cli/generator/application/gomod.go `
+  tools/dubbogo-cli/generator/sample/mod.go `
+  tools/dubbogo-cli/cmd/testGenCode/template/newApp/go.mod `
+  tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go.mod
+git commit -s `
+  -m "Pin generated CLI projects to the stable Dubbo-Go release" `
+  -m "Replace the dated prerelease dependency in both generators and golden 
projects while retaining the removed-logger regression guard." `
+  -m "Constraint: Keep the generated module language version at Go 1.25" `
+  -m "Tested: targeted generator version logger and golden consistency tests" `
+  -m "Co-authored-by: OmX <[email protected]>"
+```
+
+### 任务 3:用 fresh scaffold E2E 修复 `make test`
+
+**文件:**
+- 修改:`tools/dubbogo-cli/cmd/gen_test.go`
+- 修改:`tools/dubbogo-cli/generator/application/makefile.go:79-80`
+- 修改:`tools/dubbogo-cli/generator/sample/makefile.go:42-43`
+- 修改:`tools/dubbogo-cli/cmd/testGenCode/template/newApp/Makefile:59-60`
+- 修改:`tools/dubbogo-cli/cmd/testGenCode/template/newDemo/Makefile:22-23`
+
+- [ ] **步骤 1:添加环境门禁和真实 `make test` E2E**
+
+在 `tools/dubbogo-cli/cmd/gen_test.go` 导入 `os/exec`,并增加:
+
+```go
+func TestGeneratedProjectsCompile(t *testing.T) {
+       if os.Getenv("DUBBOGO_CLI_E2E") != "1" {
+               t.Skip("set DUBBOGO_CLI_E2E=1 to run generated-project 
compilation")
+       }
+
+       for _, tc := range generatedProjectCases() {
+               t.Run(tc.name, func(t *testing.T) {
+                       genPath := filepath.Join(t.TempDir(), tc.name)
+                       require.NoError(t, tc.generate(genPath))
+
+                       cmd := exec.Command("make", "test")
+                       cmd.Dir = genPath
+                       cmd.Env = append(os.Environ(), "GOWORK=off")
+                       output, err := cmd.CombinedOutput()
+                       require.NoError(t, err, "generated project make test 
failed:\n%s", output)
+               })
+       }
+}
+```
+
+- [ ] **步骤 2:准备 WSL E2E 工具链**
+
+运行:
+
+```bash
+export GOTOOLCHAIN=go1.25.0+auto
+export PATH="$(go env GOPATH)/bin:$PATH"
+protoc --version
+go install google.golang.org/protobuf/cmd/[email protected]
+(cd tools/protoc-gen-go-triple && go install .)
+protoc-gen-go --version
+protoc-gen-go-triple --version
+```
+
+预期:WSL 现有 `protoc` 输出 `libprotoc 3.21.12`;两个插件均能从 PATH 执行。
+
+- [ ] **步骤 3:运行 E2E 并验证红灯**
+
+运行:
+
+```bash
+cd tools/dubbogo-cli
+DUBBOGO_CLI_E2E=1 go test ./cmd -run '^TestGeneratedProjectsCompile$' -count=1 
-v
+```
+
+预期:`newApp`、`newDemo` 至少一个因 fresh scaffold 的 `make test` 没有先执行 
`proto-gen`/`tidy` 而失败;输出不得是 `protoc` 或插件不存在。
+
+- [ ] **步骤 4:最小化修复四个 Makefile 模板**
+
+把四个文件中的:
+
+```makefile
+test:
+       go test ./...
+```
+
+改为:
+
+```makefile
+test: proto-gen tidy
+       go test ./...
+```
+
+修改文件:
+
+```text
+tools/dubbogo-cli/generator/application/makefile.go
+tools/dubbogo-cli/generator/sample/makefile.go
+tools/dubbogo-cli/cmd/testGenCode/template/newApp/Makefile
+tools/dubbogo-cli/cmd/testGenCode/template/newDemo/Makefile
+```
+
+- [ ] **步骤 5:运行 E2E 并验证绿灯**
+
+运行:
+
+```bash
+cd tools/dubbogo-cli
+DUBBOGO_CLI_E2E=1 go test ./cmd -run '^TestGeneratedProjectsCompile$' -count=1 
-v
+```
+
+预期:输出明确显示 `TestGeneratedProjectsCompile/newApp` 和 `/newDemo` 均通过。
+
+- [ ] **步骤 6:运行 generator/golden 一致性测试**
+
+运行:
+
+```bash
+cd tools/dubbogo-cli
+go test ./cmd -run 'TestNewApp|TestNewDemo' -count=1 -v
+```
+
+预期:两个测试均通过。
+
+- [ ] **步骤 7:提交 fresh scaffold 修复**
+
+运行:
+
+```powershell
+git add -- `
+  tools/dubbogo-cli/cmd/gen_test.go `
+  tools/dubbogo-cli/generator/application/makefile.go `
+  tools/dubbogo-cli/generator/sample/makefile.go `
+  tools/dubbogo-cli/cmd/testGenCode/template/newApp/Makefile `
+  tools/dubbogo-cli/cmd/testGenCode/template/newDemo/Makefile
+git commit -s `
+  -m "Make fresh CLI scaffolds self-contained under make test" `
+  -m "Generate protocol sources and tidy modules before compilation, then 
exercise that public workflow against newly generated app and demo projects." `
+  -m "Constraint: Generated protobuf and Triple sources remain outside the 
templates" `
+  -m "Tested: DUBBOGO_CLI_E2E=1 generated-project compilation and golden 
consistency tests" `
+  -m "Co-authored-by: OmX <[email protected]>"
+```
+
+### 任务 4:用生成输出测试修复包装 EOF
+
+**文件:**
+- 修改:`tools/dubbogo-cli/cmd/gen_test.go`
+- 修改:`tools/dubbogo-cli/generator/application/pkg.go:39-67`
+- 修改:`tools/dubbogo-cli/generator/sample/gen_server.go:23-59`
+- 
修改:`tools/dubbogo-cli/cmd/testGenCode/template/newApp/pkg/service/service.go:20-49`
+- 
修改:`tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-server/cmd/server.go:20-55`
+
+- [ ] **步骤 1:添加真实生成输出断言**
+
+在 `tools/dubbogo-cli/cmd/gen_test.go` 增加:
+
+```go
+func TestGeneratedStreamHandlersHandleWrappedEOF(t *testing.T) {
+       testCases := []struct {
+               generatedProjectCase
+               serverFile string
+       }{
+               {
+                       generatedProjectCase: generatedProjectCase{name: 
"newApp", generate: application.Generate},
+                       serverFile:           "pkg/service/service.go",
+               },
+               {
+                       generatedProjectCase: generatedProjectCase{name: 
"newDemo", generate: sample.Generate},
+                       serverFile:           "go-server/cmd/server.go",
+               },
+       }
+
+       for _, tc := range testCases {
+               t.Run(tc.name, func(t *testing.T) {
+                       genPath := filepath.Join(t.TempDir(), tc.name)
+                       require.NoError(t, tc.generate(genPath))
+
+                       content, err := os.ReadFile(filepath.Join(genPath, 
tc.serverFile))
+                       require.NoError(t, err)
+                       require.Contains(t, string(content), "errors.Is(err, 
io.EOF)")
+               })
+       }
+}
+```
+
+- [ ] **步骤 2:运行 EOF 测试并验证红灯**
+
+运行:
+
+```bash
+cd tools/dubbogo-cli
+go test ./cmd -run '^TestGeneratedStreamHandlersHandleWrappedEOF$' -count=1 -v
+```
+
+预期:newApp 和 newDemo 都因生成内容仍为 `err == io.EOF` 而失败。
+
+- [ ] **步骤 3:最小化修改 generator 和 golden 文件**
+
+四个 Go 文件增加标准库 `errors` import,并将:
+
+```go
+if err == io.EOF {
+```
+
+改为:
+
+```go
+if errors.Is(err, io.EOF) {
+```
+
+修改文件:
+
+```text
+tools/dubbogo-cli/generator/application/pkg.go
+tools/dubbogo-cli/generator/sample/gen_server.go
+tools/dubbogo-cli/cmd/testGenCode/template/newApp/pkg/service/service.go
+tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-server/cmd/server.go
+```
+
+- [ ] **步骤 4:格式化定向 Go 文件**
+
+运行:
+
+```bash
+gofmt -w \
+  tools/dubbogo-cli/cmd/gen_test.go \
+  tools/dubbogo-cli/generator/application/pkg.go \
+  tools/dubbogo-cli/generator/sample/gen_server.go \
+  tools/dubbogo-cli/cmd/testGenCode/template/newApp/pkg/service/service.go \
+  tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-server/cmd/server.go
+```
+
+预期:只修改上述文件的 Go 格式和 import 排序。
+
+- [ ] **步骤 5:运行 EOF 和 generator/golden 测试并验证绿灯**
+
+运行:
+
+```bash
+cd tools/dubbogo-cli
+go test ./cmd -run 
'TestGeneratedStreamHandlersHandleWrappedEOF|TestNewApp|TestNewDemo' -count=1 -v
+```
+
+预期:全部通过。
+
+- [ ] **步骤 6:提交 EOF 修复**
+
+运行:
+
+```powershell
+git add -- `
+  tools/dubbogo-cli/cmd/gen_test.go `
+  tools/dubbogo-cli/generator/application/pkg.go `
+  tools/dubbogo-cli/generator/sample/gen_server.go `
+  tools/dubbogo-cli/cmd/testGenCode/template/newApp/pkg/service/service.go `
+  tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-server/cmd/server.go
+git commit -s `
+  -m "Recognize wrapped EOF errors in generated stream handlers" `
+  -m "Use errors.Is in both generator templates and golden outputs so wrapped 
end-of-stream errors terminate normally instead of being logged as failures." `
+  -m "Constraint: Preserve the existing stream control flow outside EOF 
classification" `
+  -m "Tested: generated-output EOF assertions and golden consistency tests" `
+  -m "Co-authored-by: OmX <[email protected]>"
+```
+
+### 任务 5:把 E2E 工具链接入 Makefile 和 Linux CI
+
+**文件:**
+- 修改:`tools/dubbogo-cli/Makefile:16-17`
+- 修改:`.github/workflows/github-actions.yml:38-60`
+
+- [ ] **步骤 1:增加固定 Make target**
+
+将 `tools/dubbogo-cli/Makefile` 改为:
+
+```makefile
+.PHONY: test test-generated-projects
+
+test:
+       go test ./...
+
+test-generated-projects:
+       DUBBOGO_CLI_E2E=1 go test ./cmd -run '^TestGeneratedProjectsCompile$$' 
-count=1 -v
+```
+
+Make 中的正则结束符使用 `$$`,确保传给 shell 的参数为单个 `$`。
+
+- [ ] **步骤 2:在 Linux CI 增加协议工具链和 E2E step**
+
+在 `.github/workflows/github-actions.yml` 的 CI job 中,放在依赖缓存之后、`Check Code 
Format` 之前:
+
+```yaml
+      - name: Install generated-project toolchain
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y protobuf-compiler
+          go install google.golang.org/protobuf/cmd/[email protected]
+          cd tools/protoc-gen-go-triple
+          go install .
+          echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
+
+      - name: Test generated CLI projects
+        run: make -C tools/dubbogo-cli test-generated-projects
+```
+
+- [ ] **步骤 3:运行 Make target**
+
+运行:
+
+```bash
+export PATH="$(go env GOPATH)/bin:$PATH"
+make -C tools/dubbogo-cli test-generated-projects
+```
+
+预期:输出显示 newApp/newDemo 子测试通过。
+
+- [ ] **步骤 4:检查 workflow 结构和 Diff**
+
+运行:
+
+```bash
+git diff --check
+git diff -- .github/workflows/github-actions.yml tools/dubbogo-cli/Makefile
+```
+
+预期:YAML 缩进与相邻 steps 一致,Make recipe 使用 tab,`git diff --check` 退出 0。
+
+- [ ] **步骤 5:提交 CI 接入**
+
+运行:
+
+```powershell
+git add -- tools/dubbogo-cli/Makefile .github/workflows/github-actions.yml
+git commit -s `
+  -m "Gate CLI scaffold compilation in Linux CI" `
+  -m "Install the protocol generators used by fresh projects and run the 
explicit generated-project E2E target before the existing repository checks." `
+  -m "Constraint: Ordinary CLI unit tests remain independent of a local protoc 
toolchain" `
+  -m "Tested: make -C tools/dubbogo-cli test-generated-projects and workflow 
diff checks" `
+  -m "Co-authored-by: OmX <[email protected]>"
+```
+
+### 任务 6:执行完整 Linux 验证和变异式反证
+
+**文件:**
+- 验证:整个仓库
+- 探针:`D:\test\github\review\apache-dubbo-go-pr-3587\probes\e2e-mutation`
+
+- [ ] **步骤 1:记录验证前 Git 状态**
+
+运行:
+
+```powershell
+git status --porcelain=v2 --branch --untracked-files=all
+git log --oneline --decorate -8
+```
+
+预期:工作树干净,分支只领先远端本任务 commits。
+
+- [ ] **步骤 2:运行 CLI 定向和完整验证**
+
+在 WSL 中运行:
+
+```bash
+set -euo pipefail
+export GOTOOLCHAIN=go1.25.0+auto
+export PATH="$(go env GOPATH)/bin:$PATH"
+
+cd tools/dubbogo-cli
+go test ./cmd -run 
'TestNewApp|TestNewDemo|TestGeneratedModuleVersion|TestGeneratedCodeDoesNotImportRemovedLoggerPackage|TestGeneratedProjectsCompile|TestGeneratedStreamHandlersHandleWrappedEOF'
 -count=1 -v
+go test ./...
+go vet ./...
+go mod tidy -diff
+make test-generated-projects
+```
+
+预期:所有命令退出 0;普通 `go test ./...` 中 E2E 明确 Skip,显式 Make target 中 newApp/newDemo 
明确执行并通过;`go mod tidy -diff` 无输出。
+
+- [ ] **步骤 3:运行根模块完整测试**
+
+在 WSL 仓库根目录运行:
+
+```bash
+GOTOOLCHAIN=go1.25.0+auto go test ./...
+```
+
+预期:退出 0,无失败包。
+
+- [ ] **步骤 4:运行 Linux 格式门禁**
+
+运行前记录:
+
+```powershell
+git status --porcelain=v2 --branch --untracked-files=all
+```
+
+在 WSL 仓库根目录运行:
+
+```bash
+make check-fmt
+```
+
+预期:退出 0,且命令后 `git status --short` 仍然干净。若只对本任务文件产生格式差异,使用同样的 Lore 正文、`Tested:` 
trailer、OmX co-author trailer 和 `-s` 提交独立格式修正;若修改无关文件,停止并定位工具版本或基线问题。
+
+- [ ] **步骤 5:创建隔离变异探针**
+
+运行:
+
+```powershell
+$probe='D:\test\github\review\apache-dubbo-go-pr-3587\probes\e2e-mutation'
+if(Test-Path $probe){ throw "Probe target already exists: $probe" }
+git worktree add --detach $probe HEAD
+```
+
+在探针中只把两个 generator Makefile 和两个 golden Makefile 的:
+
+```makefile
+test: proto-gen tidy
+```
+
+变异回:
+
+```makefile
+test:
+```
+
+不得 commit。
+
+- [ ] **步骤 6:运行变异探针并验证测试失败**
+
+在 WSL 探针目录运行:
+
+```bash
+export PATH="$(go env GOPATH)/bin:$PATH"
+make -C tools/dubbogo-cli test-generated-projects
+```
+
+预期:命令非零退出,newApp/newDemo 因 fresh scaffold 未执行 `proto-gen`/`tidy` 而失败。该输出证明 E2E 
能检测被修复问题。
+
+- [ ] **步骤 7:安全移除探针**
+
+先验证探针绝对路径位于:
+
+```text
+D:\test\github\review\apache-dubbo-go-pr-3587\probes
+```
+
+然后运行:
+
+```powershell
+git worktree remove --force 
'D:\test\github\review\apache-dubbo-go-pr-3587\probes\e2e-mutation'
+```
+
+预期:只删除未提交的隔离探针;主工作树和分支不变。
+
+- [ ] **步骤 8:最终本地状态检查**
+
+运行:
+
+```powershell
+git diff --check
+git status --short --branch
+git log --oneline --decorate --graph -10
+```
+
+预期:主工作树干净,历史包含设计、计划、merge 和各项修复 commits。
+
+### 任务 7:防竞态 push,并复检 PR
+
+**文件:**
+- 外部状态:`apache/dubbo-go` PR #3587
+
+- [ ] **步骤 1:重新获取 GitHub Head 和远端分支**
+
+运行:
+
+```powershell
+gh pr view 3587 -R apache/dubbo-go --json 
state,baseRefName,baseRefOid,headRefName,headRefOid,mergeable,mergeStateStatus,reviewDecision
+git ls-remote origin refs/heads/codex/fix-cli-template-logger 
refs/heads/develop
+git rev-parse refs/remotes/origin/codex/fix-cli-template-logger
+```
+
+预期:GitHub/远端 PR Head 仍为本轮开始的 
`23e50e5152c58d197317351dc835865ce26242a1`;develop 不早于 
`a62a44a266c63c00e9d22a2c018b4fdccae1dab1`。若 PR Head 已变化,停止 push,fetch 
后重新审查新增提交。
+
+- [ ] **步骤 2:普通 push,不使用 force**
+
+运行:
+
+```powershell
+git push origin codex/fix-cli-template-logger
+```
+
+预期:fast-forward push 成功。
+
+- [ ] **步骤 3:复检新 Head 和 Diff**
+
+运行:
+
+```powershell
+gh pr view 3587 -R apache/dubbo-go --json 
state,baseRefName,baseRefOid,headRefName,headRefOid,mergeable,mergeStateStatus,reviewDecision,potentialMergeCommit,statusCheckRollup
+gh pr diff 3587 -R apache/dubbo-go --name-only
+```
+
+预期:Head 等于本地 `HEAD`,PR 相对最新 develop 只保留设计/计划文档、CLI 模板/测试、Makefile 和 CI 
workflow 的统一改动;无内嵌生成文件或模板 go.sum。
+
+- [ ] **步骤 4:等待 GitHub checks**
+
+运行:
+
+```powershell
+gh pr checks 3587 -R apache/dubbo-go --watch --interval 10
+```
+
+预期:新 Head 的 CI、Integration Test、License、RISC-V、CodeQL 等已触发 checks 
最终完成;任何失败都必须读取对应日志并区分 PR 回归、基线或环境问题。
+
+- [ ] **步骤 5:最终 Head/状态复检**
+
+运行:
+
+```powershell
+gh pr view 3587 -R apache/dubbo-go --json 
state,baseRefName,baseRefOid,headRefName,headRefOid,mergeable,mergeStateStatus,reviewDecision,potentialMergeCommit,statusCheckRollup
+gh pr checks 3587 -R apache/dubbo-go --required --json 
name,state,bucket,link,workflow,event
+```
+
+预期:Head 未漂移;若 required checks 仍未报告,明确记录该证据缺口,不据此声称完整可 Merge。
diff --git a/docs/superpowers/specs/2026-08-01-unify-pr-3585-3587-design.md 
b/docs/superpowers/specs/2026-08-01-unify-pr-3585-3587-design.md
new file mode 100644
index 000000000..eb5fd5ab4
--- /dev/null
+++ b/docs/superpowers/specs/2026-08-01-unify-pr-3585-3587-design.md
@@ -0,0 +1,221 @@
+# PR #3587 合并 develop 并统一 #3585/#3587 的设计
+
+## 背景与固定基线
+
+- 仓库:`apache/dubbo-go`
+- PR:`#3587`
+- PR 分支:`codex/fix-cli-template-logger`
+- 本设计开始时的 PR Head:`23e50e5152c58d197317351dc835865ce26242a1`
+- 最新 `develop`:`a62a44a266c63c00e9d22a2c018b4fdccae1dab1`
+- `#3585` 已通过上述 commit 合入 `develop`
+- 隔离工作区:`D:\test\github\review\apache-dubbo-go-pr-3587\source`
+
+当前用户工作区 `D:\test\github\dubbo-go` 存在未提交文件,本任务不得在该目录 
checkout、merge、格式化或测试。所有实现、测试、提交和 push 都在隔离工作区完成。
+
+## 目标
+
+将最新 `develop` 以普通 merge commit 合入 `codex/fix-cli-template-logger`,以 #3585 
已合入的脚手架架构为主,保留 #3587 中仍然有效的回归保护和错误处理,最终让 #3587 只包含相对最新 `develop` 仍有价值的统一修复。
+
+完成后应满足:
+
+1. PR 分支包含 `develop@a62a44a266c63c00e9d22a2c018b4fdccae1dab1`。
+2. 不恢复 #3585 已删除的内嵌 `*.pb.go`、`*.triple.go`、模板 `go.sum` 或 generator 内嵌生成代码。
+3. 新生成的 `newApp` 和 `newDemo` 在完整工具链环境中可以从空生成目录通过 `make test`。
+4. 模板不引用已删除的 `dubbo.apache.org/dubbo-go/v3/common/logger`。
+5. 模板使用已发布的 `dubbo-go v3.3.1`,而不是日期预发布标签。
+6. 双向流模板使用可识别包装错误的 EOF 判断。
+7. Linux 下完成格式、单测、vet、生成项目编译和依赖整洁性验证。
+8. 不使用 rebase 或 force-push;最终只做普通 push 更新现有 PR。
+
+## 合并与冲突解决策略
+
+执行:
+
+```text
+git merge --no-ff origin/develop
+```
+
+冲突解决遵循以下所有权:
+
+### 以 develop/#3585 为准
+
+- `tools/dubbogo-cli/generator/application/api.go` 中删除内嵌生成代码的设计。
+- 删除 `tools/dubbogo-cli/generator/sample/api.go`。
+- 删除 `tools/dubbogo-cli/generator/sample/api_tripe.go`。
+- 删除 `tools/dubbogo-cli/generator/internal/scaffold/gosum.go`。
+- 删除模板内 `api.pb.go`、`samples_api.pb.go`、旧 Triple 文件和 `go.sum`。
+- `Makefile` 驱动 `protoc`、`protoc-gen-go` 和 `protoc-gen-go-triple` 的生成流程。
+- Go 1.25 基线。
+- 示例日志使用标准库 `log`,不为示例日志重新增加 `gost` 直接依赖。
+- 保留 `tri://localhost:20000`。当前 `client.processUserSpecifiedURLs` 明确将该格式列为支持的 
direct URL。
+- #3585 对 generator 文件关闭、临时目录生成和全局模板路径隔离的修复。
+
+### 从 #3587 保留或重新实现
+
+- 生成结果不得包含旧 `common/logger` import 的回归检查。
+- 自动编译实际生成项目的回归验证,但改为适配 #3585 的“先生成协议代码”架构。
+- 生成模板的 Dubbo-Go 依赖统一为稳定版 `v3.3.1`。
+- 流式 handler 使用 `errors.Is(err, io.EOF)`。
+
+### 不保留
+
+- #3587 新增的内嵌 `api.triple.go` 和 `samples_api.triple.go`。
+- #3587 更新的模板 `go.sum` 和 generator `gosum.go`。
+- #3587 内嵌的新 Triple 生成代码字符串。
+- 仅为日志增加的 `github.com/dubbogo/gost` 直接依赖。
+- 将合法的 `tri://localhost:20000` 改为裸地址的变更。
+
+## 生产行为设计
+
+### 新生成项目的 `make test`
+
+#3585 合入后的 fresh scaffold 不包含生成的 PB/Triple 源码,但模板中的 `test` target 只运行 `go test 
./...`。因此用户在刚执行 `newApp` 或 `newDemo` 后直接运行 `make test` 会在协议源码尚未生成时失败。
+
+两个模板统一改为:
+
+```makefile
+test: proto-gen tidy
+       go test ./...
+```
+
+对应修改:
+
+- `tools/dubbogo-cli/generator/application/makefile.go`
+- `tools/dubbogo-cli/generator/sample/makefile.go`
+- `tools/dubbogo-cli/cmd/testGenCode/template/newApp/Makefile`
+- `tools/dubbogo-cli/cmd/testGenCode/template/newDemo/Makefile`
+
+这使 `make test` 成为生成项目的完整可执行入口,同时保持 `proto-gen`、`tidy` 和 `test` 仍可独立调用。
+
+### 模板依赖版本
+
+`v3.3.1` 是当前已发布的稳定版本;`v3.3.1-20260727` 是指向旧 develop 基线的日期标签。面向新用户的模板统一使用:
+
+```text
+dubbo.apache.org/dubbo-go/v3 v3.3.1
+```
+
+Go 版本继续使用 #3585 设定的 `go 1.25`,与当前仓库 Makefile 的 `GOTOOLCHAIN=go1.25.0+auto` 一致。
+
+### EOF 处理
+
+application 和 demo server 模板中的双向流循环使用:
+
+```go
+if errors.Is(err, io.EOF) {
+       return nil
+}
+```
+
+这样能同时处理直接的 `io.EOF` 和被底层包装的 EOF。需要同步 generator 源和 golden template。
+
+## 测试设计
+
+### TDD 红灯
+
+在修改模板 Makefile 前,先增加一个 Linux E2E 测试:
+
+1. 使用真实 `application.Generate` 或 `sample.Generate` 写入 `t.TempDir()`。
+2. 在 fresh scaffold 中执行 `make test`。
+3. 捕获完整输出,失败时包含项目类型和命令输出。
+
+在当前 develop 行为上,该测试应因为 fresh scaffold 尚未生成 PB/Triple 
源码而失败。必须保存该失败输出,确认失败原因不是工具缺失或网络问题。
+
+随后只修改模板 Makefile 的依赖关系,使同一测试通过。
+
+### 普通单元回归
+
+保留或重写以下检查:
+
+- `TestNewApp` 和 `TestNewDemo`:generator 输出与 golden template 完全一致。
+- 旧 logger import 扫描:检查 generator 和 golden template 中的 Go 文件。
+- 模板依赖版本检查:生成后的 `go.mod` 必须包含稳定 `v3.3.1`,且不得包含日期预发布版本。
+- EOF 模板检查通过真实生成文件与 golden 比较覆盖,不单独复制生产逻辑到测试 helper。
+
+### E2E 测试的本地门禁
+
+需要 `protoc` 和两个插件的测试通过环境变量显式启用:
+
+```text
+DUBBOGO_CLI_E2E=1
+```
+
+未设置时测试 `Skip`,普通 `go test ./...` 不强迫所有开发者预装协议工具链。CI 必须显式设置该变量并提供工具链,所以 CI 
不会跳过该测试。
+
+`tools/dubbogo-cli/Makefile` 增加固定 target:
+
+```makefile
+test-generated-projects:
+       DUBBOGO_CLI_E2E=1 go test ./cmd -run TestGeneratedProjectsCompile 
-count=1
+```
+
+### CI 工具链
+
+在 Linux CI 中增加独立步骤:
+
+1. 安装 `protobuf-compiler`。
+2. 安装与模板一致的 `protoc-gen-go` 版本。
+3. 从当前仓库的 `tools/protoc-gen-go-triple` 模块安装本地生成器。
+4. 确认 `protoc`、`protoc-gen-go`、`protoc-gen-go-triple` 都可从 `PATH` 发现。
+5. 执行 `make -C tools/dubbogo-cli test-generated-projects`。
+
+不得仅依据进程退出码;测试输出必须显示 `newApp` 和 `newDemo` 两个子测试均执行。
+
+## 验证矩阵
+
+### 合并后定向验证
+
+- `go test ./cmd -run 
'TestNewApp|TestNewDemo|TestGeneratedCodeDoesNotImportRemovedLoggerPackage|TestGeneratedModuleVersion'
 -count=1`
+- TDD E2E 红灯与绿灯命令。
+- `go vet ./...`,工作目录为 `tools/dubbogo-cli`。
+- `go mod tidy -diff`,工作目录为 `tools/dubbogo-cli`。
+
+### Linux 完整验证
+
+- 根模块 `go test ./...`。
+- `tools/dubbogo-cli` 模块 `go test ./...`。
+- `tools/dubbogo-cli` 模块 `go vet ./...`。
+- E2E 生成项目测试。
+- 对生成后的 `newApp` 和 `newDemo` 分别确认 `go mod tidy -diff` 无差异。
+- `git diff --check`。
+- 项目格式门禁的 Linux 等价命令;若运行会改写工作树,必须在运行前记录状态,并确认最终无额外 Diff。
+
+### 测试真实性
+
+E2E 测试必须执行真实 generator、真实 Makefile、真实 `protoc`、当前仓库构建的 Triple 插件和真实 `go 
test`。不得用复制的 helper 或仅检查字符串来替代可编译性验证。
+
+验证测试有效性时,在隔离探针中临时移除 `test: proto-gen tidy` 的依赖,确认 E2E 测试重新失败;探针不得 commit 或 
push。
+
+## 提交与推送
+
+计划提交序列:
+
+1. 本设计文档 commit。
+2. `develop` 合并 commit。
+3. 测试与统一修复 commit。
+4. 必要的验证或 CI 修正 commit。
+
+所有普通 commit 使用 `Signed-off-by`。不 squash、不 rebase、不 force-push。推送前重新比较:
+
+- GitHub PR 当前 `headRefOid`
+- `origin/codex/fix-cli-template-logger`
+- 本地 upstream
+
+三者必须仍等于本轮已知远端 Head,或只包含本任务已经推送的 commit。检测到第三方新增提交时停止 push 并重新对账。
+
+## 风险与回滚
+
+- CI 安装协议工具链会增加时长;通过独立 E2E step 隔离,不增加普通开发者单测前置条件。
+- 生成项目依赖稳定 `v3.3.1`,而 Triple 插件来自当前 develop;E2E 测试负责证明这组版本真实兼容。
+- 网络或依赖下载失败必须归为环境问题,不得通过跳过测试或降低断言绕过。
+- 合并冲突解决后若发现 #3585 的完整设计与稳定 `v3.3.1` 不兼容,停止实现并报告具体编译证据,不恢复内嵌生成文件作为临时绕过。
+- push 后若 GitHub Head 与本地不一致,不进行 force-push;保留本地 commit,重新获取并合并远端变化。
+
+## 非目标
+
+- 不重新审查或修改 #3585 的其他工具模块。
+- 不恢复已删除的 `variadicrpccheck`。
+- 不重构 `client.WithClientURL` 或 Triple URL 解析。
+- 不新增新的日志抽象。
+- 不修改根模块的 Go 版本。
+- 不处理与 #3587 无关的历史 Review 评论。
diff --git a/tools/dubbogo-cli/Makefile b/tools/dubbogo-cli/Makefile
index f9c8ea4b6..0c0bff0f7 100644
--- a/tools/dubbogo-cli/Makefile
+++ b/tools/dubbogo-cli/Makefile
@@ -13,5 +13,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+.PHONY: test test-generated-projects
+
 test:
-       go test ./...
\ No newline at end of file
+       go test ./...
+
+test-generated-projects:
+       DUBBOGO_CLI_E2E=1 go test ./cmd -run '^TestGeneratedProjectsCompile$$' 
-count=1 -v
diff --git a/tools/dubbogo-cli/cmd/gen_test.go 
b/tools/dubbogo-cli/cmd/gen_test.go
index 0456d8ba1..4d162077a 100644
--- a/tools/dubbogo-cli/cmd/gen_test.go
+++ b/tools/dubbogo-cli/cmd/gen_test.go
@@ -19,6 +19,7 @@ package cmd
 
 import (
        "os"
+       "os/exec"
        "path/filepath"
        "strings"
        "testing"
@@ -33,6 +34,18 @@ import (
        "dubbo.apache.org/dubbo-go/v3/tools/dubbogo-cli/generator/sample"
 )
 
+type generatedProjectCase struct {
+       name     string
+       generate func(string) error
+}
+
+func generatedProjectCases() []generatedProjectCase {
+       return []generatedProjectCase{
+               {name: "newApp", generate: application.Generate},
+               {name: "newDemo", generate: sample.Generate},
+       }
+}
+
 func TestNewApp(t *testing.T) {
        genPath := filepath.Join(t.TempDir(), "newApp")
        require.NoError(t, application.Generate(genPath))
@@ -47,6 +60,89 @@ func TestNewDemo(t *testing.T) {
        assertFileSame(t, genPath, "./testGenCode/template/newDemo")
 }
 
+func TestGeneratedModuleVersion(t *testing.T) {
+       const stableVersion = "dubbo.apache.org/dubbo-go/v3 v3.3.1"
+       const datedPrerelease = "v3.3.1-20260727"
+
+       for _, tc := range generatedProjectCases() {
+               t.Run(tc.name, func(t *testing.T) {
+                       genPath := filepath.Join(t.TempDir(), tc.name)
+                       require.NoError(t, tc.generate(genPath))
+
+                       content, err := os.ReadFile(filepath.Join(genPath, 
"go.mod"))
+                       require.NoError(t, err)
+                       require.Contains(t, string(content), stableVersion)
+                       require.NotContains(t, string(content), datedPrerelease)
+               })
+       }
+}
+
+func TestGeneratedCodeDoesNotImportRemovedLoggerPackage(t *testing.T) {
+       const removedLoggerImport = 
`"dubbo.apache.org/dubbo-go/v3/common/logger"`
+
+       for _, dir := range []string{"../generator", "./testGenCode/template"} {
+               files, err := walkDir(dir)
+               require.NoError(t, err, "iterate generated code sources failed: 
%s", dir)
+
+               for _, file := range files {
+                       if filepath.Ext(file) != ".go" {
+                               continue
+                       }
+
+                       content, err := os.ReadFile(file)
+                       require.NoError(t, err, "read generated code source 
failed: %s", file)
+                       require.NotContains(t, string(content), 
removedLoggerImport,
+                               "generated code must not import the removed 
logger package: %s", file)
+               }
+       }
+}
+
+func TestGeneratedProjectsCompile(t *testing.T) {
+       if os.Getenv("DUBBOGO_CLI_E2E") != "1" {
+               t.Skip("set DUBBOGO_CLI_E2E=1 to run generated-project 
compilation")
+       }
+
+       for _, tc := range generatedProjectCases() {
+               t.Run(tc.name, func(t *testing.T) {
+                       genPath := filepath.Join(t.TempDir(), tc.name)
+                       require.NoError(t, tc.generate(genPath))
+
+                       cmd := exec.Command("make", "test")
+                       cmd.Dir = genPath
+                       cmd.Env = append(os.Environ(), "GOWORK=off")
+                       output, err := cmd.CombinedOutput()
+                       require.NoError(t, err, "generated project make test 
failed:\n%s", output)
+               })
+       }
+}
+
+func TestGeneratedStreamHandlersHandleWrappedEOF(t *testing.T) {
+       testCases := []struct {
+               generatedProjectCase
+               serverFile string
+       }{
+               {
+                       generatedProjectCase: generatedProjectCase{name: 
"newApp", generate: application.Generate},
+                       serverFile:           "pkg/service/service.go",
+               },
+               {
+                       generatedProjectCase: generatedProjectCase{name: 
"newDemo", generate: sample.Generate},
+                       serverFile:           "go-server/cmd/server.go",
+               },
+       }
+
+       for _, tc := range testCases {
+               t.Run(tc.name, func(t *testing.T) {
+                       genPath := filepath.Join(t.TempDir(), tc.name)
+                       require.NoError(t, tc.generate(genPath))
+
+                       content, err := os.ReadFile(filepath.Join(genPath, 
tc.serverFile))
+                       require.NoError(t, err)
+                       require.Contains(t, string(content), "errors.Is(err, 
io.EOF)")
+               })
+       }
+}
+
 func assertFileSame(t *testing.T, genPath, templatePath string) {
        // 1. get all files in template directory
        templateFiles, err := walkDir(templatePath)
diff --git a/tools/dubbogo-cli/cmd/testGenCode/template/newApp/Makefile 
b/tools/dubbogo-cli/cmd/testGenCode/template/newApp/Makefile
index aa1d921ea..eced475ba 100644
--- a/tools/dubbogo-cli/cmd/testGenCode/template/newApp/Makefile
+++ b/tools/dubbogo-cli/cmd/testGenCode/template/newApp/Makefile
@@ -56,5 +56,5 @@ clean:
 tidy:
        go mod tidy
 
-test:
+test: proto-gen tidy
        go test ./...
diff --git a/tools/dubbogo-cli/cmd/testGenCode/template/newApp/go.mod 
b/tools/dubbogo-cli/cmd/testGenCode/template/newApp/go.mod
index 1f8917524..ad55eab77 100644
--- a/tools/dubbogo-cli/cmd/testGenCode/template/newApp/go.mod
+++ b/tools/dubbogo-cli/cmd/testGenCode/template/newApp/go.mod
@@ -3,6 +3,6 @@ module dubbo-go-app
 go 1.25
 
 require (
-       dubbo.apache.org/dubbo-go/v3 v3.3.1-20260727
+       dubbo.apache.org/dubbo-go/v3 v3.3.1
        google.golang.org/protobuf v1.34.2
 )
diff --git 
a/tools/dubbogo-cli/cmd/testGenCode/template/newApp/pkg/service/service.go 
b/tools/dubbogo-cli/cmd/testGenCode/template/newApp/pkg/service/service.go
index 97a2c7670..ee5e935fc 100644
--- a/tools/dubbogo-cli/cmd/testGenCode/template/newApp/pkg/service/service.go
+++ b/tools/dubbogo-cli/cmd/testGenCode/template/newApp/pkg/service/service.go
@@ -19,6 +19,7 @@ package service
 
 import (
        "context"
+       "errors"
        "io"
        "log"
 )
@@ -38,7 +39,7 @@ func (s *GreeterServerImpl) SayHello(ctx context.Context, in 
*api.HelloRequest)
 func (s *GreeterServerImpl) SayHelloStream(ctx context.Context, stream 
api.Greeter_SayHelloStreamServer) error {
        for {
                in, err := stream.Recv()
-               if err == io.EOF {
+               if errors.Is(err, io.EOF) {
                        return nil
                }
                if err != nil {
diff --git a/tools/dubbogo-cli/cmd/testGenCode/template/newDemo/Makefile 
b/tools/dubbogo-cli/cmd/testGenCode/template/newDemo/Makefile
index 8bd58d398..4969182d9 100644
--- a/tools/dubbogo-cli/cmd/testGenCode/template/newDemo/Makefile
+++ b/tools/dubbogo-cli/cmd/testGenCode/template/newDemo/Makefile
@@ -19,5 +19,5 @@ proto-gen:
 tidy:
        go mod tidy
 
-test:
+test: proto-gen tidy
        go test ./...
diff --git 
a/tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-server/cmd/server.go 
b/tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-server/cmd/server.go
index 25135a9a2..f06ab7246 100644
--- a/tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-server/cmd/server.go
+++ b/tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go-server/cmd/server.go
@@ -19,6 +19,7 @@ package main
 
 import (
        "context"
+       "errors"
        "io"
        "log"
 )
@@ -44,7 +45,7 @@ func (s *GreeterProvider) SayHello(ctx context.Context, in 
*api.HelloRequest) (*
 func (s *GreeterProvider) SayHelloStream(ctx context.Context, stream 
api.Greeter_SayHelloStreamServer) error {
        for {
                in, err := stream.Recv()
-               if err == io.EOF {
+               if errors.Is(err, io.EOF) {
                        return nil
                }
                if err != nil {
diff --git a/tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go.mod 
b/tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go.mod
index d0d244046..5c329e06f 100644
--- a/tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go.mod
+++ b/tools/dubbogo-cli/cmd/testGenCode/template/newDemo/go.mod
@@ -3,6 +3,6 @@ module helloworld
 go 1.25
 
 require (
-       dubbo.apache.org/dubbo-go/v3 v3.3.1-20260727
+       dubbo.apache.org/dubbo-go/v3 v3.3.1
        google.golang.org/protobuf v1.34.2
 )
diff --git a/tools/dubbogo-cli/generator/application/gomod.go 
b/tools/dubbogo-cli/generator/application/gomod.go
index ea6bbc349..3938ff16b 100644
--- a/tools/dubbogo-cli/generator/application/gomod.go
+++ b/tools/dubbogo-cli/generator/application/gomod.go
@@ -23,7 +23,7 @@ const (
 go 1.25
 
 require (
-       dubbo.apache.org/dubbo-go/v3 v3.3.1-20260727
+       dubbo.apache.org/dubbo-go/v3 v3.3.1
        google.golang.org/protobuf v1.34.2
 )
 `
diff --git a/tools/dubbogo-cli/generator/application/makefile.go 
b/tools/dubbogo-cli/generator/application/makefile.go
index ce574583b..272e69314 100644
--- a/tools/dubbogo-cli/generator/application/makefile.go
+++ b/tools/dubbogo-cli/generator/application/makefile.go
@@ -76,7 +76,7 @@ clean:
 tidy:
        go mod tidy
 
-test:
+test: proto-gen tidy
        go test ./...
 `
 )
diff --git a/tools/dubbogo-cli/generator/application/pkg.go 
b/tools/dubbogo-cli/generator/application/pkg.go
index bd04f84d6..420cab491 100644
--- a/tools/dubbogo-cli/generator/application/pkg.go
+++ b/tools/dubbogo-cli/generator/application/pkg.go
@@ -39,6 +39,7 @@ package service
 
 import (
        "context"
+       "errors"
        "io"
        "log"
 )
@@ -58,7 +59,7 @@ func (s *GreeterServerImpl) SayHello(ctx context.Context, in 
*api.HelloRequest)
 func (s *GreeterServerImpl) SayHelloStream(ctx context.Context, stream 
api.Greeter_SayHelloStreamServer) error {
        for {
                in, err := stream.Recv()
-               if err == io.EOF {
+               if errors.Is(err, io.EOF) {
                        return nil
                }
                if err != nil {
diff --git a/tools/dubbogo-cli/generator/sample/gen_server.go 
b/tools/dubbogo-cli/generator/sample/gen_server.go
index 3b8325279..5dfd1ba10 100644
--- a/tools/dubbogo-cli/generator/sample/gen_server.go
+++ b/tools/dubbogo-cli/generator/sample/gen_server.go
@@ -23,6 +23,7 @@ package main
 
 import (
        "context"
+       "errors"
        "io"
        "log"
 )
@@ -48,7 +49,7 @@ func (s *GreeterProvider) SayHello(ctx context.Context, in 
*api.HelloRequest) (*
 func (s *GreeterProvider) SayHelloStream(ctx context.Context, stream 
api.Greeter_SayHelloStreamServer) error {
        for {
                in, err := stream.Recv()
-               if err == io.EOF {
+               if errors.Is(err, io.EOF) {
                        return nil
                }
                if err != nil {
diff --git a/tools/dubbogo-cli/generator/sample/makefile.go 
b/tools/dubbogo-cli/generator/sample/makefile.go
index a250a2639..2db9075ff 100644
--- a/tools/dubbogo-cli/generator/sample/makefile.go
+++ b/tools/dubbogo-cli/generator/sample/makefile.go
@@ -39,7 +39,7 @@ proto-gen:
 tidy:
        go mod tidy
 
-test:
+test: proto-gen tidy
        go test ./...
 `
 )
diff --git a/tools/dubbogo-cli/generator/sample/mod.go 
b/tools/dubbogo-cli/generator/sample/mod.go
index 156bdf917..51d603ccf 100644
--- a/tools/dubbogo-cli/generator/sample/mod.go
+++ b/tools/dubbogo-cli/generator/sample/mod.go
@@ -23,7 +23,7 @@ const (
 go 1.25
 
 require (
-       dubbo.apache.org/dubbo-go/v3 v3.3.1-20260727
+       dubbo.apache.org/dubbo-go/v3 v3.3.1
        google.golang.org/protobuf v1.34.2
 )
 `

Reply via email to