TeamManager could not manage the roster: updateRule allowed self-update
only and deleteRule was superuser-only. The same self-update rule also
left a privilege escalation open — it did not restrict fields, so any
authenticated user could PATCH their own role to "admin".
- team_members rules (migration 1781000003 + base migration + setup
script mirror):
update: (@request.auth.id = id && @request.body.role:isset = false)
|| @request.auth.role = "admin"
delete: @request.auth.role = "admin"
Self-service onboarding keeps working (it never touches role); the
role field is admin-only; deletion is admin-only.
- pb_hooks/team_members.pb.js: the ENTRA_ADMIN_EMAILS resync is now
escalate-only — it guarantees admin for allow-list members on every
login but no longer demotes, otherwise every TeamManager promotion
would revert on the member's next sign-in (ADR-004 amended).
- TeamManager.jsx: info text updated to the new behaviour.
Verified with a two-identity mock-OIDC matrix (11/11): allow-list vs
regular login, self-escalation blocked while onboarding self-update
still works, cross-user update/delete blocked, admin promote/demote/
delete work, promotion survives the member's next login, allow-list
admin stays admin. Regression: #22 matrix 9/9, #24 matrix 8/8, #18
harness 15/15, npm test 112/112, eslint clean.
Closes #27
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Respellion Learning Platform
An internal AI-powered learning platform that keeps Respellion employees up to date with the company's evolving knowledge base.
Features
- Onboarding — On first login each employee enrolls into the curriculum with one tap. Their 26-week cycle starts then and there.
- Weekly Learning Station — Each employee gets a topic for their current curriculum week, drawn from the active 26-week schedule (with a deterministic hash fallback when no curriculum is active). They engage via micro-learnings (concept explainer, scenario quiz, flashcard set) and can also generate long-form formats (Article, Slides, Infographic) on demand.
- Weekly Test — A 5-question AI-generated quiz for the week's topic. Results feed the leaderboard.
- Leaderboard & Gamification — Points for correct answers, badges for milestones (First Steps, Veteran, Perfectionist).
- R42 Chatbot — An always-available AI assistant (Claude) grounded in the knowledge graph via local TF-IDF retrieval. It can propose graph updates that admins approve or reject.
- Admin Panel — Manage the knowledge graph, upload source files, review/refine generated content, generate the curriculum, and manage the team.
Tech Stack
| Layer | Technology |
|---|---|
| Frontend | React 19 + Vite 8, React Router 7 |
| Styling | Vanilla CSS (custom properties) + Tailwind v4 utilities |
| Animations | Framer Motion |
| Icons | Lucide React |
| Graph viz | D3.js (admin knowledge graph only) |
| Backend / DB / auth | PocketBase (self-hosted, SQLite) |
| Retrieval | Local TF-IDF (no Qdrant, no embeddings API) |
| AI | Anthropic Claude (via Caddy reverse proxy) |
| Infra | Docker + Caddy; Ansible playbooks under infra/ |
Claude models (by tier): fast = claude-haiku-4-5-20251001, standard = claude-sonnet-4-6, reasoning = claude-opus-4-7.
Getting Started (local dev)
# 1. Install dependencies
npm install
# 2. Start PocketBase
./pocketbase.exe serve
# 3. Bootstrap collections (first run only)
node scripts/setup-pb-collections.mjs
# 4. Start the dev server
npm run dev
Set ANTHROPIC_API_KEY in your environment — the Vite dev server proxies /api/anthropic and injects the key server-side (see vite.config.js). PocketBase migrations in pb_migrations/ apply automatically when the binary starts.
Deployment (Docker)
docker compose up -d
The Caddyfile handles:
- SPA fallback routing
/api/anthropic/*→ Anthropic API (server-side API key injection)/api/*and/_/*→ the PocketBase sidecar
Key Files
| File | Purpose |
|---|---|
src/lib/llm.js |
Core Anthropic wrapper (callLLM): tiers, retry, schema validation, telemetry |
src/lib/db.js |
All PocketBase data access |
src/lib/extractionPipeline.js |
Uploaded file → knowledge-graph extraction |
src/lib/learningService.js |
On-demand content generation (article / slides / infographic) |
src/lib/microLearningService.js |
Micro-learning generation (3 types) |
src/lib/curriculumService.js |
26-week per-user curriculum engine |
src/lib/retrieval.js |
TF-IDF retrieval for R42 |
src/store/AppContext.jsx |
Global state; derives the user's curriculum week from their start date |
AI_AGENT.md |
Detailed context guide for AI coding agents |
Content Types
On-demand long-form content (the content collection), generated per type and merged into the cached object:
| Type | Key in DB | Description |
|---|---|---|
| Article | content.article |
Long-form reading |
| Slides | content.slides |
Slide deck with speaker notes |
| Infographic | content.infographic |
Visual summary with stats and steps |
The podcast type was removed. Do not re-add it.
Micro-learnings (the micro_learnings collection): concept_explainer, scenario_quiz, flashcard_set.
Documentation
CLAUDE.md— project overview and constraints for AI coding agents.AI_AGENT.md— full architectural guide (patterns, gotchas, decisions).docs/— subsystem specs (architecture, data model, ingestion, generation, curriculum, R42, frontend, gamification).
Description
Languages
JavaScript
95.6%
TypeScript
3.6%
CSS
0.8%