Release Notes — April 26, 2026
19 days ago we shipped a big release with the diagnostic module and prosody analysis. Since then the team pushed another 295 changes — and they’re not small. In this release you can record an ad-hoc session before the patient even arrives, your notes merge with AI work and transcription side-by-side in three columns, the public therapist profile is back in full force, and the mobile app now has a dark theme. Under the hood we went through a SOC 2 / HIPAA hardening pass, a DDD audit, and full pseudonymization of patient data on the AI side.
💚 Thanks to the team that drove this release — through reports, tests, code, and patience:
- Bohdan — ad-hoc sessions, package limits and grace period, DDD audit, KSeF invoices, dozens of fixes (SCRUM-756, 815, 1267, 1268, 1276, 1280-1290, 1293, 1309-1316, 1353-1357, 1366, 1388, 1391, 1392)
- Bartek — 3-way merge, 9 separate CBT analyses, cognitive distortions, package session counter (SCRUM-1266, 1320, 1338, 1368-1375, 1385, 1386)
- Ewa Cylwik / Ewa — Beck PDF, second AITS session for the same patient, mobile dark theme and empty state, multilingual mailing, UI colors, patient email uniqueness (SCRUM-883, 898, 902, 1089, 1094, 1297, 1304, 1307, 1319)
- Joanna — recurring calendar, hiding Zoom, on-time cancellation, self-reflection instead of session quality rating (SCRUM-789, 874, 918, 919, 1005)
- Plus Bartlomiej for cognitive distortions and the session counter, which became the foundation of the new subscription model
1. ⚡ Ad-hoc sessions — record before the patient arrives
If you’ve ever had to flip on a recorder fast — because a patient walked in unannounced, because it’s a one-time consultation, because it’s a meeting with someone not yet in your records — the new ad-hoc session module solves that whole pain point.
- Quick Record Button in the web dashboard, in PWA on phone, and in the MAUI app — one click starts recording before you even fill in the patient’s data
- AdHoc Inbox — a dedicated tray for unassigned recordings until you decide where they go
- AssignPatientModal — assign the recording to an existing patient or create a new one straight from the inbox
- Resume / Re-enter room — finish recording a session you started earlier
- Orphan retention + auto-complete — sessions unassigned for X days auto-close so the inbox stays tidy
- Video “Start without patient” — in the LiveKit room you can begin a session without colliding with an existing patient; the AdHoc lands in the inbox afterward
The section is gated behind the PromptyAI code in Settings — off by default, for users who want to see the rest first. Designed and shipped by Bohdan.
2. 📊 Package limits and grace period — transparent subscription
Subscriptions got a full overhaul. The old Starter/Standard plans are deactivated, replaced by Start / Practice / Pro — sized for actual session volume per month.
- Session counter on dashboard — see how many sessions you’ve used this month and how many remain in your package
- Limit emails — automatic notifications at 80% and 100% utilization
- Upgrade prompt — when you hit the limit, you get a gentle plan-change suggestion instead of a hard block
- Pricing from Stripe + discounts — the price you see in the app comes directly from Stripe with active discounts applied (the betatester code does not expire)
- Native subscription cancel — cancel directly in the app, no need to email support
- 14-day grace period — if a payment fails, you get 14 days with access to your own account only (no patient previews), with T+0 and T+13 emails. Real data loss only kicks in after two weeks
- Subscription banner — clean message at the top of the app with no empty bar between the menu and the alert
- Session limit simulator — in the PromptyAI panel there’s a test mode where you can see how the app behaves on a full package
Joint work by Bartlomiej (counter and limits, SCRUM-1266) and Bohdan (subscription, grace, upgrade prompt — SCRUM-756, SCRUM-1366, SCRUM-1391, SCRUM-1392).
3. 📝 3-way merge for notes — therapist, AI and transcription in one view
This is one of the biggest architectural changes under the hood, but the effect on day-to-day work is highly visible: every notes field (Notes, SessionSummary, conceptualization sections) now separately remembers what the therapist wrote, what AI generated, and what came from the dictaphone / transcription.
- Notes 3-way — the notes view shows three sources side-by-side: your text, AI suggestions, and automatic transcription, and you choose what to keep
- SessionSummary 2-way + JSON merge — in the session summary you merge your work with the AI analysis into a single coherent document
- Timestamp separator — when you edit an AI field manually, the system leaves a visible trace of when and whose fragment that is
- [AI] marker from first generation — your fields don’t blend with generated ones
- SourceBadge — colorful pill with an icon showing where a fragment comes from, instead of UPPERCASE text
- AI section replacement instead of appending — when you ask for re-generation of the summary, AI replaces the section instead of stacking another copy underneath
Designed and shipped by Bartek (SCRUM-1320, SCRUM-1368-1375). Underneath: full i18n in 8 languages and new DDD contexts.
4. 🔍 CBT analyses — 9 separate on-demand reports
The old monolithic “generate CBT analysis” was split into 9 separate items that the therapist runs when they actually need them. Less waiting, more control.
- 9 independent analyses — each generated separately, on demand
- Prompt edit cog — click the cog next to each analysis to see/modify the prompt AI will use
- CBT ANALYSES section behind PromptyAI code — section available after entering the code in Settings, for users testing new features
Cognitive distortions got their own view (reported by Bartlomiej, SCRUM-1385):
- Data view instead of an AI report — you see concrete quotes from the transcription tagged by AI, not a narrative generated from them
- All quotes after expand — no date range filter, expand reveals everything
- Available in the main menu — under Conceptualization v2
The old COGNITIVE_DISTORTIONS_INVENTORY prompt has been removed (SCRUM-1386).
5. 🌐 Public therapist profile — full revamp with edit drawers
The public profile under the aitherapy.support brand has been fully rebuilt.
- 3 new edit drawers — bio, specializations, contact details edited in elegant side panels
- Owner edit mode — when you view your own profile while logged in, edit buttons are right at hand
- Years of experience — new
YearsOfExperiencefield visible on the public profile - Color rebrand — sage/lavender palette under the aitherapy.support brand
- “Your privacy” section removed — it was redundant; GDPR is in the footer
- Lucide icons — modern, clean icon typography
Underneath, a public endpoint GET /api/public/therapists was added — returning therapists with active public profiles. Foundation for a therapist directory.
6. 🎥 Video sessions — PiP, swap cameras, fixed background blur
Video got a set of fixes that any therapist running sessions through the app will notice.
- PiP / minimize — minimize the video room to a floating mini-player and check notes in another tab without breaking the connection. Mic and camera keep working
- Swap cameras — switch who’s in the main view and who’s in mini with one click
- PiP resume — coming back from minimization, you skip the lobby (PreJoinLobby) and jump straight into the conversation
- Per-participant audio for diarization — transcription splits speakers even in PiP
- Background blur fixed — CSP was blocking MediaPipe WASM; blur now works with one click
- Cleaner AITS session UI — the AI assistant disappeared from the toolbar, less visual noise
- PiP overlay clickable — removing
pointerEvents:noneandoverflow:hidden, PiP buttons respond to clicks - “Join session” button — was hidden by a CSS override, now visible
- Stale closure in handleMaximize — fixed; PiP maximize no longer throws an error
7. 📐 Beck v2 conceptualization and ABC — PDF, version toggle, prompt editing
- Beck and ABC v2 PDF — fixed formatting and automatic inclusion in session materials (reported by Ewa, SCRUM-1297)
- Beck PDF — border-only sections, colorful specializations, clean initials, SVG arrow between sections
- ABC v1/v2 toggle in Conceptualization v2 — pick which ABC version to display
- Prompt edit cog in ABC v2 — modify the AI prompt in the ABC v2 tab
- PDF export for AI analyses and ABC per session — individual analyses exported separately
- ABC labels pluralization in 8 languages — conceptualization table has correct plural forms
- ABC empty columns fix — backend returns proper plural property names
8. 🧾 Invoices and KSeF — payment selection + state machine
The invoicing system was refactored for compliance with KSeF (Polish national e-invoicing system).
- Invoice from selected payments — pick payments and the system creates a single invoice from them (snapshot + Payment FK)
- “Issue invoice” entry point in the therapist’s finance view — quick access from the dashboard
- Item removal in the invoice issuance modal — fix mistakes on the fly
- KSeF dev/prod separation — sandbox and production work on separate credentials
- Invoice state machine (SCRUM-1357 Bohdan) — Draft → Issued → Sent → Cancelled, with a DB constraint on illegal transitions
- AE KsefToken — KSeF token encrypted with Always Encrypted
Joint work by Bohdan (SCRUM-1357, SCRUM-1388).
9. 📅 Calendar, recurring sessions, and locations
The weekly and recurring calendar got dozens of small but visible improvements — most reported by Joanna and Ewa.
- Sessions from 22:00 — were invisible on the weekly grid, now they show until end of day
- All hours on the recurring slot grid (SCRUM-874, Joanna)
- On-time cancelled session — does not show as “Unpaid” (SCRUM-918, Joanna)
- Recurrence label next to the recurring slot (SCRUM-919, Joanna) — you see “weekly”, “biweekly” next to each repeating session
- Hiding Zoom integration from UI (SCRUM-789, Joanna) — Zoom is no longer supported as a video channel
- Session popover in calendar — no longer hides off-screen
- TherapistAvailability — auto-save on every change (debounce 800 ms), no save button needed
- AddSessionModal — option “create session outside availability hours” — exceptions can force a slot
- Status “No-show” (NoShow=5) — new status, settable even from completed or cancelled
- RecurringSessionsCalendar — summary tiles above the legend and grid
Therapist locations got online meeting channels per location:
- Onsite mode — address and city fields for in-person locations
- Onsite/online switch instead of a checkbox — clearer mode choice
- Online channel per location — different locations can have different channels (AITS, Google Meet, external link), auto-propagated to sessions
- Per-patient permanent meeting rooms — every patient has a permanent AITS room link and a separate external link visible in patient settings
- Location deletion — fixed (was disappearing from DB but staying on the UI list)
And finally meeting type in recurring sessions — fixed propagation of MeetingType and MeetingLink from the recurring series configuration to each generated session and to reminder emails (SCRUM-902, Ewa).
10. 📱 Mobile app — dark theme, AdHoc Inbox, polish
Mobile moved to version 1.5.0 (build 18) with a load of user-visible changes and a few App Store / Play Store signing touches.
- Dark theme — full dark mode with readable group headers (SCRUM-1089, Ewa + SCRUM-815)
- Configurable reminders — pick when you want a session notification
- Mobile reminder 15 min — 15-minute pre-session reminder + warning when system notifications are off (SCRUM-815, Bohdan)
- Empty state for session list — instead of a blank screen, a richer onboarding (SCRUM-1094, Ewa)
- Auto-refresh banner — when the app detects an update, it shows an inviting refresh banner
- AdHoc Inbox + AssignPatient + quick-start in MAUI (SCRUM-1289, Bohdan)
- PWA mobile quick-start for ad-hoc sessions (SCRUM-1286, Bohdan)
- Responsive web view for phone — desktop unchanged, on phone the layout adapts to the narrow screen
- Deep-link handler in iOS — uses BindingContext, URL scheme registration
- iOS NSLocation purpose strings — fixing ITMS-90683 (App Store rejected build without these)
- Android AAB — explicit jarsigner step
- App Store Connect and Google Play materials — full pack plus WCAG 2.2 audit
11. 🔐 Security: SOC 2 / HIPAA, MFA, full AI pseudonymization
Underneath the product layer we did a full SOC 2 and HIPAA hardening pass plus a DDD audit with five concrete deliveries.
- MFA re-verification for critical operations — for sensitive actions (password change, data export) you re-confirm with MFA
- Backup codes for MFA — emergency codes if you lose your phone
- IAnonymizedLlmGateway (SCRUM-1353, Bohdan) — all AI model calls now go through a pseudonymizing gateway. Names, dates, places are replaced with pseudonyms before they leave the app; AI gets only the anonymized version
- Pseudonymization of email/SMS notifications (SCRUM-1354) — message templates also don’t leak patient data on send
- OAuth tokens Google/Zoom encrypted with Always Encrypted (SCRUM-1355) — integration access tokens are column-encrypted
- Patient.PendingConsent flag (SCRUM-1356) — patients without active consent get an enforcement flow + frontend badge
- Invoice state machine (SCRUM-1357) — invoice statuses are immutable in invalid directions, enforced at the DB level
- Audit immutability + user snapshot in 3 audit tables — action log cannot be modified post-fact
- DeanonymizationAuditLog — every use of patient data after deanonymization is logged together with the email + displayName snapshot of the user who performed it
- Hardcoded secrets removed — all keys and passwords from appsettings.json moved to Azure Key Vault
- Always Encrypted for all user text data (ADR-0018) — architectural direction approved; further tables migrate next release
- ISO/IEC compliance gap analysis — 95 gaps across 9 standards mapped, with a remediation plan
Backed by a full DDD audit (Sessions 1-8, Phases 1-6) — domain context restructure, architecture documentation, ADRs, mermaid + tests, playbooks, Confluence integration.
12. 👤 Patient portal — analyses in Goals, materials, better preview
- Beck conceptualization in the patient’s “Goals” tab — when you publish the conceptualization, the patient sees it in their portal
- Publishing analyses to the patient portal + notification preferences (SCRUM-1338, Bartlomiej) — control what and when the patient receives
- Portal PDF preview —
iframeinstead ofembed, CSP allowsblob:(PDFs load) - Patient portal preview from the therapist’s view — see what the patient sees (chrome in wrapper, dashboard 1:1)
- Session materials in Azure Blob Storage — SessionMaterial as blob, no more local filesystem
- Consent / documents section + registration source in patient settings
- Global patient email uniqueness (SCRUM-898) — a second patient with the same email gets a 409 + closed modal (SCRUM-898, Ewa)
- Notification template seeder for SK, CA, RU, UK, LT (SCRUM-883, Joanna) — full multilingual mailing
- Resolver fallback to platform default — when a therapist hasn’t overridden the template, the default multilingual one is sent (SCRUM-883)
13. 🎙️ Transcription — reliable recording, append, real-time with diarization
- Append to existing transcription — continue recording into the same session; segments append (intent survives a page reload)
- Per-participant audio for diarization — video transcription separates speakers on independent tracks
- Transcription survives PiP — minimizing the room doesn’t break recording
- Transcription language from session settings — STT uses the language you set for the patient
- Auto-refresh for transcription on focus / visibilitychange / return from video
- Confirm on stop recording < 2s — guard against an empty WAV from a misclick
- “Sending” badge in the session list — you see when a recording is in upload
- Real-time with diarization in video session — wasn’t starting before; now works
- OPFS backup + 48 kbps Opus — recording also saves locally to OPFS as fallback, audio parameters unified
- SAS direct upload audio (SCRUM-1276, Bohdan) — audio goes directly to Azure via SAS URL, bypassing the backend
- m4a native to Azure Speech — m4a handled natively, no conversion
- Format detection by magic bytes (SCRUM-1311, Bohdan) — when Content-Type=
application/octet-stream, format is detected from file content - Hub without DB text — SignalR realtime hub no longer persists text in DB; finishPending uses multipart POST like MAUI
- SignalR 1006 fix — removed skipNegotiation so Azure SignalR works + keep-alive tuning
- [AI] marker from first generation + reliable marker lookup
14. 💬 Notifications, patient settings, small UX
- NotificationTemplatesAdmin — HTML/Text toggle in the email editor
- NotificationSettings — email preview renders HTML with fallback
- PromptEditModal — rebuilt: tabs, model selector, required version names
- Hide_dev_ui flag reset on logout — developer flags don’t persist across sessions
- Test patient Ann Fauler — English 1:1 of Polish Anna Pełnialska for EN-only demos
- Dev UI presentation mode toggle — quick switch into presentation mode
- Bug report — Jira attachments + reporter label + centered “Submit” button
- Auth-refresh loop fixed — abort requests and return to login on 401
- “Server is starting” banner — on cold start (scale-to-zero) we show a notice instead of a blank page
- Session rating scale — self-reflection instead of quality rating + disclaimer (SCRUM-1005, Joanna)
- “Minimize” button color changed to cobalt violet (SCRUM-1307, Ewa)
- “Transcription” label color from red to green (SCRUM-1304, Ewa)
- “Record now” → “Record AdHoc session” — more accurate button name
- Sandbox with green ribbon in the top-left corner +
[SANDBOX]prefix in title — to avoid environment confusion, plus a repeatable PROD → Sandbox clone (Azure IaC + scripts)
✅ QA Checklist — what to verify after deploy
| Feature | Verify |
|---|---|
| Ad-hoc session — Quick Record | Click Quick Record on the dashboard or PWA — recording starts immediately, the session lands in the AdHoc Inbox |
| AssignPatient in AdHoc Inbox | Open an ad-hoc recording — you can assign to an existing patient or create a new one |
| Package limit | Dashboard shows the session counter for the current month and how many remain in the package |
| Grace period after failed payment | Subscription simulator in PromptyAI — force a failed payment, you get account-only access for 14 days |
| 3-way merge notes | In session details, the Notes field shows three sources side-by-side (therapist / AI / transcription); you can choose what to keep |
| 9 CBT analyses | The CBT analyses tab shows 9 separate items; each generated on demand; each has a prompt-edit cog |
| Public profile — drawers | Open your own public profile while logged in — bio, specialization, contact edit drawers are visible |
| Video PiP | During a video session click minimize — window collapses to mini-player, mic still works, you return without lobby |
| Background blur | In the video lobby enable background blur — works immediately, no CSP errors |
| Beck conceptualization PDF | Generate the conceptualization PDF — also appears in session materials |
| ABC v1/v2 toggle | In conceptualization v2 you can switch the ABC source between version 1 and 2 |
| Invoice from selected payments | Select a few payments and click “Issue invoice” — a single invoice is produced from the snapshot |
| Recurring session with Google Meet | The second and third session in the series have Google Meet correctly set and the email contains the right link |
| Sessions from 22:00 | The weekly calendar shows sessions scheduled at 22:00 and later |
| ”No-show” status | In a completed / cancelled session you can change status to “No-show” |
| Mobile dark theme | In the mobile app switch to dark theme — group headers are readable |
| AdHoc Inbox in mobile | Mobile shows the ad-hoc inbox and lets you assign a patient |
| MFA re-verification | When changing password, the app asks for MFA again |
| AI pseudonymization | In logs you see patient data going to AI as pseudonyms |
| Session materials as blob | Upload a file to session materials — it lands in Azure Blob Storage, not on local disk |
| Global patient email uniqueness | Add a second patient with the same email — you get a 409 and the modal closes |
| ”Server is starting” banner | After a longer idle period, app entry shows the banner instead of a blank page |
🛠️ What we fixed — fine print
In addition to the big features, ~80 reports from testers and users were resolved. Highlights:
- PiP overlay was not clickable — fixed
- Stale closure in handleMaximize — PiP maximize no longer throws
- PiP keeps the connection — previously
room.disconnect()triggered on the fullscreen↔PiP transition - “Join session” button was hidden by a CSS override — uncovered
- Second AITS session for the same patient (SCRUM-1319, Ewa) — drop UNIQUE INDEX on
Session.VideoInviteToken - Session numbering in conceptualization skips cancelled (SCRUM-1267, Bohdan)
- Null-safe Payment access in GetSessionDetailsAsync (SCRUM-1268, Bohdan)
- Test session collision of Anna Pełnialska with Jan at 14:00 — fixed in the seeder
- Dashboard slowdown — null-safe mapping in GetTodaySessionsAsync and GetSessionDetailsAsync
- Auto-refresh after recording from microphone ends — works
- Batch fallback doesn’t overwrite text from previous recordings or block the UI with Processing status
- Transcription language from session settings — STT respects the patient’s preference
- CSP — allowing
*.clarity.ms,blob:in connect-src, analytics in script-src - Auth-refresh loop — aborting requests after 401 and returning to login
- CSP MediaPipe WASM — background blur works
- DEV/SANDBOX ribbon also on Azure FQDN, not just custom domain
- False toast “Transcription failed” on session entry — removed
- Toast instead of red banner on failed transcription + removed “Resample” button
- Real-time with diarization in video session — starts properly
- AI streaming — per-handler AbortController instead of shared (
BodyStreamBuffer was aborted) - Nginx non-root + Azure Container Apps — multiple iterations to make it work
- Distributed lock (sp_getapplock) — startup migrations no longer conflict on parallel deploys
- DbUpdateConcurrencyException in batch transcription poller (SCRUM-1313, Bohdan)
- BlobNotFound as expected state (SCRUM-1314, Bohdan)
- Stripe webhook non-JSON payloads (scanners / bots) — silently handled (SCRUM-1315, Bohdan)
- Audio format detection by magic bytes (SCRUM-1311, Bohdan)
- Resilient audio upload + defensive Content-Type (SCRUM-1293, Bohdan)
- FK migration ActiveEmbeddingModelId → ON DELETE NO ACTION
- Dev client deploy — CACHE_BUST=
github.shaforces frontend rebuild
This is the largest, densest release of this spring. If a feature feels familiar — it’s because it was your request or your ticket. Thank you for every report and every screenshot.
Article prepared by the Therapy Support team