Filed from the reference app while slimming src/objects/ comments (objectstack-ai/hotcrm#1184).
The gap
The platform ships no business-hours calendar, no working-day definition and no holiday list. So any authored duration — an SLA clock, a follow-up due date, a renewal notice window — is added to the wall clock and nothing else.
In objectstack-ai/hotcrm that is a 16-cell SLA matrix (priority × account tier) whose numbers an operator naturally reads as support hours:
critical: { strategic: 4, enterprise: 4, mid_market: 4, smb: 4 }
high: { strategic: 6, enterprise: 8, mid_market: 8, smb: 8 }
medium: { strategic: 24, enterprise: 36, mid_market: 48, smb: 48 }
low: { strategic: 96, enterprise: 120, mid_market: 168, smb: 168 }
They are not. A P1 raised at 5pm on a Friday is due at 9pm that same Friday, and a 168-hour Low clock spans two weekends with no acknowledgement that they happened. Every SLA-breach number the app reports — is_sla_violated, the case_sla_monitor sweep, the breach widgets — inherits that.
What the app can do about it, which is nothing
The app cannot fix this at its own layer:
- A hook can compute elapsed milliseconds, but "skip Saturdays and the 25th of December" needs a calendar the deployment owns, and there is no metadata type to author one in.
- The CEL stdlib the app compiles against exposes
today / daysBetween / addDays / addMonths / date — no business-day arithmetic, no month/day accessor. A validation cannot express the rule either. - Authoring an app-level holiday object and a custom working-hours walk would be a second, private calendar implementation with no connection to anything else the platform schedules on.
So the entire mitigation is a warning in prose, repeated at each site that reads the numbers:
⚠️ CALENDAR hours, not business hours. Every number below is added to the wall clock: this app ships no business-hours calendar, no working-day definition and no holiday list […] Stated here rather than hidden because it is the one way these numbers get misread.
It appears in src/objects/_case-sla.ts, in case.hook.ts's matrix, in crm_case.sla_due_date's field note, and in the case hook's module header — four copies of a caveat that exists only because the capability does not.
Why this is a platform ask and not an app one
An SLA in calendar hours is a defensible default, and this is not asking to change it. It is asking that "business hours" be expressible, because every CRM-shaped, service-desk-shaped and contract-shaped app on this platform will need it, and each one will otherwise reinvent a private calendar — or, as here, ship a number that means something other than what its operator reads.
Roughly, the surface that would close it:
- A
business_hours metadata type — named working-hour windows per weekday, a timezone, and a holiday list — authored once and referenced by name. - Duration arithmetic that can consume it, on both surfaces that need it: something a hook can call, and a CEL function (
addBusinessHours(start, n, calendar) / businessHoursBetween(a, b, calendar)) so a validation and a flow condition can agree with the hook. - Optionally a per-object or per-field default calendar, so a
sla_due_date-shaped column does not have to name it at every write site.
Point 2 is the load-bearing half: without a CEL form, the app can compute a correct deadline in a hook and still cannot validate or filter against it, which is where the existing SLA sweep lives.
Refs objectstack-ai/hotcrm#1184.
Filed from the reference app while slimming
src/objects/comments (objectstack-ai/hotcrm#1184).The gap
The platform ships no business-hours calendar, no working-day definition and no holiday list. So any authored duration — an SLA clock, a follow-up due date, a renewal notice window — is added to the wall clock and nothing else.
In
objectstack-ai/hotcrmthat is a 16-cell SLA matrix (priority × account tier) whose numbers an operator naturally reads as support hours:They are not. A P1 raised at 5pm on a Friday is due at 9pm that same Friday, and a 168-hour Low clock spans two weekends with no acknowledgement that they happened. Every SLA-breach number the app reports —
is_sla_violated, thecase_sla_monitorsweep, the breach widgets — inherits that.What the app can do about it, which is nothing
The app cannot fix this at its own layer:
today/daysBetween/addDays/addMonths/date— no business-day arithmetic, no month/day accessor. A validation cannot express the rule either.So the entire mitigation is a warning in prose, repeated at each site that reads the numbers:
It appears in
src/objects/_case-sla.ts, incase.hook.ts's matrix, incrm_case.sla_due_date's field note, and in the case hook's module header — four copies of a caveat that exists only because the capability does not.Why this is a platform ask and not an app one
An SLA in calendar hours is a defensible default, and this is not asking to change it. It is asking that "business hours" be expressible, because every CRM-shaped, service-desk-shaped and contract-shaped app on this platform will need it, and each one will otherwise reinvent a private calendar — or, as here, ship a number that means something other than what its operator reads.
Roughly, the surface that would close it:
business_hoursmetadata type — named working-hour windows per weekday, a timezone, and a holiday list — authored once and referenced by name.addBusinessHours(start, n, calendar)/businessHoursBetween(a, b, calendar)) so a validation and a flow condition can agree with the hook.sla_due_date-shaped column does not have to name it at every write site.Point 2 is the load-bearing half: without a CEL form, the app can compute a correct deadline in a hook and still cannot validate or filter against it, which is where the existing SLA sweep lives.
Refs objectstack-ai/hotcrm#1184.