Fastwhisper / docs /ARCHITECTURE.md
Mbonea's picture
Close P1 arch-code gaps: loop summary, template-echo Rules, Move now, proof kinds.
57ed4c2
|
Raw
History Blame Contribute Delete
9.45 kB

Habit Journal β€” Architecture

Single-user PWA on a Hugging Face Docker Space: FastAPI backend + Vite/Preact frontend, JSON/JSONL under DATA_ROOT, server-owned stats. Optional OpenRouter for coach/reschedule with Rules backup.

Interactive canvas (Cursor): habit-journal-architecture.canvas.tsx


Stack at a glance

Layer Technology
Host Hugging Face Docker Space
Server Uvicorn + FastAPI (app.main:create_app)
Static UI Vite β†’ static/ (PWA shell + service worker)
Data JSON/JSONL under DATA_ROOT (default /data/loop_logger)
Optional LLM OpenRouter (coach + reschedule); Rules backup if missing

One line: HF Docker β†’ Uvicorn/FastAPI β†’ JSONL stores β†’ Vite/Preact PWA (credentials: 'include') β†’ optional OpenRouter with Rules backup.


System overview

flowchart TB
  subgraph clients [Clients]
    Browser[Browser PWA]
    Agent[Cursor agent]
  end

  subgraph space [HF Docker Space]
    Static[static/ Vite build]
    API[FastAPI create_app]
    SW[Service Worker shell-only]
  end

  subgraph data [DATA_ROOT]
    Config[config.json]
    Entries[entries.jsonl]
    Daily[daily.jsonl]
    Traces[traces.jsonl]
    Sched[schedule/]
  end

  OR[OpenRouter optional]

  Browser --> Static
  Browser --> API
  Browser --> SW
  Agent -->|Bearer AGENT_TOKEN| API
  API --> Config
  API --> Entries
  API --> Daily
  API --> Traces
  API --> Sched
  API -.->|coach / reschedule| OR

