OpenCode Workflow

Money-Log

A lightweight desktop expense tracker built with Electron + React + TypeScript. Same OpenLogos methodology, different AI tool and tech stack.

Electron React 18 TypeScript SQLite Zustand Tailwind CSS
4 Scenarios
·
73 Test Cases
·
100% Coverage
·
Gate 3.5: PASS
OpenCode Integration Uses .opencode/plugins/openlogos.js with commands/ hooks/ for methodology-driven development in the terminal.
1
WHY

Requirements — Who needs this and why?

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

User Persona & Pain Points

Working professionals, age 24-50 — monthly income 5K-30K CNY. No active bookkeeping habit, only passively check Alipay/WeChat transaction history. Need quick recording, categorized spending, and consumption trend insights.

P01 Cannot remember where money went — transaction records are scattered, no category summaries
P02 Want to track expenses but existing apps are too complex → give up after a week
P03 Manual monthly tallying by category is too tedious
P04 Shared desktop — no data protection for financial records

Scenario Overview

IDScenarioTriggerPainPriority
S01Quick expense loggingUser opens app to recordP02P0
S02View statistics reportUser enters statistics pageP01, P03P0
S03Manage expense categoriesUser manages categoriesP01P1
S04Set password lockUser enables/disables lockP04P1

Acceptance Criteria — S01: Quick Expense Logging

Normal
GIVEN User has opened the app, all categories are displayed
WHEN User selects "Dining", enters "35.5", clicks "Log"
THEN Shows "Recorded", category stays on "Dining", amount clears, ready for next entry
Exception
GIVEN User has opened the app
WHEN No category selected, user enters amount and clicks "Log"
THEN Button is disabled or shows "Please select a category"
Exception
GIVEN User selected "Entertainment" category
WHEN User enters "abc" or "-50", clicks "Log"
THEN Shows "Please enter a valid amount", does not save
2
WHAT

Product Design — What will users see and do?

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

Page Architecture

┌─────────────────────────────────────────┐
│            Money-Log Logo               │
├─────────────────────────────────────────┤
│                                         │
│   ┌─────────┐ ┌─────────┐ ┌─────────┐  │
│   │  Log    │ │  Stats  │ │ Settings│  │
│   │(default)│ │         │ │         │  │
│   └─────────┘ └─────────┘ └─────────┘  │
│                                         │
│  ┌─────────────────────────────────┐    │
│  │      [Page Content Area]        │    │
│  │                                 │    │
│  └─────────────────────────────────┘    │
│                                         │
└─────────────────────────────────────────┘

Window: 800 x 600 px (min: 600 x 500)
3 tabs: Log / Statistics / Settings

UI Prototype — Desktop App

Source: logos/resources/prd/2-product-design/2-page-design/01-prototype.html

Interactive HTML prototype with all 3 tabs (Log / Statistics / Settings) — designed before any code is written.

S01 Interaction — Quick Logging Flow

  1. App opens → display default logging page with category grid (8 preset categories with emoji icons)
  2. User taps a category → highlight selected, others dim
  3. User enters amount using numeric keyboard → real-time validation, filter non-numeric chars
  4. User optionally adds a remark (max 200 chars)
  5. Click "Log" → save to SQLite → show "Recorded" toast → clear amount, keep category
