Household pattern
Ceiling
A numeric limit on a Do-it capability that reads as a sentence and adjusts as a slider.
States
Money per order
Each order
Up to $40 an order
Money per week
Each week
Up to $120 a week
Count per day
Each day
Up to 3 coupons a day
Days (same day only)
How far it can move things
Same day only
No limit
warning tone, explicit button to set one
Each order
No limit
Nothing caps this. It can go as high as it likes without asking.
Disabled (not on Do it)
Each order
Up to $40 an order
Only applies while this is set to Do it.
With a replay check, under
Thursday's $19.80 against the week
Each week
Up to $120 a week
$97.15 before this, $116.95 with it, under the limit this week.
With a replay check, over
Wednesday's $55 against $40
Each order
Up to $40 an order
This one: $55, over the limit.
Read-only
Each order
Up to $40 an order
Highlighted (linked to)
Each order
Up to $40 an order
When to use
- Under an AutonomyLadder, one per ceiling the template defines for that capability.
- In a detail panel with `check`, to show how close an action came to the limit.
When not to
- On capabilities set to Ask first or Never. Render it disabled with a reason instead of hiding it, so the user learns it exists.
- For anything that is not a number per action, per day or per week. Hard limits in words are HardRules.
Props
| Name | Type | Notes |
|---|---|---|
| ceiling* | Ceiling | The schema object: measure, scope, limit, range, unit. |
| onChange | (limit: number | null) => void | null means no limit. The component offers that as an explicit button, never as the end of the slider. |
| disabled + disabledReason | boolean, string | "Only applies while this is set to Do it." |
| check | CeilingCheck | From the engine. Shows the measured value and whether it was over. |
| id | string | Anchor id, `ceiling-<ceilingId>`. |
| readOnly / highlighted | boolean | State flags. |
* required
Accessibility invariants
- A native range input, so keyboard and assistive tech work without extra code.
- aria-valuetext is the full sentence ("up to $40 an order"), not the bare number.
- The sentence above the slider is aria-live, so changing the value is announced once.
- "No limit" is a warning state with text and an icon, not just a colour.
Agent instructions
For anything generating UI from this system
Render a Ceiling for each entry in capability.ceilings, as children of that capability's AutonomyLadder. Pass the ceiling object straight from the contract; do not reformat the limit yourself, the component builds the sentence. When the capability is not on Do it, keep the Ceiling but pass disabled with disabledReason. Use id="ceiling-" + ceiling.id. Never hide the no-limit state or restyle it as neutral.