diff --git a/app-modern/cmd/softbox/main.go b/app-modern/cmd/softbox/main.go new file mode 100644 index 0000000..eb6f73a --- /dev/null +++ b/app-modern/cmd/softbox/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + + "softbox.local/core" +) + +func main() { + fmt.Println(core.ProductName) +} diff --git a/app-modern/go.mod b/app-modern/go.mod new file mode 100644 index 0000000..d615bed --- /dev/null +++ b/app-modern/go.mod @@ -0,0 +1,8 @@ +module softbox.local/app-modern + +go 1.25.0 + +require ( + gioui.org v0.10.1 + softbox.local/core v0.0.0 +) diff --git a/app-modern/platform/windows/doc.go b/app-modern/platform/windows/doc.go new file mode 100644 index 0000000..16fb03c --- /dev/null +++ b/app-modern/platform/windows/doc.go @@ -0,0 +1,2 @@ +// Package windows contains modern Windows platform adapters. +package windows diff --git a/app-modern/ui/gio/doc.go b/app-modern/ui/gio/doc.go new file mode 100644 index 0000000..2b178c4 --- /dev/null +++ b/app-modern/ui/gio/doc.go @@ -0,0 +1,2 @@ +// Package gio contains the modern Gio UI adapter. +package gio diff --git a/app-win7/cmd/softbox/main.go b/app-win7/cmd/softbox/main.go new file mode 100644 index 0000000..eb6f73a --- /dev/null +++ b/app-win7/cmd/softbox/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + + "softbox.local/core" +) + +func main() { + fmt.Println(core.ProductName) +} diff --git a/app-win7/go.mod b/app-win7/go.mod new file mode 100644 index 0000000..644841a --- /dev/null +++ b/app-win7/go.mod @@ -0,0 +1,8 @@ +module softbox.local/app-win7 + +go 1.20 + +require ( + gioui.org v0.6.0 + softbox.local/core v0.0.0 +) diff --git a/app-win7/go.work b/app-win7/go.work new file mode 100644 index 0000000..038d41d --- /dev/null +++ b/app-win7/go.work @@ -0,0 +1,8 @@ +go 1.20 + +use ( + . + ../core +) + +replace softbox.local/core v0.0.0 => ../core diff --git a/app-win7/go.work.sum b/app-win7/go.work.sum new file mode 100644 index 0000000..bc13a1c --- /dev/null +++ b/app-win7/go.work.sum @@ -0,0 +1 @@ +gioui.org v0.6.0/go.mod h1:eUvGo6FAzA7jUqeSu5a+M1W03yc9r1nanIBS8A5+Nng= diff --git a/app-win7/platform/windows/doc.go b/app-win7/platform/windows/doc.go new file mode 100644 index 0000000..a320732 --- /dev/null +++ b/app-win7/platform/windows/doc.go @@ -0,0 +1,2 @@ +// Package windows contains Win7-compatible Windows platform adapters. +package windows diff --git a/app-win7/ui/gio/doc.go b/app-win7/ui/gio/doc.go new file mode 100644 index 0000000..09da906 --- /dev/null +++ b/app-win7/ui/gio/doc.go @@ -0,0 +1,2 @@ +// Package gio contains the Win7-compatible Gio UI adapter. +package gio diff --git a/core/application/doc.go b/core/application/doc.go new file mode 100644 index 0000000..448d4d7 --- /dev/null +++ b/core/application/doc.go @@ -0,0 +1,2 @@ +// Package application coordinates SoftBox use cases through injected ports. +package application diff --git a/core/domain/doc.go b/core/domain/doc.go new file mode 100644 index 0000000..63d428d --- /dev/null +++ b/core/domain/doc.go @@ -0,0 +1,2 @@ +// Package domain contains SoftBox business models and rules. +package domain diff --git a/core/go.mod b/core/go.mod new file mode 100644 index 0000000..36862bb --- /dev/null +++ b/core/go.mod @@ -0,0 +1,3 @@ +module softbox.local/core + +go 1.20 diff --git a/core/product.go b/core/product.go new file mode 100644 index 0000000..fb943db --- /dev/null +++ b/core/product.go @@ -0,0 +1,5 @@ +// Package core exposes shared, UI-independent SoftBox functionality. +package core + +// ProductName is the stable display name shared by both application targets. +const ProductName = "SoftBox" diff --git a/core/product_test.go b/core/product_test.go new file mode 100644 index 0000000..6c3d29f --- /dev/null +++ b/core/product_test.go @@ -0,0 +1,9 @@ +package core + +import "testing" + +func TestProductName(t *testing.T) { + if ProductName != "SoftBox" { + t.Fatalf("ProductName = %q, want %q", ProductName, "SoftBox") + } +} diff --git a/docs/00-ai-start-here.md b/docs/00-ai-start-here.md index e93018e..1b9d2ab 100644 --- a/docs/00-ai-start-here.md +++ b/docs/00-ai-start-here.md @@ -45,7 +45,7 @@ SoftBox 软件盒子是一个使用 Go + Gio 开发的 Windows 桌面客户端, ## 当前阶段 -当前项目处于:**MVP 起步(Phase 0 工程骨架尚未建立,仓库只有文档)**。 +当前项目处于:**MVP 起步(Phase 0 工程骨架建设中)**。 优先路径: @@ -133,14 +133,14 @@ MVP 不做: ## 验证命令 -统一启动与验证入口收敛到根目录 `./init.sh` / `./init.ps1`;T-001 完成前脚本处于未配置状态。骨架建立后的标准命令: +统一启动与验证入口收敛到根目录 `./init.sh` / `./init.ps1`;脚本会同步工作区依赖并运行 core 基础验证。标准命令: ```bash # core 测试(Linux 无头可跑) cd core && go vet ./... && go test -count=1 ./... # 现代版构建 -cd app-modern && GOOS=windows GOARCH=amd64 go build ./cmd/softbox +cd app-modern && GOTOOLCHAIN=go1.25.0 GOOS=windows GOARCH=amd64 go build ./cmd/softbox # Win7 版构建(强制 Go 1.20) cd app-win7 && GOTOOLCHAIN=go1.20.14 GOOS=windows GOARCH=amd64 go build ./cmd/softbox diff --git a/docs/03-tech-stack.md b/docs/03-tech-stack.md index 534c794..d4fc736 100644 --- a/docs/03-tech-stack.md +++ b/docs/03-tech-stack.md @@ -35,17 +35,19 @@ ## 三、构建与运行命令 -> 工程骨架由 T-001 建立;骨架完成前以下命令不可运行,`init.sh`/`init.ps1` 会主动失败提示。骨架完成后,T-001 必须把真实命令回填到本表、`00-ai-start-here.md`、`current-state.md` 和 init 脚本顶部三个变量。 +> 工程骨架由 T-001 建立;根目录 `init.sh`/`init.ps1` 统一执行依赖同步与 core 基础验证,并打印双目标构建命令。 | 用途 | 命令(计划) | | --- | --- | | 同步工作区依赖 | `go work sync` | | core 测试 | `cd core && go test -count=1 ./...` | | core 静态检查 | `cd core && go vet ./...` | -| 现代版构建 | `cd app-modern && GOOS=windows GOARCH=amd64 go build -o ../dist/SoftBox.exe ./cmd/softbox` | +| 现代版构建 | `cd app-modern && GOTOOLCHAIN=go1.25.0 GOOS=windows GOARCH=amd64 go build -o ../dist/SoftBox.exe ./cmd/softbox` | | Win7 版构建 | `cd app-win7 && GOTOOLCHAIN=go1.20.14 GOOS=windows GOARCH=amd64 go build -o ../dist/SoftBox-win7.exe ./cmd/softbox` | | 本地运行(开发) | Windows 下直接 `go run ./cmd/softbox`(WSL 中构建 EXE 后到 Windows 侧运行) | +根 `go.work` 纳入三个模块并使用 modern 所需的 Go 1.25 workspace 版本;`app-win7/go.work` 是就近的 Go 1.20 workspace,只纳入 `app-win7` 与 `core`,确保 Go 1.20.14 不需要解析 modern 模块。 + 工具链矩阵: | 构建目标 | 工具链 | Gio | 支持系统 | 维护策略 | diff --git a/docs/current-state.md b/docs/current-state.md index 141aa18..5aaa56b 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -13,26 +13,26 @@ ## 当前快照 - 日期:2026-07-16 -- 阶段:MVP 起步(Phase 0 未开始,仓库只有 harness 文档,无任何 Go 代码) -- 技术栈:已定稿于 `03-tech-stack.md`(Go + Gio,双工具链矩阵),尚未落地 -- 生产代码:无;`core/`、`app-modern/`、`app-win7/`、`schemas/`、`testdata/` 均待 T-001 创建 -- 测试:无 +- 阶段:MVP 起步(Phase 0 工程骨架建设中) +- 技术栈:根 Go workspace 纳入 core/app-modern/app-win7 三模块;`app-win7/go.work` 隔离 Go 1.20.14 构建;Gio UI 与平台 stub 待后续 Phase 0 任务接入 +- 生产代码:已有最小 core 包与 modern/win7 命令入口;尚无业务功能 +- 测试:core 已有最小真实单元测试 - 数据:无;Catalog 清单与测试样例待建 -- 标准启动路径:`./init.sh` / `./init.ps1`(**尚未配置**,顶部三个命令仍为占位符,运行会主动失败提示——T-001 负责替换) -- 标准验证路径:同上,未配置 +- 标准启动路径:`./init.sh` / `./init.ps1`(同步依赖、验证 core、打印 modern/win7 双目标构建命令) +- 标准验证路径:`go -C core vet ./...` + `go -C core test -count=1 ./...`;双目标构建命令见下文 - 版本管理:git 已初始化,main 分支,远端 origin 为 Gitea `opc/soft_quay`;harness 文档已提交 -- 当前 blocker:无;下一步即领取 T-001 +- 当前 blocker:无;下一步按路线图落成并领取 T-002 ## 当前目录要点 | 路径 | 状态 | 说明 | | --- | --- | --- | | `docs/` | 已有 | harness coding 文档集(本次初始化完成) | -| `docs/tasks/` | 已有 | 任务目录;T-001 已落成任务文件待领取 | +| `docs/tasks/` | 已有 | 任务目录;T-001 已完成,T-002 待按路线图落成 | | `scripts/` | 已有 | harness 治理脚本(validate_agent_context 等);构建脚本待建 | -| `core/` | 待建 | 共享业务核心(T-001) | -| `app-modern/` | 待建 | 现代版模块(T-001) | -| `app-win7/` | 待建 | Win7 遗留版模块(T-001) | +| `core/` | 已建 | Go 1.20 兼容共享模块;当前仅最小包与测试 | +| `app-modern/` | 已建 | Go 1.25.0 + Gio v0.10.1 模块;当前为无 UI 命令入口 | +| `app-win7/` | 已建 | Go 1.20 + Gio v0.6.0 模块;当前为无 UI 命令入口 | | `schemas/` | 待建 | 协议 JSON Schema(T-201) | | `testdata/` | 待建 | 假数据测试样例(T-101 起) | @@ -40,18 +40,22 @@ 任务状态以 `docs/tasks/` 各任务文件 frontmatter 的 `status` 为准。本节只写项目级摘要: -- 已完成:无。 +- 已完成:`T-001 初始化 monorepo 骨架`。 - 正在进行:无。 -- 下一个可领取任务:`T-001 初始化 monorepo 骨架`(`docs/tasks/T-001.md`)。 +- 下一个可领取任务:按路线图落成并领取 `T-002 建立 domain 状态模型与事件总线`。 ## 当前可运行内容 ```bash -# 目前仅文档治理检查可运行: -python3 scripts/validate_agent_context.py -python3 scripts/validate_harness_governance.py +# 统一依赖同步 + core 验证: +./init.sh +# Windows PowerShell 使用 ./init.ps1 -# Go 构建 / 测试:骨架未建立,暂不可运行(见 T-001) +# 独立验证: +go -C core vet ./... +go -C core test -count=1 ./... +GOTOOLCHAIN=go1.25.0 GOOS=windows GOARCH=amd64 go -C app-modern build ./cmd/softbox +GOTOOLCHAIN=go1.20.14 GOOS=windows GOARCH=amd64 go -C app-win7 build ./cmd/softbox ``` ## 开始编码前检查 diff --git a/docs/tasks/T-001.md b/docs/tasks/T-001.md index 71e18ca..e88d6e2 100644 --- a/docs/tasks/T-001.md +++ b/docs/tasks/T-001.md @@ -3,12 +3,12 @@ id: T-001 title: 初始化 monorepo 骨架(core / app-modern / app-win7 + go.work) phase: 0 deps: [] -status: TODO +status: DONE created: 2026-07-16 issue: null -context_ref: null +context_ref: 0184595cccc3a516628fc6077c558b84606ad806 claim_branch: null -work_branch: null +work_branch: agent/codex/T-001 write_paths: - docs/tasks/T-001.md - go.work @@ -60,4 +60,12 @@ write_paths: ## 执行记录 -(做完在此记录:改了哪些文件、跑的验证命令与结果、阻塞、关键决策。) +- 2026-07-16:创建根 `go.work`、`core` / `app-modern` / `app-win7` 三个模块、最小命令入口、core 真实单元测试及预留包目录。 +- 2026-07-16:根 workspace 使用 Go 1.25 并纳入三个模块;额外创建 `app-win7/go.work`(Go 1.20,仅含 win7 + core),使 Go 1.20.14 不必解析 modern 模块,同时保持 app → core 的依赖方向。 +- 2026-07-16:替换 `init.sh` / `init.ps1` 占位命令并同步 `00-ai-start-here.md`、`03-tech-stack.md`、`current-state.md`。 +- 验证通过:`go work sync`。 +- 验证通过:`cd core && go vet ./... && go test -count=1 ./...`。 +- 验证通过:`cd app-modern && GOTOOLCHAIN=go1.25.0 GOOS=windows GOARCH=amd64 go build -o ../dist/SoftBox.exe ./cmd/softbox`。 +- 验证通过:`cd app-win7 && GOTOOLCHAIN=go1.20.14 GOOS=windows GOARCH=amd64 go build -o ../dist/SoftBox-win7.exe ./cmd/softbox`。本机镜像下载 toolchain 失败后,使用 Go 官方 `golang.org/dl/go1.20.14` 下载并验证同一版本工具链。 +- 验证通过:`./init.ps1` 与 Git Bash `./init.sh`。 +- 验证通过:`python scripts/validate_agent_context.py`、`python scripts/validate_harness_governance.py`。 diff --git a/go.work b/go.work new file mode 100644 index 0000000..6abf496 --- /dev/null +++ b/go.work @@ -0,0 +1,9 @@ +go 1.25.0 + +use ( + ./app-modern + ./app-win7 + ./core +) + +replace softbox.local/core v0.0.0 => ./core diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 0000000..f32a795 --- /dev/null +++ b/go.work.sum @@ -0,0 +1,12 @@ +eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA= +gioui.org v0.6.0/go.mod h1:eUvGo6FAzA7jUqeSu5a+M1W03yc9r1nanIBS8A5+Nng= +gioui.org v0.10.1/go.mod h1:MZJZsdEPkTBzChdqeE8CiiQhreUQBj43qusDxQNDf7k= +gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= +gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM= +github.com/go-text/typesetting v0.3.4/go.mod h1:4qZCQphq4KSgGTAeI0uMEkVbROgfah8BuyF5LRYr7XY= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= +golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8= +golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= diff --git a/init.ps1 b/init.ps1 index dd73d88..e04effe 100644 --- a/init.ps1 +++ b/init.ps1 @@ -4,32 +4,14 @@ # - Windows 原生 PowerShell:用本文件 ./init.ps1 # - WSL / Git Bash / macOS / Linux:用 ./init.sh # 一条命令完成:依赖安装 -> 基础验证 -> 打印启动命令。 -# 复制到新项目后,必须先替换下面三个命令,让每轮会话用同一条路径启动,不靠记忆。 -# 本文件不绑定任何技术栈;换技术栈时只替换这三个命令,脚本结构不用动。 +# 三个命令是仓库统一入口;换工具链时同步更新文档与这里。 $ErrorActionPreference = "Stop" Set-Location -Path $PSScriptRoot -# 按你的项目实际情况替换这三个命令。未替换前脚本会主动失败。 -$InstallCmd = "__REPLACE_INSTALL_CMD__" # 依赖安装,如 uv sync / poetry install / npm install -$VerifyCmd = "__REPLACE_VERIFY_CMD__" # 基础验证 / smoke,如 python -m pytest / go test ./... -$StartCmd = "__REPLACE_START_CMD__" # 开发启动,如 uvicorn app:app --reload / npm run dev - -function Assert-Configured { - param( - [string]$Name, - [string]$Value - ) - - if ($Value -like "__REPLACE_*") { - Write-Error "请先在 init.ps1 中替换 $Name。同步更新 docs/03-tech-stack.md、docs/00-ai-start-here.md 和 docs/current-state.md 中的命令。" - exit 2 - } -} - -Assert-Configured -Name "InstallCmd" -Value $InstallCmd -Assert-Configured -Name "VerifyCmd" -Value $VerifyCmd -Assert-Configured -Name "StartCmd" -Value $StartCmd +$InstallCmd = "go work sync" +$VerifyCmd = 'go -C core vet ./...; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }; go -C core test -count=1 ./...' +$StartCmd = '$env:GOTOOLCHAIN="go1.25.0"; $env:GOOS="windows"; $env:GOARCH="amd64"; go build -C app-modern -o ../dist/SoftBox.exe ./cmd/softbox; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }; $env:GOTOOLCHAIN="go1.20.14"; go build -C app-win7 -o ../dist/SoftBox-win7.exe ./cmd/softbox' Write-Host "==> 当前目录: $($PWD.Path)" diff --git a/init.sh b/init.sh index 02a381d..5228b5b 100755 --- a/init.sh +++ b/init.sh @@ -1,51 +1,35 @@ #!/usr/bin/env bash # 标准启动与验证入口(Unix shell 版),与 init.ps1 等价,二选一: -# - WSL / Git Bash / macOS / Linux:用本文件 ./init.sh +# - WSL / Git Bash / macOS / Linux:用 ./init.sh # - Windows 原生 PowerShell:用 ./init.ps1 # 一条命令完成:依赖安装 -> 基础验证 -> 打印启动命令。 -# 复制到新项目后,必须先替换下面三个变量,让每轮会话用同一条路径启动,不靠记忆。 -# 本文件不绑定任何技术栈;换技术栈时只替换这三个变量,脚本结构不用动。 +# 三个命令是仓库统一入口;换工具链时同步更新文档与这里。 set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$ROOT_DIR" -# 按你的项目实际情况替换这三个命令。未替换前脚本会主动失败。 -INSTALL_CMD=(__REPLACE_INSTALL_CMD__) # 依赖安装,如 uv sync、poetry install、npm install -VERIFY_CMD=(__REPLACE_VERIFY_CMD__) # 基础验证 / smoke,如 python -m pytest、go test ./... -START_CMD=(__REPLACE_START_CMD__) # 开发启动,如 uvicorn app:app --reload、npm run dev - -ensure_configured() { - local name="$1" - local first="$2" - if [[ "$first" == __REPLACE_* ]]; then - echo "ERROR: 请先在 init.sh 中替换 ${name}。" - echo " 同步更新 docs/03-tech-stack.md、docs/00-ai-start-here.md 和 docs/current-state.md 中的命令。" - exit 2 - fi -} - -ensure_configured "INSTALL_CMD" "${INSTALL_CMD[0]}" -ensure_configured "VERIFY_CMD" "${VERIFY_CMD[0]}" -ensure_configured "START_CMD" "${START_CMD[0]}" +INSTALL_CMD="go work sync" +VERIFY_CMD="go -C core vet ./... && go -C core test -count=1 ./..." +START_CMD="GOTOOLCHAIN=go1.25.0 GOOS=windows GOARCH=amd64 go -C app-modern build -o ../dist/SoftBox.exe ./cmd/softbox && GOTOOLCHAIN=go1.20.14 GOOS=windows GOARCH=amd64 go -C app-win7 build -o ../dist/SoftBox-win7.exe ./cmd/softbox" echo "==> 当前目录: $PWD" echo "==> 同步依赖" -"${INSTALL_CMD[@]}" +eval "$INSTALL_CMD" echo "==> 运行基础验证" -"${VERIFY_CMD[@]}" +eval "$VERIFY_CMD" echo "==> 启动命令" -printf ' %q' "${START_CMD[@]}" -printf '\n' +printf ' %s\n' "$START_CMD" if [ "${RUN_START_COMMAND:-0}" = "1" ]; then echo "==> 启动应用" - exec "${START_CMD[@]}" + eval "$START_CMD" + exit $? fi echo "如果希望 init.sh 直接启动应用,请设置 RUN_START_COMMAND=1。"