What
Question has no domain field, so a learner who finishes the 60-question CCA-F mock gets a single number and no idea which domain sank them.
The curriculum knows the domain for every one of its 696 questions. The seeder uses that knowledge to build a blueprint-weighted exam. Then the information is thrown away, and grading can't use it.
Why this matters more than it looks
scripts/seed_cca_courses.py hardcodes the official CCA-F split and assembles the mock to match it:
| Domain |
Weight |
agentic — Agentic Architecture |
27% |
claudecode — Claude Code Workflows |
20% |
prompt — Prompt Engineering |
20% |
tools — Tool Design & MCP |
18% |
context — Context Management |
15% |
_weighted_exam_series() even partitions Rick Hightower's 480-question pool into three non-overlapping full-length weighted exams. Real work went into making these exam-shaped.
But grading is flat:
# now_lms/vistas/evaluations.py:150
def calculate_score(attempt) -> float:
total_questions = len(attempt.evaluation.questions)
correct_answers = sum(_answer_is_correct(answer) for answer in attempt.answers)
return (correct_answers / total_questions) * 100
And evaluation_result.html renders exactly: score, pass/fail, and per-question correct/incorrect with the explanation. There is no domain anywhere in it.
So the weighting is applied at build time and invisible at grade time. A learner scoring 68% — four points under the 72% pass mark — has no way to know whether to reread Agentic Architecture or Context Management. That diagnostic breakdown is most of what makes prep material worth sitting.
Where it's lost
The seeder maps banks onto the LMS like this:
| Bank field |
Lands as |
text |
Question.text |
rationale + source |
Question.explanation |
options[] + answerIndex |
QuestionOption rows, is_correct |
domain, domainKey, domainName |
nothing — dropped |
id (bank id) |
nothing — dropped |
$ grep -c domain now_lms/db/__init__.py
0
Domains do survive as course structure — one CursoSeccion per domain, each with its lesson and practice quiz. That part works well. What's missing is per-question metadata, which is what a mixed-domain exam needs.
Suggested shape
Question.domain_key — nullable String(64), plus domain_name String(200) (or resolve names from a lookup; nullable either way so upstream's own questions are unaffected). Guarded Alembic revision, same pattern as your 20260731_120500 prior-credentials migration.
- Seeder writes it — the values are already in hand at
_add_evaluation(); it's a field assignment, not new logic.
- Grade per domain — group
attempt.answers by question.domain_key, emit {domain_name: (correct, total, pct)} alongside the flat score. The flat score stays authoritative for pass/fail so nothing about certification changes.
- Show it — a breakdown table on
evaluation_result.html, ideally with the blueprint weight beside each domain so a learner sees that Agentic Architecture is 27% of the real exam.
Backfill is optional. Existing seeded questions would have domain_key = NULL and the breakdown just doesn't render for them; a reseed picks it up. Please don't block on backfilling.
Also worth considering (your call, not a requirement)
Storing the bank id too would make a question traceable to its source file — useful when a bank gets corrected and we need to find every course that shipped the old wording.
Acceptance
Context
- Seeder:
scripts/seed_cca_courses.py (632 lines, idempotent on Curso.codigo) — reads the private intent-solutions-io/intent-curriculum via CCA_CONTENT_DIR.
- Curriculum: 696 questions across 5 banks, 0 ID collisions, every question carrying all 9 fields including
domain/domainKey/domainName. The data is clean and ready — only the destination schema is missing.
Related: #37 (ADR-6 short-answer question type) also extends the question model, so it's worth checking whether the two migrations should be sequenced.
- Jeremy Longshore
intentsolutions.io
What
Questionhas no domain field, so a learner who finishes the 60-question CCA-F mock gets a single number and no idea which domain sank them.The curriculum knows the domain for every one of its 696 questions. The seeder uses that knowledge to build a blueprint-weighted exam. Then the information is thrown away, and grading can't use it.
Why this matters more than it looks
scripts/seed_cca_courses.pyhardcodes the official CCA-F split and assembles the mock to match it:agentic— Agentic Architectureclaudecode— Claude Code Workflowsprompt— Prompt Engineeringtools— Tool Design & MCPcontext— Context Management_weighted_exam_series()even partitions Rick Hightower's 480-question pool into three non-overlapping full-length weighted exams. Real work went into making these exam-shaped.But grading is flat:
And
evaluation_result.htmlrenders exactly: score, pass/fail, and per-question correct/incorrect with the explanation. There is no domain anywhere in it.So the weighting is applied at build time and invisible at grade time. A learner scoring 68% — four points under the 72% pass mark — has no way to know whether to reread Agentic Architecture or Context Management. That diagnostic breakdown is most of what makes prep material worth sitting.
Where it's lost
The seeder maps banks onto the LMS like this:
textQuestion.textrationale+sourceQuestion.explanationoptions[]+answerIndexQuestionOptionrows,is_correctdomain,domainKey,domainNameid(bank id)Domains do survive as course structure — one
CursoSeccionper domain, each with its lesson and practice quiz. That part works well. What's missing is per-question metadata, which is what a mixed-domain exam needs.Suggested shape
Question.domain_key— nullableString(64), plusdomain_nameString(200)(or resolve names from a lookup; nullable either way so upstream's own questions are unaffected). Guarded Alembic revision, same pattern as your20260731_120500prior-credentials migration._add_evaluation(); it's a field assignment, not new logic.attempt.answersbyquestion.domain_key, emit{domain_name: (correct, total, pct)}alongside the flat score. The flat score stays authoritative for pass/fail so nothing about certification changes.evaluation_result.html, ideally with the blueprint weight beside each domain so a learner sees that Agentic Architecture is 27% of the real exam.Backfill is optional. Existing seeded questions would have
domain_key = NULLand the breakdown just doesn't render for them; a reseed picks it up. Please don't block on backfilling.Also worth considering (your call, not a requirement)
Storing the bank
idtoo would make a question traceable to its source file — useful when a bank gets corrected and we need to find every course that shipped the old wording.Acceptance
passing_score=72.0still decides certification.Context
scripts/seed_cca_courses.py(632 lines, idempotent onCurso.codigo) — reads the privateintent-solutions-io/intent-curriculumviaCCA_CONTENT_DIR.domain/domainKey/domainName. The data is clean and ready — only the destination schema is missing.Related: #37 (ADR-6 short-answer question type) also extends the question model, so it's worth checking whether the two migrations should be sequenced.
intentsolutions.io