Files
dyolink/backend/src/modules/voice/due-date.resolver.ts
Amin Mousavi dc10d8dbe3 docs: cut the comments that were not earning their place
I wrote 731 comment lines on this branch against 4,530 lines of code — 14%,
where the rest of the repo runs at 1.8%. CLAUDE.md asks for code that reads
like its surroundings, and this did not.

Removed by genre rather than by taste:

- restating the code, e.g. "JS getUTCDay() numbering: Sunday = 0" above the
  map that literally shows it, and a docblock on startOfWeek explaining that
  it returns the start of the week;
- narrating history — "this used to rebuild the whole map", "left the bar
  recording forever" — which the commit message and git blame already carry;
- saying the same thing in several places: the "cannot record is not a
  denied microphone" reason appeared three times in one file, and the
  "aborting stops a per-minute metered call" reason across three files. Each
  now lives once, where the behaviour it explains lives;
- defending decisions nobody would question, like why toLatinDigits is its
  own module;
- over-explaining defensive branches, three separate comments to distinguish
  null from missing-kind from unrecognised-kind.

What stays is what the code cannot say: the patient-right convention in
toFdi, whose failure mode is a valid code for the wrong tooth; the
"this"-vs-"next" week anchoring; StrictMode re-arming mountedRef; Safari
accepting no mimeType hint; and the invariants whose violation already cost
a bug — the body parser's middleware ordering and the dispatch panel's
auto-fill rules.

Comments only. The diff contains no non-comment line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 23:14:15 +03:30

235 lines
7.8 KiB
TypeScript

