The Graph Tree Builder
A build retrospective on the adaptive knowledge graph behind Amal, a kids' Arabic-learning app used by 95,000+ families. • Day 5 turns the frontier into something a child actually sees: a per-child, realtime subject tree — two children get two different trees from one graph.
From a set of concepts to a screen a child can use
Day 4 produced the frontier — the set of concepts a specific child is ready for. But a set of concept ids is not something you can hand to a five-year-old. The app's home screen shows a subject tree: an organized, tappable layout of subjects and the lessons inside them, in an order that makes sense to a child. The job of turning the raw frontier into that screen belongs to a component called GraphTreeBuilder.
The word "tree" here means the ordinary UI kind — a top level of subjects, each expanding into the lessons under it — not a data-structure lecture. GraphTreeBuilder's one responsibility is to arrange the child's ready concepts (plus a little context around them) into that browsable shape, freshly, for this child, right now.
The diagram shows the transformation: an unordered set of ready concepts on the left becomes a grouped, ordered, tappable tree on the right. Same information, but now shaped for a child to navigate.
FRONTIER (a flat set) SUBJECT TREE (what the child sees)
{ /b/ , /t/ , count-to-5 } ─► ┌─ Arabic Letters
│ ├─ sound /b/ ● ready
│ └─ sound /t/ ● ready
└─ Early Math
└─ count to 5 ● readyWhat "realtime" means here
Realtime in this system means the tree is built fresh when the child opens the app, from the current graph and the child's current overlay — not stored as a fixed menu that someone assembled in advance. This is the single property that separates an adaptive tree from a flat curriculum's static menu, so it is worth being precise about.
Because the overlay changes every time the child practices (strengths go up, HLR half-lives shift), a tree built on demand automatically reflects the very latest progress:
- A concept the child mastered yesterday has left the frontier, so it no longer shows as a "ready" next step today.
- A concept whose last prerequisite the child just finished has entered the frontier, so it appears as newly available.
- A concept whose memory is decaying (Day 3's HLR flags it as due for review) can be surfaced back into the tree at the right moment.
None of that requires a human to re-plan the menu. The tree is a view computed over live data, the same way the frontier is — which is exactly why it can differ from child to child and from day to day.
Two children, two trees, one graph
The clearest way to feel what GraphTreeBuilder buys you is to build the tree for two different children at the same moment and see two genuinely different screens come out of the identical graph. This is the per-child path from Day 1, finally rendered as pixels.
Take the same Arabic slice plus a little math. Child A has finished the early letters and their sounds and is moving into joining letters and small words. Child B is newer — still consolidating individual letter sounds. Their overlays differ, so their frontiers differ (Day 4), so their trees differ:
The diagram is the whole system's promise made concrete: two children open the same app in the same second and see different, appropriate home screens — one working on words and letter-joining, one working on letter sounds and early counting. Nobody built two curricula. There is one graph; the difference is entirely the two overlays flowing through the same LearnerFrontier and GraphTreeBuilder.
ONE GRAPH -> TWO REALTIME TREES
CHILD A (further along) CHILD B (just starting sounds)
┌─ Arabic Words ┌─ Arabic Letters
│ ├─ "baab" (door) ● ready │ ├─ sound /b/ ● ready
│ └─ "bayt" (house) ● ready │ └─ sound /t/ ● ready
└─ Connected Forms └─ Early Math
└─ joining baa ● ready └─ count to 5 ● ready
built at the SAME instant, from the SAME nodes+edges,
differing ONLY because the two overlays differWhere the tree builder sits
It helps to see GraphTreeBuilder in its place in the pipeline, because its job is deliberately narrow — it is the stage that turns "what is this child ready for" into "what does this child see", and hands off to the next stage that turns a chosen concept into an actual lesson.
The diagram places today's component in the middle of the flow: the frontier feeds it, and it feeds the byte generator. Keeping these as separate stages matters for the same reason Day 4's purity did — each stage does one job, so each can be tested and changed on its own.
The tree builder's specific responsibilities, and the things it deliberately leaves to others:
- It does group ready concepts into subjects, order them into a child-legible sequence, and attach the display context (labels, the assets from Day 2) each node needs to render.
- It does decide presentation order among frontier concepts — which is why the frontier was a set, not a single pick (Day 4): the choosing happens here.
- It does not generate the lesson content itself. A node in the tree is still just a concept reference; turning that concept into a playable lesson — a "byte" — is the next stage's job, and the subject of Day 6.
graph + overlay
│
▼
LearnerFrontier -> the SET of ready concepts (Day 4)
│
▼
GraphTreeBuilder -> arrange into a per-child TREE (Day 5, here)
│
▼
GraphByteGenerator -> turn a chosen concept into a byte (Day 6)Key takeaways
- GraphTreeBuilder turns the frontier (a flat set of ready concepts) into the subject tree the child actually taps through — grouped into subjects, ordered to be child-legible, with display assets attached.
- The tree is realtime: built fresh from the current graph and the child's current overlay each time, so it reflects the latest mastery and spaced-repetition state without anyone re-planning a menu.
- Because the overlay differs per child, the same graph at the same instant yields different trees for different children — the per-child path from Day 1, finally rendered as a screen.
- The tree builder is the stage that chooses presentation order among frontier concepts, which is why Day 4 returned a set rather than one pick.
- It deliberately stops at "a tree of concept references"; generating the actual lesson from a chosen concept is the next stage, GraphByteGenerator (Day 6).
Checklist
- [ ] I can explain what GraphTreeBuilder takes in (the frontier) and produces (the subject tree).
- [ ] I can define "realtime" here and give an example of how the tree changes as a child's overlay changes.
- [ ] I can work the two-children example and explain why one graph yields two different trees at the same instant.
- [ ] I can place GraphTreeBuilder between LearnerFrontier and GraphByteGenerator and say what each stage owns.
- [ ] I can state what the tree builder deliberately does not do (generate lesson content) and why that is a separate stage.