User test surfaced the actual auto-title bug: the entire pipeline
(enqueue → process → watcher) works correctly, but the task result
itself is empty:
[memoro] enqueued title task { taskId, memoId }
[memoro-llm-watcher] saw 1 done title task(s)
[memoro-llm-watcher] writing title to memo XXX: ""
[memoro-llm-watcher] applied + cleared row YYY
The watcher faithfully wrote "" to memo.title, the input placeholder
showed "Titel..." again, and we looked stuck. Three layered fixes so
this can't bite us in any execution path going forward.
1. generate-title.ts: extract rules logic + use it as runLlm fallback
Pulled the deterministic first-sentence heuristic into a private
`rulesImpl()` function so both `runLlm` and `runRules` can call
it. runLlm now invokes rulesImpl as a fallback when the cleaned
LLM output is empty. This catches the case where the model emits
only punctuation, only special tokens, or only whitespace — all
of which collapse to "" after my cleanup chain (`.trim()` → strip
surrounding quotes/markdown → strip trailing dots → `.trim()`).
The most likely real-world trigger: Gemma 4 occasionally emits a
single `.` for short prompts that hit its over-strict
"answer with ONLY the title" instruction. The cleanup turns
"." into "" and we lose the result.
2. llm-watcher.svelte.ts: date-based backstop for any empty result
Belt-and-suspenders: even if a future task implementation forgets
the rules fallback, the watcher itself now guarantees a non-empty
title. When `row.result.trim()` is empty, synthesize a label like
"Memo vom 9. April 2026" from the memo's createdAt (or the
current date if createdAt is also broken). The user always sees a
real title — never an empty placeholder.
Same write path otherwise (encryptRecord + memoTable.update +
delete queue row), just with the guaranteed-non-empty value.
3. llm-watcher.svelte.ts: enhanced diagnostic logging
The "writing title" log now includes `row.source` (which tier
actually executed) and `row.attempts`, so the next time we see
weird behavior we can tell at a glance whether it was the
browser tier, the rules tier, or the server. The empty-result
path logs `console.warn` (not info) with the raw result via
JSON.stringify so we see exactly what came back ("", ".", " ",
undefined-coerced-to-string, etc.).
After this commit lands:
- Tier 0 user: runRules returns at minimum "Ohne Titel" (its
own fallback). The watcher writes that.
- Browser tier with empty Gemma output: runLlm now falls through
to rulesImpl which also can't return empty. The watcher writes
the rules-tier output.
- Any other freak case where the result is still empty: the
watcher's date-based backstop kicks in. "Memo vom <date>".
So the user-visible "stuck on empty title" symptom is impossible in
all three layers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .. | ||
| apps | ||
| .gitignore | ||
| CLAUDE.md | ||
| README.md | ||
Mana Apps
A unified application ecosystem built on a shared authentication system, supporting multiple branded applications across web and mobile platforms.
Overview
Mana Apps is a monorepo containing web and mobile applications that provide organization management, team collaboration, and credit transfer capabilities. The system supports multiple branded applications (Memoro, Cards, Storyteller, Mana) through a flexible multi-tenant architecture.
Applications
- Web App (
apps/web) - SvelteKit-based web application - Mobile App (
apps/mobile) - React Native (Expo) app for iOS, Android, and web - Landing (
apps/landing) - Landing page (planned)
Features
- 🔐 Unified authentication with Supabase
- 🏢 Organization management with role-based access
- 👥 Team collaboration and member management
- 💰 Mana credit system with transfers and balance tracking
- 🎨 Multi-brand support with configurable themes
- 📱 Cross-platform (Web, iOS, Android)
- 🔄 Real-time updates across all platforms
- 🧪 Comprehensive testing with Vitest and Playwright
Quick Start
Prerequisites
- Node.js 20+
- pnpm (for web app)
- npm (for mobile app)
- Supabase account with project configured
- Expo CLI (for mobile development)
Setup
-
Clone the repository
git clone <repository-url> cd mana-core-apps -
Web App Setup
cd apps/web pnpm install cp .env.example .env # Edit .env with your Supabase credentials pnpm dev -
Mobile App Setup
cd apps/mobile npm install cp .env.example .env # Edit .env with your Supabase credentials npm start
Project Structure
mana-core-apps/
├── apps/
│ ├── web/ # SvelteKit web application
│ │ ├── src/
│ │ │ ├── routes/ # File-based routing
│ │ │ │ ├── (auth)/ # Public auth pages
│ │ │ │ └── (app)/ # Protected pages
│ │ │ ├── lib/
│ │ │ │ ├── components/
│ │ │ │ ├── config/ # Multi-app configuration
│ │ │ │ ├── server/ # Server-only utilities
│ │ │ │ └── types/
│ │ │ └── hooks.server.ts # Auth middleware
│ │ └── package.json
│ │
│ ├── mobile/ # React Native (Expo) app
│ │ ├── app/ # File-based routing (Expo Router)
│ │ │ ├── (drawer)/ # Drawer navigation
│ │ │ ├── auth/ # Auth screens
│ │ │ └── _layout.tsx # Root layout with auth
│ │ ├── components/ # React components
│ │ ├── utils/ # Utilities (Supabase, storage)
│ │ └── package.json
│ │
│ └── landing/ # Landing page (planned)
│
├── CLAUDE.md # Developer documentation
└── README.md # This file
Technology Stack
Web App (apps/web)
| Category | Technology |
|---|---|
| Framework | SvelteKit 2 with Svelte 5 (Runes) |
| Language | TypeScript |
| Styling | TailwindCSS 3 with PostCSS |
| Database | Supabase (PostgreSQL) |
| Auth | Supabase Auth with SSR |
| Testing | Vitest (unit) + Playwright (E2E) |
| Build Tool | Vite |
Mobile App (apps/mobile)
| Category | Technology |
|---|---|
| Framework | Expo 52 with React Native 0.76 |
| Language | TypeScript |
| Routing | Expo Router 4 (file-based) |
| Styling | NativeWind (TailwindCSS for RN) |
| Navigation | React Navigation (drawer, tabs) |
| Database | Supabase |
| Build | EAS Build |
| Platforms | iOS, Android, Web |
Development
Web App Commands
cd apps/web
# Development
pnpm dev # Start dev server (http://localhost:5173)
pnpm build # Build for production
pnpm preview # Preview production build
# Code Quality
pnpm check # Type-check with svelte-check
pnpm check:watch # Type-check in watch mode
pnpm lint # Check formatting and lint
pnpm format # Format code with Prettier
# Testing
pnpm test # Run unit tests (Vitest)
pnpm test:ui # Run tests with UI
pnpm test:e2e # Run E2E tests (Playwright)
Mobile App Commands
cd apps/mobile
# Development
npm start # Start Expo dev server
npm run ios # Run on iOS simulator
npm run android # Run on Android emulator
npm run web # Run web version (http://localhost:19006)
# Building
npm run build:dev # Build dev client
npm run build:preview # Build for internal testing
npm run build:prod # Build for production
# Code Quality
npm run lint # Lint and check formatting
npm run format # Fix linting and format code
# Setup
npm run prebuild # Generate native projects
Environment Configuration
Both apps require Supabase configuration. Create .env files based on .env.example:
Web App (apps/web/.env)
PUBLIC_SUPABASE_URL=your_supabase_project_url
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
MIDDLEWARE_URL=https://mana-middleware-111768794939.europe-west3.run.app
PUBLIC_APP_NAME=Mana Web
NODE_ENV=development
Mobile App (apps/mobile/.env)
EXPO_PUBLIC_SUPABASE_URL=your_supabase_project_url
EXPO_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
Architecture
Multi-Tenant System
The system supports multiple branded applications sharing the same authentication backend:
- Memoro - Voice recordings and memory management
- Cards - AI-powered flashcard learning
- Storyteller - Creative writing with AI assistance
- Mana - Central account and organization management
App configurations are centralized in apps/web/src/lib/config/apps.ts, defining branding, features, and routing for each application.
Authentication Flow
Web (SvelteKit):
- Server-side authentication using
@supabase/ssr - Middleware in
hooks.server.tshandles session validation - Protected routes in
(app)group require authentication - JWT validation via
safeGetSession()before allowing access
Mobile (Expo):
- Client-side authentication using
@supabase/supabase-js - Custom memory storage for session persistence
AuthProviderinapp/_layout.tsxmanages auth state- Automatic navigation based on authentication status
Database Schema
Key tables:
users- User profiles (linked viaauth_idto Supabase Auth)organizations- Organization entitiesuser_roles- User-organization relationships with rolesteams- Team entities within organizationsteam_members- User-team membershipscredit_transactions- Mana credit transfer history
See CLAUDE.md for detailed architecture documentation.
Testing
Web App
cd apps/web
# Unit tests
pnpm test # Run all tests
pnpm test:ui # Open Vitest UI
# E2E tests
pnpm test:e2e # Run Playwright tests
pnpm test:e2e --ui # Run with Playwright UI
Mobile App
Mobile testing is primarily done through Expo Go or development builds:
cd apps/mobile
npm start # Start dev server
# Then press 'i' for iOS or 'a' for Android
Deployment
Web App
Vercel (Recommended):
cd apps/web
vercel
Netlify:
cd apps/web
netlify deploy
Mobile App
iOS and Android (via EAS):
cd apps/mobile
# Preview build (internal testing)
npm run build:preview
# Production build
npm run build:prod
Configure EAS in eas.json with your build profiles.
Contributing
- Create a feature branch from
main - Make your changes
- Run linting and tests
- Submit a pull request
Code Style
- Use TypeScript for type safety
- Follow ESLint and Prettier configurations
- Write tests for new features
- Use conventional commit messages
Documentation
- CLAUDE.md - Comprehensive developer guide for Claude Code
- apps/web/README.md - Web-specific documentation
- Individual component documentation in source files
Support
For questions or issues, please contact the development team or open an issue in the repository.
License
Private - All rights reserved