Files
learning-platform/AI_AGENT.md

15 KiB
Raw Blame History

AI Agent Context Guide: Respellion Learning Platform

Welcome, fellow AI agent! If you are reading this, you are tasked with maintaining or extending the Respellion Learning Platform. This document provides the critical context, architectural patterns, and design standards you need to successfully work on this codebase.

Last updated: 2026-05-18 — Adds 52-week annual curriculum system (§12). Reflects selective content generation (3 types), ISO week alignment, GitHub sync folder change, and AI extraction token limits.

1. Architectural Overview

This is a single-page React application built with Vite, backed by PocketBase as the database and auth layer.

  • Frontend: React, React Router, Vanilla CSS (via CSS variables) + Tailwind utility classes mapped to those variables.
  • Backend: PocketBase (self-hosted). All data is stored in PocketBase collections, not localStorage.
  • Animations: Framer Motion (used extensively for page transitions, podium effects, and gamification feedback).
  • Icons: Lucide React.
  • Visualizations: D3.js (used strictly for the Admin Knowledge Graph).

2. State Management & Storage (Critical)

All persistent data lives in PocketBase. The data access layer is in src/lib/db.js, which wraps the PocketBase SDK client from src/lib/pb.js.

PocketBase Collections:

  • topics — Knowledge graph nodes (id, label, type, description).
  • relations — Knowledge graph edges (source, target, type).
  • content — AI-generated learning modules per topic (topic_id, data). The data field is a merged JSON object containing only the content types that have been generated for that topic (e.g. { article: {...}, slides: [...] }). New types are shallow-merged into the existing object by learningService.js; nothing is ever overwritten.
  • quiz_banks — Banks of AI-generated questions per topic (topic_id, questions[]).
  • quiz_results — User test scores (user_id, week_number, score, total, percentage, etc.).
  • quiz_cache — Cached weekly quiz for a user (user_id, week_number, questions[], meta).
  • learn_progress — Whether a user completed the weekly learning session (user_id, week_number, done).
  • leaderboard — Points ledger (user_id, name, points, tests_completed).
  • team_members — Registered users with PIN auth (name, role, pin).
  • sources — Uploaded source documents and their extraction status (name, status, error).
  • curriculum — Annual learning schedule (year, week_number, topic_id, theme, quarter, is_review_week, sort_order). One entry per week per year. Managed via the admin Curriculum tab.
  • settings — Key/value store for app-wide settings (key, value).

localStorage is only used for admin browser settings (not user data):

  • respellion:admin:anthropic_key — Anthropic API key.
  • respellion:admin:model — Model override.
  • respellion:admin:use_simulation — Simulation mode toggle.
  • kb:suggestions — Pending/approved/rejected graph deltas proposed by R42. Always mutated via kbStore (see §8).
  • quiz:active:{userId} — Boolean flag set while the user is mid-quiz. R42's FAB is hidden when this is true (quiz-integrity rule).
  • chat:thread:{userId} — Persisted R42 conversation, capped at 50 messages.

Session: User login is PIN-based. The logged-in user's ID is stored in sessionStorage under respellion_session and resolved against team_members on app load (see src/store/AppContext.jsx).

Week Number: The current ISO-8601 week number is calculated dynamically on app load via getWeekNumber(new Date()) in src/store/AppContext.jsx. It is not stored in the database. The ADVANCE_WEEK action still exists for admin use, but initial state always reflects the real calendar week.

Curriculum Year: The curriculum year is derived from new Date().getFullYear() via getCurriculumYear() in src/lib/curriculumService.js. It is not stored — always computed.

Important: All db.js functions are async. Always await them — omitting await will silently pass a Promise where data is expected.

Auto-Cancellation: The PocketBase JS SDK has auto-cancellation enabled by default. This causes concurrent identical requests (like db.getTopics() during React StrictMode renders or concurrent Promise.all) to abort with ClientResponseError 0. This feature is globally disabled in src/lib/pb.js via pb.autoCancellation(false) to prevent UI crashes during concurrent fetching.

3. The AI Integration (Anthropic)

The application calls the Anthropic API via a proxy to avoid CORS issues.

  • Location: src/lib/api.js.
  • Proxy: In Docker, /api/anthropic is proxied via Caddy to the Anthropic API endpoint. In local dev, configure vite.config.js to proxy the same path. The API key is injected server-side by Caddy; there is no client-side API key.
  • Token limit: generateContent uses max_tokens: 8192. Do not lower this — large knowledge-base files need the headroom.
  • JSON Enforcement: Prompts must strictly enforce that Claude returns only raw JSON. Do not let the AI wrap the response in markdown blocks — a regex strip via .match(/\{[\s\S]*\}/) is already applied in all service files.

