Production model (proposed container taxonomy): an assignment item points at a sequence_container; its items point at question_containers (serve ONE variation) or resources (slides/passages). A question's figure is part of its own content — each variation carries a matching figure. Responses attach only to the served question. Green flash = row written.
Quick Hitter — "Grape Catch"
sequence_container #70 · 4 items, all question_containers · 2 variations each · NO resources, NO context — every question carries its own dot plot
config: template: quick-hitter · navigation: linear · feedback: immediate
student surface
✕
◉ 449
This dot plot shows how many grapes (out of 5) students caught in their mouth. Each dot is a student. This is Sakeem — how many grapes did he catch?
step 1/4 · serving #9311 from container #511
database (live)
run #1 — serving variation set A (each container: members[run % count])
sequenceProgress(#70) → 0/4 answered · 0 correct · in progress — computed, never stored
sequence_containers
| id | title | config |
|---|---|---|
| #70 | Grape Catch | {template:quick-hitter,navigation:linear,feedback:immediate} |
sequence_items question_container_id XOR resource_id
| sequence_container_id | position | question_container_id | resource_id |
|---|---|---|---|
| #70 | 1 | #511 | NULL |
| #70 | 2 | #512 | NULL |
| #70 | 3 | #513 | NULL |
| #70 | 4 | #514 | NULL |
question_containers + their member questions serve ONE per run
| id | name | concept | members (variations) |
|---|---|---|---|
| #511 | Read a student's dot | read-dot-plot | #9311 ← serving #9312 |
| #512 | Count dots at a value | frequency | #9321 ← serving #9322 |
| #513 | Name the mode | mode | #9331 ← serving #9332 |
| #514 | Find the range | range | #9341 ← serving #9342 |
resources
0 rows — Quick Hitter needs none: each question's figure lives inside its own content (and varies with it)
responses 0 rows — only on SERVED questions
0 rows — answer something on the left
assignment_items this sequence = ONE slot in assignment #77 · question_container_id XOR sequence_container_id
| assignment_id | position | question_container_id | sequence_container_id |
|---|---|---|---|
| #77 | 3 | NULL | #70 |
Slide Deck Lesson — "Point-slope form"
sequence_container #75 · slides = resources (#85 #86 owned, #88 shared) interleaved with CFU question_containers (2 variations each)
config: template: slide-deck · navigation: linear, gated · feedback: immediate
student surface
?
?
Any line can be written from just one known point and the slope.
slide = resource #85 → row in events, never responses
database (live)
run #1 — serving variation set A (each container: members[run % count])
sequenceProgress(#75) → 0/2 answered · 0 correct · in progress — computed, never stored
sequence_containers
| id | title | config |
|---|---|---|
| #75 | Point-slope form | {template:slide-deck,navigation:linear,feedback:immediate} |
sequence_items question_container_id XOR resource_id
| sequence_container_id | position | question_container_id | resource_id |
|---|---|---|---|
| #75 | 1 | NULL | #85 |
| #75 | 2 | #521 | NULL |
| #75 | 3 | NULL | #86 |
| #75 | 4 | NULL | #88 |
| #75 | 5 | #522 | NULL |
question_containers + their member questions serve ONE per run
| id | name | concept | members (variations) |
|---|---|---|---|
| #521 | Identify (x₁, y₁) | identify-point | #8811 ← serving #8812 |
| #522 | Write point-slope form | point-slope-form | #8821 ← serving #8822 |
resources placement = sequence_items above · owner = lifecycle (private: clones/deletes with its sequence · NULL: library, shared across sequences)
| id | title | owner_container_id |
|---|---|---|
| #85 | Slide: Point-slope form (anatomy) | owned by #75 |
| #86 | Slide: Putting it together | owned by #75 |
| #88 | Explorer: point & slope | NULL — shared library |
responses 0 rows — only on SERVED questions
0 rows — answer something on the left
events slide views — never responses
| id | type | detail |
|---|---|---|
| #1 | slide_viewed | seq #75 pos 1 (resource #85) |
assignment_items this sequence = ONE slot in assignment #77 · question_container_id XOR sequence_container_id
| assignment_id | position | question_container_id | sequence_container_id |
|---|---|---|---|
| #77 | 2 | NULL | #75 |
Testlet — "The Inventor's Notebook"
sequence_container #78 · passage = resource #482 in context (the one true context case) · free navigation · verdicts deferred
config: template: testlet · navigation: free · feedback: deferred · context: [resource #482]
student surface
Passage: The Inventor's Notebook
When Ada Reyes donated her notebooks to the city library, archivists expected sketches of her famous water filter. Instead they found pages of failures: seventeen pump designs that cracked, a filter membrane that dissolved in a week, margins crowded with the word 'again.' Reyes had saved every dead end. In a late entry she explains why: 'The failures are the map. Anyone can copy a machine that works; only the map shows you where the swamp is.' Historians now argue the notebooks did more for young inventors than the filter itself — not because they taught success, but because they made failure look like progress.The passage is mainly about…
pick an answer — it saves immediately
passage = context (lives OUTSIDE the questions — the one true context case) · navigation: free · change answers before submit
database (live)
run #1 — serving variation set A (each container: members[run % count])
sequenceProgress(#78) → 0/3 answered · 0 correct · in progress — computed, never stored
sequence_containers
| id | title | config |
|---|---|---|
| #78 | Passage: The Inventor's Notebook | {template:testlet,navigation:free,feedback:deferred,context:[{resourceId:482}]} |
sequence_items question_container_id XOR resource_id
| sequence_container_id | position | question_container_id | resource_id |
|---|---|---|---|
| #78 | 1 | #531 | NULL |
| #78 | 2 | #532 | NULL |
| #78 | 3 | #533 | NULL |
question_containers + their member questions serve ONE per run
| id | name | concept | members (variations) |
|---|---|---|---|
| #531 | Main idea | main-idea | #9411 ← serving |
| #532 | Interpretation | interpretation | #9412 ← serving |
| #533 | Cite evidence | evidence | #9413 ← serving |
resources placement = sequence_items above · owner = lifecycle (private: clones/deletes with its sequence · NULL: library, shared across sequences)
| id | title | owner_container_id |
|---|---|---|
| #482 | Passage: The Inventor's Notebook | NULL — shared library |
responses 0 rows — only on SERVED questions
0 rows — answer something on the left
assignment_items this sequence = ONE slot in assignment #77 · question_container_id XOR sequence_container_id
| assignment_id | position | question_container_id | sequence_container_id |
|---|---|---|---|
| #77 | 4 | NULL | #78 |
Where a sequence sits — the tree ABOVE the assignment
lessons layer (above the assignment) · added 2026-06-11 · full argument + decision card: container-taxonomy proposal §6 / D7 · this view is static — the layer above the assignment is pure structure, nothing to serve
naming rule: containers group questions (below the assignment, for serving) · lessons group assignments (above it, for curriculum) · mode: "lesson" is deprecated — items carry role instead
The bridge to what you just played: the three prototypes are sequence_containers living INSIDE assignments — and in the real curriculum those assignments aren't floating. Each lesson owns four assignments by role: bb is ONE assignment whose items interleave per KC — mini (a Quick Hitter like #70), then that KC's practice, then its check, then the next KC's mini — with each item carrying its own role (instructional | practice | check); the Slide Deck (#75) is the syn-instructional; the Testlet shape (#78) is how the unit test assembles assessment-bank items. The lesson layer never touches serving — it gives "the mastery check of Lesson 5" a foreign key instead of a naming convention.
course "NY Grade 6 Math"
unit "Fractions & Decimal Operations"position 0 → "Unit 0" · owns the unit test + assessment bank
section "Multiplying Fractions"position 2 → "Section B" · the writer's unit of thought
lesson "1/n × Whole"numbering derives globally → "Lesson 5" · the unit of progress gating · also owns its learning objectives (→ concepts, → standards)
#210bb · ONE assignment — per-KC minis + practice + checks, interleaved (items carry role)
#204syn-instructional · a deck like #75
#205syn-practice · sequential
#206syn-check · the mastery check
units / sections / lessons NEW typed, fixed depth · external_id (uuid, unique) → publish = idempotent upsert
| table | parent fk | position | example |
|---|---|---|---|
| units | course_id | 0 | Fractions & Decimal Ops |
| sections | unit_id | 2 | Multiplying Fractions |
| lessons | section_id | 5 | 1/n × Whole |
unit_assignments NEW the unit test lives at unit level
| unit_id | role | assignment_id |
|---|---|---|
| #3 | unit-test | #200 (mode: assessment) |
learning_objectives NEW lesson-owned, 1:N by design (shared M:N rejected — the cross-lesson layer is concepts; proposal §6) · no LO table exists today · edges: lo_concepts (primary KCs via CTA), lo_standards
| lesson_id | text |
|---|---|
| #12 | SWBAT see 1/n × m as repeated addition |
lesson_assignments NEW role = closed enum of four · UNIQUE (lesson_id, role)
| lesson_id | role | assignment_id |
|---|---|---|
| #12 | bb | #210 |
| #12 | syn-instructional | #204 |
| #12 | syn-practice | #205 |
| #12 | syn-check | #206 |
assignment_items for #210 role ON THE ITEM learn it → practice it → prove it, one KC at a time · check semantics key off item role, not assignment mode
| pos | role | points at |
|---|---|---|
| 1 | instructional | seq #71 — KC-1's mini (quick hitter) |
| 2 | practice | qc #551 |
| 3 | practice | qc #552 |
| 4 | check | qc #561 — KC-1's check |
| 5 | instructional | seq #72 — KC-2's mini |
| 6 | practice | qc #553 |
| 7 | practice | qc #554 |
| 8 | check | qc #562 — KC-2's check |
Modules are untouched — they stay the teacher-facing classroom grouping (teacher-created, mutable, lockable) and are not part of the curriculum model. Students see this tree directly: the curriculum ships with a course view reading units/sections/lessons ("you're in Unit 0 → Section B → Lesson 5"); flat modules can't express it, and the bb interleave needs an item-role-aware player anyway. The tree is authored in the workspace repo (dirs + course.json/unit.json manifests, draft-tolerant); publish runs the reconciler once and persists its output here. Students never receive an uninterpreted tree.
External precedent: Common Cartridge/SCORM = generic files + manifest tree for exchange (our repo layer); Open edX OLX = typed fixed-depth course → chapter → sequential → vertical at runtime (our units → sections → lessons); QTI's section selection/ordering rules = serve-ONE / serve-ALL (the containers this doc prototypes). Every major standard splits along the same seam this model does.