What a case is
A case is the unit of practice in Fito: a repository a learner forks, works in, and hands back as a pull request. This page describes what one is made of, before you build any of it.
Read it if you are about to author a case. It covers the shape of the artefact and the words the authoring commands use. It does not cover the commands themselves — each of those has its own page — and it does not cover playing a case, which is Open and work a case.
A case is a repository you own
Section titled “A case is a repository you own”A case lives in its own git repository, under your account or your organisation. Fito never hosts it. What Fito stores is the https URL, the version, and the knowledge the case claims to teach.
Three kinds of ref live in that repository, and they have different lifetimes:
| Ref | What it is | Lifetime |
|---|---|---|
main |
The development branch. Where you edit | Yours, always |
vX.Y.Z |
A branch and a tag, cut at publish | Frozen forever |
case/solution-vX.Y.Z |
The exemplar solution for that version | Frozen forever |
A learner clones the frozen vX.Y.Z, so what they receive never moves under
them. When you correct a case you cut new frozen refs beside the old ones;
you never rewrite one.
The case’s identity is a UUID the backend assigns at registration. It is not
written anywhere in the repository, and it is the <id> in the case’s
/cases/<id> URL.
Two states, one repository
Section titled “Two states, one repository”The source in that repository exists in two states, and you build both.
| State | Where it lives | What it is |
|---|---|---|
| Starter | The frozen vX.Y.Z branch |
What the learner receives |
| Solution | case/solution-vX.Y.Z |
The worked example, for the evaluator |
What each state contains follows from the case type. A diagnostic starter
carries the defect and its solution repairs it. An implementation starter
carries the gap and its solution fills it. A tutorial may use the same code in
both, and still names a solution branch.
.case/ is the same on both. It describes the case, not a branch, so the
folder on the solution branch must match the one the learner receives. The
publish step checks this and reconciles the two before it pushes, so a
divergence is caught rather than shipped — but it is easier to never create one.
That is also why starter_state/known_state.yaml carries no branch field: one
file describing every ref could never name a branch correctly.
The three types
Section titled “The three types”Every case declares one type, and the type binds what the case must contain.
The structural validator enforces the binding.
| Type | What the learner does | What the type requires |
|---|---|---|
diagnostic |
Finds and repairs a defect | starter_state/known_state.yaml carries failing_tests or known_errors |
implementation |
Builds a feature | task/deliverables.yaml includes a code_change entry |
tutorial |
Follows guided steps through a codebase | task/steps.md is present, and failing_tests must not appear |
What .case/ holds
Section titled “What .case/ holds”.case/ is the contract. It sits beside the project’s own source, and it
describes the case on every ref.
Fourteen files are always required:
| File | What it carries |
|---|---|
manifest.yaml |
Title, type, version, status, duration, language, tags, authors, and how a solution is submitted |
artifacts.yaml |
The project files that matter, each with a role |
learning_objectives.yaml |
What the case develops |
prerequisites.yaml |
What the learner must already hold |
rubric.yaml |
The criteria the work is scored against |
solution.yaml |
The exemplar branch and who may see it |
situation/situation.yaml |
The list of narrative documents |
situation/01_intro.md |
The situation the learner lands in |
task/objective.md |
The single goal statement |
task/deliverables.yaml |
The expected outputs |
starter_state/description.md |
The starting condition, in prose |
starter_state/known_state.yaml |
The starting condition, machine-readable |
solution/notes.md |
The intended approach — for the evaluator, never the learner |
solution/notes-template.md |
The blank scaffold of those sections |
Four more are conditional or optional:
task/steps.md— required for atutorial, and only then.quiz.yaml— optional. A case may carry questions alongside its rubric.task/constraints.md— optional. Rules the learner must work within.- Further situation documents — a ticket, an email, a log — each listed in
situation/situation.yamlwith itsformat. acceptable_solutionsinsolution.yaml— further branches that also solve the case, each with an id and an optional description.
solution.yaml’s visibility decides when a learner may see the exemplar:
never, post_completion, or always. Generating a case proposes
post_completion unless you choose otherwise.
The reference set
Section titled “The reference set”Four of those files name knowledge components, each with a Bloom level:
learning_objectives.yaml— what the case develops. At least one entry, and at least one of them carriesweight: primary.prerequisites.yaml— what the learner should already hold. Each entry ismandatory: trueorfalse.rubric.yaml— every criterion is anchored to one component, and every learning objective is anchored by at least one criterion.quiz.yaml— each question names one or more.
A reference is written whole, in the form
<type>:<technology-slug>.<component-slug>, for example
concept:javalin.handler. The three types are concept, execution-fact and
usage-scenario.
Together these make the case’s reference set: the deduplicated pairs of component and Bloom level. It is what the backend resolves when you publish, and it is what freezes.
That rule is why choosing references is the slowest part of designing a case, and why it happens before anything is written.
What a criterion or a question may score
Section titled “What a criterion or a question may score”A rubric criterion or a quiz question met at a score of 2 or 3 records its own Bloom level as the proficiency for the component it names, and a case cover’s prerequisite badges read those same recorded proficiencies. So a criterion aimed at a prerequisite lets the case award the prerequisite it asked the learner to arrive with, and the next case that needs that component reads You have this on the strength of a case that demanded it.
Three rules hold, and they are enforced in different places:
- Every learning objective is scored by at least one rubric criterion. The validator rejects a tree that breaks this, so you find out before you publish. A quiz question does not satisfy it, because the quiz is optional.
- Every learning objective is reachable at its own level. At least one of
those criteria declares the objective’s Bloom level or a higher one, because a
criterion records no more than the level it declares. An
APPLICATIONcriterion covers aCOMPREHENSIONobjective, so one component can be graded at two depths. The validator rejects a tree that breaks this too. - Every criterion and every question names a learning objective. Here the
validator accepts what it could reject — a criterion aimed at a prerequisite,
or at a component the case references nowhere else.
/fito-case-designcompares them for you instead, and reports each miss under Open questions in the proposal for you to settle.
The third is a report rather than a refusal because the structural check also runs when a learner opens a case, and a case already published that way has to stay openable.
What stays out of the learner’s reach
Section titled “What stays out of the learner’s reach”Everything under solution/ is for the evaluator. The learner is briefed from
the situation, the task, the constraints, the deliverables, the artifacts and
the learning objectives — never from the solution notes.
Two files reach further than they look:
learning_objectives.yamlseeds the public catalogue page, which anyone can read before enrolling.prerequisites.yamland the task files are read to the learner in their briefing.
So the prose in those files carries one rule.
Descriptions name the topic, never the move
Section titled “Descriptions name the topic, never the move”A description may say what is broken and what the learner will be able to do. It must not say the move that repairs it.
The test is a single question:
Could a learner who read only this description, and nothing else from the case, apply the fix?
If yes, rewrite it.
- ✗ “Trace a failing request through the ordered handler chain, identify the before-handler that short-circuits it before routing, and apply an idiomatic fix.” — this hands over the diagnosis.
- ✓ “Trace a failing request through the ordered handler chain and repair the composition that breaks it.” — the same objective, with the diagnosis left to the learner.
tutorial cases are exempt. Their steps are the method, so there is nothing
to withhold. The rubric is exempt too: a scoring level cannot describe an
exemplary answer without naming what that answer does, and the rubric is never
read to a learner.
Nothing checks this rule. It is carried by the authoring commands and by your review.
What’s next
Section titled “What’s next”- Design a case — where building one starts.
- Knowledge components — what a case references.
- Competences and learning objectives — the difference between the two, which a case declares and which it does not.
- How a case is scored — what your rubric and quiz turn into for the learner.