4. Design System & Aesthetics

Respellion relies on a premium, modern aesthetic.

  • CSS Variables: Rely heavily on the variables defined in src/index.css.
    • Colors: var(--color-bg), var(--color-paper), var(--color-teal), var(--color-accent).
    • Radii: var(--r-sm), var(--r-lg), var(--r-org) (an organic, pill-like shape used for badges and podiums).
  • Tailwind: Tailwind is mapped to these CSS variables. Avoid hardcoding random hex codes. Use the existing Tailwind classes like bg-teal, text-fg-muted, and border-bg-warm.
  • Components: Always reuse the UI primitives in src/components/ui/ (Card.jsx, Button.jsx, Tag.jsx, Input.jsx).

5. Learning Content Types

src/lib/learningService.js supports three content types for selective generation:

Type Schema key Description
article content.article Title, intro, sections, key takeaways
slides content.slides Array of slides with bullets and speaker notes
infographic content.infographic Headline, tagline, stats, steps, quote

There is no podcast type. It was removed. Do not re-add it.

generateLearningContent(topic, force, selectedType) accepts one of the three types above, or 'all' (admin regeneration). Cache-hit logic checks content[selectedType] directly. On generation, the new data is shallow-merged into the existing cached object so other types are preserved.

The LearningContentViewer tab bar reflects exactly these three modes. The empty-state for an un-generated tab shows a "Generate [Type]" button that calls onGenerate(activeMode) passed from the parent.

6. Gamification Rules

If you are extending the Gamification system (src/pages/Leaderboard.jsx):

  • Tests grant +2 points per correct answer (handled in testService.js → saveTestResult).
  • A 100% score grants the Perfectionist badge (computed at render time in Leaderboard.jsx).
  • Completing 1 test grants First Steps, completing 5 grants Veteran.
  • Points are accumulated in the leaderboard collection via db.upsertLeaderboardEntry.

7. Docker & Deployment

