Engineering · AI · Product
The Card Cereby Couldn't See: Enriching Pinned Study Materials
What a tutor would want to know about pinned material was already stored, and thrown away.
Picture a student who has studied a flashcard set five times and pins it into a chat. Cereby saw "5 sessions, avg 85%." It did not see that Card 3, "Krebs Cycle", was missed in 4 of those 5. A pinned note about photosynthesis arrived without its tags, its folder, or the quiz made from it with a 72% average. (Both are illustrative.) All of it was stored. The pipeline discarded it.
The @ mention let a student pin notes, quizzes and flashcard sets into a chat for Cereby to answer from. This post is about the pinned-content pipeline, which turns pinned material into text for the model, and the stage we added to it in April. It was never an AI problem; it was a content-fetch problem. Asking about a subject without a pin is covered in Pin the Assessment Again: Performance Analysis Without the Pin.
Both paths threw it away
Pinned items were fetched in the browser, or on the server as a fallback, into one shape: a type, a title, a content string and metadata:
Both fetchers summed the sessions and dropped the per-card record on the way. Notes lost their tags, folder and offspring, and other types lacked time analytics, teaching content and links: a quiz kept a pointer to its source note, but neither side mentioned the other.
A stage, not a bigger fetcher
Inlining all this was the obvious move, but the flashcard fetchers were already over 100 lines each, and every fetcher existed twice. So we added a stage between fetch and format:
Each enricher declared the content types it applied to and returned text to add. A coordinator ran them within a budget, over one data layer for both databases, so each was written once, tested against a mock, and ran on either path.
Which card, not how many sessions
Each session already recorded, card by card, whether the student got it right. The mastery enricher computed each card's miss rate and labeled it struggling (over 50%), needs work (30 to 50%) or mastered (under 30%). "5 sessions, avg 85%" became:
## Per-card mastery (sorted by difficulty)
- Card 3 "Krebs Cycle": missed 4/5 sessions (80% miss rate) [struggling]
- Card 7 "ATP synthase": missed 2/5 sessions (40% miss rate) [needs work]
- Cards 1,2,4,5,6,8: consistently correct (mastered)
Notes gained their tags, folder, and whatever pointed back to them:
Tags: biology, photosynthesis
Folder: Science / Biology
Related content:
- Quiz "Photosynthesis Test": 3 attempts, avg 72%
- Flashcard set "Plant Cells": 5 sessions, avg 85%
Pinned quizzes and flashcard sets got the reverse:
## Related content
- Generated from note: "Photosynthesis" (Science / Biology)
- Also from same note: Flashcard set "Plant Cells" (5 sessions, avg 85%)
Timing needed a UI change. The quiz screen began timing each question, capped at 5 minutes so a student who walked away would not count as slow, in a new optional field that needed no migration. The enricher flagged questions over twice the attempt's average:
## Time analysis
Average time per question: 15s
Slow questions (>2x average):
- Q3 "Calculate molarity": avg 45s (3.0x average)
More pins, less room for each
Each item's budget, counted in characters, was about 2,000 tokens' worth with one or two items pinned, down to a floor of about 750 for five or more. Enrichers ran smallest and most valuable first; whichever overflowed was truncated, with a marker:
An enricher with no data added nothing, and if the stage threw, the item went out unenriched.
Before and after
| Before | After | |
|---|---|---|
| Flashcard mastery | Session-level aggregates only | Per-card miss rate, struggling cards named |
| Note context | Raw content and a creation date | Tags, folder, related quizzes and flashcards |
| Quiz analytics | Score and wrong answers only | Unusually slow questions flagged |
| Cross-content links | None | Both ways between a note and what came from it |
What this taught us
Everything we needed was already stored. Formatting had been a one-shot serialization step; making it composable gave us a clean slot without touching either end.
Per-card data was the unlock. A student needs to know which cards they keep missing, not an aggregate score.
And a token trade-off has to be explicit: so many items pinned, so many tokens each, enforced, not aspirational.