Wizard When-step and the per-section Edit-when page now gate the
HourSelect behind a checkbox. The control reads "[ ] Pause sending
by (optional)" by default — checking it reveals the hour picker;
unchecking hides it again.
The off-state is encoded as deliveryWindowEndHour=24 (next-day
midnight) so the bot's existing windowEndAt helper produces an end
that's always in the future for any reminder fired the same day,
making the gate effectively never trip. This avoids a NULL-allowing
schema migration while still giving the operator a clean "no
deadline" mode.
Existing reminders:
• Stored 24 → checkbox starts UNCHECKED, picker hidden.
• Stored anything else → checkbox starts CHECKED, picker shows
the saved value.
• Unsupplied (legacy rows) → checkbox starts UNCHECKED.
RunEtaPill picks up an optional `windowEndAt` prop. When omitted —
the no-deadline path — it renders a neutral grey pill with just the
ETA, skipping the green "Fits before deadline" / amber "Likely to
pause" comparison that wouldn't be meaningful without a deadline.
Tests:
* when-form-deadline.test.tsx (4) — fresh / 24 / real-hour /
optional-hint paths.
* run-eta-pill.test.tsx (+1) — neutral pill when windowEndAt is
undefined.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Renders an advisory ETA badge above the Schedule button:
* Green "Fits before deadline" when the projected finish lands
before the chosen deadline hour.
* Amber "Likely to pause" with a "Push the deadline later or split
into smaller runs" hint when it doesn't.
Pill is purely informational — the operator can still schedule a
run that's likely to pause; the pause/resume flow (Phase 3) covers
that case. The pill just removes the surprise.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>