Appearance
Multi-place combo steps
A step is normally atomic — it touches exactly one decimal place, like +7 or -30. A recipe can also produce composite steps that touch several places at once, like +47 or -89.
This is how you build training material for whole two- and three-digit numbers: "children work with numbers 10–99, where the tens and ones digits each independently follow the techniques they already know."
Channels
An attachment of a rule to a recipe carries a place:
| Place | Meaning |
|---|---|
0 | Ordinary, global attachment. The rule is eligible at every place. This is the default. |
1, 10, 100, 1000 | The attachment is scoped to that one decimal place — a channel. |
A composite candidate is assembled by taking one digit per configured channel and combining them, with a single shared sign for the whole step. Each channel's digit is then classified independently, as if it were its own atomic step at that place. Every channel must find a matching rule, or the whole candidate is rejected.
There is no cross-channel logic. No rule ever sees "the tens digit and the ones digit together" — each place is curated on its own terms. That is what makes the feature composable: you reuse the rules you already have, at the place you want them.
Place is per-attachment, not per-rule
The same rule can be attached to one recipe more than once, at different places — for example a "Plain (1)" rule attached at both 10 and 1. Each attachment is independent and carries its own usage and quotas.
Which places are available
A channel above the recipe's own sumMax could never fire, so only places the sum ceiling can actually reach are offered:
sumMax | Places available |
|---|---|
| 19 | 1, 10 |
| 99 | 1, 10 |
| 999 | 1, 10, 100 |
| 1999 | 1, 10, 100, 1000 |
If you expected a hundreds channel and it is not offered, raise sumMax first.
Eligibility: global rules still apply
For any candidate landing on place p — whether it is an atomic step or one channel of a composite — the eligible rules are:
everything attached at
place: 0plus everything attached atplace: p
Two consequences:
A channel-tagged rule also admits ordinary atomic steps at that place. A rule attached at place: 10 is not composite-only; it will happily match a plain -10 step too. You do not need a second, global copy of it.
A global rule participates in every channel. A rule attached at place: 0 is eligible on the tens channel and the ones channel alike.
Because every attachment defaults to place: 0, a recipe that uses no channels behaves exactly as it always did — this eligibility rule collapses to "check every attached rule".
Two worked configurations
Tens fixed at one bead, ones unrestricted
Numbers 10–19, where the tens digit only ever moves by a single bead and the ones digit follows the full set of techniques.
Recipe: sumMax: 19, minStepsCount: 7.
| Rule | Usage | Place |
|---|---|---|
| Plain (1) – Addition | filler | 10 |
| Plain (1) – Subtraction | filler | 10 |
| Plain (9) – Addition / Subtraction | target | 1 |
| Plain (7), (8) – Addition / Subtraction | review | 1 |
| the remaining Plain rules | filler | 1 |
| No negative sum | constraint | 0 |
The tens channel gets only the two "1 bead" rules, so the tens digit can only ever step by one. The ones channel gets the whole family. Note that the Plain (1) rules appear twice — once in the tens channel, once as part of the ones channel's full family. That is intended, not a mistake.
Generated steps look like +11, +16, -19 — and also plain -10, which is an ordinary atomic step where the ones channel simply contributed nothing. Composite and atomic steps coexist freely; nothing forces every step to be composite.
Both digits unrestricted
Numbers 10–99, where tens and ones each independently follow the same technique set.
Recipe: sumMax: 99, minStepsCount: 6.
| Rule | Usage | Place |
|---|---|---|
| Plain (any) | target | 1 |
| Plain (any) | target | 10 |
| No negative sum | constraint | 0 |
Two pattern attachments in total. Plain (any) is a consolidated rule matching the whole family in one expression — see the rule catalog.
Generated steps look like +25, -15, +37, -89.
Cost and limits
Candidate growth
Composite candidates grow as 2 × 9ⁿ where n is the number of channels:
| Channels | Candidates |
|---|---|
| 2 | 162 |
| 3 | 1,458 |
| 4 | 13,122 |
Channel count is not capped, but expect generation to get measurably slower past two or three channels.
The 31-rule ceiling
A recipe may attach at most 31 pattern-role rules. Channels multiply against that ceiling: N rules across C channels costs N × C slots, not N.
Attaching a full 18-rule family to two channels is 36 slots and simply will not compile. This is the trade-off the two configurations above illustrate:
| Approach | Slots | You get | You give up |
|---|---|---|---|
| Full family on one channel, minimal set on the other | 20 | Per-digit target / review / filler quotas | Variety on the restricted channel |
| A consolidated "(any)" rule per channel | 2 | Full variety on every channel | Per-digit quotas |
The consolidated "(any)" rules exist precisely as the escape hatch when you hit the ceiling and do not need per-digit quotas.
Two things that will surprise you
A place = X constraint silently rejects every composite step
place is null on a composite step, so a constraint like place = 10 or place IN (1, 10) evaluates to false for every composite candidate — regardless of which digits it actually contains. The recipe then produces only atomic steps, or reports as infeasible, with no error explaining why.
To restrict which places a composite step may touch, use digitAt(current_value, p) instead of place. See the null trap.
A place-scoped rule may not reference current_sum
Inside one channel, "the sum after this step" would mean the running total plus that single channel's digit — an intermediate state the exercise never actually passes through, since a composite step commits all of its digits together. Referencing current_sum in a place-scoped rule is rejected when the recipe is compiled.
Whole-step sum conditions belong in an ordinary constraint rule at place: 0, where they are evaluated once against the real, complete step value. A "no negative sum" constraint works unchanged on composite recipes.
How a composite step is reported
A generated composite step reports place, formula, and digitBefore as null, and instead carries a per-channel breakdown: for each place it touched, the digit and the specific attachment that matched there. An atomic step is the reverse — the three fields are set and the breakdown is absent. A step is always exactly one of the two.
See also
- Decimal places — what
place/formula/digitBeforemean and why they go null - Restricting places — single-place restriction, which still works the classic way
- Worked examples
- Troubleshooting