ump.play()
play() is the cleanup companion to check(). It never mutates values. It only recommends what the consumer should clear or reset.
Signature
Section titled “Signature”type Snapshot< F extends Record<string, FieldDef>, C extends Record<string, unknown>,> = { values: InputValues conditions?: C}
ump.play( before: Snapshot<F, C>, after: Snapshot<F, C>,): Foul<F>[]Return Shape
Section titled “Return Shape”type Foul<F extends Record<string, FieldDef>> = { field: keyof F & string reason: string suggestedValue: unknown}When A Recommendation Appears
Section titled “When A Recommendation Appears”play() produces a foul when a field holds a non-empty value that just fell out of play. There are two ways that can happen:
Availability foul — the field was enabled in before and is disabled in after.
Appropriateness foul — the field is still enabled, but a fairWhen predicate that was passing in before is now failing in after. The value is present and the field is available, but the selection is no longer appropriate given the current form state.
In both cases, a recommendation only appears when:
- The trigger above applies.
- The current value in
afteris still non-empty under that field’sisEmptyrules. - The current value differs from the suggested reset value.
Condition three matters for defaults. If a field falls out of play while it already holds its default value, recommending that same value again would be a no-op.
suggestedValue
Section titled “suggestedValue”suggestedValue is:
FieldDef.defaultwhen the field defines oneundefinedotherwise
const ump = umpire({ fields: { isAllDay: { default: true }, startTime: { default: '09:00' }, endTime: {}, }, rules: [],})Disabling startTime recommends '09:00'. Disabling endTime recommends undefined.
Conditions-Only Transitions
Section titled “Conditions-Only Transitions”Because snapshots include conditions, play() works even when field values do not change.
signupUmp.play( { values: formValues, conditions: { plan: 'business' } }, { values: formValues, conditions: { plan: 'personal' } },)That is how plan switches, feature flags, or captcha expiration can still produce reset recommendations.
Convergence
Section titled “Convergence”play() has a useful convergence property: as the consumer applies the recommended resets, the next pass eventually returns [].
That is true even for non-empty defaults because the method suppresses no-op recommendations when the field already equals its suggestedValue.
Example
Section titled “Example”const fouls = signupUmp.play( { values: { companyName: 'Acme', companySize: '50', }, conditions: { plan: 'business' }, }, { values: { companyName: 'Acme', companySize: '50', }, conditions: { plan: 'personal' }, },)
// [// {// field: 'companyName',// reason: 'business plan required',// suggestedValue: undefined,// },// {// field: 'companySize',// reason: 'business plan required',// suggestedValue: undefined,// },// ]foulMap() — lookup by field
Section titled “foulMap() — lookup by field”play() returns an array, which is convenient for rendering a banner but requires .find() when you need the foul for a specific field. foulMap() converts the array into a field-keyed map:
import { foulMap } from '@umpire/core'
const fouls = ump.play(before, after)const byField = foulMap(fouls)
byField.companyName?.reason // 'business plan required'byField.referralCode // undefined — no foul for this fieldBoth representations are useful: the array for iterating (fouls banner, reset-all button), the map for per-field access (inline foul indicators, field-level reset buttons).
Reactive foul() in signals
Section titled “Reactive foul() in signals”The @umpire/signals adapter exposes reactive.foul(name) for per-field foul access with fine-grained reactivity:
const reactive = reactiveUmp(ump, adapter)
// Per-field — only re-renders when this field's foul changesconst foul = reactive.foul('companyName')// → Foul | undefined
// Full array — for banner renderingconst allFouls = reactive.foulsreactive.foul(name) mirrors reactive.field(name) — availability and fouls have the same per-field accessor pattern.