The app is fully containerized. PocketBase runs as a sidecar service.

  • Build: docker build -t respellion-app:latest .
  • Run: docker compose up -d (see docker-compose.yml).
  • Caddy (reverse proxy): Handles SPA fallback, injects the Anthropic API key via a Authorization header on /api/anthropic/* requests, and proxies /pb/* to the PocketBase service. Config lives in Caddyfile.
  • PocketBase URL: Resolved from VITE_PB_URL env var, or falls back to window.location.origin + '/pb' (see src/lib/pb.js).

8. GitHub Knowledge-Base Sync

The Admin upload panel (src/components/admin/UploadZone.jsx) can sync markdown/text files directly from a GitHub repository.

  • Default folder: docs/knowledge-base/ of the respellion/employee-handbook repo. This is persisted in the settings collection under the key github:url so admins can change it from the UI.
  • Change detection: Each file's SHA is stored as github:sha:<filename> in settings. Files whose SHA differs are marked Updated; new files are New. Up-to-date files are skipped.
  • Extraction pipeline (src/lib/extractionPipeline.js): Calls anthropicApi.generateContent with a strict JSON-only system prompt. To prevent truncated responses on large files, the prompt limits extraction to max 15 topics and their most important relations. If the AI returns non-JSON, the file is marked failed in the sources collection.
  • Deduplication: A file already in sources with status: completed will throw and not be re-processed. Delete the source record first to force a re-analysis.
  • Do not increase topic cap beyond 15 without also verifying the max_tokens: 8192 budget is sufficient for the expected file size.

9. R42 Chatbot

The platform ships a global chatbot avatar called R42, rendered as the Respellion { r } brand mark in three states (idle / typing / error).

  • Mark component: src/components/ui/Mark.jsx. Pure SVG; renders the brand mark with state, size, theme, showFrame, brace, letter props. Requires the BallPill font (loaded via @font-face in src/index.css, served from public/fonts/BallPill-light.otf). Falls back to JetBrains Mono (already loaded).
  • Chat module: src/components/chat/.
    • ChatLauncher.jsx — global FAB; auto-hides when quiz:active:{userId} is set. Listens to the respellion:quiz-state window event for fast updates.
    • ChatWindow.jsx — 380×480 chat panel; Esc closes; renders messages from useChat.
    • useChat.js — owns the message list, persists to chat:thread:{userId}, calls anthropicApi.chat(). buildKbContext is async (PocketBase), so send() is fully async.
    • prompts.js — system prompt, greeting, and the propose_graph_delta tool spec.
    • rag.js — fetches kb:topics + kb:relations from PocketBase; lazy-loads kb:content:{id} only when a topic is mentioned. Returns { context, topics } so validateDelta can reuse the fetched topics without a second round-trip.
  • Multi-turn API: anthropicApi.chat(systemPrompt, messages, { tools }) in src/lib/api.js. Returns the raw Anthropic response ({ content: [...], stop_reason }) so callers can read both text blocks and tool_use blocks. No API key header — Caddy proxy injects it server-side, matching the existing generateContent pattern.
  • Quiz-integrity rule: src/pages/Testen.jsx sets quiz:active:{userId}=true on start and clears it on every non-quiz phase + unmount, plus dispatches a respellion:quiz-state event. Never bypass this — letting users ask R42 mid-quiz would break scoring.
  • Graph refinement: when R42's tool_use block proposes a propose_graph_delta, rag.js validates (no duplicate ids, no orphan relations, caps 3 topics / 5 relations) and surfaces a confirmation chip inline.
    • Admin user clicks JakbStore.applyDelta writes to PocketBase via db.upsertTopic / db.addRelation immediately.
    • Non-admin clicks JakbStore.appendSuggestion queues an entry in kb:suggestions localStorage (status pending).
  • Admin approval UI: src/components/admin/SuggestionsQueue.jsx, mounted at the top of the Knowledge Graph admin panel. Approve calls kbStore.approveSuggestion(id) which runs the same applyDelta merge (async, PocketBase) and flips status to approved. Reject flips to rejected for audit.
  • kbStore: src/lib/kbStore.js is the single source of truth for KB mutations from the chatbot path. Topics/relations go to PocketBase; suggestions queue goes to localStorage. Dispatches respellion:kb-updated after any write so the D3 graph and the queue panel refresh without a reload.

10. How to Add New Features

  1. Define Schema: Add a new PocketBase collection via the Admin UI or the init script (scripts/setup-pb-collections.mjs).
  2. Add DB Helpers: Add async CRUD functions in src/lib/db.js.
  3. Build UI: Create components using Card, Button, and framer-motion for smooth entry (animate-in fade-in slide-in-from-bottom-4).
  4. Wire Logic: Connect to src/store/AppContext.jsx if it requires global user context, otherwise manage state locally.
  5. Test: Run the Docker stack (docker compose up) to ensure no build errors exist.

11. Known Gotchas & Decisions

  • No podcast type. The podcast learning type was deliberately removed. The three supported types are article, slides, and infographic.
  • Week number is computed, not stored. Do not add a admin:current_week setting — the ISO week is always derived from new Date().
  • Caddy, not Nginx. Older docs/comments may reference Nginx. The reverse proxy is Caddy. Update any references you encounter.
  • AI token budget. If you see [Pipeline] AI returned non-JSON response in the logs, the response was truncated. Increase the topic cap prompt constraint before raising max_tokens.
  • PocketBase auto-cancellation is OFF. pb.autoCancellation(false) is set globally in src/lib/pb.js. Never re-enable it — it causes abort errors during concurrent fetches in React StrictMode.

12. Annual Curriculum System

The platform uses a 52-week annual curriculum so every employee covers all knowledge-base topics in one calendar year.

  • Service: src/lib/curriculumService.js — curriculum engine with topic lookup, progress tracking, and auto-generation.
  • DB functions: db.getCurriculum(year), db.getCurriculumWeek(year, week), db.setCurriculumWeek(...), db.bulkSetCurriculum(year, weeks[]).
  • Admin UI: src/components/admin/CurriculumManager.jsx — accessed via the "Curriculum" tab in the admin panel. Admins can auto-generate a schedule from KB topics or manually assign topics per week.
  • Same topic for everyone: All employees study the same topic each week — this is by design to enable team discussion and shared quizzes.
  • Quarterly structure: Weeks 113 (Q1), 1426 (Q2), 2739 (Q3), 4052 (Q4). Review/recap weeks at 13, 26, 39, 52.
  • Fallback: If no curriculum exists for the current year, getAssignedTopic() falls back to the legacy hash-based assignment for backward compatibility.
  • Progress tracking: getYearProgress(userId) and getQuarterProgress(userId, quarter) compute completion from the learn_progress collection against the curriculum.
  • Auto-generate: autoGenerateCurriculum(year) distributes all non-fact topics across 48 content weeks + 4 review weeks. If there are fewer topics than weeks, they cycle. If more, excess topics remain in the self-service library.
  • Do not remove the hash fallback — it ensures the platform works even without a configured curriculum.

Good luck! You are building a platform that empowers continuous learning. Keep it fast, keep it beautiful, and keep the user engaged.