Validate the `.case/` folder
The structural validator is the ruleset every .case/ tree must pass before it
can be published. It reads a local checkout and nothing else.
You rarely run it yourself. /fito-case-generate drives it as its edit-and-check
loop, and /fito-publish-case re-runs it before anything is pushed. This page is
for reading its output, and for re-checking a tree by hand.
Before you begin
Section titled “Before you begin”- A working directory containing
.case/. See Generate the.case/folder.
Run it by hand
Section titled “Run it by hand”node <fito-case-author skill dir>/validate.js <case-working-dir>Replace <fito-case-author skill dir> with the path to Fito’s
fito-case-author skill, and <case-working-dir> with the directory holding
.case/. Inside a pi session you can ask for the skill by name and it resolves
the path for you.
It prints JSON on standard output and sets an exit code: 0 for a pass, 1
for a failure, 2 for a usage problem such as a missing or non-existent
directory.
Read a failure
Section titled “Read a failure”{ "ok": false, "errors": [ { "file": "rubric.yaml", "message": "rubric criterion weights must sum to exactly 100 (got 90)", "path": ["criteria"] } ]}Every violation is reported at once — it never stops at the first. Each error is
scoped to the .case/ file that produced it, and usually to a field path
inside that file.
Fix the named files and run it again. Do not regenerate the tree over an error you can fix.
Read a pass
Section titled “Read a pass”{ "ok": true, "attestation": { "pass": true, "validatorVersion": "0.1.8", "rulesetVersion": "1.8.0" }, "references": [{ "componentId": "concept:javalin.handler", "bloomLevel": "APPLICATION" }], "optionalFieldsNotUsed": { "manifest.yaml": ["created_at", "updated_at"] }}attestation— the stamp a publish carries to the Fito server. It names the validator build and the ruleset version the tree was checked against.references— the case’s reference set: every component and Bloom level, deduplicated, gathered from the learning objectives, the prerequisites, the rubric and the quiz. This is the set the server resolves at publish, and the set that freezes afterwards.optionalFieldsNotUsed— the optional fields this case leaves empty, grouped by file. Nothing here is an error.
What the ruleset enforces
Section titled “What the ruleset enforces”| Rule | What it means |
|---|---|
| Required files | All fourteen are present, plus task/steps.md for a tutorial |
| File shapes | Every field carries the type and the allowed values its file declares |
| Reference form | Every reference reads <type>:<technology-slug>.<component-slug> |
| Bloom levels | Only KNOWLEDGE, COMPREHENSION and APPLICATION |
| Rubric weights | Positive whole numbers summing to exactly 100 |
| Rubric levels | Every criterion covers scores 0, 1, 2 and 3 |
| One primary objective | At least one learning objective carries weight: primary |
| Objective coverage | Every learning objective is scored by at least one rubric criterion. A quiz question does not count |
| Objective 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 |
| Situation documents | Every file listed in situation/situation.yaml exists on disk |
| Deprecation reason | A deprecated case gives a deprecated_reason |
| Case type | The artifact the type binds is present — see The three types |
| Quiz shape | A multiple_choice question has choices and an answer matching one of them; the other two types carry an evaluation |
What it does not check
Section titled “What it does not check”Two things a passing run tells you nothing about. Both are resolved by the Fito server when you publish:
- Whether the components you referenced exist. The form is checked here; the existence is not.
- Whether your git refs are reachable. The server pings the version tag and the solution branch itself.
It cannot check the topic-not-move rule either. A case that gives away its own answer validates clean. That one is carried by the authoring commands and by your review.
Versions
Section titled “Versions”The attestation carries two numbers, and they move independently:
validatorVersionis the build of the checker.rulesetVersionis the version of the rules themselves.
A published case is stamped with the pair it was checked against.
What’s next
Section titled “What’s next”- Generate the
.case/folder — where the edit-and-check loop lives. - Publish a case — where the server picks up what this run could not decide.