# 森林AI MVP 验证

本文记录 2026-07-21 在 Windows、PowerShell 环境中对当前仓库实际执行的验证。只有获得成功退出码或明确 HTTP 响应的命令列为通过；未满足环境条件的项目单独列出，不视为通过。

## 环境基线

- Go `1.26.1 windows/amd64`
- Node.js `24.11.0`
- npm `11.12.1`
- Rust `1.97.1`
- Cargo `1.97.1`
- Docker CLI：当前机器不可用
- `DATABASE_URL`：当前 shell 未设置

本地 Compose 与 `backend/etc/agent.dev.yaml` 约定 PostgreSQL 监听 `localhost:5432`，API 监听 `localhost:9150`，Vite 默认监听 `localhost:5173`。Compose 和开发配置中的固定数据库凭据只供本地开发，生产环境必须替换。

## 本次结果摘要

| 验证项 | 结果 | 说明 |
| --- | --- | --- |
| `go test ./...` | 通过 | 全部 Go 包通过；另以 `-count=1` 无缓存复核通过。 |
| `go run ./cmd/api` | 未通过 | 本机现有 PostgreSQL 的账号配置与开发配置不匹配，初始化时认证失败。 |
| `go run ./cmd/seed` | 未通过 | 与 API 相同，因本机 PostgreSQL 认证失败。 |
| Compose 启动 | 未验证 | 当前机器没有可用的 Docker CLI，未声称启动成功。 |
| Web 依赖安装 | 通过 | `npm ci` 成功，审计未发现漏洞。 |
| Web 短时启动 | 通过 | `npm run dev` 启动；因默认端口已有用户进程，占用时自动选择可用端口，HTTP 根路径返回 `200`。本次进程随后已停止。 |
| Web 视觉检查 | 通过 | `node scripts/visual-check.mjs` 退出 `0`，无未预期 console error。 |
| Web 生产构建 | 通过 | `npm run build` 退出 `0`；存在 bundle 大小警告，不影响构建结果。 |
| Web lint | 通过 | `npm run lint` 退出 `0`。 |
| Web 结构检查 | 通过 | `node scripts/structure-check.mjs` 输出 `structure ok`。 |
| 桌面端依赖安装 | 通过 | `apps/desktop` 下 `npm ci` 成功。 |
| Tauri 可执行文件构建 | 通过 | 冷构建超过单次 240 秒工具等待上限，但已完成 release 编译；同一命令增量重跑 58.3 秒退出 `0`。 |
| Tauri 安装包 | 未验证 | 本次未运行 `npm run bundle`。 |
| PostgreSQL 集成测试 | 未运行 | 当前 shell 未提供 `DATABASE_URL`。 |

## 本地启动检查

### PostgreSQL

需要 Docker Desktop。可在任意仓库子目录运行：

```powershell
$repoRoot = (git rev-parse --show-toplevel).Trim()
Set-Location $repoRoot
docker compose -f infra/docker-compose.yml up -d
docker compose -f infra/docker-compose.yml ps
```

当前机器无法识别 `docker` 命令，因此这两条命令未验证。不要根据本记录推断 Compose 已成功启动。

### 演示数据与 API

数据库可用后，在两个独立终端中运行：

```powershell
$repoRoot = (git rev-parse --show-toplevel).Trim()
Set-Location (Join-Path $repoRoot 'backend')
go run ./cmd/seed
```

```powershell
$repoRoot = (git rev-parse --show-toplevel).Trim()
Set-Location (Join-Path $repoRoot 'backend')
go run ./cmd/api
```

本次两条命令都真实执行过，但均在连接本机 PostgreSQL 时因开发账号认证失败而退出。排除本机数据库冲突并成功启动后，应检查：

```powershell
Invoke-RestMethod http://localhost:9150/healthz
Invoke-RestMethod http://localhost:9150/api/v1/status
```

预期分别返回 `status: ok` 和 UTC RFC 3339 `timestamp`。`/api/v1/status` 是登录页使用的公开连接检查；其他业务接口除登录外均要求有效 bearer 会话。

### Web 开发服务器

```powershell
$repoRoot = (git rev-parse --show-toplevel).Trim()
Set-Location (Join-Path $repoRoot 'apps/web_v1')
npm ci
npm run dev
```

本次 `npm ci` 通过。短时启动时默认端口已被用户原有 Vite 进程占用，本次实例自动选择另一个可用端口；对该实例根路径请求得到 HTTP `200`。验证结束后仅停止了本次新建的进程树，用户原有进程未被修改。

## 自动化验证

### 后端

```powershell
$repoRoot = (git rev-parse --show-toplevel).Trim()
Set-Location (Join-Path $repoRoot 'backend')
go test ./...
```

本次退出 `0`。为排除 Go 测试缓存影响，还执行了：

```powershell
$repoRoot = (git rev-parse --show-toplevel).Trim()
Set-Location (Join-Path $repoRoot 'backend')
go test -count=1 ./...
```

无缓存复核同样退出 `0`，覆盖配置、HTTP 路由、认证、项目/工作区、任务、标签、资料、Inbox、关键词搜索、AI key 加密、限流与审计等包。

### PostgreSQL 集成测试

仅在准备了隔离的实时测试数据库时运行：

```powershell
$repoRoot = (git rev-parse --show-toplevel).Trim()
Set-Location (Join-Path $repoRoot 'backend')
$env:DATABASE_URL = '<隔离测试库连接串>'
go test -tags integration ./internal/models -run TestPostgresPing -v
```

连接串必须通过安全的本地或 CI secret 注入，不能提交到仓库。本次 `DATABASE_URL` 未设置，因此没有运行，也不列为通过。

