Matías Fernández
← All work

SIDE PROJECT · FUNCTIONAL MVP

PsiNota

How I built a mobile-first, offline-first product that turns mental-health consultation audio into an editable, versioned, and auditable clinical-note draft.

At a glance

Role
Sole author · product design, architecture, and full-stack engineering
Period
2026 · PRIVATE REPOSITORY
Team
Independent project · one engineer
Stack
React 19 · TypeScript · Express · PostgreSQL · TypeORM · OpenAI · PWA · Docker
Source and demo
Private source repository and no public demo. This case study publishes the verified architecture, behavior, and quality gate without exposing clinical data.

Disclosure boundary

Disclosure boundary

Audit basisReviewed against the private repository, its automated checks, and the project technical brief. The product is presented as an engineering MVP, not certified medical software or commercially validated adoption.

Published here

  • Product flow, system boundaries, stack, engineering decisions, and failure handling.
  • Verified lint, typecheck, test, and production-build status as of the review date.
  • Explicit limits around human review, privacy, beta readiness, and regulatory work.

Kept private

  • Private source code, credentials, deployment details, and any real or representative clinical records.
  • A public demo until security, privacy, consent, retention, and market-specific requirements are reviewed.

01 / Overview

PsiNota explores a complete product problem rather than an isolated model call: preserve a consultation recording, process it reliably, produce a structured draft, and keep the professional in control of the final clinical note.

02 / The problem

The problem

A transcript does not remove the documentation burden by itself. Relevant information still has to be structured, reviewed, corrected, finalized, and traced without losing the original recording when connectivity is unreliable.

03 / My responsibility

My responsibility

  • Designed the product states and implemented authentication, consultation tracking, browser recording, resumable uploads, asynchronous processing, review, versioning, finalization, and audit history.
  • Built the React PWA and Express API, modeled the PostgreSQL domain, integrated transcription and structured extraction, and packaged the system for a pragmatic Docker Compose deployment.
  • Defined backend invariants for user isolation, sequential note versions, immutable finalized notes, persisted jobs, retries, and auditable state changes.

Constraints

Constraints

  • AI output had to remain an editable draft and require explicit professional approval; it could not become a clinical decision.
  • The recording is the hardest input to reproduce, so intermittent connectivity could not be allowed to discard it.
  • The MVP needed low-cost deployment and recoverable processing without introducing distributed infrastructure before product validation.

04 / Architecture

Architecture

Consultation-to-note path

The PWA protects capture locally and uploads directly or in chunks. The API validates identity and ownership, creates a persisted job, and streams progress. A transactional worker transcribes, extracts, renders, versions, and audits the draft in PostgreSQL.

  1. 01Capture PWAReact · IndexedDB · offline queue
  2. 02Application APIExpress · JWT · Zod · chunk uploads
  3. 03Persistent jobsPostgreSQL · transactional claim · heartbeat
  4. 04Clinical draftOpenAI · versions · audit · human approval

05 / Key decisions

Key decisions

01

Persist work instead of holding requests open

Context
Transcription and extraction can outlive HTTP timeouts and fail after partial progress.
Decision
Return 202 Accepted, persist processing jobs, and let workers claim them with FOR UPDATE SKIP LOCKED, heartbeat, stale-job recovery, and retries.
Consequence
The UI receives progress through SSE with REST fallback, while interrupted work remains observable and recoverable.
02

Make human review a domain state

Context
A fluent model output is not a final clinical document and must not silently replace professional judgment.
Decision
Create an AI-render version, record every edit as a new version, and require explicit finalization that locks later changes.
Consequence
The product distinguishes generated, reviewed, and finalized states and can reconstruct how the note changed.
03

Protect capture before optimizing processing

Context
A failed upload after a consultation risks losing work that may be impossible to repeat.
Decision
Store pending audio in IndexedDB, queue it locally, resume via Background Sync, and support chunked uploads on unstable networks.
Consequence
Connectivity becomes a recoverable product state instead of a reason to restart capture.

06 / Trade-offs

Trade-offs

PostgreSQL-backed jobs before a separate queue

What it enablesTransactional state, fewer moving parts, low operating cost, and recovery suited to MVP volume.

What it costsAPI and worker share a database coordination boundary that will need reconsideration if throughput or isolation requirements grow.

Local volume storage for the MVP

What it enablesA deployable vertical product on one inexpensive VPS with simple backup and restore mechanics.

What it costsProduction use needs encrypted object storage, signed access, verified deletion, and market-specific retention controls.

SSE with REST fallback

What it enablesSimple server-to-client progress updates with a recovery path when streaming is interrupted.

What it costsIt is one-way communication and requires careful reconnection and authorization behavior at proxy boundaries.

07 / Failure modes

Failure modes

  1. Pending audio remains in IndexedDB and the local queue until connectivity returns instead of disappearing after a failed request.
  2. Workers heartbeat during long work; stale jobs can be detected and retried instead of remaining permanently in progress.
  3. Every clinical query is scoped by the userId from the verified JWT; the API never trusts a client-supplied owner identifier.
  4. Finalized notes reject later edits, while prior versions and audit events preserve the sequence that produced the final document.

08 / Results and evidence

Results and evidence

7 / 7

automated tests passing at the verified gate

Measured result · individualOpen evidence ↗
3

production builds verified: API, web app, and service worker

Measured result · individualOpen evidence ↗
202

Accepted response contract for asynchronous processing

Implemented capability · individualOpen evidence ↗
OFFLINE

local audio recovery path implemented before upload

Implemented capability · individualOpen evidence ↗

09 / Delivery state

Delivery state

  1. 01
    Delivered · Capture → reviewed note

    End-to-end functional MVP

    Authentication, consultation states, audio or text input, asynchronous processing, editing, versioning, finalization, and audit history.

  2. 02
    Delivered · PWA · offline queue · resumable upload

    Resilient mobile workflow

    IndexedDB, Background Sync, direct and chunked uploads, SSE progress, retries, and stale-job recovery.

  3. 03
    Delivered · Lint · types · tests · builds

    Verified engineering gate

    API and web typechecks, seven automated tests, and production builds for API, web app, and service worker passed on 2026-08-25.

  4. 04
    Planned · Security · E2E · observability · product evidence

    Closed beta and production hardening

    Threat modeling, encrypted audio storage, verified deletion, restore drills, Playwright coverage, operational metrics, and validation with consented fictitious or de-identified data.

10 / What I would improve next

What I would improve next

  1. Cover the critical create, capture, process, edit, and finalize journey with Playwright and ephemeral PostgreSQL integration tests.
  2. Complete a threat model, encrypt audio at rest, verify retention-driven deletion, and rehearse backup restore before any real-data beta.
  3. Run a small consented beta and measure processing success, median draft latency, editing rate, time saved, return use, and unit cost without promoting targets into outcomes.

12 / Continue the conversation

Continue the conversation

The résumé provides the complete career context. For client-sensitive details, contact me and I can discuss the work at the appropriate disclosure level.

Contact me Open résumé