FieldTypeRequiredValidation
CategoryIcon grid selectionYesMust select one
AmountNumeric inputYes> 0, max 999999.99
RemarkText inputNoMax 200 chars
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 Renderer["Renderer Process (React)"]
        UI["React Components
UI Layer"] State["Zustand Store
State Management"] IPC["IPC Bridge
Process Communication"] end subgraph Client["Client (Electron Main Process)"] Main["Main Process
Backend Logic"] DB["SQLite Database
Local Storage"] Store["electron-store
Config Storage"] end UI --> State State --> IPC IPC --> Main Main --> DB Main --> Store

UI in Renderer (React + Zustand), data in Main Process (better-sqlite3). IPC bridge handles all communication. Password lock config stored in electron-store separately from expense data.

Step 1

Scenario → Sequence Diagram

Source: logos/resources/prd/3-technical-plan/2-scenario-implementation/S01-快速记账.md

S01: Quick Expense Logging — Sequence Diagram

sequenceDiagram
    participant U as User
    participant UI as React UI
    participant Store as Zustand Store
    participant IPC as IPC Bridge
    participant DB as SQLite Database

    U->>UI: Step 1: Select category
    UI->>UI: Step 2: Update selection — highlight active
    UI->>U: Step 3: Show selected state

    U->>UI: Step 4: Enter amount via keyboard
    UI->>UI: Step 5: Validate format — filter invalid chars
    UI->>UI: Step 6: Enable/disable button by validity
    UI->>U: Step 7: Display amount

    U->>UI: Step 8: Click "Log" button
    UI->>Store: Step 9: Submit {amount, category_id}
    Store->>IPC: Step 10: saveRecord(amount, category_id)
    IPC->>DB: Step 11: INSERT INTO records
    DB-->>IPC: Step 12: Return write result
    IPC-->>Store: Step 13: Return success/failure
    Store-->>UI: Step 14: Update state
    UI->>U: Step 15: Show "Recorded" toast

    UI->>UI: Step 16: Clear amount input
    UI->>UI: Step 17: Keep category selected
    UI->>U: Step 18: Ready for next entry

Steps

  1. User taps a category label (e.g. "Dining").
  2. React UI updates selected state, visually highlighting the current category.
  3. UI shows selection feedback to user.
  4. User enters amount via keyboard.
  5. React UI validates input format, filtering non-numeric characters.
  6. React UI enables/disables the "Log" button based on input validity (disabled when no category selected or amount is 0).
  7. UI displays the entered amount in real time.
  8. User clicks the "Log" button to submit.
  9. Zustand Store receives expense data amount, category_id.
  10. Store calls saveRecord() via IPC to the main process.
  11. SQLite executes INSERT INTO records.
  12. Database returns write result (success or error).
  13. IPC returns result to Store.
  14. Store updates state, notifying UI.
  15. UI shows "Recorded" toast notification.
  16. React UI clears the amount input field for the next entry.
  17. React UI keeps category selected to reduce repeated selection.
  18. UI enters ready state, waiting for the next expense entry.
Exception Cases
EX-8.1 No category selected → button disabled
EX-8.2 Amount is 0 or empty → button disabled
EX-11.1 DB write failure → show error toast, keep input
EX-11.2 Amount exceeds 2 decimal places → auto-truncate to 2
Step 2

API Specs + DB Schema

API Specification — Electron IPC Channels

Source: logos/resources/api/local-api.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-01Amount is required{}400: missing required field
UT-S01-02Amount must be > 0{amount: 0}400: amount must be > 0
UT-S01-05Category ID must exist in DBcategory_id: 999400: category not found
UT-S01-06Remark max 200 chars"a".repeat(201)400: remark too long
UT-S01-10Amount truncated to 2 decimals12.345Stored as 12.34
UT-S01-11Non-numeric chars filtered"abc"Filtered to empty

S01 Scenario Tests (excerpt)

IDDescriptionCovers
ST-S01-01Full logging flow: select → enter → save → toastSteps 1-18
ST-S01-02Consecutive logs with same categorySteps 1-18
ST-S01-05Invalid amount format rejectedEX-5.1
73 total test cases across S01-S04, covering expense logging, statistics, category management, and password lock — all designed before writing a single line of implementation code.
Step 4

Code Generation — Design-driven implementation via OpenCode

Source: src/main/database.js · src/renderer/pages/ · src/__tests__/

OpenCode — Terminal-first AI Coding

OpenCode reads the sequence diagram and API spec via slash commands, then generates Electron main process + React frontend + Vitest tests in batch. Each batch closes the loop: business code → test code → OpenLogos reporter.

Data Layer — Electron Main Process → maps to S01 Steps 10-13

src/main/database.jsJavaScript
class DatabaseManager {
  // S01 Steps 10-13: Save a new expense record
  saveRecord(amount, category_id, remark = null, created_at = null) {
    const amountInCents = Math.round(amount * 100);
    const timestamp = created_at || new Date().toISOString();
    this.db.run(
      `INSERT INTO records (amount, category_id, remark, created_at)
       VALUES (?, ?, ?, ?)`,
      [amountInCents, category_id, remark, timestamp]
    );
    const result = this.db.exec('SELECT last_insert_rowid() as id');
    this.save();
    return result[0].values[0][0];
  }
}

Unit + Scenario Tests → verifies S01 acceptance criteria

src/__tests__/S01.test.tsTypeScript
describe('S01: Quick Logging - Unit Tests', () => {
  describe('UT-S01-01: Amount is required', () => {
    it('should reject empty amount', async () => {
      const data = {};
      const hasAmount = data.amount !== undefined;
      expect(hasAmount).toBe(false);
    });
  });

  describe('UT-S01-10: Amount truncated to 2 decimals', () => {
    it('should auto-truncate to 2 decimal places', () => {
      const amount = 12.345;
      const formatted = Math.floor(amount * 100) / 100;
      expect(formatted).toBe(12.34);
    });
  });
});

describe('S01: Quick Logging - Scenario Tests', () => {
  describe('ST-S01-01: Full logging flow', () => {
    it('should correctly convert amount to cents', () => {
      const amount = 35.5;
      const amountInCents = Math.round(amount * 100);
      expect(amountInCents).toBe(3550);
    });
  });
});
4React Pages
1Zustand Store
73Test Cases
S01–S04Full Coverage
Step 5

openlogos verify — Automated Acceptance

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

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

Design-time Coverage Assertions

Phase 1 normal acceptance criteria: all covered
Phase 1 exception acceptance criteria: all covered
EX exception cases: all covered
API required fields: all covered
DB UNIQUE/CHECK constraints: all covered
… across S01-S04 test cases
Δ
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 — Feature: Add Remark to Expense Logging

Source: logos/changes/archive/每天记账增加备注功能/proposal.md

Add remark field to expense logging

Feature S01 — Quick Logging

Request: Users need to add notes to expenses (e.g. "hotpot dinner", "taxi to hospital"). The DB column remark already exists — but the front-end UI had no input field, and the IPC channel didn't pass the parameter through.

Impact Analysis

RequirementsNo changeOriginal requirement already mentions optional remark
Product DesignAffectedAdd remark textarea below amount input in the quick-log panel
Sequence DiagramAffectedS01 Step 7 updated: user fills remark → passed to IPC
API SpecAffectedsave-record channel — add remark param (string, optional, max 200 chars)
DB SchemaNo changerecords.remark column already exists (TEXT, nullable)
Test CasesAffectedAdd UT-S01-12 (remark max 200 chars), update ST-S01-01 to include remark flow
CodeAffectedRenderer: add textarea component. Main process: pass remark to DB insert
Verdict: Design-level change — cascades from product design through scenario, API, test, and code. Requirements and DB schema are unaffected. Without impact analysis, a developer would likely just add the UI field and forget to update the sequence diagram, API spec, and test cases.

More Archived Proposals

Money-Log has 8 archived change proposals — each following the same structured delta workflow.

Fix: password not persisted

Bug Fix S04

Code-level fix — electron-store write not awaited before close.

Monthly spending detail view

Feature S02

Design-level — new list view under statistics chart.

Want to try it yourself?

Clone the repo and run Money-Log locally, or start your own project with OpenLogos.