Claude Code 工作流

FlowTask

一个用 Tauri 2 + React + Rust 构建的本地优先桌面任务管理器。下面每一份产物都是用 OpenLogos 方法论、借助 Claude Code 生成的。

Tauri 2 React 18 Rust SQLite TypeScript Zustand
4 场景
·
18 测试用例
·
100% 覆盖率
·
Gate 3.5: PASS
·
已部署
·
Smoke PASS
Claude Code 集成 使用原生 .claude-plugin,配合 skills/ commands/ hooks/,通过对话式 AI 编程实现方法论驱动的开发。
1
WHY

需求 —— 谁需要它,为什么?

来源:logos/resources/prd/1-product-requirements/01-requirements.md

用户画像与痛点

小李,28 岁,自由职业者 —— 同时兼顾多个项目,任务散落在便利贴、手机备忘录和笔记本里。常常错过截止日期,因为没有一个统一的地方来追踪一切。

P01 任务散落在多个地方 → 难以查找,容易遗漏
P02 没有分类 → 工作/生活/学习的任务分不清 → 时常混乱
P03 没有账户体系 → 在共用设备上没有个人空间感

场景概览

ID场景触发痛点优先级
S01新用户注册并开始使用首次启动应用P01, P03P0
S02登录并管理今天的任务已注册用户打开应用P01, P02P0
S03按分类组织任务用户想给任务归类P02P1
S04修改账户密码用户想修改密码P03P1

验收标准 —— S01:注册

正常
GIVEN 用户首次打开应用,本地不存在任何账户
WHEN 用户输入用户名(2-20 字符)和密码(≥8 字符),点击「注册」
THEN 系统创建本地账户,自动登录,跳转到任务视图并显示空状态引导
异常
GIVEN 用户名「xiaoli」本地已存在
WHEN 用户尝试用「xiaoli」注册
THEN 显示「该用户名已被占用,请换一个」
异常
GIVEN 用户在注册页
WHEN 密码少于 8 个字符
THEN 显示「密码至少需要 8 个字符」
2
WHAT

产品设计 —— 用户会看到什么、做什么?

来源:logos/resources/prd/2-product-design/1-feature-specs/01-feature-specs.md

信息架构

FlowTask
├── Auth Module
│   ├── Register Page (first launch)
│   └── Login Page (returning user)
└── Main App (after login)
    ├── Task View (default)
    │   ├── Category Sidebar
    │   ├── Task List
    │   └── Create/Edit Task Panel
    └── Account Settings
        └── Change Password Form

UI 原型 —— 注册与主视图

来源:logos/resources/prd/2-product-design/2-page-design/

注册 / 登录
任务视图(主界面)

在产品设计阶段生成的交互式 HTML 原型 —— 每个页面都在写任何代码之前就设计好了。

S01 交互规格 —— 注册流程

  1. 应用启动,检测到不存在本地账户 → 显示注册页
  2. 注册页包含:用户名输入框、密码输入框、确认密码输入框、注册按钮、「已有账户?登录」链接
  3. 实时校验:用户名 2-20 字符、密码 ≥8 字符、两次密码一致
  4. 提交成功 → 创建本地账户(密码加密)→ 自动登录 → 跳转到任务视图
  5. 任务视图首次进入显示空状态:「还没有任务,点击 + 创建你的第一个任务」
字段类型必填校验
用户名文本输入框2-20 个字符
密码密码输入框≥ 8 个字符
确认密码密码输入框必须与密码一致
3
HOW

实现 —— 从架构到验证通过的代码

Step 0

架构概览

来源:logos/resources/prd/3-technical-plan/1-architecture/01-architecture-overview.md

