- 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.
180 lines
6.9 KiB
Markdown
180 lines
6.9 KiB
Markdown
# 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.
|