Claude Code Workflow

FlowTask

A local-first desktop task manager built with Tauri 2 + React + Rust. Every artifact below was generated through the OpenLogos methodology using Claude Code.

Tauri 2 React 18 Rust SQLite TypeScript Zustand
4 Scenarios
·
18 Test Cases
·
100% Coverage
·
Gate 3.5: PASS
Claude Code Integration Uses native .claude-plugin with skills/ commands/ hooks/ for methodology-driven development via conversational AI coding.
1
WHY

Requirements — Who needs this and why?

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

User Persona & Pain Points

Xiao Li, 28, Freelancer — juggles multiple projects, with tasks scattered across sticky notes, phone memos, and notebooks. Frequently misses deadlines because there's no single place to track everything.

P01 Tasks scattered across multiple locations → hard to find, easy to miss
P02 No categories → can't distinguish work/life/study tasks → constant confusion
P03 No account system → no sense of personal space on shared devices

Scenario Overview

IDScenarioTriggerPainPriority
S01New user registers and starts usingFirst app launchP01, P03P0
S02Login and manage today's tasksRegistered user opens appP01, P02P0
S03Organize tasks by categoryUser wants to classify tasksP02P1
S04Change account passwordUser wants to change passwordP03P1

Acceptance Criteria — S01: Register

Normal
GIVEN User opens app for the first time, no local account exists
WHEN User enters username (2-20 chars) and password (≥8 chars), clicks "Register"
THEN System creates local account, auto-logs in, redirects to task view with empty-state guidance
Exception
GIVEN Username "xiaoli" already exists locally
WHEN User tries to register with "xiaoli"
THEN Shows "This username is taken, please choose another"
Exception
GIVEN User is on register page
WHEN Password is fewer than 8 characters
THEN Shows "Password must be at least 8 characters"
2
WHAT

Product Design — What will users see and do?

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

Information Architecture

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 Prototype — Register & Main View

Source: logos/resources/prd/2-product-design/2-page-design/

Register / Login
Task View (Main)

Interactive HTML prototypes generated during Product Design phase — every page is designed before any code is written.

S01 Interaction Spec — Register Flow

  1. App launches, detects no local account → display Register page
  2. Register page contains: username input, password input, confirm password input, register button, "Already have an account? Log in" link
  3. Real-time validation: username 2-20 chars, password ≥8 chars, passwords match
  4. Submit success → create local account (password encrypted) → auto-login → redirect to Task View
  5. Task View first entry shows empty state: "No tasks yet, click + to create your first task"
FieldTypeRequiredValidation
UsernameText inputYes2-20 characters
PasswordPassword inputYes≥ 8 characters
Confirm PasswordPassword inputYesMust match password
3
HOW

Implementation — From architecture to verified code

Step 0

Architecture Overview

Source: 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

Business logic (forms, state, routing) in TypeScript; Rust handles only DB reads/writes and bcrypt encryption — minimizing Rust code while staying frontend-developer-friendly.

Step 1

Scenario → Sequence Diagram

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

S01: User Registration — Sequence Diagram

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)

Steps

  1. User opens FlowTask desktop app.
  2. React FE calls invoke('check_has_user') to ask Rust if any account exists locally.
  3. Tauri Rust queries SQLite: SELECT COUNT(*) FROM users.
  4. SQLite returns count = 0 — no local account.
  5. Rust returns has_user: false to FE.
  6. React FE renders Register page (if account exists, show Login instead).
  7. User fills username, password, confirm password, clicks "Register".
  8. React FE validates: username 2-20 chars, password ≥8, passwords match. On failure → show error, skip backend call. → EX-8.1 / EX-8.2 / EX-8.3
  9. React FE calls invoke('register') passing raw password to Rust (IPC, no network).
  10. Rust checks username uniqueness: SELECT id FROM users WHERE username = ?.
  11. SQLite returns empty — username available. → EX-10.1 (if taken)
  12. Rust hashes password with bcrypt (cost=12). Raw password never stored.
  13. Rust writes user: INSERT INTO users. → EX-13.1 (if write fails)
  14. SQLite returns new user record (id, username, created_at).
  15. Rust returns session to FE.
  16. React FE writes to Zustand Store, establishing login session (no token needed for desktop app).
  17. React FE redirects to Task View with empty-state guidance.
Exception Cases
EX-8.1 Username empty or wrong length → show error, skip backend
EX-8.2 Password < 8 chars → show error, skip backend
EX-8.3 Passwords do not match → show error, skip backend
EX-10.1 Username taken → return USERNAME_EXISTS
EX-13.1 DB write failure → return DB_WRITE_ERROR
Step 2