graph TB
    subgraph FE["Frontend (React 18 + TypeScript)"]
        Pages["React Components
UI Pages"]
        Store["Zustand Store
Global State"]
        IPC["Tauri IPC
Frontend-Backend Bridge"]
    end

    subgraph BE["Backend (Rust Commands)"]
        Commands["auth · task · category
Auth / Task CRUD / Category CRUD / bcrypt"]
    end

    subgraph DB["Data Layer"]
        SQLite[("SQLite
Local File Storage")]
    end

    Pages --> Store
    Store --> IPC
    IPC --> Commands
    Commands --> SQLite

业务逻辑(表单、状态、路由)放在 TypeScript;Rust 只负责数据库读写和 bcrypt 加密 —— 在保持前端开发者友好的同时,尽量减少 Rust 代码。

Step 1

场景 → 时序图

来源:logos/resources/prd/3-technical-plan/2-scenario-implementation/S01-register.md

S01:用户注册 —— 时序图

sequenceDiagram
    participant U as User
    participant FE as React Frontend
    participant BE as Tauri Commands (Rust)
    participant DB as SQLite

    U->>FE: Step 1: Open app
    FE->>BE: Step 2: invoke('check_has_user')
    BE->>DB: Step 3: SELECT COUNT(*) FROM users
    DB-->>BE: Step 4: return count = 0
    BE-->>FE: Step 5: return { has_user: false }
    FE-->>U: Step 6: Render Register page

    U->>FE: Step 7: Fill username, password, confirm & click Register
    FE->>FE: Step 8: Validate — name 2-20 chars, pwd ≥8, match
    Note over FE: Fail → EX-8.1 / EX-8.2 / EX-8.3

    FE->>BE: Step 9: invoke('register', { username, password })
    BE->>DB: Step 10: SELECT id FROM users WHERE username = ?
    DB-->>BE: Step 11: return empty (username available)
    Note over BE: Taken → EX-10.1

    BE->>BE: Step 12: bcrypt::hash(password, cost=12)
    BE->>DB: Step 13: INSERT INTO users (username, password)
    DB-->>BE: Step 14: return new user { id, username, created_at }
    Note over BE: Write fail → EX-13.1

    BE-->>FE: Step 15: return { user_id, username }
    FE->>FE: Step 16: Write Zustand Store { currentUser }
    FE-->>U: Step 17: Redirect to Task View (empty state)

步骤

  1. 用户打开 FlowTask 桌面应用。
  2. React 前端调用 invoke('check_has_user'),询问 Rust 本地是否存在任何账户。
  3. Tauri Rust 查询 SQLite:SELECT COUNT(*) FROM users
  4. SQLite 返回 count = 0 —— 没有本地账户。
  5. Rust 向前端返回 has_user: false
  6. React 前端渲染注册页(若账户已存在,则改为显示登录页)。
  7. 用户填写用户名、密码、确认密码,点击「注册」。
  8. React 前端校验:用户名 2-20 字符、密码 ≥8、两次密码一致。校验失败 → 显示错误,跳过后端调用。→ EX-8.1 / EX-8.2 / EX-8.3
  9. React 前端调用 invoke('register'),将明文密码传给 Rust(IPC,无网络)。
  10. Rust 检查用户名是否唯一:SELECT id FROM users WHERE username = ?
  11. SQLite 返回空 —— 用户名可用。→ EX-10.1(若已被占用)
  12. Rust 用 bcrypt(cost=12)对密码做哈希。明文密码从不存储。
  13. Rust 写入用户:INSERT INTO users。→ EX-13.1(若写入失败)
  14. SQLite 返回新用户记录(id、username、created_at)。
  15. Rust 向前端返回会话。
  16. React 前端写入 Zustand Store,建立登录会话(桌面应用无需 token)。
  17. React 前端跳转到任务视图,并显示空状态引导。
异常情况
EX-8.1 用户名为空或长度不符 → 显示错误,跳过后端
EX-8.2 密码 < 8 字符 → 显示错误,跳过后端
EX-8.3 两次密码不一致 → 显示错误,跳过后端
EX-10.1 用户名已被占用 → 返回 USERNAME_EXISTS
EX-13.1 数据库写入失败 → 返回 DB_WRITE_ERROR
Step 2

API 规格 + 数据库 Schema

API 规格 —— Tauri IPC 命令

来源:logos/resources/api/tasks.yaml

数据库 Schema —— SQLite 表

来源:logos/resources/database/schema.yaml

Step 3

测试用例设计(先于代码!)

来源:logos/resources/test/S01-test-cases.md

S01 测试用例 —— 单元测试(节选)

ID描述输入预期
UT-S01-01用户名恰好 2 字符(下边界)"ab"通过
UT-S01-03用户名 1 字符(低于边界)"a"错误:需 2-20 字符
UT-S01-06密码恰好 8 字符(下边界)"Pass1234"通过
UT-S01-07密码 7 字符(低于边界)"Pass123"错误:需 ≥ 8 字符
UT-S01-11重复用户名 → 数据库 UNIQUE 约束"xiaoli"(已存在)USERNAME_EXISTS
UT-S01-14密码以 bcrypt 哈希存储"Pass1234"$2b$ 开头

S01 测试用例 —— 场景测试(节选)

ID描述覆盖
ST-S01-01完整注册流程:启动 → 注册 → 任务视图Steps 1-17
ST-S01-02用户名为空 → 前端拦截EX-8.1
ST-S01-05重复用户名 → 后端报错EX-10.1
Step 4

代码生成 —— 经由 Claude Code 的设计驱动实现

来源:src-tauri/src/lib.rs · src/pages/ · src-tauri/tests/orchestration.rs

Claude Code —— 对话式 AI 编程

每个 Tauri 命令都标注了它实现的场景步骤。Claude Code 读取时序图和 API 规格,然后以闭环批次生成后端 + 前端 + 测试。

后端 —— Rust Tauri 命令 → 对应 S01 Steps 9-15

src-tauri/src/lib.rsRust
// ── S01 Step 9-15: register ───────────────────────
#[tauri::command]
async fn register(
    params: RegisterParams,
    pool: tauri::State<'_, SqlitePool>,
) -> CmdResult<UserSession> {
    let row = sqlx::query("SELECT COUNT(*) as count FROM users WHERE username = ?")
        .bind(&params.username).fetch_one(pool.inner()).await
        .map_err(|_| CommandError::new("DB_ERROR", "Query failed"))?;
    if row.get::<i64, _>("count") > 0 {
        return Err(CommandError::new("USERNAME_EXISTS", "Username taken"));
    }
    let pw_hash = hash(&params.password, DEFAULT_COST)
        .map_err(|_| CommandError::new("HASH_ERROR", "Hash failed"))?;
    let result = sqlx::query("INSERT INTO users (username, password) VALUES (?, ?)")
        .bind(&params.username).bind(&pw_hash)
        .execute(pool.inner()).await
        .map_err(|_| CommandError::new("DB_WRITE_ERROR", "Write failed"))?;
    Ok(UserSession {
        user_id: result.last_insert_rowid(),
        username: params.username,
        tasks: vec![], categories: vec![],
    })
}

编排测试 → 端到端验证 S01

src-tauri/tests/orchestration.rsRust
#[tokio::test]
async fn ot_s01_01_register_success() {
    let pool = setup_db().await;
    let session = cmd_register("xiaoli".into(), "Pass1234".into(), &pool)
        .await.expect("register should succeed");
    assert_eq!(session.username, "xiaoli");
    assert!(session.user_id > 0);
    assert!(session.tasks.is_empty());
}

#[tokio::test]
async fn ot_s01_02_register_username_exists() {
    let pool = setup_db().await;
    cmd_register("xiaoli".into(), "Pass1234".into(), &pool).await.unwrap();
    let err = cmd_register("xiaoli".into(), "Other123".into(), &pool)
        .await.expect_err("duplicate should fail");
    assert_eq!(err.code, "USERNAME_EXISTS");
}
10Tauri 命令
6React 页面
18编排测试
S01–S04全覆盖
Step 5

openlogos verify —— 自动化验收

来源:logos/resources/verify/acceptance-report.md

Gate 3.5: PASS
生成于 2026-04-07
18 已定义
18 已执行
18 已通过
0 失败
100% 覆盖率

验收标准可追溯性(节选)

S01-AC-001 注册返回带 user_id 的 UserSession → UT-S01-01 PASS
S01-AC-002 重复用户名返回 USERNAME_EXISTS → UT-S01-02 PASS
S02-AC-005 创建任务返回完整的 TaskItem → UT-S02-05 PASS
S03-AC-003 删除分类将 task.category_id 置为 null → UT-S03-03 PASS
S04-AC-001 修改密码返回 success=true → UT-S04-01 PASS
…… 另有 13 条,全部 PASS

部署与 smoke 门禁

示例项目补齐部署完成标记,并通过部署后的最小健康检查。

Gate 3.8: PASS
部署状态 DEPLOY_DONE

部署执行已完成,状态由 OpenLogos 受控记录。

Smoke 状态 SMOKE_PASS

部署后 smoke 用例通过,可追溯到 smoke 报告。

Δ
DELTA

变更追踪 —— 每次迭代都洞察影响范围

传统的 AI 编程(「vibe coding」)只改代码,却忘了设计文档、API 规格和测试用例。久而久之,文档与现实脱节,变得毫无用处。OpenLogos 用一套结构化的 delta 工作流解决这一问题 —— 任何变更,无论多小,都必须评估它在整条产物链上的连锁影响。

1
proposal.md原因、类型、影响范围、概述
2
tasks.md按 Phase 划分的任务清单
3
Delta 文件逐任务产出变更
4
合并openlogos merge
5
代码 + 测试实现并验证
6
部署openlogos deploy-done
7
smokeopenlogos smoke
8
归档关闭提案

变更传播规则

变更类型决定了最小更新范围。一个看似微小的功能请求,可能会沿着整条链条层层传导:

变更类型
所需的最小更新
需求
需求设计场景API/DB测试代码
设计
需求设计场景API/DB测试代码
接口
需求设计场景API/DB测试代码
代码级
需求设计场景API/DB测试代码

真实示例 —— 代码级修复

来源:logos/changes/archive/fix-register-redirect/proposal.md

fix-register-redirect

Bug 修复 S01 —— 注册流程

问题:注册后,应用没有跳转到任务视图。用户卡在注册页。

根因:useEffect 的条件要求 view === "loading",但注册后 view"register" —— 跳转从未触发。

影响分析

需求无变更需求已正确规定了跳转
产品设计无变更设计本身是正确的
时序图无变更S01 Steps 16→17 已定义了跳转
API 规格无变更Tauri IPC 命令不变
数据库 Schema无变更无 schema 改动
测试用例无变更现有的 ST-S01-01 已覆盖此路径
代码受影响App.tsx —— 扩展跳转条件以覆盖 register 与 login 视图
结论:仅为代码级修复。该 bug 是缺失了一个条件分支 —— 所有上游产物(需求、设计、API、DB)都是正确的。同时也防御性地覆盖了 S02 登录跳转路径。

想亲手试试?

克隆仓库在本地运行 FlowTask,或用 OpenLogos 开启你自己的项目。