RaymondVerhoef 54137122a7
All checks were successful
On Pull Request to Main / test (pull_request) Successful in 38s
On Pull Request to Main / publish (pull_request) Successful in 1m10s
On Pull Request to Main / deploy-dev (pull_request) Successful in 3m7s
fix: read Entra claims from id_token + UPN e-mail fallback (#24)
First Microsoft logins failed with 400 "email: Cannot be blank": Graph's
/oidc/userinfo endpoint omits the email claim for accounts without a
mail attribute, and team_members requires an e-mail (it also drives the
ENTRA_ADMIN_EMAILS role mapping). Reproduced against a mock Entra with
PocketBase v0.30.4; the user-supplied response body matched the
missing-email fingerprint exactly.

- provider config (reconciler + migration fast-path): userInfoURL is now
  empty, so PocketBase reads the claims from the Entra id_token, which
  always carries preferred_username (UPN) and email when available. The
  #18 reconciler flips the already-deployed Labs provider automatically
  on the next boot — no migration needed.
- pb_hooks/entra_oidc.pb.js: onRecordAuthWithOAuth2Request hook falls
  back to the lowercased UPN when the email claim is absent. Guest UPNs
  (ext_user#EXT#@tenant...) are excluded explicitly — "#" is RFC-valid
  in an e-mail local part, so both a naive regex AND PocketBase's own
  validation would accept them (caught by test V5). The hook also logs
  every failed OAuth2 attempt with the underlying error to the container
  log, which PocketBase otherwise only writes to its internal logs db.

Verified with a switchable mock-Entra matrix (8/8): no-email→UPN e-mail
+ allow-list role, email claim wins when present, no duplicate on
re-login, guest UPN yields a clean validation error, anonymous REST
create stays rejected, both log lines present. Regression: issue-22
matrix 9/9 (baseline pinned to 1a1351d now that #23 is merged), DoD
harness 15/15, npm test 112/112.

Closes #24

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 19:26:25 +02:00
fix
2026-05-25 20:51:11 +02:00

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
No description provided
Readme 95 MiB
Languages
JavaScript 95.6%
TypeScript 3.6%
CSS 0.8%