import { jalaliToIsoDate } from '../../common/jalali';
import { civilDateJsWeekday } from '../../common/zoned-civil-time';
import type { DueIntent, UnresolvedItem, Weekday } from './voice.types';
/**
* Spoken deadline → ISO civil date.
*
* The model never does calendar arithmetic; it says what it heard and this decides what
* that means. Jalali conversion in particular is arithmetic here, not inference — an LLM
* asked to turn "۲۵ مهر" into ISO answers confidently and is often wrong, and
* `@IsDateString()` would accept the wrong answer.
*
* Works entirely in civil dates. The caller derives `todayIso` from the actor's IANA zone
* (see `civilDateInZone`) rather than passing a zone in here, so nothing in this file has
* to reason about instants.
*/
const WEEKDAY_TO_JS: Record<Weekday, number> = {
saturday: 6,
sunday: 0,
monday: 1,
tuesday: 2,
wednesday: 3,
thursday: 4,
friday: 5,
};
const MAX_DAYS_AHEAD = 365 * 5;
export type DueResolution = {
dueDate: string | null;
unresolved: UnresolvedItem | null;
};
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
function isRealCivilDate(iso: string): boolean {
if (!ISO_DATE.test(iso)) return false;
const [y, m, d] = iso.split('-').map(Number);
if (m < 1 || m > 12 || d < 1 || d > 31) return false;
const probe = new Date(Date.UTC(y, m - 1, d));
return (
probe.getUTCFullYear() === y &&
probe.getUTCMonth() === m - 1 &&
probe.getUTCDate() === d
);
}
function toIso(utcMs: number): string {
return new Date(utcMs).toISOString().slice(0, 10);
}
function utcMsOf(iso: string): number {
const [y, m, d] = iso.split('-').map(Number);
return Date.UTC(y, m - 1, d);
}
function addDays(iso: string, days: number): string {
return toIso(utcMsOf(iso) + days * 86_400_000);
}
/** Calendar-month addition with end-of-month clamping (31 Jan + 1 month = 28/29 Feb). */
function addMonths(iso: string, months: number): string {
const [y, m, d] = iso.split('-').map(Number);
const targetMonthIndex = m - 1 + months;
const targetYear = y + Math.floor(targetMonthIndex / 12);
const targetMonth = ((targetMonthIndex % 12) + 12) % 12;
const lastDay = new Date(
Date.UTC(targetYear, targetMonth + 1, 0),
).getUTCDate();
return toIso(Date.UTC(targetYear, targetMonth, Math.min(d, lastDay)));
}
function unresolved(spoken: string): DueResolution {
return { dueDate: null, unresolved: { spoken, reason: 'invalid_date' } };
}
/**
* What to quote back when a deadline could not be resolved. Every field is nullable on the
* wire and `toVoiceIntent` casts rather than checks, and the sheet renders this verbatim —
* so a half-classified deadline must fall back to '', not to `"null null"`.
*/
function describe(intent: DueIntent): string {
const usable = (value: unknown): value is number =>
typeof value === 'number' && Number.isFinite(value);
switch (intent?.kind) {
case 'weekday':
// `which` is legitimately null (it means "this"), the weekday is not.
return [intent.which, intent.weekday]
.filter((part) => typeof part === 'string')
.join(' ');
case 'offset':
return usable(intent.amount)
? `+${intent.amount} ${intent.unit ?? ''}`.trim()
: '';
case 'jalali':
return [intent.jy, intent.jm, intent.jd].every(usable)
? `${intent.jy}/${intent.jm}/${intent.jd}`
: '';
case 'gregorian':
return [intent.y, intent.m, intent.d].every(usable)
? `${intent.y}-${intent.m}-${intent.d}`
: '';
default: {
// An unrecognised `kind`, already established as a string — echo what was heard.
const kind = (intent as { kind?: unknown })?.kind;
return typeof kind === 'string' ? kind : '';
}
}
}
/**
* "Next Thursday" is week-relative, so this changes the answer: the Iranian week starts
* Saturday, the Dutch and English week Monday. Hardcoding either puts the other locale's
* deadline a week out.
*/
const WEEK_START_BY_LOCALE: Record<string, number> = {
fa: WEEKDAY_TO_JS.saturday,
en: WEEKDAY_TO_JS.monday,
nl: WEEKDAY_TO_JS.monday,
};
const DEFAULT_WEEK_START = WEEKDAY_TO_JS.monday;
export function weekStartForLocale(locale: string): number {
return WEEK_START_BY_LOCALE[locale] ?? DEFAULT_WEEK_START;
}
function startOfWeek(iso: string, weekStartJs: number): string {
const back = (civilDateJsWeekday(iso) - weekStartJs + 7) % 7;
return addDays(iso, -back);
}
/**
* `'this'` is occurrence-anchored: the soonest occurrence strictly after today, so it can
* never resolve into the past.
*
* `'next'` is *week*-anchored, not "this plus seven" — adding a week to `'this'` overshoots
* by seven days whenever `'this'` has already rolled into next week. The two legitimately
* coincide: said on a Thursday, "the coming Saturday" and "Saturday next week" are one day.
*/
function resolveWeekday(
intent: Extract<DueIntent, { kind: 'weekday' }>,
todayIso: string,
weekStartJs: number,
) {
const targetJs = WEEKDAY_TO_JS[intent.weekday];
if (targetJs === undefined) return null;
// A bare weekday ("پنجشنبه") carries no qualifier, and the model may leave `which`
// null. Treat that as 'this' rather than failing an utterance that named a real day.
const which = intent.which === 'next' ? 'next' : 'this';
if (which === 'this') {
const todayJs = civilDateJsWeekday(todayIso);
let delta = (targetJs - todayJs + 7) % 7;
if (delta === 0) delta = 7;
return addDays(todayIso, delta);
}
if (which === 'next') {
const offsetInWeek = (targetJs - weekStartJs + 7) % 7;
return addDays(startOfWeek(todayIso, weekStartJs), 7 + offsetInWeek);
}
return null;
}
function resolveOffset(
intent: Extract<DueIntent, { kind: 'offset' }>,
todayIso: string,
) {
const { amount, unit } = intent;
if (!Number.isInteger(amount) || amount < 0) return null;
if (unit === 'day')
return amount <= MAX_DAYS_AHEAD ? addDays(todayIso, amount) : null;
if (unit === 'week')
return amount <= 260 ? addDays(todayIso, amount * 7) : null;
if (unit === 'month')
return amount <= 60 ? addMonths(todayIso, amount) : null;
return null;
}
export function resolveDueDate(
intent: DueIntent | null | undefined,
todayIso: string,
weekStartJs: number = DEFAULT_WEEK_START,
): DueResolution {
// Absent is not an error — most utterances carry no deadline.
if (intent === null || intent === undefined) {
return { dueDate: null, unresolved: null };
}
if (typeof intent !== 'object') {
return unresolved(String(intent).slice(0, 120));
}
// No `kind` at all says nothing about a deadline, so it is not "heard but lost". An
// *unrecognised* kind did try to say something, and is flagged below.
if (typeof (intent as { kind?: unknown }).kind !== 'string') {
return { dueDate: null, unresolved: null };
}
if (!isRealCivilDate(todayIso)) {
return unresolved(describe(intent));
}
let resolved: string | null = null;
switch (intent.kind) {
case 'weekday':
resolved = resolveWeekday(intent, todayIso, weekStartJs);
break;
case 'offset':
resolved = resolveOffset(intent, todayIso);
break;
case 'jalali':
resolved = jalaliToIsoDate(intent.jy, intent.jm, intent.jd);
break;
case 'gregorian': {
const candidate = `${String(intent.y).padStart(4, '0')}-${String(intent.m).padStart(2, '0')}-${String(intent.d).padStart(2, '0')}`;
resolved = isRealCivilDate(candidate) ? candidate : null;
break;
}
default:
resolved = null;
}
if (!resolved) return unresolved(describe(intent));
// A date the model invented can land anywhere; past or decades away is not a deadline.
const daysAhead = (utcMsOf(resolved) - utcMsOf(todayIso)) / 86_400_000;
if (daysAhead < 0 || daysAhead > MAX_DAYS_AHEAD)
return unresolved(describe(intent));
return { dueDate: resolved, unresolved: null };
}