### Web

```powershell
$repoRoot = (git rev-parse --show-toplevel).Trim()
Set-Location (Join-Path $repoRoot 'apps/web_v1')
node scripts/visual-check.mjs
npm run build
npm run lint
node scripts/structure-check.mjs
```

四条命令本次均退出 `0`。视觉检查覆盖桌面和移动登录页、工作台、项目导航、Inbox 确认、AI 会话受控创建、深色模式、无横向溢出和无未预期 console error。

`npm run build` 生成：

- `apps/web_v1/dist/index.html`
- `apps/web_v1/dist/assets/`
- `apps/web_v1/dist/senlinai-icon.svg`

`node scripts/visual-check.mjs` 生成的本地证据位于 `apps/web_v1/test-results/`，包括：

- `login-react-acro.png`
- `login-react-acro-mobile.png`
- `workbench-react-acro-light.png`
- `project-react-acro-light.png`
- `project-react-acro-dark.png`
- `project-navigation-mobile.png`
- `channel-navigation-mobile.png`
- `project-inbox-confirmed.png`
- `project-inbox-confirmed-mobile.png`
- `project-ai-controlled.png`

`dist/` 与 `test-results/` 都是本地产物，已被 `.gitignore` 排除，不应提交。

### 桌面端

```powershell
$repoRoot = (git rev-parse --show-toplevel).Trim()
Set-Location (Join-Path $repoRoot 'apps/desktop')
npm ci
npm run build
```

本次最终重跑退出 `0`。`npm run build` 使用 `tauri build --no-bundle`，并先执行 Web 生产构建。Windows 可执行文件为：

```text
apps/desktop/src-tauri/target/release/senlinai_workbench.exe
```

本次产物大小为 `8,783,360` 字节。冷构建需要下载或编译 Rust 依赖，可能耗时数分钟；本次第一次调用因外部等待工具达到 240 秒上限而返回超时，但编译日志已显示 release 完成和产物生成，随后相同命令增量重跑获得退出码 `0`。

安装包是独立验证项：

```powershell
$repoRoot = (git rev-parse --show-toplevel).Trim()
Set-Location (Join-Path $repoRoot 'apps/desktop')
npm run bundle
```

本次没有运行该命令，不记录安装包结果。

## API 契约抽查

当前路由注册与 Web API client 均使用 `/api/v1`：

- 公共：`GET /api/v1/status`、`POST /api/v1/auth/login`。
- 项目与工作区：项目列表/创建/读取/更新，以及项目工作区读取。
- 任务与标签：任务创建/更新、项目标签列表/创建。
- 资料与计划任务：multipart 文件资料创建、计划任务元数据创建。
- Inbox：收集、生成建议、按服务端建议 ID 确认。
- 搜索：带 `q` 参数的关键词搜索，响应为 `items` 数组。
- AI 会话：项目会话列表与受控创建，响应为会话 DTO。

抽查结论：创建接口返回 `201`，读取和当前更新接口返回 `200`；JSON DTO 使用 camelCase，对外路径参数使用 UUIDv7，错误为 `error.code` 与 `error.message` 包装结构。API 不直接返回 Gorm 模型或内部自增 ID。

## 人工 MVP 流程

在 Compose、seed、API 和 Web 均成功启动后，按以下顺序人工验收：

1. 打开 Web 客户端，在登录页输入服务器 IP 地址或域名。
2. 使用仅供开发的演示账号登录，并确认生产部署未使用默认凭据。
3. 创建项目，修改项目设置，再刷新确认数据保留。
4. 创建项目内标签和任务，验证任务卡显示真实标签与状态。
5. 上传文件资料，确认返回路径是相对存储路径，且越权项目不能留下文件。
6. 创建计划任务元数据，确认界面没有暗示自主 Agent 已执行。
7. 收集一条 Inbox 内容，先分析建议；此时不应出现正式对象。
8. 选择一条或多条服务端建议并确认，验证生成对象保留来源 Inbox ID。
9. 用关键词搜索项目、任务和笔记，确认用户只能看到有权访问的结果。
10. 创建项目 AI 会话，确认缺 key、限流和服务错误有明确提示，且不会自动生成正式对象。
11. 在桌面与移动视口检查项目/频道导航、焦点状态和横向溢出。
12. 启动桌面可执行文件，逐项检查登录持久化、拖拽上传、通知和全局快捷收集等 MVP 目标；未实际走通的能力不得记为通过。

## 安全复核清单

- 生产环境已替换数据库凭据、`auth_secret`、AI key 加密密钥和所有开发默认值。
- 未提交数据库连接串、用户 AI key、会话凭据、日志、数据库文件、上传文件或构建产物。
- 登录凭据由签名且会过期的 bearer session 校验，handler 从认证上下文获取用户。
- 用户 AI key 加密存储；AI 操作在 key/provider 选择前占用限流配额，并写 provider、key 类型、action、status 和 error 审计。
- AI 输出只形成待确认草稿；用户确认前不创建任务、笔记或资料。
- 项目标签保持项目内范围；任务分享的 note/source 与任务属于同一项目。
- 被指派人只看到任务和明确分享的关联对象。
- PostgreSQL 集成测试只使用显式 `DATABASE_URL` 指向的隔离测试库。

## 文档一致性检查

已完成以下一致性检查：两份文档中的旧品牌、旧 API 前缀、旧前端目录和旧技术栈关键词扫描无匹配；Markdown 相对链接均指向现有文件；命令使用的工作目录、入口和脚本均与仓库实际路径一致。