API Specs + DB Schema

API Specification — Tauri IPC Commands

Source: logos/resources/api/tasks.yaml

Database Schema — SQLite Tables

Source: logos/resources/database/schema.yaml

Step 3

Test Case Design (before code!)

Source: logos/resources/test/S01-test-cases.md

S01 Test Cases — Unit Tests (excerpt)

IDDescriptionInputExpected
UT-S01-01Username exactly 2 chars (lower boundary)"ab"Pass
UT-S01-03Username 1 char (below boundary)"a"Error: 2-20 chars required
UT-S01-06Password exactly 8 chars (lower boundary)"Pass1234"Pass
UT-S01-07Password 7 chars (below boundary)"Pass123"Error: ≥ 8 chars required
UT-S01-11Duplicate username → DB UNIQUE constraint"xiaoli" (exists)USERNAME_EXISTS
UT-S01-14Password stored as bcrypt hash"Pass1234"Starts with $2b$

S01 Test Cases — Scenario Tests (excerpt)

IDDescriptionCovers
ST-S01-01Full register flow: launch → register → task viewSteps 1-17
ST-S01-02Empty username → frontend interceptEX-8.1
ST-S01-05Duplicate username → backend errorEX-10.1
Step 4

Code Generation — Design-driven implementation via Claude Code

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

Claude Code — Conversational AI Coding

Every Tauri command is annotated with the scenario step it implements. Claude Code reads the sequence diagram and API spec, then generates backend + frontend + tests in closed-loop batches.

Backend — Rust Tauri Command → maps to 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![],
    })
}

Orchestration Test → verifies S01 end-to-end

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 Commands
6React Pages
18Orchestration Tests
S01–S04Full Coverage
Step 5

openlogos verify — Automated Acceptance

Source: logos/resources/verify/acceptance-report.md

Gate 3.5: PASS
Generated on 2026-04-07
18 Defined
18 Executed
18 Passed
0 Failed
100% Coverage

AC Traceability (excerpt)

S01-AC-001 Register returns UserSession with user_id → UT-S01-01 PASS
S01-AC-002 Duplicate username returns USERNAME_EXISTS → UT-S01-02 PASS
S02-AC-005 Create task returns complete TaskItem → UT-S02-05 PASS
S03-AC-003 Delete category sets task.category_id to null → UT-S03-03 PASS
S04-AC-001 Change password returns success=true → UT-S04-01 PASS
… 13 more, all PASS
Δ
DELTA

Change Tracking — Every iteration is impact-aware

Traditional AI coding ("vibe coding") fixes the code but forgets the design doc, the API spec, and the test cases. Over time, documentation drifts from reality and becomes useless. OpenLogos solves this with a structured delta workflow — every change, no matter how small, must evaluate its ripple effect across the entire artifact chain.

1
proposal.mdReason, type, impact scope, summary
2
tasks.mdPhase-based task checklist
3
Delta filesProduce changes per task
4
Mergeopenlogos merge
5
Code + TestImplement & verify
6
ArchiveClose the proposal

Change Propagation Rules

The change type determines the minimum update scope. A seemingly small feature request can cascade through the entire chain:

Change Type
Minimum Updates Required
Requirement
ReqDesignScenarioAPI/DBTestCode
Design
ReqDesignScenarioAPI/DBTestCode
Interface
ReqDesignScenarioAPI/DBTestCode
Code-level
ReqDesignScenarioAPI/DBTestCode

Real Example — Code-level Fix

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

fix-register-redirect

Bug Fix S01 — Register Flow

Problem: After registration, the app didn't redirect to Task View. User was stuck on the Register page.

Root cause: useEffect condition required view === "loading", but after register, view was "register" — redirect never fired.

Impact Analysis

RequirementsNo changeRequirement correctly specifies redirect
Product DesignNo changeDesign is correct as-is
Sequence DiagramNo changeS01 Steps 16→17 already define redirect
API SpecNo changeTauri IPC commands unchanged
DB SchemaNo changeNo schema modifications
Test CasesNo changeExisting ST-S01-01 covers this path
CodeAffectedApp.tsx — extend redirect condition to cover register & login views
Verdict: Code-level fix only. The bug was a missing condition branch — all upstream artifacts (requirements, design, API, DB) are correct. Defensively also covers the S02 login redirect path.

Want to try it yourself?

Clone the repo and run FlowTask locally, or start your own project with OpenLogos.