Issue #3618769: Let each field say how a rule reaches it, instead of the engine holding the lists

The last of the four lists [#3618694] set out to remove. Three facts decide what a rule does to a resource field: whether it reaches the field at all, what the field's reader lands on when no level answers, and whether a rule value is copied into the row instead of followed from it.

All three were lists of names in the engine — FOLLOWED (19), FALLS_BACK_TO (17), COPIED_ON_CREATE (9) — and two thirds of those names are fields the engine does not own. A field renamed on the far side simply stopped matching, and nothing failed: no inherit choice on the widget, the stored value read literally by its reader, a rule reaching nothing.

Declared on the definition now, by the module that builds it: RuledField::FOLLOWS, RuledField::FALLS_BACK_TO, RuledField::COPIED_ON_CREATE. Twenty-eight declarations across six modules. The fallback belongs there for a reason of its own — it is what the reader lands on, and the reader lives beside the field. Each manager names the unit nothing recognizes, and seating a party together; the engine was holding those answers on their behalf.

Why the definition is the only place it could live. The inherit value is added in an alter on these very definitions, and the registry that knows which fields a rule reaches answers by reading them, so asking it from inside that alter recurses until PHP gives up. The old comment said exactly that, and it was right — it is why the lists existed. But the alter is handed the definitions it would have to ask about, so reading the fact off the one in hand asks nobody anything. The constraint is respected rather than worked around.

The guard. Declaring moves the failure rather than removing it: a module that forgets leaves its field out of the rule chain, and that still fails nothing on its own. So the expected sets are written down in a test, module by module, and a forgotten declaration fails naming the field — seen to fail before being trusted. A third test asks what an operator would: is "Inherit" offered on this field, is it what a new resource starts as, and does the choice say what inheriting all the way up lands on.

One dead entry removed. FALLS_BACK_TO carried settlement_grace, which nothing could ever read: the alter requires the field to be followed first, and a duration is not a list of strings. Gone rather than translated into a declaration nothing would use.

Verification. phpstan level 3 [OK], phpcs and cspell clean. Eight kernel classes green including the rule resolver, the rule form, the rule maker and the boundary guard. The new test installs all six declaring modules at once, which is the only way to see that every provider actually declared.

Merge request reports

Loading
Loading