Files
agent/docs/superpowers/plans/2026-07-18-project-workbench-mvp.md

28 KiB
Raw Blame History

项目工作台 MVP Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 构建一个以项目为中心的私有部署工作台 MVP包含项目、Inbox、任务、Markdown 笔记资料、AI 会话、全文搜索、任务分发和 Tauri 桌面壳。

Architecture: 使用前后端分离 monorepo。backend 提供 Go Gin + Gorm + PostgreSQL APIapps/web 提供响应式 Svelte Web 客户端,apps/desktop 用 Tauri 包装 Web 客户端并提供桌面能力。先实现清晰模块边界,再逐步接入 AI、搜索和桌面特性。

Tech Stack: Go 1.22+, Gin, Gorm, PostgreSQL 16+, Svelte, TypeScript, Vite, Tauri 2, Vitest, Testing Library for Svelte, Playwright, Docker Compose.

Global Constraints

  • 私有部署优先,用户通过管理员创建账号或邀请码注册。
  • 数据以服务端为准,本地客户端只是 Tauri 壳,不做离线编辑或本地优先同步。
  • 文件第一版存储在服务端本地磁盘,路径逻辑集中在文件服务中。
  • 标签是项目内标签,不做全局标签体系。
  • AI 默认允许回退到系统 key必须有调用日志、基础限流、错误记录和用户 key 安全存储。
  • AI 不得自动创建正式对象Inbox 分析结果必须由用户确认。
  • 任务分发只面向系统内用户,接收者只能看到任务和显式共享的关联对象。
  • 全局搜索使用 PostgreSQL 关键词和全文搜索,语义搜索不进入 MVP。
  • 前端只使用 Svelte不使用 React。
  • 登录页必须允许用户填写服务器 IP 或域名,并将其保存为 API base URL。
  • PostgreSQL 集成验证通过 DATABASE_URL 读取测试服务器地址;不要提交真实凭据。
  • 前端桌面优先,同时保持移动浏览器可用。
  • 每个任务结束时运行该任务列出的验证命令并提交。

File Structure

D:\work\senlinai\agent
├── AGENTS.md
├── apps
│   ├── web
│   │   ├── package.json
│   │   ├── index.html
│   │   ├── vite.config.ts
│   │   ├── tsconfig.json
│   │   └── src
│   │       ├── app
│   │       ├── components
│   │       ├── features
│   │       ├── lib
│   │       └── test
│   └── desktop
│       ├── package.json
│       └── src-tauri
├── backend
│   ├── cmd
│   │   └── api
│   ├── internal
│   │   ├── ai
│   │   ├── auth
│   │   ├── config
│   │   ├── db
│   │   ├── domain
│   │   ├── files
│   │   ├── httpx
│   │   ├── inbox
│   │   ├── notes
│   │   ├── projects
│   │   ├── search
│   │   └── tasks
│   ├── migrations
│   └── tests
├── infra
└── docs

后端 Go module 使用:

senlinai-agent/backend

Task 1: Monorepo 与本地开发基础

Files:

  • Create: D:\work\senlinai\agent\README.md
  • Create: D:\work\senlinai\agent\.gitignore
  • Create: D:\work\senlinai\agent\infra\docker-compose.yml
  • Create: D:\work\senlinai\agent\backend\go.mod
  • Create: D:\work\senlinai\agent\backend\cmd\api\main.go
  • Create: D:\work\senlinai\agent\backend\internal\config\config.go
  • Create: D:\work\senlinai\agent\backend\internal\httpx\router.go
  • Create: D:\work\senlinai\agent\backend\internal\httpx\router_test.go

Interfaces:

  • Produces: config.Load() config.Config

  • Produces: httpx.NewRouter(cfg config.Config) *gin.Engine

  • Produces: GET /healthz -> {"status":"ok"}

  • Step 1: 创建目录和 Go module

New-Item -ItemType Directory -Force backend\cmd\api, backend\internal\config, backend\internal\httpx, infra
Set-Location backend
go mod init senlinai-agent/backend
go get github.com/gin-gonic/gin@latest github.com/stretchr/testify@latest

Expected: backend/go.mod exists.

  • Step 2: 写 healthz 失败测试

