- Introduced "Pension Scheme & Benefits" detailing secondary employment benefits and pension specifics. - Created "Roles & Accountabilities" outlining the Holacracy role structure and responsibilities within Respellion. - Added "Security" section covering GDPR compliance and workplace safety protocols. - Established "Spending and Contracting" policy detailing expense categories and submission processes. - Documented "Who We Are" to define Respellion's identity, services, and operational model under Holacracy and ISO 9001.
6.9 KiB
Architecture: Respellion Learning Platform
Overview
A mobile-first single-page web application that gives employees a structured knowledge library, a 26-week per-user learning curriculum, and an AI assistant (R42). The knowledge graph stored in PocketBase is the single source of truth for all content, micro-learnings, curriculum scheduling, and chat retrieval.
Unlike the original design (a Next.js multi-service system with Qdrant), the shipped platform is a React/Vite SPA talking directly to PocketBase, with the Anthropic API reached through a thin reverse proxy. All application logic — AI orchestration, retrieval, generation, scoring — runs in the browser.
Browser (React SPA)
├── PocketBase SDK ───────────────► PocketBase (SQLite): all structured data + auth + files
└── callLLM ──► /api/anthropic ──► Anthropic API (key injected by the proxy)
(Caddy in prod / Vite proxy in dev)
Runtime topology
┌─────────────────────────────┐ ┌──────────────────────────┐
│ Caddy container │ │ PocketBase container │
│ - serves built SPA (/srv) │ /api/*│ - SQLite data │
│ - /api/anthropic/* → Claude │───────►│ - auth (team_members) │
│ - /api/*, /_/* → PocketBase │ /_/* │ - file storage │
│ - injects ANTHROPIC_API_KEY │ │ - migrations (pb_migrations)
└─────────────────────────────┘ └──────────────────────────┘
In local dev, vite.config.js replaces Caddy: it proxies /api/anthropic to
https://api.anthropic.com and injects ANTHROPIC_API_KEY. PocketBase runs
directly (./pocketbase.exe serve).
Tech stack
| Layer | Technology | Rationale |
|---|---|---|
| Frontend | React 19 + Vite 8, React Router 7 | Fast SPA, single codebase for admin + employee |
| Styling | CSS variables + Tailwind v4 | Premium design system; Tailwind mapped to variables |
| Backend state | PocketBase (SQLite) | Auth, file storage, admin UI — no infra overhead |
| Retrieval | Local TF-IDF (src/lib/retrieval.js) |
Grounds R42 with zero external vector infra |
| AI | Claude via Anthropic API (proxied) | Structured tool output, long-form drafting, chat |
| Auth | PocketBase team_members + PIN |
Lightweight internal auth, role = admin / (employee) |
| Infra | Docker + Caddy, Ansible (infra/) |
Containerized deploy to dev/prod |
There is no Qdrant, no OpenAI/embeddings service, and no separate Node backend.
AI model responsibilities
callLLM (src/lib/llm.js) selects a Claude model by tier:
| Tier | Model | Used for |
|---|---|---|
fast |
claude-haiku-4-5-20251001 |
R42 chat, weekly quiz batch, flashcard sets |
standard |
claude-sonnet-4-6 |
KB extraction, article/slides/infographic, micro-learnings, curriculum generation |
reasoning |
claude-opus-4-7 |
reserved for heavier reasoning tasks |
Admins can override the model string per tier from the Settings tab.
Knowledge ingestion pipeline
Admin uploads .txt / .md (≤5 MB) in the Sources tab
↓
extractionPipeline.js chunks the text (~8000 chars, 800 overlap)
↓
Per chunk: callLLM (standard tier) with the emit_knowledge_graph tool
→ topics (id, label, type, description, learning_relevance)
→ relations (source, target, type)
↓
Results merged into the `topics` and `relations` collections
(topic id de-dup; relevance_locked topics keep their relevance)
↓
Source status tracked in `sources` (processing → completed / failed / cancelled)
There are no embeddings. Retrieval for R42 is computed on the fly with TF-IDF
over topics (label + description).
See docs/ingestion-spec.md.
Content generation
Two generators, both via callLLM with forced tool use and Zod-validated output:
- Long-form content (
learningService.js) →contentcollection. Three types generated on demand and shallow-merged:article,slides,infographic. - Micro-learnings (
microLearningService.js) →micro_learningscollection. Three types:concept_explainer,scenario_quiz,flashcard_set.
See docs/generation-spec.md.
Curriculum lifecycle (per-user)
Generation
Input: published topics grouped by theme, ordered by complexity_weight.
curriculumService.generateCurriculumDraft() asks Claude for a 26-week schedule
via emit_curriculum_schedule, validates it, and stores a curriculum_versions
row (status='draft'). Admin previews and confirms → active. Only one active
version exists; the prior active becomes superseded.
Per-user cycling
The curriculum is not anchored to the calendar. Each employee enrolls when
they choose (first-login onboarding), which sets team_members.curriculum_started_at.
Their position is derived:
personalWeek = floor(daysSinceStart / 7) + 1 // absolute counter, ≥1
curriculumWeek = ((personalWeek - 1) % 26) + 1 // 1..26 slot
cycle = floor((personalWeek - 1) / 26) + 1 // 1, 2, 3, ...
After week 26 the cycle restarts at week 1 with the same content.
See docs/curriculum-spec.md.
Weekly session flow (employee)
Enroll (first login) → curriculum_started_at set
↓
Dashboard shows current cycle / week / assigned topic
↓
Learning Station: complete ≥1 micro-learning for the week's topic(s)
↓
Weekly Test: 5 AI-generated questions → +2 points per correct answer
↓
Leaderboard updates; badges evaluated at render time
R42 — chat service design
R42 is a KB-grounded assistant on every screen (src/components/chat/).
- Persists the conversation per user in
localStorage(chat:thread:{userId}, cap 50; ~12 turns sent to the API). - Builds context with the TF-IDF index (top-K topics + verbatim mentions), injects related relations and limited deep content.
- Can propose a
propose_graph_delta(≤3 topics, ≤5 relations). Admins apply directly; non-admins queue a suggestion for admin approval. - Hidden during quizzes (the
quiz:active:{userId}integrity rule).
See docs/r42-spec.md.
Gamification
- Points: +2 per correct quiz answer, stored in
leaderboard. - Badges (render-time): First Steps (1 test), Veteran (5 tests), Perfectionist (100% score).
- Leaderboard excludes admins.
See docs/gamification-spec.md.
Security and privacy
- Auth: PocketBase
team_memberswith PIN; roleadminunlocks the Admin panel. - The Anthropic API key never reaches the client — it is injected by Caddy / the Vite proxy.
- R42 conversations are stored client-side per user; no server-side chat history.
- Source documents and the knowledge graph are managed by admins.