Let a policy be switched off without removing it, keeping the settings it was tuned with
Adds an enabled flag to a constraint policy, so an operator can stop it enforcing without removing it.
Storage. Being switched off is what is stored; being enforced is the absence of the key. So a policy nobody has touched carries nothing extra, a policy stored by any writer that does not know about the flag reads as enforced, and no data has to be migrated. ConstraintPolicyResolver::isEnabled() is the single reader.
The three seams.
ConstraintPolicyResolver::normalize()rebuilt every entry as an id and its settings, silently dropping anything else, so it now carries the flag through.ConstraintPolicyManager::attachmentsFor()skips a policy that is off. It is the one gate feeding bothevaluate()andceilingFor(), so a switched-off limit neither refuses nor caps a quantity control. The skip is before the missing-plugin check, so a policy that is off and names a plugin that has gone is neither logged nor answered by the stand-in that refuses.ConstraintPolicyResolver::sharesLimit()does not go through the manager. It expands across_resource_limitname into the resources sharing it, so without this a limit switched off on one resource would keep dragging that resource into the set an enabled limit elsewhere counts.
Applies at every checkpoint, the hold included, unlike the per-transaction waiver an operator sets by confirming anyway, which every checkpoint but the hold honors. A policy that is off is not a rule for as long as it is off.
UI. An Enforce this policy checkbox on the settings form, outside the plugin's own settings subform, and a Status column on the Policies tab stating in words which policies are enforced. The form rebuilds the stored entry on save, so it carries the flag: editing a number does not switch a policy back on.
Tests. Each was run against the unmodified code first and seen to fail.
- A switched-off limit caps nothing and keeps the ceiling it was tuned with, and publishes no ceiling to a quantity control (
TransactionQuantityLimitTest). - A switched-off cross-resource limit leaves its resource out of the pool an enforced limit counts, asserted from the enforced side so it cannot pass on the manager's gate alone (
OrderConstraintTest). - A switched-off policy is storable on a resource type, which is where the config schema is proved, and an enforced one stores nothing extra (
PolicyAttachmentStorageTest). - The checkbox round-trips, the tab says which policies are enforced, and editing a ceiling leaves a policy switched off (
ConstraintPolicyUiTest).
Docs and the French translation are in the same commit.
Also in this MR: the policy screens name the policy. A host may carry several policies, and the same plugin twice with different settings, so screens headed Edit policy and Remove this policy? asked an operator to trust the link they arrived by. The add, edit and remove screens now name it, the way core's image effect forms do: the route keeps its static title and the form overrides #title. ConstraintPolicyManager::labelFor() resolves the name, answering with the stored id when nothing defines the plugin, because the id is what somebody has to go and correct. Pinned by testPolicyScreensNameThePolicy, which was run against the unmodified titles and seen to fail on all three. The host stays out of the title: it is already in the breadcrumb.
Riding along rather than in its own issue to spend one pipeline instead of two; say the word and it can be split out.