Create backend/internal/httpx/router_test.go:

package httpx

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/require"
	"senlinai-agent/backend/internal/config"
)

func TestHealthz(t *testing.T) {
	router := NewRouter(config.Config{Env: "test"})
	req := httptest.NewRequest(http.MethodGet, "/healthz", nil)
	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, req)
	require.Equal(t, http.StatusOK, rec.Code)
	require.JSONEq(t, `{"status":"ok"}`, rec.Body.String())
}
  • Step 3: 确认测试失败
Set-Location D:\work\senlinai\agent\backend
go test ./internal/httpx -run TestHealthz -v

Expected: FAIL because NewRouter or config.Config is undefined.

  • Step 4: 实现 config、router、main

Create backend/internal/config/config.go:

package config

import "os"

type Config struct {
	Env         string
	DatabaseURL string
	StorageDir  string
	SystemAIKey string
}

func Load() Config {
	return Config{
		Env:         getenv("APP_ENV", "development"),
		DatabaseURL: getenv("DATABASE_URL", "postgres://agent:agent@localhost:5432/agent?sslmode=disable"),
		StorageDir:  getenv("STORAGE_DIR", "./data/files"),
		SystemAIKey: os.Getenv("SYSTEM_AI_KEY"),
	}
}

func getenv(key string, fallback string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	return fallback
}

Create backend/internal/httpx/router.go:

package httpx

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"senlinai-agent/backend/internal/config"
)

func NewRouter(cfg config.Config) *gin.Engine {
	if cfg.Env == "test" {
		gin.SetMode(gin.TestMode)
	}
	router := gin.New()
	router.Use(gin.Recovery())
	router.GET("/healthz", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"status": "ok"})
	})
	return router
}

Create backend/cmd/api/main.go:

package main

import (
	"log"

	"senlinai-agent/backend/internal/config"
	"senlinai-agent/backend/internal/httpx"
)

func main() {
	cfg := config.Load()
	router := httpx.NewRouter(cfg)
	if err := router.Run(":8080"); err != nil {
		log.Fatal(err)
	}
}
  • Step 5: 增加基础本地开发文件

Create .gitignore:

node_modules/
dist/
target/
.env
data/
coverage/
*.log

Create infra/docker-compose.yml:

services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: agent
      POSTGRES_PASSWORD: agent
      POSTGRES_DB: agent
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Create README.md:

# SenlinAI Agent Workbench

Private project-centered workbench MVP.

## Development

```powershell
docker compose -f infra/docker-compose.yml up -d
Set-Location backend
go run ./cmd/api

- [ ] **Step 6: 验证并提交**

```powershell
Set-Location D:\work\senlinai\agent\backend
go test ./... -v
Set-Location D:\work\senlinai\agent
git add README.md .gitignore infra backend
git commit -m "chore: scaffold project workbench"

Task 2: 数据库模型与迁移边界

Files:

  • Create: D:\work\senlinai\agent\backend\internal\db\db.go
  • Create: D:\work\senlinai\agent\backend\internal\domain\models.go
  • Create: D:\work\senlinai\agent\backend\internal\domain\models_test.go

Interfaces:

  • Produces: db.Open(databaseURL string) (*gorm.DB, error)

  • Produces: domain.AutoMigrate(database *gorm.DB) error

  • Produces models: User, Project, InboxItem, Task, Note, Source, AISession, Tag, ProjectEvent, AIKey, AICallLog, TaskShare

  • Step 1: 添加 Gorm 依赖

Set-Location D:\work\senlinai\agent\backend
go get gorm.io/gorm@latest gorm.io/driver/postgres@latest gorm.io/driver/sqlite@latest
  • Step 2: 写 AutoMigrate 失败测试

Create backend/internal/domain/models_test.go:

package domain

