Files
learning-platform/docs/data-model.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

8.0 KiB
Raw Blame History

Data model: Respellion Learning Platform

Overview

All structured data lives in PocketBase (SQLite). There is no vector store — retrieval is computed at runtime with a local TF-IDF index over topics (src/lib/retrieval.js).

Schema is defined by JS migrations in pb_migrations/ (applied automatically by the PocketBase binary) and mirrored for local bootstrap in scripts/setup-pb-collections.mjs. The data-access layer is src/lib/db.js.

All collections use PocketBase's auto id, plus created / updated autodate fields unless noted otherwise.


PocketBase collections

topics

Knowledge graph nodes. Created during ingestion, enriched for curriculum.

Field Type Notes
id text kebab-case slug (e.g. holacratic-roles)
label text display name
type text concept · role · process (ingestion); fact is excluded from learning
description text 12 sentence summary
learning_relevance text core · standard · peripheral · exclude
relevance_locked bool if true, re-ingestion will not overwrite learning_relevance
theme text subject grouping (used by curriculum generation)
complexity_weight number 15 (curriculum ordering)
difficulty text introductory · intermediate · advanced

Topics with type='fact' or learning_relevance='exclude' are filtered out of learning, micro-learning, curriculum, and test selection.


relations

Knowledge graph edges between topics.

Field Type Notes
source text topic id
target text topic id
type text related_to · depends_on · part_of · executed_by

Edges are de-duplicated on the (source, target, type) tuple.


content

On-demand long-form learning content, one record per topic.

Field Type Notes
topic_id text topic this content belongs to
data json merged object — only generated types are present

data shape (each key generated independently and shallow-merged):

{
  "article":     { "title", "intro", "sections": [{ "heading", "body" }], "keyTakeaways": [] },
  "slides":      [ { "title", "bullets": [], "speakerNote" } ],
  "infographic": { "headline", "tagline", "stats": [{ "value", "label", "icon" }],
                   "steps": [{ "number", "title", "description", "icon" }], "quote", "colorTheme" }
}

There is no podcast key. The podcast type was removed.


micro_learnings

Generated micro-learning artifacts. One record per topic per type.

Field Type Notes
topic_id relation → topics cascade delete
type select concept_explainer · scenario_quiz · flashcard_set
content json structured output, schema varies per type
status select draft · published (only published is visible to employees)

Content JSON per type:

// concept_explainer
{ "sections": [ { "title": "string", "content": "string (HTML: <p>, <ul>, <li>, <strong>)" } ] }   // ≥3 sections

// scenario_quiz
{ "scenario": "string",
  "options": [ { "text": "string", "isCorrect": true, "explanation": "string" } ] }                 // 34 options, exactly 1 correct

// flashcard_set
{ "cards": [ { "front": "string", "back": "string" } ] }                                            // 510 cards

A former reflection_prompt type was dropped and is no longer generated.


micro_learning_completions

Append-only completion events. Never updated or deleted.

Field Type Notes
team_member_id relation → team_members the employee
micro_learning_id relation → micro_learnings the artifact completed
topic_id relation → topics denormalized topic
type text type at time of completion
session_week number the user's absolute curriculum week (week 1 = day they enrolled)

The 26-week slot and cycle are derived from session_week; there is no stored cycle field.


curriculum_versions

Versioned 26-week schedules. New version on each (re)generation.

Field Type Notes
version_number number increments per generation
status text draft · active · superseded (exactly one active)
generation_reason text why this version was created
confirmed_by text admin id who activated it
confirmed_at text ISO datetime
schedule json array of 26 week objects (below)
coverage_stats json { themes_kb, themes_scheduled, topics_kb, topics_scheduled }

schedule[] week object:

{ "week_number": 1,              // 1..26
  "theme": "string",
  "topic_ids": ["topic-id"],     // 1+ topic ids
  "estimated_duration": 30,      // 15..45 minutes
  "week_rationale": "string" }

team_members

Registered users with PIN auth. This is the auth + employee record.

Field Type Notes
name text display name
pin text login PIN
role text admin or empty/employee
curriculum_started_at date timestamp the user enrolled (week 1 anchor); empty until enrolled
enrollment_status text not_started · active

A user is gated through the /onboarding screen until enrollment_status='active' (admins are exempt when heading to the admin panel).


sources

Uploaded source documents and their extraction status.

Field Type Notes
name text original filename
status text processing · completed · failed · cancelled
error text failure message, if any
progress json { current, total, message } during chunked extraction

leaderboard

Points ledger, one row per user.

Field Type Notes
user_id text team member id
name text display name
points number cumulative (+2 per correct quiz answer)
tests_completed number count of completed tests
learnings_completed number reserved counter

Admins are filtered out of the public leaderboard at render time.


settings

App-wide key/value store.

Field Type Notes
key text setting key
value text stringified value

llm_calls

Best-effort telemetry for every Anthropic call (written by callLLM).

Field Type Notes
task text logging label (e.g. learning.article, chat.r42)
model text resolved model string
tier text fast · standard · reasoning
duration_ms number wall-clock
input_tokens / output_tokens number usage
cache_read_tokens / cache_create_tokens number prompt-cache usage
stop_reason text end_turn · tool_use · max_tokens
ok bool success flag
error_msg text error, if any

Dropped / legacy collections

These existed in earlier iterations and have been removed. Their db.js helpers remain as deprecated no-op stubs — do not build on them:

quiz_banks, quiz_results, quiz_cache, learn_progress, and the v1 curriculum collection.


Client-side storage (not PocketBase)

localStorage is used only for admin/browser-local state:

Key Purpose
admin:model:{fast,standard,reasoning} per-tier model overrides (legacy admin:model)
admin:use_simulation stub LLM responses instead of calling Anthropic
kb:suggestions R42 graph-delta suggestion queue (managed via kbStore)
quiz:active:{userId} mid-quiz flag (hides R42)
chat:thread:{userId} R42 conversation, capped at 50 messages

sessionStorage.respellion_session holds the logged-in team member id.


Retrieval (no vector DB)

R42 context is built by src/lib/retrieval.js:

buildIndex(topics)         → TF-IDF index over (label + description), cached by array ref
retrieveTopK(index, q, k)  → top-K topics, score = Σ (1 + log(tf)) · log((N+1)/(df+1))

src/components/chat/rag.js combines top-K results with verbatim topic mentions, filters relations to the retrieved set, and injects limited deep content for explicitly named topics.