Notes:

  • Browser loads the static shell; /api/* is network-only (SW must never cache API).
  • Agent uses Authorization: Bearer AGENT_TOKEN on /api/agent/* (session login also works as fallback).

Backend layers

flowchart LR
  subgraph edge [HTTP]
    RAuth[auth]
    REnt[entries]
    RDaily[daily]
    RPlan[plan]
    RAgent[agent]
    RCoach[coach]
    RStats[stats]
    RDbg[debug/export/settings/health]
  end

  subgraph services [Services]
    CoachSvc[CoachService]
    Resched[RescheduleService]
    Evidence[evidence + stats_math]
    SchedMath[schedule_math]
  end

  subgraph stores [Stores]
    EntryStore
    DailyStore
    ScheduleStore
    TraceStore
    ConfigStore
  end

  RAuth --> ConfigStore
  REnt --> EntryStore
  RDaily --> DailyStore
  RPlan --> ScheduleStore
  RPlan --> Resched
  RAgent --> ScheduleStore
  RAgent --> DailyStore
  RAgent --> EntryStore
  RCoach --> CoachSvc
  CoachSvc --> Evidence
  CoachSvc --> TraceStore
  RStats --> Evidence
  Resched --> SchedMath
  ScheduleStore --> SchedMath

Module map

Module Responsibility
app/routers/* HTTP edge; envelope {ok, data, error}
app/store_* JSONL persistence + file locks
app/stats_math.py, app/evidence.py Server-owned ranks & SERVER_PICKS
app/coach_service.py, app/backup_replies.py OpenRouter or Rules backup
app/schedule_* Plan math, locked EV, reschedule
app/deps.py, app/security_passwords.py Session + AGENT_TOKEN bearer

API surface

Group Endpoints
Health GET /api/health, GET /api/ready
Auth POST login / logout / change-password, GET /api/auth/me
Entries CRUD /api/entries + filters
Daily PUT/GET /api/daily/{day}, GET /api/daily?start=&end=
Plan Plan CRUD, feedback, seed, reschedule, priors, templates, health
Agent GET /api/agent/context, PUT /api/agent/plan/{day}
Coach POST /api/coach, POST /api/entries/{id}/coach
Stats GET /api/stats, GET /api/stats/remedies
Debug Traces, paste bundles
Export Entries/daily JSONL + CSV
Settings GET /api/settings/status (no secrets)

Frontend UI

Nav IA

Home | Plan | (+) Log | Daily | Settings

History remains at #/history (reachable from Home β€œSee all”). Center FAB stays Log (one tap).

flowchart TB
  Login["#/login"] --> AuthGate{session?}
  AuthGate -->|yes| Shell[App shell]
  Shell --> TabBar

  subgraph tabs [Bottom nav]
    Home["#/ Home"]
    Plan["#/plan Plan"]
    FAB["(+) Log"]
    Daily["#/daily Daily"]
    Settings["#/settings"]
  end

  TabBar --> Home
  TabBar --> Plan
  TabBar --> FAB
  TabBar --> Daily
  TabBar --> Settings

  Home --> History["#/history"]
  History --> Entry["#/entry/:id"]
  Home --> Coach["#/coach"]
  Home --> Stats["#/stats"]
  Settings --> Remedies["#/remedies"]
  FAB --> Log["#/log"]
  Log -->|high FSE| ProofSheet[Proof brick sheet]
  ProofSheet --> DailyAPI[PUT /api/daily]
  Plan --> BlockSheet[Block feedback sheet]

Routes

Route Page Role
#/ Home Week score, pending, plan next, indoors/proof nudges
#/plan, #/plan/:date Plan Day timeline, seed, reschedule, block feedback
#/log Log Fast entry + high-FSE proof wizard
#/daily Daily 6-pt scoreboard + movement / fantasy / proof fields
#/settings Settings Status, password, export
#/history History Entry list (via Home)
#/entry/:id Entry Detail + coach
#/coach Coach Free-text coach + SERVER_PICKS
#/stats Stats Server-owned p_helped / ranks
#/remedies Remedies Remedy leaderboard
#/login Login Password β†’ session cookie

UI stack

  • Vite + Preact + TypeScript
  • Motion (press / sheets)
  • lucide-preact icons
  • Plus Jakarta Sans, warm journal UI (#EBE7E0)
  • credentials: 'include' on all API calls; 401 β†’ #/login

Key components

Component Role
TabBar Home | Plan | (+) Log | Daily | Settings
Timeline + BlockSheet Plan day + thick feedback
ResolvePendingSheet Pending resolve in < 3 taps
Toast / Sheet / Pressable Shared chrome
CoachResult Rules | Model badge

Auth

sequenceDiagram
  participant U as User
  participant UI as PWA
  participant API as FastAPI
  participant Disk as config.json

  U->>UI: password
  UI->>API: POST /api/auth/login
  API->>Disk: verify PBKDF2 hash
  API-->>UI: Set-Cookie session
  UI->>API: GET /api/* credentials include
  Note over API: 401 β†’ redirect #/login

  participant A as Agent
  A->>API: GET /api/agent/context
  Note over A,API: Authorization Bearer AGENT_TOKEN
  • Human: PBKDF2 password in config.json β†’ signed session cookie (sv + exp).
  • Agent: Bearer AGENT_TOKEN on /api/agent/* (or session).
  • Envelope: { ok, data, error: { code, message } }.
  • Server owns points, p_helped, ranks, d_hat β€” LLM must not invent %.

Coach pipeline

flowchart TB
  Req[POST /api/coach] --> Evidence[build_evidence SERVER_PICKS]
  Evidence --> Key{OPENROUTER_API_KEY?}
  Key -->|no / force_backup| Rules[backup_replies]
  Key -->|yes| OR[OpenRouter]
  OR -->|parse fail / label at high I| Rules
  Rules -->|I ge 7| HighFSE[high_intensity_fse]
  OR --> Trace[traces.jsonl]
  Rules --> Trace
  Trace --> UI[CoachResult Rules or Model]

At intensity β‰₯ 7, Rules prefer interruptive remedies (leave room, interrupt walk without cinematic headphones, one proof line) β€” not β€œLabeling” alone.


Schedule / Plan

flowchart TB
  AgentPull[GET agent/context] --> Caps[capacity_hint + operators]
  Caps --> Put[PUT agent/plan or UI seed]
  Put --> PlanFile[plan-YYYY-MM-DD.json]
  UI[Timeline + BlockSheet] --> FB[POST feedback]
  FB --> Feedback[feedback.jsonl]
  Feedback --> Priors[priors.json d_hat]
  UI --> Resched[POST reschedule]
  Resched -->|OR or rules| PlanFile

P1 scope lock: thin loop only (timeline + thick feedback + priors + capacity cuts). EV formula locked; no PERMA / Ξ΄ / H in code. See SCHEDULE.md.


Domain: loop-break / movement

flowchart LR
  Log[Log entry] -->|intensity ge 7| Proof[Proof wizard]
  Proof --> DailyRow[Daily proof_brick_*]
  DailyRow --> Home[Home banners]
  DailyRow --> AgentCtx[agent/context movement]
  Walk[Interrupt vs fantasy walk] --> DailyRow
  Coach[Coach Rules] -->|intensity ge 7| Interrupt[Interrupt not Labeling]
  PlanTpl[fantasy_slot_25 template] --> Plan[Day plan]

Daily additive fields include: stayed_indoors_all_day, left_room, left_home, walk splits, headphones / cinematic flags, proof_brick_*, fantasy scheduled vs unplanned minutes. 6-pt Daily formula unchanged.


Persistence layout

Under DATA_ROOT (default /data/loop_logger):

Path Purpose
config.json Password hash, session version
entries.jsonl Log entries
daily.jsonl Scoreboard + movement / proof / fantasy
traces.jsonl Coach debug traces
coach_brief.md Coach brief
schedule/plan-YYYY-MM-DD.json Versioned day plan
schedule/feedback.jsonl Block thick feedback
schedule/priors.json d_hat / p_done / fun priors
schedule/meta.json Reschedule cooldown

Path definitions live in app/paths.py.


Related docs

Local tooling

  • scripts/pull_handoff_bundle.py β€” pull live Space state into a paste bundle (handoff/ gitignored)
  • .env β€” local secrets only (gitignored): HF_TOKEN, HF_SPACE_URL, APP_PASSWORD, AGENT_TOKEN