import (
	"testing"

	"github.com/stretchr/testify/require"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func TestAutoMigrateCreatesCoreTables(t *testing.T) {
	database, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
	require.NoError(t, err)
	require.NoError(t, AutoMigrate(database))
	for _, table := range []string{
		"users", "projects", "inbox_items", "tasks", "notes", "sources",
		"ai_sessions", "tags", "project_events", "ai_keys", "ai_call_logs", "task_shares",
	} {
		require.True(t, database.Migrator().HasTable(table), "missing table %s", table)
	}
}
  • Step 3: 实现 domain models

Create backend/internal/domain/models.go with the structs listed in the interface. Required fields:

package domain

import (
	"time"

	"gorm.io/gorm"
)

type User struct { ID uint `gorm:"primaryKey"`; Email string `gorm:"uniqueIndex;not null"`; DisplayName string `gorm:"not null"`; PasswordHash string `gorm:"not null"`; Role string `gorm:"not null;default:user"`; CreatedAt time.Time; UpdatedAt time.Time }
type Project struct { ID uint `gorm:"primaryKey"`; OwnerID uint `gorm:"index;not null"`; Name string `gorm:"not null"`; Description string; CreatedAt time.Time; UpdatedAt time.Time }
type InboxItem struct { ID uint `gorm:"primaryKey"`; ProjectID uint `gorm:"index;not null"`; CreatedBy uint `gorm:"index;not null"`; SourceType string `gorm:"not null"`; Title string; Body string; Status string `gorm:"not null;default:open"`; CreatedAt time.Time; UpdatedAt time.Time }
type Task struct { ID uint `gorm:"primaryKey"`; ProjectID uint `gorm:"index;not null"`; CreatedBy uint `gorm:"index;not null"`; AssigneeID *uint `gorm:"index"`; Title string `gorm:"not null"`; Description string; Status string `gorm:"not null;default:open"`; SortOrder int `gorm:"not null;default:0"`; DueAt *time.Time; CreatedAt time.Time; UpdatedAt time.Time }
type Note struct { ID uint `gorm:"primaryKey"`; ProjectID uint `gorm:"index;not null"`; CreatedBy uint `gorm:"index;not null"`; Title string `gorm:"not null"`; Markdown string `gorm:"type:text"`; CreatedAt time.Time; UpdatedAt time.Time }
type Source struct { ID uint `gorm:"primaryKey"`; ProjectID uint `gorm:"index;not null"`; CreatedBy uint `gorm:"index;not null"`; Kind string `gorm:"not null"`; Title string `gorm:"not null"`; URL string; FilePath string; ContentText string `gorm:"type:text"`; CreatedAt time.Time; UpdatedAt time.Time }
type AISession struct { ID uint `gorm:"primaryKey"`; ProjectID uint `gorm:"index;not null"`; CreatedBy uint `gorm:"index;not null"`; Title string `gorm:"not null"`; Context string `gorm:"type:text"`; CreatedAt time.Time; UpdatedAt time.Time }
type Tag struct { ID uint `gorm:"primaryKey"`; ProjectID uint `gorm:"index;not null"`; Name string `gorm:"not null"`; CreatedAt time.Time }
type ProjectEvent struct { ID uint `gorm:"primaryKey"`; ProjectID uint `gorm:"index;not null"`; ActorID uint `gorm:"index;not null"`; EventType string `gorm:"not null"`; EntityType string `gorm:"not null"`; EntityID uint `gorm:"not null"`; Summary string `gorm:"not null"`; CreatedAt time.Time }
type AIKey struct { ID uint `gorm:"primaryKey"`; UserID uint `gorm:"uniqueIndex;not null"`; Provider string `gorm:"not null"`; EncryptedAPIKey string `gorm:"not null"`; CreatedAt time.Time; UpdatedAt time.Time }
type AICallLog struct { ID uint `gorm:"primaryKey"`; UserID uint `gorm:"index;not null"`; Provider string `gorm:"not null"`; UsedKeyType string `gorm:"not null"`; Action string `gorm:"not null"`; Status string `gorm:"not null"`; Error string; CreatedAt time.Time }
type TaskShare struct { ID uint `gorm:"primaryKey"`; TaskID uint `gorm:"index;not null"`; ObjectType string `gorm:"not null"`; ObjectID uint `gorm:"not null"`; CreatedAt time.Time }

func AutoMigrate(database *gorm.DB) error {
	return database.AutoMigrate(&User{}, &Project{}, &InboxItem{}, &Task{}, &Note{}, &Source{}, &AISession{}, &Tag{}, &ProjectEvent{}, &AIKey{}, &AICallLog{}, &TaskShare{})
}
  • Step 4: 实现 PostgreSQL 连接

Create backend/internal/db/db.go:

package db

import (
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

func Open(databaseURL string) (*gorm.DB, error) {
	return gorm.Open(postgres.Open(databaseURL), &gorm.Config{})
}
  • Step 5: 验证并提交
Set-Location D:\work\senlinai\agent\backend
go test ./... -v
Set-Location D:\work\senlinai\agent
git add backend
git commit -m "feat: add core data model"

Task 3: 私有部署认证与邀请注册

Files:

  • Create: D:\work\senlinai\agent\backend\internal\auth\service.go
  • Create: D:\work\senlinai\agent\backend\internal\auth\service_test.go
  • Create: D:\work\senlinai\agent\backend\internal\auth\middleware.go

Interfaces:

  • Produces: auth.Service.CreateInvite(adminID uint, email string) (string, error)

  • Produces: auth.Service.RegisterWithInvite(token, email, displayName, password string) (*domain.User, error)

  • Produces: auth.Service.Login(email, password string) (string, error)

  • Produces: auth.RequireUser(tokenVerifier func(string) (uint, error)) gin.HandlerFunc

  • Write tests for invite registration, wrong-email rejection, and login failure.

  • Implement invite token signing with HMAC SHA-256.

  • Store password hashes with bcrypt.

  • Add bearer-token middleware that sets currentUserID.

  • Run go test ./... -v from backend.

  • Commit with git commit -m "feat: add private auth foundation".


Task 4: 项目、项目内标签与 Dashboard API

Files:

  • Create: D:\work\senlinai\agent\backend\internal\projects\service.go
  • Create: D:\work\senlinai\agent\backend\internal\projects\service_test.go
  • Create: D:\work\senlinai\agent\backend\internal\projects\handlers.go
  • Modify: D:\work\senlinai\agent\backend\internal\httpx\router.go

Interfaces:

  • Produces: projects.Service.CreateProject(ownerID uint, name string, description string) (*domain.Project, error)

  • Produces: projects.Service.ListProjects(ownerID uint) ([]domain.Project, error)

  • Produces: projects.Service.CreateTag(projectID uint, name string) (*domain.Tag, error)

  • Produces: projects.Service.ListTags(projectID uint) ([]domain.Tag, error)

  • Produces: projects.Service.Dashboard(projectID uint) (Dashboard, error)

  • Test that identical tag names can exist in different projects.

  • Test that dashboard counters only count objects under the requested project.

  • Implement project CRUD service.

  • Implement project-local tag service.

  • Implement GET /projects, POST /projects, GET /projects/:id/dashboard.

  • Run go test ./... -v from backend.

  • Commit with git commit -m "feat: add project workspace service".


Task 5: Inbox 收集与 AI 建议确认

Files:

  • Create: D:\work\senlinai\agent\backend\internal\inbox\service.go
  • Create: D:\work\senlinai\agent\backend\internal\inbox\service_test.go
  • Create: D:\work\senlinai\agent\backend\internal\inbox\handlers.go

Interfaces:

  • Produces: inbox.Service.Capture(input CaptureInput) (*domain.InboxItem, error)

  • Produces: inbox.Service.Analyze(itemID uint, userID uint) ([]Suggestion, error)

  • Produces: inbox.Service.Confirm(itemID uint, selected []Suggestion) error

  • Produces: Suggestion{Kind string, Title string, Body string}

  • Test capture of text, link, and file metadata into project inbox.

  • Test that Analyze returns suggestions but creates no official objects.

  • Test that Confirm creates selected task/note/source and marks the inbox item processed.

  • Implement analyzer interface with a static fake for tests.

  • Implement handlers for capture, analyze, and confirm.

  • Run go test ./... -v from backend.

  • Commit with git commit -m "feat: add inbox capture workflow".


Task 6: 任务、指派与显式共享

Files:

  • Create: D:\work\senlinai\agent\backend\internal\tasks\service.go
  • Create: D:\work\senlinai\agent\backend\internal\tasks\service_test.go
  • Modify: D:\work\senlinai\agent\backend\internal\domain\models.go

Interfaces:

  • Produces: tasks.Service.Assign(taskID uint, assigneeID uint) error

  • Produces: tasks.Service.ShareObject(taskID uint, objectType string, objectID uint) error

  • Produces: tasks.Service.VisibleLinkedObjects(taskID uint, viewerID uint) ([]LinkedObject, error)

  • Test that assignee can see the task.

  • Test that assignee cannot see linked note/source until explicitly shared.

  • Test unsupported shared object type returns an error.

  • Implement task assignment and sharing service.

  • Record assignment and sharing as project events.

  • Run go test ./... -v from backend.

  • Commit with git commit -m "feat: add task assignment sharing".


Task 7: 笔记、资料、本地文件存储与全文搜索

Files:

  • Create: D:\work\senlinai\agent\backend\internal\files\service.go
  • Create: D:\work\senlinai\agent\backend\internal\files\service_test.go
  • Create: D:\work\senlinai\agent\backend\internal\notes\service.go
  • Create: D:\work\senlinai\agent\backend\internal\search\service.go
  • Create: D:\work\senlinai\agent\backend\internal\search\service_test.go
  • Create: D:\work\senlinai\agent\backend\migrations\0001_search_indexes.sql

Interfaces:

  • Produces: files.Service.Save(projectID uint, originalName string, content io.Reader) (StoredFile, error)

  • Produces: notes.Service.CreateNote(projectID uint, userID uint, title string, markdown string) (*domain.Note, error)

  • Produces: search.Service.Search(userID uint, query string) ([]Result, error)

  • Test that file storage writes under projects/<projectID>/.

  • Test that file path traversal in original filename is neutralized with filepath.Base.

  • Test note body search through Search(userID, query).

  • Implement local file service.

  • Implement note/source service.

  • Implement portable test search with SQLite LIKE.

  • Add PostgreSQL tsvector indexes for notes, sources, inbox items, tasks, and AI sessions.

  • Add PostgreSQL search query using plainto_tsquery('simple', query).

  • Run go test ./... -v from backend.

  • Commit with git commit -m "feat: add files notes and search".


Task 8: AI Provider 网关、Key 选择、日志与 Session

Files:

  • Create: D:\work\senlinai\agent\backend\internal\ai\gateway.go
  • Create: D:\work\senlinai\agent\backend\internal\ai\gateway_test.go
  • Create: D:\work\senlinai\agent\backend\internal\ai\sessions.go

Interfaces:

  • Produces: ai.Gateway.SelectKey(userID uint) (SelectedKey, error)

  • Produces: ai.Gateway.RecordCall(userID uint, provider string, usedKeyType string, action string, status string, errText string) error

  • Produces: ai.SessionService.Create(projectID uint, userID uint, title string) (*domain.AISession, error)

  • Test that user key takes precedence over system key.

  • Test fallback to system key when user key is absent.

  • Test no available key returns an explicit error.

  • Test AI call logging records action, provider, key type, status, and error.

  • Implement AI session creation under a project.

  • Run go test ./... -v from backend.

  • Commit with git commit -m "feat: add ai gateway foundation".


Task 9: Web App Shell、项目 Dashboard 与标签页

Files:

  • Create: D:\work\senlinai\agent\apps\web\package.json
  • Create: D:\work\senlinai\agent\apps\web\src\app\App.tsx
  • Create: D:\work\senlinai\agent\apps\web\src\app\tabs.ts
  • Create: D:\work\senlinai\agent\apps\web\src\lib\api.ts
  • Create: D:\work\senlinai\agent\apps\web\src\features\projects\ProjectDashboard.tsx
  • Create: D:\work\senlinai\agent\apps\web\src\features\projects\ProjectDashboard.test.tsx

Interfaces:

  • Produces: api.getProjects(): Promise<Project[]>

  • Produces: api.getProjectDashboard(projectID: number): Promise<ProjectDashboardSummary>

  • Produces: openTab(existing: WorkspaceTab[], next: WorkspaceTab): WorkspaceTab[]

  • Scaffold Vite Svelte TypeScript app.

  • Install Svelte, Vitest, Testing Library for Svelte, jest-dom, jsdom.

  • Implement server address input on the login shell and persist it as API base URL.

  • Test dashboard renders pending inbox, current tasks, notes, and AI sessions.

  • Implement API client.

  • Implement browser-style tab state helper.

  • Implement dashboard component.

  • Run npm test -- --run and npm run build from apps/web.

  • Commit with git commit -m "feat: add web workspace shell".


Task 10: Inbox、任务、笔记资料与 AI Session UI

Files:

  • Create: D:\work\senlinai\agent\apps\web\src\features\inbox\InboxPanel.tsx
  • Create: D:\work\senlinai\agent\apps\web\src\features\inbox\SuggestionList.tsx
  • Create: D:\work\senlinai\agent\apps\web\src\features\tasks\TaskList.tsx
  • Create: D:\work\senlinai\agent\apps\web\src\features\notes\MarkdownEditor.tsx
  • Create: D:\work\senlinai\agent\apps\web\src\features\ai\AISessionCards.tsx
  • Create: D:\work\senlinai\agent\apps\web\src\features\inbox\SuggestionList.test.tsx

Interfaces:

  • Produces: SuggestionList with explicit checkbox confirmation.

  • Produces: MarkdownEditor controlled component using value and onChange.

  • Produces: AISessionCards card list for project sessions.

  • Test that SuggestionList does not confirm unselected suggestions.

  • Implement inbox capture panel.

  • Implement suggestion list with checkbox selection and editable basic fields.

  • Implement task list with status and assignee display.

  • Implement controlled Markdown editor.

  • Implement AI session card list.

  • Run npm test -- --run and npm run build from apps/web.

  • Commit with git commit -m "feat: add workspace feature panels".


Task 11: Tauri 桌面壳与快速收集

Files:

  • Create: D:\work\senlinai\agent\apps\desktop\package.json
  • Create: D:\work\senlinai\agent\apps\desktop\src-tauri\tauri.conf.json
  • Create: D:\work\senlinai\agent\apps\desktop\src-tauri\src\main.rs
  • Create: D:\work\senlinai\agent\apps\web\src\features\capture\QuickCapture.svelte

Interfaces:

  • Produces: Tauri app pointing at apps/web build/dev server.

  • Produces: global shortcut command opening quick capture window.

  • Produces: QuickCapture component with project destination and content input.

  • Scaffold Tauri app under apps/desktop.

  • Configure Tauri product name SenlinAI Workbench.

  • Configure dev URL http://localhost:5173.

  • Configure frontend dist ../../web/dist.

  • Implement QuickCapture with project selector and content textarea.

  • Verify npm run build from apps/web.

  • Verify npm run tauri build from apps/desktop, documenting any missing native dependency.

  • Commit with git commit -m "feat: add tauri desktop shell".


Task 12: 端到端验证与 MVP 文档

Files:

  • Create: D:\work\senlinai\agent\apps\web\e2e\project-workbench.spec.ts
  • Modify: D:\work\senlinai\agent\README.md
  • Create: D:\work\senlinai\agent\docs\mvp-verification.md

Interfaces:

  • Produces: one documented manual verification script.

  • Produces: Playwright smoke test for project dashboard and inbox suggestion confirmation.

  • Install Playwright in apps/web.

  • Add smoke test that verifies the project workbench screen and dashboard render.

  • Document backend verification: go test ./... -v from backend.

  • Document web verification: npm test -- --run, npm run build, npx playwright test.

  • Document desktop verification: build web first, then run Tauri build.

  • Document manual MVP flow: create project, capture inbox item, analyze, confirm task/note, assign task, share linked object, search, open AI session, quick capture.

  • Run all available verification commands.

  • Commit with git commit -m "test: add mvp verification plan".


Self-Review

Spec coverage:

  • 项目工作区、Dashboard、Inbox、任务、笔记资料、AI 会话、项目内标签、任务分发、文件存储、AI key 策略、搜索、Tauri 壳和验证流程均有对应任务。
  • 语义搜索、真实即时通讯、项目级成员体系、公开分享链接和移动原生 App 明确不进入 MVP。

Placeholder scan:

  • 本计划没有使用待填充占位内容。
  • 所有任务都有明确文件、接口、验证命令和提交命令。

Type consistency:

  • 后端统一使用 backend 目录。
  • Go module path 统一为 senlinai-agent/backend
  • 后端服务均以明确构造函数创建。
  • 核心模型来自 domain 包。
  • 前端 API 类型和 Dashboard 组件字段保持一致。

Implementation Status

Last updated: 2026-07-18.

  • Checkpoint 0: 规格、实施计划和 AGENTS.md 已提交。
  • Checkpoint 1: Monorepo 与 backend 基础脚手架已完成,GET /healthz 测试通过。
  • Checkpoint 2: 核心 Gorm 数据模型和 AutoMigrate 测试已完成。
  • Checkpoint 3: 私有部署邀请注册、bcrypt 登录和 bearer middleware 已完成。
  • Checkpoint 4: 项目、项目内标签和 Dashboard service/handler 已完成。
  • Checkpoint 5: Inbox 收集、AI 建议分析接口和确认创建对象流程已完成。
  • Checkpoint 6: 任务指派、显式共享和项目事件记录已完成。
  • Checkpoint 7: 本地文件服务、Markdown note service、搜索 service 和 PostgreSQL tsvector 索引迁移已完成。
  • Checkpoint 8: AI key 选择、系统 key fallback、调用日志和项目 AI session service 已完成。
  • Checkpoint 9: Svelte Web shell、服务器 IP/域名输入、Dashboard、API client 和标签页 helper 已完成。
  • Checkpoint 10: Svelte Inbox、AI 建议确认、任务列表、Markdown 编辑器、AI session 卡片和快速收集组件已完成。
  • Checkpoint 11: Tauri 2 桌面壳文件已完成Web 构建可供 Tauri 使用。
  • Checkpoint 12: 后端单元测试、PostgreSQL integration ping、Web component tests、Web build 和 Playwright smoke test 已完成。
  • Router/main wiring audit: /api route registrar 已接入,cmd/api 启动时连接 PostgreSQL、执行 AutoMigrate并注册项目与 Inbox handlers。
  • Search coverage audit: 搜索 service 已覆盖 project、task、note、source、inbox 和 AI session。
  • Credential hygiene audit: 当前文档不再保存真实 PostgreSQL 测试库连接串,集成验证统一通过 DATABASE_URL 注入。
  • Desktop binary verification: Cargo/Rust is available, and npm run build from apps/desktop passes with tauri build --no-bundle, producing target/release/senlinai_workbench.exe.

Verification evidence from the latest run:

  • backend: go test ./... -v passed.
  • PostgreSQL test server: go test -tags integration ./internal/db -run TestPostgresPing -v passed using DATABASE_URL.
  • apps/web: npm test -- --run passed.
  • apps/web: npm run build passed.
  • apps/web: npx playwright test passed.
  • apps/desktop: npm run build is blocked before compilation by incomplete Rust stable toolchain.

2026-07-18 Full Audit Follow-Up

  • Backend API startup now wires PostgreSQL, AutoMigrate, project routes, and inbox routes through /api.
  • Auth sessions are signed and expiring; legacy forgeable user:<id> tokens are rejected.
  • Project and inbox HTTP handlers use the authenticated bearer-token user instead of a hard-coded user ID.
  • Project dashboard reads are owner-scoped before counters are returned.
  • Search now covers projects, tasks, notes, sources, inbox items, and AI sessions.
  • Inbox confirmation keeps provenance through source_inbox_item_id on generated tasks, notes, and sources.
  • Task sharing rejects note/source objects from a different project.
  • AI user keys are encrypted at rest and gateway rate-limit checks are covered by tests.
  • Svelte web client calls the backend /api prefix, passes type checking, and has Playwright coverage for inbox suggestion confirmation.
  • Current Markdown docs no longer include the live PostgreSQL test credential; integration tests must receive it through DATABASE_URL.
  • Desktop executable build passes with Cargo/Rust available: npx tauri build --no-bundle --verbose produces apps/desktop/src-tauri/target/release/senlinai_workbench.exe.
  • Desktop installer bundling remains blocked on this machine because Tauri CLI packaging times out before producing target/release/bundle artifacts. Use npm run bundle to retry installer generation.