feat(treatment): add voice detail entry #65
Reference in New Issue
Block a user
Delete Branch "feat/voice-treatment-entry"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Four defects found by review of the preceding commits. "next <weekday>" was occurrence-anchored ("this" plus seven) rather than week- anchored. Said on a Thursday, "Thursday next week" resolved to +14 instead of +7: next week runs Sat 10-18 to Fri 10-24, so its Thursday is 10-23, not 10-30. A lab case a week late. "next" now counts from the start of the following Saturday-start week, which also lets "this" and "next" correctly coincide — said on a Thursday, "the coming Saturday" and "Saturday next week" are the same day. "this" stays occurrence-anchored so it can never resolve into the past. The other three all come from the same root cause: exported functions that are reachable from untrusted model output must degrade, not throw or drop. - a non-object `due` (the model emitting a bare string) was treated as "no deadline spoken" and silently discarded; only null/undefined mean absent now, anything else is flagged so the clinician sees something was heard and lost - isJalaliLeapYear / jalaliDaysInMonth threw for years outside the conversion table, contradicting the module's own "degrade to null" contract; they now return false / 0, which also makes isValidJalaliDate's day check naturally false - civilDateInZone passed a client-supplied zone straight to Intl, which raises RangeError before any fallback; it now validates and backstops to UTC, so a bad zone costs at most a day rather than a 500 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>MediaRecorder handling and the API call live in lib/, not in ui/, so TreatmentDetailsEditor can stay presentational and take only a `voice` prop. Container choice is made at record time and needs no transcode: Chrome and Android give webm/opus, Safari and iPad give mp4/aac, and the transcription endpoint accepts both. Safari's `audio/mp4` is sent as `m4a`, the name the vendor's container list actually uses, so iPad recordings do not fail while Chrome works. Older Safari shipped MediaRecorder without isTypeSupported, so that path lets the browser choose rather than refusing outright. From review of this commit: - The auto-stop at maxMs guaranteed a 413. The client measures the final length after the recorder has stopped, so a recording that runs to the cap always reports slightly over it, and the server rejected exactly the recording the auto-stop existed to save. The server now allows a documented 2s tolerance and the client keeps reporting the true length, so telemetry stays honest. - getUserMedia is async, so a permission granted after unmount installed a live stream the cleanup effect had already run past — leaving the browser's recording indicator lit with nothing listening. Guarded with a mounted ref. - Client-side failures are now ApiError-shaped ({code, statusCode}) rather than bare Errors, because getUserFacingError only resolves that shape; without it errors.VOICE_MIC_DENIED was dead in all three locales. Cancelling aborts the request, which closes the connection and aborts the metered vendor call server-side rather than letting it settle unseen. The level meter is best-effort: a blocked AudioContext costs the meter, not the recording. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>"ترمیم برای دندون دو" set the treatment type but reported the tooth as unreadable. Nothing was misheard: position 2 arrived intact, with no quadrant, because none was spoken — four teeth carry position 2 and the resolver correctly refused to pick one. Only the label was wrong, and it sent the clinician looking for a transcription fault. Adds a tooth_missing_quadrant reason that names what is missing and shows how to say it ("دو بالا راست"), and tells the model explicitly to report a quadrant-less number with arch and side null rather than guessing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>feat/voice-treatment-entryto feat(treatment): add voice detail entryb5560b2f6bto8c16a46740Kheir bebini babam.