# Speech app review — 11 September 2026

The app has substantial foundations for a connected therapy workflow. The immediate priority is making a small, complete workflow dependable: assess a child, choose a target, run a session, publish home practice, record the family's experience, and review progress.

## Scope and evidence

Inspected the live public catalog and sign-in experience, signed-in super-admin dashboard, Content Quality, Activity Builder, Native Review, and organization details at speech.neurapy.ai. Tested the dashboard date selector and practitioner navigation. Reviewed the local assessment, session player, parent home-practice, layout, and supporting server code.

The current super-admin session has no active organization. Patients returns `403 Active organization required`, and the account menu has no organization selector. Practitioner and parent workflows therefore received source review, not a completed live walkthrough. The child player also received source review only. The local checkout has not been verified as identical to the deployed release. No clinical efficacy assessment, full security audit, production data writes, or source-code fixes were performed.

## Prioritized findings

### 1. High — assessment completion can overtake its final save

**Code finding.** `Assessment.vue` debounces saves by 800 ms. `flush()` clears the dirty state before the request succeeds. `complete()` invokes `flush()` and immediately issues the completion request without awaiting successful persistence. Completion uses the ratings already in the database; subsequent changes are rejected once an assessment is completed.

On a slow connection, changing a rating and completing immediately can finalize an older assessment. Save failures can also leave the client with no dirty changes to retry.

**Fix:** await every outstanding save before completing, retain failed changes, display an explicit save failure, and disable completion until persistence is confirmed. A server operation that atomically saves final ratings and completes the assessment would avoid the ordering ambiguity.

**Verify:** delayed save followed by immediate completion; failed save; rapid consecutive edits; navigation with unsaved changes.

References: `resources/js/Pages/Speech/Children/Assessment.vue:46–72`; `app/Domain/Speech/Assessment/Services/AssessmentService.php`, `save()` and `complete()`.

### 2. High — retry does not recover an unsaved child response

**Code finding.** `Play.vue` creates the trial payload and its UUID inside `record()`. On a failed POST, it shows an error. The error screen's “Try again” calls `loadNext()`, rather than resending the same trial. An answer whose POST never reached the server can be lost. If the server committed the answer but the response was lost, the client cannot reconcile that outcome through this button.

**Fix:** retain the pending trial and UUID until acknowledged; retry that exact payload; reconcile success before fetching another trial. The server already supports deduplication using `client_trial_id`.

**Verify:** failure before the server receives the trial, failure after server commit, and repeated retry. Each answer should appear exactly once.

References: `resources/js/Pages/Speech/Sessions/Play.vue:231–263,363–365`; `app/Domain/Speech/Therapy/Services/TrialRecorder.php:26`.

### 3. High — available content is materially behind the authored catalog

**Live finding.** Content Quality reports 48 skills without published activities. Activity Builder lists 192 activities, with drafts throughout the inspected list. English has 100 concepts without audio; Hindi has 100 without audio and 40 without words. Native Review shows 0% approved for the listed languages, including the two marked live. Its English and Hindi rows each show 780 phrases waiting.

This does not prove that sessions cannot run: the Activity Builder explicitly describes templates as a fallback. It does mean activity counts should not be treated as ready-to-use therapy coverage.

**Fix:** assemble a narrow reviewed starter set; show readiness by skill and language before practitioners launch it; distinguish authored, reviewed, published, and playable content. Review content suitability individually rather than bulk-publishing drafts solely to clear the counts.

