Working repository for DENTE, a small-clinic dental CRM/MIS intended to grow into a hosted SaaS.
Current status: architecture and product discovery. HECTON-8 rules are intentionally not applied to this product.
Primary document:
docs/00-product-architecture.md- current refusal-risk audit:
docs/04-product-risk-audit.md - speech transcription plan:
docs/05-speech-transcription-plan.md - speech provider research:
docs/06-speech-provider-research.md - competitive voice/CRM audit:
docs/07-competitive-voice-and-crm-audit.md - imaging/DICOM viewer plan:
docs/10-imaging-dicom-viewer-plan.md - document generation forms:
docs/12-document-generation-forms.md - DENTE Telegram bot plan:
docs/13-dente-telegram-bot-plan.md
Prototype persistence:
- mutable demo/MVP state is saved to
apps/api/.data/dental-crm-state.jsonby default; - set
DENTAL_STATE_FILEto move the state file; - set
DENTAL_STATE_PERSISTENCE=offto return to seed-only in-memory behavior; - this is not a replacement for the planned PostgreSQL tenant database, backups, and auth enforcement.
Useful checks:
npm run typechecknpm run buildnpm run smoke:telegram-botnpm run smoke:telegram-control-ui-sourcenpm run smoke:telegram-handoff-sourcenpm run smoke:api-text-encodingnpm run smoke:web-text-encodingnpm run smoke:mobilechecks mobile layouts through headless Edge/Chromium CDP and fails on horizontal overflow; optionalSMOKE_SCREENSHOT_PATH,SMOKE_SELECTOR, andSMOKE_CLICK_SELECTORcapture the exact tested screen.
Implemented prototype surface:
- hash-routed multi-view shell for Shift, Schedule, Patients, Imaging, Visit, Documents, Payments, Communications, and Settings;
- dedicated Payments view for treatment plan totals, payment capture, balances, service catalog, and tax-deduction eligible amounts;
- treatment-plan scenario variants for urgent, standard, optimal, and maintenance strategies, including phases, tradeoffs, and clinical warnings;
- configurable clinical rule engine for required bundles, important warnings, follow-up rules, and doctor/assistant ownership;
- dedicated Communications view for confirmations, payment reminders, post-visit instructions, recalls, message templates, and contact history;
- DENTE Telegram bot server/UI foundation: env-only token configuration, shared-bot settings, clinic-owned bot mode through server-only per-clinic env configuration, scoped multi-clinic status/webhook routing by
organizationId+botConfigId, persisted settings and persisted clinic bot config id for scoped status checks, production webhook-secret enforcement, update idempotency, safe command triage with real non-PHI webhook replies, Russian inline keyboards for safe commands/linking/map/review flows, allowlisted callback handling withanswerCallbackQuery, one-time patient/staff link codes with deep-link/QR SVG/Russian share text, QR-first start/clinic prompts, QR SVG download/copy actions in Settings, paged link-code and chat-link ledgers with status counters, one active chat binding per subject after relink, redacted chat fingerprints, encrypted server-only chat transport refs, persisted chat bindings/events, settings-tab control plane, Russian safe message previews that block PHI by default, visual-card previews, safe outbound readiness outbox with blocked reasons and item warnings, scheduled-send guard, audited single-item TelegramsendPhotodelivery with text fallback from encrypted server-only chat refs, readable Russian validation errors for malformed Telegram payloads with no secret/mojibake leakage, andnpm run smoke:telegram-bot/npm run smoke:telegram-control-ui-source/npm run smoke:telegram-validation; - Telegram visual cards are now configured per scenario (
mainMenu,appointment,documents,tax,billing,care,review,staff) in first-run onboarding and Settings; patient menus, document/tax/payment/care/review outbox previews and sent messages pick the matching HTTPS card, fall back towelcomeImageUrl, then fall back to text, while keeping the same Russian inline buttons andГлавное менюescape route; - Telegram review requests,
/reviewreplies and map callbacks now use only clinic-level HTTPS review/maps URLs saved in server settings; they send the configured visual clinic card when available, are blocked until a safe URL is configured, and never include patient, appointment, diagnosis, tooth, treatment, payment, or tax identifiers; - Telegram document submenu buttons and clear patient free-text requests for tax documents, medical records and patient forms now create or reuse administrator tasks for linked patients, store stable workflow codes on those tasks, record inbound Telegram communication events, keep repeated presses/messages deduplicated, and still keep generated PDFs/PHI in DENTE or the protected portal instead of Telegram;
- communication task cards for Telegram document requests now use stable workflow codes instead of localized titles, expose Russian quick actions for the real follow-up forms: tax application/current certificate/legacy certificate/register, medical copy request/extract/release receipt/visit certificate, and intake/personal-data/treatment/procedure/minor/photo-video consents; they preselect the DENTE document workflow and selected patient without auto-issuing a legal PDF from the queue;
- Telegram private-chat free text and unsafe attachments are no longer silent: Russian words like
документы,налог,медкарта,анкета,согласие,памятка,расписание,нужен звонок,отзыв, andкартаroute to inline-button flows; linked patients with specific document phrases create the same DENTE administrator handoff tasks as buttons, while photo/PDF/voice updates get a safe explanation with document/care/admin buttons and no echoedfile_id; - generated document drafts now require a visible Russian review step before issuing: the operator sees the patient, amount, tax year/INN when relevant, opens the HTML draft, then presses
Выдать после проверкиinstead of changing the legal status from a single row click; - document generation now includes a dedicated structured outpatient medical card 025/у (
Медицинская карта пациента, получающего медицинскую помощь в амбулаторных условиях) anchored to Health Ministry Order N 274n; it is assembled from signed visit sources, patient/clinic administrative facts and dental clinical rows, recovers the doctor-facing draft locally per patient/visit/form until changed, blocks issue on unsigned or out-of-period source facts, and stays explicit that DENTE HTML/PDF is not ЕГИСЗ/УКЭП electronic exchange; - Telegram schedule replies for linked patients now include signed inline buttons for the nearest appointment (
Подтвердить,Перенести,Позвоните мне) while keeping the schedule text administrative-only; appointment callback signatures are scoped byorganizationId + clinicId + botConfigId + appointmentId + action + expiry, contain no patient/diagnosis/payment facts, stay within Telegramcallback_datalimits, reject cross-bot replay, and handled callbacks create stable reschedule/call workflow tasks with a safe next-action keyboard (Расписание,Документы,Позвать администратора,Конфиденциальность) instead of a dead end; - Telegram care-topic buttons for extraction, implant, filling and hygiene now create or reuse doctor-owned post-visit instruction tasks with stable care workflow codes when no personalized DENTE recommendation has been issued yet; the bot smoke verifies all four callback paths, Communications shows
Подготовить памятку, opens the post-visit recommendations form, applies the matching care preset, and the bot still sends only generic safe text until DENTE/portal has a real issued recommendation; - Telegram outbox API now returns the exact inline keyboard markup that will be sent, exposes server-side
status/templateKindfilters, count-before-page totals and cursor pagination, and the Settings UI previews Telegram-style rows with Russian labels plusссылка/действиеmarkers instead of flattening buttons into ambiguous text chips; - Telegram outbox list, single-item send, manual
send-due, link-code creation/ledgers, chat-link ledgers and appointment callback verification now carry the selected clinic-owned runtime scope withorganizationId+clinicId+botConfigId; link-code creation validates the subject and clinic against that resolved runtime, document/care/contact callbacks write tasks, communication events and audit into the linked chat's organization, the Settings UI appends that scope automatically for saved clinic bot configs, and the bot smoke proves a scoped code and a signed appointment callback cannot be reused through a different bot config while second-clinic callbacks and scoped outbox use the selected bot token, portal URL and visual card instead of the shared DENTE bot runtime; - Telegram document-ready, tax-status, payment, post-visit and recall outbox messages now use the configured visual card plus button-first Russian keyboards (
Открыть DENTE,Документы/Налоговая,Памятки/Расписание,Связаться,Конфиденциальность); portal buttons add only safedente_source=telegramanddente_section=home|documents|tax|billing|care|schedulehandoff parameters, while the text remains generic and does not include diagnosis, fiscal receipt, amount, payer INN, PDF or medical document contents; - the web shell consumes Telegram portal handoff query parameters on first load, opens the matching DENTE section (
Главная,Документы,Налоговая,Оплаты,Памятки,Расписание), preselects the relevant document/tax form when needed, shows a Russian handoff notice, and strips the query from the URL so browser history does not retain patient/document/appointment/payment identifiers or stale Telegram state; - Telegram link-code and chat-link APIs now expose server-side
status/subjectType/subjectIdfilters,organizationId+botConfigIdscope, status counters and cursor pagination; Settings uses those counts andПоказать ещеinstead of hiding older pending codes or revoked chat bindings behind hardcoded local slices; the selected QR target mode and staff member are saved as UI preferences until the operator changes them; - document issue confirmation now requires explicit signature attestation before status change: signature mode, signing time, recipient/staff facts, identity check, opened-document check, recipient signature, and clinic representative signature. The browser saves reusable signature mode/staff defaults through UI preferences until the operator changes them, with the old local store kept only as migration fallback;
- document void/correction is now an attested workflow, not a generic status flip:
/api/documents/:id/voidrequires a structured reason code/text, staff name/role, archive/status confirmations, patient/payer notification flag and optional correction document link; the Documents UI collects this before voiding and audit facts expose the attestation; - Telegram outbox direct-send lookup is not capped to the first page: an item visible through pagination can still be sent or return its real blocking reason by id instead of a false 404;
- Telegram due reminders can be sent by a bounded in-process worker controlled by env (
DENTE_TELEGRAM_OUTBOX_WORKER_ENABLED, interval, batch limit, dry-run and run-on-start); the manual/api/telegram/outbox/send-dueroute remains for admin/ops runs, and failed due receipts can be retried instead of becoming permanent idempotent replays; - Server-side UI preference loading normalizes older partial preference blobs with safe defaults instead of failing
/api/settings/preferences, so saved role, schedule, import, imaging, Telegram, and document defaults remain durable across schema growth. - shift dashboard with patient cockpit, dictation draft, tooth chart, document checklist;
- Settings sub-sections for clinic account, specialty protocols, data sources, AI, imports, and audit instead of one overloaded admin page;
- first-run clinic setup panel guides a new doctor/clinic through role, specialty, clinic mode, legal profile, staff role/specialty, chairs, working hours for doctors/assistants/chairs, Telegram, and imports; the import step now edits persisted price-list, patient migration, smart import, document-ingestion, imaging, DICOMweb, and OHIF defaults inline instead of only linking to Settings; it auto-saves dirty clinic profile and schedule edits, flushes them before step navigation/mode switch/dismissal, blocks completion when the active first appointment has team/schedule readiness blockers, saves completion state with server UI preferences, keeps a legacy local fallback, and can be reopened from the header or Settings without blocking daily work;
- UI preferences keep interface language fallback, selected role focus, visit specialty, selected patient, schedule filters, default doctor/assistant/chair for new appointments, payment method, tax document year for KND 1151156 2024+ and legacy 2021-2023 certificate drafts, import modes, price-list AI toggle, imaging filter, local DICOMweb/OHIF URLs, Telegram QR target selection, and selected clinic Telegram bot config id across refresh under
dental-crm:web-ui-preferences:v1and mirror to/api/settings/preferencesafter server hydration; this preference blob is validated on load and does not store patient text, amounts, audio, images, DICOM pixels, fiscal receipt data, payer identity, raw Telegram codes, or chat ids; - the web shell conditionally mounts top-level work surfaces by route instead of rendering shift, patient cockpit, imaging, documents, finance, communications, compliance, and settings as hidden DOM at the same time; Settings tabs also mount only the active clinic/access/Telegram/protocols/rules/prices/sources/AI/imports/audit section instead of keeping every admin tool hidden in the DOM; the Documents workspace mounts only the selected structured payload editor instead of keeping all legal/tax/patient form cards hidden in the DOM;
npm run smoke:web-render-gating-sourceandnpm run smoke:document-payload-ui-sourceprevent regressions; /api/settings/clinic/profilesaves the clinic legal/contact/license profile used by generated contracts, acts, payment paperwork, and tax-document drafts;- patient administrative profiles now persist identity document, patient taxpayer INN, registration/residential address, insurance/SNILS notes, legal representative facts, and preferred document recipient; generated consent/copy-request/release paperwork uses those facts instead of forcing the doctor/admin to retype them, and patient-as-payer payments can reuse the saved INN/document details for tax paperwork;
- clinic and staff schedule settings now persist workday start/end, working days, appointment buffer, and per-staff working hours, including different hours per weekday for a doctor/assistant; appointments carry an assigned assistant, accept only real ISO datetimes with explicit timezone, readiness checks clinic/doctor/assistant windows, and schedule suggestions surface buffer violations plus doctor/assistant/chair overload from configured capacity instead of hardcoded day length;
- file-backed settings persistence now also proves doctor, assistant, chair, and patient preferred appointment-window schedule configuration survives a fresh API module load;
- Settings rule studio for clinic-specific clinical bundles, important warnings, follow-up rules, severity, owner role, and patient-facing explanation;
- patient import preview and safe commit;
- Settings-only import studio for CSV, Excel-copy, old MIS export, OCR text from journal photos, voice dictation, and free text;
- smart mixed-export parser that separates patient rows, imaging rows, DICOM/IMA/TRG/CBCT paths, and ignored noise before safe commit;
/api/ingestion/extractprovides a built-in document/table extractor for ZIP archives, TXT, CSV, TSV, JSON, XML, HTML, RTF, PDF best-effort, DOCX, XLSX, PPTX, and image placeholders before routing text or uploaded images into smart import, patient import, imaging manifests, or price-list analysis;- ZIP ingestion is read-only and extracts supported nested documents/tables while listing binary DICOM/image entries as manifest references, so archived CBCT/RVG/OPG exports can go to imaging preview instead of becoming binary text garbage;
- source and ZIP entry file names are redacted in extraction responses to stable safe labels (
Файл архива #...,Архив #...) before preview text, warnings, or extracted file lists reach the browser; - document ingestion returns a safety quality block (
ready,review,ocr_required,unsupported) with confidence, suggested target, detected signals, and next action before any preview/commit step; - CSV diagnostic report for smart imports with classification, confidence, preview status, and warnings;
- dedicated Imaging page for periapical/RVG, bitewing, OPG, TRG/ceph, CBCT, photos, and future PACS/DICOM connectors, so the Shift screen keeps only counts and a fast entry point;
- built-in 2D X-ray viewer controls for rotate, flip, invert, brightness, contrast, zoom, and reset without loading a heavy imaging workstation in the default doctor flow;
- the patient imaging viewer adds type filters and a compact typed viewer plan per study kind: RVG/Bitewing/OPG/TRG/CBCT/photo tools, presets, warnings, and next action. CBCT is explicitly marked as MPR/DICOMweb workbench material, not a diagnostic single-image view;
- the imaging viewer keeps a local-first and server-saved viewer session per study: brightness/contrast/invert/rotate/zoom, MPR projection/window/slab/crosshair state, and doctor note annotations survive refresh without modifying source pixels; the viewer now shows local/server save timestamps, keeps failed saves queued locally, and exposes a compact retry action;
- imaging import preview/commit for CSV/TSV/manifest text with patient matching, file paths, source detection, and row statuses;
- read-only watched-folder scan preview for server-side DICOM/IMA/JPG/PNG/TIFF/BMP/WebP image folders;
/api/imaging/dicom/folder-series-previewreads local DICOM/IMA headers and ZIP-contained DICOM entries read-only from a server folder, extracts Study/Series/SOP UID, modality, descriptions, instance number, date, and patient name, then builds the same series/MPR readiness preview without loading or storing pixel data;/api/imaging/dicom/first-frame-previewadds a bounded Settings-only orientation preview for direct uncompressed little-endian MONOCHROME DICOM/IMA files. It skips blank readable candidates, uses sampled min/max windowing when source WL/WW makes a thumbnail unusable, and returns a transient PNG data URL, image geometry, windowing facts, and warnings without patient name, raw local path, persistence, or diagnostic claims; the UI now adds transient rotate/mirror/invert/zoom/brightness/contrast controls for orientation only, while compressed/encapsulated DICOM stays on the external/OHIF/Cornerstone path;/api/imaging/dicom/series-previewgroups DICOM/IMA/CBCT manifests by Study/Series UID or folder fallback, reports file counts, patient matches, modality, recommended viewer path, and warnings without storing pixel data;/api/imaging/dicomweb/checksafely probes a DICOMweb root through QIDO-RS with server-side auth boundaries, timeout handling, status/warning output, and no PACS secret exposure to the browser;/api/imaging/dicom/viewer-launch-manifestbuilds a non-mutating OHIF/Cornerstone/external-viewer handoff manifest from the selected DICOM series, current viewer state, annotations, resource policy, and Study/Series UID;/api/imaging/dicom/viewer-tool-stateexports a pixel-free Cornerstone/OHIF tool-state bundle from the saved CRM viewer state, MPR controls, and annotations, so external or embedded viewers can restore markup without losing doctor work;/api/imaging/dicom/workstation-readinesscompares a selected CBCT/DICOM series resource policy with the current browser workstation facts (WebGL2, IndexedDB, memory/cores, storage, online state) and returns in-browser/downsample/external-viewer guidance without blocking the doctor;/api/imaging/dicom/render-cache-planturns the GPU render plan into a concrete first-slice/window/cache task list: worker count, decode/upload concurrency, first paint budget, GPU/CPU memory budgets, IndexedDB cache use, and external-viewer fallback;/api/imaging/dicom/viewer-workbench-manifestcombines workstation readiness, render-cache plan, viewer launch manifest, and tool-state bundle into one CT/MPR workbench contract so admins get one safe action instead of five technical buttons;- Settings -> Sources stores the latest CT/MPR workbench bundle in browser local recovery and can export it as JSON; the bundle contains metadata/state only, never DICOM pixels;
/api/imaging/dicom/workbench-bundlesalso saves the latest CT/MPR workbench bundle server-side in the persisted state with checksum/backups and redacted local file paths, including nested warning strings, so another workstation can recover launch/tool-state/cache intent while raw DICOM pixels remain in PACS/local folders/devices;- Settings -> Sources can reconnect a server-restored CT/MPR bundle to the current PC folder: run
Найти DICOMor paste a folder path, thenПереподключить папкуrebuilds the local workbench with real local references and saves a redacted server copy again; - Settings -> Sources now has one-click
Подготовить CTon local DICOM discovery/organizer candidates. It runs folder workup, chooses the safest series path, creates the CT/MPR workbench manifest, stores browser recovery, and attempts redacted server persistence without making the admin press five separate technical buttons; - Settings -> Sources and Settings -> Imports also have a
Первый срезaction for local DICOM folders/candidates. It is an orientation check only: direct uncompressed slices can render as a transient preview, while compressed CT is reported as unsupported instead of blocking the doctor or faking diagnosis; /api/imaging/dicom/folder-workup-plancombines read-only folder header scan, series grouping, workstation readiness, and render-cache planning into one admin action for CBCT/DICOM folders; it never loads pixels and always returns warning-only doctor behavior;/api/imaging/dicom/local-folder-discoverysearches configured roots plus Downloads/Desktop/Documents/Pictures/OneDrive export folders for likely local CT/DICOM folders using bounded filename/header checks only; Settings shows PHI-safe local aliases, source labels, and fingerprints until the admin explicitly selects a folder;/api/imaging/local-organizer/scan-previewexpands local discovery into a metadata-only CT/DICOM + dental 3D organizer: it finds DICOM folders, ZIP/7z/RAR references, STL/OBJ/PLY/GLB/GLTF/3MF model files, classifies upper/lower/bite/crown/bridge/implant-guide/aligner/scan-body roles, and groups useful case candidates without storing pixels or meshes; organizer cards also use safe aliases/fingerprints instead of auto-exposing patient-like folder names;- Settings -> Sources keeps the last selected local DICOM/CT folder path in browser-local recovery only, shows a safe alias/source/fingerprint strip after refresh, and never sends that raw local path to server persistence unless the admin explicitly runs a local folder action;
- Settings -> Sources now also has a browser-only
Pick local CTpath:showDirectoryPickerwhen available, with a file-input directory fallback. It summarizes selected local DICOM/archive/3D/image files from metadata and DICOM magic bytes only, stores a PHI-safe local summary, and still routes pixel viewing/workup to the local worker, DICOMweb, OHIF, or Cornerstone boundary. The fallback has a dedicated synthetic non-PHI smoke (npm run smoke:browser-file-input-dicom) that verifies no-extensionDICMdetection and raw path/name redaction; - DICOM/CBCT folder workup now has a repeatable synthetic no-PHI API smoke (
npm run smoke:dicom-folder-workup) that generates a local CT stack, verifies first-frame orientation preview, MPR/panoramic workup, high-end GPU cache planning, low-power external handoff, and server-side pixel-free bundle path redaction; - workstation readiness now includes a GPU render plan: GPU class, texture strategy (
single_3d_texture,bricked_3d_textures,stack_2d_textures, or external viewer), quality mode, downsample factor, slice batch size, estimated GPU memory, Web Worker/OffscreenCanvas use, and first-paint strategy; /api/system/local-bridges/readinesssafely checks optional local workstation modules for Whisper.cpp, Vosk, CT/DICOM worker, OCR worker, and external viewer without sending clinical payloads or exposing secrets; Settings -> Audit shows the result as admin diagnostics, not as doctor-facing noise;/api/system/local-bridges/use-plansturns local module readiness, server recognition availability, and built-in parser capability into scenario plans for dictation, OCR, price-list photos, CT/MPR, and imaging import; every plan is warning-only for the doctor and preserves local-first recovery;- DICOM series preview now recognizes ZIP/7z/RAR archive paths and expands ZIP central directories into virtual DICOM entries for read-only readiness checks; 7z/RAR are flagged for external extraction instead of silently pretending to load them;
- CBCT/CT previews return an explicit
mprReadinesscontract with 3-plane MPR, oblique axes, panoramic reconstruction eligibility, window/level, crosshair, measurement, export, blocker, and next-action metadata; - DICOM/CBCT readiness also returns a resource policy: required workstation tier, load strategy, estimated browser memory, max client slice cap, cache mode, thumbnail-first rule, and safety caps so weak PCs get preview/handoff while strong workstations can open full MPR;
- clinical MPR presets now separate exact protocol fit from safe fallback planes: unavailable panoramic/oblique routes can fall back to an available plane for navigation, but the UI does not claim an exact clinical protocol or offer a no-op fit action;
- the Imaging page keeps CBCT advanced controls collapsed by default: doctors see quick status and external viewer handoff first, while axial/coronal/sagittal/oblique/panoramic projection choice, axis angle, slab thickness, window preset, crosshair, and linked-plane intent stay available on demand; Settings -> Sources keeps the admin readiness panel, DICOM import checks, DICOMweb connector check, and OHIF launch manifest builder;
- clinic account settings for solo doctor, one-chair office, small clinic, and network clinic modes;
- workspace profiles and role access policies for solo doctor, one-chair office, small clinic, network/branch operation, doctors, admins, assistants, managers, and owners;
- role-focus switcher in the working shift so the same screen highlights the doctor, administrator, assistant, manager, or owner queue without hiding the rest of the product;
- patient insight cards that compress clinical risk, open communication tasks, missing documents, recall, and balance into one readable patient context layer;
- role-aware recommended actions that replace static shift tiles with the next best doctor/admin/assistant/manager/owner actions;
- appointment readiness scoring in the schedule so each visit shows the responsible role, score, and minimal prep checks;
- compact schedule suggestions that surface attention-needed visits, gaps, and overload without adding a dispatcher page;
- typed visit close checklist that combines EMR completeness, clinical warnings, image review, legal documents, payment, AI draft review, and post-visit instructions into one compact role-owned closure block;
- visit focus bar for tired-doctor usability: patient, visit reason, warning count, readiness score, image/document counts, and dictation as the primary action before optional protocol details;
- visit specialty focus is filtered to the current doctor, chair, appointment reason, and selected specialty, while the full specialty catalog stays inside the collapsed protocol library;
- quick dictation phrase chips for common clinical note fragments so a doctor can assemble the visit note without hunting through templates;
- optional browser voice button on Visit appends recognized speech into the same autosaved dictation field, with typing still available when speech recognition is unsupported;
/api/speech/status,/api/speech/transcribe-chunk,/api/speech/chunks, and/api/speech/recordings/:recordingId/assembleform a server-key-only STT gateway for chunked voice upload, provider fallback, saved transcript chunks, recording recovery, and IndexedDB local retry queue support;- server-side recording assembly rebuilds the transcript from saved chunks, reports missing chunk indexes, and prevents duplicate chunk text from being appended twice in the active Visit screen;
/api/speech/statusnow auto-selects a configured STT provider, exposes fallback chain/setup hints and safe key-pool counts to Settings, separatesserverTranscriptionEnabledfromserverTranscriptionCurrentlyAvailable, and keeps the doctor-facing Visit screen to one server STT action;/api/speech/statusexposes a server-only dental STT prompt policy for Groq/OpenAI: prompt version, enabled state, term count, max length, and warnings without exposing provider keys or making the doctor choose a vendor;/api/speech/gateway-healthgives Settings a safe readiness report for STT providers: active/fallback providers, available/configured/cooling-down key counts, timeout/retry policy, prompt/offline-parser status, and redacted per-key health fingerprints without exposing secret values;/api/speech/providers/runtimegives Settings a safe provider matrix: wired/catalog/local status, key counts, missing env names, retry/timeout policy, and next setup step without exposing secrets;- local Whisper.cpp/Vosk bridges can be selected through
DENTAL_SPEECH_PROVIDER=local|vosk;/api/speech/transcribe-chunkforwards audio only to configured localhost/private LAN endpoints and does not require cloud STT keys; - local Whisper.cpp/Vosk status now separates configured URL from current availability: a cached
/healthprobe must succeed before queued audio chunks are flushed to the local bridge; /api/speech/recording-strategyreturns the current capture plan for online/offline/local-only/long-recording cases, so the client can keep recording recoverable without showing vendor complexity to the doctor;/api/speech/chunksonly returns chunks for an explicitrecordingId; it also accepts activepatientId/visitIdscope and no longer exposes a global transcript list;/api/speech/recordings/recoveryindexes saved server chunks by recording, reports missing indexes, failed chunks, transcript preview, and next recovery action after refresh/offline retry; recovery and server-side assembly now require activevisitIdorpatientIdscope, so a global PHI recording list is not exposed;- every saved STT chunk now carries quality metadata (
clear,review,empty,failed), confidence when the provider exposes it, word/character counts, duration/byte signals, provider warnings, and the next safe action; recovery treats questionable chunks as review-needed instead of silently clean; - retrying the same
recordingId + chunkIndexcan improve a saved server STT chunk (failed/needs_provider_key->fallback_text/transcribed) while worse retries cannot overwrite already usable text; a whole recording is now identity-locked to the same visit/patient/source/language, so mismatched chunks are rejected with409instead of mixing another patient or replacing another chunk; - STT keys can be pooled via
GROQ_API_KEYS,GROQ_API_KEY_1..N,OPENAI_API_KEYS,DEEPGRAM_API_KEYS,ASSEMBLYAI_API_KEYS, orCLOUDFLARE_API_TOKENS; the API randomly rotates available keys, cools down keys after 429/auth/timeout/network errors, and never returns key values to the client; - STT key health persists only provider fingerprints, cooldowns, and counters in
.data/speech-key-health.jsonby default, so restarting the API does not immediately reuse a known bad or exhausted key; - API startup can import server-only speech keys from external
.envfiles viaDENTAL_SPEECH_ENV_FILEorDENTAL_EXTRA_ENV_FILES; the loader follows external paths declared inside.env.localitself, and multi-key env lists are merged/deduplicated across files without copying old project secrets into this repo; - Groq/OpenAI transcription requests receive a short dental terminology prompt pack covering FDI notation, imaging terms, prosthetic/implant/hygiene vocabulary, scanner/material names, and the rule to transcribe verbatim without adding clinical facts;
- Groq STT chunks are clamped to a 10-second floor even if env is misconfigured lower;
npm run smoke:speech-groq-chunk-floorverifies the strategy with a synthetic key and does not call Groq; - STT key rotation has a no-network regression smoke (
npm run smoke:speech-key-rotation) that uses synthetic Groq keys, forces 429/timeout cooldowns, verifies retry selection, and reads the persisted health file to prove raw keys are not stored; - smart voice recording uses server-provided chunking policy and browser Web Audio RMS monitoring to cut on silence/max duration, while MediaRecorder also runs with a hard timeslice so background tab throttling cannot create one huge blob;
- server voice recording is local-first: every audio chunk is appended to the local IndexedDB queue before upload, then deleted by id only after a successful server transcription response;
- server voice recording is no longer blocked by missing STT keys, all keys cooling down, or offline mode: audio chunks stay in the local IndexedDB queue and are not flushed/deleted until
/api/speech/statusreports a currently available server/local transcription path; - destructive transcript clear has an immediate undo path, so an accidental tap does not make the local draft unrecoverable in the current session;
- adjacent STT chunks are merged with tail deduplication and real chunk duration metadata, reducing repeated words at boundaries while keeping recovery/audit useful;
/api/speech/polish-transcriptand the VisitОчистить STTaction normalize noisy dictation with deterministic dental rules before drafting EMR, without adding clinical facts;- offline deterministic speech cleanup now covers common prosthetic, implant, hygiene, scanner, and material vocabulary such as E.max, zirconia, metal-ceramic, PMMA, veneers, inlay/onlay/overlay, endocrown, abutment, sinus-lift, bone grafting, iTero, Medit, and 3Shape;
- rule-based visit drafts now carry a doctor-facing quality gate with confidence, detected teeth, specialty focus, parser signals, missing critical fields, and the next safe action; it is a warning layer, not a blocker;
- the Visit screen has a compact safety strip for local autosave, server draft sync, browser/device readiness, queued STT audio, and recovery state so a doctor can see data protection without opening Settings;
- optional server-only neural polish can run after deterministic speech cleanup through a configured server endpoint, disabled by default and guarded against added tooth/diagnosis facts;
- OpenAI/Groq neural polish now uses the same server-only key-pool safety as STT: random available-key selection, retry limit, cooldown after 429/auth/timeout/5xx, sanitized provider errors, and fingerprint-only key health persistence;
/api/pricelist/analyzeparses copied tables, OCR text, and photo-OCR text into validated dental price-list JSON: service category, specialty, material, crown/restoration type, brand, unit, price, warnings, and service mapping candidate;- Settings -> Прайс adds an admin-only preview and collapsed recognition catalog for treatment kinds, materials, crown/restoration types, implant systems, grafts/membranes, anesthetics, lab/imaging markers, and common brands, with optional server image/text extraction, browser-side photo compression, and deterministic fallback;
- visit dictation and structured EMR fields autosave locally in the browser per active visit and restore only when the local draft is newer than the server visit;
/api/visits/:visitId/draft/autosavestores a server-side draft snapshot before EMR acceptance, so typed/STT text can be recovered after refresh or another workstation without signing the medical record;- accepting/saving a reviewed visit note now has a browser queue: if the API is unavailable, the note stays local and retries server sync later;
- accepted visit-note saves carry client mutation ids, base/server revisions, and save receipts, so retry sync is duplicate-safe and conflict warnings stay non-blocking;
- server dictation draft and browser offline fallback use the same shared deterministic specialty-aware rule parser for therapy, prosthetics, surgery, orthodontics, periodontology, hygiene/prevention, pediatric dentistry, implantology, radiology, and general exams;
- editable structured EMR draft panel directly under dictation, with a real save/accept action that writes the doctor-reviewed version into the active visit and records audit;
- compact clinical warning disclosure on Visit, with full clinical rule explanation available on demand instead of occupying the default work surface;
- staff roles, explicit doctor/assistant specialty selection, chair/room inventory, and role-derived access flags in Settings;
- shift intelligence layer that calculates mode readiness, doctor/assistant/chair load, role queues, and schedule warnings from the same dashboard contract;
- migration/integration preset catalog for 32top, IDENT, Cliniccards, Open Dental, spreadsheets, OCR paper archive, RVG/OPG/TRG/CBCT folders, PACS/архивы снимков, and accounting/tax flows;
- AI recognition job pipeline for voice dictation, paper-journal OCR, image summaries, document drafts, confidence, warnings, and explicit next steps;
- Settings -> AI speech provider catalog for browser dictation, Groq Whisper, OpenAI transcription, Deepgram, AssemblyAI, Cloudflare Workers AI, Whisper.cpp, and Vosk, with server-only key boundaries;
- Settings -> AI now also tracks Azure AI Speech, Google Cloud Speech-to-Text, Hugging Face ASR, and future native mobile speech as admin/provider choices, not doctor-facing treatment controls;
- STT provider cards include cost/free-tier notes so admins can choose a pilot without exposing vendor setup to the doctor;
- specialty protocol template library for general exams, therapy, prosthetics, surgery, orthodontics, periodontology, hygiene, pediatric dentistry, implantology, and radiology;
- import batch metrics and audit events;
- quick creation of contract, completed works act, tax deduction certificate, consent, and treatment plan;
- document generation covers the core clinic form set: intake questionnaire, personal-data consent, informed consent, procedure-specific consent packet, legal representative/minor consent, photo/video consent, refusal, treatment plan, treatment-plan acceptance, anesthesia consent/log, medication instructions, lab work order, cost estimate, installment schedule, paid-services contract, invoice/payment memo, completed works act, refund/correction request, recommendations, visit attendance certificate, warranty memo, medical-record extract/copy request/release receipt, X-ray/CBCT referral, tax deduction application, KND 1151156 data draft for 2024+, legacy pre-2024 tax certificate draft for 2021-2023, and tax payment registry;
- issued documents store structured signature attestation with the immutable HTML snapshot; medical copy requests, medical-record extracts, and medical-document release receipts also stamp a release journal entry with source snapshot SHA-256, release receipts link back to the issued copy-request snapshot when available, KND 1151156 issue freezes patient/clinic/payment facts for XML, unparseable or reversed medical-chain dates block issue, broken legacy mojibake text is repaired before document payloads/signature/void facts are stored, and PDF/tax XML export is blocked until signature attestation exists;
- issued documents now store immutable local HTML snapshots with SHA-256 metadata; opening issued or later-voided issued HTML verifies the stored snapshot and returns an integrity error instead of re-rendering mutable patient/profile data on snapshot loss, missing hash metadata, or hash mismatch; API responses expose snapshot hash/date/issuer but not the server filesystem path;
/api/documents/:id/audit-factsreturns the operator passport for source authority, official source URLs, archive status, blockers and warnings, tax XML source/XML snapshot hashes when present,html?download=1downloads the verified archived HTML as an attachment, and/api/documents/:id/pdfprints that same verified archive through server-side Chromium/Edge into a real PDF when the deployment has a configured browser, with a boundedDENTE_PDF_EXPORT_TIMEOUT_MSwait for slower servers; - the first successful
/api/documents/:id/tax-xmlexport for an issued KND 1151156 certificate runs an internal DENTE structural preflight, then persists the exact draft XML bytes, SHA-256, source-snapshot hash and tax-office code; later downloads return that archived draft XML instead of re-reading edited patient, clinic, payment or environment state. The issue passport marks it asexternal_validation_required: official XSD validation, KEP signing and EDO/TKS submission stay outside DENTE until that contour is implemented; - payment capture stores fiscal receipt date/number, payer name/INN/birth date/identity document/relationship, and explicit tax deduction service code
1/2; the API no longer invents a default tax code when staff omit it. Issued payment receipts, refund/correction requests, KND/tax registry documents, and legacy tax certificate drafts require fiscal receipt numbers/dates plus explicit payer identity facts, render fiscal-date selected-year rows with clinic license data, render separate taxpayer/patient facts with the KND same-person flag, reject unsupported payer relationships, can be scoped to one payer INN, exclude payments from other tax years or other taxpayers, block KND 1151156 for pre-2024 years, block the legacy certificate for 2024+ years, and reject duplicate tax-certificate issue for the same patient/year/form/taxpayer even when staff select a later new fiscal receipt from that taxpayer; - a same-year tax certificate replacement is allowed only after the previous certificate is voided through the structured tax-correction attestation; the replacement smoke proves the old issued record stays archived/voided and the new certificate issues from a fresh explicit payment scope;
- typed service catalog, treatment plan items, payment ledger, billing summary, and
/api/billing/paymentsendpoint; document-linked payments inherit the document visit when needed and zero-ruble paid records are rejected at input validation; - typed treatment plan scenarios inspired by the archived
stomplanbudget/versioning concept; - typed clinical rules, clinical evaluations, clinical summaries, and
/api/clinical/rules/evaluateendpoint; /api/clinical/rulescreate endpoint and/api/clinical/rules/:ruleIdupdate endpoint for clinic rule customization;/api/visits/:visitId/draft/acceptendpoint for accepting a reviewed dictation/AI draft into the active EMR;- typed communication templates, communication tasks, communication events, communication summary, and
/api/communications/tasks/completeendpoint; - generated HTML drafts for documents.
- patient intake questionnaire, personal-data processing consent, tax deduction application, informed consent, procedure-specific consent packet, anesthesia consent/log, medication order, lab work order, photo/video/xray consent, X-ray/CBCT referral, medical-document release receipt, and refund/correction request support structured issue payloads; the Documents UI validates and sends those payloads at creation time, the renderer prints captured facts into HTML, and creation/issue are blocked when payload is missing;
npm run smoke:document-payloadsandnpm run smoke:document-payload-ui-sourceverify this slice, including conditional mounting for the selected payload editor. Route-level lifecycle smokes now prove create/issue/audit/immutable HTML for patient forms (npm run smoke:patient-forms-lifecycle) and visit/workflow forms (npm run smoke:visit-workflow-forms-lifecycle: informed consent, procedure-specific consent packet, anesthesia log, medication order, lab order, X-ray/CBCT referral, attendance certificate, warranty memo, intervention refusal, refund/correction request). - every
DocumentKindhas a rendered HTML draft and a synthetic no-PHI regression (npm run smoke:documents-catalog) that fails on missing templates, missing patient identity, missing clinic legal issue guards, missing official source URLs for official-form/workflow records, missing KND/consent/refusal control fragments,undefined, or script output. The FNS KND 1151156 order attachments are pinned separately indocs/legal-sources/fns-knd-1151156.json, andnpm run smoke:official-document-sourcesverifies appendices 1-4, XSD 5.01 URL/filename/bytes/SHA-256, shared metadata links, and docs wiring without claiming official XSD submission readiness. npm run smoke:tax-knd-xmlproves KND 1151156 XML source facts are frozen at document issue, the internal structural preflight is present, the first successful XML draft is archived byte-for-byte, the audit passport keeps the external XSD/KEP/EDO boundary visible, and later changes to patient/clinic/payment/tax-office state do not alter that XML.- document creation guardrails (
npm run smoke:document-guards) block patient/visit mismatches, reject visit-required forms without a visit, replace browser-supplied planned totals with server-side patient/visit treatment-plan facts, and prevent KND certificate/registry drafts from using planned or fallback amounts instead of paid records. - dashboard text returned from the API passes deterministic mojibake repair for legacy seed strings before reaching the UI; the repair utility is shared inside the API instead of being buried in sample data, and
npm run smoke:api-text-encodingnow checks the full dashboard payload plus rendered document HTML and issue-block reasons, failing if Russian schedule, readiness, clinical, document, protocol, communication, warning, or document-output strings regress intoÐ.../Ñ...output again.npm run smoke:document-issue-chainsalso injects broken release/signature facts and verifies readable Russian in the issue response, audit passport and archived HTML. - document drafts now have explicit issue/void endpoints with audit events; issue requires signature attestation, void requires structured void attestation, already-issued documents cannot be issued again, voided documents stay in history, voided issued documents keep their archived HTML snapshot, and unresolved-placeholder drafts are blocked before issue.
- file-backed prototype persistence for mutable patients, staff, chairs, documents, payments, communication tasks/events, imaging studies, imports, AI jobs, clinical rules, clinic profile, and audit events.
- file-backed persistence now writes SHA-256 checksums and rotates local JSON backups before overwrite; Settings -> Audit shows file, checksum, backup count, and latest backup status.
/api/system/persistence/verifychecks the current state file and recent backups for readability/checksum status;/api/system/persistence/exportdownloads a no-store JSON snapshot for emergency owner/admin backup before risky migrations and records the download in audit.- API responses now send baseline privacy/security headers:
Cache-Control: no-store,nosniff, frame denial, no referrer, restricted browser permissions, and API-only CSP. - Protected clinical/financial read and write routes now require
x-dente-admin-secretin production whenDENTE_CLINICAL_ADMIN_SECRET,DENTE_SETTINGS_ADMIN_SECRET, orDENTE_TELEGRAM_ADMIN_SECRETis configured; production fails closed without one, while local prototypes can explicitly useDENTE_CLINICAL_ALLOW_UNGUARDED_READS=1andDENTE_CLINICAL_ALLOW_UNGUARDED_MUTATIONS=1. - PWA shell assets are present: manifest, app icon, service worker, and offline page. The service worker is same-origin only, uses network-first navigation plus network-first JS/CSS shell assets with cached fallback, can clear stale shell cache on app request, and avoids caching
/api/*, medical-document, DICOM pixel, or mesh/CAD/STL responses. - Settings -> Audit includes a browser continuity check for local draft writes, IndexedDB audio queue support, PWA/service-worker state, Cache Storage, quota estimate, persistent-storage grant, and queued sync counts; the doctor sees only a one-card device status in Visit.
- Settings -> Audit also includes local workstation module preflight for optional desktop acceleration: local speech recognition, local OCR, CT/DICOM worker, and external viewer can be configured per clinic PC while the app keeps deterministic/server fallbacks when no module is available.
- Settings -> Audit shows compact local module use-plans, so admins see whether the current workstation should use server recognition, local OCR/CT processing, metadata preview, or manual review without pushing that complexity into Visit.
- price-list parsing now has a synthetic no-PHI smoke for offline taxonomy coverage and invalid image protection:
npm run smoke:pricelist-analyzerverifies core dental categories/materials/brands and confirms malformed image payloads do not trigger Groq calls. - Web production build is split into app, React vendor, shared schema, Zod, and icons chunks so the doctor-facing shell no longer ships as one oversized JavaScript file.