A self-paced onboarding track that introduces a new employee to every KB theme in breadth (not depth), so they grasp how Respellion works day to day and week to week. Offered as a CTA inside the Dashboard "New here?" explainer card; always available regardless of enrollment. Design: - Theme is the trackable unit; the 5 "days" are a read-time presentation grouping, so re-chunking never loses progress. Completion is stored per theme in onboarding_completions. - Per-theme overview generated lazily on first open (fast-tier emit_onboarding_overview tool), cached in onboarding_overviews keyed by theme + a topics_fingerprint that triggers regeneration when the theme's topic set changes. - Reachable via /onboarding-track using the existing skipEnrollmentGate prop, decoupled from the 26-week curriculum (distinct from /onboarding, the enrollment page). Backend: - pb_migrations/1781200000_created_onboarding.js: two collections with authenticated-only rules and unique indexes; TEXT team_member_id (no relation) per the post-#18/#27 convention. Mirrored in scripts/setup-pb-collections.mjs. - src/lib/onboardingService.js: pure helpers (orderThemes, distributeThemesIntoDays, computeTopicsFingerprint, computeOnboardingProgress, buildOnboardingPlan) + generation + I/O. - db.js onboarding helpers use pb.filter() bindings (theme is free text). - LLM tool + Zod schema + registry + simulation stub. Frontend: - src/pages/OnboardingTrack.jsx (day list, per-theme overview, completion banner, progress ring/day bar). - Dashboard "New here?" card CTA + X/5-days progress chip (hidden when the KB has no themes). Docs: data-model, generation-spec (§D), frontend-spec updated. Verified: 22 new unit tests (npm test 134/134), eslint clean on changed files, npm run build OK, PocketBase v0.30.4 boot applies the migration (collections + unique indexes + authed rules confirmed), and a backend contract check (upsert idempotency, unique-index guard, special-char theme filtering). Closes #30 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5.3 KiB
Generation spec: learning content & micro-learnings
Two generators turn a topic into learner-facing material. Both go through
callLLM with forced tool use and Zod-validated output. All content is cached in
PocketBase so it is generated once per topic/type.
A. Long-form content — src/lib/learningService.js
Stored in the content collection (one record per topic, data is a merged
object). Three types, generated on demand:
| Type | Tool | Min requirements |
|---|---|---|
article |
emit_learning_article |
≥3 sections, ≥2 takeaways |
slides |
emit_learning_slides |
≥4 slides |
infographic |
emit_learning_infographic |
≥3 stats, ≥3 steps |
generateLearningContent(topic, force, selectedType):
- tier
standard,maxTokens: 8192 selectedTypeis one of the three, or'all'(emit_learning_all) for admin regeneration- cache check looks at
content[selectedType]; on generation the new payload is shallow-merged into the cached object so other types survive - there is no podcast type
Article refinement (refineLearningContent): the admin describes a change and
the model edits via targeted patch tools — set_intro, set_section,
add_section, remove_section, replace_takeaways — so only the affected parts
change. Patches are applied and re-validated in src/lib/articlePatches.js.
B. Micro-learnings — src/lib/microLearningService.js
Stored in the micro_learnings collection (one record per topic per type,
status='published'). Three types:
| Type | Tool | Tier | Shape |
|---|---|---|---|
concept_explainer |
emit_concept_explainer |
standard | { sections: [{ title, content (HTML) }] }, ≥3 sections |
scenario_quiz |
emit_scenario_quiz |
standard | { scenario, options: [{ text, isCorrect, explanation }] }, 3–4 options, exactly 1 correct |
flashcard_set |
emit_flashcard_set |
fast (Haiku) | { cards: [{ front, back }] }, 5–10 cards |
getOrGenerateMicroLearning(topicId, type):
- returns the cached published record if one exists (
findExisting) - otherwise loads the topic, calls
callLLMwith forced tool choice, and creates amicro_learningsrecord with the validatedcontent
A former
reflection_prompttype was dropped. Do not re-add it.
Completion is recorded (append-only) by useMicroLearningCompletions into
micro_learning_completions with { team_member_id, micro_learning_id, topic_id, type, session_week }.
C. Weekly quiz — src/lib/testService.js
Generates a 5-question multiple-choice test for the user's current week.
- Topic selection (
selectTestTopics): primary topic from the active curriculum week (else hash fallback) + a few review topics for breadth. - Batch generation (
callQuizBatchModel): a singlefast-tier call (emit_quiz_questions,maxTokens: 4096, 25s timeout) returns all 5 questions. - Quality gates (
validateBatchQuality): no duplicate options; no banned fillers ("all/none of the above", "both A and B"); explanations ≥20 chars; reject ifcorrectIndexis dominated by one position (>80%) and re-roll. - Scoring (
saveTestResult):pointsEarned = score * 2, written toleaderboardviadb.upsertLeaderboardEntry.
Question shape: { id, question, topicLabel, options[4], correctIndex (0–3), explanation, difficulty }.
D. Onboarding overviews — src/lib/onboardingService.js
Powers the 5-day onboarding track (issue #30): a short, breadth-first overview of one theme for a brand-new employee — deliberately light, not a deep lesson.
- Tool:
emit_onboarding_overview· tier:fast(Haiku) ·maxTokens: 1500, 60s timeout · schemaonboardingOverviewSchema. - Shape:
{ title, what_it_is, why_it_matters, key_points[3–5], topics_covered[{topic_id,label}] }.why_it_mattersis framed around day-to-day / week-to-week work at Respellion. - Cache:
onboarding_overviews, one row per theme, keyed by theme name plus atopics_fingerprint(stable hash of the theme's sorted topic ids).getOrGenerateOnboardingOverview(theme, topics, {force})returns the cached row when the fingerprint matches; a mismatch (topic added/removed) orforceregenerates. - Simulation:
emit_onboarding_overviewhas a stub inSIMULATION_TOOL_STUBS.
Theme ordering + day grouping are pure helpers in the same module
(orderThemes, distributeThemesIntoDays, computeTopicsFingerprint,
computeOnboardingProgress, buildOnboardingPlan), unit-tested in
src/lib/__tests__/onboardingService.test.js. Completion is recorded per theme
in onboarding_completions ({ team_member_id, theme }), not per day.
Shared infrastructure (src/lib/llm.js)
- Tiers:
fast(Haiku 4.5),standard(Sonnet 4.6),reasoning(Opus 4.7); per-tier admin overrides viaadmin:model:{tier}. - Structured output: prefer tool use with forced
toolChoice; inputs validated bytoolSchemaRegistry. Text responses go throughparseStructuredText. - Caching: wrap stable system text with
cachedSystem(...). - Retry/limits:
src/lib/llmRetry.js— backoff + jitter on 408/425/429/5xx/529, honorsRetry-After, rate limiters for bulk work. - Telemetry: every call logged to
llm_calls. - Simulation: with
admin:use_simulation, calls return stub output (no API hit).