Evidence: [Content Quality](https://speech.neurapy.ai/admin/speech/content-quality), [Activities](https://speech.neurapy.ai/admin/speech/activities), [Native Review](https://speech.neurapy.ai/admin/speech/review).

### 4. Medium — multilingual speech fallback has no selected language

**Code finding.** The child player falls back to `SpeechSynthesisUtterance(instr.text)` without setting `lang` or a matching voice. For Hindi content on a device with an English default voice, pronunciation or playback may be unsuitable. Playback failure for an existing audio URL is also swallowed without a visible recovery message.

**Fix:** pass the instruction's locale to the player, select an available matching voice, and handle missing voices or failed recordings explicitly. Keep the status explanation in the adult controls.

**Verify:** English and Hindi on the supported devices, absent locale voices, and an audio request failure.

Reference: `resources/js/Pages/Speech/Sessions/Play.vue:205–229`.

### 5. Medium — dashboard date selection does not filter reporting

**Live and code finding.** Selecting “30 days” leaves the chart showing September 5–11. The Vue component stores `dateRange` but never uses it to query or filter data; the server fixes the reporting window at seven days. Refresh also sends no selected dates.

**Fix:** connect the selected range to server queries, comparison periods, chart labels, and totals. Persist the chosen period through refresh and show it on reports.

References: `resources/js/Pages/Dashboard/Index.vue:18,118,148`; `routes/web.php:46–55`.

### 6. Medium — no-organization state looks like a populated clinic dashboard

**Live finding.** The admin dashboard presents two patients, zero credits, no active plan, and an urgent top-up message. Patients then returns a plain 403 because no organization is selected. The visible account menu offers no organization switching option.

**Code finding.** Dashboard patient queries apply organization filtering conditionally, only when an organization ID and the expected column exist. Without an active organization, they can query the shared patient table without that filter. This was observed under super-admin access; it is not evidence of unauthorized access by another role.

**Fix:** use an explicit platform-admin landing page or require organization selection before showing clinic metrics. Missing context should not be represented as a zero balance. Audit missing-organization handling under ordinary accounts separately.

References: [Dashboard](https://speech.neurapy.ai/dashboard), [Patients](https://speech.neurapy.ai/org/patients); `routes/web.php:85–88,119–121`.

### 7. Medium — parent response controls exclude keyboard users

**Code finding.** Home-practice response and help chips are labels wrapping checkbox/radio inputs styled with `display: none`. The hidden inputs cannot receive keyboard focus, and the labels provide no keyboard replacement.

**Fix:** retain native inputs with a visually hidden style, visible focus states, and appropriately grouped labels. Test the whole practice log using keyboard and a screen reader.

Reference: `resources/js/Pages/PatientPortal/SpeechHome.vue:145–150,269`.

## Recommendations for the four experiences

| View | Existing strengths | Highest-value improvement |
| --- | --- | --- |
| Admin | Curriculum, concepts, activities, language review, media quality, and audit tooling exist. | Lead with readiness and review queues. Group the long sidebar into Curriculum, Content, Quality, and Operations. |
| Practitioner | Assessment, targets, configurable session proposals, prompt levels, home programs, and progress are represented in code. | Make the primary path explicit: Child → Assess → Plan → Start session → Summary → Home practice. Keep billing and infrastructure secondary during therapy. |
| Parent | Routine scripts, acceptance of gestures/AAC/sounds/words, family vocabulary, simple progress language, and Hindi UI strings. | Default to today's practice; require an intentional outcome selection instead of assuming one success; clearly identify family-reported evidence in progress. |
| Child | A large start control, replay, breaks, target sizing, contrast, reduced motion, and switch scanning are implemented. | Make child mode a deliberate presentation state with adult controls hidden by default and a reliable way for the adult to return. Validate audio and failed-save recovery on real devices. |

The public homepage currently leads with tracking/eye-hand activities, blank thumbnails, missing descriptions, and technical scene identifiers. Give Speech its own landing experience and explain the communication goal of each activity. The reviewed curriculum appears centered on language and functional communication; describe that scope clearly rather than implying every speech-therapy use case has been assessed here.

## Recommended next pass

1. Resolve the save, retry, language fallback, and report-filter findings.
2. Finish a reviewed starter pathway in one language, including its home routine and progress summary.
3. Use a designated demo practitioner and parent account to run that same pathway end to end, including failure recovery.
4. Test child interaction and parent logging on a phone/tablet, including keyboard/switch access where supported.

The remaining live role walkthrough needs an active demo organization and the corresponding practitioner/parent sessions.
