Skip to content

Recipe concepts

A rule decides whether one candidate step is allowed. A recipe decides what a whole exercise is allowed to look like — how long it is, how large the running sums may get, and what job each rule does inside it.

A recipe is not one exercise. It is a blueprint: the trainer picks the exact step count at generation time, and the generator searches for a concrete sequence that satisfies everything the recipe asks for.

Rule vs recipe

RuleRecipe
ScopeOne candidate step (or one sequence, for sequence-scope rules)A whole exercise
Answers"Is this move legal?""What kind of exercise may be generated?"
StorageGlobal and reusable across many recipesReferences rules by attaching them
OutputPassed / Failed / SkippedA generated exercise, or a failure with a diagnosis

A rule never proposes a value — see What is a rule?. A recipe is what turns the permitted set into an actual sequence, by giving each rule a job.

Recipe fields

FieldTypeNotes
slugstringStable identifier, e.g. RECIPE-4.
namestring1–200 characters.
descriptionstring | nullUp to 5000 characters. The learning goal, in plain language.
minStepsCountinteger ≥ 1Minimum meaningful step count. Defaults to 1.
sumMaxinteger 1–1999Inclusive running-sum ceiling. Defaults to 99.
isActivebooleanWhether the recipe is offered for generation.
isSystembooleanMarks seeded, canonical recipes.
rulesCountintegerNumber of rule attachments; maintained by the server.

There is no placeFilter field and no per-exercise sum narrowing. Everything beyond sumMax and minStepsCount is expressed through the rules you attach — see Restricting places.

The RecipeRule attachment

A recipe does not own rules; it attaches them through a join record. The attachment — not the rule — carries the recipe-specific configuration:

Attachment fieldMeaning
ruleIdWhich global rule is being used.
usageWhat job the rule does in this recipe: target, review, forbidden, constraint, or filler.
place0 for an ordinary global attachment; 1, 10, 100, or 1000 to scope it to one decimal-place channel. See Multi-place combo steps.
minCount / maxCount / exactCountHow many times the rule must or may match. All nullable.

The same rule can be attached to a recipe more than once, as long as each attachment uses a different place. Uniqueness is on the (recipe, rule, place) triple — a repeated (rule, place) pair is rejected as a duplicate. A single replace payload may carry up to 200 attachments.

Attach the same rule twice on purpose

Attaching one rule at place: 1 and at place: 10 is a legitimate, common configuration: the same rule plays a different role in two channels of the same recipe. It is only an error when both attachments name the same place.

The five usage categories

UsageMeaningRequired rule role
targetThe main learning objective. The pattern the exercise is about.pattern
reviewPreviously learned material, allowed to reappear as repetition.pattern
fillerPermitted padding around the target steps. Anything the student may already do.pattern
forbiddenMust never appear. Logically equivalent to maxCount = 0.pattern
constraintValidates whether a candidate step or sequence is allowed at all.constraint

Role compatibility is enforced server-side, not merely suggested: attaching a constraint-role rule as a target, or a pattern-role rule as a constraint, is rejected. Only pattern-role attachments may carry a non-zero place.

Why a recipe needs fillers

A candidate step must match at least one target, review, or filler rule to be admitted. A step that matches nothing is rejected by default — the generator runs a whitelist, so a recipe can never quietly emit a pattern the student has not been taught just because nobody thought to forbid it.

The practical consequence: a recipe with a narrow target and no fillers can only produce sequences its target alone can sustain. Fillers are what give the search room to move.

A recipe with no filler may not be able to open

Generated exercises start from an empty abacus, so prev_sum is 0 at step 0 and digitBefore is 0 at every place. A recipe whose only pattern rules require a non-zero digitBefore has no legal first move and reports infeasible. The fix is a filler rule that can open from zero — see Troubleshooting.

Count quotas

Three optional integers on each attachment shape how often a rule may match:

FieldMeaning
minCountThe rule must match at least this many steps.
maxCountThe rule may match at most this many steps.
exactCountThe rule must match exactly this many steps.

Rules:

  • All three are non-negative integers, or null.
  • exactCount cannot be combined with minCount or maxCount. Use one style or the other.
  • When both are present, minCount must be ≤ maxCount.

Typical usage by category:

UsageTypical counts
targetminCount or exactCount
reviewminCount, maxCount, or exactCount
fillerusually all null
forbiddenall null — the usage already means "zero times"
constraintall null — counts do not apply to constraints

minStepsCount and the effective minimum

minStepsCount is the pedagogical minimum an author sets. The recipe never stores an exact step count; the trainer supplies that at generation time.

Separately, the count quotas imply their own floor. If a recipe demands at least one target step and at least two review steps, no exercise shorter than three steps can satisfy it:

text
derivedMinStepsCount   = implied by the count quotas
effectiveMinStepsCount = max(minStepsCount, derivedMinStepsCount)

A requested step count is valid when stepsCount >= effectiveMinStepsCount. Only minStepsCount is stored; the derived and effective values are computed.

The derived value is estimated by summing exactCount ?? minCount ?? 0 across target, review, and filler attachments. That is deliberately an over-estimate: one step can satisfy two rules at once (the canonical rule set is not a partition — some classifications overlap), so the true floor can be lower. The admin UI therefore shows it as a hint rather than a hard block.

sumMax

sumMax is the inclusive ceiling on every running sum an exercise may pass through. The floor is a hard 0 and is not configurable — a soroban has no negative state.

It is a recipe field rather than a rule because the generator's feasibility analysis has to know the width of the sum axis before any rule runs. Rules can narrow the range further, but never widen it.

Sum range covers the field in full, including how to confine sums to a window narrower than sumMax — which needs more care than it first appears.

See also

Reference and cookbook for mental-math rule and recipe authoring.