Files
learning-platform/docs/architecture.md
RaymondVerhoef 07af2783dc
All checks were successful
On Push to Main / test (push) Successful in 1m33s
On Push to Main / publish (push) Successful in 1m31s
On Push to Main / deploy-dev (push) Successful in 2m3s
Add comprehensive documentation for key organizational aspects
- 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.
2026-05-27 08:24:56 +02:00

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) → content collection. Three types generated on demand and shallow-merged: article, slides, infographic.
  • Micro-learnings (microLearningService.js) → micro_learnings collection. 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_members with PIN; role admin unlocks 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.