Quiet hours: one rule

Nine places decide whether it is too late or too early to message someone, and they disagree. This page plans one rule in one place. Nothing is being built yet.

Proposed — pending review · Not scheduled — planned only · 2026-10-06

DecidedTodayBefore and afterThe planWhat changesQuestionsAppendix

Decided (Fede, 2026-10-06)

"I don't want different logic spread out… if it's not centralized, we should do it first. So it'll be a refactor. And then we should pass in overrides or exceptions… another example could be an emergency."

Exceptions come after the refactor. Not approved yet ("don't add that yet").

"the quiet hour should conform to that TCPA regulation."

One window: 8 AM to 9 PM, on the recipient's clock.

"I don't think we should build it right now."

This page is the plan only.

Today: nine rules

Show
#WhereHours and whose clockAt night
1Single sends to prospects and residents8 AM–9 PM, building clock, guesses Chicago if no zoneCaller decides
2Renewal messages to residents8 AM–9 PM, building clock, refuses if no zoneSkips; the workflow waits
3Waiting-message holds for prospects (follow-ups, tour nudges, collections and vendor chasers)8 AM–9 PM, building clock, guesses ChicagoHolds until 8 AM
4Held and approved texts (prospects, residents, vendors)8 AM–9 PM, building clock, refuses if no zoneHolds until 8 AM
5Handyman and vendor dispatchPer handyman, default quiet 9 PM–7 AM, building clockHolds. Emergencies always go through
6Collections legal check8 AM–9 PM, building clock (area code only as a cross-check), no guessRefuses
7Collections rule checkHour table, hour handed inAdds a note only
8Self-guided staff notesSends at any hour (temporary, Fede 10/06). Was quiet 9 PM–7 AM, building clockSends
9Mass texts to residentsFlagged late 9 PM–7 AM, building clock, guesses ChicagoWarns the manager, sends anyway if they say now

Before and after

Show
TODAY: 9 RULES Renewals (8–9, refuse) Prospect holds (8–9, Chicago) Handyman (21–7) Staff notes, mass text, ... + 5 more, each with its ownhours, clock and night rule AFTER: ONE POLICY Every sender asks:"send now, or when?" Inputs: recipient, their clock, channel, now Answers: send now / hold until 8 AM One window: 8 AM–9 PM, recipient local time Stricter states = named profiles on the same policy Exceptions (emergency, ...) come later, not approved
Where it lives: one pure module, not the message dispatcher. Long-running workflows need a "wait until T" answer.

The plan: a chain of small PRs

Show

Not scheduled — planned only Each PR is one concern, about 300 lines or less, and merges alone. PRs 1 to 7 change no behavior. PRs 8 to 11 do, and come after.

PROne concernBehaviorProof
1The policy module and named profiles for today's windows. No callers.No changeTable test: one row per rule above, today's answer pinned, oddities included
2One shared clock reader; the six copies call itNo changeSame table test through each copy, plus daylight-saving edge cases
3Renewals onto the policyNo changePinned per caller: renewal.ts calls, auto-start, bypass flag
4Waiting-message holds (prospect, tour, collections and vendor chasers). Delete the old wait calculatorNo changePinned hold times across 24 hours, each zone
5One-off sends (voice callback, application send, re-engagement display)No changePinned per caller
6Held texts and the two collections checksNo changePinned, including the "no clock" refusal
7Maintenance. Emergency stays an input, same valuesNo changePinned per handyman window, emergency passes
8–11The four behavior changes below, one PR eachChangesBefore and after test per PR, plus replay of recent real sends
12Build check: no hour logic outside the policy module. Deletes the old exportsNo changePlanted violation fails the check
LaterNamed exceptions and a dispatcher backstopNot approved—

The "pinned" proof: each caller's test records today's answer for every hour of the day in each zone first, then must pass unchanged after the move. A deliberately wrong policy must turn it red.

What changes behavior (the TCPA ruling)

Show
PRBeforeAfter
8Self-guided staff notes send at any hour (temporary, Fede 10/06). Before that: dropped 9 PM–7 AMHeld until 8 AM, sent 8 AM–9 PM
9Handyman default quiet 9 PM–7 AMWindow 8 AM–9 PM (emergencies unchanged until exceptions exist)
10Mass text with "now" at night sends anywayHeld until 8 AM
11Building clock for prospect, renewal and voice sendsRecipient clock from area code, building zone as fallback

PR 11 also reverses the 2026-08-25 collections ruling that moved collections to the building clock.

Open questions for Fede

Show

a. Do staff and vendor notes also follow 8 AM–9 PM?

TCPA covers solicitations, so internal notes are not legally bound.

b. Handyman emergencies always page: first named exception later?

c. A building has no time zone

Only matters when the recipient's area code also fails.

d. Which states, and does a lawyer confirm the state profiles?

The code only has a Colorado rule set (collections). Western Slope is Colorado. I could not confirm the states for JP&Co or Situs from the code, so those are unverified.

Appendix: files and areas

For engineers
  • Home: src/lib/domain/compliance/quiet-hours/policy.ts next to recipient-clock.ts (contactWindowInTimeZone, area-code lookup, no fallback).
  • PR 2: Intl clock copies in temporal/activities/quiet-hours.ts, maintenance/quiet-hours.ts, self-guided-text-leaf.ts, mass-comms/send-time.ts, recipient-clock.ts, prospect-outreach.ts.
  • PR 3 (row 2): temporal/activities/renewal.ts, *ForProperty variants, bypassQuietHours.
  • PR 4 (row 3): prospect-outreach.ts resolveOutreachQuietHoldActivity, msUntilNextWindowOpen.
  • PR 5 (row 1): voice-callback, pms-generated-application-send, leasing/reengagement-view.ts.
  • PR 6 (rows 4, 6, 7): escalation/held-message-rule.ts, bake-gate.ts, held-text.ts, vendors/dial-approved-session.ts, collections/approved-dun-send*.ts, compliance/collections/assess.ts (~line 393).
  • PR 7 (row 5): maintenance/quiet-hours.ts, handyman-window.ts, temporal/activities/maintenance-comms.ts.
  • PR 8: self-guided-text-leaf.ts, self-guided-leave.ts, self-guided-arrival.ts.
  • PR 9: DEFAULT_HANDYMAN_QUIET_HOURS. PR 10: mass-comms/send-time.ts, mass-sends new page.
  • Not quiet hours: tools-leasing.ts, tour/policy.ts. Separate bug: handle-propose-schedule.ts uses server-local getHours().

This page is a planning aid, not legal advice. A lawyer should confirm the TCPA window and any state rules before they ship.

PropFlow Docs